告别数据抖动!用STM32F103RCT6和ADS1115实现高稳定电压采集的滤波实战
STM32F103RCT6与ADS1115高精度电压采集的噪声抑制实战
在工业传感器监测、电池管理系统等场景中,ADC采集的电压信号常因环境干扰出现微小波动。这种看似不起眼的"数据抖动",轻则导致显示数值闪烁,重则引发系统误判。本文将基于STM32F103RCT6与16位ADC芯片ADS1115的组合,深入解析五种实用滤波算法的实现与选型策略。
1. 硬件架构与噪声源分析
1.1 系统组成框架
典型的电压采集系统包含三个关键部分:
- 传感器前端:温度、压力等传感器输出的模拟信号
- 信号调理电路:通常包含分压、跟随、滤波等基础电路
- ADC转换模块:ADS1115的差分输入范围可达±6.144V
注意:当ADDR引脚接地时,ADS1115的I2C地址固定为0x90,需在代码中明确定义
1.2 常见噪声类型对照表
| 噪声类型 | 特征描述 | 典型来源 |
|---|---|---|
| 热噪声 | 宽带白噪声 | 电阻元件发热 |
| 电源纹波 | 周期性波动(50/100Hz) | 开关电源、电机干扰 |
| 量化噪声 | 阶梯状误差 | ADC分辨率限制 |
| 交叉干扰 | 突发性尖峰 | 邻近信号线串扰 |
2. 基础滤波算法实现
2.1 滑动平均滤波
最易实现的滤波方法,适合处理高斯白噪声:
#define SAMPLE_SIZE 8 float movingAverage(float newSample) { static float buffer[SAMPLE_SIZE]; static uint8_t index = 0; static float sum = 0; sum -= buffer[index]; buffer[index] = newSample; sum += buffer[index]; index = (index + 1) % SAMPLE_SIZE; return sum / SAMPLE_SIZE; }窗口大小选择经验:
- 快速变化信号:4-8点
- 缓慢变化信号:16-32点
- 功耗敏感场景:避免过大窗口
2.2 中位值平均滤波
结合中值滤波和平均滤波的优势,对脉冲噪声有较好抑制:
float medianAverageFilter(float samples[], uint8_t size) { float temp; // 冒泡排序 for(int i=0; i<size-1; i++) { for(int j=0; j<size-i-1; j++) { if(samples[j] > samples[j+1]) { temp = samples[j]; samples[j] = samples[j+1]; samples[j+1] = temp; } } } // 去除两端极值后求平均 float sum = 0; for(int k=1; k<size-1; k++) { sum += samples[k]; } return sum/(size-2); }3. 进阶滤波技术
3.1 一阶滞后滤波
适合资源有限的实时系统,计算量极小:
#define ALPHA 0.2f // 滤波系数(0~1) float firstOrderFilter(float newSample) { static float lastValue = 0; lastValue = ALPHA * newSample + (1-ALPHA) * lastValue; return lastValue; }系数调整技巧:
- α接近1:快速响应但滤波效果弱
- α接近0:平滑性好但响应延迟明显
- 电池电压监测推荐α=0.1~0.3
3.2 卡尔曼滤波简化实现
针对线性系统的高效估计算法:
typedef struct { float q; // 过程噪声协方差 float r; // 观测噪声协方差 float p; // 估计误差协方差 float k; // 卡尔曼增益 float x; // 系统状态 } KalmanFilter; float kalmanUpdate(KalmanFilter* kf, float measurement) { // 预测阶段 kf->p = kf->p + kf->q; // 更新阶段 kf->k = kf->p / (kf->p + kf->r); kf->x = kf->x + kf->k * (measurement - kf->x); kf->p = (1 - kf->k) * kf->p; return kf->x; }提示:初次使用时建议q=0.001, r=0.1,再根据实际效果微调
4. 多算法性能对比测试
4.1 测试环境配置
使用信号发生器注入1Vpp正弦波叠加10mV噪声,通过STM32的USART输出原始数据与滤波结果:
# 最小化测试代码框架 while(1) { raw = ADS1115_Read(ADC_CH0); filtered = kalmanUpdate(&kf, raw); printf("%.3f,%.3f\n", raw*0.1875/1000, filtered*0.1875/1000); HAL_Delay(10); }4.2 性能指标对比表
| 算法类型 | 响应时间(ms) | RAM占用(Byte) | 噪声抑制比 | 代码复杂度 |
|---|---|---|---|---|
| 滑动平均 | <1 | 32 | 60% | ★☆☆☆☆ |
| 中位值平均 | 5 | 40 | 75% | ★★☆☆☆ |
| 一阶滞后 | <1 | 4 | 50% | ★☆☆☆☆ |
| 卡尔曼滤波 | 2 | 20 | 85% | ★★★☆☆ |
| 窗口加权平均 | 3 | 64 | 70% | ★★☆☆☆ |
5. 工程实践优化建议
5.1 采样率与滤波协同设计
ADS1115支持8SPS到860SPS的可编程数据速率,不同场景下的推荐配置:
高速动态测量(如电机电流):
- DR=860SPS
- 配合滑动平均(窗口4-8点)
精密静态测量(如温度监测):
- DR=8SPS
- 使用中位值平均+滞后滤波组合
5.2 异常值处理机制
在滤波前增加数据有效性检查:
#define VALID_RANGE 0.5f // 允许波动范围(V) float lastValidValue = 0; float safeFilter(float newSample) { if(fabs(newSample - lastValidValue) > VALID_RANGE) { return lastValidValue; // 保持上次有效值 } lastValidValue = movingAverage(newSample); return lastValidValue; }实际项目中,将ADS1115的ALERT引脚连接到STM32的外部中断引脚,可以立即感知超限信号。这种硬件级的异常检测机制比软件轮询更加及时可靠。
