单片机常见滤波算法
单片机常用滤波算法详解
文档说明
本文档聚焦单片机应用场景,整理了 6 种最常用的滤波算法,涵盖算法原理(含核心公式)、优缺点分析、可直接移植的 C 语言代码,适配 51、STM32 等主流单片机,兼顾 “资源占用少、易实现” 的嵌入式开发特点。
目录
-
滤波算法概述
-
各滤波算法详解
-
2.1 限幅滤波(程序判断滤波)
-
2.2 中位值滤波
-
2.3 算术平均滤波
-
2.4 滑动平均滤波
-
2.5 加权滑动平均滤波
-
2.6 一阶滞后滤波(指数平滑滤波)
-
算法选型指南
-
总结
1. 滤波算法概述
单片机采集传感器数据(如 ADC 电压、温度、压力)时,易受电磁干扰、机械抖动等影响产生噪声,滤波算法的核心是剔除无效噪声,保留有效信号趋势。嵌入式场景下,滤波算法需满足:RAM / 算力占用低、代码简洁、参数易调试。
2. 各滤波算法详解
2.1 限幅滤波(程序判断滤波)
2.1.1 算法原理
核心逻辑:设定数据允许变化的最大幅度阈值,若当前采样值与上一次有效值的偏差超出阈值,判定为噪声,沿用前一次有效值;否则取当前采样值。
核心判断条件:
|X(n) - Y(n-1)| leq Delta rightarrow Y(n) = X(n)
|X(n) - Y(n-1)| > Delta rightarrow Y(n) = Y(n-1)
-
X(n):第 n 次采样值
-
Y(n):第 n 次滤波输出值
-
Y(n-1):第 n-1 次滤波输出值
-
Delta:最大允许偏差阈值
2.1.2 优缺点
| 优点 | 缺点 |
|---|---|
| 代码极简,RAM / 算力占用极低 | 无法滤除连续突变的噪声 |
| 响应速度极快(无延迟) | 阈值Delta需根据传感器精度调试,设置不当易失真 |
| 适合剔除单次瞬时跳变噪声 | 仅能抑制 “幅度超阈值” 的噪声,对小幅随机噪声无效 |
2.1.3 核心代码(C 语言)
// 配置参数:根据传感器精度调整最大允许偏差
#define LIMIT_DELTA 10
// 保存上一次有效滤波值,初始值可设为传感器默认值
uint16_t last_limit_val = 0;
/**
* @brief 限幅滤波函数
* @param current_val 当前采样值(如ADC读取值)
* @return 滤波后的值
*/
uint16_t limit_filter(uint16_t current_val) {
uint16_t filter_val;
// 偏差超出阈值则沿用前值,否则取当前值
if (abs(current_val - last_limit_val) > LIMIT_DELTA) {
filter_val = last_limit_val;
} else {
filter_val = current_val;
}
// 更新上一次有效值
last_limit_val = filter_val;
return filter_val;
}
2.2 中位值滤波
2.2.1 算法原理
核心逻辑:连续采集奇数个样本,对样本排序后取中间值作为输出,彻底剔除极端异常值。
核心步骤:
-
采集 N 个样本(N 为奇数,如 3/5/7);
-
对样本排序;
-
取第N/2位的中间值作为滤波输出。
2.2.2 优缺点
| 优点 | 缺点 |
|---|---|
| 彻底剔除脉冲干扰、极端异常值 | 需连续采集 N 个样本,响应速度受采样个数影响 |
| 对随机噪声抑制效果好 | 不适合快速变化的信号(如高速电压波动) |
| 代码实现简单,无需复杂计算 | 采样过程需短延时,占用少量 RAM 存储样本 |
2.2.3 核心代码(C 语言)
// 配置参数:采样个数(必须为奇数,建议3/5/7)
#define MEDIAN_SAMPLE_CNT 5
/**
* @brief 中位值滤波函数
* @note adc_read()需替换为实际的传感器采样函数
* @return 滤波后的值
*/
uint16_t median_filter(void) {
uint16_t sample_buf[MEDIAN_SAMPLE_CNT];
uint16_t temp;
// 1. 连续采集N个样本
for (uint8_t i = 0; i < MEDIAN_SAMPLE_CNT; i++) {
sample_buf[i] = adc_read(); // 替换为实际采样逻辑
delay_ms(1); // 短延时避免采样过于集中
}
// 2. 冒泡排序(嵌入式场景最易实现)
for (uint8_t i = 0; i < MEDIAN_SAMPLE_CNT - 1; i++) {
for (uint8_t j = 0; j < MEDIAN_SAMPLE_CNT - 1 - i; j++) {
if (sample_buf[j] > sample_buf[j+1]) {
temp = sample_buf[j];
sample_buf[j] = sample_buf[j+1];
sample_buf[j+1] = temp;
}
}
}
// 3. 返回中间值
return sample_buf[MEDIAN_SAMPLE_CNT / 2];
}
2.3 算术平均滤波
2.3.1 算法原理
核心逻辑:连续采集 N 个样本,计算算术平均值作为输出,平滑随机噪声。
核心公式:
Y = frac{1}{N} sum_{i=1}^{N} X_i
-
X_i:第 i 次采样值
-
N:采样个数
-
Y:滤波输出值
2.3.2 优缺点
| 优点 | 缺点 |
|---|---|
| 实现简单,对平稳随机噪声平滑效果好 | N 越大滤波效果越好,但响应速度越慢 |
| 计算量小,适合资源受限的单片机 | 对脉冲干扰抑制差(极端值会拉低 / 拉高平均值) |
| 适合慢变信号(如温度、湿度) | 需连续采集 N 个样本,实时性差 |
2.3.3 核心代码(C 语言)
// 配置参数:采样个数(N越大滤波越强,响应越慢)
#define AVG_SAMPLE_CNT 10
/**
* @brief 算术平均滤波函数
* @note adc_read()需替换为实际的传感器采样函数
* @return 滤波后的值
*/
uint16_t avg_filter(void) {
// 用32位变量避免求和溢出
uint32_t sum = 0;
for (uint8_t i = 0; i < AVG_SAMPLE_CNT; i++) {
sum += adc_read();
delay_ms(1);
}
// 返回平均值
return (uint16_t)(sum / AVG_SAMPLE_CNT);
}
2.4 滑动平均滤波
2.4.1 算法原理
核心逻辑:建立固定长度的缓冲区,新采样值入队时,最旧的采样值出队,始终对缓冲区中所有值求平均,平衡实时性与滤波效果。
核心公式:
Y(n) = frac{1}{M} left( sum_{i=1}^{M-1} Y_{buf}(i) + X(n) right)
-
M:缓冲区长度
-
Y_{buf}(i):缓冲区中第 i 个旧值
-
X(n):当前新采样值
-
Y(n):当前滤波输出值
2.4.2 优缺点
| 优点 | 缺点 |
|---|---|
| 实时性优于算术平均滤波(无需等待 N 个样本) | 需占用缓冲区 RAM(M 越大,RAM 占用越多) |
| 输出无明显延迟,平滑效果可调 | 对脉冲干扰抑制效果一般 |
| 适合常规传感器(如液位、压力) | 缓冲区长度 M 需调试,过大会降低响应速度 |
2.4.3 核心代码(C 语言)
// 配置参数:缓冲区长度(建议为2的幂次,优化除法效率)
#define SLIDE_BUF_LEN 8
// 采样缓冲区(初始化为0)
uint16_t slide_buf[SLIDE_BUF_LEN] = {0};
// 缓冲区索引(循环更新)
uint8_t buf_index = 0;
// 缓冲区总和(避免重复求和,提升效率)
uint32_t slide_sum = 0;
/**
* @brief 滑动平均滤波函数
* @param current_val 当前采样值
* @return 滤波后的值
*/
uint16_t slide_avg_filter(uint16_t current_val) {
// 1. 减去即将被移出的旧值
slide_sum -= slide_buf[buf_index];
// 2. 新值入队
slide_buf[buf_index] = current_val;
slide_sum += current_val;
// 3. 更新索引(循环缓冲区)
buf_index = (buf_index + 1) % SLIDE_BUF_LEN;
// 4. 返回平均值(SLIDE_BUF_LEN为2的幂次时,可用移位代替除法)
return (uint16_t)(slide_sum / SLIDE_BUF_LEN);
}
2.5 加权滑动平均滤波
2.5.1 算法原理
核心逻辑:对滑动缓冲区中的样本分配不同权重(近期样本权重更高),计算加权和后除以权重总和,兼顾实时性与平滑效果。
核心公式:
Y = frac{sum_{i=1}^{M} W_i times X_i}{sum_{i=1}^{M} W_i}
-
M:缓冲区长度
-
W_i:第 i 个样本的权重(近期样本权重更高)
-
X_i:第 i 个样本值
-
Y:滤波输出值
2.5.2 优缺点
| 优点 | 缺点 |
|---|---|
| 近期数据权重高,更贴合信号实时趋势 | 代码复杂度略高,权重需调试 |
| 兼顾滤波效果与响应速度 | 占用 RAM 略多(缓冲区 + 权重数组) |
| 适合有噪声且需快速响应的信号 | 算力消耗略高于普通滑动平均 |
2.5.3 核心代码(C 语言)
// 配置参数:缓冲区长度
#define WEIGHT_BUF_LEN 5
// 权重系数(近期数据权重更高,总和建议为10/100,方便计算)
uint8_t weight[WEIGHT_BUF_LEN] = {1, 2, 3, 4, 5};
// 权重总和(1+2+3+4+5)
#define WEIGHT_SUM 15
// 采样缓冲区
uint16_t weight_buf[WEIGHT_BUF_LEN] = {0};
// 缓冲区索引
uint8_t weight_index = 0;
/**
* @brief 加权滑动平均滤波函数
* @param current_val 当前采样值
* @return 滤波后的值
*/
uint16_t weight_slide_filter(uint16_t current_val) {
// 1. 新值入队
weight_buf[weight_index] = current_val;
weight_index = (weight_index + 1) % WEIGHT_BUF_LEN;
// 2. 计算加权和
uint32_t weight_sum = 0;
for (uint8_t i = 0; i < WEIGHT_BUF_LEN; i++) {
// 索引偏移,保证最新值对应最高权重
uint8_t idx = (weight_index + i) % WEIGHT_BUF_LEN;
weight_sum += weight_buf[idx] * weight[i];
}
// 3. 返回加权平均值
return (uint16_t)(weight_sum / WEIGHT_SUM);
}
2.6 一阶滞后滤波(指数平滑滤波)
2.6.1 算法原理
核心逻辑:通过平滑系数 α 融合当前采样值与上一次滤波结果,平衡滤波效果与响应速度。
核心公式:
Y(n) = alpha times X(n) + (1 - alpha) times Y(n-1)
-
alpha:平滑系数(0 < α < 1)
-
X(n):当前采样值
-
Y(n-1):上一次滤波输出值
-
Y(n):当前滤波输出值
2.6.2 优缺点
| 优点 | 缺点 |
|---|---|
| 代码极简,RAM / 算力占用极低 | 对快速变化信号响应慢(α 越小越慢) |
| α 可调,兼顾滤波效果与响应速度 | α 设置不当易导致信号滞后或滤波不足 |
| 适合慢变 + 随机噪声的信号(如温度) | 浮点版需单片机支持 FPU(如 STM32) |
2.6.3 核心代码(C 语言)
整数版(适配 51 等无 FPU 单片机)
// 配置参数:平滑系数(0<α<10,对应实际α=α/10)
#define ALPHA 2
// 保存上一次滤波值
uint16_t last_lag_val = 0;
/**
* @brief 一阶滞后滤波(整数版,无浮点运算)
* @param current_val 当前采样值
* @return 滤波后的值
*/
uint16_t lag_filter(uint16_t current_val) {
// 公式优化:Y(n) = (α*X(n) + (10-α)*Y(n-1)) / 10
last_lag_val = (ALPHA * current_val + (10 - ALPHA) * last_lag_val) / 10;
return last_lag_val;
}
浮点版(精度更高,适配 STM32 等有 FPU 单片机)
// 平滑系数(0<α<1,α越大响应越快)
#define ALPHA_FLOAT 0.2
// 保存上一次滤波值
float last_lag_float_val = 0.0;
/**
* @brief 一阶滞后滤波(浮点版)
* @param current_val 当前采样值
* @return 滤波后的值
*/
float lag_filter_float(float current_val) {
last_lag_float_val = ALPHA_FLOAT * current_val + (1 - ALPHA_FLOAT) * last_lag_float_val;
return last_lag_float_val;
}
3. 算法选型指南
| 应用场景 | 推荐算法 | 关键调参建议 |
|---|---|---|
| 按键消抖、电压瞬时跳变 | 限幅滤波 | Δ 设为传感器精度的 2~3 倍 |
| 工业电磁干扰、脉冲噪声 | 中位值滤波 | 采样个数设为 3/5(兼顾速度与效果) |
| 温度、湿度等慢变平稳信号 | 算术平均 + 一阶滞后 | 算术平均 N=10,α=0.2 |
| 液位、压力(需平衡响应与滤波) | 滑动平均滤波 | 缓冲区长度 M=8/16 |
| 快速变化且含噪声的信号 | 加权滑动平均 | 权重按 “1:2:3:4:5” 递增 |
| 资源极度受限(如 51 单片机) | 限幅 / 一阶滞后 | 优先用整数版代码,避免浮点运算 |
4. 总结
单片机滤波算法的核心是 “按需选型”:
-
资源优先:选限幅、一阶滞后(RAM / 算力占用最低);
-
抗脉冲干扰:选中位值滤波;
-
慢变信号平滑:选算术平均 + 一阶滞后;
-
实时性要求高:选滑动平均 / 加权滑动平均。
实际应用中可组合使用(如 “限幅 + 中位值”),先通过限幅剔除瞬时跳变,再通过中位值滤除脉冲干扰,兼顾效果与效率。所有代码均可直接移植,只需将adc_read()替换为实际的传感器采样函数(如 ADC 读取、串口数据接收)。

浙公网安备 33010602011771号