数据预处理:异常值检测与剔除、滑动窗口滤波、中值滤波、数据归一化
各位工程师朋友,大家好。今天我们来聊聊传感器标定中一个绕不开的环节——数据预处理。
说实话,我见过不少新手,拿到传感器数据就直接往算法里塞。结果呢?标定出来的曲线歪歪扭扭,根本没法用。为什么?因为原始数据里藏着太多“脏东西”。
我个人习惯,拿到数据第一件事不是分析,而是“洗数据”。今天就把我这些年积累的四个核心技巧分享给你:异常值检测、滑动窗口滤波、中值滤波、数据归一化。
核心观点:数据预处理做得好,标定精度至少提升30%。这不是夸张,是我在多个项目中验证过的。
一、异常值检测与剔除
异常值,说白了就是那些“离谱”的数据点。比如你测温度,室温25°C,突然蹦出一个85°C——这明显是传感器抽风了。
常用的方法有两种:
- 3σ原则:假设数据服从正态分布,超出均值±3倍标准差的数据视为异常。这个方法简单粗暴,适合大多数场景。
- 箱线图法:基于四分位数,用IQR(四分位距)来判断。鲁棒性更好,不受极端值影响。
我在项目中遇到过一个问题:用3σ原则剔除异常值时,如果数据本身就有多个异常点,均值和标准差会被“带偏”。这时候我建议先用箱线图法粗筛一遍,再用3σ原则精筛。
我的小技巧:不要一次性剔除所有异常值。分批次处理,每次剔除后重新计算统计量,效果更稳定。
import numpy as np
def detect_outliers_3sigma(data):
mean = np.mean(data)
std = np.std(data)
lower = mean - 3 * std
upper = mean + 3 * std
return [x for x in data if lower <= x <= upper]
def detect_outliers_iqr(data):
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr
return [x for x in data if lower <= x <= upper]
注意:异常值不一定是“坏数据”。有时候它代表真实的物理现象,比如传感器瞬间受到冲击。剔除前一定要结合业务场景判断。
二、滑动窗口滤波
滑动窗口滤波,我习惯叫它“移动平均”。原理很简单:取当前点前后N个点的平均值,代替当前点的值。
你想想看,传感器数据通常带有高频噪声,比如电源纹波、环境振动。滑动窗口滤波就像一把梳子,把这些毛刺梳平了。
关键参数:窗口大小
- 窗口太小:滤波效果不明显,噪声还在
- 窗口太大:信号延迟严重,细节丢失
我曾经在一个压力传感器标定项目里,窗口大小试了5、10、20、50。最后发现15个点最合适——既滤掉了泵的脉动噪声,又保留了压力变化的趋势。
def sliding_window_filter(data, window_size=5):
half = window_size // 2
result = []
for i in range(len(data)):
start = max(0, i - half)
end = min(len(data), i + half + 1)
result.append(np.mean(data[start:end]))
return result
避坑指南:我曾经犯过一个错误——对整段数据一次性做滑动窗口滤波。结果边界处的数据因为窗口不完整,出现了明显的偏差。后来我改用“镜像填充”或“边缘复制”来处理边界,效果好了很多。
三、中值滤波
中值滤波和滑动窗口滤波有点像,但它取的是中位数,而不是平均值。
为什么要用中位数?因为中位数对异常值不敏感。举个例子:一组数据 [1, 2, 3, 4, 100],平均值是22,中位数是3。你看,一个100就把平均值拉飞了,但中位数纹丝不动。
中值滤波特别适合处理:
- 椒盐噪声(随机出现的白点或黑点)
- 传感器瞬间跳变(比如接触不良导致的毛刺)
我记得有一次做激光测距传感器的标定,数据里时不时蹦出几个“飞点”。用滑动窗口滤波根本压不住,换成中值滤波后,那些飞点就像被橡皮擦掉了一样干净。
def median_filter(data, window_size=5):
half = window_size // 2
result = []
for i in range(len(data)):
start = max(0, i - half)
end = min(len(data), i + half + 1)
result.append(np.median(data[start:end]))
return result
经验之谈:中值滤波的窗口大小建议选奇数。为什么?因为奇数个数的中位数就是中间那个数,偶数个数的中位数需要取平均,计算量稍大,而且效果差别不大。
四、数据归一化
归一化,就是把不同量纲的数据映射到同一个尺度上。比如温度0-100°C,压力0-10MPa,如果不归一化,压力变化对模型的影响会被温度“淹没”。
两种常用方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Min-Max归一化 | x' = (x - min) / (max - min) | 数据分布有明确边界,比如角度0-360° |
| Z-score标准化 | x' = (x - mean) / std | 数据分布近似正态,或者有异常值 |
我个人习惯,标定数据预处理时优先用Min-Max归一化。为什么?因为标定通常需要把传感器输出映射到物理量,Min-Max能保持原始数据的分布形状,方便后续拟合。
注意:归一化时,min和max要用训练集的统计量,不能包含测试集的数据。否则会造成数据泄露,标定结果虚高。
def min_max_normalize(data):
min_val = np.min(data)
max_val = np.max(data)
return (data - min_val) / (max_val - min_val)
def z_score_normalize(data):
mean = np.mean(data)
std = np.std(data)
return (data - mean) / std
知识体系总览
下面这张图,是我自己总结的数据预处理流程。你把它打印出来贴在工位上,每次做标定前看一眼,保证不会漏步骤。
这张图把整个流程串起来了。你注意看,异常值检测是第一步,滤波是第二步,归一化是最后一步。顺序不能乱,否则会影响效果。
最后说一句:数据预处理没有银弹。每种方法都有它的适用场景,关键是要理解你的数据特点。多试几次,找到最适合的那一套组合拳。
好了,今天就聊到这里。希望这些实战经验能帮你少走弯路。