SPEC — Perception¶
- Estado: congelado en Fase 0
- ADRs principales: ADR-0008 (mecanismo), ADR-0009 (política), ADR-0010 (catálogo revisado + acoplamiento), ADR-0011 (vetos T0 sobre piloto)
- Versión del contrato:
PERCEPTION_PROTOCOL_VERSION = 1
1. Responsabilidades¶
El módulo perception/ es la única capa que observa el mundo y emite estimaciones derivadas de sensores. Sus responsabilidades:
- Consumir
SensorSample[T]del HAL. - Producir
Estimate[T]convalidity,covariance,sourceystamp_sim_nscorrectos (perdocs/specs/uncertainty.md). - Detectar y reportar transiciones de
PerceptionMode. - Mantener canales de telemetría
/perception/{producer_id}/*y/perception/mode.
No es responsabilidad de perception/:
- Estimar el estado completo del vehículo (
state.aggregator, EKF — verdocs/specs/state.md). - Construir o consultar un mapa global (eso es SLAM en Fase 4).
- Decidir qué hace el dron ante degradación (ADR-0009 /
mission/). - Sintetizar comandos para actuadores (
actuators/).
2. Productores perceptuales¶
Cada productor implementa el contrato PerceptionProducer[T] y vive en su propio módulo. Los productores de Fase 3–4 son:
class PerceptionProducer(Protocol, Generic[T]):
producer_id: str
nominal_rate_hz: float
nominal_covariance_envelope: np.ndarray
max_age_ns: int
def step(self, samples: Sequence[SensorSample[Any]], now_ns: int) -> Estimate[T] | None: ...
def health(self) -> ProducerHealth: ...
| Producer | producer_id |
Output | Tasa | Fase de aparición |
|---|---|---|---|---|
| Inercial preintegrada | imu.preint |
PreintegratedIMU |
200 Hz | Fase 3 |
| Visual odometry frontal | vo.front |
Estimate[Pose] (delta-pose) |
30 Hz | Fase 3 |
| Profundidad estéreo (si stereo presente) | depth.stereo |
Estimate[DepthMap] |
15 Hz | Fase 4 |
| Altímetro fusionado | altimeter.fused |
Estimate[float] (altitud AGL) |
50 Hz | Fase 3 |
| Detector de features | features.fast |
Estimate[FeatureSet] |
30 Hz | Fase 3 |
| Loop closure | slam.loop |
Estimate[LoopHypothesis] |
event-driven | Fase 4 |
| Detector de luminancia | light.luminance |
Estimate[float] |
5 Hz | Fase 3 |
| Detector de motion-aggressive | motion.aggressive |
Estimate[AggressiveMetric] |
50 Hz | Fase 3 (añadido por ADR-0010) |
Cualquier productor nuevo debe declararse en este spec antes de añadir su implementación; producers no documentados están prohibidos en el bus.
3. Detección de modos perceptuales¶
La detección de PerceptionMode (ADR-0008 §3) se centraliza en perception.mode_detector.PerceptionModeDetector. Este componente:
- Suscribe a los canales de telemetría de cada productor (validity, métricas internas).
- Evalúa, con la cadencia de
nominal_hold_ms(default 200 ms), las condiciones de entrada/salida de cada modo perdocs/specs/uncertainty.md§7. - Mantiene una FSM con las transiciones permitidas (§4).
- Publica
PerceptionModeChangeden el bus al cambiar de modo.
El detector no decide comportamiento; solo nombra el modo. La política la consume mission/ y T2/T1 (ver docs/specs/mission.md).
4. Transiciones permitidas y endurecimiento de FSM¶
Las transiciones entre modos son una FSM dirigida. No todas las transiciones son legales; el detector las restringe para evitar oscilación y para forzar paso por estados intermedios.
┌──────────────────────────────────────────┐
│ ▼
NOMINAL ───► LOW_TEXTURE ────────► VIO_LOST ────────► PERCEPTION_DEAD
│ │ │ │
│ ▼ │ │
├──► LOW_LIGHT ────────────────────┐ │ │
│ │ ▼ ▼ │
├──► IMU_SATURATION ──────────► (cualquiera) │
│ │
├──► MOTION_AGGRESSIVE ─► LOW_TEXTURE (degradación) │
│ │
└──► MAP_AMBIGUOUS ◄──────────────► NOMINAL │
│
NOMINAL ◄──── (cualquiera, tras recovery sostenido) ◄──────┘
4.1 Doble condición de transición (hold + K muestras)¶
Cada transición legal está gobernada por dos condiciones simultáneas que deben cumplirse:
- Hold temporal: la condición de entrada (o salida) del modo se sostiene durante
window_ms(defaultnominal_hold_ms = 200 ms). - K muestras consecutivas: la condición se cumple en al menos
k_consecutivemuestras seguidas del productor relevante, sin gap, sin alternancia. Defaults por modo endocs/specs/uncertainty.md§7.
Ambas condiciones operan en conjunción. Si la señal cruza el umbral cada 3 frames a 30 Hz, hold se cumple pero K no — la transición no dispara. Esto cierra el agujero de oscilación bajo señal realista identificado en docs/reviews/uncertainty_red_team_review.md §2.3.
4.2 Reglas adicionales¶
- De
NOMINALa cualquier degradado: doble condición debe cumplirse; default 200 ms + K muestras. - De un modo degradado a
NOMINAL: requiere paso por el modo padre menos restrictivo y doble condición sobre el envelope nominal (nominal_hold_ms+nominal_k_consecutive). IMU_SATURATION,MOTION_AGGRESSIVEyPERCEPTION_DEADno pasan aNOMINALen un solo paso; exigennominal_hold_ms × 2(default 400 ms) ynominal_k_consecutivepara evitar histeresis pobre.- Transiciones a
PERCEPTION_DEADson siempre permitidas desde cualquier estado y bypasean la doble condición (input es la única consideración cuando la situación es crítica). - Salir de
PERCEPTION_DEADexigeVALIDsostenido en todos los productores durantenominal_hold_ms × 2ynominal_k_consecutive × 2. - Si
MOTION_AGGRESSIVEpersiste más allá deaggressive_recovery_timeout_ms(default 2000), el detector emite transición aLOW_TEXTUREpor degradación sostenida (per ADR-0010 §1).
4.3 Garantía contra oscilación¶
El detector debe pasar el test test_fsm_no_oscillation_under_alternating_signal (especificado en uncertainty.md §11): una señal sintética alternante a frecuencia 0.45 / window_ms no produce más de 2 transiciones por minuto. Cualquier configuración (override de window_ms o k_consecutive) que rompa esta garantía es rechazada en CI por test parametrizado.
5. Métricas mínimas por productor¶
Cada productor publica las siguientes métricas a /perception/{producer_id}/metrics a su tasa nominal. Son las que el detector consume y son obligatorias.
5.1 vo.front¶
feature_count: int— features tracked en el último frame.mean_track_length: float— promedio de longitud de track sobre features activas.innovation_norm: float— norma de innovación del frame.frames_since_last_keyframe: int.gate_pass: bool— si el frame pasó el gate de innovación.
5.2 imu.preint¶
accel_axis_saturation: tuple[float, float, float]— fracción de full scale por eje.gyro_axis_saturation: tuple[float, float, float].bias_drift_rate: float— rate de cambio reciente del bias estimado.
5.3 light.luminance¶
mean_luminance: float ∈ [0, 1].agc_gain_frac: float ∈ [0, 1]— fracción de ganancia AGC en uso.agc_saturated: bool.
5.4 slam.loop¶
best_candidate_score: float.second_best_score: float.ambiguity_margin: float—(best − second) / max(best, eps).
5.5 altimeter.fused¶
agreement_residual_m: float— desacuerdo entre fuentes de altitud (barómetro, ToF, GT).
5.6 motion.aggressive (añadido por ADR-0010)¶
commanded_rate_rps_max: float— máximo absoluto entre ejes del comando de body rate recibido en la última ventana.measured_accel_mps2_excl_g: float— magnitud de aceleración medida en cuerpo excluyendo gravedad.degraded_producers_in_window: tuple[str, ...]— IDs de productores que reportaronDEGRADEDo peor en la misma ventana (vo.front,imu.preint, etc.).entry_condition_met: bool— true cuando rate o accel exceden threshold ylen(degraded_producers_in_window) ≥ 1.
Este productor es derivado: no consume sensores directamente, lee comandos del bus (/cmd/*) y telemetría de otros productores. Su latencia debe ser menor que aggressive_window_ms / 4 para que el detector pueda actuar dentro del hold.
Productores futuros declaran sus métricas en este spec antes de añadirlas al detector.
6. Contratos vinculantes¶
- Sin GPS en
perception/. Productores que consumen GPS están deshabilitados por default en todos los scenarios (docs/specs/sensors.md); habilitarlos requiere flag scenarioenable_gps: true. No hay productorgps.*por defecto en Fase 3+. - Sin estado del vehículo. Productores no consumen
VehicleState. Toda la información viene deSensorSampley de su propio buffer interno. - Determinismo. Para un mismo seed y misma secuencia de
SensorSample, todo productor genera la misma secuencia deEstimatebit-a-bit (ADR-0002). Aleatoriedad solo viaRandomSource.child(producer_id). - Sealing de arrays. Aplica regla de
core.uncertainty(§3.2 deuncertainty.md). - Sin asignación de modo. Un productor reporta su
validityy sus métricas; no decide el modo global. Eso lo hacePerceptionModeDetector. - Telemetría completa. Toda métrica de §5 que un productor expone es persistida en MCAP. Submuestreo opcional para canales pesados (ej. histogramas de features) documentado en
docs/specs/telemetry.md. - Latencia documentada. Cada productor declara su latencia p50/p99 en su docstring y la mide; superar 2× el target documentado emite
WARN.
7. Restricciones¶
- Prohibido importar
mission/oactuators/desdeperception/. - Prohibido publicar
Estimateconsource.kind == "groundtruth"desde productores derivados de sensores; GT solo viene del backend de simulación. - Prohibido
time.time()otime.monotonic(); usarSimClockactivo. - Prohibido emitir
validity == VALIDcuando un criterio de §5 está fuera de envelope nominal del productor (sería contradicción conuncertainty.md§3.6). - Prohibido fusionar productores dentro de
perception/. La fusión vive enstate/(EKF) y el resultado es un únicoEstimate[NavigationState]conkind="fused".
8. Pruebas obligatorias¶
| Test | Cubre |
|---|---|
test_producer_outputs_estimate_only |
§6.1, §6.5 |
test_producer_deterministic_with_seed |
§6.3 |
test_mode_detector_transitions_obey_fsm |
§4 |
test_mode_detector_publishes_event_on_change |
§3, ADR-0008 §4 |
test_mode_detector_no_oscillation_under_hysteresis |
§4 (recovery time) |
test_low_texture_entry_at_threshold |
§3 + uncertainty.md §7 |
test_motion_aggressive_requires_command_and_degradation |
§5.6 + ADR-0010 §1 |
test_fsm_double_condition_blocks_oscillation |
§4.1 + §4.3 |
test_perception_does_not_import_mission_or_actuators |
§7 |
test_gps_producer_disabled_by_default |
§6.1 |
Cobertura objetivo del módulo perception/: > 85 %.
9. Lo que NO está en Fase 0 (pero el spec lo prepara)¶
- VO real: aparece en Fase 3 (T3 del research track U1–U6, ver
docs/roadmaps/research_track_uncertainty.md). - Loop closure y SLAM: Fase 4.
- Productores aprendidos (ML): solo como complemento opt-in en Fase 6+, dentro del mismo envelope
Estimate[T]. - Multi-cámara fusionada: no comprometido; sería un productor adicional documentado aquí cuando exista.