An automated vehicle fuses several noisy sensors into one estimate of where it is and how fast it is going, and then it acts on that estimate. So the estimate had better be trustworthy, and the hard question is what happens when it is not. A GPS fix jumps. An IMU drifts. A wheel slips. Two things decide whether the system is safe: does it notice, and does it fail safe instead of failing. Over the last stretch of an open, simulation-only build I put that whole integrity layer together, from noticing a bad reading to shipping the result so a stranger can reproduce every number. None of the underlying methods are mine to claim. The work, and the point of this write-up, is how the standard pieces get wired into something you can actually trust.

The dangerous state is not a broken sensor.
It is not knowing whether you can trust your own answer.

01 Deciding when to stop is harder than detecting the fault

Detecting a lying sensor is the easy half. A GPS jump produces a large, instant surprise against what the fusion expected, and a standard consistency check gates it out. The hard half is deciding what to do next, because that decision is where a system is safe or dangerous. Stop for every bit of sensor noise and the vehicle is useless. Keep driving on an answer it should not trust and it is unsafe.

The reframe that made this tractable was to stop counting faulty sensors and ask a different question: can I still check my own estimate. One lying sensor is outvoted by the others, isolated, and gated, and the estimate stays good. That is a degraded mode you drive through, not an emergency. The state that earns a stop is subtler. It is when the sensors that would catch a bad answer are themselves compromised, so there is nothing trustworthy left to pull the estimate back, and no way even to tell how wrong it has become. Three states fall out of that one honest criterion: healthy, degraded, and safe-stop, decided by whether any trustworthy correction remains.

Position error over an escalating drive, and the safety decision below it: healthy, then degraded, then safe-stop.
Over an escalating drive: a single GPS jump is isolated and the system degrades but stays close to truth; two faults at once leave no trustworthy correction, and it calls a safe-stop.

02 The fault your own system calls healthy

The fast per-reading check has a deliberate blind spot, and the honest thing was to show it failing before fixing it. A moderate IMU bias sails straight through as healthy, because the fusion simply absorbs it. The good sensors quietly correct for the bias every step, the position estimate stays fine, and no single reading looks surprising enough to flag.

But absorbed is not the same as gone. To keep absorbing a biased prediction, the corrections have to pull the same way every step, and that constant one-sided pull leaves a fingerprint: the errors stop being random noise around zero and pick up a persistent lean, each one far too small to trip a per-reading alarm. So the second monitor watches the running average of that error over a window, using the filter's own prediction as a small digital twin of expected behaviour. A healthy sensor averages to zero. A bias pushes the average off zero long before any one reading looks odd. It trades speed for sensitivity, and it caught the exact bias the first check had waved through, about a second in. Loud faults want a fast check; quiet faults want a slow, patient one. A real integrity layer needs both.

Position error stays low while the drift statistic climbs past its threshold during the injected bias.
The estimate stays within about 0.3 m of truth, the bias really is absorbed, yet the drift statistic climbs past its threshold, which the instantaneous check never does.

03 One green run is a demo, not a measurement

Every milestone up to here ended with a sentence like caught the fault at onset with zero false alarms. That reads well and it is true, and it proves almost nothing, because a detector that fires on everything would pass that test too. A fault monitor only means something as three numbers, measured together: how often it catches real faults, how rarely it cries wolf, and how fast. Any one alone is gameable.

Because every fault here is injected, the ground truth is always known, so I could run the monitors across many random seeds and a sweep of fault sizes and get real measurements rather than estimates. The picture is a curve, not a point. False alarms are genuinely rare, three stray flags in fifty-four thousand readings on clean drives, about 0.006%, which is where a 99.9 percent threshold should land and what keeps the detection numbers honest. A GPS jump is invisible below the sensor noise, then climbs a sharp S-curve to caught every time, within a single sample, once it clears a few metres. The absorbed bias is caught by the slower drift monitor, with a higher floor and seconds to confirm rather than milliseconds. Two monitors, two honestly different operating envelopes.

GPS-jump detection rate rising to 1.0 with time-to-detect collapsing, and the higher detection floor for the slower drift monitor.
GPS-jump detection climbs to 1.0 above a few metres while time-to-detect collapses to about one sample; the absorbed-bias detection floor for the slower drift monitor.
◉
Honest limits: the floor and the blind spot

The floor is the sensor noise itself, which is physics, not a tuning failure: a GPS jump smaller than the GPS noise, or an IMU bias below the drift monitor's sensitivity, is not reliably detectable. And there is one fault the whole system misses, a slow GPS-only drift with nothing to contradict it, flagged plainly and confirmed with a detection rate near zero. A stress test that only reported the wins would be marketing; reporting the floor and the blind spot is what makes the rest trustworthy.

04 Open versus closed is a seam, not a wall

The last step added no new detection idea. It did the two unglamorous things that turn four increments into something a stranger can trust and a business could build on. First, make every result reproducible from a clean checkout: one command reruns the scenarios and prints the metrics, another rebuilds every figure from the same runs, so nothing is a stored number sitting next to code that no longer produces it. If the write-up and the harness ever disagree, the harness wins.

bash
$ python -m av_integrity report
integrity core: reference SafetyMonitor
M1  GPS jump (8 m): detected=True  RMSE 5.6 m -> 0.4 m with gating
M4  false-alarm rate (nominal): 0.006% / check
    GPS jump 5.0 m: detection 1.00, time-to-detect 0.00 s
    IMU bias 1.0:  detection 1.00, time-to-detect 3.95 s

Second, draw the line between the open part and the private part as one real, testable seam instead of a wall. Everything public is an honest, un-tuned reference. A tuned detector, better thresholds, a learned residual model, is worth keeping private. The temptation is a wall, a separate fork and a pile of contact-us, but walls rot: the two sides drift apart and you maintain two systems that no longer agree. The alternative is a single function that decides which monitor runs. With nothing installed it returns the public reference; point an environment variable at a private module and the same function loads that instead, behind the identical interface, graded by the exact same stress test. The private code never enters the public repository, and an improvement is an honest like-for-like comparison. That is the systems-integration idea the whole project has been about, turned on itself: the value is in owning the seam.

Key takeaway

An integrity layer is not one clever algorithm, it is a set of standard pieces wired so they check each other and fail safe on an honest criterion. Notice with a fast check, catch the quiet faults with a slow one, decide by whether you can still verify your own answer, prove it as measured rates with the blind spots named, and separate open from closed with a seam rather than a secret. The methods are textbook. The engineering is in the integration, the honest evaluation, and the boundaries.

The whole thing is public and Apache-2.0, and reproducible to the last number: clone it, pip install -e ., and python -m av_integrity report regenerates every figure here from real runs. It is built on standard methods, an Extended Kalman filter, an innovation consistency check, and windowed change-detection, all cited in the repo. The work is the integration.See the code →