Skip to content

fix(tools): correct s6_inspect PSD check and NIS headline#423

Merged
Ravenwater merged 1 commit into
mainfrom
fix/s6-inspect-psd-and-nis-headline
Jun 21, 2026
Merged

fix(tools): correct s6_inspect PSD check and NIS headline#423
Ravenwater merged 1 commit into
mainfrom
fix/s6-inspect-psd-and-nis-headline

Conversation

@Ravenwater

Copy link
Copy Markdown
Contributor

Summary

While running the S6 inspector on real EuRoC to root-cause the VIO pipeline, two of its diagnostics turned out to be measurement artifacts, not filter faults. This corrects both. No SDK/backend change — the filter math was right.

The two artifacts

1. Spurious "non-PSD" on partial-window tracks

psd_after used is_positive_definite (bare Cholesky), which rejects any zero eigenvalue. But the MSCKF covariance is legitimately rank-deficient:

  • the newest clone is a stochastic copy of the IMU pose → exact zero-covariance directions, and
  • the 4-DoF gauge (yaw + global position) is unobservable.

Those eigenvalues sit at 0 ± machine-eps. Full-window updates nudged them slightly positive (Cholesky passed); partial-window updates left them at ±1e-17 (Cholesky tripped) — producing a deterministic, false "100% non-PSD on partial tracks / 18% overall" signal. Eigendecomposition of a flagged covariance: min eigenvalue −1.1e-17, 6 eigenvalues ≈ 0, all in the gauge / clone-copy directions.

Fix: use is_positive_semidefinite (the invariant the Joseph form actually guarantees). Non-PSD warning drops 18% → 0 on MH_02_easy.

2. NIS headline dominated by the gated outlier tail

The NIS/dof headline averaged over all valid updates including χ²-gated outliers (rejected, never applied), reading ~4.0 and printing "over-confident (R under-modeled)". The updates the filter actually applied are consistent — accepted NIS/dof = 0.28 on MH_02.

Fix: report accepted-only as the honest per-update consistency metric; keep all-valid as a clearly-labelled secondary. (#212 system-level over-confidence is a state-NEES property, measured end-to-end by vio_pipeline, not from these innovation residuals.)

Validation

MH_02_easy, 300 frames, after fix:
  NIS/dof:  accepted 0.284 over 3599 applied updates  → applied updates consistent
            all-valid 4.029 over 3935 (incl. gated outlier tail — not the consistency metric)
  (no non-PSD warning)

Changes

  • tools/include/branes/tools/s6_update_inspect.hppis_positive_definiteis_positive_semidefinite; field comment corrected.
  • tools/src/s6_inspect.cpp — accepted-only NIS accumulator + corrected headline.

Test Results

Target gcc build gcc test clang build clang test
s6_update_inspect OK PASS (38 assertions / 4 cases) OK PASS (38 / 4)

Test plan

  • Fast CI passes (gcc + clang + MSVC + lint)
  • Promote to ready when satisfied

Relates to #371, #380, #212

🤖 Generated with Claude Code

The S6 inspector reported two misleading diagnostics on real EuRoC data, both
measurement artifacts rather than filter faults:

1. psd_after used is_positive_definite (bare Cholesky), which fails on any zero
   eigenvalue. The MSCKF covariance is legitimately rank-deficient: the newest
   clone is a stochastic copy of the IMU pose (exact zero-covariance directions)
   and the 4-DoF gauge (yaw + global position) is unobservable, so those
   eigenvalues sit at 0 +/- machine-eps. Full-window updates nudged them slightly
   positive (Cholesky passed) while partial-window updates left them at +/-1e-17
   (Cholesky tripped) -> a deterministic, spurious "100% non-PSD on partial
   tracks" signal (eigendecomposition: min eig -1.1e-17, 6 eigenvalues ~0).
   Switched to is_positive_semidefinite, the invariant the Joseph form actually
   guarantees; the non-PSD warning drops from 18% to 0 on MH_02.

2. The NIS/dof headline averaged over all valid updates including the chi2-gated
   outlier tail (rejected, never applied), reading ~4.0 and printing
   "over-confident (R under-modeled)". The applied (accepted) updates are
   consistent (accepted NIS/dof 0.28 on MH_02). Now reports accepted-only as the
   honest per-update metric and keeps all-valid as a labelled secondary. #212
   system-level over-confidence is a state-NEES property measured end-to-end, not
   from these innovation residuals.

Inspector gate test passes (gcc + clang); no SDK/backend change.

Relates to #371, #380, #212

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 803482a6-0d5e-4e27-8514-3eaeb959598f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/s6-inspect-psd-and-nis-headline

Comment @coderabbitai help to get the list of available commands and usage tips.

@Ravenwater Ravenwater marked this pull request as ready for review June 21, 2026 04:03
@Ravenwater Ravenwater merged commit b384355 into main Jun 21, 2026
14 checks passed
@Ravenwater Ravenwater deleted the fix/s6-inspect-psd-and-nis-headline branch June 21, 2026 04:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant