Diagnostics · Edge · Real-Time
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.
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.
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
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.
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.
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.
input measured y · input u · model (observer) · threshold τ · maturation N
x̂ ← 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
x̂ ← 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
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.
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.
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:
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
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.
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.
N, and only then is P0AE0 stored with its freeze-frame. Debouncing is how the edge spends its false-alarm budget wisely.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.
| Dimension | On-board edge detector | Offline fleet pipeline |
|---|---|---|
| Time budget | microseconds, deterministic | minutes to hours |
| Memory | tens of KB | a data lake |
| Models | observers, limits, tiny AE | any size, frequently retrained |
| On error | false MIL, real-world cost | mis-ranked analyst queue |
| Governed by | ISO 26262 / ASIL | internal data discipline |
| Posture | conservative, bounded, audited | exploratory, powerful |
Part C — Edge meets cloud
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
This post leans on fault-diagnosis theory, estimation, functional safety, and embedded machine learning. Entry points, grouped by the question they answer.
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.
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.
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.
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.
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.