Files
gps-denied-onboard/src/gps_denied_onboard/components/c1_vio/interface.py
T
Oleksandr Bezdieniezhnykh 6c7d24f7e0 [AZ-331] C1 VioStrategy: Protocol + DTOs + factory + C5 migration
Freezes the c1_vio Public API per
_docs/02_document/contracts/c1_vio/vio_strategy_protocol.md v1.0.0:

- VioStrategy Protocol (4 methods: process_frame, reset_to_warm_start,
  health_snapshot, current_strategy_label) in
  components/c1_vio/interface.py.
- DTOs (VioOutput, VioHealth, FeatureQuality, WarmStartPose) + VioState
  enum in _types/nav.py — L1 placement so C5 + C13 consume them without
  crossing the components.* boundary (AZ-270 AC-6). The new VioOutput
  shape (frame_id: str, relative_pose_T: gtsam.Pose3,
  pose_covariance_6x6, imu_bias, feature_quality, emitted_at_ns)
  replaces the AZ-263 scaffolding in _types/vio.py, which is now
  deleted.
- VioError family (VioInitializingError / VioDegradedError /
  VioFatalError) in components/c1_vio/errors.py. Documented
  rationale: the degraded-operation path returns a VioOutput with
  inflated covariance + VioHealth.state=DEGRADED rather than raising
  VioDegradedError — the error type exists only for the rare
  degraded->fatal transition.
- C1VioConfig per-component config block (strategy enum,
  lost_frame_threshold default 9, warm_start_max_frames default 5)
  with constructor-time validation rejecting unknown strategy labels.
- StrategyNotAvailableError added to runtime_root/errors.py;
  composition-time error distinct from the VioError family.
- Composition-root factory build_vio_strategy in
  runtime_root/vio_factory.py with three BUILD_* gates (BUILD_OKVIS2,
  BUILD_VINS_MONO, BUILD_KLT_RANSAC). Concrete strategy modules are
  imported lazily via __import__ AFTER the flag check — Tier-0
  workstation builds with the flag OFF MUST NOT load the strategy
  module (Risk-2 / I-5; verifiable via sys.modules).
- 36 conformance tests cover all 9 ACs + NFR-perf-factory
  (p99 build under 200 ms x 1000 calls) + NFR-reliability-error-family.
  AC-8 introspects the contract file's Shape table and asserts method
  parity against the runtime Protocol; AC-9 asserts the frame_id
  annotation is 'str' (PEP-563 stringified).

C5 migration (consumers of the new VioOutput shape):
- gtsam_isam2_estimator.py + eskf_baseline.py: replaced
  vio.timestamp -> vio.emitted_at_ns (drops _datetime_to_ns on the
  VIO path), vio.pose_se3 -> vio.relative_pose_T (gtsam.Pose3 direct;
  drops _pose_se3_to_gtsam / _pose_se3_to_array), vio.covariance_6x6
  -> vio.pose_covariance_6x6 (rename).
- key_for_frame signature widened to UUID | int | str to accept the
  new str frame_id.
- 4 C5 test files migrated to the new VioOutput shape with helper
  fixtures producing ImuBias + FeatureQuality + str frame_id.
- c5_state/interface.py TYPE_CHECKING import path updated.

Bootstrap healthcheck + test_types_importable updated to drop the
deleted _types/vio module and pick up _types/inference (AZ-297) in
the same sweep.

Full unit-test sweep: 884 passed, 2 pre-existing environment skips
(cmake, actionlint).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 04:44:31 +03:00

95 lines
3.2 KiB
Python

"""C1 ``VioStrategy`` Protocol (AZ-331).
PEP 544 ``typing.Protocol`` with ``runtime_checkable=True``; four
methods that span the camera-ingest hot path
(``process_frame``), F8 reboot recovery (``reset_to_warm_start``),
diagnostics (``health_snapshot``), and self-identification
(``current_strategy_label``).
Concrete impls — :class:`Okvis2Strategy` (AZ-332),
:class:`VinsMonoStrategy` (AZ-333), :class:`KltRansacStrategy`
(AZ-334) — live in sibling modules and are imported lazily by
:mod:`gps_denied_onboard.runtime_root.vio_factory`.
The contract at
``_docs/02_document/contracts/c1_vio/vio_strategy_protocol.md``
v1.0.0 is the authoritative shape; this module mirrors it 1:1.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Literal, Protocol, runtime_checkable
if TYPE_CHECKING:
from gps_denied_onboard._types.calibration import CameraCalibration
from gps_denied_onboard._types.nav import (
ImuWindow,
NavCameraFrame,
VioHealth,
VioOutput,
WarmStartPose,
)
__all__ = ["VioStrategy"]
@runtime_checkable
class VioStrategy(Protocol):
"""On-Jetson visual / visual-inertial odometry runtime.
Implementations:
:class:`Okvis2Strategy` (production-default, OKVIS2 SLAM),
:class:`VinsMonoStrategy` (research-only),
:class:`KltRansacStrategy` (mandatory simple-baseline per ADR-002).
Selection is owned by the composition root.
Invariants (see ``vio_strategy_protocol.md`` v1.0.0):
- Single-threaded per instance (one camera-ingest writer thread).
- ``current_strategy_label()`` is constant per instance and
equals ``config.vio.strategy`` exactly.
- Error envelope is closed — only members of
:class:`VioError` escape ``process_frame``.
"""
def process_frame(
self,
frame: "NavCameraFrame",
imu: "ImuWindow",
calibration: "CameraCalibration",
) -> "VioOutput":
"""Camera-ingest hot-path call (one per nav-camera frame).
``VioOutput.frame_id`` MUST equal ``frame.frame_id`` (C5
alignment invariant). Raises :class:`VioInitializingError`
while booting (state == INIT; no output emitted) and
:class:`VioFatalError` once ``consecutive_lost`` exceeds the
configured threshold. During DEGRADED operation the method
returns normally with an inflated covariance — NOT raises
:class:`VioDegradedError`.
"""
...
def reset_to_warm_start(self, hint: "WarmStartPose") -> None:
"""Destructive re-init from an F8-reboot warm-start hint.
Clears keyframe window, IMU integration state, feature
tracks. Subsequent ``process_frame`` calls re-initialise from
the hint. Raises :class:`VioFatalError` only on irrecoverable
backend init failure.
"""
...
def health_snapshot(self) -> "VioHealth":
"""Most-recent health state — FDR-stamped per the AC-NEW-3 audit."""
...
def current_strategy_label(
self,
) -> Literal["okvis2", "vins_mono", "klt_ransac"]:
"""Identify which concrete strategy is wired here.
Returned string equals ``config.vio.strategy`` exactly.
AC-NEW-3 FDR audit relies on this property.
"""
...