传感器数据采集:选型与原理实战

各位工程师朋友,咱们接着聊。上一章我们把售货机的电源系统理清楚了,这一章我带你看看传感器这块。说白了,售货机能不能「感知」周围环境,全靠这些小家伙。我在轨道交通项目里摸爬滚打这么多年,传感器选型这块踩过的坑,真能写本书。

温度传感器:别小看这玩意儿

温度传感器,听起来简单吧?但选型不对,数据能偏到你怀疑人生。

我常用的方案:

  • DS18B20:数字输出,一线总线,抗干扰强。适合多点测温。
  • NTC热敏电阻:模拟输出,成本低。但要注意线性度问题。
  • PT100:工业级,精度高。但贵,一般用在关键部位。

数据采集原理:

DS18B20 用的是 1-Wire 协议。说白了,就是一根数据线既传数据又传时钟。我刚开始用的时候,总觉得这协议不靠谱,后来发现只要时序控制好,稳定得很。

// DS18B20 读取温度示例(伪代码)
uint8_t ds18b20_reset(void);
void ds18b20_write_byte(uint8_t data);
uint8_t ds18b20_read_byte(void);
float ds18b20_get_temperature(void) {
    uint8_t temp_l, temp_h;
    int16_t raw_temp;
    ds18b20_reset();
    ds18b20_write_byte(0xCC);  // 跳过ROM
    ds18b20_write_byte(0x44);  // 启动转换
    delay_ms(750);             // 等待转换完成
    ds18b20_reset();
    ds18b20_write_byte(0xCC);
    ds18b20_write_byte(0xBE);  // 读取暂存器
    temp_l = ds18b20_read_byte();
    temp_h = ds18b20_read_byte();
    raw_temp = (temp_h << 8) | temp_l;
    return raw_temp * 0.0625;  // 12位分辨率
}

我的经验: 在轨道交通售货机上,温度传感器别直接贴在金属外壳上。我遇到过因为热传导误差,导致温度读数比实际高3-5℃的情况。加个隔热垫,效果立竿见影。

湿度传感器:别被「精度」忽悠了

湿度传感器,我踩过最大的坑就是「精度漂移」。你想想看,售货机在站台风吹日晒的,湿度传感器用半年,读数就开始飘。

我推荐:

  • SHT30:数字输出,精度±2%RH,长期稳定性好。我项目里用得最多。
  • DHT22:便宜,但响应慢。适合不要求实时性的场景。
  • 电容式湿度传感器:抗污染能力强,但电路设计要小心。

数据采集原理:

SHT30 用的是 I2C 接口。其实原理不复杂,就是传感器内部有个电容,湿度变化引起电容变化,然后通过 ADC 转成数字量。嗯,这里要注意:I2C 总线上的上拉电阻,别随便选。我见过因为上拉电阻太小,导致通信失败的案例。

// SHT30 读取湿度示例
uint8_t sht30_read_humidity(float *humidity) {
    uint8_t data[6];
    // 发送测量命令 0x2C 0x06
    i2c_write(SHT30_ADDR, 0x2C);
    i2c_write(SHT30_ADDR, 0x06);
    delay_ms(20);
    // 读取6字节数据
    i2c_read(SHT30_ADDR, data, 6);
    // 校验CRC(这里省略)
    *humidity = 100.0 * ((data[3] << 8) | data[4]) / 65535.0;
    return 1;
}

避坑指南: 我曾经在南方某地铁站部署售货机,湿度传感器频繁失效。后来发现是冷凝水问题。解决方案:在传感器上方加个「遮阳帽」,或者用加热型湿度传感器。记住,湿度传感器最怕结露。

震动传感器:售货机的「心电图」

震动传感器,说白了就是监测售货机有没有被「欺负」。地铁站人流密集,被撞一下、踢一脚是常事。

选型方案:

  • SW-420:震动开关,便宜。但只能检测「有没有震动」,不能量化。
  • ADXL345:三轴加速度计,数字输出。能检测震动方向和强度。我推荐这个。
  • 压电式震动传感器:灵敏度高,但电路要加电荷放大器。

数据采集原理:

ADXL345 用的是 SPI 或 I2C 接口。内部有个 MEMS 结构,说白了就是微小的「弹簧-质量块」系统。震动时质量块位移,电容变化,然后转成数字量。

// ADXL345 读取震动数据
void adxl345_read_xyz(int16_t *x, int16_t *y, int16_t *z) {
    uint8_t data[6];
    // 设置测量模式
    adxl345_write_reg(0x2D, 0x08);  // 使能测量
    // 读取X、Y、Z轴数据
    adxl345_read_regs(0x32, data, 6);
    *x = (data[1] << 8) | data[0];
    *y = (data[3] << 8) | data[2];
    *z = (data[5] << 8) | data[4];
}

我的习惯: 震动传感器不要直接焊在主板上。用排线连接,这样能减少主板震动对传感器的影响。另外,采样频率别设太高,50Hz 足够。太高了数据量太大,MCU 处理不过来。

门磁传感器:开门关门,一清二楚

门磁传感器,用来检测售货机柜门是否关好。这玩意儿要是出问题,要么货被偷了都不知道,要么关门了还报警。

选型方案:

  • 干簧管+磁铁:最经典方案。成本低,可靠性高。我推荐。
  • 霍尔传感器:比干簧管寿命长,但需要供电。
  • 微动开关:机械式,容易磨损。不推荐用在频繁开关的门上。

数据采集原理:

干簧管说白了就是个磁控开关。磁铁靠近时,触点闭合;远离时,触点断开。直接接 GPIO 就行,记得加个上拉电阻。

// 门磁检测示例
uint8_t door_is_open(void) {
    // 假设门磁接在GPIO_PIN_5,低电平表示门开
    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET) {
        return 1;  // 门打开
    } else {
        return 0;  // 门关闭
    }
}

避坑指南: 我曾经遇到过干簧管「粘连」的问题——磁铁移开后,触点还吸在一起。原因是电流太大,把触点烧熔了。解决方案:串联一个1kΩ电阻限流。另外,门磁线缆要用屏蔽线,不然地铁站的电磁干扰会让你误报不断。

电流/电压传感器:售货机的「血压」

电流电压传感器,用来监测售货机各模块的供电状态。说白了,就是看看有没有「过压」、「欠压」、「过流」。

选型方案:

  • ACS712:霍尔效应电流传感器。隔离测量,安全。我常用。
  • INA219:I2C接口,能同时测电流和电压。精度高,适合低功耗场景。
  • 分压电阻+ADC:测电压最便宜的方法。但要注意电阻精度和温漂。

数据采集原理:

ACS712 内部有个霍尔元件,电流流过时产生磁场,霍尔元件输出对应的电压。说白了,就是「电流→磁场→电压」的转换。INA219 更智能,内部集成了 ADC 和运算放大器,直接输出数字量。

// INA219 读取电流和电压
float ina219_read_current(void) {
    uint16_t raw_current;
    float current;
    // 读取电流寄存器(0x04)
    raw_current = ina219_read_reg(0x04);
    // 根据量程计算实际电流
    current = (raw_current - 2048) * 0.1;  // 单位mA
    return current;
}

float ina219_read_voltage(void) {
    uint16_t raw_voltage;
    float voltage;
    // 读取电压寄存器(0x02)
    raw_voltage = ina219_read_reg(0x02);
    voltage = raw_voltage * 0.004;  // 单位V
    return voltage;
}

关键参数对比:

传感器 测量范围 精度 接口 成本
ACS712 ±5A / ±20A / ±30A ±1.5% 模拟
INA219 ±3.2A ±0.5% I2C
分压电阻+ADC 取决于分压比 取决于电阻精度 模拟

我的经验: 电流传感器千万别直接串在电源线上。我见过有人把 ACS712 串在 220V 交流线上,结果...嗯,冒烟了。记住,ACS712 是隔离的,但它的供电和信号输出是低压侧,别搞混。另外,采样电阻的功率要算好,不然电阻会发热,影响精度。

传感器数据融合:1+1>2

单个传感器数据,说实话,意义有限。但把多个传感器数据融合起来,就能做很多事。

举个例子:

  • 温度+湿度 → 判断是否结露
  • 震动+门磁 → 判断是否有人暴力开门
  • 电流+电压 → 判断压缩机是否堵转

我习惯在 MCU 里做一个简单的「传感器数据融合模块」。说白了,就是写个状态机,根据多个传感器的数据综合判断售货机状态。

// 传感器数据融合示例
typedef struct {
    float temperature;
    float humidity;
    int16_t vibration_x;
    int16_t vibration_y;
    int16_t vibration_z;
    uint8_t door_status;
    float current;
    float voltage;
} SensorData_t;

uint8_t check_abnormal_condition(SensorData_t *data) {
    // 判断是否结露
    if (data->temperature < 10 && data->humidity > 85) {
        return 1;  // 结露风险
    }
    // 判断是否暴力开门
    if (data->door_status == 1 && 
        (abs(data->vibration_x) > 500 || abs(data->vibration_y) > 500)) {
        return 2;  // 暴力开门
    }
    // 判断压缩机堵转
    if (data->current > 3.0 && data->voltage < 200) {
        return 3;  // 压缩机异常
    }
    return 0;  // 正常
}

避坑指南: 我曾经在数据融合时犯过一个低级错误——把所有传感器的采样频率都设成一样。结果导致 MCU 在某个时刻同时处理多个传感器数据,CPU 占用率飙升。解决方案:给每个传感器分配不同的采样周期,错开采样时间。比如温度每 5 秒采一次,震动每 100ms 采一次,门磁每 200ms 采一次。

好了,传感器这块就聊到这儿。下一章我们讲「执行器控制」,到时候我会聊聊电机驱动、加热器控制这些实战内容。记住,传感器选型没有「最好」,只有「最合适」。多看看数据手册,多跑跑测试,比什么都强。