3、传感器数据采集:模拟量与数字量传感器、ADC采样原理、I2C/SPI总线协议、数据滤波算法(中值/均值)
大家好,我是老张。今天咱们聊聊传感器数据采集。
做物联网运维这些年,我见过太多设备因为数据采集不准而出问题。说白了,传感器就是物联网的「眼睛」和「耳朵」。眼睛花了,耳朵背了,后面再好的算法也是白搭。
3.1 模拟量传感器 vs 数字量传感器
先分清楚两类传感器。这个基础概念搞混了,后面全乱套。
模拟量传感器,输出的是连续变化的电压或电流信号。比如温度传感器LM35,温度每升高1℃,输出电压增加10mV。我早期做温室大棚项目时,用的就是这种。优点是便宜、响应快,缺点嘛——信号容易受干扰。
数字量传感器,内部集成了ADC和通信接口,直接输出数字信号。比如DS18B20温度传感器,一根线就能传数据。我后来做冷链监控,全换成了数字传感器。为什么?抗干扰能力强,布线也简单。
核心区别一句话:模拟量传感器输出连续信号,需要外部ADC;数字量传感器内部已数字化,直接通过协议读取。
| 对比项 | 模拟量传感器 | 数字量传感器 |
|---|---|---|
| 输出信号 | 电压/电流(连续) | 数字编码(离散) |
| 抗干扰能力 | 较弱 | 较强 |
| 成本 | 低 | 略高 |
| 典型代表 | LM35、光敏电阻 | DS18B20、BMP280 |
3.2 ADC采样原理
模拟量传感器输出的电压,MCU不认识。MCU只认0和1。这时候就需要ADC——模数转换器。
ADC的核心参数有两个:分辨率和采样率。
- 分辨率:比如10位ADC,能把0-5V分成1024份。每份约4.88mV。12位就是4096份,精度更高。
- 采样率:每秒采多少次。采集温度,每秒10次就够了。采集音频,至少每秒44100次。
我记得有一次做振动监测,用了10位ADC,结果数据波动大得没法看。换成12位后,问题立马解决。嗯,这里要注意:分辨率不是越高越好,还要考虑成本和速度。
我的经验:工业现场环境,建议至少12位ADC。如果信号变化缓慢,可以适当降低采样率来换取稳定性。
3.3 I2C/SPI总线协议
数字量传感器怎么和MCU通信?主流方案就两个:I2C和SPI。
I2C协议,两根线:SCL(时钟)和SDA(数据)。支持多设备挂在一根总线上,每个设备有唯一地址。我做过一个项目,一条I2C总线上挂了8个传感器,省了不少IO口。
但I2C有个坑——距离长了容易出问题。我曾经在一条2米长的I2C总线上挂传感器,数据经常丢包。后来加了电平转换芯片才搞定。
SPI协议,四根线:SCK、MOSI、MISO、CS。速度比I2C快得多,但占用的IO口也多。每个设备需要独立的片选线(CS)。
| 对比项 | I2C | SPI |
|---|---|---|
| 线数 | 2根 | 4根 |
| 速度 | 标准100kHz-400kHz | 可达数十MHz |
| 多设备支持 | 地址寻址,方便 | 片选控制,占用IO |
| 适用场景 | 低速、多设备 | 高速、少设备 |
避坑指南:我曾经在I2C总线上忘记加外部上拉电阻,结果通信时好时坏。查了两天才找到原因。记住:I2C的SDA和SCL必须接上拉电阻,典型值4.7kΩ。
3.4 数据滤波算法(中值/均值)
传感器数据采集回来,直接能用吗?别急。原始数据里全是噪声。你想想看,一个温度传感器,1秒内跳变3-5℃——这正常吗?肯定不正常。
这时候就需要滤波。我常用的两种:
均值滤波:连续采N次,取平均值。简单粗暴,适合平稳信号。比如采集室温,采10次取平均,效果不错。
中值滤波:连续采N次,排序后取中间值。这个对付突发干扰特别有效。比如有人碰了一下传感器,产生了一个异常高值。均值滤波会被带偏,中值滤波直接忽略它。
我做过一个水位监测项目,传感器偶尔被水浪冲击产生尖峰。用均值滤波,水位值忽高忽低。换成中值滤波后,数据稳如老狗。
// 均值滤波示例
#define FILTER_N 10
int filter_buffer[FILTER_N];
int filter_index = 0;
int mean_filter(int new_value) {
int sum = 0;
filter_buffer[filter_index] = new_value;
filter_index = (filter_index + 1) % FILTER_N;
for (int i = 0; i < FILTER_N; i++) {
sum += filter_buffer[i];
}
return sum / FILTER_N;
}
// 中值滤波示例
int median_filter(int new_value) {
int temp[FILTER_N];
// 更新缓冲区
for (int i = 0; i < FILTER_N - 1; i++) {
filter_buffer[i] = filter_buffer[i + 1];
}
filter_buffer[FILTER_N - 1] = new_value;
// 复制并排序
memcpy(temp, filter_buffer, sizeof(temp));
// 冒泡排序(简单示意)
for (int i = 0; i < FILTER_N - 1; i++) {
for (int j = 0; j < FILTER_N - i - 1; j++) {
if (temp[j] > temp[j + 1]) {
int t = temp[j];
temp[j] = temp[j + 1];
temp[j + 1] = t;
}
}
}
return temp[FILTER_N / 2];
}
我的建议:实际项目中,我经常把两种滤波组合使用。先中值滤波剔除异常值,再均值滤波平滑数据。效果比单独用一种好得多。
好了,传感器数据采集这块就聊到这儿。下一章咱们讲数据传输协议,MQTT和CoAP,到时候见。
公众号:蓝海资料掘金营,微信deep3321