3、数据采集基础:采样定理、量化误差、采样率选择

好,咱们进入第三章。说实话,这一章是后面所有滤波和融合算法的地基。你想想看,如果原始数据都采得稀里糊涂,后面再牛的卡尔曼滤波也救不回来。我见过太多工程师,花大把时间调滤波参数,结果问题出在采样率压根没选对——嗯,这种坑我自己也踩过。

3.1 采样定理:别让信号“说谎”

先聊采样定理。说白了就是一句话:采样频率必须大于信号最高频率的两倍。这就是奈奎斯特采样定理的核心。

为什么会这样?我打个比方。你拿手机拍旋转的车轮,有时候会发现车轮好像在倒转。这就是采样率不够,产生了“混叠”。在传感器数据里,混叠会让高频信号伪装成低频信号,你根本察觉不到。

核心公式:

fs > 2 × fmax

其中 fs 是采样率,fmax 是信号中的最高频率分量。

我在做惯性导航项目时遇到过一件事。一个加速度计输出的振动信号,我凭感觉设了100Hz采样率。结果数据里总有个诡异的0.5Hz漂移,查了三天硬件,最后发现是电机振动(约80Hz)混叠下来的假信号。从那以后,我养成了一个习惯:采样前先看频谱

我的经验:

实际工程中,我一般取3~5倍的最高频率,而不是刚好2倍。留点余量,心里踏实。比如信号最高100Hz,我会选300~500Hz采样。

3.2 量化误差:精度与成本的博弈

采样率搞定后,下一个问题就是:用多少位来存这个数据?这就是量化。

量化误差,说白了就是真实值和数字值之间的差距。比如一个12位ADC,满量程3.3V,那么它的分辨率是:

分辨率 = 3.3V / 2^12 = 3.3V / 4096 ≈ 0.8mV

也就是说,你永远无法分辨小于0.8mV的变化。这个误差是随机的,但范围是固定的:±半个LSB(最低有效位)。

ADC位数 量化级数 3.3V下的分辨率 典型应用
8位 256 12.9 mV 低成本温度传感器
10位 1024 3.2 mV 简单电位计读取
12位 4096 0.8 mV 大多数传感器(我常用)
16位 65536 0.05 mV 高精度称重传感器
24位 16777216 0.2 μV 地震仪、生物电信号

我个人习惯:货架传感器场景下,12位是甜点。8位太粗糙,16位以上成本高且噪声也大。你想想看,一个12位的加速度计,配上合适的采样率,已经能应付绝大多数仓储环境监测了。

注意:

量化误差和噪声不是一回事。量化误差是原理性的,你换再好的芯片也消除不了。但可以通过过采样+求平均来“稀释”它——后面章节会细讲。

3.3 采样率选择:不是越快越好

很多新手觉得采样率越高越好。其实不然。我见过有人用1kHz采温度,结果数据全是噪声,还占满了存储空间。

采样率选择,我总结了三步法:

  1. 确定信号带宽:你的传感器在测什么?温度变化多快?振动频率多高?
  2. 确定所需分辨率:你需要多精细的数据?0.1°C够不够?
  3. 考虑系统资源:MCU跑得动吗?存储够吗?功耗允许吗?

举个例子。我在做一个冷库温度监测项目时,温度变化很慢,每分钟也就波动0.5°C。我选了1Hz采样率,每秒钟采一次。为什么?因为温度信号最高频率大概在0.01Hz左右,1Hz已经远超2倍要求了。而且1Hz的数据量小,一个STM32F0就能跑一年。

但如果是振动监测,比如货架上的加速度计检测叉车经过时的震动,那信号可能有50Hz。这时候我会选200Hz采样,留足余量。

我的选型口诀:

慢信号,低采样,省电省存储。
快信号,高采样,防混叠要记牢。
实在拿不准,先看频谱再定调。

3.4 避坑指南:我曾经踩过的坑

讲几个真实案例,你以后遇到了能少走弯路。

坑一:忽略抗混叠滤波器

我曾经在一个项目里,直接用ADC采电机电流信号。采样率设了1kHz,觉得够快了。结果数据里总有莫名其妙的低频波动。后来发现电机PWM频率是20kHz,虽然我采样率低,但20kHz的信号通过非线性效应产生了低频分量。解决办法:在ADC前端加一个简单的RC低通滤波器,截止频率设在500Hz左右。

坑二:量化位数选太高

有个同事用24位ADC采一个普通压力传感器。结果最后4位全是噪声,白白浪费了资源。其实那个传感器本身精度只有0.5%,12位完全够用。记住:系统的精度由最弱的环节决定,不是ADC位数越高越好。

坑三:采样率频繁切换

有些工程师喜欢根据数据变化快慢动态调整采样率。想法很好,但实现起来容易出问题。滤波器的参数往往和采样率绑定,一变采样率,滤波效果就变了。我建议:固定采样率,用软件调整数据输出频率。比如内部固定100Hz采样,但你可以每10个点输出一个平均值,等效10Hz输出。

一个小技巧:

如果你不确定该用多少采样率,先设一个较高的值(比如1kHz),采一段数据,然后用FFT看看频谱。信号集中在哪个频段,你就知道了。这比拍脑袋靠谱得多。

3.5 实战:一个温度采集的配置示例

最后给个实际配置的例子。假设我们要监测仓库温度,温度变化很慢,传感器是DS18B20(数字输出,精度0.5°C)。

/* 温度采集配置示例 */
#define SAMPLE_RATE_HZ    1       // 每秒采一次
#define ADC_RESOLUTION    12      // 12位精度(DS18B20原生12位)
#define ANTI_ALIASING_FILTER 0    // 数字传感器,内部已处理

void temperature_task_init(void) {
    // 配置定时器,每1000ms触发一次采样
    timer_set_period(TIM2, 1000);  // 单位ms
    timer_enable(TIM2);
    
    // 配置DS18B20为12位模式
    ds18b20_set_resolution(DS18B20_12BIT);
    
    printf("温度采集初始化完成:采样率=%dHz, 分辨率=%d位\n", 
           SAMPLE_RATE_HZ, ADC_RESOLUTION);
}

void temperature_task_callback(void) {
    float temp = ds18b20_read_temperature();
    // 这里可以加一个简单的滑动平均滤波
    // 后面章节会详细讲
    buffer_push(temp);
}

这个配置很简单,但很实用。1Hz采样率,12位精度,对于仓库温度监测绰绰有余。你想想看,如果换成100Hz采样,MCU大部分时间都在读温度,功耗上去了,数据量大了,但精度并没有提升——何必呢?

好,这一章就到这里。采样定理、量化误差、采样率选择,这三个概念是数据采集的基石。下一章我们开始讲具体的滤波算法,到时候你会感谢今天打下的基础。