mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 19:51:13 +00:00
[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>
This commit is contained in:
@@ -1,20 +1,94 @@
|
||||
"""C1 `VioStrategy` Protocol.
|
||||
"""C1 ``VioStrategy`` Protocol (AZ-331).
|
||||
|
||||
Concrete strategies: OKVIS2 (default), VINS-Mono (research-only), KLT/RANSAC
|
||||
(mandatory simple baseline). See `_docs/02_document/components/01_c1_vio/`.
|
||||
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 Protocol
|
||||
from typing import TYPE_CHECKING, Literal, Protocol, runtime_checkable
|
||||
|
||||
from gps_denied_onboard._types.nav import ImuWindow, NavCameraFrame
|
||||
from gps_denied_onboard._types.vio import VioOutput
|
||||
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):
|
||||
"""Visual-Inertial-Odometry strategy."""
|
||||
"""On-Jetson visual / visual-inertial odometry runtime.
|
||||
|
||||
def step(self, frame: NavCameraFrame, imu: ImuWindow) -> VioOutput:
|
||||
"""Process a single nav-camera frame + IMU window and return a VIO update."""
|
||||
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.
|
||||
"""
|
||||
...
|
||||
|
||||
Reference in New Issue
Block a user