单片机开发中常用的7种滤波算法!
- 2025-07-07 13:20:55
🔷🔷🔷
在每一个嵌入式项目中,工程师都像一位在嘈杂环境中努力捕捉清晰声音的调音师。来自传感器的原始数据,就如同混杂着环境噪声的初始音源,而滤波算法,正是我们手中那款功能各异的“调音台”。我们的挑战在于,这个“调音台”——微控制器(MCU)——性能并非无限,它的计算速度和内存空间都极其宝贵,并且整个系统还要求我们做出近乎实时的响应。是在追求极致平滑的信号时耗尽MCU的算力,还是选择一个轻量级方案以保证系统的流畅运行?这正是一门关于“权衡与取舍”的艺术。为了帮助您在这门艺术中游刃有余,我们精选了7种嵌入式开发中最常用、最高效的滤波算法。接下来,让我们一同探索这些工具的特性,学习如何为您的项目搭配出最优的“滤波组合拳”。
限幅滤波法 (Clipping Filter)
-------------
🟠原理: 设定一个允许的最大偏差阈值。当采集到新值时,将其与上一次的有效值比较,如果二者差值的绝对值超过该阈值,就认为新值是无效的“野点”,并用上一次的值来代替它。
🟠优点:实现非常简单,计算开销极小。能有效消除偶然出现的、幅度很大的脉冲干扰。
🟠缺点:对于连续的、小幅度的噪声无能为力。如果信号本身变化就很快,可能会将正常的信号变化当作干扰滤除。
🟠适用场景: 测量值基本稳定,但偶尔会受到强干扰导致数据跳变。如:ADC采样、数字量输入检测。
float limit_filter(float new_value)
{
static int num = 0;
static float value = 0; //需要赋一个初值
num ++;
if(num == 1)
value = new_value;
else
{
if ( ( new_value - value > DEVIATION ) || ( value - new_value > DEVIATION ))
return value;
}
return new_value;
}
中位值滤波法 (Median Filter)
-------------
🟠原理: 开辟一个N个元素的缓冲区,连续采集N个样本放入其中(N通常取奇数,如3, 5, 7)。将这N个样本进行排序,取最中间的那个值作为本次滤波的输出。
🟠优点:对椒盐噪声(随机出现的尖峰脉冲)有极佳的抑制效果。相比均值滤波,它能更好地保留信号的边缘细节,不易模糊。
🟠缺点:需要一个缓冲区来存储N个样本,占用RAM。排序过程消耗CPU资源,在N较大时,计算量会明显增加。
🟠适用场景: 信号中存在不规则的尖峰干扰,且需要保留信号的跳变特性。如:有开关噪声干扰的传感器、图像处理领域。
float middle_filter( float new_value)
{
static float value_buf[MIDDLE_FILTER_N];
float temp ;
uint8_t count, i;
for ( count = 0; count < MIDDLE_FILTER_N - 1; count++)
{
value_buf[count] = value_buf[count + 1] ;
}
value_buf[MIDDLE_FILTER_N - 1] = new_value;
for (i = MIDDLE_FILTER_N; i > 0 ; i --)
{
if ( value_buf[i] < value_buf[i - 1] )
{
temp = value_buf[i];
value_buf[i] = value_buf[i - 1];
value_buf[i - 1] = temp;
}
if(value_buf[i] > 0)
{
if ( value_buf[i] < value_buf[i - 1] )
{
temp = value_buf[i];
value_buf[i] = value_buf[i - 1];
value_buf[i - 1] = temp;
}
}
else if(value_buf[i] < 0)
{
if ( value_buf[i] > value_buf[i - 1] )
{
temp = value_buf[i];
value_buf[i] = value_buf[i - 1];
value_buf[i - 1] = temp;
}
}
}
return value_buf[(MIDDLE_FILTER_N - 1) / 2];
}
算术平均滤波法 (Arithmetic Mean Filter)
-------------
🟠原理: 连续采集N个样本,将这N个样本值求和,再除以N,得到的结果即为滤波输出。为了提高实时性和效率,通常使用“滑动平均”(Moving Average)的方式实现。
🟠优点:对周期性、符合高斯分布的随机噪声有很好的平滑效果。算法简单,N越大,信号越平滑。
🟠缺点:对信号变化的响应迟钝,有明显的滞后。对偶然的脉冲干扰抑制效果差,一个坏点会拉偏整个平均值。
🟠适用场景: 信号变化缓慢、实时性要求不高的场合。如:测量温度、湿度、电压等慢变量。
float average_filter(float new_value)
{
static float average_value_buf[AVERAGE_N];
static float average_sum = 0;
uint8_t count;
average_sum -= average_value_buf[0];
for ( count = 0; count < AVERAGE_N - 1; count++)
{
average_value_buf[count] = average_value_buf[count + 1] ;
}
average_value_buf[AVERAGE_N - 1] = new_value;
average_sum += average_value_buf[11];
return (average_sum /(AVERAGE_N * 1.0) );
}
消抖滤波法 (Debounce Filter)
-------------
🟠原理: 此算法专门用于处理机械开关(如按键)的抖动。它不是平滑模拟信号,而是确认一个数字状态的改变。方法是检测到输入状态变化后,等待一个短暂的延时(如20ms),再次确认输入状态是否与之前一致。如果一致,才认为状态真正发生了改变。
🟠优点:能可靠地消除机械触点闭合与断开瞬间的电平抖动。是处理按键、开关等数字输入必不可少的环节。
🟠缺点:会带来一个固定的响应延迟。不适用于模拟量的滤波。
🟠适用场景: 所有机械按键、开关、继电器等数字量输入信号的检测。
float shake_filter( float new_value , float now_value)
{
static uint8_t count = 0;
if(now_value != new_value)
{
count++;
if (count >= SHAKE_N)
{
count = 0;
return new_value;
}
}
return now_value;
}
一阶滞后滤波法 (First-Order Lag Filter)
-------------
🟠原理: 也常被称为一阶低通滤波或RC滤波。它模拟了硬件RC电路的滤波效果。其核心是一个递推公式:本次输出 = A * 本次采样 + (1-A) * 上次输出。系数A(0 < A < 1)决定了滤波的强度,A越小,滤波效果越平滑,但响应越慢。
🟠优点:计算量极小,资源消耗低,只需存储上一次的输出值实时性好,平滑度可以通过调整系数A来灵活控制。
🟠缺点:存在相位滞后。对脉冲干扰抑制能力一般。
🟠适用场景: 通用性极强,是MCU中最常用的滤波算法之一,适用于绝大多数需要平滑处理且对实时性有要求的模拟量。
float first_order_lag_filter( float new_value)
{
static float first_order_value , first_order_last_value;
first_order_value = first_order_last_value;
first_order_last_value = new_value;
return (1 - FIRST_LAG_PROPORTION) * first_order_value +
FIRST_LAG_PROPORTION * new_value;
}
加权递推平均滤波法 (Weighted Recursive Average Filter)
-------------
🟠原理:这是一阶滞后滤波法的一种延伸和别称。它强调了“递推”(Recursive,本次输出依赖于上次输出)和“加权”(Weighted,对当前采样值和历史滤波结果赋予不同权重)这两个核心思想。一阶滞后滤波法是其最经典、最简单的实现形式。
🟠优点:在平滑度和响应速度之间取得了很好的平衡。资源消耗小,实现高效。
🟠缺点:相比简单的算术平均,理解上稍复杂一点。
🟠适用场景: 与一阶滞后滤波法完全相同,适用于需要平衡平滑度和灵敏度的各种场合。
uint8_t coe[WEIGHT_AVERAGE_N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t sum_coe = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12;
float weighted_filter(float new_value)
{
static float weight_average_buf[WEIGHT_AVERAGE_N];
uint8_t count;
float sum = 0;
for ( count = 0; count < AVERAGE_N - 1 ; count++)
{
weight_average_buf[count] = weight_average_buf[count+ 1] ;
}
weight_average_buf[AVERAGE_N - 1] = new_value;
for (count = 0 ; count < WEIGHT_AVERAGE_N; count++)
sum += weight_average_buf[count] * coe[count];
return (sum / (sum_coe * 1.0));
}
卡尔曼滤波 (Kalman Filter)
-------------
🟠原理:一种最优化的自回归数据处理算法。它不仅仅是“滤波”,更是一个“预测-校正”的循环过程。它会建立一个系统状态模型,根据上一时刻的状态预测当前时刻的状态,再用当前的测量值来校正这个预测,从而得到一个最优的估计值。
🟠优点:理论上是处理高斯白噪声下线型系统问题的最优滤波器。非常适合用于融合多种传感器数据(如加速度计和陀螺仪融合姿态解算)。不仅能滤波,还能预测系统状态。
🟠缺点:数学模型复杂,理解和实现难度大。计算量远大于前几种算法,对MCU性能要求高。需要对系统进行精确建模,模型不准会导致效果变差。
🟠适用场景: 高精度系统,如飞行器姿态解算(IMU)、机器人定位导航、电池电量SOC估算等。
// 一维卡尔曼滤波的简化模型 (估计一个恒定电压)
float x_hat = 0; // 状态的后验估计值 (最优估计)
float P = 1; // 后验估计的协方差 (不确定度)
float Q = 0.01; // 过程噪声协方差 (预测模型本身的不确定度)
float R = 0.1; // 测量噪声协方差 (传感器测量的误差)
float K; // 卡尔曼增益
float kalman_filter(float measurement) {
// 1. 预测 (因为模型是恒定电压,预测值就是上一次的最优值)
float x_hat_minus = x_hat;
float P_minus = P + Q;
// 2. 更新 (校正)
K = P_minus / (P_minus + R); // 计算卡尔曼增益
x_hat = x_hat_minus + K * (measurement - x_hat_minus); // 更新最优估计
P = (1 - K) * P_minus; // 更新不确定度
return x_hat;
}
END


往期精选:

请点下【♡】给小编加鸡腿


- 点赞 0
-
分享
微信扫一扫
-
加入群聊
扫码加入群聊