信号处理与预处理:别让脏数据骗了你的判定
产线上采集到的信号,很少有直接能用的。我用过一百多种传感器,从振动、电流到温度、压力,每个原始信号都带着噪声、偏移、断点和时间错位。不处理干净就做 Pass/Fail 判定?我只能说,结果大概率和抽奖差不多。
这一章,咱们把预处理拆成四个关键动作:滤波去噪、归一化、缺失值处理、时序对齐。做完这四步,信号才算“能看”。
滤波去噪:先把高频毛刺和低频漂移干掉
产线上最常见的噪声有两类:高频随机毛刺和工频干扰。我见过一条汽车零件产线,振动信号的 50Hz 谐波直接淹没了轴承故障特征,一开始怎么调模型都过不了。后来加了一个陷波滤波器,准确率从 62% 直接跳到 94%。
常用的滤波方法,我整理了一张表,你挑着用:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 低通滤波 | 抑制高频噪声,保留趋势 | 简单快 | 边缘变钝 |
| 高通滤波 | 去除直流漂移 | 消除基线慢变 | 可能放大噪声 |
| 带通/陷波 | 固定频率干扰(如 50Hz) | 精准去除 | 参数需要知道频率 |
| 中值滤波 | 椒盐噪声、孤立野点 | 不模糊阶跃 | 窗口大小敏感 |
| 小波去噪 | 非平稳信号,突变保留 | 多分辨率好 | 计算量大 |
我个人的习惯:先做一次中值滤波干掉离群点,再根据噪声频段上低通或陷波。顺序别搞反——先低通会把离群点展宽,更难处理。
举一个 Python 示例,假设你有一维振动信号:
import scipy.signal as sg
# 低通滤波,截止频率 100Hz,采样率 1000Hz
b, a = sg.butter(4, 100/(1000/2), btype='low')
filtered = sg.filtfilt(b, a, raw_signal)
# 中值滤波 窗口 5
median_filtered = sg.medfilt(raw_signal, kernel_size=5)
注意 filtfilt 没有相位延迟,我推荐用它。普通 lfilter 会引入时延,对对齐步骤麻烦。
归一化:把不同量纲拉到同一个尺子上
归一化的原因很简单——压力传感器量程 0-100 MPa,温度传感器 0-200°C,直接塞进模型?量级大的那个会主导权重,判定就偏了。
两类归一化方法最常用:
- Min-Max 归一化:缩放到 [0,1]。适合数据边界已知的场景。注意离群点会把正常值压缩得很小——我曾经有一条线,某个传感器偶尔跳到最大值,导致正常数据几乎变成一条直线。后来改用分位数裁剪再归一化,效果就对了。
- Z-score 标准化:减去均值除以标准差。适合信号波动范围不稳定,但大致符合正态分布的情况。工业信号中很多电流、振动符合这个假设。
警告:归一化的参数(min、max、mean、std)必须在训练集上计算,然后应用到测试集和产线实时数据。不能在全部数据上算一次,否则会引入未来信息,导致离线验证虚高。
缺失值处理:断点比坏数据更致命
产线设备偶尔掉线,传感器瞬间归零,或者通信丢包,都会产生缺失值。我的经验是:宁可插值也不要直接填充 0。零点会让模型误以为是“正常停止”——我吃过这个亏,一个焊接夹具的空程信号被当作正常数据送进模型,模型学到了“到 0 就是停”,结果出事了。
常用的方法排序:
- 线性插值:默认选择。短缺口(1~3 个点)效果很好。
- 样条插值:缺口稍长(5~10 个点),且信号较平滑时用。
- 前向/后向填充:只能用于长时间缺失的辅助变量(如环境温度),主信号别用。
- 删除:如果连续缺失超过信号长度的 5%,我建议直接扔了这一段。硬补只会制造虚假特征。
import numpy as np
import pandas as pd
# 线性插值
df = pd.DataFrame({'signal': raw_signal})
df['signal'] = df['signal'].interpolate(method='linear')
# 样条插值(需要设置 order)
df['signal'] = df['signal'].interpolate(method='spline', order=2)
避坑:如果缺失发生在信号波形的拐点、峰值附近,线性插值会削峰。我曾经在一个注塑机压力峰值处插值,结果真实 120 MPa 被拉到了 105 MPa,Pass/Fail 边界全乱了。这种情况用样条或者自适应插值更好。
时序对齐:不同采样频率、不同触发时间的统一
产线上一台设备三个传感器:振动 10 kHz,电流 1 kHz,温度 10 Hz。不做对齐,你拿什么特征做判定?我见过有人直接把高低频信号拼接——简直灾难。
对齐的核心思想是:选择一个基准时间轴,其他传感器信号通过插值投影到该轴上。
具体操作流程:
- 确定基准信号:通常是采样率最高、触发最早的信号。我一般用振动信号做基准。
- 生成基准时间戳向量
t_ref。 - 对其他每个传感器,用
numpy.interp或scipy.interpolate将其采样值映射到t_ref。
import numpy as np
# 基准时间轴 t_ref (假设振动信号)
# 电流信号 time_curr, value_curr
curr_aligned = np.interp(t_ref, time_curr, value_curr)
注意两点:
- 如果需要相位准确,尽量用
scipy.signal.resample或resample_poly,但速度慢。对于产线实时应用,线性插值已经够用。 - 如果信号间有明显物理延时(比如温度传感器滞后于电流),要先用互相关或已知延时做移位补偿。我个人的方法:计算互相关峰值位置,然后用这个偏移量提前对齐。
关键总结:预处理不是为了好看,是为了让模型看到真实的物理状态。滤波去除噪声,归一化消除量纲,插值填补断点,对齐统一时间——四步做完,你的信号才能放心喂给判定模型。
嗯,到这儿预处理的核心套路就讲完了。这四个步骤的顺序我一般建议:先滤波,再缺失值处理(因为插值前需要干净信号),然后归一化,最后对齐。但如果你用的是深度学习模型直接消费原始信号,顺序可以适当调整——不过这是后话了。