别怕数学!用Python的Scipy.fft给传感器数据‘降噪’,5分钟搞定信号清洗

当你从加速度计、麦克风或温度传感器采集数据时,那些看似随机的毛刺和波动是否让你头疼?这些噪声可能掩盖了真正有价值的信号特征。但别担心,傅立叶变换就像一副"频率眼镜",能让你瞬间看穿噪声的本质。本文将用真实传感器数据演示如何用Python在5分钟内完成专业级降噪。

1. 传感器噪声:从现象到本质

上周我用树莓派采集了一组工业振动传感器的数据,原始信号看起来像被猫抓过的心电图。这种噪声通常来自三类干扰:

  • 环境干扰 :50Hz工频干扰(国内交流电频率)是最常见的"隐形杀手"
  • 传感器缺陷 :低端加速度计的基线漂移会引入低频噪声
  • 传输损耗 :无线传输时的信号衰减会产生脉冲噪声
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import rfft, rfftfreq, irfft

# 模拟真实加速度计数据(单位:g)
t = np.linspace(0, 10, 5000)  # 10秒采样,500Hz采样率
true_signal = 0.5 * np.sin(2*np.pi*15*t)  # 15Hz机械振动
noise = (0.2 * np.sin(2*np.pi*50*t) +      # 工频干扰
         0.3 * np.random.randn(len(t)))     # 随机噪声
raw_data = true_signal + noise

关键参数提示:采样率必须至少是信号最高频率的2倍(奈奎斯特定理),工业振动分析通常需要500Hz以上采样率。

2. 频域魔法:FFT实战四步法

2.1 快速傅立叶变换分解

n = len(raw_data)
yf = rfft(raw_data)          # 实数信号的FFT优化版
xf = rfftfreq(n, 1/500)      # 生成频率坐标(采样率500Hz)

plt.figure(figsize=(12,4))
plt.plot(xf, np.abs(yf))     # 取模得到振幅谱
plt.xlabel('Frequency (Hz)')
plt.ylabel('Amplitude')

你会看到三个明显特征:

  1. 15Hz处的主峰(真实振动信号)
  2. 50Hz处的干扰峰(工频噪声)
  3. 广泛分布的小振幅噪声(随机干扰)

2.2 噪声频率精准打击

建立频率过滤器:

# 保留10-20Hz范围(已知设备正常振动频率)
mask = (xf > 10) & (xf < 20)
yf_clean = mask * yf

# 可选:硬阈值过滤(适用于未知噪声)
# threshold = 0.2 * np.max(np.abs(yf))
# yf_clean = yf * (np.abs(yf) > threshold)

2.3 逆变换还原信号

clean_data = irfft(yf_clean)

plt.figure(figsize=(12,6))
plt.plot(t[:200], raw_data[:200], 'c', label='Raw')
plt.plot(t[:200], clean_data[:200], 'k', linewidth=2, label='Clean')
plt.legend()

3. 工程化调优技巧

3.1 参数选择黄金法则

参数 推荐值 作用说明
采样率 目标频率×10 避免频谱混叠
FFT点数 2^n ≥ 采样时长×采样率 提高频率分辨率
滤波带宽 目标频率±10% 平衡信号保留与噪声抑制

3.2 常见问题解决方案

频谱泄漏 :添加汉宁窗改善

window = np.hanning(len(raw_data))
yf_windowed = rfft(raw_data * window)

基线漂移 :先做高通滤波

from scipy.signal import butter, filtfilt
b, a = butter(4, 1, 'highpass', fs=500)  # 1Hz截止
dc_removed = filtfilt(b, a, raw_data)

4. 从实验室到生产线:实战案例

某风电设备厂商的振动监测系统曾出现误报警,通过FFT分析发现:

  1. 原始频谱在47Hz和53Hz出现异常峰
  2. 检查发现是变频器谐波干扰
  3. 添加带阻滤波后,故障识别准确率提升40%
# 带阻滤波实现
def band_stop(yf, xf, stop_range):
    mask = ~((xf > stop_range[0]) & (xf < stop_range[1]))
    return mask * yf

yf_clean = band_stop(yf, xf, [47, 53])

对于物联网开发者,可以封装成实时处理模块:

class RealtimeDenoiser:
    def __init__(self, sample_rate, target_freq):
        self.sample_rate = sample_rate
        self.target_range = [target_freq*0.9, target_freq*1.1]
        
    def process(self, data_chunk):
        yf = rfft(data_chunk)
        xf = rfftfreq(len(data_chunk), 1/self.sample_rate)
        mask = (xf > self.target_range[0]) & (xf < self.target_range[1])
        return irfft(mask * yf)
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐