第4章 数据采集与预处理:采样率设置、滤波去噪、异常值剔除、数据归一化
传感器标定这件事,说白了就是让传感器说“人话”。但传感器本身是个老实孩子,它只会把物理量转成电信号,中间掺杂的各种噪声、毛刺、异常跳变,它一概不管。所以数据采集与预处理,就成了标定流程里最磨人、也最关键的一步。
我刚开始做传感器标定时,吃过不少亏。有一次采集压力传感器数据,采样率设得太低,结果标定出来的曲线跟实际差了10%。后来才明白——采样率不是随便拍脑袋定的。今天咱们就把这块掰开揉碎了讲清楚。
核心要点:数据预处理做得好,标定精度差不了。这一步花的时间,后面建模、验证阶段都会加倍还给你。
4.1 采样率设置:别太快,也别太慢
采样率怎么定?我个人的习惯是:先搞清楚传感器信号的最高频率成分。根据奈奎斯特采样定理,采样率至少是信号最高频率的2倍。但实际工程中,我一般取5~10倍。
举个例子:
- 温度传感器(变化慢):采样率 1~10 Hz 就够了
- 加速度传感器(振动信号):采样率 100~1000 Hz 起步
- 麦克风(音频信号):采样率 8~48 kHz 常见
我在项目中遇到过一件事:一个工业压力传感器,信号本身只有几赫兹的变化,但现场有50Hz的工频干扰。如果采样率只设10Hz,工频干扰会被混叠到低频段,根本滤不掉。所以采样率必须高于干扰频率的2倍,我通常设到200Hz以上,再用数字滤波处理。
小技巧:如果你不确定信号频率,可以先设一个较高的采样率(比如1kHz),采集一段数据后做FFT分析,看看频谱分布,再回头调整采样率。这叫“先粗采,再精采”。
4.2 滤波去噪:把脏东西洗掉
传感器信号里最常见的噪声有三种:
- 高频噪声:来自电路、电磁干扰
- 工频干扰:50Hz/60Hz 的市电干扰
- 低频漂移:温度变化、器件老化引起
滤波方法我常用这几类:
| 噪声类型 | 推荐滤波器 | 说明 |
|---|---|---|
| 高频噪声 | 低通滤波(移动平均、巴特沃斯) | 截止频率设为信号最高频率的1.5~2倍 |
| 工频干扰 | 陷波滤波器(Notch Filter) | 专门干掉50Hz或60Hz |
| 低频漂移 | 高通滤波或差分测量 | 去除缓慢变化的基线漂移 |
我一般先用移动平均滤波做快速处理,再用巴特沃斯低通滤波做精细处理。移动平均的窗口大小很关键——窗口太小滤不干净,窗口太大会把有用信号也抹平了。我的经验是:窗口大小取信号周期的1/10左右。
// 移动平均滤波示例(C语言)
#define WINDOW_SIZE 10
float buffer[WINDOW_SIZE];
int index = 0;
float sum = 0;
float moving_average(float new_sample) {
sum -= buffer[index];
buffer[index] = new_sample;
sum += new_sample;
index = (index + 1) % WINDOW_SIZE;
return sum / WINDOW_SIZE;
}
注意:滤波会引入相位延迟。如果你需要实时控制,建议用零相位滤波(如filtfilt函数),或者用卡尔曼滤波这种更高级的方法。我曾经因为没注意相位延迟,导致控制系统的响应慢了半拍,折腾了两天才找到原因。
4.3 异常值剔除:别让一颗老鼠屎坏了一锅粥
传感器偶尔会抽风——突然跳一个离谱的值。这种异常值如果不剔除,标定出来的模型会严重偏离真实情况。
我常用的异常值检测方法:
- 3σ原则:数据超出均值±3倍标准差,视为异常
- 箱线图法:超出Q1-1.5*IQR或Q3+1.5*IQR的为异常
- 中值绝对偏差法:对非正态分布数据更鲁棒
我个人偏爱中值绝对偏差法,因为它对离群点不敏感。举个例子:
// 中值绝对偏差法(Python伪代码)
import numpy as np
def remove_outliers_mad(data, threshold=3.0):
median = np.median(data)
mad = np.median(np.abs(data - median))
modified_z_scores = 0.6745 * (data - median) / mad
return data[np.abs(modified_z_scores) < threshold]
我曾经在一个温度标定项目里,传感器偶尔会因为接触不良跳出一个-40℃的值。如果不剔除,标定曲线会往左偏一大截。用了MAD方法后,这些异常值被干净地剔除了,标定精度从±2℃提升到了±0.5℃。
避坑指南:异常值剔除后,记得检查剔除比例。如果超过5%的数据被剔除,说明传感器本身可能有问题,或者你的阈值设得太严了。我曾经遇到过这种情况——不是传感器坏了,而是现场振动太大,后来加了减震措施才解决。
4.4 数据归一化:让不同量纲的数据站在同一起跑线
归一化,说白了就是把不同范围的数据映射到同一个尺度上。比如一个传感器输出0~5V,另一个输出4~20mA,如果不归一化,直接拿去建模,量纲大的那个会主导模型。
常用的归一化方法:
- Min-Max归一化:映射到[0,1]区间
- Z-score标准化:均值为0,标准差为1
- 小数定标归一化:除以10的幂次
我一般这样选:
- 如果数据分布比较均匀,用Min-Max归一化
- 如果数据有离群点,用Z-score标准化更稳健
- 如果后续要用神经网络,建议用Z-score,因为梯度下降更稳定
// Min-Max归一化示例
float min_max_normalize(float value, float min, float max) {
return (value - min) / (max - min);
}
// Z-score标准化示例
float z_score_normalize(float value, float mean, float std) {
return (value - mean) / std;
}
重要提醒:归一化的参数(min、max、mean、std)必须在训练集上计算,然后应用到验证集和测试集上。千万别用全部数据计算参数——那会造成数据泄露,标定结果会虚高。我见过有人犯这个错,标定精度显示99%,实际应用时只有60%。
4.5 本章知识体系
下面这张图把数据采集与预处理的流程串起来了。你可以把它当作一个检查清单,每次做标定前对照着走一遍。
嗯,数据预处理这块内容不少,但每一步都值得认真对待。采样率设对了,滤波选好了,异常值剔干净了,归一化做规范了——后面的标定建模工作就会顺畅很多。
我个人的经验是:预处理阶段多花一小时,后面建模阶段能省一整天。别急着跳到最后一步,先把数据伺候舒服了。