← Índice del curso

Sensores y Actuadores · Tema 34

Taller: posicionamiento con paso a paso y servo

Dos motores paso a paso mueven una pluma en el plano y un servo la levanta y la baja: es un plotter mínimo y un banco de pruebas perfecto del posicionamiento. El taller reúne lo visto en los temas 32 y 33 y resuelve los tres problemas de todo sistema en lazo abierto: acelerar sin perder pasos, saber dónde está el cero y mover dos ejes a la vez sin torcer la trayectoria.

01 · Punto de partida

Qué vamos a construir

El montaje es un plotter de dos ejes: un motor NEMA 17 mueve el eje X y otro el eje Y, cada uno con su driver A4988 en 1/16 de paso y una correa dentada GT2 con polea de 20 dientes (40 mm de avance por vuelta). Con 3200 micropasos por vuelta, resulta 3200 / 40 = 80 micropasos por milímetro, es decir 0,0125 mm de resolución nominal. Un servo (tema 32) levanta o baja la pluma. Dos finales de carrera marcan el origen de cada eje.

Los requisitos, de más simple a más exigente:

  • Mover un eje una distancia dada, sin perder pasos (rampas).
  • Encontrar el origen al encender (homing) y no pasarse de los límites.
  • Mover los dos ejes para que la pluma siga una línea recta (movimiento coordinado).
  • Sincronizar la pluma con los movimientos, sin que el dibujo salga con trazos de más.

Es el mismo problema que resuelven una impresora 3D o una CNC, con la diferencia de la escala. Y, como en los talleres anteriores, el resultado es un módulo: irA(x, y), plumaArriba(), plumaAbajo() y origen(), que el resto del programa usa sin saber cómo funciona.

02 · Paso 1

Arquitectura y tiempos

Tareas del posicionador.
TareaDóndeRitmo
Emitir los pulsos STEPLazo rápido o interrupción de un timerHasta unos 2400 Hz por eje (30 mm/s)
Calcular el siguiente intervalo (perfil)Junto a cada pasoUn cálculo corto por paso
Leer los finales de carreraLazo (y homing)Cada vuelta del lazo
Lista de trazos y máquina de estados de la plumaLazo principalSin bloqueos (tema 41)

La regla de oro es no bloquear el lazo: cada paso es un evento «si ya venció el intervalo, emití el pulso». Si el lazo hace otras tareas lentas (por ejemplo, Serial.print largos o un delay), los pasos se retrasan y el movimiento sale entrecortado. A 2400 Hz hay un paso cada 417 µs; en un AVR, el lazo debe volver cada pocos cientos de microsegundos. Si las velocidades son más altas, se pasa a un timer con interrupción. Otro aspecto: la librería Servo de Arduino usa un timer, que puede interferir con otras librerías de pulsos; conviene verificar la compatibilidad o usar un servo controlado por un PCA9685.

03 · Paso 2

Cableado y montaje

  • Drivers: cada A4988 con STEP y DIR a pines del microcontrolador, EN (activo en bajo) compartido, MS1-MS3 en alto para 1/16, SLEEP y RESET unidos. VMOT a 12 o 24 V con un capacitor de 100 µF por driver, lo más cerca posible.
  • Vref: regulado para la corriente del motor (tema 33), con disipador en cada driver.
  • Finales de carrera: un contacto por eje con INPUT_PULLUP, cableado como normalmente cerrado hacia masa (cuando se acciona, se abre). Así, un cable cortado se lee igual que un tope accionado y el sistema se detiene en lugar de seguir ciegamente (tema 6).
  • Servo: alimentado desde una fuente de 5 V propia (no desde el regulador de la placa), con masa común y un capacitor de 470 µF; la señal en un pin con PWM de hardware o con la librería Servo.
  • Masa común entre drivers, servo y microcontrolador, y cables de motor trenzados y separados de los de los finales de carrera.

Y, como siempre con drivers de paso a paso: nunca conectar ni desconectar los motores con la alimentación encendida.

04 · Paso 3

El perfil de velocidad

Mover un eje una distancia D con velocidad máxima v y aceleración a tiene dos formas posibles:

  • Trapezoidal, si D ≥ v² / a: acelera, viaja a velocidad constante y frena. Tiempo: 2v/a + (D − v²/a) / v.
  • Triangular, si no alcanza a llegar a v: acelera la mitad del recorrido y frena la otra mitad. Velocidad pico: √(a·D); tiempo: 2√(D/a).

Con v = 30 mm/s y a = 300 mm/s² (en micropasos: 2400 por segundo y 24 000 por segundo al cuadrado), la aceleración dura 0,1 s y recorre 1,5 mm. Un trazo de 100 mm es trapezoidal y dura 3,43 s; uno de 10 mm, 0,43 s; uno de 2 mm es triangular, llega a 24,5 mm/s y dura 0,16 s. Sin rampa, la única velocidad segura sería la de arranque (unos 300 Hz, 3,75 mm/s): 100 mm tardarían 26,7 s, casi ocho veces más.

Una forma elegante de implementarlo sin planificar tiempos: calcular la velocidad en función de los pasos hechos i y los pasos que faltan, como si cada extremo fuera una rampa:

v(i) = mín( vmáx , √(2·a·(i+1)) , √(2·a·(n−i)) )

El primer término es el límite de velocidad, el segundo la velocidad alcanzable acelerando desde cero, y el tercero la máxima desde la que todavía se puede frenar a tiempo. Los tres juntos dan el trapecio, o el triángulo cuando el recorrido es corto, sin tratar los casos por separado. Para i = 0, la velocidad inicial es √(2a) = 219 micropasos/s con a = 24 000, por debajo de la frecuencia de arranque.

const float V_MAX = 2400.0;                          // micropasos/s (30 mm/s)
const float A_MAX = 24000.0;                         // micropasos/s² (300 mm/s²)

// velocidad (µpasos/s) para el paso i de un movimiento de n pasos
float velocidadEnPaso(uint32_t i, uint32_t n) {
    float vAcel = sqrt(2.0 * A_MAX * (i + 1));       // acelerando desde parado
    float vFren = sqrt(2.0 * A_MAX * (n - i));       // máxima desde la que aún se puede frenar
    return min(V_MAX, min(vAcel, vFren));
}

uint32_t intervaloUs(float v) { return (uint32_t)(1000000.0 / v); }

05 · Paso 4

Homing

Los pasos se cuentan desde donde el programa cree que está, y al encender no tiene idea. El homing define el origen con un final de carrera. Un método robusto en dos pasadas:

  1. Levantar la pluma (para no rayar ni chocar).
  2. Pasada rápida: mover el eje hacia el final de carrera a velocidad moderada (por ejemplo 15 mm/s) hasta que se accione. Se frena de golpe: es un movimiento de velocidad baja, sin rampa larga.
  3. Retroceso: alejarse 1 mm del final de carrera (más que el sobreimpulso de la pasada rápida).
  4. Pasada lenta: acercarse a 2,5 mm/s hasta accionarlo de nuevo.
  5. Definir el cero: posicion = 0 en ese instante (o con un desplazamiento fijo medido).

La razón de la segunda pasada es el retardo de detección: entre que el contacto cambia y el programa lo nota pasa hasta un período del lazo, y durante ese tiempo el eje avanza v × período más allá del punto de conmutación. Con 15 mm/s y el lazo leyendo cada 5 ms, el error es de 0,075 mm; con 20 ms, 0,3 mm. A 2,5 mm/s, los mismos retardos dan 0,0125 y 0,05 mm: seis veces menos. La repetibilidad del cero la fija la pasada lenta (y el propio contacto: histéresis y desgaste).

bool topeAccionado(uint8_t pin) { return digitalRead(pin) == HIGH; }     // NC con pull-up: abierto = accionado

void homing(Eje &e) {
    plumaArriba();
    moverHastaTope(e, -1, 1200);                       // rápida: 1200 µpasos/s = 15 mm/s
    moverRelativo(e, +80, 800);                        // retrocede 1 mm
    moverHastaTope(e, -1, 200);                        // lenta: 200 µpasos/s = 2,5 mm/s
    e.pos = 0;                                         // origen
    e.homeado = true;
}

void moverHastaTope(Eje &e, int8_t sentido, uint16_t vel) {
    uint32_t intervalo = 1000000UL / vel;
    uint32_t limite = 20000;                           // máximo de pasos buscando el tope (límite de seguridad)
    while (!topeAccionado(e.pinTope) && limite--) {
        darPaso(e, sentido);
        delayMicroseconds(intervalo);                  // el homing ocurre al arrancar y puede ser bloqueante
    }
    if (limite == 0) fallaHoming(e);                   // nunca encontró el tope: cable, tope o motor
}

Dos protecciones: el límite de pasos durante la búsqueda (si el tope no se acciona nunca, el eje avanzaría para siempre) y los límites de software tras el homing (no aceptar posiciones fuera de 0..ANCHO). Hasta que haya homing, el módulo debe rechazar movimientos absolutos.

06 · Paso 5

Movimiento coordinado

Si se ordena irA(60, 30) y cada eje se mueve por su cuenta, el eje Y (30 mm) llega antes que el X (60 mm) y la pluma dibuja una diagonal seguida de un tramo horizontal, no una recta. Con los parámetros del ejemplo (30 mm/s, 300 mm/s²), Y llega a los 1,10 s y X a los 2,10 s, y en el trayecto la pluma se aparta hasta 13 mm de la recta ideal.

Para dibujar una recta, los dos ejes deben llegar al mismo tiempo y avanzar en proporción. El algoritmo de Bresenham lo resuelve con enteros: el eje con mayor recorrido (el dominante) da un paso en cada ciclo; el otro da un paso solo cuando la acumulación del error lo requiere. Así la desviación respecto de la recta ideal nunca supera medio micropaso (0,006 mm). La velocidad y la aceleración se aplican al eje dominante, escaladas por la proporción del trazo:

vdominante = vavance × (Δdominante / L)   L = √(Δx² + Δy²)

Para el trazo de 60 × 30 mm, L = 67,08 mm; con el avance de 30 mm/s, X va a 26,8 mm/s y Y a 13,4 mm/s. El trazo dura 2,34 s, un 11 % más que el movimiento independiente del eje X (2,10 s), lo que es el precio de la recta correcta.

struct Linea {
    int32_t dx, dy;                                    // magnitudes (siempre positivas), en µpasos
    int8_t sx, sy;                                     // sentidos
    int32_t n;                                         // pasos del eje dominante
    int32_t i, err;                                    // paso actual y acumulador de Bresenham
    bool xDominante;
    uint32_t proximoUs;
    bool activa;
};
Linea linea;

void iniciarLinea(int32_t destX, int32_t destY) {
    int32_t ddx = destX - ejeX.pos, ddy = destY - ejeY.pos;
    linea.dx = abs(ddx); linea.dy = abs(ddy);
    linea.sx = ddx >= 0 ? 1 : -1; linea.sy = ddy >= 0 ? 1 : -1;
    linea.xDominante = linea.dx >= linea.dy;
    linea.n = linea.xDominante ? linea.dx : linea.dy;
    linea.i = 0; linea.err = 0;
    linea.proximoUs = micros();
    linea.activa = linea.n > 0;
}

void avanzarLinea() {                                  // se llama sin parar desde el loop
    if (!linea.activa || (int32_t)(micros() - linea.proximoUs) < 0) return;
    int32_t menor = linea.xDominante ? linea.dy : linea.dx;
    if (linea.xDominante) darPaso(ejeX, linea.sx); else darPaso(ejeY, linea.sy);
    linea.err += menor;                                // Bresenham: el eje menor da un paso cuando se acumula un paso completo
    if (2 * linea.err >= linea.n) {
        linea.err -= linea.n;
        if (linea.xDominante) darPaso(ejeY, linea.sy); else darPaso(ejeX, linea.sx);
    }
    linea.i++;
    float v = velocidadEnPaso(linea.i - 1, linea.n) * escalaDominante;   // escalada por n / L
    linea.proximoUs += intervaloUs(v);
    if (linea.i >= linea.n) linea.activa = false;
}

07 · Paso 6

La pluma con el servo

Subir y bajar la pluma es un movimiento de un servo entre dos ángulos calibrados (por ejemplo, 70° arriba y 105° abajo). El servo no informa cuándo llegó, así que el programa debe esperar el tiempo que tarda: un servo común recorre 60° en unos 0,1 s, de modo que 35° llevan unos 60 ms; con el margen de la inercia de la pluma y de la banda muerta, se espera 150 a 250 ms. La espera se hace sin bloquear: una pequeña máquina de estados.

  • Antes de mover con la pluma baja: asegurar que bajó (esperar). De lo contrario, el comienzo del trazo se pierde.
  • Antes de desplazarse en vacío: subir la pluma y esperar, para no rayar.
  • Al terminar o ante una falla: subir la pluma (estado seguro).

Un detalle práctico: el servo puede temblar cuando los motores paso a paso consumen sus picos de corriente (ruido en la alimentación), y los pulsos de STEP a alta frecuencia pueden perturbar la señal del servo. Una fuente aparte, capacitores y masa común bien hecha lo resuelven. Y se puede ahorrar energía y ruido con detach() cuando la pluma no necesita sostenerse (aunque, al volver a conectar, el servo vuelve a su posición con un tirón).

08 · Paso 7

El programa completo

La lógica de más alto nivel es una lista de trazos y una máquina de estados que ejecuta cada uno en secuencia: levantar la pluma, ir al inicio, bajarla, dibujar, levantarla.

struct Trazo { int16_t x0, y0, x1, y1; };             // en décimas de milímetro
const Trazo CUADRADO[] = { {100,100,500,100}, {500,100,500,500}, {500,500,100,500}, {100,500,100,100} };
const uint8_t N_TRAZOS = sizeof(CUADRADO) / sizeof(Trazo);

enum Estado { INICIO, SUBIENDO, YENDO, BAJANDO, DIBUJANDO, FIN };
Estado estado = INICIO;
uint8_t actual = 0;
uint32_t tEstado = 0;

int32_t aMicropasos(int16_t decimas) { return (int32_t)decimas * 8; }     // 80 µpasos/mm = 8 por décima

void loop() {
    avanzarLinea();                                    // prioridad: los pasos no se pueden retrasar
    switch (estado) {
        case INICIO:
            plumaArriba(); tEstado = millis(); estado = SUBIENDO; break;
        case SUBIENDO:
            if (millis() - tEstado >= 200) {
                iniciarLinea(aMicropasos(CUADRADO[actual].x0), aMicropasos(CUADRADO[actual].y0)); estado = YENDO;
            } break;
        case YENDO:
            if (!linea.activa) { plumaAbajo(); tEstado = millis(); estado = BAJANDO; } break;
        case BAJANDO:
            if (millis() - tEstado >= 200) {
                iniciarLinea(aMicropasos(CUADRADO[actual].x1), aMicropasos(CUADRADO[actual].y1)); estado = DIBUJANDO;
            } break;
        case DIBUJANDO:
            if (!linea.activa) { plumaArriba(); tEstado = millis(); actual++; estado = (actual < N_TRAZOS) ? SUBIENDO : FIN; } break;
        case FIN: break;
    }
}

09 · Paso 8

Verificar

  1. Calibración de escala. Mové 100 mm y medí con regla: ajustá micropasos/mm (poleas, correa, diámetro) hasta que coincida.
  2. Repetibilidad del homing. Hacé homing diez veces, mandá a una posición fija y medí con calibre: la dispersión debe ser de pocas centésimas de milímetro.
  3. Cierre del dibujo. Trazá un cuadrado de 40 mm y repetilo veinte veces sobre el mismo lugar: si las líneas no coinciden, se pierden pasos.
  4. Prueba de retorno. Tras un recorrido largo y variado, volver al origen: el eje debe llegar al final de carrera con la cuenta en cero. Si la cuenta da distinta, hubo pérdida de pasos.
  5. Aceleración y velocidad. Subí la velocidad y la aceleración hasta que se pierdan pasos (se oye un zumbido o hay saltos) y trabajá con el 60 a 70 % de ese valor.
  6. Rectas y diagonales. Dibujá diagonales de distintas pendientes y comprobá que sean rectas y lleguen al punto.
  7. Pluma. Verificá que no quedan puntos de más al inicio y al final de los trazos, ni rayas en los desplazamientos.

10 · Paso 9

Fallas y robustez

Fallas típicas de un posicionador de lazo abierto.
FallaSíntomaDefensa
Pérdida de pasosEl dibujo se corre; no cierraRampas, menos velocidad, más corriente, prueba de retorno
Final de carrera defectuosoHoming que choca o nunca terminaCableado normalmente cerrado, límite de pasos en la búsqueda
Rebote o ruido en el final de carreraCero distinto en cada homingDos pasadas, filtro RC o antirrebote (tema 6)
Fuera de límitesEl carro choca contra el extremoLímites de software, rechazo de movimientos sin homing
ResonanciaZumbido o vibración a ciertas velocidadesCambiar velocidad o microstepping, evitar velocidad crítica
Servo temblorosoPluma que vibraFuente propia, capacitor, masa común, cables separados
Driver calienteSe apaga por temperaturaReducir corriente, disipador, deshabilitar en reposo
Corte de energíaPosición perdidaRepetir el homing al arrancar

El modo de falla distintivo del lazo abierto es el silencioso: el sistema hace lo que el programa cree y se aparta de la realidad sin avisar. Las defensas son estructurales: referenciar a menudo (volver al origen entre trabajos), mantener margen de par y de velocidad, y, si el trabajo lo exige, agregar un sensor (un encoder en el eje) que verifique.

11 · Analogía con programación

Planificar movimientos, coordinar ejes y estimar el homing

Calculemos los tres elementos del taller: el perfil de distintos trazos, la diferencia entre mover dos ejes de forma independiente o coordinada (con Bresenham) y el error de cero del homing según la velocidad y el retardo de detección.

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

UP_MM = 80                                   # micropasos por milímetro (1/16, correa GT2 de 40 mm por vuelta)
V_MAX, A_MAX = 30.0, 300.0                   # mm/s y mm/s²


def perfil(distancia, v_max=V_MAX, a=A_MAX):
    d_acel = v_max ** 2 / (2 * a)
    if distancia >= 2 * d_acel:
        return "trapezoidal", v_max, 2 * v_max / a + (distancia - 2 * d_acel) / v_max
    v_pico = math.sqrt(a * distancia)
    return "triangular", v_pico, 2 * v_pico / a


print("trazo     micropasos  perfil        v pico    tiempo   sin rampa (300 Hz)")
for d in (2, 10, 100):
    forma, v, t = perfil(d)
    print(f"{d:4d} mm  {d * UP_MM:10d}  {forma:11s}  {v:5.1f} mm/s  {t:6.3f} s  {d * UP_MM / 300:7.1f} s")
print(f"Frecuencia de STEP a {V_MAX:.0f} mm/s: {V_MAX * UP_MM:.0f} Hz; arranque con a = {A_MAX * UP_MM:.0f} µp/s²: {math.sqrt(2 * A_MAX * UP_MM):.0f} µp/s")


# ---- Posición en el tiempo de un eje con perfil de velocidad ----
def posicion(distancia, t, v_max=V_MAX, a=A_MAX):
    d_acel = v_max ** 2 / (2 * a)
    if distancia >= 2 * d_acel:
        ta, tc = v_max / a, (distancia - 2 * d_acel) / v_max
        total = 2 * ta + tc
        if t >= total: return distancia
        if t < ta: return 0.5 * a * t * t
        if t < ta + tc: return d_acel + v_max * (t - ta)
        return distancia - 0.5 * a * (total - t) ** 2
    v_pico = math.sqrt(a * distancia)
    ta = v_pico / a
    if t >= 2 * ta: return distancia
    if t < ta: return 0.5 * a * t * t
    return distancia - 0.5 * a * (2 * ta - t) ** 2


# ---- Trazo de 60 x 30 mm: ejes independientes frente a movimiento coordinado ----
dx, dy = 60.0, 30.0
longitud = math.hypot(dx, dy)
t_indep = max(perfil(dx)[2], perfil(dy)[2])
desvio = max(abs(posicion(dx, k / 1000) * dy - posicion(dy, k / 1000) * dx) / longitud for k in range(round(t_indep * 1000) + 1))
t_coord = perfil(longitud)[2]
print()
print(f"Independientes: {t_indep:.3f} s y la pluma se aparta hasta {desvio:.1f} mm de la recta")
print(f"Coordinado: longitud {longitud:.2f} mm, {t_coord:.3f} s; X a {V_MAX * dx / longitud:.1f} mm/s, Y a {V_MAX * dy / longitud:.1f} mm/s")


# ---- Bresenham con enteros ----
def bresenham(dx_i, dy_i):
    y, err, peor = 0, 2 * dy_i - dx_i, 0.0
    for x in range(1, dx_i + 1):
        if err > 0:
            y += 1
            err += 2 * (dy_i - dx_i)
        else:
            err += 2 * dy_i
        peor = max(peor, abs(y - dy_i * x / dx_i))
    return y, peor


for pasos_x, pasos_y in ((4800, 2400), (4800, 2000), (3999, 1234)):
    y_final, peor = bresenham(pasos_x, pasos_y)
    print(f"Bresenham {pasos_x} x {pasos_y}: termina en y = {y_final}; desviación máxima {peor:.3f} micropasos ({peor / UP_MM:.4f} mm)")

# ---- Error de cero del homing: v × retardo de detección ----
print()
print("lazo lee cada   pasada rápida (15 mm/s)   pasada lenta (2,5 mm/s)")
for ms in (1, 5, 20):
    print(f"{ms:8d} ms   {15 * ms / 1000:14.4f} mm   {2.5 * ms / 1000:20.4f} mm")
retardo = 0.005
t_homing = 100 / 15 + (1 + 15 * retardo) / 15 + 1 / 2.5
print(f"Homing desde 100 mm con lazo de 5 ms: {t_homing:.2f} s en total")

Resultado: con 80 micropasos/mm, un trazo de 2 mm (160 micropasos) es triangular, llega a 24,5 mm/s y dura 0,163 s; uno de 10 mm (800), trapezoidal, 0,433 s; y uno de 100 mm (8000), 3,433 s frente a 26,7 s sin rampa. A 30 mm/s la frecuencia de STEP es 2400 Hz y la velocidad inicial es 219 micropasos/s. El trazo de 60 × 30 mm con ejes independientes tarda 2,100 s y la pluma se aparta hasta 13,1 mm de la recta; coordinado, tarda 2,336 s (X a 26,8 mm/s, Y a 13,4 mm/s). Bresenham termina siempre en el punto exacto, con una desviación máxima de 0,5 micropasos (0,006 mm). El error de cero del homing con el lazo leyendo cada 5 ms es de 0,075 mm con una sola pasada rápida y de 0,0125 mm con la lenta; con 20 ms, 0,3 y 0,05 mm. Un homing desde 100 mm con lazo de 5 ms dura 7,14 s.

12 · Exploración interactiva

El banco de pruebas del posicionador

El primer instrumento dibuja el perfil de velocidad de un eje y verifica si el motor lo soporta. El segundo calcula el tiempo y el error del homing. El tercero compara el movimiento independiente con el coordinado de dos ejes, con el dibujo de las trayectorias. Los límites del motor son los del modelo ilustrativo del tema 33.

Instrumento 1: el perfil de velocidad

Perfil—
Tiempo total—
Velocidad pico · frecuencia de STEP—
Aceleración · distancia de rampa—
Estado
—

Instrumento 2: homing

Sobreimpulso de la pasada rápida—
Error del cero—
Duración del homing—
Estado
—

Instrumento 3: movimiento coordinado

Desvío máximo de la recta—
Tiempo total—
Velocidad de cada eje—
Estado
—

Recorrido guiado

  1. Con 100 mm, 30 mm/s y 300 mm/s²: perfil trapezoidal, 3,43 s, 2400 Hz de STEP (dentro de los 2653 Hz del motor a 24 V). Bajá la distancia a 2 mm: el perfil se vuelve triangular y la velocidad pico no llega a 30.
  2. Subí la velocidad a 50 mm/s: 4000 Hz, por encima del límite: el motor perdería pasos. Pasá a 12 V: el límite baja a 1327 Hz y hasta los 30 mm/s (2400 Hz) lo superan. La tensión es lo que da velocidad.
  3. Subí la aceleración a 1500 mm/s²: la rampa dura 20 ms y la velocidad inicial √(2a) supera los ~300 Hz a los que el motor puede arrancar sin rampa: el aviso indica riesgo de pérdida de pasos por aceleración.
  4. En el homing, con 15 mm/s y lectura cada 5 ms: sobreimpulso de 0,075 mm; con dos pasadas el error del cero baja a 0,0125 mm. Desmarcá las dos pasadas: el error es el de la rápida.
  5. Pasá a lectura cada 100 ms con 15 mm/s: 1,5 mm de sobreimpulso, más que el retroceso de 1 mm, y el eje queda apretando el tope. Un lazo lento requiere una velocidad de búsqueda menor.
  6. En el movimiento coordinado, 60 × 30 mm independientes: la trayectoria se curva y se aparta ~13 mm de la recta. Pasá a «Coordinado»: la trayectoria es la recta y el tiempo sube de 2,10 a 2,34 s.
  7. Probá Δx = Δy = 50 mm: coordinado, ambos ejes a 21,2 mm/s (la velocidad sobre la diagonal es 30 mm/s). Con ejes independientes y Δx = Δy, la trayectoria sale recta, porque llegan juntos.
Desafío: la letra «A»

Para dibujar una «A» de 40 mm de alto hacen falta tres trazos: dos diagonales y una horizontal. ¿Cuántas veces sube y baja la pluma, y cuánto tiempo se pierde en ello? Tres trazos independientes, cada uno con la pluma levantada para ir al inicio y bajada para dibujar: 6 movimientos del servo. Con 200 ms de espera cada uno, 1,2 s solo en la pluma, comparable con el tiempo de dibujo. Optimizaciones: ordenar los trazos para minimizar los desplazamientos en vacío, unir trazos contiguos sin levantar la pluma, y reducir la espera al mínimo que el servo necesita (medilo).

13 · Método de trabajo

Lo aprendido, para cualquier posicionador de lazo abierto

  1. Calculá la escala (micropasos por unidad) y la frecuencia de STEP a la velocidad máxima.
  2. Usá siempre un perfil de velocidad con aceleración limitada; calculalo con la fórmula de los pasos hechos y por hacer.
  3. Hacé homing en dos pasadas, con límite de pasos y finales de carrera normalmente cerrados.
  4. Coordiná los ejes con Bresenham y aplicá la velocidad al eje dominante.
  5. Sincronizá los actuadores auxiliares (servo, bomba, láser) con esperas calculadas y máquinas de estados.
  6. Definí límites de software y rechazá movimientos sin homing.
  7. Probá con ensayos de retorno y de cierre para detectar pérdida de pasos.
  8. Trabajá con margen: el 60 a 70 % de la velocidad y la aceleración en que empiezan los fallos.

Si el dibujo no cierra, se perdieron pasos (velocidad, aceleración o corriente); si el origen cambia, el homing o el final de carrera; si las diagonales salen curvas, falta coordinación; si la pluma deja manchas, la espera del servo es corta.

14 · Comprensión

Confusiones frecuentes

«Cada eje puede moverse por su cuenta y llegan igual»

Llegan en tiempos distintos, y la trayectoria deja de ser una recta. Para una recta hay que coordinar ambos ejes.

«Si el programa cuenta bien los pasos, la posición es exacta»

La cuenta del programa no sabe si el motor perdió pasos. La posición real puede diferir sin aviso.

«El homing con una pasada rápida es suficiente»

El sobreimpulso por el retardo de detección da un error proporcional a la velocidad; una segunda pasada lenta lo reduce mucho.

«La aceleración solo importa para movimientos largos»

En los cortos, el perfil es triangular y la velocidad pico depende de la aceleración: mandar mucha aceleración con distancias chicas igual pierde pasos.

«El servo ya llegó cuando se ejecutó write()»

write() solo fija la orden; el servo tarda decenas de milisegundos en moverse y no informa cuándo llegó.

«Un final de carrera normalmente abierto es lo mismo que uno normalmente cerrado»

Con el cerrado, un cable cortado se lee como tope accionado y el sistema se detiene; con el abierto, el cable cortado pasa inadvertido y el eje choca.

15 · Práctica guiada

Predecí antes de comprobar

1. Escala

Un eje con motor de 200 pasos, 1/8 de paso y una polea de 16 dientes con correa de paso 2 mm. ¿Cuántos micropasos por milímetro?

Ver solución

Avance por vuelta = 16 × 2 = 32 mm; micropasos por vuelta = 200 × 8 = 1600; 50 micropasos/mm (0,02 mm cada uno).

2. Perfil

Con v = 40 mm/s y a = 400 mm/s², ¿cuánto dura la aceleración y qué distancia recorre? ¿Un trazo de 3 mm es trapezoidal o triangular?

Ver solución

t = 40 / 400 = 0,1 s; distancia = 40² / (2 × 400) = 2 mm. Un trazo de 3 mm es menor que 2 × 2 = 4 mm: triangular, con velocidad pico √(400 × 3) = 34,6 mm/s.

3. Frecuencia

A 25 mm/s con 50 micropasos/mm, ¿qué frecuencia de STEP hay? ¿La soporta un motor que llega a 1300 Hz?

Ver solución

25 × 50 = 1250 Hz: está dentro del límite, pero con poco margen (4 %). Conviene bajar a unos 20 mm/s.

4. Homing

El lazo lee el final de carrera cada 10 ms. ¿Qué sobreimpulso produce una pasada a 12 mm/s y una a 2 mm/s?

Ver solución

12 × 0,010 = 0,12 mm; 2 × 0,010 = 0,02 mm.

5. Coordinación

Un trazo de 80 × 60 mm con avance de 40 mm/s. ¿Qué longitud tiene y a qué velocidad va cada eje?

Ver solución

L = √(80² + 60²) = 100 mm; X: 40 × 80/100 = 32 mm/s; Y: 40 × 60/100 = 24 mm/s.

6. Diagnóstico

Un cuadrado de 40 mm se dibuja bien una vez, pero al repetirlo 20 veces las líneas se corren hacia un lado. ¿Qué pasó y qué cambiás?

Ver solución

Pérdida de pasos acumulada en un eje (aceleración o velocidad excesivas, corriente baja o trayecto con roce). Se reduce la aceleración y la velocidad, se sube la corriente del driver, se revisa la correa y se agrega un homing periódico.

16 · Síntesis

Ideas para recordar

  • Un posicionador de paso a paso funciona en lazo abierto: la posición es la cuenta de pasos, y no hay aviso si se pierden.
  • El perfil de velocidad es trapezoidal (o triangular en trazos cortos); v(i) = mín(vmáx, √(2a(i+1)), √(2a(n−i))) lo da sin casos especiales.
  • El homing define el cero con un final de carrera normalmente cerrado, en dos pasadas (rápida y lenta), con límite de pasos.
  • El error del cero del homing es velocidad × retardo de detección; la pasada lenta lo reduce.
  • El movimiento coordinado con Bresenham hace que los dos ejes lleguen juntos y la trayectoria sea una recta, con una desviación máxima de medio micropaso.
  • Con ejes independientes, la trayectoria se curva: 13 mm de desvío en el ejemplo de 60 × 30 mm.
  • Los actuadores auxiliares (el servo de la pluma) se sincronizan con esperas calculadas en una máquina de estados no bloqueante.
  • Se verifica con pruebas de retorno y de cierre; se trabaja con margen de velocidad y aceleración, y se hace homing de nuevo al encender.

¿Listo para el bloque 8?

Antes de avanzar, calculá el tiempo de un trazo de 50 mm con v = 25 mm/s y a = 250 mm/s², explicá por qué un homing necesita dos pasadas y decí qué pasa si dos ejes se mueven de forma independiente en un trazo en diagonal. Si podés justificar las tres respuestas, ya sabés posicionar con motores paso a paso.

En el próximo tema empieza el bloque de salidas visuales y sonoras con los LEDs: corriente, brillo y color.