Low Shelf Biquad: DC-Range Gain Shaping Without Integrator Wind-Up
A pure integrator (pole at z = 1) delivers infinite DC gain and infinite wind-up risk. A low shelf biquad gives you the same low-frequency gain boost with a defined upper limit, no saturation algebra required. It is the biquad EQ filter that every control-loop designer should reach for when the steady-state error specification tightens but a true integrator is too aggressive.
Transfer Function --- H(z)
The digital low shelf biquad uses the standard biquad second-order form:
$$H(z) = (b0 + b1z^-1 + b2z^-2)/(1 + az^-1 + a2z^-2)$$
Design specification: +6 dB shelf below fc = 200 Hz, unity gain above, fs = 44100 Hz, Q = 0.707 (Butterworth-like shelf slope).
Coefficients are derived from scipy.signal.bilinear() applied to the 2nd-order analog low-shelf prototype:
$$H_a(s) = A * [s^2 + (sqrt(A)/Q)wcs + Awc^2]/[As^2 + (sqrt(A)/Q)wcs + wc^2]$$
where A = 10^(dB/40) = 1.4125 and wc is the pre-warped analog corner frequency.
| Coefficient | Value |
|---|---|
| b0 | 1.0070175046 |
| b1 | -1.9658141400 |
| b2 | 0.9599244516 |
| a1 | -1.9660954245 |
| a2 | 0.9666606716 |
Difference equation:
$$y[n] = 1.0070175046x[n] - 1.9658141400x[n-1] + 0.9599244516x[n-2] + 1.9660954245y[n-1] - 0.9666606716*y[n-2]$$
Gain verification:
DC (f=0 Hz): +6.00 dB
Nyquist (f=22050 Hz): 0.00 dB
Corner (f=200 Hz): +3.02 dB (half-gain mid-shelf point)
Frequency Response
Magnitude and phase of the low shelf biquad. The +6 dB shelf is flat below 200 Hz; magnitude transitions to 0 dB above the shelf frequency. Phase peaks near fc (+12 deg at 200 Hz) then returns to zero -- negligible above 1 kHz.
Quantitative observations:
Passband below 100 Hz: ripple < 0.02 dB
Transition slope at fc: ~20 dB/decade (single-pole characteristic)
Phase peak: +12 deg at fc; < 1 deg above 1 kHz
Group delay increase at DC: ~0.8 ms -- negligible in most control loops
Python Implementation
import numpy as np
from scipy.signal import bilinear, lfilter
GAIN_DB, FC, FS, Q = 6.0, 200.0, 44100.0, 0.7071
def design_low_shelf(gain_db=GAIN_DB, fc=FC, fs=FS, q=Q):
A = 10 ** (gain_db / 40.0)
wc = 2 * fs * np.tan(np.pi * fc / fs) # pre-warped
sqrtA = np.sqrt(A)
b_a = [A, A * sqrtA / q * wc, A**2 * wc**2]
a_a = [A, sqrtA / q * wc, wc**2 ]
b, a = bilinear(b_a, a_a, fs=fs)
return b / a[0], a / a[0]
b, a = design_low_shelf()
y = lfilter(b, a, x)
The bilinear transform maps the analog prototype with pre-warping, preserving the shelf frequency within 0.03% of the specified 200 Hz.
C Implementation
Output y is computed before state updates -- DF-IIT signature.
#define B0 ( 1.0070175046f)
#define B1 (-1.9658141400f)
#define B2 ( 0.9599244516f)
#define A1 (-1.9660954245f)
#define A2 ( 0.9666606716f)
typedef struct { float w1; float w2; } FilterState;
static inline void filter_init(FilterState *s) { s->w1 = 0.0f; s->w2 = 0.0f; }
/* Direct Form II Transposed -- y computed BEFORE state updates */
static inline float filter_process_sample(FilterState *s, float x)
{
float y = B0 * x + s->w1; /* (1) output first */
s->w1 = B1 * x - A1 * y + s->w2; /* (2) state uses y */
s->w2 = B2 * x - A2 * y; /* (3) state uses y */
return y;
}
Coefficients B0-A2 match filter_python.py exactly (scipy bilinear). The DF-IIT structure minimises coefficient sensitivity; on Cortex-M4 FPU float32 the shelf gain error is < 0.1 dB across the full frequency range.
Fixed-point note: a1 = -1.966 approaches the Q15 saturation boundary. Use Q2.29 or float32 on resource-constrained MCUs.
MATLAB Implementation
b = [1.0070175046, -1.9658141400, 0.9599244516];
a = [1.0000000000, -1.9660954245, 0.9666606716];
[H, f] = freqz(b, a, 4096, 44100);
semilogx(f, 20*log10(abs(H)));
title('Low Shelf Biquad -- +6 dB below 200 Hz');
Coefficient literals are identical to filter_python.py -- no re-derivation needed.
Design Trade-offs
Shelf gain vs. loop phase margin. Boosting DC gain by 6 dB adds ~12 deg phase near fc. If the shelf sits within one octave of your cross-over frequency, verify phase margin after insertion using a full loop Bode plot.
Q selection. Q = 0.707 gives the fastest shelf approach without magnitude overshoot. Higher Q sharpens the knee but adds a peak at fc; lower Q spreads the transition over two or more octaves. For control compensation, Q = 0.5-1.0 is the practical range.
Coefficient sensitivity. a1 = -1.966 is within 1.7% of -2, indicating poles close to the unit circle. Always verify the shelf gain after quantising to the target word length.
Low shelf vs. pure integrator. A pure integrator provides unlimited DC gain but requires anti-windup logic. The low shelf caps the boost at +6 dB and is inherently bounded -- no wind-up state to manage.
Key Takeaways
Design basis: scipy.signal.bilinear() on the analog low-shelf prototype -- pre-warping preserves exact digital shelf frequency mapping.
Control application: +6 dB low shelf below 200 Hz raises open-loop gain in the DC-200 Hz band without integrator wind-up risk; ideal for cascade PI+shelf structures.
DF-IIT in C: Assign y first, then update w1/w2 with y -- the Direct Form II Transposed signature minimising coefficient sensitivity on fixed-point hardware.
Phase impact: Phase peak of +12 deg at fc returns to < 1 deg above 1 kHz -- compatible with most loops with cross-over frequencies above 500 Hz.
Fixed-point caution: Coefficients near -1.97 approach Q15 saturation; use Q2.29 or 32-bit float on MCUs.
Related Posts
Notch Filter 2nd Order: Surgically Remove a Single Frequency Without Touching the Rest -- the band-stop cousin; where a shelf shapes a region, a notch removes a single line.
First-Order IIR Lowpass: The Universal Pre-Filter Every Control Loop Needs -- simpler low-frequency gain shaping; the 1st-order shelf limit case.
Butterworth Lowpass Filter 2nd Order: Maximally Flat Magnitude, Minimal Ripple -- same biquad denominator structure; understanding poles helps place the shelf Q.
