← Índice del curso

Sensores y Actuadores · Tema 26

Taller: velocidad y posición con encoder

Con el encoder del tema anterior armamos un módulo reutilizable: decodifica por interrupciones, lleva la posición en cuentas, vueltas y milímetros, estima la RPM con una ventana y un filtro, y detecta cuándo el motor se bloquea. Es el sensor que necesita el control de motores del tema 31.

01 · Punto de partida

Qué vamos a construir

El taller usa un montaje muy común: un motor DC con caja reductora y encoder magnético en el eje del motor (del tipo JGA25 o N20), con 11 pulsos por vuelta del canal A y una reducción 30:1, moviendo una rueda de 65 mm. Todavía no manejamos el motor con un driver (eso viene en los temas 27 y 30): lo giramos a mano o lo alimentamos con una fuente a través de un driver listo. Lo que construimos es el lado sensor:

  • Posición en cuentas, vueltas de la rueda y milímetros recorridos.
  • Velocidad en RPM de la rueda y en mm/s, con una lectura estable.
  • Sentido de giro.
  • Aviso de bloqueo: motor ordenado a girar que no avanza.
  • Un contador de errores de cuadratura para saber si se perdieron flancos.

Las constantes del montaje son: CPR_motor = 11 × 4 = 44 cuentas por vuelta del motor; con la reducción 30:1, CPR_rueda = 44 × 30 = 1320; y como la circunferencia es π × 65 ≈ 204,2 mm, cada cuenta equivale a 0,1547 mm.

02 · Paso 1

Arquitectura y tiempos

Como en el taller anterior (tema 21), el diseño separa lo que ocurre en cada escala de tiempo:

Tareas del módulo y su ritmo.
TareaDóndeRitmo
Decodificar A y B, actualizar la posiciónInterrupción (ISR)En cada flanco (hasta miles por segundo)
Leer la posición, calcular velocidad y filtrarLazo principal con millis()Cada 20 ms (50 Hz)
Detectar bloqueo y reportarLazo principalCada 20 ms; el reporte, cada 100 ms

La ISR hace lo mínimo (consultar la tabla, sumar, guardar el estado) y nunca imprime ni espera. El lazo principal copia la posición de forma atómica (tema 21 de Microcontroladores) y hace el resto. La frecuencia de 50 Hz coincide con la ventana de velocidad y con el lazo de control del tema 31.

La carga: a 300 RPM en la rueda llegan 300/60 × 1320 = 6600 cuentas/s. Con ~4 µs por interrupción, el procesador dedica un 2,6 % del tiempo a decodificar: sobra margen en un AVR. Si usaras un encoder de 1024 PPR en el eje del motor sin reducción, el número sería cien veces mayor y habría que repensarlo (tema 25).

03 · Paso 2

Cableado y montaje

El motor con encoder trae seis cables. Los dos de los extremos son el motor (M1 y M2) y van al driver; los otros cuatro son el encoder: alimentación (3,3 a 5 V), masa y los canales A y B.

Conexiones del módulo (Arduino Uno como ejemplo).
CableVa aNota
Encoder VCC5 V (o 3,3 V si la placa es de 3,3 V)Debe coincidir con el nivel lógico de la placa
Encoder GNDGND de la placa y del driverMasa común con la fuente del motor
Canal APin 2 (INT0)El Uno solo tiene interrupciones externas en 2 y 3
Canal BPin 3 (INT1)En un ESP32 sirve cualquier pin
Motor M1 y M2Salidas del driverNunca desde un pin del microcontrolador (tema 27)

Dos cuidados del montaje: el motor es una fuente de ruido eléctrico, por lo que conviene un capacitor cerámico de 100 nF soldado entre sus terminales y los cables del encoder cortos o trenzados, lejos de los del motor; y si el encoder es de colector abierto se necesitan pull-ups (basta INPUT_PULLUP). Los cables del encoder intercambiados invierten el sentido, algo que se corrige en el software.

04 · Paso 3

Decodificar por interrupciones

La ISR usa la tabla de transiciones del tema 25 y suma un contador de errores para los saltos de dos estados:

const uint8_t PIN_A = 2, PIN_B = 3;
const int8_t TABLA[16] = {  0, +1, -1,  0,
                           -1,  0,  0, +1,
                           +1,  0,  0, -1,
                            0, -1, +1,  0 };

volatile int32_t posicion = 0;                       // cuentas con signo
volatile uint8_t anterior = 0;
volatile uint16_t errores = 0;                       // saltos de dos estados (flancos perdidos)

void isrEncoder() {
    uint8_t actual = (digitalRead(PIN_A) << 1) | digitalRead(PIN_B);
    int8_t paso = TABLA[(anterior << 2) | actual];
    if (paso == 0 && actual != anterior) errores++;
    posicion += paso;
    anterior = actual;
}

void leerEncoder(int32_t &pos, uint16_t &err) {         // copia consistente
    noInterrupts();
    pos = posicion;
    err = errores;
    interrupts();
}

Dos decisiones: la posición es int32_t (la razón se ve en el paso 4) y el contador de errores es un indicador de salud del sistema: si crece, se están perdiendo flancos por ruido, por una frecuencia excesiva o por interrupciones deshabilitadas demasiado tiempo.

05 · Paso 4

Posición, vueltas y distancia

Con la posición en cuentas se obtienen las magnitudes físicas con una multiplicación:

vueltas = cuentas / CPR   ángulo = (cuentas mod CPR) × 360 / CPR   distancia [mm] = cuentas × π × D / CPR
const float CPR = 1320.0;                             // 11 PPR x4, reducción 30:1
const float DIAMETRO_MM = 65.0;
const float MM_POR_CUENTA = 3.14159265 * DIAMETRO_MM / CPR;   // 0,1547 mm

float vueltas(int32_t cuentas)      { return cuentas / CPR; }
float distanciaMm(int32_t cuentas)  { return cuentas * MM_POR_CUENTA; }
float anguloRueda(int32_t cuentas) {
    int32_t resto = cuentas % (int32_t)CPR;           // en C el resto conserva el signo
    if (resto < 0) resto += (int32_t)CPR;
    return resto * 360.0 / CPR;
}

Atención al tamaño del contador. Un int16_t llega a 32 767 cuentas: con 1320 por vuelta, eso son solo 24,8 vueltas, unos 5 metros de recorrido. Más allá, el número da la vuelta y pasa a negativo: a 25 vueltas (33 000 cuentas) el contador marca −32 536 y el robot «cree» que retrocedió 5 metros. Con int32_t el límite está a más de 2000 millones de cuentas. En el AVR, el acceso a un entero de 32 bits no es atómico (necesita varias instrucciones), por eso la copia con interrupciones deshabilitadas del paso 3.

También conviene definir el cero: al arrancar, la posición es 0 y representa «donde estaba». Si hace falta una referencia física, se hace un homing contra un tope mecánico, un final de carrera (tema 6) o el índice Z, y se reinicia el contador con interrupciones deshabilitadas. Para una distancia relativa basta restar la posición inicial.

06 · Paso 5

Velocidad con ventana y filtro

Cada 20 ms se mide la diferencia de cuentas y se convierte a RPM de la rueda:

RPM = (Δcuentas / CPR) / T × 60   escalón = 60 / (CPR × T) = 60 / (1320 × 0,02) ≈ 2,27 RPM

El escalón es la cuantización: una cuenta de más o de menos mueve la lectura 2,27 RPM, que a 150 RPM es el 1,5 %, pero a 10 RPM sería el 23 %. La lectura bruta es entonces «granulada», y un controlador que derive de ella amplificaría esa granularidad. Se suaviza con un filtro exponencial (tema 5):

filtrada ← filtrada + α × (bruta − filtrada)

Con α = 0,3 y un muestreo cada 20 ms, la constante de tiempo del filtro es T × (1 − α) / α ≈ 47 ms: la lectura llega al 63 % de un cambio en aproximadamente 3 muestras. Menor α suaviza más pero retrasa más: un retardo grande en la medición reduce la estabilidad de un lazo de control. El compromiso es el mismo que con cualquier filtro, solo que ahora el retardo entra en el lazo.

A velocidades muy bajas (la rueda casi parada), la ventana de 20 ms tiene 0 o 1 cuenta. Ahí conviene medir el período entre cuentas (tema 25) o alargar la ventana. Para este taller alcanza con una ventana fija y con declarar «parado» una velocidad menor al escalón.

const uint16_t T_LAZO = 20;                           // ms
const float ALFA = 0.3;

int32_t posicionPrevia = 0;
float rpmFiltrada = 0;

void actualizarVelocidad(int32_t pos, uint32_t dtMs) {
    float bruta = ((pos - posicionPrevia) / CPR) / (dtMs / 1000.0) * 60.0;
    rpmFiltrada += ALFA * (bruta - rpmFiltrada);
    posicionPrevia = pos;
}

07 · Paso 6

Sentido y detección de bloqueo

El sentido sale del signo de la diferencia de cuentas: positivo avanza, negativo retrocede. Si el montaje queda al revés, se invierte con una constante (#define INVERTIR -1) en lugar de cambiar cables.

El bloqueo es el caso en que el motor recibe una orden de girar y la rueda no se mueve: un obstáculo, una pared, un engranaje trabado. El motor consume la corriente máxima y puede recalentarse o dañar el driver. El encoder lo delata sin sensores extra: comando alto y cero cuentas durante un tiempo. Una sola ventana sin cuentas no alcanza (el arranque tiene inercia), por eso se exige que el estado dure, por ejemplo, 200 ms:

const float COMANDO_MIN = 0.20;                       // por debajo, es normal estar parado
const uint16_t T_BLOQUEO = 200;                       // ms sin moverse con comando alto

uint16_t msSinMovimiento = 0;
bool bloqueado = false;

void detectarBloqueo(float comando, int32_t delta) {
    if (comando > COMANDO_MIN && delta == 0) msSinMovimiento += T_LAZO;
    else msSinMovimiento = 0;
    bloqueado = (msSinMovimiento >= T_BLOQUEO);       // al bloquearse, el programa debería cortar el motor
}

El umbral y el tiempo dependen del mecanismo: demasiado corto produce falsas alarmas en cada arranque, demasiado largo deja al motor castigado. Y la acción correcta ante un bloqueo es quitar la potencia, no insistir (tema 44).

08 · Paso 7

El programa completo

// ... constantes, TABLA, ISR y funciones de los pasos 3 a 6 ...

float comando = 0.5;                                  // 0..1, lo fija el control del tema 31
uint32_t tLazo = 0, tReporte = 0;

void setup() {
    Serial.begin(115200);
    pinMode(PIN_A, INPUT_PULLUP);
    pinMode(PIN_B, INPUT_PULLUP);
    anterior = (digitalRead(PIN_A) << 1) | digitalRead(PIN_B);   // estado inicial real
    attachInterrupt(digitalPinToInterrupt(PIN_A), isrEncoder, CHANGE);
    attachInterrupt(digitalPinToInterrupt(PIN_B), isrEncoder, CHANGE);
}

void loop() {
    uint32_t ahora = millis();
    if (ahora - tLazo >= T_LAZO) {
        uint32_t dt = ahora - tLazo;
        tLazo = ahora;

        int32_t pos; uint16_t err;
        leerEncoder(pos, err);

        int32_t delta = pos - posicionPrevia;         // antes de que actualizarVelocidad lo reescriba
        actualizarVelocidad(pos, dt);
        detectarBloqueo(comando, delta);

        if (ahora - tReporte >= 100) {
            tReporte = ahora;
            Serial.print(distanciaMm(pos), 1);   Serial.print(" mm  ");
            Serial.print(rpmFiltrada, 1);        Serial.print(" RPM  ");
            Serial.print(err);                   Serial.print(" err  ");
            Serial.println(bloqueado ? "BLOQUEO" : "ok");
        }
    }
}

Dos detalles: se inicializa anterior con el estado real de los pines al arrancar (si no, el primer flanco puede contarse mal), y delta se calcula antes de actualizar la posición previa. La estructura sin bloqueos es la del tema 41.

09 · Paso 8

Verificar

  1. Vuelta exacta. Marcá la rueda y girá una vuelta completa a mano: la posición debe pasar de 0 a 1320 cuentas (±1) y la distancia a 204,2 mm.
  2. Ida y vuelta. Girá 5 vueltas hacia adelante y 5 hacia atrás: debe volver a cero. Si queda un residuo, se perdieron flancos.
  3. Sentido. Avanzar debe dar cuentas positivas; si da negativas, invertí la constante o los canales.
  4. Distancia. Hacé rodar la rueda sobre una cinta métrica 1 metro: la lectura debe dar ~1000 mm; la diferencia se debe al diámetro real (medilo con calibre) o al deslizamiento.
  5. Velocidad. Con el motor a un comando fijo, compará con un tacómetro (una aplicación de celular con la cámara, o contá las vueltas en 10 s). Esperá coincidencia dentro del 2 %.
  6. Pérdida de flancos. Con el motor a máxima velocidad durante un minuto, el contador de errores debe seguir en 0.
  7. Bloqueo. Frená la rueda con la mano con el comando en 50 %: en ~200 ms debe aparecer «BLOQUEO».

10 · Paso 9

Fallas y robustez

Fallas típicas de un encoder y cómo se manifiestan.
FallaSíntomaDefensa
Un canal desconectadoCuentas erráticas, errores en aumento, sentido inciertoSi el contador de errores crece rápido, declarar el encoder en falla y detener el motor
Ruido del motor en las líneasCuentas y errores aun con el eje quietoCapacitor en el motor, cables trenzados, filtro RC en las entradas
Pérdida de flancos por velocidadDeriva acumulada, erroresModo x1/x2 o contador por hardware
Desbordamiento del contadorPosición que salta de signoUsar int32_t y restar la posición inicial
Motor ordenado y sin cuentasBloqueo, encoder desconectado o motor sin alimentaciónDetección de bloqueo; cortar el comando
Deslizamiento de la ruedaLa distancia contada supera la realEs un límite del sensor: el encoder mide el eje, no el piso

Un encoder desconectado y un motor bloqueado producen el mismo síntoma («comando alto y cero cuentas»), y es un error pretender distinguirlos solo con este sensor. El módulo informa «sin movimiento» y la capa superior decide la acción (típicamente, cortar la potencia y avisar).

11 · Analogía con programación

El motor simulado, el filtro y el desbordamiento

Probemos las decisiones del taller sin hardware. Un motor de primer orden (constante de tiempo 0,3 s, 300 RPM a pleno) recibe un 50 % de comando; el programa mide cada 20 ms, filtra con α = 0,3 y detecta un bloqueo que se produce en t = 3 s. Después, el desbordamiento de un contador de 16 bits.

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

import math

CPR = 11 * 4 * 30                   # cuentas por vuelta de la rueda: 11 PPR x4, reducción 30:1
DIAMETRO = 65.0                     # mm
MM_CUENTA = math.pi * DIAMETRO / CPR
TAU = 0.3                           # s, constante de tiempo del motor
RPM_MAX = 300.0
DT = 0.001                          # s, paso de simulación
VENTANA = 20                        # pasos de 1 ms entre mediciones de velocidad
ALFA = 0.3


def simular(pwm, total, bloqueo=None):
    omega = fase = 0.0
    previo = sin_mov = 0
    ema = None
    detectado = None
    filas = {}
    for k in range(1, round(total / DT) + 1):
        t = k * DT
        if bloqueo is not None and t >= bloqueo - 1e-9:
            omega = 0.0                                          # el eje se traba
        else:
            omega += (RPM_MAX * pwm - omega) * DT / TAU
        fase += omega / 60 * CPR * DT
        cuentas = math.floor(fase)
        if k % VENTANA == 0:
            delta = cuentas - previo
            previo = cuentas
            bruto = delta / CPR / (VENTANA * DT) * 60
            ema = bruto if ema is None else ema + ALFA * (bruto - ema)
            sin_mov = sin_mov + 1 if (pwm > 0.2 and delta == 0) else 0
            if sin_mov * VENTANA >= 200 and detectado is None:
                detectado = t
            filas[round(t, 3)] = (omega, bruto, ema, cuentas)
    return filas, detectado


print(f"CPR de la rueda = {CPR}; {MM_CUENTA:.4f} mm por cuenta; escalón de velocidad = {60 / (CPR * VENTANA * DT):.2f} RPM")
filas, detectado = simular(0.5, 4.0, bloqueo=3.0)
print("  t (s)  real   bruta  filtrada  cuentas")
for t in (0.1, 0.5, 1.0, 2.0, 3.0):
    o, b, e, c = filas[t]
    print(f"  {t:4.1f}  {o:6.1f}  {b:6.1f}  {e:6.1f}  {c:7d}")
print(f"Bloqueo detectado en t = {detectado:.2f} s")


def como_int16(n):
    return (n + 32768) % 65536 - 32768


print()
print("vueltas  cuentas  int16   mm reales  mm int16")
for vueltas in (10, 24, 25, 40):
    c = round(vueltas * CPR)
    r = como_int16(c)
    print(f"{vueltas:7d}  {c:7d}  {r:6d}  {c * MM_CUENTA:9.0f}  {r * MM_CUENTA:8.0f}")

Resultado: la rueda tiene 1320 cuentas por vuelta, 0,1547 mm por cuenta y un escalón de velocidad de 2,27 RPM con la ventana de 20 ms. Con el 50 % de comando la velocidad real tiende a 150 RPM: a 0,5 s vale 121,7 (la bruta marca 122,7 y la filtrada 115,6, que llega tarde), a 1 s 144,7 y a 2 s 149,8 (bruta 150,0 y filtrada 149,8). A 3 s el eje se traba: el bloqueo se detecta en t = 3,20 s, 200 ms después. En el contador de 16 bits, 24 vueltas (31 680 cuentas, 4901 mm) todavía caben; a 25 vueltas el valor registrado es −32 536 (−5033 mm en lugar de +5105) y a 40 vueltas, −12 736 (−1970 mm en lugar de 8168).

12 · Exploración interactiva

El banco de pruebas del módulo de encoder

El primer instrumento simula el motor en tiempo real: elegí el comando, la ventana de medición y el filtro, y mirá la velocidad real, la bruta y la filtrada; podés trabar el eje para ver la detección de bloqueo. El segundo muestra el recorrido de la rueda y cómo se desborda un contador de 16 bits.

Instrumento 1: velocidad en tiempo real

RPM real · bruta · filtrada—
Escalón de la medición—
Posición—
Estado
—

Instrumento 2: odómetro y desbordamiento

Cuentas reales—
Cuentas registradas—
Distancia real · registrada—
Error—

Recorrido guiado

  1. Con comando 50 %, ventana de 20 ms y α = 0,3: la velocidad real sube a 150 RPM en ~1 s. La bruta (naranja) sube con pequeños saltos de 2,27 RPM; la filtrada (verde) es más lisa pero va atrasada.
  2. Bajá α a 0,05: la filtrada casi no tiene saltos pero tarda varios segundos en llegar. Subila a 1,00: sigue a la bruta sin retraso y sin suavizar. Es el compromiso entre ruido y velocidad de respuesta.
  3. Pasá la ventana a 100 ms: el escalón baja a 0,45 RPM (bruta más fina), pero cada medición llega cada 100 ms y el control tendría retardo.
  4. Bajá el comando a 25 %: la velocidad es de ~75 RPM, el escalón de 2,27 RPM pesa un 3 %. A 5 % sería la lectura de un escalón o dos.
  5. Marcá «Trabar el eje» con comando 50 %: la velocidad cae a cero y a los 200 ms aparece BLOQUEO. Con comando 10 % (por debajo del 20 %), el eje parado no se declara bloqueado.
  6. En el odómetro, con int16_t y 10 vueltas todo es exacto (2042 mm). Subí a 25 vueltas: el contador marca −32 536 y la distancia −5033 mm. Con uint16_t da la vuelta recién a las 49,6 vueltas; con int32_t no hay problema.
Desafío: ajustar la ventana al lazo de control

Se quiere controlar la velocidad con un lazo de 10 ms y el mismo encoder (1320 CPR). ¿Qué escalón tiene la medición con una ventana de 10 ms y qué consecuencia tiene? 60 / (1320 × 0,01) = 4,55 RPM por cuenta: a 100 RPM, un 4,5 % de granularidad, demasiado para un lazo con acción derivativa. Opciones: medir cada 20 ms y actuar cada 10, filtrar, usar el método de período, o un encoder de mayor resolución. Medir más seguido empeora la resolución: la resolución de velocidad y la rapidez de la medición se compensan.

13 · Método de trabajo

Lo aprendido, para cualquier encoder

  1. Calculá las constantes: CPR final (con reducción), mm o grados por cuenta, escalón de velocidad y frecuencia máxima de cuentas.
  2. Mantené la ISR mínima y hacé el resto en el lazo, con copia atómica de la posición.
  3. Usá un contador de 32 bits y trabajá con diferencias, no con valores absolutos.
  4. Elegí la ventana según la velocidad mínima que querés medir y el retardo que tolera el control.
  5. Filtrá lo justo: el retardo del filtro forma parte del lazo.
  6. Vigilá la salud: contador de errores y detección de bloqueo.
  7. Verificá con referencias físicas: una vuelta marcada, una cinta métrica, un tacómetro.

Si la posición «se va» con el tiempo, buscá flancos perdidos; si la velocidad oscila a baja marcha, es la cuantización; si salta de signo a las 25 vueltas, es el tamaño del contador.

14 · Comprensión

Confusiones frecuentes

«Basta con leer el contador cada tanto y restar»

Con un entero de 32 bits en un AVR, leerlo sin proteger puede dar un valor corrupto si una interrupción lo actualiza a mitad de la lectura. Hay que copiarlo con interrupciones deshabilitadas.

«Un filtro más fuerte siempre mejora la velocidad»

Suaviza, pero retrasa. En un lazo de control, el retardo puede volver inestable el sistema. Se elige el mínimo que da una lectura utilizable.

«Con 1320 cuentas por vuelta, la velocidad es muy precisa»

La precisión depende de la ventana: con 20 ms el escalón es de 2,27 RPM; la resolución de posición y la de velocidad son cosas distintas.

«La distancia del encoder es la que recorrió el robot»

Es la que giró la rueda. Si desliza, patina o el diámetro real difiere del supuesto, hay error. Se verifica con una cinta métrica.

«Si no hay cuentas, el motor está roto»

También puede ser un bloqueo, un encoder desconectado o falta de alimentación. El encoder solo informa que no hay movimiento.

«Los errores de cuadratura se pueden ignorar»

Son la única señal de que se perdieron flancos. Si crecen, la posición ya no es confiable.

15 · Práctica guiada

Predecí antes de comprobar

1. Constantes

Un motor con encoder de 12 PPR en el eje, reducción 20:1 y rueda de 80 mm. Calculá CPR de la rueda (x4) y los mm por cuenta.

Ver solución

CPR = 12 × 4 × 20 = 960. Circunferencia = π × 80 = 251,3 mm; por cuenta: 251,3 / 960 = 0,262 mm.

2. Escalón de velocidad

Con ese CPR y una ventana de 50 ms, ¿cuánto vale una cuenta en RPM? ¿Y cuántas cuentas hay por ventana a 120 RPM?

Ver solución

Escalón = 60 / (960 × 0,05) = 1,25 RPM. A 120 RPM (2 vueltas/s): 1920 cuentas/s, o 96 por ventana de 50 ms.

3. Desbordamiento

Con un contador int16_t y 960 cuentas por vuelta, ¿a cuántas vueltas se desborda? ¿Y con uint16_t?

Ver solución

32 767 / 960 = 34,1 vueltas con int16_t; 65 535 / 960 = 68,3 vueltas con uint16_t. Con int32_t, más de 2 millones de vueltas.

4. Constante del filtro

Con T = 20 ms y α = 0,3, ¿cuánto vale la constante de tiempo aproximada del filtro y qué pasa con α = 0,1?

Ver solución

τ ≈ T (1 − α) / α = 20 × 0,7 / 0,3 ≈ 47 ms. Con α = 0,1: 20 × 0,9 / 0,1 = 180 ms, casi cuatro veces más lento.

5. Detección de bloqueo

El comando está en 60 % y las cuentas de los últimos 8 períodos de 20 ms fueron todas cero. Con un tiempo de bloqueo de 200 ms, ¿hay bloqueo? ¿Y si el comando fuera 15 %?

Ver solución

8 × 20 = 160 ms < 200 ms: todavía no; faltan dos períodos más. Con comando 15 % (menor que el 20 % de mínimo) nunca se declara bloqueo: es normal estar parado.

6. Diagnóstico

Después de 10 vueltas hacia adelante y 10 hacia atrás, el contador marca 37 en lugar de 0, y el contador de errores es 12. ¿Qué sospechás?

Ver solución

Se perdieron flancos (los 12 errores lo confirman): ruido en las líneas del encoder, interrupciones deshabilitadas demasiado tiempo, o frecuencia mayor de la que el procesador alcanza. Se revisan el cableado y los capacitores del motor, y se mide la frecuencia máxima.

16 · Síntesis

Ideas para recordar

  • El módulo de encoder separa la ISR (decodificar) del lazo principal (posición, velocidad, estado).
  • El CPR final incluye la reducción: 11 PPR × 4 × 30 = 1320 cuentas por vuelta de la rueda, 0,1547 mm por cuenta con 65 mm de diámetro.
  • La posición va en int32_t y se copia con interrupciones deshabilitadas; un int16_t se desborda a las ~25 vueltas.
  • La velocidad es Δcuentas por ventana; el escalón es 60/(CPR·T) y crece cuando se acorta la ventana.
  • El filtro exponencial suaviza la lectura pero agrega retardo: un compromiso que entra en el lazo de control.
  • El bloqueo se detecta con «comando alto y cero cuentas durante un tiempo», y la acción es cortar la potencia.
  • El contador de errores de cuadratura es el indicador de salud del encoder.
  • La distancia del encoder es la de la rueda, no la del piso: se verifica con una referencia física.

¿Listo para el bloque 7?

Antes de avanzar, calculá el CPR y el escalón de velocidad de tu propio motor con encoder, decí por qué usás int32_t para la posición y explicá cómo distinguís un motor bloqueado de uno detenido por orden. Si podés justificar las tres respuestas, ya medís posición y velocidad con confianza.

En el próximo tema empieza el bloque de actuadores con transistores, MOSFET y drivers: cómo un pin de unos pocos miliamperios maneja una carga de amperios.