%% Referencias de código (no se renderizan en el PNG, solo para mantenimiento futuro) %% Fuente: src/python/src/navigation/core_navigator/ %% CoreNavigator.step() -- evalúa el riesgo en cada tick %% _handle_stuck_escape() -- maniobra de desatasco %% control/controllers/collision_avoidance_controller.py -- nivel de riesgo (RiskLevel) %% control/controllers/stuck_detector.py -- StuckDetector %% Constantes: src/config/navigation/escape/escape.toml %% side_correction_steer_deg = 16.5 / side_correction_s = 0.20 (amenaza lateral) %% rev_steer_deg = 44.0 / rev_speed = -0.20 (giro en K) %% k_turn_min_s = 0.54 (riesgo OBSTACLE) / k_turn_max_s = 1.08 (riesgo CRITICAL) %% max_escape_s = 1.8 -- TOPE, no duración: casi nunca se alcanza %% stuck_timeout_s = 2.0 / stuck_move_threshold = 0.03 %% escalate_after_attempts = 3 / escape_side_commit_attempts = 2 %% Común a Open Challenge y Obstacle Challenge -- en Obstacle Challenge, el %% LIDAR ya llega con el eco de las señales enmascarado (ver regla-senales.mmd), %% así que esta vigilancia opera igual en ambas pistas. %% Esquiva genérica ante obstáculo -- detalle común en: %% schemes/flowcharts/common/mermaid/esquiva-generica.mmd %% - %% AMPLIADO 2026-09-10 con lo medido sobre el corpus y sobre bags de hardware. %% El diagrama anterior tenía una sola caja "esquiva" y ocultaba que hay DOS %% maniobras distintas con criterios distintos. Lo que se sabe hoy: %% - %% * DÓNDE SALTAN: el 81% de los escapes se dispara con la amenaza a 45-90 grados, %% es decir en las ESQUINAS DELANTERAS del chasis, no de frente. La geometría %% del disparo importa más que el umbral. %% - %% * EL GIRO EN K ELIGE SIN SABER: es el único escape que ignora lo que el robot %% ya sabe. Elige su LADO sin consultar el plan (se opone al plan el 57% de las %% veces, contra el 11% de la corrección lateral) y su DISTANCIA sin mirar el %% espacio (son segundos fijos escogidos por el riesgo DE FRENTE: el caso %% crítico pide 21.6 cm de recorrido contra un hueco TRASERO de 17 cm en la %% mediana y 7 cm en el p10). El 24% de los retrocesos NO CABEN detrás. %% - %% * EL RETROCESO GIRA A MEDIO VOLANTE: rev_steer_deg = 44 grados de 85 %% disponibles, y cada episodio rota solo 14.4 grados. El tope max_escape_s NO %% ES LA RESTRICCIÓN (se usan 0.75 s de 1.8), así que la única forma de ganar %% rotación es ÁNGULO, no tiempo. Un tope no es una duración. %% - %% * EL BUCLE NO ES "SE SOLTÓ DEMASIADO PRONTO": medido, el patrón es "se liberó, %% y volvió a meterse". Solo 1 de 52 redisparos ocurre antes de 1.0 s, la %% holgura mediana al soltar es 0.545 m, y aun así el robot recorre 4-25 cm y %% vuelve a chocar. Afinar el escape es ESFUERZO PERDIDO; lo que hay que %% arreglar es la TRAYECTORIA a la que vuelve. flowchart TD Risk{"¿El LIDAR detecta
un peligro adelante?"} Risk -- "No" --> Stuck{"¿Lleva 2 s sin
avanzar 3 cm?"} Risk -- "Sí" --> Where{"¿Dónde está
la amenaza?"} Where -- "A un lado
(amenaza lateral)" --> Side["Corrección lateral:
0.2 s de volante suave
sin dejar de avanzar"] Where -- "De frente
(81% llega a 45-90 grados,
o sea por las esquinas
delanteras del chasis)" --> KTurn["Giro en K: retrocede
girando a medio volante
durante 0.54-1.08 s"] KTurn --> Fit>"PUNTO DÉBIL MEDIDO: la duración se
elige por el riesgo DE FRENTE, sin mirar
el hueco DE ATRÁS. El 24% de estos
retrocesos no caben detrás"] Side --> Resume Fit --> Attempts{"¿Van 3 escapes
seguidos?"} Attempts -- "Sí" --> Escalate["Escala: alarga la maniobra
y prueba el otro lado"] Attempts -- "No" --> Resume Escalate --> Resume Stuck -- "Sí" --> StuckEscape["Retrocede para
desatascarse"] Stuck -- "No" --> Resume StuckEscape --> Resume(["Sigue conduciendo
con normalidad"]) Resume --> Back>"Y aquí está el problema real: el escape
SÍ libera al robot (holgura mediana de
0.545 m al soltar), pero la trayectoria a
la que vuelve lo mete otra vez. Se vuelve
a chocar tras 4-25 cm"]