如何使用Embedded C抑制传感器偶发的突增尖峰值?
适合嵌入式C的传感器尖峰消除算法及实现
1. 改进版限幅滤波(带历史窗口)
之前的前后值对比仅依赖相邻采样,容易漏判极端尖峰或误过滤正常波动。改进后的算法结合滑动窗口的均值与偏差判断,能更精准识别尖峰。
原理:维护固定长度的滑动窗口存储最近N个有效采样值。新采样输入时,先计算窗口内的均值和绝对偏差,若当前值超出「均值±k倍偏差」范围,则判定为尖峰,用窗口均值替代;否则将新采样加入窗口并更新窗口。
C实现示例
#include <stdint.h> #define WINDOW_SIZE 5 // 根据采样率调整,3-10为宜 #define DEVIATION_MULTIPLIER 2 // 可调整,2-3倍偏差兼顾灵敏度与容错性 int16_t sensor_window[WINDOW_SIZE]; uint8_t window_ptr = 0; uint8_t window_filled = 0; // 计算窗口内均值 static int16_t calc_window_avg(void) { int32_t sum = 0; uint8_t count = window_filled ? WINDOW_SIZE : window_ptr; for (uint8_t i = 0; i < count; i++) { sum += sensor_window[i]; } return (int16_t)(sum / count); } // 计算窗口内绝对偏差(简化版,替代标准差) static int16_t calc_window_dev(int16_t avg) { int32_t sum_dev = 0; uint8_t count = window_filled ? WINDOW_SIZE : window_ptr; for (uint8_t i = 0; i < count; i++) { int16_t dev = sensor_window[i] - avg; sum_dev += (dev > 0) ? dev : -dev; } return (int16_t)(sum_dev / count); } int16_t filter_spike(int16_t new_sample) { int16_t filtered_val; int16_t avg = calc_window_avg(); int16_t dev = calc_window_dev(avg); // 尖峰判断 if ((new_sample > avg + DEVIATION_MULTIPLIER * dev) || (new_sample < avg - DEVIATION_MULTIPLIER * dev)) { filtered_val = avg; } else { filtered_val = new_sample; // 更新滑动窗口 sensor_window[window_ptr] = new_sample; window_ptr++; if (window_ptr >= WINDOW_SIZE) { window_ptr = 0; window_filled = 1; } } return filtered_val; }
2. 滑动窗口中位数滤波
中位数滤波对脉冲尖峰的抑制效果优于均值滤波,因为中位数不受极端值干扰,适合偶尔出现尖峰的场景。
原理:维护滑动窗口,每次新采样输入后,对窗口内的值排序,取中位数作为输出。嵌入式中用插入排序可减少计算量。
C实现示例
#include <stdint.h> #include <string.h> #define MEDIAN_WINDOW_SIZE 5 // 建议选奇数,如3、5、7 int16_t median_window[MEDIAN_WINDOW_SIZE]; uint8_t median_ptr = 0; uint8_t median_filled = 0; // 插入排序简化版,用于窗口内排序 static void sort_window(int16_t *arr, uint8_t len) { for (uint8_t i = 1; i < len; i++) { int16_t temp = arr[i]; uint8_t j = i; while (j > 0 && arr[j-1] > temp) { arr[j] = arr[j-1]; j--; } arr[j] = temp; } } int16_t median_filter(int16_t new_sample) { // 更新窗口 median_window[median_ptr] = new_sample; median_ptr++; if (median_ptr >= MEDIAN_WINDOW_SIZE) { median_ptr = 0; median_filled = 1; } uint8_t count = median_filled ? MEDIAN_WINDOW_SIZE : median_ptr; int16_t temp_window[MEDIAN_WINDOW_SIZE]; memcpy(temp_window, median_window, count * sizeof(int16_t)); sort_window(temp_window, count); // 返回中位数 return temp_window[count / 2]; }
3. 轻量卡尔曼滤波(针对慢变传感器)
如果是温度、压力这类慢变传感器,轻量卡尔曼滤波既能抑制尖峰,又能保留真实信号的缓慢变化,适合资源有限的嵌入式场景。
原理:通过预测和更新两个步骤,基于当前估计值和新采样值计算最优估计。遇到尖峰时,卡尔曼增益会自动降低异常值的权重。
C实现示例
#include <stdint.h> // 参数需根据传感器特性调整 #define Q 0.01 // 过程噪声方差,信号越稳定取值越小 #define R 0.1 // 测量噪声方差,传感器精度越高取值越小 float kalman_estimate = 0.0; float kalman_error = 1.0; // 初始估计误差 float kalman_filter(float new_sample) { // 预测步骤 float estimate_pred = kalman_estimate; float error_pred = kalman_error + Q; // 更新步骤 float kalman_gain = error_pred / (error_pred + R); kalman_estimate = estimate_pred + kalman_gain * (new_sample - estimate_pred); kalman_error = (1.0 - kalman_gain) * error_pred; return kalman_estimate; }
选型建议
- 若为偶尔出现的脉冲尖峰,优先选中位数滤波,实现简单且效果稳定。
- 若传感器信号有正常波动但尖峰极端,选改进版限幅滤波,资源占用比中位数滤波更低。
- 若为慢变传感器,选轻量卡尔曼滤波,兼顾滤波效果与信号跟踪能力。
所有算法的窗口大小、参数需结合传感器采样率、尖峰幅度、真实信号变化速度调试,建议通过串口输出原始数据与滤波后数据,观察效果再优化参数。
内容的提问来源于stack exchange,提问作者Aravinth
相关产品推荐
相关产品推荐

