嵌入式滤波算法模块
嵌入式滤波算法实战:从理论到代码的 11 种实现方案
引言:为什么嵌入式系统离不开滤波?
你是否遇到过温湿度传感器数据跳变 5℃?电机电流采样被高频噪声淹没?在嵌入式开发中,这类问题几乎每天都在发生——原始信号 ≠ 可用信号,这正是物理世界与数字系统之间的核心矛盾。当传感器采集的温度、压力、电流等模拟量经过 ADC 转换后,往往夹杂着电源波动、电磁干扰、机械振动等各种噪声,直接使用这些"脏数据"可能导致控制系统误判、设备异常停机,甚至引发安全事故。
滤波技术就像一位精准的"翻译官",它能有效剥离信号中的噪声干扰,保留真实物理量的本质特征。无论是温湿度传感器的微小漂移,还是工业现场的强电磁干扰,合适的滤波算法都能让数字系统"听懂"物理世界的真实语言。
本文将聚焦嵌入式场景下的滤波技术实战,通过 11 种经典滤波算法的全流程解析,带你从零构建可复用的滤波模块。我们会严格遵循"理论选型 → 代码实现 → 实战调优"的递进逻辑:先通过直观案例理解每种算法的适用场景(比如滑动平均适合平滑缓慢变化的温度信号,卡尔曼滤波适配动态系统的实时预测),再提供可直接移植的 C 语言代码片段(包含内存优化技巧与边界条件处理),最后结合工业级项目经验总结调参指南(如窗口大小与响应速度的平衡法则)。无论你是刚接触嵌入式开发的新手,还是需要解决实际工程问题的资深工程师,都能在这里找到从原理到落地的完整解决方案。
滤波算法基础与选型指南
嵌入式场景下的噪声类型与滤波需求匹配
在嵌入式系统中,传感器采集的数据往往会受到各种噪声干扰,这些"数据杂音"可能导致控制逻辑误判、测量精度下降。要解决这个问题,第一步是准确识别噪声类型,再匹配合适的滤波算法。本文将通过"噪声类型 → 算法特性 → 选型决策树"的逻辑,帮你快速找到最优滤波方案。
三大典型噪声与适配算法
1. 随机噪声:数据中的"无规律抖动"
随机噪声表现为数据在真实值附近无规律波动,常见于温度传感器、压力传感器等模拟信号采集场景。这类噪声的特点是波动幅度较小但频率高,如同在平稳的信号上叠加了"细微波纹"。
适配算法包括:
-
滑动平均滤波:通过计算最近 N 个采样值的平均值来平滑波动。核心参数 N 决定平滑程度:N 值越大噪声抑制效果越好,但响应速度会降低。实际应用中建议根据数据动态性选择 5-15 的窗口大小,例如温度监测可设 N=10,而快速变化的液位检测建议 N=5。
/******************************************************************************
* @brief 滑动平均滤波:把连续N个采样值看为一个队列,队列长度固定为N,每次采样到的新数据放入队尾,
* 并扔掉原来队首的一次数据(先进先出原则)。把队列中的N个数据进行算数平均运算,就可以获得新的滤波结果。
* @param value_buf 待处理的数据
* @param len 长度
* len 的选取:流量:N=12; 压力:N=4; 液面:N=4~12; 温度:N=1~4;
* @return 返回滤波后的值
* @note 优点:对周期性干扰有良好的抑制作用,平滑度高;试用于高频振荡的系统
* @note 缺点:灵敏度低;对偶然出现的脉冲性干扰的抑制作用较差,不适于脉冲干扰较严重的场合
* 比较浪费RAM(改进方法,减去的不是队首的值,而是上一次得到的平均值)
******************************************************************************/
#define N 10
int value_buf[N];
int sum = 0;
int curNum = 0;
int moveAverageFilter(int value)
{
if(curNum < N)
{
value_buf[curNum] = value;
sum += value_buf[curNum];
curNum++;
return sum/curNum;
}
else
{
sum -= sum/N;
sum += value;
return sum/N;
}
}
-
加权平均滤波:对近期数据赋予更高权重,在平滑噪声的同时提升响应速度。核心参数为各采样点的权重系数,通常采用"近期权重大、远期权重小"的原则,如权重序列[0.1, 0.2, 0.3, 0.4]。
/******************************************************************************
* @brief 加权移动平均(WMA):计算的是一个固定窗口内的所有数据点的平均值
* @param data 待处理的数据
* @param window_size 指数移动平均的窗口大小(平滑因子)
* @return 返回滤波后的值
* @note 优点:最近的数据点具有更大的权重,这使得滤波器更加敏感于最近的变化,同时也能平滑数据。
* @note 缺点:如果权重选择不当,可能会导致过度平滑或不足平滑。
******************************************************************************/
double weighted_moving_average(double *data,int window_size) {
double sum = 0.0;
double weight_sum = 0.0;
for (int i = 0; i < window_size; i++) {
sum += data[i] * (window_size - i);
weight_sum += (window_size - i);
}
return sum / weight_sum;
}
关键选型依据:当数据变化缓慢(如环境监测),优先选滑动平均滤波;若需平衡实时性与平滑度(如电机电流监测),加权平均滤波更合适。
2. 脉冲噪声:数据中的"突发干扰" 脉冲噪声表现为偶尔出现的"尖峰信号",可能由电磁干扰、传感器接触不良等引起,如工业现场的强电设备启停时,电压传感器可能突然出现远超正常范围的数值。
适配算法包括:
-
中位值滤波:取连续奇数个采样值的中间值作为有效数据,能有效剔除孤立脉冲。核心参数是窗口大小(建议3-7个采样点),窗口过大会导致响应延迟。例如在按键检测中,采用3次采样取中位值可过滤机械抖动。
/******************************************************************************
* @brief 中位值滤波:输入一组连续采集后的数据,将数据按大小排列,然后取中间值为本次有效值
* @param value_buf 待处理的数据
* @param len 长度
* @return 返回滤波后的值
* @note 优点:能够克服偶然因素引起的波动干扰,对温度、液位等变化缓慢的被测参数有良好的滤波效果
* @note 缺点:对于流量、速度等快速变化的参数不宜
******************************************************************************/
int middleValueFilter(int value_buf[],int len)
{
int i,j,k,temp;
for(j = 0 ; j < len-1; ++j)
{
for(k = 0; k < len-j-1; ++k)
{
//从小到大排序,冒泡法排序
if(value_buf[k] > value_buf[k+1])
{
temp = value_buf[k];
value_buf[k] = value_buf[k+1];
value_buf[k+1] = temp;
}
}
}
return value_buf[(len-1)/2];
}
-
限幅平均滤波:先判断新数据是否在合理范围内(通过阈值限制),超出则丢弃,未超出则纳入平均计算。核心参数为"限幅阈值Δ",通常设为正常数据波动范围的1.5-2倍,如光照传感器正常波动±50,可设Δ=100。
/******************************************************************************
* @brief 限幅平均滤波:相当于“限幅滤波法”+“递推平均滤波法”,每次采样到的新数据先进行
* 限幅处理再送入队列进行递推平均滤波处理。
* @param value_buf 待处理的数据
* @param len 数据长度,len一定要大于M的设定值
* @return 返回滤波后的值
* @note 优点:对于偶然出现的脉冲性干扰,可消除有其引起的采样值偏差。
* 效果显著,有效的解决了实际场景下突变噪声对AD采样的影响
* @note 缺点:比较浪费RAM
******************************************************************************/
#define Z 50 //限制幅度阈值
#define M 12
int data[M];
int First_flag=0;
int LAverageFilter(int value_buf[],int len)
{
int i;
int temp,sum,flag=0;
data[0] = value_buf[0];
for(i=1;i<M && i <len && flag <3;i++)
{
temp = value_buf[i];
if((temp-data[i-1])>Z||((data[i-1]-temp)>Z))
{
i--;flag++;
}
else
{
data[i]=temp;
}
}
if(flag >= 3) {
//需要调整A的值或者保证数据的最低效率处理方法:算数平均滤波
for(i=0;i<len;i++) {
sum += value_buf[i];
}
return sum/len;
}
for(i=0;i<M && i<len;i++)
{
sum+=data[i];
}
return sum/M;
}
抗脉冲技巧:对于高频脉冲(如每100个数据出现1-2次异常),中位值滤波效率更高;若脉冲伴随小幅波动,限幅+平均的组合方案效果更佳。
3. 高频噪声:数据中的"高频毛刺" 高频噪声表现为信号中叠加的高频振荡,常见于电机驱动、射频电路等强电磁环境,示波器上常显示为"密集毛刺"。这类噪声的频率通常高于有用信号,可通过低通滤波"过滤高频成分"。
适配算法包括:
-
一阶低通滤波:通过RC滤波原理实现,核心参数为滤波系数α(0<α<1)。α值越小高频抑制效果越强,如α=0.1时滤波效果明显但响应慢,α=0.5时兼顾滤波与响应。计算公式: filtered_data = α*new_data + (1-α)*last_data
。
/******************************************************************************
* @brief 一阶互补滤波
* @param newValue 新采集出来的值
* @param oldValue 上一次采集出来的值
* @param a 权重(范围0~1,需要不断的调整)
* @return 滤波后的值
* @note 优点:对周期性干扰具有良好的抑制作用,适用于波动频率较高的场合。
* 缺点:相位滞后,灵敏度低,滞后程度取决于a值大小,不能消除滤波频率高于采样频率的1/2的干扰信号.
******************************************************************************/
int firstOrderFilter(int newValue, int oldValue, float a)
{
return a * newValue + (1-a) * oldValue;
}
-
二阶Butterworth滤波:比一阶滤波具有更陡峭的幅频特性,适合严格抑制特定高频段。核心参数为截止频率fc,需根据采样频率fs计算(通常取fc=fs/10~fs/5)。
/******************************************************************************
* @brief 低通滤波器:使用一个二阶巴特沃斯低通滤波器,使用离散时间实现。适用于去除高频噪声或平滑信号。
* @param cutoff_frequency 截止频率
* @param sample_rate 采样频率
* @param data 输入信号数组
* @param size 数组的大小
* @return 滤波后的值
* @note 优点:非常适合信号处理,可以有效地过滤掉高频噪声。
* 缺点:需要一定的信号处理知识,特别是对于数字滤波器的设计。
******************************************************************************/
double butterworth_low_pass(double *data, int size, double cutoff_frequency,