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.
The headline: structural over-confidence
Section titled “The headline: structural over-confidence”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.
Candidate decomposition
Section titled “Candidate decomposition”| Candidate | Stage | Status |
|---|---|---|
| Yaw null-space leak (observability) | S2/S6 | Confirmed 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/S6 | Both 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_d | S2 | Cleared — drops only 0.35% of Q; propagation-only NEES consistent at shipped Q |
Unmodeled calibration uncertainty (no T_CI/t_d/intrinsics states) | S0, S10 | Spatial 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 gate | S4 | Gate added (fb_max_residual), unit-tested, default-off pending end-to-end validation; RANSAC still future work |
| Parallax gate on triangulation | S5 | Refuted 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 scale | S1 | Open 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) | S6 | Cleared — 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 understated | S6 / S10 | Innovation-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 diagnostic program
Section titled “The diagnostic program”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).
Tracking index
Section titled “Tracking index”| Area | Issue |
|---|---|
| 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.