4. 数据采集与信号处理:从模拟到数字的必修课
数据采集这活儿,说白了就是跟信号打交道。你传感器再好,信号调理再到位,到了ADC这一步如果没处理好,前面全白搭。我做了十几年嵌入式,见过太多人栽在采样率设置和滤波上——不是采出来的数据没法用,就是CPU被滤波算法拖死。
这一章咱们就聊聊数据采集里最核心的几个概念:采样定理、抗混叠滤波、数字滤波,还有数据校准。嗯,都是实战里绕不开的硬骨头。
4.1 采样定理(Nyquist):别让你的信号“说谎”
先问个问题:一个1kHz的正弦波,你用多快的速度去采,才能把它还原出来?
答案是至少2kHz。这就是奈奎斯特采样定理的核心——采样频率必须大于信号最高频率的两倍。低于这个值,就会出现一个很要命的现象:混叠(Aliasing)。
核心公式:
fs > 2 × fmax
其中 fs 是采样频率,fmax 是信号中的最高频率分量。
混叠是什么感觉?我举个例子。你拿手机拍风扇,有时候会看到扇叶好像倒着转,甚至不转。这就是时间域上的混叠——采样率(帧率)跟不上风扇转速,视觉系统就被骗了。
在数据采集里,混叠更危险。一个100Hz的噪声信号,你用150Hz去采,采出来会变成一个50Hz的假信号。你以为是真实数据,其实是噪声伪装成的。我曾经在一个振动监测项目里遇到过这问题,折腾了两天才发现是采样率设低了。
避坑指南:
我曾经在工业现场吃过亏——传感器输出有高频噪声,我按信号基频的2倍设采样率,结果数据全是乱的。后来才明白:采样率要按信号中最高频率分量来算,不是按你关心的频率算。
实际工程中,我一般取3~5倍,留足余量。
4.2 抗混叠滤波:采样前的最后一道防线
光提高采样率够吗?不够。因为真实世界里,信号里总有你意想不到的高频成分——电源噪声、电机干扰、射频耦合……这些频率你根本不知道,也没法提前避开。
所以,ADC之前必须加一个低通滤波器,把高于 fs/2 的成分干掉。这就是抗混叠滤波器。
| 滤波器类型 | 特点 | 适用场景 |
|---|---|---|
| 一阶RC低通 | 简单、便宜、-20dB/dec | 对阻带要求不高的低速信号 |
| 二阶有源低通 | -40dB/dec,截止特性好 | 大多数工业采集场景 |
| 高阶椭圆滤波器 | 过渡带极陡,但有通带纹波 | 高精度、窄过渡带需求 |
我个人习惯:低速信号(几百Hz以内)用二阶巴特沃斯有源滤波器,通带平坦,相位失真小。高速信号(几十kHz以上)直接用集成抗混叠滤波器芯片,省心。
实战技巧:
抗混叠滤波器的截止频率一般设在 fs/2 的 80%~90%。比如采样率 1kHz,截止频率设在 400~450Hz。留点余量,别卡在边界上。
4.3 数字滤波:软件层面的信号净化
模拟滤波做完了,数据进到MCU里,是不是就干净了?不一定。传感器本身的噪声、量化噪声、环境干扰,还会混进来。这时候就需要数字滤波上场了。
数字滤波的好处是灵活——改个参数就行,不用动硬件。我常用的有三种:移动平均、中值滤波、卡尔曼滤波。各有各的脾气。
4.3.1 移动平均滤波
最简单,也最常用。把最近N个采样值加起来求平均,输出一个平滑值。
// 移动平均滤波 - 循环队列实现
#define WINDOW_SIZE 10
float moving_average(float new_sample) {
static float buffer[WINDOW_SIZE] = {0};
static int index = 0;
static float sum = 0;
static int count = 0;
sum -= buffer[index];
buffer[index] = new_sample;
sum += new_sample;
index = (index + 1) % WINDOW_SIZE;
if (count < WINDOW_SIZE) count++;
return sum / count;
}
优点:计算量小,平滑效果好。缺点:对突变信号反应慢,而且会引入相位延迟。窗口越大,延迟越大。
我在做温度采集时常用这个——温度变化慢,延迟几秒无所谓,平滑最重要。
4.3.2 中值滤波
对付脉冲噪声(比如静电干扰、开关噪声)特别有效。原理很简单:取N个采样值,排序,取中间那个。
// 中值滤波 - 冒泡排序取中值
#define MEDIAN_WINDOW 5
float median_filter(float new_sample) {
static float buffer[MEDIAN_WINDOW] = {0};
static int index = 0;
float temp[MEDIAN_WINDOW];
int i, j;
buffer[index] = new_sample;
index = (index + 1) % MEDIAN_WINDOW;
// 拷贝并排序
for (i = 0; i < MEDIAN_WINDOW; i++) temp[i] = buffer[i];
for (i = 0; i < MEDIAN_WINDOW - 1; i++) {
for (j = 0; j < MEDIAN_WINDOW - 1 - i; j++) {
if (temp[j] > temp[j+1]) {
float t = temp[j];
temp[j] = temp[j+1];
temp[j+1] = t;
}
}
}
return temp[MEDIAN_WINDOW / 2];
}
你想想看,如果采样值里突然蹦出一个异常大的数,移动平均会被拉偏,但中值滤波完全不受影响。我做过一个压力传感器项目,现场有气动阀开关,每次动作都会产生一个尖峰干扰,中值滤波一把就解决了。
注意:
中值滤波窗口一般取奇数(3、5、7)。窗口太大,计算量上去了,而且会丢失细节。我一般不超过9个点。
4.3.3 卡尔曼滤波
这个稍微复杂点,但效果是真的好。卡尔曼滤波不是简单平滑,它是在估计系统状态——结合测量值和预测值,给出一个最优估计。
一维卡尔曼滤波的五个核心公式:
// 一维卡尔曼滤波
typedef struct {
float Q; // 过程噪声协方差
float R; // 测量噪声协方差
float x; // 估计值
float P; // 估计误差协方差
float K; // 卡尔曼增益
} Kalman1D;
float kalman_update(Kalman1D *kf, float measurement) {
// 预测
kf->P = kf->P + kf->Q;
// 更新
kf->K = kf->P / (kf->P + kf->R);
kf->x = kf->x + kf->K * (measurement - kf->x);
kf->P = (1 - kf->K) * kf->P;
return kf->x;
}
卡尔曼滤波的厉害之处在于:它能自适应。信号平稳时,它信任预测值;信号突变时,它信任测量值。我做过一个无人机高度计融合项目,气压计和超声波的数据就是靠卡尔曼滤波融合的,效果比单纯平滑好太多。
参数调优经验:
Q和R的比值决定了滤波器的响应速度。Q/R越大,滤波器越“相信”测量值,响应越快但噪声也多。我一般先设Q=0.01、R=1,然后根据实际波形微调。
4.4 数据校准与归一化:让数据说“真话”
滤波做完了,数据看起来平滑了。但还有一个问题:传感器出来的原始值,跟真实物理量之间往往有偏差。这就是校准要做的事。
4.4.1 校准:消除系统误差
校准分两步:零点和满量程。
- 零点校准:传感器在无输入时,输出应该是多少?比如压力传感器在通大气时,输出应该是0。但实际可能有个偏移量,记下来,后面减掉。
- 满量程校准:给一个已知的标准输入,看传感器输出是多少。比如给100kPa压力,输出是2.5V,那增益系数就是 100 / 2.5 = 40 kPa/V。
// 两点校准
float calibrate(float raw_value, float zero_offset, float gain) {
return (raw_value - zero_offset) * gain;
}
我习惯在系统初始化时做一次自动校准。如果环境温度变化大,还得做温度补偿——这个后面章节会细讲。
4.4.2 归一化:统一量纲
归一化就是把不同量纲、不同范围的数据,映射到同一个区间(通常是0~1或-1~1)。为什么要做?因为很多算法(比如神经网络、PID控制)对输入范围敏感。
最常用的归一化方法:
// 最小-最大归一化
float normalize_minmax(float value, float min_val, float max_val) {
if (max_val == min_val) return 0.5;
return (value - min_val) / (max_val - min_val);
}
举个例子:一个温度传感器输出范围是0~3.3V,对应温度-40~85°C。归一化后,电压值变成0~1,方便后续处理。
实战建议:
归一化的min和max最好用实际采集到的范围,而不是理论值。理论值往往偏大,导致归一化后数据集中在0.2~0.8之间,浪费了动态范围。
4.5 本章知识体系总览
说了这么多,咱们用一张图把整个数据采集与信号处理的流程串起来。从传感器原始信号,到最终可用的校准数据,每一步都不能少。
这张图把咱们这一章的核心流程串起来了。从传感器出来的模拟信号,先过抗混叠滤波器,再进ADC采样,然后做数字滤波、校准、归一化,最后得到可用的数据。每一步都有坑,每一步也都有技巧。
嗯,这一章的内容就到这儿。数据采集这事儿,理论不难,难的是现场的那些“意外”。多留个心眼,多留点余量,数据就不会骗你。