Keywords: Butterworth Filter | Bandpass Filtering | Scipy Signal Processing | Digital Filter | Python Signal Analysis
Abstract: This article provides a comprehensive guide to implementing Butterworth bandpass filters using Python's Scipy library. Starting from fundamental filter principles, it systematically explains parameter selection, coefficient calculation methods, and practical applications. Complete code examples demonstrate designing filters of different orders, analyzing frequency response characteristics, and processing real signals. Special emphasis is placed on using second-order sections (SOS) format to enhance numerical stability and avoid common issues in high-order filter design.
Introduction
Digital signal processing finds extensive applications in biomedical engineering, audio processing, and communication systems. Butterworth filters are particularly favored for their maximally flat frequency response in the passband. Based on high-scoring Stack Overflow answers and Scipy official documentation, this article provides a complete implementation guide for Butterworth bandpass filters.
Fundamentals of Butterworth Filters
Butterworth filters are infinite impulse response (IIR) filters characterized by their maximally flat frequency response in the passband. For bandpass filters, two cutoff frequencies must be specified: low cutoff frequency (lowcut) and high cutoff frequency (highcut). In digital filter design, these frequencies typically require normalization based on the sampling rate.
Filter Design Parameters
Designing a Butterworth bandpass filter requires clear specification of the following key parameters:
- Sampling Rate (fs): Signal sampling frequency in Hertz (Hz)
- Low Cutoff Frequency (lowcut): Lower limit of the passband
- High Cutoff Frequency (highcut): Upper limit of the passband
- Filter Order (order): Parameter determining filter steepness, with higher orders producing steeper transition bands
Core Implementation Code
Below is the implementation of Butterworth bandpass filter using Scipy.signal.butter function:
from scipy.signal import butter, sosfilt, sosfreqz
def butter_bandpass(lowcut, highcut, fs, order=5):
"""
Generate second-order sections coefficients for Butterworth bandpass filter
Parameters:
lowcut: Low cutoff frequency (Hz)
highcut: High cutoff frequency (Hz)
fs: Sampling frequency (Hz)
order: Filter order
Returns:
sos: Second-order sections coefficient matrix
"""
nyq = 0.5 * fs # Nyquist frequency
low = lowcut / nyq # Normalized low cutoff frequency
high = highcut / nyq # Normalized high cutoff frequency
sos = butter(order, [low, high], analog=False, btype='band', output='sos')
return sos
def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
"""
Apply Butterworth bandpass filter to input signal
Parameters:
data: Input signal array
lowcut: Low cutoff frequency (Hz)
highcut: High cutoff frequency (Hz)
fs: Sampling frequency (Hz)
order: Filter order
Returns:
y: Filtered signal
"""
sos = butter_bandpass(lowcut, highcut, fs, order=order)
y = sosfilt(sos, data)
return y
Filter Characteristics Analysis
To verify filter performance, we can plot its frequency response:
import numpy as np
import matplotlib.pyplot as plt
# Example parameters
fs = 5000.0 # Sampling rate 5kHz
lowcut = 500.0 # 500Hz low cutoff
highcut = 1250.0 # 1250Hz high cutoff
# Plot frequency response for different orders
plt.figure(figsize=(10, 6))
for order in [3, 6, 9]:
sos = butter_bandpass(lowcut, highcut, fs, order=order)
w, h = sosfreqz(sos, worN=2000, fs=fs)
plt.plot(w, abs(h), label=f"Order = {order}")
plt.plot([0, 0.5 * fs], [np.sqrt(0.5), np.sqrt(0.5)], '--',
label='-3dB Point', color='red')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Gain')
plt.title('Butterworth Bandpass Filter Frequency Response')
plt.grid(True)
plt.legend()
plt.xlim(0, 2000)
plt.show()
Practical Application Example
The following example demonstrates filtering a synthetic signal containing multiple frequency components:
# Generate test signal
T = 0.05 # Signal duration
nsamples = int(T * fs) # Number of samples
t = np.arange(0, nsamples) / fs # Time vector
# Create signal with multiple frequency components
signal_component1 = 0.1 * np.sin(2 * np.pi * 1.2 * np.sqrt(t))
signal_component2 = 0.01 * np.cos(2 * np.pi * 312 * t + 0.1)
signal_component3 = 0.02 * np.cos(2 * np.pi * 600 * t + 0.11) # Target frequency
signal_component4 = 0.03 * np.cos(2 * np.pi * 2000 * t) # High-frequency noise
x = signal_component1 + signal_component2 + signal_component3 + signal_component4
# Apply bandpass filter
y = butter_bandpass_filter(x, lowcut, highcut, fs, order=6)
# Plot results
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(t, x, label='Original Signal')
plt.xlabel('Time (seconds)')
plt.ylabel('Amplitude')
plt.title('Original Multi-frequency Signal')
plt.grid(True)
plt.legend()
plt.subplot(2, 1, 2)
plt.plot(t, y, label='Filtered Signal', color='orange')
plt.xlabel('Time (seconds)')
plt.ylabel('Amplitude')
plt.title('Bandpass Filtered Signal (500-1250Hz)')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
Numerical Stability Considerations
In high-order filter design, traditional transfer function form (ba) may face numerical stability issues. Since version 0.16.0, Scipy supports second-order sections (sos) output format, which significantly improves numerical stability by decomposing high-order filters into cascades of second-order systems.
Parameter Selection Recommendations
In practical applications, filter parameter selection should consider the following factors:
- Order Selection: Higher orders provide steeper transition bands but increase computational complexity and may introduce phase distortion
- Cutoff Frequencies: Should be set reasonably based on actual signal frequency components to avoid over-filtering or under-filtering
- Sampling Rate: Must satisfy the Nyquist sampling theorem, meaning sampling rate should be at least twice the highest signal frequency
Conclusion
This article provides a complete solution for implementing Butterworth bandpass filters using Scipy.signal.butter. By employing the second-order sections format, we can design numerically stable high-order filters. The example code demonstrates the complete workflow from filter design to practical application, offering valuable reference for engineers and researchers in biomedical signal processing, audio analysis, and related fields.