← Índice del curso

Robótica y Automatización · Tema 9

Encoders y medición de giro

Ordenar que un motor gire no demuestra cuánto giró ni a qué velocidad. Vamos a leer sus señales, reconocer el sentido, convertir cuentas en movimiento y entender cuándo una estimación de velocidad deja de representar bien al eje.

01 · Punto de partida

Observar el giro real

En el tema anterior estimamos orientación con sensores inerciales. Ahora observaremos el giro de un eje con un encoder. Esa medida permitirá comprobar si el motor responde, estimar velocidad y preparar los lazos de control de movimiento.

Un encoder transforma movimiento en señales. Puede usar principios ópticos, magnéticos o contactos, entre otros. En este tema nos concentraremos en un encoder incremental de dos canales en cuadratura, habitual en motores para robots.

La medición pertenece al eje donde está instalado el encoder. Si está antes de un reductor, hay que convertirla. Si una rueda patina, medir su giro no confirma el avance del chasis.

02 · Qué información entrega

Incremental y absoluto

Incremental

Entrega eventos asociados al movimiento. El programa acumula cambios desde una referencia. Al encender no conoce necesariamente el ángulo físico inicial ni lo ocurrido mientras estuvo apagado.

Absoluto

Entrega un código de posición dentro del rango que cubre. Puede ser de una vuelta o de varias. La interpretación depende del formato, resolución y referencia mecánica del dispositivo.

Algunos incrementales agregan un canal de índice Z, normalmente asociado a una posición por vuelta. Puede servir para referenciar, pero no reemplaza la medición de cada transición ni garantiza una posición multivuelta al arrancar.

Reiniciar el contador a cero establece un cero de software. Para vincularlo con una posición física conocida puede hacer falta una búsqueda de origen con final de carrera o índice, usando una maniobra y límites adecuados.

03 · Secuencia de estados

Dos canales para conocer el sentido

Los canales A y B producen señales desplazadas aproximadamente un cuarto de ciclo eléctrico. Al observar ambos niveles obtenemos cuatro estados. Para este tema definimos como sentido positivo:

AB: 00 → 01 → 11 → 10 → 00
Sentido negativo: la misma secuencia recorrida al revés

En una transición ideal solo cambia un bit. En nuestra convención B sube antes que A en el recorrido positivo. El signo es una elección: intercambiar A y B lo invierte, y hay que relacionarlo con el sentido físico del robot.

Canales A y B en cuadratura Cinco muestras muestran la secuencia positiva 00, 01, 11, 10, 00. Primero sube B, después A, luego baja B y finalmente baja A. AB0001111000

Un salto 00 → 11 cambia ambos bits. Puede indicar eventos perdidos, lectura demasiado lenta o ruido. No alcanza para saber si el movimiento real hizo dos pasos positivos o dos negativos. Nuestro decodificador lo marca inválido y no inventa una dirección.

Repetir el mismo estado no agrega cuentas. Volver al estado anterior por una transición válida resta lo que se había sumado. Esta propiedad ayuda a interpretar cambios de dirección; no elimina todos los efectos de ruido o rebote.

04 · Convenciones de conteo

Pulsos, cuentas y modos x1, x2 y x4

Supongamos que un canal produce P ciclos completos por vuelta del eje del encoder. Podemos contar distintos eventos:

Definiciones adoptadas aquí; leé siempre cómo define sus cifras el fabricante.
ModoEvento contadoCuentas por vuelta del encoder
x1Solo flancos ascendentes de A, con sentido obtenido de A/BP
x2Ambos flancos de A2 · P
x4Cada transición válida de A o B4 · P

Con P = 20 ciclos por vuelta resultan 20, 40 u 80 cuentas. Las siglas PPR y CPR no son uniformes entre productos: no multipliques por cuatro sin revisar qué cifra ya incluye el conteo.

Por ejemplo, Pololu especifica en este motor con encoder de 12 CPR que esas cuentas ya incluyen ambos flancos de ambos canales en el eje del motor. La biblioteca Encoder de PJRC también declara que trabaja en x4.

Aumentar el modo de conteo reduce el incremento angular nominal, pero no garantiza exactitud mecánica ni que el procesador capture todos los eventos.

05 · Del motor a la salida

Reducción, ángulo y recorrido

Si el encoder está antes de una reducción de relación G, el motor da G vueltas por una vuelta de salida. Con C cuentas efectivas por vuelta del motor:

Csalida = Cmotor · G
vueltassalida = N / Csalida
ángulosalida = 360° · N / Csalida

Con 80 cuentas de motor y una reducción exacta de 30:1 tenemos 2400 cuentas por vuelta de salida. Si acumulamos 600 cuentas, estimamos un cuarto de vuelta: 90°. Cada cuenta representa 0,15° nominales de salida.

Si esa salida mueve una rueda de radio 3 cm, el recorrido ideal para 600 cuentas es 2 × π × 3 × 600 / 2400 ≈ 4,71 cm, bajo rodadura sin deslizamiento.

Una resolución angular nominal fina no elimina la holgura al invertir el sentido: el motor puede girar un tramo antes de que la carga responda.

06 · Diferencia de posición

Velocidad por ventana de tiempo

Tomamos la diferencia de cuentas entre dos instantes. Si C corresponde a una vuelta del eje que queremos describir:

ΔN = Nactual − Nanterior
RPM = 60 · ΔN / (C · Δtsegundos)
ωrad/s = 2 · π · ΔN / (C · Δt)

Con C = 2400 y 40 cuentas en 100 ms: RPM = 60 × 40 / (2400 × 0,1) = 10. Una diferencia de −40 produce −10 RPM según el signo elegido.

La medida es una velocidad media durante el intervalo, no una observación instantánea. Si el eje invierte el sentido dentro de la ventana, el desplazamiento neto puede ser cero aunque haya habido movimiento.

El incremento mínimo de velocidad representable por una cuenta es:

ΔRPMuna cuenta = 60 / (C · Δt)

Con C = 80 y Δt = 0,1 s, una cuenta equivale a 7,5 RPM. Un eje que gira a 5 RPM puede alternar ventanas con cero y una cuenta. Agrandar la ventana mejora ese escalón, pero aumenta el tiempo necesario para observar cambios.

07 · Otra forma de medir

Medir el período entre eventos

A baja velocidad puede convenir medir cuánto tarda en llegar el siguiente evento en vez de esperar una ventana con muchas cuentas. Si los eventos considerados están igualmente espaciados y hay C de ellos por vuelta:

RPM ≈ 60 / (C · Tevento, segundos)

Con C = 80 y un intervalo de 0,15 s resulta 5 RPM. Para el signo necesitás también información de sentido. No mezcles un período medido entre flancos ascendentes de A con una C definida contando los cuatro flancos.

Este método necesita dos eventos para empezar y deja una pregunta al detenerse: ¿cuánto tiempo conservamos la última velocidad? Definí un timeout y la antigüedad del dato. Ausencia de eventos puede significar reposo, giro demasiado lento o falla de adquisición.

La separación real entre flancos puede no ser perfectamente uniforme y el reloj tiene resolución limitada. A velocidades altas, el período entre eventos es pequeño y cobra importancia el error de tiempo. Una estrategia puede cambiar o combinar métodos, conservando sus estados de validez.

08 · Adquisición y concurrencia

Capturar sin perder transiciones

Leer A y B ocasionalmente desde un lazo lento puede saltar estados. Una opción es usar interrupciones en los cambios; otra, periféricos de conteo o decodificación que trabajen sin atender cada flanco desde el programa principal.

Para x4, la tasa aproximada de eventos es Cmotor × RPMmotor / 60. Con 80 cuentas y 6000 RPM de motor son 8000 eventos por segundo: en promedio, uno cada 125 µs. El sistema debe tolerar también variaciones y el resto de sus tareas.

  • La rutina de interrupción debe ser breve: leer, decodificar y actualizar contadores.
  • No imprimas por serie ni esperes con retardos dentro de la ISR.
  • Copiá datos compartidos de forma coherente antes de calcular fuera de la interrupción.
  • Usá niveles eléctricos compatibles y revisá el tipo de salida: push-pull, colector abierto o diferencial no se conectan igual.

La referencia de interrupciones de Arduino describe su uso y restricciones. Una variable volatile indica que puede cambiar fuera del flujo normal, pero no convierte por sí sola una lectura de varios bytes en una operación atómica.

El debounce de 20 ms de un pulsador no se traslada sin más a un encoder de motor: eliminaría pulsos reales. Los encoders mecánicos de mando pueden necesitar otra estrategia; diferenciá ese caso de señales ópticas o magnéticas de alta frecuencia.

09 · Código para la placa

Decodificación x4 en un UNO R3

Este ejemplo es para Arduino UNO R3 con un encoder incremental de señales A/B eléctricamente compatibles con sus entradas. Con la alimentación desconectada, conectá A a D2, B a D3 y una referencia GND común. Alimentá el encoder según su ficha; no supongas que cualquier salida admite conexión directa.

Usamos C = 80 cuentas por vuelta del eje observado como ejemplo hipotético. Sustituí ese valor por el real. No conectamos ni activamos un motor: empezá girando el eje lentamente a mano si su mecánica lo permite.

#include <util/atomic.h>  // Específico de AVR, como el UNO R3

const byte PIN_A = 2;
const byte PIN_B = 3;
const float CUENTAS_POR_VUELTA = 80.0f;
const unsigned long VENTANA_US = 100000;
volatile int32_t cuentasVentana = 0;
volatile uint16_t erroresVentana = 0;
volatile byte anteriorAB = 0;
unsigned long inicioVentana = 0;

// Índice: (estado anterior << 2) | estado actual.
const int8_t PASO[16] = {
   0,  1, -1,  0,
  -1,  0,  0,  1,
   1,  0,  0, -1,
   0, -1,  1,  0
};

byte leerAB() {
  return (digitalRead(PIN_A) << 1) | digitalRead(PIN_B);
}

void cambioEncoder() {
  const byte actual = leerAB();
  const byte previo = anteriorAB;
  if ((previo ^ actual) == 3) erroresVentana++;
  else cuentasVentana += PASO[(previo << 2) | actual];
  anteriorAB = actual;
}

void setup() {
  // Adecuado aquí para señales que admiten pull-up a la lógica del UNO.
  pinMode(PIN_A, INPUT_PULLUP);
  pinMode(PIN_B, INPUT_PULLUP);
  Serial.begin(115200);
  anteriorAB = leerAB();
  inicioVentana = micros();
  attachInterrupt(digitalPinToInterrupt(PIN_A), cambioEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN_B), cambioEncoder, CHANGE);
}

void loop() {
  if (micros() - inicioVentana < VENTANA_US) return;
  int32_t delta;
  uint16_t errores;
  unsigned long instante;
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    instante = micros();
    delta = cuentasVentana;
    errores = erroresVentana;
    cuentasVentana = 0;
    erroresVentana = 0;
  }
  const float dt = (instante - inicioVentana) / 1000000.0f;
  inicioVentana = instante;
  if (errores != 0) {
    Serial.println("Transiciones invalidas: revisar adquisicion");
    return;
  }
  const float rpm = 60.0f * delta / (CUENTAS_POR_VUELTA * dt);
  Serial.print("Delta="); Serial.print(delta);
  Serial.print("; dt="); Serial.print(dt, 4);
  Serial.print(" s; RPM="); Serial.println(rpm, 2);
}

La copia y el reinicio de contadores ocurren juntos en una sección atómica breve. La división y el registro se hacen después. Se usa el intervalo medido, no se supone que la ventana haya durado exactamente 100 ms.

El código rechaza la ventana si detecta un salto de dos bits, pero cero errores detectados no garantiza cero eventos perdidos. Perder un ciclo completo puede dejar el mismo estado. Las dos llamadas a digitalRead() tampoco son una captura simultánea: este ejemplo sirve para tasas moderadas verificadas, no como garantía a cualquier velocidad.

Los contadores por ventana deben mantenerse dentro de su rango. Para frecuencias elevadas, considerá lectura conjunta del puerto, bibliotecas optimizadas o periféricos especializados. El bloque AVR no es portable directamente a ESP32 u otras arquitecturas.

10 · Exploración interactiva

Laboratorios: estados y velocidad cuantizada

1. Decodificar una transición por vez

El encoder simulado tiene 20 ciclos por vuelta de motor. Los botones recorren estados eléctricos, no vueltas ni posiciones de un pulsador mecánico.

Estado AB00
Cuentas acumuladas0
Cuentas / vuelta de salida80
Ángulo estimado de salida0°
Saltos inválidos observados0

Recorré los estados para observar el conteo.

Últimas 12 transiciones observadas.
AnteriorActualCambio contadoInterpretación
Sin transiciones.

Cambiar modo o reducción reinicia el laboratorio para no mezclar escalas. Ante un salto inválido se conserva el conteo y se toma el nuevo estado como referencia para continuar; el movimiento perdido queda sin reconstruir.

Recorrido guiado de cuadratura

  1. En x4 y 1:1, un ciclo positivo suma 4 cuentas y 18°. Son cuatro estados de un ciclo eléctrico, no una vuelta completa.
  2. En x1, el mismo ciclo suma 1 cuenta; en x2 suma 2. El ángulo final sigue siendo 18° porque cambia también la escala.
  3. Con x4 y reducción 30:1, un ciclo suma 4 cuentas sobre 2400 por vuelta de salida: 0,6°.
  4. Avanzá un estado y retrocedé uno: el conteo neto vuelve al anterior cuando ambos eventos están incluidos en el modo elegido.
  5. Reiniciá y saltá dos estados: aparece un error y no se inventan dos cuentas. El ángulo acumulado ya no representa todo el movimiento desconocido.

2. Una velocidad constante puede dar lecturas que saltan

Se simulan 12 ventanas consecutivas con giro positivo constante, sin ruido ni pérdida de flancos. El conteo empieza alineado con una fase cero y solo registra eventos enteros.

Cuentas en la primera ventana—
RPM de la primera ventana—
Escalón de una cuenta—
RPM sobre las 12 ventanas juntas—

Ajustá resolución y ventana.

Cuentas y velocidad media estimada por ventana.
VentanaFinΔNRPM estimadas

Con 5 RPM, 80 cuentas y ventanas de 100 ms, una cuenta equivale a 7,5 RPM. Aparecen ventanas de 0 y 7,5 RPM aunque el eje no cambie su velocidad. En 12 ventanas se acumulan 8 cuentas y la estimación conjunta resulta 5 RPM.

Subí a 2400 cuentas o alargá la ventana a 500 ms para reducir el escalón. Después bajá a 10 ms y observá por qué actualizar más rápido puede empeorar la cuantización de cada lectura. La estimación conjunta necesita esperar las 12 ventanas y tampoco es exacta en todas las configuraciones.

11 · Código ejecutable

Cuantización en Python

Reproduzcamos la segunda simulación. El contador acumulado es la parte entera de los eventos ideales; después calculamos diferencias entre ventanas. No redondeamos cada ventana de forma independiente, porque eso perdería la fase acumulada.

Probalo en el navegador. El código es editable. Python se descarga con Pyodide la primera vez y requiere conexión. No mide ni controla hardware.

from math import floor

rpm_real = 5
cuentas_por_vuelta = 80
ventana_s = 0.1
numero_ventanas = 12
anterior = 0
total = 0

for k in range(1, numero_ventanas + 1):
    eventos_ideales = rpm_real * cuentas_por_vuelta * ventana_s * k / 60
    # Tolerancia solo para errores numéricos cerca de un entero exacto.
    acumulado = floor(eventos_ideales + 1e-9)
    delta = acumulado - anterior
    anterior = acumulado
    total += delta
    estimada = 60 * delta / (cuentas_por_vuelta * ventana_s)
    print(f"Ventana {k:2}: {delta} cuentas; {estimada:.2f} RPM")

tiempo_total = numero_ventanas * ventana_s
rpm_conjunta = 60 * total / (cuentas_por_vuelta * tiempo_total)
escalon = 60 / (cuentas_por_vuelta * ventana_s)
print(f"Escalón: {escalon:.2f} RPM")
print(f"Estimación conjunta: {rpm_conjunta:.2f} RPM")

Con los valores iniciales, el patrón de cuentas es 0, 1, 1 y se repite cuatro veces. El escalón es 7,50 RPM y la estimación conjunta 5,00 RPM. Cambiá las cuentas por vuelta a 2400: se obtienen 20 cuentas en cada ventana y 5 RPM en este caso ideal.

El modelo supone velocidad constante, fase inicial cero y ventanas exactas. En el robot también aparecen variación real de velocidad, errores de tiempo y adquisición. Un filtro puede suavizar el resultado, pero no crea resolución instantánea ni elimina el retardo.

12 · Método y diagnóstico

Pruebas y errores frecuentes

  1. Identificá eje, salida eléctrica, convención de cuentas y relación real de transmisión.
  2. Comprobá A/B a baja velocidad y verificá el signo elegido.
  3. Girando una vuelta física conocida, compará el conteo con la escala prevista.
  4. Probá ambos sentidos e inversiones; registrá saltos inválidos y holgura.
  5. Aumentá la velocidad de forma controlada y verificá captura bajo la carga real del programa.
  6. Probá parada y desconexión: definí antigüedad y estados de confianza, sin confundir ausencia de pulsos con una prueba de inmovilidad.
«Mi encoder dice 12 CPR: entonces en x4 son 48»

Solo si la cifra representa ciclos por canal y todavía no incluye flancos. Algunos fabricantes ya indican cuentas x4. Revisá la definición y confirmá con una vuelta física.

«Una lectura de cero RPM demuestra que el motor está detenido»

Puede ser una ventana sin eventos por baja velocidad, un cable desconectado o movimiento de ida y vuelta con desplazamiento neto cero. Interpretá la observación con su tiempo y contexto.

«volatile resuelve cualquier problema de concurrencia»

No evita que una ISR modifique un dato multibyte durante su copia ni hace atómica una secuencia de leer y borrar. Usá mecanismos adecuados a la arquitectura.

«Si no hubo saltos inválidos, no perdí cuentas»

Perder varias transiciones puede dejar un estado aparentemente válido o igual al anterior. El diagnóstico detecta ciertos errores, no todos.

«Puedo aplicar el debounce del pulsador al motor»

Un intervalo largo de rechazo puede borrar muchísimos eventos válidos. El tratamiento debe respetar la frecuencia y el tipo de encoder.

«Más cuentas garantizan más exactitud de posición del robot»

Mejoran el paso nominal de medición, pero no eliminan holgura, flexión, radio incorrecto o patinamiento. Medir giro sigue siendo distinto de medir la trayectoria real.

13 · Práctica guiada

Predecí antes de comprobar

1. Modos de conteo

Un canal produce 100 ciclos por vuelta del encoder. ¿Cuántas cuentas corresponden a x1, x2 y x4 según las definiciones del tema?

Ver solución

100, 200 y 400 cuentas por vuelta, respectivamente. La respuesta supone que 100 describe ciclos y no un conteo que ya incluya varios flancos.

2. Secuencia y dirección

Desde 00 se observa 10 → 11 → 01 → 00. ¿Cuánto suma en x4? ¿Qué ocurre ante un salto directo 00 → 11?

Ver solución

La secuencia es negativa: suma −4 cuentas. El salto directo cambia ambos bits y se marca inválido sin asignar una dirección. El movimiento omitido no puede reconstruirse con esos dos estados.

3. Reducción

Un encoder entrega 12 cuentas efectivas por vuelta de motor, ya en el modo utilizado, y la reducción es exactamente 50:1. ¿Cuántas cuentas por vuelta de salida hay? ¿Qué representan 150 cuentas?

Ver solución

12 × 50 = 600 cuentas/vuelta. 150 cuentas equivalen a 0,25 vueltas o 90° de salida. No se multiplica otra vez por cuatro.

4. Velocidad con signo

Con 600 cuentas por vuelta de salida, se observan −30 cuentas en 50 ms. ¿Qué RPM estimás?

Ver solución

60 × (−30) / (600 × 0,05) = −60 RPM. Es la velocidad media neta de salida durante esa ventana.

5. Resolución de velocidad

Con 80 cuentas por vuelta, compará el escalón de una cuenta para ventanas de 10 y 100 ms.

Ver solución

Con 10 ms: 60 / (80 × 0,01) = 75 RPM. Con 100 ms: 7,5 RPM. Una ventana más larga mejora ese escalón a cambio de menor respuesta temporal.

6. Carga de adquisición

Un encoder produce 400 cuentas x4 por vuelta de motor y el motor gira a 3000 RPM. ¿Qué tasa de eventos debe capturarse?

Ver solución

400 × 3000 / 60 = 20000 eventos/s, un promedio de 50 µs entre eventos. Deben evaluarse ISR, otros bloqueos y carga del programa; la cifra no garantiza que una plataforma concreta alcance.

14 · Síntesis

Ideas para recordar

  • Un encoder incremental observa cambios; necesita una referencia para asociar su cero con una posición física.
  • La cuadratura usa el orden de los estados A/B para conocer el sentido.
  • x1, x2 y x4 cuentan eventos distintos; verificá qué incluye la cifra de la ficha.
  • La reducción transforma cuentas de motor a cuentas del eje útil.
  • Velocidad por ventana significa desplazamiento neto dividido por tiempo medido.
  • Ventanas cortas y pocas cuentas producen grandes escalones de velocidad.
  • La captura debe respetar frecuencia, niveles eléctricos y acceso coherente a datos compartidos.
  • Los errores detectados, la antigüedad y los supuestos mecánicos forman parte de la confianza en la medida.

¿Listo para el tema 10?

Antes de avanzar, dibujá un ciclo A/B, explicá cómo reconocer el sentido y calculá cuentas por vuelta y escalón de RPM para un encoder con reducción. Indicá qué pruebas harías antes de confiar esa medida a un controlador de velocidad.

En el próximo tema comenzamos el bloque de actuadores y motores: cómo elegirlos según torque, velocidad, potencia y relación de engranajes.