Sistema de Movimiento
Arquitectura General
Bucle de ejecución
| Nivel | Contexto | Frecuencia | Responsabilidad |
|---|---|---|---|
| ISR (SysTick) | Interrupción | 1 kHz | Control PID en tiempo real |
| Main Loop | Bucle principal | Variable | Funciones bloqueantes de movimiento |
El main loop llama a funciones bloqueantes (move_straight, move_arc_turn, etc.) que establecen velocidades objetivo y esperan condiciones de finalización. El ISR ejecuta el control PID en segundo plano.
sequenceDiagram
participant ML as Main Loop
participant ISR as ISR (1 kHz)
ML->>ML: move_straight(d, speed)
ML->>ML: set_target_linear_speed()
activate ML
ML->>ML: while(dist < target) { ... }
loop Cada 1 ms (espera ocupada)
ISR->>ISR: control_loop()
ISR->>ISR: update_ideal_linear_speed()
ISR->>ISR: PID lineal (error velocidad)
ISR->>ISR: PID angular (error giro)
ISR->>ISR: PID sensores (error paredes)
ISR->>ISR: set_motors_pwm()
end
deactivate ML
Note over ML,ISR: distancia alcanzada → move_straight retorna
Archivos: move.h, move.c (~60 KB, el archivo más grande del proyecto).
Tipos de Movimiento
26 tipos definidos en enum movement (move.h):
Especiales
| Movimiento | Descripción |
|---|---|
MOVE_NONE |
No operación |
MOVE_HOME |
Secuencia de vuelta al inicio |
MOVE_START |
Primer movimiento desde la salida |
MOVE_END |
Secuencia de fin de carrera |
MOVE_DIAGONAL |
Recta en diagonal (solo en run sequences) |
Giros en Arco (Exploración)
| Movimiento | Descripción |
|---|---|
MOVE_LEFT / MOVE_RIGHT |
Giro 90° en arco (exploración) |
Giros en Arco (Carrera)
| Movimiento | Descripción |
|---|---|
MOVE_LEFT_90 / MOVE_RIGHT_90 |
Giro 90° |
MOVE_LEFT_180 / MOVE_RIGHT_180 |
Giro 180° (U-turn) |
MOVE_LEFT_TO_45 / MOVE_RIGHT_TO_45 |
Giro terminando a 45° |
MOVE_LEFT_TO_135 / MOVE_RIGHT_TO_135 |
Giro terminando a 135° |
MOVE_LEFT_45_TO_45 / MOVE_RIGHT_45_TO_45 |
Curva S entre paredes a 45° |
MOVE_LEFT_FROM_45 / MOVE_RIGHT_FROM_45 |
Giro desde pared a 45° |
MOVE_LEFT_FROM_45_180 / MOVE_RIGHT_FROM_45_180 |
180° desde pared a 45° |
Rectos y Giros en Sitio
| Movimiento | Descripción |
|---|---|
MOVE_FRONT |
Avanzar una celda |
MOVE_BACK |
Girar 180° en sitio, avanzar una celda |
MOVE_LEFT_INPLACE / MOVE_RIGHT_INPLACE |
Giro 90° en sitio |
MOVE_BACK_WALL |
Como BACK pero contra una pared |
MOVE_BACK_STOP |
Como BACK pero sin avanzar celda |
Movimientos Lineales
move_straight(distance, speed, check_wall_loss, stop) (move.c:1339-1378)
Movimiento recto bloqueante de bajo nivel:
- Establecer velocidad angular = 0.
- Establecer velocidad objetivo =
speed. - Mientras
distancia_recorrida < distance: - Si
check_wall_loss: detectar pérdida de pared y reajustar distancia. - Si
stop: calcular distancia de frenada necesaria. -
Si
stop: esperar a que velocidad ideal llegue a 0. -
Stop distance: calculada con
calc_straight_to_speed_distance(ideal_speed, 0)usandobreak_accel. - ⚠️ Usa velocidad ideal en lugar de velocidad real (ver MV-05).
move_straight_until_front_distance(distance, speed, stop) (move.c:1387-1415)
Avanza/retrocede hasta que el sensor frontal detecta la distancia especificada. La dirección se determina automáticamente.
run_straight(distance, start_offset, end_offset, cells, has_begin, speed, final_speed, next_turn_sign) (move.c:1492-1637)
Movimiento recto multi-celda para speed run:
- Activar corrección de sensores laterales.
- Para cada celda:
- Avanzar
CELL_DIMENSION+ offsets. - Detectar cambio de celda por distancia recorrida.
- Comprobar pérdida de pared (wall loss).
- Última celda: verificar pared del lado del próximo giro.
- Calcular distancia de frenada si
final_speed != speed. - Al detectar pérdida de pared: reajustar distancia.
run_diagonal(distance, end_offset, cells, speed, final_speed) (move.c:1727-1768)
Similar a run_straight pero para diagonales:
- Longitud de celda: CELL_DIAGONAL (127.3 mm)
- Corrección diagonal de sensores frontales (>1 celda)
- Sin corrección diagonal en la última celda (<127.3 mm del final)
Movimientos Angulares
move_arc_turn(turn) (move.c:1776-1803)
Giro en arco con perfil de velocidad sinusoidal (jerk limitado):
Fase 1 (entrada): ω(t) = ω_max × sin(t/T × π/2) [aceleración suave]
Fase 2 (arco): ω(t) = ω_max [velocidad constante]
Fase 3 (salida): ω(t) = ω_max × sin((t−T_arc)/T × π/2) [deceleración suave]
Donde T = transition y el arco total = 2 × transition + arc.
La variable independiente es la distancia lineal recorrida (no el tiempo), lo que hace el perfil independiente de la velocidad lineal.
factor = travelled / turn.transition; // 0 → 1 en fase de entrada
angular_speed = sign × max × sin(factor × π/2);
move_inplace_turn(movement) (move.c:1805-1842)
Giro en el sitio (velocidad lineal = 0) con perfil sinusoidal temporal.
⚠️ La dirección está fijada a izquierda (−1) para MOVE_BACK, MOVE_BACK_WALL y MOVE_BACK_STOP. Ver MV-02.
move_inplace_angle(angle, rads) (move.c:1850-1872)
Giro a velocidad angular constante hasta alcanzar un ángulo absoluto usando el integrador del giroscopio.
Orquestración de Speed Run
move_run_sequence(sequence_movements) (move.c:1920-2067)
Convierte la secuencia de movimientos del floodfill en comandos físicos:
Para cada movimiento en la secuencia:
├── MOVE_START / MOVE_FRONT / MOVE_DIAGONAL
│ └── Acumular distancia (straight_cells++)
├── MOVE_HOME
│ ├── Ejecutar tramo recto/diagonal acumulado
│ └── Ejecutar MOVE_HOME
└── MOVE_LEFT_* / MOVE_RIGHT_*
├── Verificar si hay suficiente distancia para decelerar
│ └── Si no: DEGRADAR velocidad de giro
├── Ejecutar tramo recto/diagonal acumulado (con end_offset del giro)
└── Ejecutar giro
Acumulación de Tramos Rectos
Los tramos rectos consecutivos se acumulan en un solo run_straight multi-celda:
secuencia: F F F R F F S
└─┬─┘ └─┬─┘
3 celdas 2 celdas
Esto minimiza transiciones y mantiene velocidades altas en rectas largas.
Degradación de Velocidad de Giro
Si no hay suficiente distancia para decelerar hasta la velocidad del giro, se degrada la estrategia:
while (distance + end_offset < calc_straight_to_speed_distance(ideal_speed, turn.linear_speed)) {
if (speed_strategy <= SPEED_NORMAL) {
turn = kinematics_settings[SPEED_NORMAL].turns[...]; // mínimo
break;
}
turn = kinematics_settings[--speed_strategy].turns[...]; // bajar un nivel
}
La degradación es solo hacia abajo. Si sobra distancia, no se intenta un giro más rápido (ver MV-09).
Corrección por Pérdida de Pared
check_wall_loss_correction(initial_walls) (move.c:942-978):
- Compara el estado de paredes al entrar en la celda contra el estado actual.
- Si una pared lateral que existía desaparece: activa flag
current_cell_wall_lost. - Reajusta la distancia restante a
WALL_LOSS_TO_SENSING_POINT_DISTANCE(116 mm).
⚠️ Solo detecta pérdida de paredes laterales, no frontales (ver MV-17).
Detección de Saturación
Saturación de PWM
if (abs(pwm) >= MOTORES_SATURATION_PWM) // 1000 de 1024
saturation_count++;
Saturación de Velocidad Angular
if (abs(encoder_angular_speed) >= 50) // rad/s
angular_saturation_count++;
Acción ante Saturación
- Motores → 0 (parada de emergencia).
- Ventilador → 0.
- LED RGB parpadea (rojo = PWM, azul = angular).
- Si pasan 3 segundos: se detiene la carrera.
Durante MOVE_BACK_WALL, la comprobación se deshabilita para evitar falsos positivos al empujar contra una pared.
Funciones del gestor de movimientos Principal
move(movement) — Movimientos de alto nivel
MOVE_HOME → move_home()
MOVE_START / MOVE_FRONT → move_front()
MOVE_END → move_end()
MOVE_LEFT* / MOVE_RIGHT* → move_side(movement)
MOVE_BACK* → move_back(movement)
move_front()
move_straight(CELL_DIMENSION − SENSING_POINT_DISTANCE − current_cell_start_mm, ...)
enter_next_cell()
move_side(movement)
- Ejecuta tramo recto pre-giro (distancia =
turn.start). - Ejecuta
move_arc_turn(turn). - Ejecuta tramo recto post-giro (distancia =
turn.end). - Deshabilita correcciones de sensores durante el giro.
Problemas Conocidos
Los problemas del sistema de movimiento están documentados en el registro de issues con IDs MV-01 a MV-19. Los más relevantes:
- MV-01: PID sin anti-windup (6 integradores) — 🔴 Crítico
- MV-02:
move_inplace_turn()siempre gira a la izquierda — 🔴 Crítico - MV-05: Distancia de frenada usa velocidad ideal, no real — 🟡 Moderado
- MV-08: Fórmulas
move_inplace_turn()no cinemáticamente exactas — 🟡 Moderado - MV-10: Posible inconsistencia de signos en control angular — 🟡 Moderado
Diagrama de Flujo
flowchart TD
subgraph MAIN["Main Loop (bloqueante)"]
direction TB
A["move(movement)"]
A --> B["move_front()"]
A --> C["move_side(movement)"]
A --> D["move_back(movement)"]
A --> E["move_run_sequence()<br>[speed run]"]
B --> B1["move_straight(180 mm, speed)"]
B1 --> B1a["set_target_linear_speed()"]
B1 --> B1b["while(dist < target)<br>(ISR ejecuta PID)"]
C --> C1["move_straight(turn.start)"]
C --> C2["move_arc_turn(turn)"]
C --> C3["move_straight(turn.end)"]
C2 --> C2a["Perfil sinusoidal"]
D --> D1["move_inplace_turn(180°)"]
E --> E1["Acumula tramos rectos"]
E --> E2["Ajusta velocidad de giro"]
E --> E3["run_straight(d, offsets, cells)"]
E --> E4["run_side(movement, turn, next)"]
E --> E5["run_diagonal(d, end, cells)"]
end
Documento generado el 2026-06-12. Ver también Cinemática, Control PID, Encoders y Giroscopio.