第三章 信号预处理:去趋势项、滤波、重采样与降噪

各位同学,咱们今天聊聊信号预处理。说实话,这一步在很多人眼里是「苦力活」,觉得不如后面做特征提取、故障识别来得高级。但我得说句实在话——预处理做不好,后面全是白搭。我见过太多人拿着原始信号直接扔进模型,结果跑出来一堆漂亮曲线,仔细一看全是噪声和趋势项在跳舞。

信号预处理,说白了就是给原始信号「洗个澡」。把脏东西洗掉,把有用的部分露出来。咱们一步步来。

3.1 去趋势项:把「地基」找平

什么是趋势项?你想想看,一个轴承从冷机启动到稳定运行,温度在变,润滑油膜在变,传感器可能也在慢慢漂移。这些变化反映在信号上,就是一个缓慢的、非周期性的偏移。这就是趋势项。

趋势项的危害在哪?它会压低或抬高信号的均值,让你算出来的均方根值、峰值这些特征全部失真。我有个项目,现场测振动数据,算出来趋势项占了总能量的30%多,不把它去掉,你根本看不到真正的故障频率。

去趋势项最常用的方法就是多项式拟合。说白了,就是用一条曲线去拟合信号的整体走势,然后把它减掉。

import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

# 生成带趋势项的模拟信号
t = np.linspace(0, 10, 1000)
trend = 0.5 * t + 2  # 线性趋势
vibration = np.sin(2 * np.pi * 5 * t) + 0.3 * np.sin(2 * np.pi * 12 * t)
signal_with_trend = vibration + trend + 0.2 * np.random.randn(len(t))

# 去趋势项
detrended_signal = signal.detrend(signal_with_trend, type='linear')

# 可视化对比
plt.figure(figsize=(12, 4))
plt.subplot(1,2,1)
plt.plot(t, signal_with_trend)
plt.title('原始信号(含趋势项)')
plt.subplot(1,2,2)
plt.plot(t, detrended_signal)
plt.title('去趋势后信号')
plt.show()
我的小经验:去趋势项时,type参数可以选'linear'或'constant'。线性趋势用'linear',常数偏移用'constant'。如果信号有更复杂的走势,可以用高阶多项式拟合,但别超过3阶——我试过5阶,结果把故障特征都给拟合掉了,得不偿失。

3.2 滤波:把「噪音」挡在门外

滤波这件事,我刚开始做的时候也犯过迷糊。低通、高通、带通,到底用哪个?其实很简单——

  • 低通滤波:保留低频,滤掉高频。适合看轴承的旋转频率、转频及其谐波。
  • 高通滤波:保留高频,滤掉低频。适合看轴承的固有频率、早期故障冲击。
  • 带通滤波:只保留某个频段。这是最常用的,因为故障特征往往集中在某个频带里。

我举个例子。有一次诊断一台离心泵的轴承,原始信号里低频有工频干扰,高频有电磁噪声。我用了带通滤波,把500Hz到2000Hz之间的信号提取出来,故障频率一下就清晰了。嗯,这里要注意——滤波器的阶数别太高,否则会有相位失真。

from scipy.signal import butter, filtfilt

def butter_bandpass(lowcut, highcut, fs, order=4):
    nyq = 0.5 * fs
    low = lowcut / nyq
    high = highcut / nyq
    b, a = butter(order, [low, high], btype='band')
    return b, a

def bandpass_filter(data, lowcut, highcut, fs, order=4):
    b, a = butter_bandpass(lowcut, highcut, fs, order=order)
    y = filtfilt(b, a, data)
    return y

# 使用示例
fs = 10000  # 采样率10kHz
filtered_signal = bandpass_filter(detrended_signal, 500, 2000, fs, order=4)
避坑指南:我曾经用过一个4阶巴特沃斯滤波器,结果发现滤波后的信号有延迟。后来改用filtfilt(零相位滤波),问题解决了。记住:filtfilt是双向滤波,不会引入相位偏移,但计算量会大一些。

3.3 重采样:让数据「对齐」

重采样,说白了就是改变信号的采样率。什么时候需要?

  • 两个传感器采样率不同,需要统一
  • 原始采样率太高,数据量太大,想降采样
  • 变转速工况下,需要角域重采样

我最常用的是scipy的resample函数。它内部会先做抗混叠滤波,再插值,效果不错。

from scipy import signal

# 降采样:从10000Hz降到2000Hz
original_fs = 10000
target_fs = 2000
num_samples = int(len(filtered_signal) * target_fs / original_fs)
resampled_signal = signal.resample(filtered_signal, num_samples)
我的习惯:重采样前,先检查一下原始信号有没有高频噪声。如果有,先滤波再重采样,否则混叠效应会让你后悔。我有个项目就是没注意这个,结果降采样后出现了假频,折腾了两天才发现。

3.4 降噪:把「杂质」筛掉

降噪的方法很多,我挑几个最实用的说说。

方法 适用场景 我的评价
小波阈值降噪 非平稳信号、冲击特征 效果好,但参数调起来麻烦
EMD(经验模态分解) 非线性、非平稳信号 自适应,但模态混叠是个坑
SVD(奇异值分解)降噪 周期性信号 简单粗暴,适合批量处理
中值滤波 脉冲噪声 速度快,但会平滑掉细节

我个人最常用的是小波阈值降噪。为什么?因为轴承故障信号往往是瞬态冲击,小波变换在时频域都有很好的局部化能力,能把冲击保留下来,把噪声滤掉。

import pywt

def wavelet_denoise(data, wavelet='db4', level=4):
    # 小波分解
    coeffs = pywt.wavedec(data, wavelet, level=level)
    # 计算阈值(使用通用阈值)
    sigma = np.median(np.abs(coeffs[-1])) / 0.6745
    threshold = sigma * np.sqrt(2 * np.log(len(data)))
    # 软阈值处理
    coeffs_thresh = [pywt.threshold(c, threshold, mode='soft') for c in coeffs]
    # 重构信号
    denoised = pywt.waverec(coeffs_thresh, wavelet)
    return denoised

# 使用示例
denoised_signal = wavelet_denoise(resampled_signal, wavelet='db4', level=4)

核心要点总结:

  1. 去趋势项是第一步,不做的话后续特征全偏
  2. 滤波要选对类型,带通滤波最常用
  3. 重采样前先抗混叠,否则会出假频
  4. 降噪方法选小波,参数调好效果佳

好了,信号预处理这块就聊到这儿。你想想看,这些步骤其实都是在做同一件事——把信号里「不想要」的部分去掉,把「想要」的部分留下来。做得好,后面的故障诊断就事半功倍;做得不好,那就是在垃圾堆里找金子。

下一章咱们聊聊时域特征提取,到时候我会分享一些我踩过的坑,保证让你少走弯路。

专注资料整理