3.16. Controllo dei servo¶
Un servo hobbistico (RC) è un piccolo motore con riduttore alloggiato in un involucro sigillato con il controllo di posizione ad anello chiuso integrato. All’interno dell’involucro si trovano un motore in corrente continua, un riduttore a ingranaggi, un potenziometro collegato all’albero di uscita e una piccola scheda di pilotaggio che confronta la lettura del potenziometro con un setpoint proveniente dall’esterno. Il driver fa girare il motore nel verso che riduce l’errore e lo arresta quando la posizione corrisponde. Dal lato della camera nulla di tutto ciò è visibile: ci si limita a dire al servo dove andare.
3.16.1. Il segnale PWM¶
Un servo riceve il proprio setpoint sotto forma di segnale PWM a una frequenza di frame fissa di 50 Hz, in cui la larghezza dell’impulso seleziona la posizione:
Un impulso di 1,0 ms porta l’albero a un estremo della sua corsa.
Un impulso di 1,5 ms posiziona l’albero al centro.
Un impulso di 2,0 ms porta l’albero all’altro estremo.
Qualsiasi valore intermedio corrisponde a una posizione intermedia.
Il frame PWM del servo è lungo 20 ms; la larghezza dell’impulso (da 1,0 a 2,0 ms) seleziona la posizione.¶
A differenza dei LED e dei motori, il servo non calcola la media del PWM. La larghezza dell’impulso stessa è il comando: la logica interna del servo misura ogni impulso, imposta di conseguenza il proprio obiettivo e fa girare il motore finché l’uscita non corrisponde. Il duty cycle come frazione (tra il 5 % e il 10 % sull’intero intervallo) è incidentale: ciò che conta è la larghezza assoluta dell’impulso, che è quanto il software deve controllare.
3.16.2. Cablaggio¶
I servo hobbistici usano un connettore a tre fili:
Alimentazione (comunemente rosso): l’alimentazione propria del servo, in genere da 4,8 V a 6 V. Non alimentare il servo dal binario a 3,3 V della camera: non può fornire la corrente di stallo e il binario subirebbe un brown-out.
Massa (comunemente nero o marrone): il percorso di ritorno per l’alimentazione del servo, collegato alla massa della camera in modo che anche il segnale abbia un riferimento condiviso.
Segnale (comunemente bianco, giallo o arancione): la linea PWM proveniente dal GPIO della camera.
3.16.3. Codice¶
duty_u16() funzionerebbe, ma imposta il duty come frazione del periodo – scomodo per un segnale in cui ciò che conta è la larghezza assoluta dell’impulso e il periodo è fisso. duty_ns() imposta direttamente la larghezza dell’impulso in nanosecondi:
from machine import PWM, Pin
servo = PWM(Pin("P7"), freq=50, duty_ns=1_500_000) # centre
La portante è a 50 Hz (periodo di 20 ms); il tempo alto di ogni ciclo è esattamente 1500 µs. Una piccola funzione di supporto rende esplicita la mappatura da posizione a impulso:
def set_position(angle):
# angle: 0..180 degrees mapped to 1.0..2.0 ms
pulse_us = 1000 + (angle * 1000) // 180
servo.duty_ns(pulse_us * 1000)
set_position(0) # full one way
set_position(90) # centre
set_position(180) # full the other way
Una variazione lenta lungo l’intervallo:
import time
for angle in range(0, 181, 5):
set_position(angle)
time.sleep_ms(20)
for angle in range(180, -1, -5):
set_position(angle)
time.sleep_ms(20)
L’intervallo da 1,0 a 2,0 ms è lo standard, ma molti servo accettano un intervallo più ampio (spesso da 500 µs a 2500 µs) per la corsa completa. Il data sheet del servo riporta gli esatti limiti di larghezza dell’impulso; valori al di fuori di tale intervallo possono far sbattere il motore contro i suoi finecorsa meccanici.
Per un servo con un intervallo non standard, si portino i limiti in costanti e si parametrizzi la mappatura:
PULSE_MIN_US = 500 # full one way (from the data sheet)
PULSE_MAX_US = 2500 # full the other way
def set_position(angle):
span_us = PULSE_MAX_US - PULSE_MIN_US
pulse_us = PULSE_MIN_US + (angle * span_us) // 180
servo.duty_ns(pulse_us * 1000)