%% Referencias de código (no se renderizan en el PNG, solo para mantenimiento futuro)
%% Fuente: src/python/src/navigation/planning/sign_router/
%% selección: SignRouter._active_sign_candidates()
%% tabla de reglas por tramo/sentido: routing.py::ROUTING_TABLE
%% consulta de la regla: routing.py::pass_side_lateral_axis()
%% planificador de carril (LA RUTA QUE SE EJECUTA): planning/sign_lane.py::apply_sign_lanes()
%% deformación por waypoint (SUPRIMIDA en config): sign_router/deformation.py
%% anclaje opcional a la altura de la señal: SignRouter._pin_depth()
%% -
%% CORREGIDO 2026-09-10. Dos errores de fondo en la versión anterior:
%% -
%% 1) LA REGLA NO ES ABSOLUTA. Este diagrama decía "rojo siempre por afuera,
%% verde siempre por adentro, sin importar el sentido". Es al revés: la regla
%% (WRO 2026 9.19) es RELATIVA AL SENTIDO DE MARCHA -- el vehículo pasa a su
%% PROPIA DERECHA de un pilar rojo y a su PROPIA IZQUIERDA de uno verde. Las
%% 4 filas CLOCKWISE de ROUTING_TABLE son la NEGACIÓN de sus pares
%% COUNTERCLOCKWISE, precisamente por eso. La tabla fue absoluta entre
%% 2026-07-05 y 2026-09-03 (correcta en antihorario, al revés en TODA ronda
%% horaria) y el error quedó oculto porque scenario_simulator/scoring.py
%% juzgaba con la misma convención que el router conducía. Todo conteo de
%% pass-side anterior a 2026-09-03 está medido con la regla equivocada.
%% Corolario: sin dirección asentada la regla no es evaluable en absoluto
%% (pass_side_lateral_axis devuelve None) -- ver esquiva-generica.mmd.
%% -
%% 2) LO QUE SE EJECUTA ES EL CARRIL, NO LA DEFORMACIÓN. El diagrama dibujaba
%% _apply_deformation() como el mecanismo. En la config que embarca
%% (src/config/navigation/signs/sign_router.toml) sign_lane_planner = true y
%% sign_lane_suppress_deform = true, así que quien mueve la trayectoria es
%% apply_sign_lanes(): un carril lateral con meseta plana y rampas, no un
%% empujón de un único punto de paso.
%% -
%% Constantes (src/config/navigation/signs/sign_router.toml):
%% activation_dist_m = 1.40 / passed_dist_m = 1.60 -- ventana de la maniobra
%% sign_lane_ramp_m = 0.9 / sign_lane_hold_m = 0.25 -- forma del carril
%% sign_lane_corner_entry_m = 0.5 -- arco de curva que el carril puede tomar
%% sign_clearance_margin_m = 0.10 -- margen sobre semianchos chasis+señal
%% commit_hysteresis = true -- una vez comprometida, se mantiene ESA señal
%% -
%% ADVERTENCIA DE ALCANCE (medido 2026-09-09, sobre bag de hardware): el carril
%% está dimensionado para 1.40 m de anticipación y NUNCA la recibe. La cámara
%% ve la señal a 0.66 m y el router se compromete a 0.38 m, así que la maniobra
%% se ejecuta con una fracción del recorrido para el que fue diseñada. No es un
%% fallo de la regla ni del carril: es falta de distancia.
%% -
%% Diagrama en TD (vertical) a propósito -- en LR la cadena de nodos quedaba
%% demasiado ancha y el texto se veía borroso al escalar el PNG.
%% El nodo con borde punteado (Pin) marca un paso opcional, no obligatorio.
flowchart TD
Sign["La cámara detecta
una señal de tránsito"] --> Active["Se elige la señal activa
más cercana que el robot
aún no ha pasado"]
Active --> Commit["Una vez elegida, se mantiene ESA
señal hasta superarla, en vez de
recalcular la más cercana cada tick"]
Commit --> Dir{"¿Está asentado el
sentido de la vuelta?"}
Dir -- "No" --> NoRule(["La regla de paso NO es evaluable.
Se esquiva como obstáculo genérico
(ver esquiva-generica.mmd)"])
Dir -- "Sí" --> Lookup["Busca en la tabla de reglas
el tramo de la pista y el
sentido de la vuelta actuales"]
Lookup --> Color{"¿De qué
color es?"}
Color -- "ROJA" --> Right["Pasar por la DERECHA
del robot"]
Color -- "VERDE" --> Left["Pasar por la IZQUIERDA
del robot"]
Right --> Note>"La regla se define respecto al ROBOT,
no a la pista. Antihorario: su derecha
es la pared exterior. Horario: su derecha
es el cuadro central. Por eso la tabla
tiene filas opuestas para cada sentido"]
Left --> Note
Note --> Lane["Traza un CARRIL lateral hacia ese lado:
meseta plana de 0.25 m a la altura de la
señal, con rampas de 0.9 m a cada lado
que vuelven a la línea central"]
Lane --> Taper["El carril se activa de forma progresiva
entre 1.40 m y 1.60 m de distancia,
para evitar cambios bruscos de dirección"]
Taper --> Pin["Ancla el punto justo
a la altura de la señal"]:::optional
Pin --> Steer["El carril se usa como
objetivo de dirección"]
classDef optional stroke-dasharray: 5 5