Recipe · diagnostics
Hidden parasitic drain
0.75 amps. Not enough to trip a breaker, not enough to warm a wire. But on a 100 Ah forklift battery discharging at 8 amps, that leakage steals 9% of usable energy every shift. The current sensor on the traction bus reads 8 amps — it only monitors the drive motor. A faulty hydraulic valve controller on the auxiliary bus draws 0.75 amps directly from the battery terminals, bypassing the sensor entirely. The BMS, measuring cell voltage, sees the combined drain but has no way to separate the leakage from normal discharge. The fleet manager sees only "battery replaced early — again."
A kalman1d node with a control input predicts how much current
should change the charge each tick. When the BMS measurement
consistently falls below that prediction, the innovation drifts
negative — the fingerprint of a leakage the sensor cannot see.
pageHinkley accumulates statistical evidence of that shift and flags
it. sanitize and median3 clean the input upstream.
flow('parasitic-drain-detector')
.sanitize('sane', 'soc', { failureReason: 'failReason' },
{ ranges: { soc: { min: 0, max: 100 } } })
.median3('m3', 'soc', { median3: 'm3' })
.kalman1d('kf', 'm3',
{ filtered: 'estimate', innovation: 'innovation' },
{ control: 'current', controlModel: 0.0167,
sensorVariance: 0.01, processVariance: 0.0005 })
.pageHinkley('ph', 'innovation',
{ phShift: 'drainDetected', phTestStatistic: 'phStat' },
{ delta: 0.005, lambda: 3, detectDrop: true,
minWarmUpSamples: 30 })
.run()Drag the slider past tick 200 and watch innovation develop a faint negative bias — the leakage’s fingerprint.
What You’re Seeing
The gray line is the raw BMS State of Charge — a noisy voltage-derived estimate that drops as the battery discharges. The cyan line is the Kalman-filtered estimate, smoother than the raw reading. The purple curve on the right axis is the innovation — the difference between what the filter predicted and what the BMS measured.
For the first 200 ticks, the current sensor sees all the discharge. The filter predicts from current (coulomb counting), the BMS confirms, and innovation fluctuates around zero — the model matches reality.
At tick 200 (the amber vertical), a faulty hydraulic valve controller on the auxiliary bus starts leaking 0.75A directly from the battery terminals — bypassing the traction current sensor. The sensor still reads 8A. The BMS, measuring cell voltage, reflects the true combined drain — the battery drops as if 8.75A were flowing. Innovation drifts negative: the BMS consistently reads a fraction of a percent lower than predicted. The shift is too small to see by eye on any single tick. But it is persistent, and persistence is what Page-Hinkley is built to find.
About 70 ticks later (the rose vertical), Page-Hinkley has accumulated enough evidence to confirm the shift. At 60-second sampling, that is roughly 70 minutes from fault onset to automated detection — from a 0.75A leakage buried in sensor noise.
Toggle “Without Control.” The filter no longer uses current to predict — it just smooths the BMS readings. Innovation looks the same in both halves. No detection marker appears. The 0.75A leakage is indistinguishable from normal noise. The control input is what makes detection possible.
Where This Pattern Fits
| Domain | What leaks | Why it is invisible |
|---|---|---|
| EV fleets | Auxiliary loads downstream of the traction current sensor | Fleet telemetry reports “low battery” without identifying the cause |
| HVAC compressors | Refrigerant leak forces longer run cycles at the same power draw | Energy meter reads higher but the compressor looks healthy |
| Water distribution | Leak in a branch downstream of the main flow meter | Upstream meter cannot see downstream losses |
| UPS systems | Parasitic load on a battery bank between inverter and load | UPS reports “battery weak” without pinpointing the drain |
| Hydraulic presses | Internal seal leak wastes pressure the pump sensor cannot see | Pressure builds slower but the pump appears to run normally |
How It Works
The Kalman filter with a control input does coulomb counting: at each
tick it predicts the SoC change from the known current. The
controlModel parameter G converts amps to SoC% per tick. For a
100 Ah battery at 60-second intervals:
G = dt / (capacity × 3600) × 100 = 60 / (100 × 3600) × 100 ≈ 0.0167
At 8A of discharge, the filter predicts a 0.133% SoC drop per tick. Then it corrects with the BMS reading. When prediction and measurement agree, innovation hovers near zero.
The processVariance (0.0005) is deliberately small — it tells the
filter that SoC should change only because of the current, with very
little unexplained drift. This makes the filter trust its coulomb-counting
model. When the model is right (normal operation), innovation stays
small. When the model is wrong (leakage), the unexplained SoC drop
accumulates as a persistent innovation bias — 0.75A on a 100 Ah battery
produces a −0.0125% bias per tick. That is one-eighth of the sensor
noise on any single reading. No threshold alarm can catch it. But
Page-Hinkley accumulates the evidence tick by tick until the shift is
statistically confirmed.
The sensorVariance (0.01) matches the BMS noise — σ² = 0.1² for a
typical voltage-derived SoC estimate. Getting this right matters: too
low and the filter over-trusts the measurement, passing noise through
to the innovation. Too high and the filter ignores real BMS readings,
delaying the diagnostic signal.
Automated detection with Page-Hinkley
The innovation is the diagnostic signal, but a human should not have to
watch it. pageHinkley monitors the innovation stream and accumulates
statistical evidence that its mean has shifted. The delta parameter
(0.005) is the minimum shift worth detecting — below that, the change
is within normal noise. lambda (3) controls how much evidence is
needed before flagging. Higher lambda means fewer false alarms but
slower detection.
detectDrop: true tells Page-Hinkley to watch for a negative shift —
the direction a leakage pushes the innovation. A positive shift
(sensor reading higher than predicted) would indicate a different
fault, like current sensor drift.
Different faults, different signatures
The innovation is a signed signal. Its direction and pattern identify the fault:
- Parasitic drain (this recipe) — innovation drifts negative. The BMS consistently reads lower than the current-based prediction. The bias is proportional to the leakage: 0.75A on a 100 Ah battery creates a −0.0125% bias per tick — one-eighth of the sensor noise.
- Capacity fade — the battery ages and its usable capacity drops, but the model still assumes the original 100 Ah rating. The filter under-predicts each tick’s SoC change. Innovation drifts negative under load and returns to zero at rest — a load-dependent bias that grows over months.
- Current sensor drift — the sensor develops a constant offset and reads high. The filter overestimates the drain each tick. Innovation flips positive — the BMS consistently reads higher than predicted. The opposite sign distinguishes a sensor problem from a battery problem.
References
- Kalman, R.E. (1960). A new approach to linear filtering and prediction problems. Journal of Basic Engineering, 82(1), 35–45. doi:10.1115/1.3662552
- Page, E.S. (1954). Continuous inspection schemes. Biometrika, 41(1/2), 100–115. doi:10.2307/2333009
- Plett, G.L. (2004). Extended Kalman filtering for battery management systems of LiPB-based HEV battery packs. Journal of Power Sources, 134(2), 252–261. doi:10.1016/j.jpowsour.2004.02.031
Next Steps
- Sudden Shifts — the same node in a different mode: detect step changes with innovation gating instead of tracking gradual drift
- Kalman 1D Filter Explorer — drag sliders to build intuition about each parameter
- Composition Patterns — how independent nodes compose into richer flows