3.16. Controle de servo¶
Um servo de hobby (RC) é um pequeno motor com engrenagens em uma carcaça selada, com controle de posição em malha fechada já embutido. Dentro da carcaça ficam um motor DC, uma caixa de redução, um potenciômetro ligado ao eixo de saída e uma pequena placa driver que compara a leitura do potenciômetro com um setpoint que chega de fora. O driver gira o motor no sentido que reduz o erro e para quando a posição corresponde ao alvo. Do lado da câmera, nada disso é visível – você apenas diz ao servo para onde ir.
3.16.1. O sinal PWM¶
Um servo recebe seu setpoint como um sinal PWM a uma taxa de quadros fixa de 50 Hz, em que a largura do pulso seleciona a posição:
Um pulso de 1,0 ms leva o eixo a uma extremidade de seu curso.
Um pulso de 1,5 ms estaciona o eixo no centro.
Um pulso de 2,0 ms leva o eixo à outra extremidade.
Qualquer valor intermediário corresponde a uma posição intermediária.
O quadro de PWM do servo tem 20 ms de duração; a largura do pulso (1,0 – 2,0 ms) seleciona a posição.¶
Diferentemente de LEDs e motores, o servo não faz a média do PWM. A própria largura do pulso é o comando: a lógica interna do servo mede cada pulso, ajusta seu alvo de acordo e gira o motor até que a saída corresponda. O ciclo de trabalho como fração (entre 5 % e 10 % ao longo de toda a faixa) é incidental – o que importa é a largura absoluta do pulso, que é o que o software precisa controlar.
3.16.2. Fiação¶
Servos de hobby usam um conector de três fios:
Alimentação (comumente vermelho): a alimentação própria do servo, normalmente de 4,8 V a 6 V. Não alimente o servo pelo trilho de 3,3 V da câmera – ele não consegue fornecer a corrente de travamento e o trilho sofrerá uma queda de tensão.
Terra (comumente preto ou marrom): o caminho de retorno da alimentação do servo, ligado ao terra da câmera para que o sinal também tenha uma referência compartilhada.
Sinal (comumente branco, amarelo ou laranja): a linha de PWM vinda do GPIO da câmera.
3.16.3. Código¶
duty_u16() funcionaria, mas define o ciclo de trabalho como uma fração do período – inconveniente para um sinal em que o que importa é a largura absoluta do pulso e o período é fixo. duty_ns() define a largura do pulso diretamente em nanossegundos:
from machine import PWM, Pin
servo = PWM(Pin("P7"), freq=50, duty_ns=1_500_000) # centre
A portadora é de 50 Hz (período de 20 ms); o tempo em nível alto em cada ciclo é exatamente 1500 µs. Uma pequena função auxiliar torna explícito o mapeamento de posição para pulso:
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
Uma varredura lenta ao longo da faixa:
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)
A faixa de 1,0 – 2,0 ms é o padrão, mas muitos servos aceitam uma faixa mais ampla (frequentemente de 500 µs a 2500 µs) para o curso completo. A folha de dados do servo lista os limites exatos de largura de pulso; valores fora dessa faixa podem fazer o motor bater violentamente em seus batentes mecânicos.
Para um servo com faixa não padrão, eleve os limites a constantes e parametrize o mapeamento:
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)