You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:25:22