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