3. 传感器数据采集:倾斜传感器原理与选型、震动传感器原理与选型、ADC采样配置、传感器数据滤波算法

好,咱们进入第三章。这一章是硬骨头,也是整个货架报警系统里最核心的感知层。说白了,传感器选错了,后面算法写得再漂亮也是白搭。我见过太多项目,最后排查问题发现是传感器型号没选对,白白浪费几周时间。

3.1 倾斜传感器:原理与选型

倾斜传感器,顾名思义,就是检测物体有没有歪。货架一旦倾斜超过某个角度,说明可能快倒了,必须报警。

原理上分两种:

  • 机械式(水银开关/滚珠开关): 里面有个小珠子或者水银球,倾斜到一定角度就接通电路。便宜、简单,但精度低,有抖动误报问题。我早期做安防项目用过,后来发现货架被叉车碰一下就会误报,果断弃了。
  • MEMS加速度计式: 利用微机电系统测量重力加速度在三个轴上的分量,通过反正切算出倾斜角。精度高、可调阈值、无机械触点。我现在做货架报警,首选这种。

选型要点:

  • 量程: 货架倾斜一般不会超过45度,选±90度量程足够。别选±180度的,分辨率会下降。
  • 输出接口: 模拟电压输出(需要ADC)或数字I2C/SPI输出。我个人习惯用数字接口,省去ADC校准的麻烦。
  • 功耗: 如果是电池供电,选待机电流小于10μA的型号。

我的经验: 曾经在一个冷链仓库项目里,普通倾斜传感器低温下漂移严重。后来换了工业级(-40℃~85℃)的MEMS传感器才搞定。环境温度对传感器影响很大,选型时一定要看工作温度范围。

3.2 震动传感器:原理与选型

震动传感器用来检测货架有没有被撞击或者持续晃动。比如叉车撞了一下,或者地震来了,都需要触发报警。

常见类型:

  • 压电式: 压电陶瓷受到震动产生电荷,输出一个电压脉冲。灵敏度高,但只能检测动态震动,静态倾斜检测不了。适合检测撞击事件。
  • MEMS加速度计(震动模式): 和倾斜传感器可以是同一个芯片,只是算法不同。通过检测加速度的突变来判断震动。我建议直接用三轴加速度计,一芯两用,既测倾斜又测震动。

选型关键参数:

参数 说明 我的建议值
灵敏度 单位g对应的电压或数字量 ≥300mV/g(模拟)或 16384 LSB/g(数字)
带宽 能检测到的震动频率范围 0.5Hz ~ 100Hz(货架震动频率较低)
阈值可调 是否支持软件设置触发阈值 必须支持,否则不同货架需要不同硬件

避坑指南: 我曾经选了一款高灵敏度的压电传感器,结果货架旁边有人走路都能触发报警。后来加了数字滤波和阈值迟滞才解决。记住:传感器灵敏度不是越高越好,要和实际应用场景匹配。

3.3 ADC采样配置

如果你选的是模拟输出的传感器,那就绕不开ADC。嗯,这里我多说几句,ADC配置不好,数据全是噪声。

关键配置项:

  1. 参考电压: 一般用单片机内部参考(如2.5V或3.3V),或者外部精密参考。我建议用外部参考,内部参考温漂大,精度差。
  2. 分辨率: 10位还是12位?对于倾斜检测,10位(1024级)够用,但想检测0.1度级别的变化,建议12位以上。
  3. 采样率: 震动信号需要较高的采样率(100Hz以上),倾斜信号10Hz就够了。别一股脑全用最高采样率,功耗会飙升。
// 以STM32为例,配置ADC采样倾斜传感器
// 单次转换模式,12位分辨率,采样时间55.5个周期

void ADC_Config(void) {
    ADC_InitTypeDef ADC_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    // 配置PA0为模拟输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // ADC配置
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStruct.ADC_ScanConvMode = DISABLE;      // 单通道
    ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; // 单次转换
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStruct);

    // 配置采样时间(55.5个周期,适合高阻抗传感器)
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);

    // 使能ADC
    ADC_Cmd(ADC1, ENABLE);
    // 校准
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

小技巧: 采样时间别设太短。传感器输出阻抗高时,采样时间短了电容充不满,读数会偏小。我一般设55.5个周期起步,如果传感器输出阻抗大于10kΩ,建议用239.5个周期。

3.4 传感器数据滤波算法

原始ADC数据直接拿来用?别闹。货架在仓库里,叉车经过、人员走动、甚至风吹都会带来噪声。不滤波的话,系统会疯掉。

我常用的两种滤波:

3.4.1 中值滤波

原理很简单:连续采N个值,排序后取中间那个。对脉冲噪声(比如突然的撞击)效果极好。

// 中值滤波实现(窗口大小5)
#define MEDIAN_WINDOW_SIZE 5

uint16_t MedianFilter(uint16_t new_sample) {
    static uint16_t buffer[MEDIAN_WINDOW_SIZE];
    static uint8_t index = 0;
    uint16_t temp[MEDIAN_WINDOW_SIZE];
    uint16_t i, j, tmp;

    // 更新环形缓冲区
    buffer[index] = new_sample;
    index = (index + 1) % MEDIAN_WINDOW_SIZE;

    // 拷贝到临时数组并排序(冒泡排序,简单够用)
    for(i = 0; i < MEDIAN_WINDOW_SIZE; i++) {
        temp[i] = buffer[i];
    }
    for(i = 0; i < MEDIAN_WINDOW_SIZE - 1; i++) {
        for(j = 0; j < MEDIAN_WINDOW_SIZE - 1 - i; j++) {
            if(temp[j] > temp[j+1]) {
                tmp = temp[j];
                temp[j] = temp[j+1];
                temp[j+1] = tmp;
            }
        }
    }
    // 返回中间值
    return temp[MEDIAN_WINDOW_SIZE / 2];
}

适用场景: 震动检测。当货架被撞击时,会产生一个尖锐的脉冲信号,中值滤波能完美滤掉这个脉冲,只保留正常的震动趋势。但注意,窗口越大,延迟越大,实时性会下降。

3.4.2 滑动平均滤波

把最近N个采样值加起来求平均。对周期性噪声(比如电机振动)抑制效果好,但对脉冲噪声反应迟钝。

// 滑动平均滤波实现(窗口大小8)
#define AVG_WINDOW_SIZE 8

uint16_t MovingAverageFilter(uint16_t new_sample) {
    static uint16_t buffer[AVG_WINDOW_SIZE];
    static uint8_t index = 0;
    static uint16_t sum = 0;
    static uint8_t count = 0;

    // 减去最旧的值,加上最新的值
    sum -= buffer[index];
    sum += new_sample;
    buffer[index] = new_sample;
    index = (index + 1) % AVG_WINDOW_SIZE;

    if(count < AVG_WINDOW_SIZE) {
        count++;
    }
    return sum / count;
}

我的用法: 倾斜数据用滑动平均,因为倾斜变化是缓慢的,平均一下能滤掉高频抖动。震动数据用中值滤波,因为要保留突变信息。你想想看,如果倾斜也用中值滤波,货架慢慢歪了,中间值可能还是原来的值,报警就延迟了。

3.4.3 组合使用

实际项目中,我经常把两种滤波串起来用。先中值滤波去掉脉冲噪声,再滑动平均平滑数据。效果比单一滤波好很多。

// 组合滤波:先中值,再滑动平均
uint16_t CombinedFilter(uint16_t raw_adc) {
    uint16_t median_result = MedianFilter(raw_adc);
    uint16_t avg_result = MovingAverageFilter(median_result);
    return avg_result;
}

注意: 滤波会引入延迟。中值滤波窗口5延迟约2~3个采样周期,滑动平均窗口8延迟约4个采样周期。如果采样率是100Hz,总延迟约60ms,对货架报警来说完全可接受。但如果你做高速碰撞检测,就要权衡了。

好了,这一章的内容就这些。传感器选型、ADC配置、滤波算法,这三样搞定了,数据采集这块就稳了。下一章咱们聊聊怎么把这些数据变成报警逻辑,以及怎么和上位机通信。到时候见。