Skip to content

contested-nav

Is the filter right about how wrong it is?

A captioned explainer built from one benchmark run: the same GNSS outage with the camera off and on, the measured accuracy and calibration, and the decision that follows.

Position error against the uncertainty the filter claims, through a GNSS outage, without and with vision

contested-nav is a 21-state error-state Kalman filter and an evaluation harness for GNSS-denied navigation. It measures not only how accurate the trajectory is, but whether the uncertainty the filter reports is honest.

Configuration ATE RMSE Claimed 1σ Mean NEES (expected 3) 2σ-per-axis coverage (expected 99.3%)
GNSS denied, vision off 3.760 m 0.567 m 4.1 100.0%
GNSS denied, vision on 2.322 m 0.162 m 286.2 20.5%

Accuracy improved. Calibration failed. The filter converges and is confidently wrong, so visual fusion ships off by default. The overconfidence held in every run of every sweep; the accuracy gain did not (see Results).

Mostly synthetic evidence

The table above comes from a deterministic known-answer fixture. The recorded-data pages use a real IMU with GNSS simulated from the ground truth. No number here is a field measurement.

How it works

flowchart TB
    IMU["IMU, always on"] --> F
    GNSS["GNSS, can be denied"] --> FDIR["Fault detection: screens each measurement"]
    CAM["Camera motion, opt-in"] -.-> FDIR
    FDIR --> F["Navigation filter: position, and how sure it is"]
    F --> ACC["Accuracy: error against the truth"]
    F --> HON["Honesty: is the truth inside the claimed area?"]
    TRUTH["Known truth: synthetic runs, recorded datasets"] --> ACC
    TRUTH --> HON
    ACC --> GATE{"Gate B1"}
    HON --> GATE
    GATE -- "met" --> ON["Camera fusion on"]
    GATE -- "not met" --> OFF["Camera fusion off by default (ADR-0003)"]

Every run is scored twice: how far the estimate is from the truth, and whether the truth falls inside the area the filter claims. A feature that improves the first and breaks the second stays off. A short captioned explainer is on the project page; the module-level design is in docs/architecture.md.

Where to start