Part II: Classical Signal Processing and Feature Engineering
Chapter 6: Filtering and Denoising Sensor Signals

FIR and IIR filters

"A moving average is just an FIR filter that never went to design school."

A Well-Tuned AI Agent

The Big Picture

Every linear, time-invariant filter you will ever deploy on a sensor stream belongs to one of two families, defined by a single structural choice: does the output depend only on past inputs, or also on past outputs? That choice splits the world into finite impulse response (FIR) and infinite impulse response (IIR) filters. It decides how much memory the filter needs, whether it can distort the timing of your signal, whether it can blow up, and how many multiplies your microcontroller burns per sample. Section 6.1 gave you two hand-tuned instances, the moving average and the exponential smoother. This section names the families they belong to and hands you the design freedom to build anything in between.

This section assumes you are comfortable with discrete-time signals, the sampling rate \(f_s\), and the notion of a frequency response from Chapter 3. We stay deliberately structural here: the specific low-pass, high-pass, band-pass, and notch shapes come in Section 6.3, and real-time latency budgets in Section 6.6.

The two difference equations

A linear time-invariant filter turns an input stream \(x[n]\) into an output \(y[n]\) with a difference equation. The general form is

$$y[n] = \sum_{k=0}^{M} b_k\, x[n-k] \;-\; \sum_{k=1}^{N} a_k\, y[n-k].$$

The two sums are the whole story. The \(b_k\) weight the current and past inputs: this is the feedforward part. The \(a_k\) weight past outputs and feed them back in: this is the feedback part.

A FIR filter sets every \(a_k = 0\). The output is a fixed weighted sum of the last \(M+1\) input samples, nothing more. Its impulse response, what comes out when you feed in a single spike, is exactly the coefficient list \(\{b_k\}\), and then it is over. Finite in, finite out: that is where "finite impulse response" comes from. A length-5 moving average is the FIR filter with \(b_k = 1/5\).

An IIR filter keeps at least one nonzero \(a_k\), so each output loops back into future outputs. A single spike therefore echoes forever, decaying but never truly reaching zero. The exponential smoother \(y[n] = \alpha x[n] + (1-\alpha) y[n-1]\) is the simplest IIR filter that exists: one feedforward tap, one feedback tap, an impulse response that rings on as \((1-\alpha)^n\).

Key Insight

FIR versus IIR is not "simple versus fancy." It is feedforward-only versus feedback. That one bit determines four downstream properties at once: guaranteed stability, the possibility of exact linear phase, the number of coefficients needed for a given sharpness, and how a transient (or a numerical error) propagates. Pick the family first, then design within it.

Poles, zeros, and why IIR can explode

Take the z-transform of the difference equation and the filter becomes a ratio of polynomials, the transfer function

$$H(z) = \frac{\sum_{k=0}^{M} b_k z^{-k}}{1 + \sum_{k=1}^{N} a_k z^{-k}}.$$

The roots of the numerator are zeros (frequencies the filter kills); the roots of the denominator are poles (frequencies it amplifies). A FIR filter has denominator equal to 1, so it has no poles anywhere except the origin. This is why a FIR filter is always stable: there is no denominator to divide by zero, no feedback loop to run away. An IIR filter is stable only if every pole sits strictly inside the unit circle \(|z| < 1\). Push a pole to or past the circle, through an aggressive design or through finite-precision rounding on a 16-bit device, and the output diverges or oscillates on its own.

That fragility buys enormous efficiency. Feedback lets an IIR filter place sharp resonances and deep notches with a handful of coefficients, because each pole does heavy lifting. Matching the same steepness with a FIR filter can take ten to a hundred times as many taps. The tradeoff is the recurring theme of this section: FIR pays in coefficients for safety and phase honesty; IIR pays in stability risk and phase distortion for cheapness.

Phase: the property sensor engineers forget

Magnitude response tells you which frequencies survive. Phase response tells you how much each frequency is delayed, and getting it wrong warps the shape of a waveform even when the spectrum looks fine. A symmetric FIR filter (coefficients that read the same forwards and backwards) has exactly linear phase: every frequency is delayed by the identical number of samples, \((M)/2\). The waveform comes out shifted in time but undistorted. IIR filters cannot be linear phase; their feedback smears different frequencies by different delays, so a sharp ECG R-peak or an accelerometer impact edge shifts and skews.

Practical Example: the ECG that lied about its timing

A clinical team built a wearable ECG patch and cleaned the 0.5 to 40 Hz band with a compact 4th-order Butterworth IIR filter, chosen because it ran cheaply on the patch's tiny core. The heart-rate estimate was perfect. But the derived feature the cardiologists actually wanted, the QT interval (the time from the Q wave to the end of the T wave), came out systematically biased, because the IIR filter delayed the low-frequency T wave more than the sharp QRS complex. Switching the offline analysis to a linear-phase FIR filter, and reserving the cheap IIR only for the on-device live display, removed the bias. The lesson: when a downstream measurement is a timing between events, phase linearity is not a luxury. This same care carries into Chapter 29.

Zero-phase filtering: cheating with hindsight

When you process a recording after the fact rather than live, you can sidestep the phase problem entirely. Run any filter forward, then run it backward over its own output. The forward pass delays each frequency by some amount; the backward pass applies the identical delay in the opposite direction, and the two cancel exactly. The result has zero phase distortion and double the magnitude attenuation, from even a cheap IIR filter. This is filtfilt. It is offline only, because the backward pass needs future samples you do not have in a live stream; that constraint is exactly what Section 6.6 wrestles with.

import numpy as np
from scipy.signal import firwin, butter, lfilter, filtfilt

fs = 100.0          # accelerometer sample rate, Hz
cutoff = 5.0        # keep motion below 5 Hz, drop sensor hiss above

# FIR: 51 symmetric taps -> exact linear phase, always stable
fir_b = firwin(numtaps=51, cutoff=cutoff, fs=fs)

# IIR: 4th-order Butterworth -> same job, 5 coeffs, but nonlinear phase
iir_b, iir_a = butter(N=4, Wn=cutoff, btype="low", fs=fs)

x = np.load("wrist_accel.npy")               # a noisy 1-D signal

y_fir     = lfilter(fir_b, 1.0, x)           # causal, one-directional
y_iir     = lfilter(iir_b, iir_a, x)         # causal, cheap, phase-warped
y_iir_zp  = filtfilt(iir_b, iir_a, x)        # offline, zero-phase

print(len(fir_b), len(iir_b))                # 51 vs 5 coefficients
The same 5 Hz low-pass built two ways on a 100 Hz wrist-accelerometer stream. The FIR needs 51 taps for linear phase; the 4th-order Butterworth IIR needs 5 coefficients but warps timing unless you wrap it in filtfilt for offline zero-phase output. The printed 51 vs 5 is the efficiency gap in one line.

Right Tool: let SciPy do the design math

Deriving Butterworth pole locations or a windowed-sinc FIR by hand is dozens of lines of transcendental algebra and easy to get subtly wrong. scipy.signal.firwin and scipy.signal.butter collapse each design to a single call and return coefficients ready for lfilter. The forward-backward zero-phase trick, another 15 or so lines of edge-padding and state bookkeeping done correctly, is one call to filtfilt. You supply the cutoff and the order; the library supplies the numerics.

Choosing a family

Reach for FIR when phase linearity matters (timing features, waveform morphology, multi-channel alignment), when you need guaranteed stability on fixed-point hardware, or when the filter must be adapted or retrained online. Reach for IIR when compute and memory are scarce and a few coefficients must do sharp work, as on the microcontrollers of Chapter 61, and when a modest, predictable phase shift is acceptable. Note the family relationship to state estimation: the recursive, feedback structure of an IIR filter is the same idea that, generalized to a probabilistic state model, becomes the Kalman filter of Chapter 9.

Exercise

Take the 100 Hz accelerometer signal from the code above. (1) Design a 5 Hz low-pass as both a 51-tap FIR and a 4th-order IIR. (2) Feed each a single unit impulse and plot the impulse responses; confirm the FIR ends after 51 samples while the IIR rings on. (3) Overlay lfilter (causal IIR) against filtfilt (zero-phase) on a sharp step edge and measure, in samples, how far the causal version shifts the edge. Report the shift and relate it to the filter order.

Self-Check

  1. Which coefficients, \(a_k\) or \(b_k\), make a filter recursive, and what property of the impulse response follows from having any nonzero \(a_k\)?
  2. Why is a FIR filter unconditionally stable while an IIR filter is not?
  3. You need to measure the interval between two peaks in a vibration signal from a recorded dataset. Which filtering approach preserves the timing, and does it work in a live stream?

What's Next

In Section 6.3, we put these two families to work shaping frequency: designing low-pass, high-pass, band-pass, and notch responses, reading their magnitude plots, and matching each shape to a concrete sensor-cleaning job.