← Back to Autonomy

Diagnostics · Edge · Real-Time

Diagnosis on the Vehicle

Everything so far happened at an analyst’s desk. But the first diagnostician is the ECU — detecting faults in real time, on tiny compute, under a strict false-alarm budget.

Abstract

The whole diagnosis series, until now, has been offline: an analyst sits with logs and pries a root cause out of them. But the very first diagnostician is the car itself. Long before a code reaches a fleet pipeline, an electronic control unit decided to set it — in a few milliseconds, with a few kilobytes of RAM, with no labels and no second chances, under a hard rule that it must not cry wolf. This post drops down to that edge. We build the on-board detector from model-based observers and residuals, layer the cheap limit and plausibility checks beneath them, and add lightweight on-vehicle anomaly detection. Then we meet the two forces that shape every edge decision — a punishing false-alarm budget that demands debouncing and fault maturation, and the safety/certification regime (ISO 26262, ASIL) that keeps edge diagnosis bounded and auditable rather than free-wheeling. Finally we close the loop: the on-board detector emits a DTC, a freeze-frame, and a snapshot that feed the offline fleet RCA pipeline — and fleet RCA tunes the detector’s thresholds and ships them back by OTA. Worked throughout on one running fault: charging code P0AE0, a high-resistance charge connector, caught by the car itself.

§ 01 — The First Responder

Before the analyst, there is the ECU

Every post in this series so far has assumed a luxury: an analyst, a workstation, the full history of a car or a fleet, and all the time in the world to think. The vehicle on the road has none of those.

When charging code P0AE0 finally lands in a fleet database, it arrives as a finished fact — a stored Diagnostic Trouble Code with a timestamp and a snapshot. But something decided to store it, in real time, while the car was charging in a parking garage. That something is an electronic control unit (ECU) running embedded diagnostic code, and it is the true first responder of vehicle health.

The edge is a different world from the analyst’s desk, governed by four hard constraints:

This post is the on-vehicle complement to Fault Detection and Isolation, which laid out the theory of residuals and isolation logic. Here we put that theory under the constraints of real embedded hardware, and then connect it upward to the offline fleet methods of the rest of the series. Throughout, we follow one fault: a high-resistance charge connector that sets P0AE0, caught by the car itself.

Part A — How a vehicle detects its own faults

Three layers, cheapest first

A model that predicts one signal from others; the cheap sanity checks beneath it; and a small learned model on top. Stacked, they are the car’s own diagnostician.

§ 02 — Model-Based Observers & Residuals

Predict a signal, then watch the gap

The core trick of on-board diagnosis is almost embarrassingly simple: if you know how the system should behave, you can predict one signal from the others — and the gap between predicted and measured is where faults hide.

That gap has a name: the residual. You hold a small model of the physical system — how charge current, pack voltage, and temperature relate — and at every step you compute what the model expects. Then:

residual = measured − predicted

When the car is healthy, the residual is small noise hovering around zero — the model and the world agree. When a fault appears, the world drifts away from the model and the residual grows. Threshold the residual and you have a fault flag. This is the whole idea behind parity relations (algebraic equations among signals that should hold for a healthy system) and state observers.

An observer is just a model that runs alongside the real system and corrects itself using the measurements it can see, so its prediction of the signals it is checking stays honest. A Luenberger observer does this with a fixed correction gain; a Kalman filter does it with a gain tuned to the noise, so the residual it produces is statistically well-behaved — small and zero-mean when healthy, which is exactly what makes a clean threshold possible. In plain words: the observer is a well-informed understudy that mimics the real system, and the moment the real system misbehaves, the understudy’s disagreement is your alarm.

Figure 1 — The observer / residual loop
inputs u (charge cmd) real vehicle (charger + pack) observer (Kalman / Luenberger) measured y predicted ŷ correction residual r |r| > threshold? → fault flag healthy: r ≈ 0 (noise) · fault: r grows away from zero
The observer is the understudy. The real vehicle and the observer both receive the same inputs; the observer also peeks at the measurements to keep its prediction honest. Subtract predicted from measured and you get the residual — near zero when healthy, growing when a fault drifts the system away from the model. Threshold it and you have an on-board detector.

One residual rarely tells you which fault. The art of isolation is to design a set of residuals, each sensitive to some faults and blind to others, so the pattern of which residuals trip points at one cause. But the detection primitive is always this gap, and on the vehicle it must be computed continuously, cheaply, and with a flag that does not flicker.

Algorithm 1residual_monitor(y, u, model)

input measured y · input u · model (observer) · threshold τ · maturation N

model.state # observer’s running estimate

cnt 0 # maturation counter, persists across loops

each control loop:

ŷ model.predict(x̂, u) # what a healthy system should show

r y − ŷ # the residual

model.update(x̂, u, y) # correct estimate from measurement

if set_conditions_met() and |r| > τ:

cnt min(cnt + 1, N) # trip toward maturation

else:

cnt max(cnt − 1, 0) # heal back down — don’t flicker

if cnt ≥ N: store_dtc(with_freeze_frame()) # matured → real, store it

§ 03 — Limit, Plausibility & Rate Checks

The cheap first line, run every loop

Before any observer math, the ECU runs a set of checks so cheap they cost almost nothing — and they catch the grossest faults instantly, with no model at all.

These are the embedded equivalent of common sense, and they execute on every signal, every loop:

These checks are first for two reasons. They are nearly free — a comparison or two — so they fit any timing budget. And they protect everything downstream: an observer fed a garbage measurement will produce a garbage residual, so you reject the obviously-bad input before it ever reaches the model. In our running case, a limit check confirms the charge current and pack voltage are plausible numbers; only then does the observer get to ask the subtler question of whether the relationship between them has drifted.

Layering, not competing

Cheap checks and model-based residuals are not rivals — they are a hierarchy. Limits catch the broken sensor; observers catch the degrading component that still reports plausible numbers. The connector fault of our example sails straight past every limit check (every reading is in range) and is only caught by the residual. That is exactly why you need both layers.

§ 04 — Edge ML

A small learned model on top

Some faults have no tidy physical model and no fixed limit — they are just “this doesn’t look like a healthy car.” For those, a tiny learned model on the vehicle can flag the anomaly the equations miss.

The idea is one-class anomaly detection: train a small model on data from healthy cars only — because that is all you reliably have — and have it learn the shape of normal. On the vehicle it then scores each new window of signals by how far it sits from that learned normal. The workhorse is a tiny autoencoder: a network squeezed through a narrow bottleneck that learns to reconstruct healthy data well. Feed it a healthy window and it reconstructs it almost perfectly; feed it an anomalous one and the reconstruction error spikes — that error is the residual, learned rather than derived. (The same one-class idea appears with an isolation forest or a one-class boundary; the autoencoder is just the friendliest for streaming sensor data.)

But the edge is a harsh home for any model, and four constraints shape what is buildable:

Figure 2 — The edge-ML pipeline, and the four walls it lives in
signal window V · I · temp · t tiny autoencoder trained on healthy only reconstruction err = anomaly score score > τ? → flag memory model + buffers must fit in tens of KB latency inference inside one control-loop slice unlabeled no field labels — learn normal only drift + OTA normal shifts; ship new weights over-air
Learned residuals, hard walls. A small autoencoder trained only on healthy data turns “doesn’t look normal” into a number. But it must fit in kilobytes, infer within a loop slice, learn without field labels, and survive drift — the slow change in what “normal” means as cars age — which is why the model itself has to be updatable over the air.

The fourth wall, drift, is the subtle one. A model trained on this year’s healthy cars slowly goes stale as the fleet ages, as a software version changes the baseline, or as a new climate region joins the data. So edge ML is never “ship once and forget” — it is a model that the fleet keeps re-training centrally and pushing back to the cars by OTA. That update path is the thread we pick up in Part C.

Part B — The constraints that shape it

Why the edge is conservative by design

Two forces bend every on-board decision toward caution: the cost of a false alarm, and the demands of functional safety. They are why the car’s detector is bounded, debounced, and auditable.

§ 05 — The False-Alarm Budget & Debouncing

Why on-board must be conservative

An offline analyst who raises a false alarm wastes an afternoon. An on-board detector that raises one lights a warning lamp in front of a driver — and that has a real, measurable cost the analyst never pays.

A spurious malfunction indicator lamp (MIL) triggers a dealer visit, a diagnostic that finds nothing (the dreaded no-fault-found), warranty cost, and a driver who trusts the next warning a little less. Multiply across a fleet and even a low false-alarm rate becomes expensive and corrosive. So the edge detector is tuned to a false-alarm budget: a target rate of false trips it is allowed, and that budget forces it to be conservative — it would rather miss a marginal fault for a few more cycles than flag a healthy car.

The mechanism that buys this conservatism is fault maturation (debouncing). A residual crossing its threshold once does not store a code. Instead a maturation counter increments each loop the fault is present and the conditions are right to test it (the “set conditions” — e.g. only judge the charge connector while actually charging above some current). The code is stored only when the counter reaches its threshold N; if the fault disappears, the counter heals back down. A single noisy spike, a one-loop sensor glitch, a transient during a gear change — none of these survive the counter, because none of them persist.

Figure 3 — A maturation counter rejects the spike, matures the real fault
residual |r| threshold τ glitch (1 loop) sustained fault maturation counter store at N store P0AE0 + freeze-frame bump heals → no code
Persistence, not a single crossing. The transient spike pushes the residual over threshold for one loop; the counter ticks up once and immediately heals back to zero — no code. The real fault holds the residual over threshold under set-conditions, the counter climbs to N, and only then is P0AE0 stored with its freeze-frame. Debouncing is how the edge spends its false-alarm budget wisely.
§ 06 — Safety & Certification

Bounded, deterministic, auditable

There is a second reason edge diagnosis is conservative, and it is not economics — it is law and engineering discipline. Anything running on a safety-relevant ECU lives inside a functional-safety regime that free-wheeling cloud ML simply does not face.

The governing standard for road vehicles is ISO 26262, which classifies each function by an ASIL (Automotive Safety Integrity Level, from A to D) according to how badly it can hurt someone if it fails. A diagnostic that gates a high-voltage charging contactor is not a low-stakes script; it inherits a safety level and, with it, a set of obligations:

This is precisely why the on-board detector is bounded where a cloud model is free. The fleet pipeline of the later posts can run a sprawling, slow, frequently-retrained model over a data lake, because if it is wrong it merely mis-prioritizes an analyst’s queue. The edge detector cannot: it acts in real time on a safety-relevant system, so it is kept small, deterministic, and explainable. The division of labor in this whole series is really a division of risk — conservative certified detection on the vehicle, exploratory power offline.

DimensionOn-board edge detectorOffline fleet pipeline
Time budgetmicroseconds, deterministicminutes to hours
Memorytens of KBa data lake
Modelsobservers, limits, tiny AEany size, frequently retrained
On errorfalse MIL, real-world costmis-ranked analyst queue
Governed byISO 26262 / ASILinternal data discipline
Postureconservative, bounded, auditedexploratory, powerful

Part C — Edge meets cloud

The detector that improves itself

The car’s code feeds the fleet pipeline; the fleet pipeline tunes the car’s thresholds and ships them back. Detection and analysis become one loop — and we follow P0AE0 all the way around it.

§ 07 — The Closed Loop

From edge to cloud and back again

The on-board detector and the offline fleet pipeline are not two separate worlds — they are two halves of one loop. The car detects; the fleet learns; the fleet re-tunes the car. Each pass makes the detector itself better.

Going up: when the maturation counter trips, the ECU stores a Diagnostic Trouble Code and, critically, a freeze-frame — a snapshot of the conditions at the moment of the fault (current, temperature, state of charge, balancing state, the residual value). It may also retain a short snapshot of the surrounding signal trace. These are exactly the standardized artifacts that telematics uploads to the fleet, where they become the raw evidence for the offline RCA of posts four through six — de-mixing the cohort, naming the cause, sizing the campaign.

Going down: the fleet pipeline does something the single car never could — it sees all the cars at once. It learns where the threshold sits too tight (too many false MILs) or too loose (faults caught too late), it re-fits the healthy-baseline model as the fleet drifts, and it ships the improved thresholds, set-conditions, and even retrained edge-model weights back to the vehicles by OTA. The detector you built in Part A is not frozen at the factory; it is continuously sharpened by the fleet it belongs to.

Figure 4 — The edge ↔ cloud closed loop
ON THE VEHICLE limit checks → observers → residuals → maturation stores DTC + freeze-frame deterministic · ASIL · KB THE FLEET PIPELINE de-mix cohorts · RCA significance · surveillance re-tunes thresholds + model slow · powerful · data lake ↑ telemetry: DTC · freeze-frame · snapshot ↓ OTA: new thresholds · set-conditions · weights the detector is improved each pass
One loop, two speeds. The vehicle’s conservative, certified detector emits standardized evidence upward; the fleet’s powerful, slow analysis sends sharpened thresholds and models downward. Detection and root-cause analysis are not separate disciplines — they are the two arcs of a single self-improving loop, and OTA is the wire that closes it.
§ 08 — Worked Example

Catching the high-resistance connector on-board

Let us run the whole machine on our one fault. A charge connector has developed extra contact resistance; offline, this is the connector cluster behind P0AE0. On the vehicle, here is how the car catches it itself — and hands it up.

The physics is friendly. A high-resistance connection drops voltage in proportion to current — V = I·R — so the fault hides at low current and reveals itself under load. An observer on the charging path estimates the pack voltage the model expects for the measured current and temperature. When everything is healthy, expected and measured agree and the residual is noise. As charge current climbs, the extra resistance drops real voltage below what the model expects, and the residual grows. Limit checks see nothing wrong — every reading is in range — so this is squarely a model-based catch.

1 · Set-conditions arm the test

while charging · current above threshold · temperature in band

The detector only judges the connector when it is actually being stressed — charging above a current floor. At rest or trickle-charging, the fault is invisible and the test stays disarmed, so it cannot false-trip on an irrelevant condition.

2 · The residual grows under load

r = V_measured − V_expected(I, T) · climbs with charge current

As the car pulls high charge current, the resistive drop pushes measured pack voltage below the observer’s expectation. The residual crosses threshold — but only while the high-current set-condition holds, i.e. during the demanding part of each charge.

3 · Maturation over several charges

counter increments under set-conditions · heals otherwise · trips at N

One charge is not enough. The maturation counter climbs each charging session the residual stays over threshold under set-conditions, and heals between. Across several charges it reaches N — confirming a persistent fault, not a single bad session.

4 · Store P0AE0 with a freeze-frame

DTC P0AE0 · snapshot: temp, charge current, balancing state, residual

The code stores with a freeze-frame capturing the conditions at the trip — temperature, the charge current that exposed it, the cell balancing state, and the residual magnitude. This snapshot is the seed of evidence the fleet will later use.

5 · Upload → seed the fleet de-mix

telematics → fleet pipeline → cohort de-mixing (posts 4–6)

Telematics uploads the DTC and freeze-frame. In the fleet pipeline this car becomes one point in the connector cluster; its freeze-frame (high current, particular temperature band) is exactly the signal that lets the offline RCA separate the connector cohort from the firmware cohort sharing the same code.

Notice the symmetry that closes the series. The freeze-frame the car captured — high current, a temperature band, the balancing state — is the same evidence the offline de-mixing keys on. The car did not just detect a fault; it captured why it detected it, in a form the fleet can group. And when the fleet later finds the connector threshold should arm at a slightly different current, that tuning rides back down by OTA to every car’s detector. The loop is complete.

§ 09 — Practical Notes

Field rules for diagnosis at the edge

1
cheapest first

Run limit and plausibility checks before any model

Range, stuck-at, and rate checks cost almost nothing and protect every downstream stage. Reject the obviously-broken input before it can poison an observer’s residual.

2
predict, then subtract

The residual is the detector

A model that predicts one signal from the others turns “is this car healthy?” into “is this gap near zero?” An observer keeps that prediction honest; threshold the gap.

3
arm the test

Only judge a fault under its set-conditions

Test the charge connector while charging above a current floor, not at rest. Disarming the test when the fault cannot show is half of not crying wolf.

4
debounce always

Mature a fault before you store it

A single threshold crossing is noise until proven otherwise. A maturation counter that climbs to N and heals back down rejects spikes and stores only persistent faults.

5
spend the budget

Tune to a false-alarm budget, not to zero misses

A false MIL has a real cost — dealer visits, no-fault-found, lost trust. The edge would rather wait a few cycles than flag a healthy car. Conservatism is a feature.

6
stay bounded

Keep it deterministic, partitioned, and auditable

ISO 26262 and ASIL demand bounded timing, freedom from interference, and a traceable decision path. This is why heavyweight, free-wheeling ML stays in the cloud.

7
learn normal only

For model-less faults, use one-class edge ML — and plan for drift

A tiny autoencoder trained on healthy data flags what equations miss. But “normal” shifts as the fleet ages, so make the model updatable over the air.

8
capture the why

Always store a freeze-frame, and close the loop

A code without its conditions is half the evidence. Snapshot current, temperature, and state at the trip — it seeds the fleet de-mix, and the fleet’s answer tunes your detector back by OTA.

§ 10 — References & Further Reading

Where these ideas come from

This post leans on fault-diagnosis theory, estimation, functional safety, and embedded machine learning. Entry points, grouped by the question they answer.

Fault diagnosis & residual generation

R. Isermann, Fault-Diagnosis Systems: An Introduction from Fault Detection to Fault Tolerance (2006). The standard text on model-based diagnosis — observers, parity relations, and residual evaluation for real systems.

J. Gertler, Fault Detection and Diagnosis in Engineering Systems (1998). Parity relations and structured residuals — how to design a residual set so the trip pattern isolates the cause.

M. Blanke, M. Kinnaert, J. Lunze & M. Staroswiecki, Diagnosis and Fault-Tolerant Control (Springer). Connects detection to reconfiguration; a thorough treatment of residuals, isolation, and acting on them.

A. S. Willsky, “A Survey of Design Methods for Failure Detection in Dynamic Systems” (Automatica, 1976). The early map of the field — still a clear orientation to detection in dynamic systems.

Estimation & observers

R. E. Kalman, “A New Approach to Linear Filtering and Prediction Problems” (1960). The filter behind the well-behaved residual — an optimal estimator that keeps the prediction honest under noise.

Functional safety & on-board diagnostics

ISO 26262, Road vehicles — Functional safety. The standard defining ASIL levels, determinism, freedom from interference, and the discipline that bounds edge diagnosis.

ISO 14229 (UDS) & SAE J1979 (OBD-II). The diagnostic communication and on-board diagnostics standards — DTCs, freeze-frames, and how a stored code is read and reported.

Embedded machine learning

P. Warden & D. Situnayake, TinyML (2019). Practical machine learning on microcontroller-class hardware — the constraints, the model sizes, and what is actually buildable at the edge.

A note on these references

These are orientation points into large literatures, cited to show the lineage of the method rather than as the source of any single claim here. Titles and years are given so you can find them.

§ — The Series

The Diagnosis Series

Nine connected parts, from one car’s evidence to a fleet campaign and back to prevention. See the full map & reading guide →

1

Root-Cause Analysis Under Uncertain, Heterogeneous Evidence

Grade, anchor, and fuse messy, unequal-trust clues into one cause.

2

When Is the Evidence Enough?

The sufficiency gate, value of information, and handling conflict.

3

Harder Cases in Diagnosis

Multiple faults, intermittency, and lying sensors.

4

Fleet-Scale Root-Cause Analysis

One code across a population: de-mix, stratify, remediate.

5

Fleet RCA, Made Rigorous

Significance, surveillance, forecasting, closing the loop.

6

Proving Cause with Experiments

Turn observation into proof with staged rollouts.

7

Diagnosis on the Vehicle  you are here

Real-time fault detection at the edge, feeding the fleet.

8

The Data Plumbing Behind Fleet Diagnosis

Joining, aligning, and cleaning the data that makes it work.

9

From Root Cause to No Cause

Prevention and the corrective-action loop — engineer it out.