Files
gps-denied-onboard/_docs/03_implementation/batch_54_cycle1_report.md
T
Oleksandr Bezdieniezhnykh ceb24b5a62 [AZ-334] C1 KLT/RANSAC strategy — engine-rule simple-baseline VIO
Implement KltRansacStrategy, the ADR-002 engine-rule mandatory
simple-baseline VioStrategy for E-C1. Pure-Python facade over
OpenCV's cv2.goodFeaturesToTrack / calcOpticalFlowPyrLK /
findEssentialMat / recoverPose pipeline — no C++/pybind11 binding
by design so a Tier-0 workstation runs the strategy with
`pip install opencv-python` and the BUILD_KLT_RANSAC=ON gate alone.
Constructor + state machine + FDR transition spine mirror
Okvis2Strategy + VinsMonoStrategy so the AZ-331 factory + IT-12
comparative harness treat all three as drop-in substitutable; the
duplication is the consolidation target now formally in scope for
the next cumulative review (batches 52-54).

AC coverage: AC-1..AC-11 + NFR-perf mapped to passing tests
(25 tests, 23 pass + 2 tier-2 skipped on dev/CI runners; all 25
pass under GPS_DENIED_TIER=2). Honest-covariance invariant (AC-9)
implemented as residual-scatter / (N_inliers - 5) with an inlier-
count penalty — no client-side floor or smoother; cov Frobenius
grows monotonically across DEGRADED. Camera-agnostic source
(AC-11) enforced by CI-grep gate that excludes docstring text.

Test-Run Cadence: focused suite tests/unit/c1_vio/ green (95 passed,
6 skipped); config-loader + compose-root suites green; full-suite
gate deferred to Step 16 per implement skill.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 02:40:01 +03:00

7.7 KiB

Batch 54 — Cycle 1 Report

Date: 2026-05-14 Tasks: AZ-334 (C1 KLT/RANSAC Strategy) Verdict: COMPLETE — PASS_WITH_WARNINGS

Summary

Implemented KltRansacStrategy, the ADR-002 engine-rule mandatory simple-baseline VIO for C1. Pure-Python facade over OpenCV's cv2.goodFeaturesToTrack / cv2.calcOpticalFlowPyrLK / cv2.findEssentialMat / cv2.recoverPose pipeline — no C++/pybind11 binding by design so a Tier-0 workstation can run the strategy with pip install opencv-python and the AZ-331 factory's BUILD_KLT_RANSAC=ON gate. Mirrors the AZ-332 OKVIS2 + AZ-333 VINS-Mono facade pattern on the orchestration spine so the AZ-331 factory + IT-12 comparative harness treat all three strategies as drop-in substitutable.

Files added / modified

Added (3)

  • src/gps_denied_onboard/components/c1_vio/klt_ransac.py — Python facade, ~770 lines.
  • tests/unit/c1_vio/test_klt_ransac_strategy.py — AC-1..AC-11 + NFR-perf + KltRansacConfig validation tests, ~990 lines, 25 tests (23 pass + 2 tier-2 skipped on dev/CI runners).
  • _docs/03_implementation/reviews/batch_54_review.md — code review report.

Modified (4)

  • src/gps_denied_onboard/components/c1_vio/config.py — add KltRansacConfig dataclass + klt_ransac field on C1VioConfig; __all__ updated. (Pre-existing KNOWN_STRATEGIES already had klt_ransac from AZ-331.)
  • src/gps_denied_onboard/components/c1_vio/__init__.py — export KltRansacConfig.
  • cpp/klt_ransac/CMakeLists.txt — replace AZ-263 placeholder with a deliberate "pure-Python; no native target" STATUS message so the build graph stays symmetric with cpp/okvis2/ and cpp/vins_mono/ while the BUILD_KLT_RANSAC=ON flag only gates the Python module import at the AZ-331 composition-root factory.
  • tests/unit/c1_vio/test_protocol_conformance.py — introduce the _STRATEGIES_WITHOUT_NATIVE_BINDING category and route klt_ransac through it inside test_ac5_build_vio_strategy_flag_on_but_module_missing so the parametrised factory test correctly handles the pure-Python shape (no native binding to fail on; the construction should succeed and return a VioStrategy instance instead).

AC coverage (AC-1..AC-11 + NFR-perf)

All 11 ACs + NFR-perf mapped to passing tests (see batch_54_review.md Phase 2 table). AC-9 + NFR-perf are tier-2-tagged per the task spec; they skip on macOS dev + GitHub Actions Linux runner with Tier-2-only test; set GPS_DENIED_TIER=2 to run. AC-3 / AC-6 / AC-9 / AC-10 / AC-11 / NFR-perf monkeypatch cv2.findEssentialMat + cv2.recoverPose + RansacFilter.filter_correspondences to deterministic values so the unit suite exercises the FACADE's state machine without depending on real OpenCV geometry on synthetic correspondences; real-geometry validation lives in C1-IT-12 (Jetson Tier-2 fixture).

Test results

Focused suite — tests/unit/c1_vio/

95 passed, 6 skipped in 1.67s

The 6 skips are the 2 OKVIS2 tier-2 tests + the 2 VINS-Mono tier-2 tests + the 2 new KLT/RANSAC tier-2 tests (test_ac9_* and test_nfr_perf_*).

Adjacent regression — config / compose-root

17 passed in 1.96s

(test_az269_config_loader.py, test_az270_compose_root.py — all green; the new KltRansacConfig registration did not break the schema-loader or compose-root layered-import guards.)

Tier-2 verification

GPS_DENIED_TIER=2 pytest tests/unit/c1_vio/test_klt_ransac_strategy.py
→ 25 passed in 0.43s

All 25 tests pass under the tier-2 gate (including AC-9 honest- covariance monotonicity over 48 synthetic frames + NFR-perf p95 record).

Full suite

Deferred per the implement skill's Test-Run Cadence — the full unit-suite gate runs exactly once at Step 16 (end of implementation phase), not per-batch. Focused tests + cumulative review (every K batches) catch cross-batch regressions before then.

Architectural decisions

  • No native binding by design: KLT/RANSAC is pure Python over OpenCV's Python bindings. The cpp/klt_ransac/CMakeLists.txt placeholder is preserved (with an explanatory STATUS message) for build-graph symmetry with cpp/okvis2/ and cpp/vins_mono/; the BUILD_KLT_RANSAC=ON flag only gates the Python module import at the AZ-331 composition-root factory.
  • Constructor shape matches factory: KltRansacStrategy(config, *, fdr_client, clock=None) mirrors Okvis2Strategy + VinsMonoStrategy so the AZ-331 factory invokes all three via the same call shape. The task spec's illustrative constructor (with explicit injection of CameraCalibration / ImuPreintegrator / RansacFilter / Logger) was deliberately not adopted because it would diverge from the existing factory contract; the same dependencies are resolved internally instead — RansacFilter is static (AZ-282 stateless helper) and ImuPreintegrator is constructed lazily on the first process_frame call (it needs the per-call CameraCalibration which is not available at construction time).
  • Honest covariance, AC-9 compliant: per-frame covariance = np.eye(6) * (sigma_sq + inlier_penalty) / max(inlier_count - 5, 1) where sigma_sq = median_residual_px**2 and inlier_penalty = threshold_px / max(inlier_count, 1). No client-side floor or smoother; the formula grows monotonically as inliers drop or residuals scatter. Tier-2 test walks 48 scripted-inlier frames through the DEGRADED window and verifies monotonicity.
  • Camera agnostic, AC-11 enforced: no adti20 / adti26 literals in executable source (docstring mentions are excluded from the CI grep via AST-aware stripping in the test). The per-call CameraCalibration argument carries intrinsics; the same code path produces sensible VioOutput for two distinct calibrations (different f, cx, cy).
  • State machine mirrors OKVIS2 / VINS-Mono: INIT until warm_start_max_frames is exhausted, then TRACKING if inlier count ≥ min_features_for_pose, else DEGRADED; sustained pose-recovery failure for lost_frame_threshold consecutive frames raises VioFatalError with state == LOST. Exactly one vio.health FDR record per transition (AC-10).
  • Error envelope closed: every OpenCV cv2.error is caught at each call site and rewrapped into VioFatalError with __cause__ chaining (AC-4). RansacFilterError + ImuPreintegrationError are also caught and rewrapped.

Out of scope / deferred

  • Real geometry validation against Derkachi fixtures — Tier-2 follow-up; C1-IT-12 binds KLT/RANSAC alongside OKVIS2.
  • Warm-start hint persistence (AZ-335) — separate batch.
  • Strategy-facade consolidation hygiene PBI — now formally in scope for the cumulative review covering batches 52-54 (next trigger). All three strategy shapes (OKVIS2, VINS-Mono, KLT/RANSAC) are now visible so the right factoring (template-method base class for the orchestration spine + per-strategy geometry hook) can be scoped without over-fitting.

Honest-covariance numeric envelope (AC-9 evidence)

Tier-2 test walks the following inlier-count sequence through the DEGRADED gate (min_features_for_pose=50):

Frame range Inlier count State Cov Frobenius (approx)
1 first-frame INIT 24.49 (10·√6)
2..29 80 TRACKING ≈ 0.0056
30 40 DEGRADED ≈ 0.013
31..43 35..12 DEGRADED strictly increasing
44..49 10 DEGRADED ≈ 0.127

The Frobenius norm grows monotonically across the entire DEGRADED window — the AC-9 honest-covariance invariant is upheld by the formula's structure, not by a floor clamp.