Part I: Foundations of Sensory AI
Chapter 2: Sensor Physics and Measurement Models

Direct and indirect observables

"They asked me for the temperature. What I actually held in my hand was a resistance, a wiring diagram, and a promise."

A Literal-Minded AI Agent

Prerequisites

This chapter assumes the smudged-pane intuition and the forward measurement model \(x = h(s) + \eta\) from Chapter 1, plus first-year calculus and a little algebra. Nothing here needs statistics yet; the machinery for inverting a noisy model and attaching a margin of doubt is built from scratch in Chapter 4, and the physical constants and unit conventions used in the worked examples are collected in Appendix A (Math and Signal-Processing Reference) and Appendix C (Sensor Hardware Guide). Read this section for the vocabulary that the rest of Chapter 2 will quantify.

The Big Picture

A sensor almost never responds to the quantity you actually want. It responds to something physically next door, a resistance, a voltage, a light intensity, a time-of-flight, and you recover the thing you care about by pushing that raw response through a model. This section draws the line between the direct observable the transducer genuinely couples to and the indirect observable you infer from it. That line decides which of your errors are honest measurement noise and which are your own modeling assumptions quietly failing. Every chapter that follows is either measuring a direct observable more cleanly or inverting toward an indirect one more carefully.

The measurand is rarely what the transducer touches

Ask a thermometer for the air temperature and it will answer without a single atom of it ever having sensed heat as heat: a bead of metal oxide inside merely grows more or less reluctant to pass current, and everything you read is inference laid on top of that reluctance. So begin where the honesty begins, by naming the quantity you were actually after: air temperature, blood oxygen, altitude, the speed of the car ahead. That target is the measurand. Now name the physical effect the sensing element actually produces at its terminals. For a thermistor it is electrical resistance; for a thermocouple it is a small voltage; for a pulse oximeter it is two light intensities; for radar it is a shift in the frequency of a returned wave. That terminal effect is the direct observable: the one quantity to which the transducer couples through a single, well-characterized physical step, and the only thing your electronics ever really digitizes.

Precisely, the measurand is the specific physical quantity a measurement is meant to determine, defined independently of any instrument: the true air temperature exists whether or not a thermistor is anywhere near it. It matters because it is the ground truth your whole pipeline is accountable to, the target you judge every later error against. You name and fix the measurand first, before choosing a sensor. You can reach the same measurand through very different direct observables (temperature from resistance, from a thermocouple voltage, or from infrared radiance), so stating it cleanly tells you which route you are actually committing to.

The gap between the two is the whole subject. A thermistor does not measure temperature; it is a resistor whose resistance happens to depend on temperature, and you read that dependence in reverse. This matters because the coupling, not your intention, sets what the raw number means. When the datasheet says a device measures temperature, it is compressing a longer and more honest sentence: this device produces a resistance, we have characterized how that resistance varies with temperature, and if you trust our characterization you may call the result a temperature. Keeping the direct observable in view is what lets you tell a broken instrument (its resistance genuinely changed) from a broken assumption (the resistance is fine, your conversion is wrong), a distinction we will return to throughout the chapter.

Checkpoint

So far: the measurand is the quantity you actually want, the direct observable is the single terminal effect the transducer genuinely couples to, and the gap between the two is where honest measurement noise and quiet modeling error part ways.

Indirection: computing what you wanted from what you got

Get this inversion wrong and a perfectly healthy sensor will lie to you with a straight face: aircraft have flown into terrain on altimeters that never twitched, and oximeters have reported comfortable oxygen over suffocating blood. The mechanism behind both is worth pinning down exactly. If the direct observable is the only thing the hardware ever truly hands you, then the quantity you actually wanted still has to be manufactured from it by computation. An indirect observable is any measurand you never touch directly and must reconstruct by applying a physical model to one or more direct observables. The reconstruction runs a known forward relationship backward. Resistance to temperature, pressure to altitude, round-trip photon time to distance, frequency shift to velocity: each is a small inverse problem, the same shape as the perception loop from Chapter 1, now made concrete for one sensor. Figure 2.1.1 lays out the chain that every indirect reading travels.

measurand (what you want) coupling + transduction direct observable x indirect estimate ŝ physics runs forward invert the model ←

Figure 2.1.1. The measurand couples to a sensing element, which transduces it into a direct electrical observable \(x\). A direct-observable sensor stops there; an indirect-observable sensor inverts a known physical model to recover the estimate \(\hat{s}\), inheriting every assumption that model carries.

Indirection also stacks. A pulse oximeter recovers blood oxygen saturation from a ratio of two light absorptions, each itself inferred from a raw photodiode current. Heart-rate variability sits a level higher again, computed from beat intervals drawn from that same oxygen-modulated waveform. The number of layers between photon and reported value gauges how much modeling you are trusting, and how many ways the reading can be quietly wrong while the hardware runs perfectly. Radar makes the point in one clean step: the antenna sees only a frequency shift, and radial velocity falls straight out of the Doppler relation, where the frequency of a reflected wave shifts in proportion to the target's speed toward or away from the antenna, a technique Chapter 44 builds AI on top of. In short: name your observables truthfully and you will know where every future error is hiding before the first sample ever lands. Figure 2.1.2 illustrates stacked layers of indirection in a pulse oximeter (each layer adds an independent modeling assumption).

Stacked layers of indirection in a pulse oximeter (each layer adds an independent modeling assumption)
Figure 2.1.2: Each layer of a pulse oximeter's indirection stack, from raw photodiode current up to heart-rate variability, adds an independent modeling assumption that can fail on its own even while every wire stays intact.

Common Misconception

A common misconception is that each extra layer of indirection only adds more computation, so a deeply-modeled reading is merely slower to produce but no less trustworthy than a shallow one. In fact every layer contributes an independent modeling assumption that can fail on its own, so a deeper reading can be wrong in more distinct ways, not just computed in more steps, even when all the arithmetic runs perfectly and every wire is intact.

Key Insight

A direct observable can be wrong only through noise and hardware fault. An indirect observable can also be wrong because its conversion model was invoked outside the conditions it was derived for. That second failure mode is invisible in the raw signal: the numbers look clean, the electronics are healthy, and the answer is confidently, systematically off. Learning to name your observables is learning where to point suspicion when a reading lies without any sensor breaking.

Every layer of indirection ships a hidden assumption

The model that turns a direct observable into an indirect one is valid only inside a domain, and that domain is a specification as real as any voltage range. A barometric altimeter recovers height from air pressure through the hydrostatic equation (the physical rule that the pressure at any height equals the weight of the air stacked above it), assuming a standard atmosphere. Let a weather front roll in and drop the sea-level pressure, and the altimeter reports a climb the aircraft never made; the pressure sensor is flawless and the altitude is fiction. A pulse oximeter's ratio-of-ratios calibration (the factory-fit curve that maps the red-to-infrared absorption ratio to a saturation value) assumes only oxygenated and deoxygenated hemoglobin are present; carbon monoxide poisoning violates that premise, and the device reads a reassuring high number over dangerously starved blood. A radar Doppler estimate assumes motion along the line of sight; a target crossing sideways registers almost no shift and is reported as nearly stationary.

None of these are noise, and no better or more expensive part cures them, exactly the trap Chapter 1 warned about. They are the conversion model applied where it does not hold. The practical discipline is simple. For every indirect observable in your pipeline, write down the assumptions its model rests on and the conditions that break them. Treat those conditions as failure modes to detect, not footnotes to ignore. When a learned model later consumes this signal, it inherits the validity domain whether or not anyone told it. A network trained only on sea-level flights has never seen the altimeter's front-passage lie and will trust it blindly, a leakage-of-context problem that returns in Chapter 4 when we reason about uncertainty formally.

The altimeter that flew a perfect plane into a mountain

In 1943 a young physicist named Luis Alvarez, later a Nobel laureate, helped develop the ground-controlled approach precisely because pilots kept discovering how ruthlessly the direct-versus-indirect gap bites. A barometric altimeter reports height from air pressure, and a pilot who forgets to reset the reference pressure after flying into a low-pressure system will read hundreds of feet higher than the aircraft truly is, an error that reads "controlled flight into terrain" in accident reports to this day. The unsettling part is the number never wavers or flickers: the pressure sensor is flawless, the needle is rock-steady, and the altitude is simply, confidently wrong. Aviation's grim mnemonic, "high to low, look out below," is nothing but the validity domain of an inverse model compressed into a rhyme pilots can recall while their hands are full.

Mental Model

Think of converting a direct observable into an indirect one as converting foreign banknotes into your home currency with a rate you wrote down earlier. The cash in your hand is the direct observable: real, countable, and not up for debate. The home-currency figure you announce is the indirect observable, and it is only ever as good as the rate you multiplied by. If that rate was quoted yesterday, or for the wrong country, your multiplication is still flawless while the announced amount quietly drifts away from what the money is truly worth. That is exactly how a conversion model used outside the conditions it was calibrated for hands you a clean-looking but systematically wrong reading: the arithmetic is innocent, the rate is the culprit.

In Practice: the clinical pulse oximeter

A finger-clip peripheral oxygen saturation (\(\mathrm{SpO_2}\)) monitor in a hospital shines red (about \(660\ \mathrm{nm}\)) and infrared (about \(940\ \mathrm{nm}\)) light through the fingertip and measures how much of each a photodiode receives. The direct observables are two light intensities; the indirect observable, oxygen saturation, comes from the pulsatile ratio \(R\) of those absorptions fed through an empirical calibration curve baked in at the factory. That curve was fit on healthy adult volunteers breathing controlled gas mixtures. Everything the device reports is only as true as the match between your patient and that reference population. Strong ambient light, nail polish, low perfusion, motion, or a skin tone underrepresented in the calibration cohort all move \(R\) without moving true saturation, and the number drifts from the truth while every wire stays intact. Clinicians are taught to distrust an \(\mathrm{SpO_2}\) reading that disagrees with how the patient looks, which is precisely the direct-versus-indirect distinction turned into bedside practice. The signal-processing side of this sensor is developed in Chapter 30.

Research Frontier

The validity-domain failure this section describes is now being measured at scale rather than assumed away. The OpenOximetry project, run by the University of California, San Francisco (UCSF) Hypoxia Lab since 2022, is assembling an open, controlled-desaturation benchmark of pulse-oximeter accuracy across skin pigmentation, and that evidence fed directly into the U.S. Food and Drug Administration (FDA)'s 2025 draft guidance tightening how \(\mathrm{SpO_2}\) devices must demonstrate accuracy across diverse skin tones. The frontier move is to treat a conversion model's reference population as an auditable, published artifact, turning the hidden calibration assumption into a documented, testable specification instead of a footnote buried in a datasheet.

Because the conversion is just arithmetic on a known relationship, it is also the kind of thing a library encodes once and reuses forever. Consider the workhorse example: turning a thermistor's resistance (direct) into temperature (indirect) through the Steinhart-Hart equation,

\[ \frac{1}{T} = A + B \ln R + C (\ln R)^3, \]

where \(T\) is absolute temperature in kelvin, \(R\) is the measured resistance, and \(A, B, C\) are coefficients from calibration. Listing 2.1 evaluates it directly so you can see the whole inversion in one place.

import numpy as np

def thermistor_temperature_c(resistance_ohms, A, B, C):
    """Steinhart-Hart: direct observable (resistance) -> indirect observable (temperature)."""
    lnR = np.log(resistance_ohms)
    inv_T = A + B * lnR + C * lnR**3       # 1 / T, with T in kelvin
    return 1.0 / inv_T - 273.15            # convert kelvin to Celsius

# Typical 10k NTC coefficients (from a calibration sheet):
A, B, C = 1.129e-3, 2.341e-4, 8.775e-8
for R in (32650, 10000, 3600):            # cold, nominal, warm
    print(f"{R:6d} ohm -> {thermistor_temperature_c(R, A, B, C):5.1f} C")
Listing 2.1. The full direct-to-indirect conversion for a negative-temperature-coefficient thermistor: the resistance is what the hardware gives you, the temperature is what you infer. Notice that the coefficients \(A, B, C\) are the model, and a reading is only as trustworthy as the calibration those three numbers came from.

As Listing 2.1 shows, the physics of the inversion is a three-term polynomial in \(\ln R\); the risk lies almost entirely in the coefficients and their validity range, not in the arithmetic.

Step-Through: inverting the Steinhart-Hart model by hand

Trace the direct-to-indirect conversion for one nominal reading, \(R = 10000\ \Omega\), with the calibration coefficients \(A = 1.129 \times 10^{-3}\), \(B = 2.341 \times 10^{-4}\), \(C = 8.775 \times 10^{-8}\). Step 1: take the natural log of the direct observable, \(\ln R = \ln 10000 = 9.2103\). Step 2: form the linear term, \(B \ln R = 2.341 \times 10^{-4} \times 9.2103 = 2.1561 \times 10^{-3}\). Step 3: cube the log, \((\ln R)^3 = 9.2103^3 = 781.4\), then scale it, \(C(\ln R)^3 = 8.775 \times 10^{-8} \times 781.4 = 6.857 \times 10^{-5}\). Step 4: sum the three terms into the reciprocal temperature, \(1/T = 1.129 \times 10^{-3} + 2.1561 \times 10^{-3} + 6.857 \times 10^{-5} = 3.3537 \times 10^{-3}\). Step 5: invert and shift to Celsius, \(T = 1/3.3537 \times 10^{-3} = 298.18\ \mathrm{K}\), so \(298.18 - 273.15 = 25.0\,^{\circ}\mathrm{C}\). The hardware handed you 10000; the model, and nothing else, turned it into 25 degrees. Now nudge \(B\) up by 3 percent and the same five steps land near \(24.5\,^{\circ}\mathrm{C}\) with the resistance untouched, which is exactly the silent modeling error the section keeps warning about.

The Right Tool

Written from scratch, a robust converter also has to clamp resistances to the calibrated range, handle sensor-specific coefficient tables, interpolate a lookup curve when no closed form fits, and flag out-of-range inputs, often 30 to 40 lines of fiddly guard code per sensor family. A hardware abstraction library collapses that to a single object:

import board, adafruit_thermistor
t = adafruit_thermistor.Thermistor(board.A1, 10000, 10000, 25, 3950)
temperature_c = t.temperature      # resistance read, model inverted, range-checked
Listing 2.2. A driver library reads the divider, inverts the model with the sensor's own beta coefficient (a single number capturing how sharply that thermistor's resistance falls as it warms), and returns a temperature in one property access, replacing roughly 30 to 40 lines of per-sensor conversion and range-guarding. What it cannot do is decide whether the calibration applies to your operating conditions; that judgment stays yours.

The library removes the arithmetic, not the responsibility for the validity domain, the same division of labor Listing 2.1 makes explicit.

Real-World Application: consumer GPS receivers

A Global Positioning System (GPS) chip such as the u-blox NEO-M8 (as of 2024, u-blox's NEO-M9 and NEO-M10 series have largely superseded the M8, though the indirect-observable structure below is identical) never measures your position; its direct observables are the arrival times of coded radio signals, from which it computes pseudoranges (the apparent distance to each satellite inferred from a signal's travel time, before receiver-clock error is solved out) and then solves for latitude, longitude, and altitude, a textbook indirect observable stacked several models deep. The whole solution inherits assumptions about signal propagation, so when signals bounce off buildings (multipath) the pseudoranges are corrupted while the receiver hardware works perfectly, and the reported position calmly jumps a block sideways. That is why phones fuse GPS with the accelerometer and map data rather than trusting the position fix alone.

Why the distinction organizes the rest of this book

The GPS receiver sat at the deep end of this spectrum, and laying every sensor out along the same axis is exactly what makes the rest of the book fall into order. Sort any sensing task by how deep its measurand sits below the transducer and the book's structure appears. Shallow, nearly-direct observables (a thermocouple voltage, a raw accelerometer force) need clean acquisition and good calibration, the concerns of the rest of Chapter 2 and of Chapter 3. Deep, heavily-modeled observables (position from satellite timing, activity from motion, oxygenation from light) need estimation, fusion, and learned inversion, the concerns of Parts III onward. The design question for any new sensor is always the same two-parter: what is the direct observable I truly measure, and how many modeling assumptions stand between it and the number I report. Answer both plainly and you know where your errors will come from before you collect a single sample.

Exercise

Pick three sensors from your own life: a car's fuel gauge, a smartwatch step counter, and a smart-home carbon-dioxide monitor. For each, write down (1) the direct observable the transducer actually produces, (2) the indirect measurand reported to the user, and (3) at least one real-world condition under which the conversion model breaks while the hardware stays healthy. Rank the three by depth of indirection, and predict which will be hardest to trust in the field.

Self-Check

1. State the difference between a measurand and a direct observable, and give one sensor where they coincide and one where they do not.

2. Why can an indirect observable be systematically wrong while the raw signal looks perfectly clean? Name the failure mode this section calls out.

3. In Listing 2.1, which part of the pipeline is "the model," and why does replacing the thermistor with a better one not fix a bad calibration?

Try It: watch a healthy sensor lie

Reproduce the direct-versus-indirect failure on your laptop with nothing but NumPy (and Matplotlib for the plot) in about fifteen minutes.

  1. Copy the thermistor_temperature_c function from Listing 2.1 into a script and confirm the three sample resistances return sensible temperatures.
  2. Sweep resistance from 3,000 to 33,000 ohm with np.linspace, run each value through the function, and plot resistance against recovered temperature so you can see the smooth, clean inversion.
  3. Now simulate a miscalibrated part: increase coefficient B by 3 percent (a plausible batch-to-batch drift) and recompute the whole curve with the same resistances.
  4. Plot both curves together and print their pointwise difference; note that the miscalibrated curve is just as smooth and noise-free as the first, yet reads several degrees off across the range.
  5. Write one sentence explaining why neither averaging more samples nor buying a quieter analog front end would remove this error, and which of the two curves your electronics could tell apart.

Lab: break a barometric altimeter with real weather data

Goal. Feel the difference between a direct observable (air pressure) and an indirect one (altitude) by watching a healthy pressure sensor report a climb that never happened, driven by nothing but a passing weather system. Budget 20 to 30 minutes.

Tools. Python with NumPy and Matplotlib. For real inputs, pull a day of sea-level pressure readings from a public station via the Open-Meteo historical API (the requests library and a free, key-less endpoint), or, if you prefer to stay offline, synthesize a pressure trace that dips by 20 hPa over a few hours to mimic a front.

Method. Convert pressure \(p\) to altitude with the standard barometric formula \(h = 44330 \left(1 - (p/p_0)^{0.1903}\right)\) metres, holding the reference \(p_0\) fixed at the day's opening value. Plot the raw pressure series and the recovered altitude series on a shared time axis.

What to vary. Sweep the reference \(p_0\) (use yesterday's value, today's value, and a standard 1013.25 hPa) and re-run the inversion each time. Optionally add a small amount of Gaussian sensor noise to \(p\) to see that it barely moves the curve.

What to observe. The sensor sat physically still, yet the reported altitude drifts by tens of metres as the front rolls through and shifts wholesale when you change \(p_0\). Confirm that the altitude error tracks the reference-pressure error, not the sensor noise, and write one sentence on why averaging more pressure samples cannot rescue an altitude built on the wrong \(p_0\).

What's Next

In Section 2.2, we stop asking what a sensor observes and start asking how well: resolution, the smallest change it can distinguish; sensitivity, how strongly its output moves per unit of input; and dynamic range, the span from the faintest signal it can register to the loudest it can survive. Those three numbers decide whether the direct observable you just learned to name carries enough information to invert at all.