ADC采样滤波的常用算法与选型指南

ADC采样滤波的常用算法与选型指南 在嵌入式系统开发中ADC模数转换 是连接模拟世界与数字世界的桥梁。无论是电源电压监控、环境温度采集、还是音频信号处理我们都离不开 ADC。但ADC 采样必然伴随噪声—— 电源纹波、电磁干扰、电路杂散信号都会让采样数据产生波动、毛刺直接使用原始数据会导致系统判断错误、控制抖动、精度暴跌。噪声不可避免但可以通过软件滤波算法驯服。不同应用场景对数据的实时性、平滑度、抗突发干扰能力要求天差地别选错滤波算法轻则数据滞后重则功能失效。本文将深度拆解 5 种嵌入式最常用的 ADC 滤波算法算术平均滤波、滑动平均滤波、中值滤波、一阶滞后滤波、限幅滤波从核心原理、优缺点、适用场景到可直接移植的 C 语言标准代码再到实战波形对比与选型决策树帮你一次性搞定 ADC 滤波所有问题。首先把总结内容放在前面1 算术平均滤波原理连续采集N次ADC采样数据将所有数据求和后除以N得到的平均值作为最终有效数据。核心逻辑是用大量采样抵消噪声的正负波动。适用场景温湿度传感器、气压传感器等信号几乎无突变的场景。/** * brief 算术平均滤波 * param hadc: ADC句柄 * param times: 采样次数 * retval 滤波后结果 */ uint16_t Arithmetic_Average_Filter(ADC_HandleTypeDef *hadc, uint8_t times) { uint32_t sum 0; // 连续采样N次 for(uint8_t i0; itimes; i) { HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc, 10); sum HAL_ADC_GetValue(hadc); } // 求平均值 return sum / times; }2 滑动平均滤波原理维护一个固定长度N的采样队列每次新采样值覆盖队列中最旧的值再对整个队列求平均。相比算术平均他不会等待N次采样完成实时性大幅提升。适用场景电源电压监控、液位检测等需要实时性、同时要求数据平滑的场景。#define SLIDE_WIN_SIZE 10 // 滑动窗口大小 uint16_t slide_buf[SLIDE_WIN_SIZE] {0}; uint8_t slide_index 0; /** * brief 滑动平均滤波 * param hadc: ADC句柄 * retval 滤波后结果 */ uint16_t Slide_Average_Filter(ADC_HandleTypeDef *hadc) { uint32_t sum 0; // 新值覆盖旧值 HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc, 10); slide_buf[slide_index] HAL_ADC_GetValue(hadc); // 循环队列 if(slide_index SLIDE_WIN_SIZE) slide_index 0; // 求和平均 for(uint8_t i0; iSLIDE_WIN_SIZE; i) { sum slide_buf[i]; } return sum / SLIDE_WIN_SIZE; }3 中值滤波原理采集N次采样值后从小到大排序剔除最大值和最小值的干扰取中间位置的值作为结果对突发尖峰、脉冲干扰有“秒杀级”的过滤效果。适用场景工业现场、电机周边、强电磁环境下的ADC采样、专门对抗尖峰毛刺。/** * brief 中值滤波 * param hadc: ADC句柄 * param times: 采样次数建议奇数3/5/7 * retval 滤波后结果 */ uint16_t Median_Filter(ADC_HandleTypeDef *hadc, uint8_t times) { uint16_t buf[times]; // 采样 for(uint8_t i0; itimes; i) { HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc, 10); buf[i] HAL_ADC_GetValue(hadc); } // 冒泡排序 for(uint8_t i0; itimes-1; i) { for(uint8_t j0; jtimes-i-1; j) { if(buf[j] buf[j1]) { uint16_t temp buf[j]; buf[j] buf[j1]; buf[j1] temp; } } } // 返回中间值 return buf[times/2]; }4 一阶滞后滤波原理软件模拟硬件RC低通滤波器本次结果 系数 × 新采样值 1 - 系数× 上一次结果。仅需保存上一次结果内存占用极低。适用场景单片机、资源极度紧张的设备信号变化平缓的采集场景。#define ALPHA 0.2f // 滤波系数 0α1越小越平滑 uint16_t last_filter_val 0; /** * brief 一阶滞后滤波 * param hadc: ADC句柄 * retval 滤波后结果 */ uint16_t First_Order_Filter(ADC_HandleTypeDef *hadc) { HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc, 10); uint16_t new_val HAL_ADC_GetValue(hadc); // 核心公式 uint16_t filter_val (uint16_t)(ALPHA * new_val (1 - ALPHA) * last_filter_val); last_filter_val filter_val; return filter_val; }5 限幅滤波原理设定最大允许变化阈值若本次采样值与上一次差值超过阈值直接判定为无效数据使用上一次值替代。作用是剔除极端错误值必须搭配其他滤波算法使用。适用场景所有ADC采样的第一道防线防止异常数据击穿后续逻辑。#define AMP_LIMIT 50 // 最大变化幅度阈值 uint16_t last_val 0; /** * brief 限幅滤波前级保护 * param now_val: 本次原始采样值 * retval 限幅后结果 */ uint16_t Amplitude_Limit_Filter(uint16_t now_val) { if(now_val last_val) { // 超过上限丢弃本次值 if((now_val - last_val) AMP_LIMIT) return last_val; } else { if((last_val - now_val) AMP_LIMIT) return last_val; } last_val now_val; return now_val; }6 组合滤波限幅滤波中值滤波滑动平均uint16_t Combo_Filter(ADC_HandleTypeDef *hadc) { // 1. 限幅滤波 HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc,10); uint16_t adc_val Amplitude_Limit_Filter(HAL_ADC_GetValue(hadc)); // 2. 中值滤波 uint16_t median_val Median_Filter(hadc, 5); // 3. 滑动平均滤波 return Slide_Average_Filter(hadc); }