Skip to content

Results

Synthetic 30 s run, 5 Hz GNSS, 20 Hz vision, noisy IMU, 21-state ESKF. ATE is reported with alignment=none: the filter is initialised in the reference frame, so there is no global offset for a fitted transform to absorb. This is the strictest of the four conventions the code supports.

Scenario ATE RMSE (m) Claimed 1σ (m) Mean NEES (exp. 3) Coverage @ 2σ Verdict
GNSS only (control) 0.528 0.254 4.3 100.0% mixed: bulk overconfident, tail underconfident
Dead reckoning (no aiding) 2.359 n/a n/a n/a no covariance reported
Anchor as measurement noise (defect) 1.063 0.094 163.7 16.3% overconfident
Vision only 2.321 0.158 264.2 0.7% overconfident
GNSS denied 5–20 s, vision off (control) 3.760 0.567 4.1 100.0% mixed: bulk overconfident, tail underconfident
GNSS denied 5–20 s, vision on 2.322 0.162 286.2 20.5% overconfident
GNSS denied, 30% camera frames dropped 2.008 0.192 203.7 17.8% overconfident
GNSS denied 5–20 s, vision on, stochastic clone 0.725 0.375 2.5 100.0% underconfident
GNSS denied, 30% frames dropped, re-referenced, single anchor 1.973 0.192 191.5 17.7% overconfident
GNSS denied, 30% frames dropped, re-referenced, stochastic clone 0.503 0.307 2.2 100.0% underconfident
Vision only, stochastic clone 1.959 1.326 2.3 100.0% underconfident
Stochastic clone, 5% gross visual outliers 0.727 0.382 2.5 100.0% underconfident
Stochastic clone, drifting translation scale 0.787 0.375 2.9 100.0% underconfident
Stochastic clone, visual errors correlated over 2 s 1.931 0.375 13.1 58.3% overconfident
Stochastic clone, correlated errors, visual noise assumed 4x 2.259 0.531 4.9 100.0% mixed: bulk overconfident, tail underconfident

Four things deserve more than a glance.

The defect row is not a gate-threshold problem. Folding the visual anchor error into the measurement covariance, rather than treating it as filter state, drops coverage to 16%. Both variants are in the benchmark so the comparison is reproducible, and the broken one is kept in the default run on purpose.

Turning vision on makes the filter much less calibrated, though not much less accurate. The outage control with vision off ends at 3.76 m with its uncertainty grown to match, so coverage stays at 100%. The aided case claims 0.16 m while being 2.32 m wrong. Note what adaptive inflation did to this comparison: before it, the FDIR gate made the aided case worse on ATE (5.06 m), so a reader sorting by ATE at least got sent to the more honest filter. Recovering the error put the less-calibrated configuration back on top, and the ATE column no longer flags it at all. Ranking by error is not a calibration check, and this table is a demonstration of that rather than an argument against the fix.

Dead reckoning has no claimed-σ or NEES value. An integrator with no uncertainty model has nothing to calibrate. Printing a covariance it never computed would be the same error as printing one it does not deserve, so the columns are n/a rather than zero.

Two verdicts, not one

Calibration is reported as a bulk test (mean NEES against its expectation) and a tail test (coverage inside the 2σ ellipsoid, by Wilson interval), and a disagreement is named rather than resolved. A filter can be marginally overconfident almost everywhere while never once producing a tail escape; collapsing that into a single "calibrated" boolean discards half the evidence and hides the shape of the error distribution.

The mixed rows are exactly that case: mean NEES of 4.0 against an expected 3 is a real, statistically detectable overconfidence, and yet no epoch escapes 2σ. Both statements are true and only one of them is usually quoted.

Error against claimed uncertainty

Coverage against expectation

Error through the outage

Mean NEES by scenario

Alignment is not a detail

ATE is reported four ways, because the convention changes the number by metres and an ATE quoted without its alignment is not a result:

Alignment Fitted on What it hides
none nothing nothing; the raw error in the filter's own frame
rigid all poses accumulated drift, absorbed into the fit
rigid_start first 20% only nothing after the first fifth of the run
similarity all poses, with scale a scale error in the estimate

All headline numbers above use none. A rigid fit uses positions only, so attitude error passes through it untouched and is reported separately; a reader who assumed "rigid alignment" meant 6-DoF pose alignment would otherwise quote an attitude error several times smaller than reality.

A stochastic clone of the previous pose removes the overconfidence on this fixture. The rows ending in "stochastic clone" run the same scenarios with the previous visual pose carried as six error states that start perfectly correlated with the live pose (ADR-0017). With GNSS denied, the clone is calibrated where the single anchor is not, and its position error is below the no-vision control's. The two degraded-camera rows use a stream in which each delivered measurement is taken against the last delivered frame; the single-anchor row on that stream is the control and stays overconfident, so the generator was not what made the anchor fail. This is a synthetic result with a Gaussian surrogate front end. The clone is opt-in: the shipped setting is still the single anchor, with visual fusion off.

What the clone does not survive. The last four clone rows depart from the ideal front end one way at a time. Gross outliers are rejected by the gate and the filter stays calibrated. A drifting translation scale does not matter, because the inertial unit holds the metric scale. Visual errors correlated over 2 s do break it: the clone assumes independent errors and is overconfident again. Telling the filter the visual noise is four times larger restores calibration, at a cost in accuracy. The factor was found by trial, not derived. A 150 s run without GNSS (tests/test_clone_long_run.py) shows the claimed uncertainty still growing and the error inside it, and GNSS accepted when it returns.