寻求去除50Hz工频噪声的自适应滤波器方案及优化方法
Hey there! I totally get the frustration of dealing with stubborn 50Hz line noise—even after tweaking Q-values, it just hangs around. Let’s break down some solid solutions, starting with the adaptive filter example you asked for, plus a few other tricks that might do the trick.
Adaptive filters like the Least Mean Squares (LMS) are perfect here because they automatically track variations in the 50Hz noise (like small frequency drifts) without needing perfect prior knowledge. The key is having a reference noise source—this can be a pure 50Hz sine wave you generate, or even a sensor reading directly from the power line (way more accurate if you can get it).
Here’s a straightforward Python implementation:
import numpy as np import matplotlib.pyplot as plt def lms_adaptive_filter(input_signal, reference_noise, mu=0.01, order=10): # Initialize filter weights w = np.zeros(order) filtered_signal = np.zeros_like(input_signal) for n in range(order, len(input_signal)): # Grab the last 'order' samples of the reference noise (reverse for convolution) x = reference_noise[n-order:n][::-1] # Estimate the noise component in the input signal noise_estimate = np.dot(w, x) # Subtract the estimated noise to get a cleaner signal filtered_signal[n] = input_signal[n] - noise_estimate # Update filter weights using the error signal error = filtered_signal[n] w = w + mu * error * x return filtered_signal # Example setup fs = 500 # Sampling frequency (Hz) t = np.linspace(0, 1, fs) # Create a noisy signal: 10Hz useful signal + 50Hz noise + small random noise clean_signal = np.sin(2 * np.pi * 10 * t) line_noise = 0.5 * np.sin(2 * np.pi * 50 * t) noisy_signal = clean_signal + line_noise + 0.05 * np.random.randn(len(t)) # Reference noise (use a power line sensor reading here if possible!) reference_noise = np.sin(2 * np.pi * 50 * t) # Apply the LMS filter filtered_signal = lms_adaptive_filter(noisy_signal, reference_noise, mu=0.005, order=15) # Plot results to compare plt.figure(figsize=(12, 6)) plt.subplot(3, 1, 1) plt.plot(t, noisy_signal) plt.title("Noisy Signal (10Hz + 50Hz Line Noise)") plt.subplot(3, 1, 2) plt.plot(t, filtered_signal) plt.title("Filtered Signal with LMS Adaptive Filter") plt.subplot(3, 1, 3) plt.plot(t, clean_signal - filtered_signal) plt.title("Residual Error") plt.tight_layout() plt.show()
- Tips for tuning: Keep
mu(step size) small to avoid filter instability, and increaseorderif the noise has more complex harmonics (just note that higher order means more computation).
If you can’t get a reliable reference noise source, these alternatives might work better:
- Multi-Harmonic Notch Filter
50Hz line noise often comes with harmonics (100Hz, 150Hz, etc.)—a single notch filter won’t catch all of them. Try chaining multiple notch filters to target the base frequency and its harmonics:
from scipy import signal fs = 500 target_freqs = [50, 100, 150] # Base + 2nd/3rd harmonics Q = 25 # Balances notch narrowness and stability filtered_signal = noisy_signal.copy() for freq in target_freqs: b, a = signal.iirnotch(freq, Q, fs) # Use filtfilt to avoid phase distortion filtered_signal = signal.filtfilt(b, a, filtered_signal)
- Wavelet Transform Denoising
Great for non-stationary signals (where your useful signal changes over time). Wavelets break the signal into frequency scales—you can zero out the scale corresponding to 50Hz to eliminate noise:
import pywt # Choose a wavelet and decomposition level wavelet = "db4" level = 5 # Decompose the noisy signal coeffs = pywt.wavedec(noisy_signal, wavelet, level=level) # For fs=500Hz, level 3 covers ~31-62Hz (perfect for 50Hz) coeffs[3] = np.zeros_like(coeffs[3]) # Zero out the 50Hz scale # Reconstruct the clean signal filtered_signal = pywt.waverec(coeffs, wavelet)
- Synchronous Detection (Lock-In Amplifier)
If your useful signal is periodic and you know its frequency, this method suppresses all non-synchronous noise (including 50Hz) by correlating the signal with a reference matching your useful signal:
# Reference signal matching your useful signal frequency (e.g., 10Hz) ref_signal = np.sin(2 * np.pi * 10 * t) # Demodulate the noisy signal demodulated = noisy_signal * ref_signal # Low-pass filter to extract the DC component (carries useful signal info) b, a = signal.butter(2, 1, "low", fs=fs) filtered_dc = signal.filtfilt(b, a, demodulated) # Reconstruct the clean useful signal clean_reconstructed = 2 * filtered_dc * ref_signal
Start with the LMS filter if you can get a good reference—it’s unbeatable for tracking noise drift. If not, the multi-harmonic notch filter or wavelet denoising are solid go-tos. Tweak parameters like filter order, Q-values, or wavelet scales to squeeze out that last bit of noise!
内容的提问来源于stack exchange,提问作者Ivo

