Skip to content

Engineering Status

This page is the single place where the documentation connects to the live engineering threads. Everything you need to understand the pipeline lives in the stage pages; the GitHub issues here are provenance and status, not required reading.

On EuRoC the filter is accurate enough but over-confident — its NEES runs persistently above the state dimension (≈ 43 vs an expected ~15 on MH_05). The covariance is too small for the true error. Per Reading the metrics, that is the dangerous failure mode: a controller trusting the covariance acts decisively on a wrong position.

This is being localized by measurement, stage by stage — not by guessing. The method: for each stage, a probe (native-unit health), a test (does the invariant hold), and a sensitivity sweep (how much does violating the pre-condition cost). The worked diagnostic chain — the Q/R sweeps, the whiteness discriminator, and the refuted FEJ experiment — is on Consistency Analysis.

CandidateStageStatus
Yaw null-space leak (observability)S2/S6Confirmed on real data; R-IEKF validated as the fix. The observability_probe (#337) localized this synthetically; obs_inspect (#212) now measures it per-update on real EuRoC, driving the shipped Jacobian over the live clone window. (All leak figures here are the dimensionless gauge-leak norm ‖H·N‖ — the residual measurement information the stacked Jacobian projects onto the unobservable gauge; 0 = perfectly annihilated.) On both V2_03 and V1_01 the consistent leak is ~1e-15 (the production Jacobian is correct), translation is structurally protected — its leak is exactly 0 (the clone-δp and feature-δp columns are ∓Hf and cancel bit-for-bit, so it measures 0 on every update, not merely ~1e-15) — and the yaw leak is nonzero on 100% of updates and grows with the clone window’s attitude drift (σθ → 5.6°) — exactly the structural NEES over-confidence (attitude-NEES ≈ 993 on V1_01, NIS a healthy 1.5). At the filter’s own claimed clone σ the standard yaw leak is mean 0.39 / max 619; the right-invariant (R-IEKF) parameterization on the same perturbations leaks 2.3e-16 (estimate-independent gauge) — the real-data evidence that R-IEKF is the fix
First-Estimates Jacobians (FEJ)S2/S6Both FEJ variants diverge — escalate to R-IEKF. The earlier “refuted” was unmeasured; now gated by the #337 probe. Measurement-only FEJ regressed (MH_05 0.76→20.9 m); full FEJ (propagation-Φ + measurement) is catastrophic (V1_01 0.29→2281 m, attitude-NEES 993→134k; MH_05 →11 km) — a forever-propagated IMU first-estimate trajectory dead-reckons to garbage. Correct FEJ needs short-lived per-clone anchors, not a frozen IMU path. The robust cure is the Right-Invariant EKF (state-independent Jacobians → yaw observable-by-construction). use_fej default-off, never merged (parked feat/fej-*)
Diagonal-only process noise Q_dS2Cleared — drops only 0.35% of Q; propagation-only NEES consistent at shipped Q
Unmodeled calibration uncertainty (no T_CI/t_d/intrinsics states)S0, S10Spatial extrinsics cleared; time offset t_d is the remaining lever — the blunt isotropic R-term (calib_rot_sigma) fixed V2_03 NIS (14.7 → 1.41) but regressed ATE/NEES, so T_CI was promoted to online state (estimate_extrinsics, FD-validated Jacobians, recovers a 2.9° error synthetically). On V2_03 it only minorly helps (NEES 140 → 110 at a 3° prior) and leaves NIS unchanged (~14.6) — the spatial extrinsics are accurate enough; the over-confidence is understated R + the unmodeled t_d (Tier-2). Both terms kept default-off
Forward-backward outlier gateS4Gate added (fb_max_residual), unit-tested, default-off pending end-to-end validation; RANSAC still future work
Parallax gate on triangulationS5Refuted as the fix — gate (min_parallax_deg) validated end-to-end on V2_03: enabling it regresses ATE (0.27 → 1.99 m) and NEES (140 → 322) by starving the filter; the low-parallax admission is a real gap but not the driver. Kept default-off
Isotropic initial covariance P₀ / init scaleS1Open as over-confidence seed; ruled out as the e2e lever. Measured: yaw seeded at 5.73° σ (same as leveled roll/pitch) despite being unobservable. Also found: the dynamic-init path can misfire on a near-static start (MH_02: 93% scale error, isotropic P₀) while the static path gives a structured seed. But suppressing the bad dynamic init does not move e2e accuracy (MH_02 ATE 0.79 → 0.79 m, NIS 1.20 → 1.12; V2_03 unchanged) — the filter washes the init error out within seconds, so init is not the over-confidence driver
Update algebra (null-space, Joseph, S)S6Cleared — synthetically and on real data. Measured NIS/dof = 1.00 on a self-consistent (P, R) scene; null-space residual ~2e-16, Joseph PSD. obs_inspect’s consistent leak (~1e-15 on real V2_03/V1_01) confirms the shipped Jacobian annihilates the gauge on real geometry. (The S6 inspector’s earlier “18% non-PSD” was an inspector-predicate artifact — is_positive_definite rejecting the legitimately rank-deficient gauge/clone-copy directions; fixed to is_positive_semidefinite.) Over-confidence is input-side (P⁻/R/parameterization), not the EKF math
Measurement noise R understatedS6 / S10Innovation-level only — not the state-level driver. The S6 sweep shows NIS/dof rises 1 → 1.64 → 4.17 as true noise outgrows modeled R. But on real EuRoC the accepted-update NIS/dof is healthy (0.28 on MH_02; the χ²-gate removes the outlier tail that inflated the all-valid mean to 4.0). The #212 over-confidence is a state-NEES property (measured end-to-end), invisible to innovation NIS — pointing back at the yaw-gauge leak, not a blunt R deficit

The stage contracts and probes are the instrument. The canonical pipeline is distilled into the eleven staged contracts (S0–S10) of this section, each with a probe utility under tools/ and a probe header under sdk/eval/. The consistency instrument itself (NEES/NIS/whiteness) is the always-on half.

Alongside the synthetic probes (the CI gate, known ground truth), each implemented stage now also has a real-data inspector (epic #371 , complete): it re-runs the shipped stage operator on a real EuRoC sequence and renders the result across three tiers — image-domain SVG overlays (gen-overlay.mjs), the 3-D landmark/covariance viewer (scene3d.js), and filter-internal covariance / NIS / convergence figures (gen-*-figures.mjs). Built for every stage whose operator exists in the filter — S0, S4 (image), S5 (3-D), and S6, S1, S2, S3, S9, S10 (filter-internal). The two not built, S7 (persistent SLAM features) and S8 (zero-velocity / ZUPT), are optional layers not present in the cortex filter and become buildable only once those subsystems are added.

A cross-stage diagnostic, obs_inspect (#212/#337), taps every real update and drives the shipped measurement Jacobian over the live clone window to measure the per-direction null-space leak ‖H·N‖ — the real-data instrument that localized the over-confidence to the yaw gauge (see the candidate table) and validated the R-IEKF parameterization as the fix (mean yaw leak 0.39 → 2.3e-16 on the same real V2_03 clone-window perturbations).

AreaIssue
Over-confidence (the umbrella) #212
Consistency instrument (NEES/NIS) #264
Diagnostic-instrumentation epic #261
Real-data stage inspectors (epic — complete) #371
Right-Invariant EKF (the yaw-leak fix) #347 , #348
Stream characterization (Allan → Q) #268
Dynamic init on real EuRoC #211 , #247

This index is the maintained list — when an issue closes, it changes here, and the stage pages stay correct regardless.