Introduction
The Butterworth filter is an IIR filter with a maximally flat magnitude response in the passband. Published by Stephen Butterworth in 1930, it features a smooth frequency response with no ripple and is one of the most widely used filter designs in signal processing.
This article covers the mathematical foundations of the Butterworth filter through practical design and implementation with SciPy.
Magnitude Response
Magnitude Squared Function
The magnitude squared function of an \(N\)-th order Butterworth lowpass filter is:
\[|H(j\Omega)|^2 = \frac{1}{1 + \left(\frac{\Omega}{\Omega_c}\right)^{2N}} \tag{1}\]where \(\Omega_c\) is the cutoff angular frequency (-3dB point) and \(N\) is the filter order.
Maximally Flat Property
Taylor expansion of equation \((1)\) around \(\Omega = 0\) shows that the first \(2N-1\) derivatives of \(|H(j\Omega)|^2\) are all zero. This is why it is called “maximally flat.”
Behavior by Frequency
- \(\Omega = 0\): \(|H| = 1\) (perfect passthrough)
- \(\Omega = \Omega_c\): \(|H| = 1/\sqrt{2} \approx -3\,\text{dB}\)
- \(\Omega \gg \Omega_c\): \(|H| \approx (\Omega_c/\Omega)^N\) (roll-off rate: \(-20N\) dB/decade)
Pole Locations
Butterworth filter poles are equally spaced on a circle of radius \(\Omega_c\) (the Butterworth circle) in the s-plane. The \(k\)-th pole of an \(N\)-th order filter is:
\[s_k = \Omega_c \exp\left(j\frac{\pi(2k + N - 1)}{2N}\right), \quad k = 1, 2, \ldots, N \tag{2}\]Only poles in the left half-plane (negative real part) are used for a stable filter.
Design Procedure
1. Determine Filter Order
Compute the required order from passband/stopband specifications:
\[N \geq \frac{\log(\varepsilon_s^2/\varepsilon_p^2)}{2\log(\Omega_s/\Omega_p)} \tag{3}\]2. Analog Prototype Design
Design a normalized Butterworth filter with \(\Omega_c = 1\).
3. Digital Conversion
Convert the analog filter to digital using the bilinear transform:
\[s = \frac{2}{T}\frac{1 - z^{-1}}{1 + z^{-1}} \tag{4}\]Pre-warping compensates for the frequency axis distortion:
\[\Omega_a = \frac{2}{T}\tan\left(\frac{\omega_d T}{2}\right) \tag{5}\]Python Implementation
Filter Design and Frequency Response with SciPy
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# --- Filter design ---
fs = 1000 # Sampling frequency [Hz]
fc = 100 # Cutoff frequency [Hz]
orders = [2, 4, 8] # Filter orders
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
for N in orders:
sos = signal.butter(N, fc, fs=fs, output='sos')
w, h = signal.sosfreqz(sos, worN=4096, fs=fs)
axes[0].plot(w, 20 * np.log10(np.abs(h) + 1e-12), label=f'N={N}')
axes[1].plot(w, np.degrees(np.unwrap(np.angle(h))), label=f'N={N}')
axes[0].set_xlabel('Frequency [Hz]')
axes[0].set_ylabel('Magnitude [dB]')
axes[0].set_title('Butterworth Filter - Magnitude Response')
axes[0].set_xlim(0, 500)
axes[0].set_ylim(-80, 5)
axes[0].axvline(fc, color='gray', linestyle='--', alpha=0.5)
axes[0].axhline(-3, color='gray', linestyle=':', alpha=0.5, label='-3 dB')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].set_xlabel('Frequency [Hz]')
axes[1].set_ylabel('Phase [degrees]')
axes[1].set_title('Butterworth Filter - Phase Response')
axes[1].set_xlim(0, 500)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Noise Removal Application
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# --- Generate signal ---
fs = 1000
t = np.arange(0, 1, 1/fs)
clean = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 30 * t)
noisy = clean + 0.8 * np.random.randn(len(t))
# --- Design and apply Butterworth filter ---
sos = signal.butter(4, 50, fs=fs, output='sos')
filtered_causal = signal.sosfilt(sos, noisy) # Causal filter
filtered_zero = signal.sosfiltfilt(sos, noisy) # Zero-phase filter
# --- Plot ---
fig, axes = plt.subplots(3, 1, figsize=(10, 8), sharex=True)
axes[0].plot(t, noisy, alpha=0.5, label='Noisy')
axes[0].plot(t, clean, 'k', linewidth=1.5, label='Original')
axes[0].set_title('Input Signal')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].plot(t, filtered_causal, label='sosfilt (causal)')
axes[1].plot(t, clean, 'k', linewidth=1.5, label='Original')
axes[1].set_title('Causal Filtering (sosfilt)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
axes[2].plot(t, filtered_zero, label='sosfiltfilt (zero-phase)')
axes[2].plot(t, clean, 'k', linewidth=1.5, label='Original')
axes[2].set_title('Zero-Phase Filtering (sosfiltfilt)')
axes[2].set_xlabel('Time [s]')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
sosfilt is a causal filter (with phase delay), while sosfiltfilt achieves zero-phase by filtering forward and backward, but cannot be used for real-time processing.
Comparison with Other IIR Filters
| Filter | Passband | Stopband | Transition Band | Phase |
|---|---|---|---|---|
| Butterworth | Maximally flat | Monotonic | Wide | Relatively smooth |
| Chebyshev Type I | Equiripple | Monotonic | Narrow | Steep changes |
| Chebyshev Type II | Monotonic | Equiripple | Narrow | Relatively smooth |
| Elliptic (Cauer) | Equiripple | Equiripple | Narrowest | Steepest |
For the same order, elliptic filters have the sharpest transition band but introduce passband ripple. Butterworth is optimal when ripple is unacceptable.
Related Articles
- Frequency Characteristics of the EMA Filter - Frequency characteristics of EMA, a first-order IIR filter.
- Lowpass Filter Design and Comparison - Comparison of various lowpass filters including Butterworth.
- Fast Fourier Transform (FFT): Theory and Python Implementation - FFT details for analyzing filter frequency characteristics.
- Types and Comparison of Moving Average Filters - Moving average filters as representative FIR filters.
- Fundamentals of Filtering in Signal Processing - Systematic overview of various filters.
References
- Butterworth, S. (1930). “On the theory of filter amplifiers”. Wireless Engineer, 7(6), 536-541.
- Oppenheim, A. V., & Schafer, R. W. (2009). Discrete-Time Signal Processing (3rd ed.). Prentice Hall.
- SciPy scipy.signal.butter documentation