3.16. Control de servos

Un servo de aficionado (RC) es un pequeño motorreductor en una carcasa sellada con control de posición en lazo cerrado integrado. Dentro de la carcasa hay un motor de corriente continua, una caja reductora, un potenciómetro conectado al eje de salida y una pequeña placa controladora que compara la lectura del potenciómetro con una consigna que llega desde el exterior. El controlador hace girar el motor en el sentido que reduce el error y se detiene cuando la posición coincide. Desde el lado de la cámara nada de esto es visible: simplemente le indicas al servo a dónde ir.

3.16.1. La señal PWM

Un servo recibe su consigna como una señal PWM a una frecuencia de fotograma fija de 50 Hz, donde el ancho de pulso selecciona la posición:

  • Un pulso de 1,0 ms lleva el eje a un extremo de su recorrido.

  • Un pulso de 1,5 ms deja el eje en el centro.

  • Un pulso de 2,0 ms lleva el eje al otro extremo.

Cualquier valor intermedio se asigna a una posición intermedia.

Tres filas de trazas de onda cuadrada apiladas verticalmente. Cada fila muestra un periodo de 20 ms de un PWM de 50 Hz con un pulso alto estrecho al principio: 1,0 ms en la fila superior, 1,5 ms en la del medio, 2,0 ms en la inferior.

El fotograma PWM del servo dura 20 ms; el ancho de pulso (1,0 – 2,0 ms) selecciona la posición.

A diferencia de los LED y los motores, el servo no promedia el PWM. El propio ancho de pulso es el comando: la lógica interna del servo mide cada pulso, fija su objetivo en consecuencia y hace girar el motor hasta que la salida coincide. El ciclo de trabajo como fracción (entre el 5 % y el 10 % en todo el rango) es incidental: lo que importa es el ancho de pulso absoluto, que es lo que el software necesita controlar.

3.16.2. Cableado

Los servos de aficionado usan un conector de tres hilos:

  • Alimentación (habitualmente rojo): la alimentación propia del servo, normalmente de 4,8 V a 6 V. No alimentes el servo desde el riel de 3,3 V de la cámara – no puede suministrar la corriente de bloqueo y el riel sufrirá una caída de tensión.

  • Tierra (habitualmente negro o marrón): el camino de retorno para la alimentación del servo, conectado a la tierra de la cámara para que la señal también tenga una referencia común.

  • Señal (habitualmente blanco, amarillo o naranja): la línea PWM proveniente del GPIO de la cámara.

3.16.3. Código

duty_u16() funcionaría, pero fija el ciclo de trabajo como fracción del periodo – incómodo para una señal en la que lo que importa es el ancho de pulso absoluto y el periodo es fijo. duty_ns() fija el ancho de pulso directamente en nanosegundos:

from machine import PWM, Pin

servo = PWM(Pin("P7"), freq=50, duty_ns=1_500_000)  # centre

La portadora es de 50 Hz (periodo de 20 ms); el tiempo en alto de cada ciclo es exactamente de 1500 µs. Una pequeña función auxiliar hace explícita la correspondencia entre posición y 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

Un barrido lento a lo largo del rango:

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)

El rango de 1,0 – 2,0 ms es el estándar, pero muchos servos aceptan un rango más amplio (a menudo de 500 µs a 2500 µs) para el recorrido completo. La hoja de datos del servo indica los límites exactos del ancho de pulso; valores fuera de ese rango pueden estrellar el motor contra sus topes mecánicos.

Para un servo con un rango no estándar, lleva los límites a constantes y parametriza la correspondencia:

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)