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

Spatial sensor networks

"They asked what the temperature was in the warehouse. I had forty answers and no idea what to say about the space between them."

A Spatially Anxious AI Agent

Why this section matters

The sensors in this chapter almost never stand alone. Air-quality monitors blanket a city, temperature probes hang on every rack in a data hall, soil-moisture nodes dot a field, and thermostats scatter across the floors of a tower. What you actually care about is rarely the reading at a device: it is the field, the continuous quantity that exists everywhere in the space and that your sensors merely sample at a few points. Turning a handful of scattered point readings into a defensible map, complete with a statement of how wrong the map is where no sensor sits, is the job of spatial statistics. This section treats a sensor network as a sampler of a spatial field, shows how to interpolate that field with honest uncertainty, and shows how the same machinery tells you where to put the next node. This is the spatial complement to the temporal machinery that dominates the rest of the book.

This section builds on the probability and estimation vocabulary of Chapter 4, and on the sampling-theory mindset of Chapter 3: where that chapter sampled a signal in time, here we sample a field in space, and undersampling has the same consequence, aliasing, in a two-dimensional disguise. Knowing where each sensor is matters as much as what it reads, which is why the localization methods of Chapter 25 are a silent prerequisite for everything below.

A network is a sampler of a spatial field

Let \(z(\mathbf{s})\) be the quantity of interest (temperature, particulate concentration, humidity) as a function of location \(\mathbf{s}\) in some domain. A network of \(N\) sensors gives you \(z(\mathbf{s}_1), \dots, z(\mathbf{s}_N)\), and the field everywhere else is unknown. The foundational assumption that makes any interpolation possible is spatial autocorrelation: near things are more alike than distant things (Tobler's first law of geography). If readings were independent across space, no map could be drawn; because they are not, an unmeasured point can borrow strength from its neighbors. The whole discipline is about quantifying how that similarity decays with distance and using it to fill the gaps.

Two hazards distinguish spatial networks from a single well-placed sensor. First, coverage is uneven: nodes cluster where deployment was cheap (near power and Wi-Fi) and thin out exactly where terrain is hard, biasing any naive average. Second, the nodes are heterogeneous. A low-cost optical particulate sensor and a reference-grade instrument three blocks apart do not read the same field plus noise; they carry different calibrations, drifts, and cross-sensitivities, so a spatial model must treat sensor quality as a first-class variable, not fold it into one noise term.

Interpolate the field, do not average the sensors

The instinct to report "the network mean" is almost always wrong, because the mean answers a question nobody asked and hides the sampling bias of where the nodes happen to be. The right target is the field value at a specified location (or its spatial integral over a defined region), estimated with a model of spatial correlation. That reframing buys you two things a raw average cannot: a prediction at places with no sensor, and a per-location uncertainty that grows smoothly as you move away from the data. A map without that uncertainty band is a decoration; a map with it is an instrument.

From inverse distance to kriging

The simplest interpolator is inverse-distance weighting (IDW): predict a weighted average of the observations with weights \(w_i \propto \lVert \mathbf{s}_\* - \mathbf{s}_i \rVert^{-p}\). It is trivial and it is honest about nothing: it invents no uncertainty, it overshoots near clustered points, and the exponent \(p\) is a free knob with no physical meaning. IDW is a reasonable first draft and a poor final answer.

Kriging, the workhorse of geostatistics, replaces the guessed weights with weights derived from the data's own spatial correlation structure, summarized by the variogram. The empirical semivariogram estimates half the expected squared difference between pairs of readings as a function of their separation \(h\):

$$\hat{\gamma}(h) \;=\; \frac{1}{2\,|N(h)|} \sum_{(i,j)\in N(h)} \bigl(z(\mathbf{s}_i) - z(\mathbf{s}_j)\bigr)^2 ,$$

where \(N(h)\) is the set of sensor pairs separated by roughly \(h\). A fitted variogram exposes three physically meaningful numbers: the nugget (variance at zero distance, capturing sensor noise and micro-scale variation), the sill (the plateau, the field's total variance), and the range (the distance beyond which readings are effectively uncorrelated). The range is the spatial answer to the temporal question "how much history do I need": it tells you how far a single sensor's influence reaches, and therefore how dense your network must be to avoid spatial aliasing. Kriging then produces the best linear unbiased prediction and, decisively, a closed-form prediction variance at every location.

Gaussian processes: the modern face of kriging

Kriging is, in modern language, a Gaussian process (GP) with a covariance kernel that mirrors the variogram (kernel and variogram are two encodings of the same correlation structure). Casting the problem as a GP is worth the relabelling: you inherit principled kernel choice, marginal-likelihood hyperparameter fitting, and easy composition of kernels for anisotropy or added covariates such as elevation and land use. The GP posterior gives both a predicted mean surface and a predictive standard deviation that is largest exactly where sensors are sparse, which is precisely the uncertainty map the key insight demanded. The code below fits a GP to a scattered network and predicts the field on a dense grid.

import numpy as np
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, WhiteKernel, ConstantKernel

# 25 scattered sensor nodes over a 10 x 10 km domain (km coordinates).
rng = np.random.default_rng(0)
S = rng.uniform(0, 10, size=(25, 2))              # node locations
truth = lambda s: 20 + 6*np.sin(s[:, 0]/2) + 4*np.cos(s[:, 1]/3)
z = truth(S) + 0.4*rng.standard_normal(25)         # readings + sensor noise

# Kernel = signal variance * RBF(length-scale) + per-node measurement noise.
kernel = (ConstantKernel(10.0) * RBF(length_scale=2.0)
          + WhiteKernel(noise_level=0.2))
gp = GaussianProcessRegressor(kernel=kernel, normalize_y=True,
                              n_restarts_optimizer=4).fit(S, z)

# Predict the field on a dense grid with per-cell uncertainty.
gx, gy = np.meshgrid(np.linspace(0, 10, 60), np.linspace(0, 10, 60))
grid = np.column_stack([gx.ravel(), gy.ravel()])
mean, std = gp.predict(grid, return_std=True)
fitted = gp.kernel_
print("fitted length-scale (spatial range, km):",
      round(fitted.k1.k2.length_scale, 2))
print("max predictive std (km-corner, sparse):", round(std.max(), 2),
      " min (near a node):", round(std.min(), 2))
A Gaussian-process spatial interpolation of a 25-node network: the fitted RBF length-scale recovers the field's correlation range, and the predictive standard deviation is smallest at monitored points and largest in the sparse corners, giving an uncertainty-aware map rather than a bare surface.

The fitted length-scale printed by that snippet is the GP's estimate of the variogram range, learned from the data rather than guessed. The predictive standard deviation, small near nodes and large in the gaps, is the map's own confession of where it is guessing, and it is what a downstream decision (issue an alert, dispatch an inspector) should be gated on. This posterior is also the natural input to the probabilistic fusion of Chapter 49 when a satellite or model field is combined with the ground network.

Kriging and GP interpolation without the linear algebra

Implementing kriging from scratch means building the pairwise distance matrix, binning and fitting the empirical variogram, assembling and solving the kriging system, and propagating the variance, comfortably 150 or more lines with edge cases at every step. The PyKrige library collapses ordinary kriging to a few calls, and scikit-learn's GaussianProcessRegressor (used above) does the GP form in roughly three lines including hyperparameter optimization:

from pykrige.ok import OrdinaryKriging
ok = OrdinaryKriging(S[:, 0], S[:, 1], z, variogram_model="spherical")
zhat, var = ok.execute("grid", np.linspace(0, 10, 60), np.linspace(0, 10, 60))
Ordinary kriging with PyKrige: variogram fitting, the kriging system, and the variance surface reduce from about 150 lines to three, returning both the interpolated field and its per-cell variance.

The library fits the variogram, solves the system, and returns the variance grid; you supply coordinates, values, and a model family. That is roughly a 150-to-3 line reduction, and it frees you to spend your attention on kernel choice and validation rather than on Cholesky factorizations.

A city air-quality network that knew where it was blind

A municipal environmental team ran a dense grid of low-cost fine-particulate (PM2.5) monitors of the kind popularized by community networks, plus a handful of reference-grade regulatory stations. During a wildfire-smoke episode the public dashboard needed a block-by-block exposure map, but the low-cost nodes clustered in wealthier, better-wired neighborhoods and thinned out over the industrial flats downwind. Rather than average the sensors, the team fit a Gaussian process with the reference stations weighted by their lower noise level, and published the predictive-standard-deviation layer alongside the concentration map. The uncertainty layer lit up bright over the under-monitored flats, which did two things at once: it stopped the city from over-trusting a smooth-looking interpolation in exactly the area with the most vulnerable residents, and it produced a ranked shortlist of locations where one added monitor would shrink the most uncertainty. Three nodes moved that week, chosen by the model.

Where to put the next sensor

The same GP that maps the field answers the deployment question, because its predictive variance depends only on sensor locations, not on the values they will read. That lets you evaluate a candidate placement before installing anything. Two objectives dominate. A variance-reduction (D-optimal style) criterion places the next node where posterior variance is currently highest, shrinking the worst-case error. A mutual-information criterion, the near-standard formulation for GP sensor placement, instead picks the location that is most informative about the rest of the field, which tends to avoid the greedy trap of piling sensors on a single noisy hotspot. Both reduce to cheap evaluations of the GP posterior over a set of candidate sites, and both respect the range parameter: once nodes are spaced well inside the correlation range, an additional one buys almost nothing, giving you a principled stopping rule for network growth.

Space and time together, and the graph view

Real fields evolve, so the honest object is a spatiotemporal field \(z(\mathbf{s}, t)\), and the modeling choice is how to couple the two axes. A separable covariance (a spatial kernel times a temporal kernel) is cheap and often adequate; genuinely moving phenomena such as a smoke plume or a heat wave demand a non-separable model, and this is where the field connects back to the sequence models of Part IV. When the network topology and inter-node relationships carry information (nodes on the same feeder, rooms sharing an air handler, monitors along one street canyon), the network is best represented not as scattered points in a plane but as a graph, and the spatial correlation becomes edge structure. That representation is the direct input to the graph neural networks of Chapter 54, which learn the spatial coupling from data instead of assuming a stationary kernel, and it is the substrate on which the missing-and-faulty-sensor recovery of the next-but-one section operates.

Exercise: map the field and rank a new site

Using the GP code above: (1) hold out five of the twenty-five nodes, refit on the remaining twenty, and compute the held-out prediction error and whether each true value falls inside the GP's 95% predictive interval; this is leave-out spatial cross-validation and it is the only honest way to trust the map. (2) Sweep the RBF length-scale over a decade and show how the uncertainty map inflates or collapses, connecting the length-scale to the variogram range. (3) Over a grid of 200 candidate sites, find the location that maximizes the drop in average predictive variance if a new node were added there, and confirm it lands in a sparse region rather than atop an existing node. Write two sentences on why value-blind placement (using variance only) is possible at all.

Self-check

  1. Why is "the network mean" usually the wrong summary of a spatial sensor network, and what should replace it?
  2. What do the nugget, sill, and range of a variogram each mean physically, and which one tells you how dense your network must be?
  3. The GP predictive variance depends on sensor locations but not on their readings. Why does that fact make it possible to plan a deployment before installing anything?

What's Next

In Section 39.5, we set the map in motion: having learned to see the spatial field at one instant, we turn to forecasting how energy demand and environmental conditions evolve, and to closing the loop with control. The spatial correlation structure built here becomes a covariate for those forecasts, and the uncertainty maps become the risk budget that a controller must respect.