Part VIII: Industrial, Energy, and Infrastructure Sensor AI
Chapter 39: Energy, Buildings, and Environmental Sensors

Missing and faulty sensors

"A dead sensor never lies. It just keeps telling you the same true thing about a moment that ended three weeks ago."

A Patient AI Agent

Why this section matters

Every model in this chapter has quietly assumed that the sensor feeding it is alive, honest, and reporting on schedule. Real energy, building, and environmental deployments break all three assumptions constantly. Meters lose network connectivity, humidity probes saturate, a chilled-water temperature sensor freezes at its last value, an outdoor air-quality station drifts a full unit over a season, and a low-cost soil node runs its battery flat mid-summer. The data that reaches your pipeline is therefore a mix of true readings, absent readings, and confidently wrong readings, and nothing in the raw stream tells you which is which. This section is about that reality: how to name the failure modes, how to detect them, and how to keep forecasting and control running when part of the sensing fabric is missing or lying. Getting this wrong is not a rounding error. A single stuck sensor treated as ground truth can drive a control loop to waste energy for weeks.

This section builds on the anomaly and change-detection machinery of Chapter 12 and the uncertainty vocabulary of Chapter 4. Where earlier sections in this chapter treated telemetry as raw material to model, here we treat the telemetry itself as suspect, and we ask what a perception system owes a downstream consumer when its inputs are unreliable.

A taxonomy of sensor failure modes

Sensor problems in the field fall into a small, recurring set of shapes, and naming them precisely is the first step, because each shape needs a different detector and a different repair. The core catalogue:

The reason to insist on this taxonomy is that the failures differ in detectability. A dropout announces itself by absence. A spike violates a rate-of-change bound. But a stuck sensor and a slowly drifting one both produce values that pass every range check individually; they are only visible in their temporal or spatial context, which is precisely why naive validation misses them.

A missing value and a mask are not the same object

The most consequential design decision in this whole area is how you represent absence. A common and costly mistake is to fill a gap with a plausible number and then hand it downstream indistinguishably from a real reading. The disciplined alternative is to carry two aligned channels: the (possibly imputed) value and a binary mask that records, for every timestamp, whether the value was observed or invented. The mask is not bookkeeping; it is information. It lets a forecaster down-weight imputed inputs, lets an evaluation exclude filled points from its error metric so you never score a model on its own guesses, and lets a control policy fall back to a safe mode when too much of its input is synthetic. Impute if you must, but never let the pipeline forget that it did.

Detecting faults before they poison the model

Detection layers on top of the taxonomy. Range and rate checks catch the easy cases: reject values outside physical bounds, and reject changes faster than the process can move (a room does not warm ten degrees in one second). Stuck sensors need a temporal test: flag any channel whose rolling standard deviation stays below a small threshold for longer than the signal's natural quiet period, tuned so that a genuinely stable overnight setpoint is not mistaken for a dead probe. Drift and bias are the hardest to catch in isolation because a single sensor has no reference for truth; they surface only through redundancy. Two nearby air-quality nodes that once agreed and now diverge by a growing margin reveal a drift that neither could show alone, which is one more reason the spatial networks of this chapter earn their redundancy. The general principle, developed formally in Chapter 9, is analytical redundancy: predict what a sensor should read from a model of the process and its neighbors, and treat a persistent gap between prediction and reading as evidence of a fault.

import numpy as np
import pandas as pd

def flag_sensor_faults(x, fs_hz=1/60, flat_minutes=30, spike_z=6.0):
    """Return per-sample fault labels for a 1-D sensor series.
    'missing' = NaN input, 'stuck' = flatlined, 'spike' = local outlier."""
    x = pd.Series(x, dtype="float64")
    labels = pd.Series("ok", index=x.index)
    labels[x.isna()] = "missing"

    # Stuck: rolling std stays ~0 over the flat window.
    win = max(2, int(flat_minutes * 60 * fs_hz))
    roll_std = x.rolling(win, min_periods=win).std()
    labels[(roll_std < 1e-6) & (labels == "ok")] = "stuck"

    # Spike: robust z-score on the first difference (median/MAD, outlier-safe).
    d = x.diff()
    mad = (d - d.median()).abs().median() + 1e-9
    z = 0.6745 * (d - d.median()).abs() / mad
    labels[(z > spike_z) & (labels == "ok")] = "spike"
    return labels
A minimal three-mode fault flagger: absence, flatline, and spike each get a distinct label rather than being silently smoothed away. The median/MAD z-score is used instead of mean/standard-deviation so that the very spikes we hunt do not inflate the threshold that is supposed to catch them.

The flagger above deliberately returns labels, not a cleaned array. That keeps the mask explicit and lets the caller decide, per fault type, whether to impute, drop, or route to a human. Notice it uses a robust dispersion estimate; the accompanying exercise asks you to feel why the non-robust version fails.

Repair: imputation and virtual sensors

Once a fault is flagged, you either fill the gap or synthesize the reading. For short dropouts, temporal imputation is enough: forward-fill for slowly varying setpoints, linear or spline interpolation for smooth physical quantities, seasonal fill for signals with a strong daily cycle. These break down over long gaps, where the signal can change more than any local extrapolation predicts. The stronger tool is a virtual (soft) sensor: a model that reconstructs the missing channel from the channels that remain, exploiting the fact that building and environmental variables are heavily correlated. A supply-air temperature can be inferred from valve position, airflow, and return temperature; a failed outdoor node can be back-filled from neighboring stations by kriging, the spatial Gaussian-process interpolation introduced with the sensor networks of this chapter. Because these correlations are structured as a graph of related channels, the graph neural networks of Chapter 54 have become a strong default for learned imputation across a sensor network, and the missing-modality fusion strategies of Chapter 50 address the same problem one level up, when an entire sensing stream disappears.

Whatever the method, an imputed value must carry uncertainty. A gap filled by interpolating across two seconds deserves near-total trust; a gap filled by extrapolating across three days deserves almost none, and the downstream forecaster should be told the difference. This is where the conformal and calibrated-uncertainty methods of Chapter 18 pay off: an imputation that reports an honest interval, wide where it is guessing and tight where it is confident, lets everything downstream reason about how much of its input is real.

The chiller that chased a frozen thermometer

At a university district-energy plant, a chilled-water supply-temperature sensor iced over during a humid week and froze at 6.1 degrees Celsius. The reading was in range, changed by nothing, and tripped no alarm. The plant's optimization loop, seeing a supply temperature that never rose no matter how it modulated the chillers, kept pushing compressor output to correct an error that did not exist, and overcooled the loop for eleven days before an operator noticed the energy bill. The fix was not a better controller; it was a stuck-sensor detector exactly like the rolling-variance test above, plus a virtual sensor that cross-checked supply temperature against return temperature and flow. When the physical probe flatlined, the plant switched to the virtual estimate, widened its uncertainty, and backed off the aggressive optimization until a technician swapped the sensor. Two lines of fault logic would have saved eleven days of wasted compressor work.

Do not hand-roll the imputation zoo

The temporal fills, gap-aware resampling, and multivariate imputers you would otherwise write by hand are already packaged. A from-scratch masked, multivariate imputer with per-channel strategies and a preserved mask is easily 150 or more lines; with pandas for gap-aware resampling and scikit-learn's IterativeImputer or KNNImputer it collapses to roughly 5 lines, and the library handles the missing-value bookkeeping, the per-feature model fitting, and the convergence loop for you. Reserve your own code for the one thing libraries cannot know: the mask policy and the fault-specific routing that decide when imputation is even appropriate.

Designing for graceful degradation

The deepest lesson is architectural. A perception system for the physical world should be built assuming its sensors will fail, not patched after they do. That means three habits. First, the mask travels with the data end to end, so evaluation never scores a model on imputed points and control always knows how much of its input is synthetic (this is the sensor-failure thread that runs through the whole book, and it connects directly to the leakage-safe evaluation discipline). Second, models are trained with realistic missingness injected, so a network that will face dropouts at deployment has seen dropouts in training rather than meeting them for the first time in production. Third, the system defines an explicit fallback ladder: full sensing, degraded sensing with virtual estimates and widened uncertainty, and a safe default mode when too much is missing to act responsibly. A forecaster or controller that silently trusts a flatline is not robust; a system that notices the flatline, reports reduced confidence, and falls back to a conservative estimate is.

Exercise: break the naive detector

Take the flag_sensor_faults function and replace the median/MAD z-score with an ordinary mean/standard-deviation z-score. Construct a synthetic series with one genuine spike and re-run both versions. (a) Show that the non-robust version can fail to flag the spike, and explain why the outlier inflates its own threshold. (b) Now add a slow linear drift of 0.5 units per day to an otherwise clean signal and confirm that neither version flags it. What second sensor or model would you add to make the drift detectable, and what assumption does that fix rely on?

Self-check

  1. Why is a stuck (flatlined) sensor more dangerous to a control loop than a dropped (missing) one, even though the dropout loses more raw data?
  2. What does the mask channel let you do that carrying only the imputed value cannot, in both evaluation and control?
  3. Why can bias and drift usually not be detected from a single sensor in isolation, and what property of a sensor network makes them detectable?

What's Next

In Section 39.7, we close the chapter by turning all of this reliable, fault-aware telemetry toward its purpose: sustainability applications, from carbon-aware building operation to grid-scale demand response, where the value of a robust sensing pipeline is measured in kilowatt-hours and emissions avoided.