Problemas Conocidos
Fecha de análisis: 2026-06-30
Última actualización: 2026-06-30
Total: 12 issues — 2 críticos, 5 moderados, 5 leves
Resumen por Prioridades
| Prioridad |
Cantidad |
Issues |
| 🔴 Críticos |
2 |
CM-01, SW-01 |
| 🟡 Moderados |
5 |
SS-01, SS-02, MV-01, CT-01, EE-01 |
| 🟢 Leves |
5 |
SW-02, SW-03, BL-01, SS-03, SW-04 |
| TOTAL |
12 |
|
Correcciones Recientes ✅
| ID |
Fecha |
Descripción |
| — |
— |
— |
🔴 Críticos
CM-01 — printf transmite por USART1 y USART6 simultáneamente
| Archivos | usart.c:3-21 |
| Descripción | La función _write() redirige todo printf a ambos USART simultáneamente: USART1 (consola debug) y USART6 (placa FOC). Cualquier salida de debug — como la de los modos RAW, CALIBRATED o POSITION — se transmite también a la placa FOC. El parser de comandos de la placa FOC recibe estos bytes e intenta interpretarlos como comandos de motor (Lnn, Rnn, E, D). |
| Impacto | Alto — Si el modo debug está activo durante la competición, la salida serial puede: (a) causar desbordamiento del buffer de 8 bytes en la ISR de USART3 del FOC, (b) ser interpretada como comandos espurios de motor si contienen los caracteres D/E/R/L seguidos de dígitos. Durante la competición normal solo se usa DEBUG_SENSOR_LEDS (sin salida serial), por lo que el riesgo se limita a tener un modo debug serial activo accidentalmente. |
| Mitigación | El debug serial solo se activa manualmente desde el menú. Durante la competición normal se ejecuta debug_sensors_leds() que no genera salida serial. El parser del FOC descarta comandos con primer carácter no reconocido (aunque el buffer puede desbordarse). |
| Solución | Separar la transmisión: _write() debe enviar solo a USART1. Crear una función separada para enviar comandos a USART6 (p.ej., usart6_send()) o usar usart_send_blocking(USART6, ...) directamente en send_command() sin pasar por printf. |
SW-01 — Bucle principal limitado a ~10 Hz con delay(100) incondicional
| Archivos | main.c:53, control.c:133 |
| Descripción | El bucle principal contiene un delay(100) incondicional en la línea 53. El continue de la línea 55 está comentado, por lo que el flujo siempre pasa por el delay. Esto limita la frecuencia del bucle principal a ~10 Hz. La estrategia PID en control.c:133 verifica if (get_clock_ticks() - strat_pid_last_ms > 1), pero esta condición siempre es cierta porque han pasado ≥100 ms desde la última iteración. El comentario del desarrollador en control.c:267 indica la intención de mover control_main_loop() a una ISR de 1 ms. |
| Impacto | Alto — La frecuencia de control efectiva es ~10 Hz en lugar de los ~1 kHz potenciales. Para un robot de sumo esto puede ser aceptable (el rival no se mueve a altísima velocidad), pero limita la capacidad de reacción. La comprobación de 1 ms en strat_pid() es superflua y confusa. |
| Mitigación | El control a 10 Hz es funcional para minisumo, donde las dinámicas son relativamente lentas. La detección de línea (en la ISR de SysTick a 1 kHz) no se ve afectada. |
| Solución | Opción A: Eliminar el delay(100) y añadir un control de tiempo no bloqueante en el bucle principal para ejecutar la estrategia a la frecuencia deseada. Opción B (preferida según el TODO del desarrollador): Mover control_main_loop() a una ISR de timer independiente a 1 kHz, manteniendo el bucle principal solo para menú y debug. Esto requiere añadir protección de concurrencia para variables compartidas. |
🟡 Moderados
Sensores
SS-01 — Posición del rival usa detección binaria en vez de intensidad analógica
| Archivos | sensors.c:84-114 |
| Descripción | La función update_sensors_position() usa get_sensor_digital(sensor) (booleano) para decidir si un sensor contribuye al centroide, y pondera por RIVAL_SENSOR_MAX (constante 2600) en lugar de usar get_sensor_calibrated(sensor). El código original con valores calibrados está comentado (/* get_sensor_calibrated(sensor) */). Esto reduce la estimación de posición a ~6 valores discretos, perdiendo la información de intensidad de la señal. |
| Impacto | Medio — La persecución PID solo puede distinguir ~6 posiciones discretas del rival. Con valores analógicos, el control podría ser más suave y preciso, reaccionando proporcionalmente a la intensidad de la señal. |
| Mitigación | El debounce de 75 muestras evita fluctuaciones. Para sumo, 6 posiciones discretas pueden ser suficientes dado el tamaño del dohyo y la velocidad del robot. |
| Solución | Descomentar get_sensor_calibrated(sensor) y eliminar RIVAL_SENSOR_MAX en el cálculo de sensor_avg y sensor_sum en las líneas 90-91. Verificar que la posición resultante tiene el rango esperado y ajustar las ganancias PID si es necesario. |
SS-02 — Debounce asimétrico en detección de rival
| Archivos | sensors.c:158-159 |
| Descripción | is_rival_detected() requiere 150 ms de presencia continua del rival para activarse (rival_detected && get_clock_ticks() - rival_detected_ms > 150), pero la desactivación es inmediata cuando rival_detected se pone a false (línea 112). Esto crea un debounce asimétrico: 150 ms para detectar, 0 ms para perder. |
| Impacto | Medio — Breves pérdidas de señal (p.ej., por ruido o movimiento rápido del rival) causan que el robot "pierda" al rival inmediatamente. Las aperturas que dependen de is_rival_detected() pueden terminar prematuramente. La estrategia PID no usa is_rival_detected() para el seguimiento continuo (usa directamente get_sensors_position()), pero las aperturas y transiciones de estrategia sí dependen de esta función. |
| Mitigación | El debounce de 75 muestras en la detección digital de cada sensor reduce el ruido aguas arriba. La estrategia PID no se ve afectada porque usa directamente la posición. |
| Solución | Añadir un debounce simétrico en la desactivación: mantener rival_detected = true durante N ms adicionales tras la última detección. Un valor de 50-100 ms evitaría desconexiones espurias sin hacer al robot insensible a la pérdida real del rival. |
Movimiento
MV-01 — Velocidades de apertura superan el clamp de set_motors_speed
| Archivos | control.c:63, control.c:91, motors.c:4-13 |
| Descripción | opening_right_arc() solicita velocidad 90 (set_motors_speed(90, 40)) y opening_left_arc() solicita velocidad 90 (set_motors_speed(40, 90)). Sin embargo, set_motors_speed() satura a ±80. La velocidad efectiva es 80, no 90, sin ninguna indicación al desarrollador. |
| Impacto | Medio — Las aperturas RIGHT_ARC y LEFT_ARC no alcanzan la velocidad prevista en la fase inicial. La diferencia entre 90 y 80 puede afectar la trayectoria esperada de la apertura. |
| Mitigación | Los tiempos de las aperturas se calibraron empíricamente con el clamp ya activo, por lo que el comportamiento observado es el correcto. El problema es de mantenimiento: si alguien aumenta el clamp a 100, estas aperturas se comportarán de forma diferente. |
| Solución | Opción A: Cambiar 90 → 80 en control.c para que el código refleje el valor real. Opción B: Aumentar el límite de clamp a 100 y verificar que la placa FOC puede manejar ese rango. |
Control
CT-01 — Estrategia STEPS usa bucles busy-wait bloqueantes
| Archivos | control.c:162-168 |
| Descripción | strat_steps() contiene dos bucles while bloqueantes que detienen todo el sistema: 50 ms de avance (while (get_clock_ticks() - ms < 50)) seguidos de 10 ms de parada (while (get_clock_ticks() - ms < 60)). Durante estos períodos no se ejecuta ninguna otra lógica (detección de línea, LEDs, etc.). |
| Impacto | Medio — Durante los pulsos de movimiento (~10% del tiempo en STEPS), la detección de línea no se procesa en el bucle principal. La ISR del SysTick sigue ejecutándose (lee sensores y actualiza posición), por lo que la información se acumula, pero no se actúa sobre ella hasta que el bucle while termina. Si el robot pisa la línea durante un pulso de avance, no reaccionará hasta que el bloqueo termine (hasta 50 ms después). |
| Mitigación | La estrategia STEPS está diseñada para combate conservador. La probabilidad de alcanzar la línea en una ráfaga de 50 ms es baja. Si se detecta rival cercano, la estrategia cambia a PID inmediatamente. |
| Solución | Reescribir strat_steps() con una máquina de estados no bloqueante usando strat_steps_last_ms como referencia temporal, similar al patrón usado en las aperturas. |
Almacenamiento
EE-01 — eeprom_save bloquea el sistema durante ~500 ms
| Archivos | eeprom.c:6-25 |
| Descripción | eeprom_save() ejecuta un bucle busy-wait de 500 ms para el parpadeo de confirmación, seguido de borrado y escritura de flash. Durante este tiempo (~500 ms total) el sistema está completamente bloqueado. Las interrupciones no se deshabilitan, pero la ejecución del bucle principal se detiene. La escritura en flash se realiza con interrupciones activas; si la ISR del SysTick (que llama a update_sensors_readings()) o cualquier otra ISR ocurre durante el borrado/escritura, podría leer datos de la flash generando un bus fault o datos corruptos. |
| Impacto | Medio — La operación de guardado solo ocurre durante la configuración del mando RC5 (modo IDLE), no en competición. El riesgo de bus fault por acceso a flash durante escritura es bajo en STM32F4 (el core queda en espera durante las operaciones flash), pero las lecturas de DMA desde el bus AHB podrían verse afectadas. |
| Mitigación | La operación solo se ejecuta en IDLE. El tiempo de bloqueo es aceptable para una operación de configuración puntual. El ADC usa DMA para transferencias autónomas, por lo que no debería producirse pérdida de datos. |
| Solución | Deshabilitar interrupciones durante la operación flash (__disable_irq() / __enable_irq()). Reemplazar el bucle de parpadeo bloqueante por una máquina de estados que use el contador de ticks para parpadear sin bloquear. |
🟢 Leves
SW-02 — Buffer estático de 8 bytes en ISR de USART6 sin protección de desbordamiento
| Archivos | setup.c:130-148 |
| Descripción | La ISR de USART6 usa un buffer estático de 8 bytes (static char command[8]) con un índice i que se incrementa sin verificar límites. Si se reciben más de 7 caracteres sin un \n, se produce desbordamiento del buffer. Este es el mismo patrón documentado en OPRcontrolFOC como SW-01. |
| Impacto | Bajo — La placa FOC no envía respuestas largas por USART. Los comandos del FOC al main son confirmaciones cortas o inexistentes. El riesgo real es bajo en la práctica. |
| Mitigación | La placa FOC no genera tráfico significativo hacia la placa principal. El buffer de 8 bytes es suficiente para las respuestas esperadas. |
| Solución | Añadir verificación de límites: if (i < sizeof(command) - 1) { command[i++] = data; }. |
SW-03 — Debounce de botones usa delay bloqueante de 50 ms
| Archivos | buttons.c:9-13, buttons.c:21-25 |
| Descripción | get_start_btn() y get_menu_mode_btn() implementan debounce mediante doble lectura separada por delay(50). Cada llamada bloquea durante 50 ms, y en el caso del botón de menú se llama repetidamente desde bucles while anidados. |
| Impacto | Bajo — Los botones solo se consultan en estado IDLE (competición no iniciada), donde la latencia no es crítica. El retraso de 50 ms no afecta a la experiencia de usuario en la configuración. |
| Mitigación | El bucle principal ya tiene un delay(100), por lo que el delay adicional de 50 ms está dentro de un contexto ya lento. |
| Solución | Implementar debounce por timestamp: almacenar el momento del último cambio de estado y solo confirmar el nuevo estado tras N ms de estabilidad, sin bloquear. |
BL-01 — LED de sensor de línea sin apagado en set_sensor_led_debug
| Archivos | leds.c:177-185 |
| Descripción | Los casos LED_SENSOR_LINE_LEFT y LED_SENSOR_LINE_RIGHT en set_sensor_led_debug() solo manejan state == true (encienden LEDs), pero no tienen cláusula else para apagarlos. Una vez encendidos, no hay código que los apague individualmente. Adicionalmente, estos casos nunca se ejecutan en el flujo actual porque debug_sensors_leds() salta los sensores de línea. |
| Impacto | Bajo — Código muerto en la práctica actual. Si en el futuro se activa el debug de sensores de línea (líneas comentadas 36-41 en debug.c), los LEDs se encenderían pero no se apagarían, quedando permanentemente iluminados. |
| Mitigación | Las líneas que activarían el debug de línea están comentadas. La función set_sensor_led(false) apaga todos los LEDs de sensor como grupo. |
| Solución | Añadir cláusulas else para apagar los LEDs de línea, o eliminar los casos no utilizados si no se planea activar el debug de línea. |
SS-03 — Include de stdint.h con comillas en lugar de ángulos
| Archivos | sensors.h:7 |
| Descripción | sensors.h incluye "stdint.h" con comillas en lugar de <stdint.h> con ángulos. Aunque funciona (el compilador busca en los include paths del sistema como fallback), es técnicamente incorrecto y puede causar confusión. |
| Impacto | Bajo — Sin impacto funcional. El compilador resuelve correctamente el archivo. |
| Mitigación | No requiere acción inmediata. |
| Solución | Cambiar #include "stdint.h" por #include <stdint.h> en sensors.h:7. |
SW-04 — Múltiples TODOs sobre funcionalidad incompleta
| Archivos | control.c:8, control.c:38, control.c:267, control.c:287-289, control.c:315-316 |
| Descripción | El código contiene varios TODOs sin resolver: (1) estrategia por defecto hardcodeada a PID con nota de actualizar, (2) preocupación sobre referencias circulares en set_state(last_state), (3) migración de control_main_loop() a ISR independiente, (4) estrategia por defecto en caso de error no definida, (5) apertura por defecto en caso de error no definida. Además hay código de ajuste dinámico de sine_step comentado (líneas 143-156). |
| Impacto | Bajo — El comportamiento actual es funcional y seguro (motores se detienen en caso de error). Los TODOs representan deuda técnica y funcionalidad potencial no implementada. |
| Mitigación | Los casos default en los switches ponen velocidad 0, lo que es seguro. La estrategia PID por defecto es razonable para minisumo. |
| Solución | Priorizar y resolver los TODOs según necesidad. El ajuste dinámico de sine_step podría proporcionar más par a baja velocidad (rival cercano) y más velocidad punta cuando el rival está lejos. |
Issues en OPRcontrolFOC
La placa FOC tiene 13 issues documentados en su propia documentación:
OPRcontrolFOC — Problemas Conocidos.
Destacamos los más relevantes para UltiBot:
| ID |
Descripción |
Impacto en UltiBot |
| HW-01 |
SYSCLK declarado 84 MHz, real 72 MHz |
Los cálculos de velocidad pueden estar desfasados |
| CT-01 |
Control solo PI (KD=0) |
Respuesta de velocidad sin término derivativo |
| EG-01 |
Offsets de encoder +22/-61 hardcodeados |
Específicos del montaje en UltiBot |
| SW-01 |
Buffer USART3 de 8 bytes sin protección |
Misma vulnerabilidad que SW-02 en placa principal |
Documento generado el 2026-06-30. Ver también Arquitectura Software, Control, Sensores.