传感器数据采集:选型与原理实战
各位工程师朋友,咱们接着聊。上一章我们把售货机的电源系统理清楚了,这一章我带你看看传感器这块。说白了,售货机能不能「感知」周围环境,全靠这些小家伙。我在轨道交通项目里摸爬滚打这么多年,传感器选型这块踩过的坑,真能写本书。
温度传感器:别小看这玩意儿
温度传感器,听起来简单吧?但选型不对,数据能偏到你怀疑人生。
我常用的方案:
- 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 采一次。
好了,传感器这块就聊到这儿。下一章我们讲「执行器控制」,到时候我会聊聊电机驱动、加热器控制这些实战内容。记住,传感器选型没有「最好」,只有「最合适」。多看看数据手册,多跑跑测试,比什么都强。