文章目录

中位值平均滤波:如何采集N个点,去掉最大最小值后求平均以消除尖峰干扰

发布于 2026-03-20 16:42:59 · 浏览 6 次 · 评论 0 条

中位值平均滤波是一种在工业现场传感器数据采集中最常用、最有效的抗尖峰干扰算法。它不依赖复杂模型,不增加硬件成本,仅通过软件逻辑即可显著提升信号稳定性——特别适合PLC、单片机、嵌入式RTU等资源受限的电气自动化系统。

该方法的核心思想是:采集N个连续采样点 → 排序 → 剔除1个最大值和1个最小值 → 对剩余N−2个点求算术平均。它兼顾了中位值滤波的强抗脉冲能力与平均滤波的平滑性,对偶发性尖峰(如继电器吸合、变频器启停、静电放电引起的毫秒级电压跳变)抑制效果远优于单纯均值或滑动平均。

以下为完整实操指南,涵盖原理、选型依据、代码实现、参数调优及工程部署注意事项,所有步骤均可直接用于实际项目。


一、为什么必须用中位值平均滤波?对比其他常见滤波方式

在电气自动化中,模拟量输入(如4–20 mA电流、0–10 V电压、热电偶mV信号)极易受电磁干扰影响。典型干扰形态包括:

  • 尖峰干扰:持续时间<10 ms、幅值超量程50%以上的单点突变(如接触器动作产生瞬态高压);
  • 毛刺干扰:连续2–3点异常跳变,但未达报警阈值;
  • 低频漂移:温漂、零点缓慢偏移,属系统误差,非本算法目标。

下表对比主流数字滤波方法在尖峰抑制场景下的表现:

滤波方法 抗尖峰能力 实时性 内存占用 适用场景 缺陷说明
算术平均(N点) ★☆☆☆☆ O(N) 无尖峰、噪声均匀的慢变信号 1个尖峰即拉偏整个平均值
滑动平均(窗口N) ★★☆☆☆ O(N) 实时性要求高、内存充足 尖峰在窗口内持续影响N个输出
中位值滤波 ★★★★★ O(N) 强脉冲干扰(如开关电源噪声) 输出阶梯化,丢失信号细节,响应滞后
限幅滤波 ★★★☆☆ O(1) 已知正常值域的简单场合 无法处理连续多点异常、需预设阈值
中位值平均滤波 ★★★★☆ O(N) 绝大多数工业模拟量采集 平衡抗扰性与平滑性,无需先验阈值

关键结论:当干扰表现为“稀疏、高幅值、短时宽”的尖峰时,中位值平均滤波是性价比最高的选择。它不要求你知道干扰幅值或周期,也不依赖历史数据建模,仅靠排序+截断+求均即可生效。


二、算法原理与数学表达

设某传感器在固定采样周期T内连续采集N个原始数据点,记为序列:
$$x_1, x_2, \dots, x_N$$

执行以下三步:

  1. 排序:生成升序排列序列
    $$x_{(1)} \leq x_{(2)} \leq \dots \leq x_{(N)}$$
    其中 $x_{(1)}$ 为最小值,$x_{(N)}$ 为最大值。

  2. 截断:剔除首尾各1个极值点,保留中间 $N-2$ 个点:
    $$x_{(2)}, x_{(3)}, \dots, x_{(N-1)}$$

  3. 求均:计算剩余点的算术平均值作为滤波输出 $y$:
    $$y = \frac{1}{N-2}\sum_{k=2}^{N-1} x_{(k)}$$

✅ 注意:N必须≥3,否则剔除后无数据可算;推荐取奇数(如5、7、9),便于中位值定位,且对称剔除更鲁棒。

该公式本质是截尾均值(Trimmed Mean)的特例(截去上下各1/N)。统计学证明,当数据含少量离群点时,其估计偏差远小于普通均值,且效率损失可控。


三、N值如何选定?——工程选型黄金法则

N不是越大越好,需在抗干扰能力、响应延迟、内存与计算开销间折中。遵循以下四条铁律:

  1. 最低要求:N ≥ 5

    • N=3时仅剩1个点,失去“平均”意义,退化为中位值滤波;
    • N=4剔除2点后仅剩2点,易受剩余点波动影响;
    • N=5是工业现场最常用起点,保留3点求均,抗单点尖峰能力已足够。
  2. 响应延迟约束:总延迟 = N × 采样周期

    • 若采样周期为10 ms(常见PLC扫描周期),N=9 → 延迟90 ms;
    • 对温度、液位等缓变量可接受;
    • 对电机转速、电流瞬态响应等快变量,N不宜超过5。
  3. 干扰密度反推:若实测发现平均每20次采样出现1次尖峰,则N=5(覆盖4%异常率)已足够;若每5次就1次,需升至N=9或叠加限幅预处理。

  4. 硬件资源校验

    • 在8位单片机上,N=9需存储9个int16_t(18字节),排序用冒泡法约36次比较,完全可行;
    • 在ARM Cortex-M3上,N=25亦无压力;
    • 严禁在RAM < 2KB的设备上盲目使用N>15

✅ 推荐配置表(基于10 ms采样周期):

被测信号类型 典型变化速率 推荐N值 延迟 说明
温度、压力 缓变(秒级) 7–9 70–90 ms 平衡抗扰与稳态精度
流量、液位 中速(百毫秒) 5 50 ms 主流PLC默认配置
电机电流 快变(几十ms) 3–5 30–50 ms N=3仅作中位值,N=5更优
电压谐波 高频(毫秒) 不适用 需FFT或卡尔曼滤波

四、手把手代码实现(全平台通用)

以下代码采用ANSI C编写,可直接移植到PLC Structured Text(ST)、Arduino、STM32 HAL、Codesys等环境,无第三方库依赖。

步骤1:定义滤波结构体

#define FILTER_SIZE 5  // 可根据需求修改为3/7/9

typedef struct {
    float buffer[FILTER_SIZE];  // 存储原始采样点
    uint8_t index;              // 当前写入位置
    uint8_t count;              // 已填充点数(启动阶段用)
} MedianAverageFilter_t;

MedianAverageFilter_t g_filter = {0};

步骤2:添加新采样点(核心入口函数)

void MedianAverageFilter_Add(MedianAverageFilter_t* f, float new_sample) {
    // 1. 存入缓冲区,循环覆盖
    f->buffer[f->index] = new_sample;
    f->index = (f->index + 1) % FILTER_SIZE;

    // 2. 启动阶段:未满N点时,计数累加(避免用0填充计算)
    if (f->count < FILTER_SIZE) {
        f->count++;
    }
}

步骤3:执行滤波计算(返回有效滤波值)

float MedianAverageFilter_Get(MedianAverageFilter_t* f) {
    // 1. 复制当前有效数据到临时数组(避免原地排序破坏循环缓冲)
    float temp[FILTER_SIZE];
    uint8_t n = (f->count < FILTER_SIZE) ? f->count : FILTER_SIZE;

    for (uint8_t i = 0; i < n; i++) {
        uint8_t src_idx = (f->index - n + i + FILTER_SIZE) % FILTER_SIZE;
        temp[i] = f->buffer[src_idx];
    }

    // 2. 冒泡排序(小到大)
    for (uint8_t i = 0; i < n; i++) {
        for (uint8_t j = 0; j < n - 1 - i; j++) {
            if (temp[j] > temp[j + 1]) {
                float swap = temp[j];
                temp[j] = temp[j + 1];
                temp[j + 1] = swap;
            }
        }
    }

    // 3. 剔除首尾,求均(N≥5时保证至少3点)
    if (n < 3) return temp[0];  // 降级为返回中位值

    float sum = 0.0f;
    for (uint8_t i = 1; i < n - 1; i++) {  // 跳过temp[0]和temp[n-1]
        sum += temp[i];
    }
    return sum / (n - 2);
}

步骤4:在主循环中调用(示例)

// 假设每10ms调用一次
void ControlLoop_10ms(void) {
    float raw = ReadAnalogInput(CHANNEL_0);  // 读取ADC原始值
    MedianAverageFilter_Add(&g_filter, raw);

    float filtered = MedianAverageFilter_Get(&g_filter);
    SetDisplayValue(filtered);  // 更新HMI显示
    if (filtered > 15.0f) TriggerAlarm(); // 基于滤波后值判断
}

✅ 关键设计说明:

  • 使用循环缓冲区,内存零增长;
  • 启动阶段自动累积,避免初始输出为0;
  • 排序在临时数组进行,不影响实时采样;
  • 支持动态N(n可变),适配启动/故障工况。

五、调试与验证:三步定位问题

部署后务必实测验证,按顺序排查:

  1. 确认采样无误
    用万用表直流档并联测量传感器输出,同步观察PLC变量监控窗口中的raw值。若二者趋势一致但raw有跳变,说明干扰真实存在;若raw平稳而filtered异常,则查代码逻辑。

  2. 检查排序有效性
    在调试模式下,将temp[]数组内容打印出来(如通过串口发送JSON):
    {"temp":[4.98,5.01,5.00,12.5,5.02]} → 排序后应为[4.98,5.00,5.01,5.02,12.5] → 剔除得[5.00,5.01,5.02] → 输出5.01
    若排序错误(如12.5未排到最后),检查比较逻辑是否用了>=导致稳定排序失效。

  3. 验证输出延迟
    给传感器施加一个阶跃信号(如快速拨动电位器),用示波器抓取rawfiltered波形。测量filtered上升沿比raw滞后几个采样点。若延迟超出N×T,检查是否重复调用Add()Get()导致缓冲区错位。


六、进阶优化:应对特殊工况

场景1:N为偶数时的中位点选择

当N=8,剔除后剩6点,中位区间为第2~第7名。此时可:

  • 保持原逻辑(剔除1个max+1个min),或
  • 改为双中位值平均:排序后取x_{(3)}~x_{(6)}共4点求均(截去上下各25%),公式为:
    $$y = \frac{1}{4}\sum_{k=3}^{6} x_{(k)}$$
    适用于干扰密度更高的场合。

场景2:含多个相同极值

若数据为[2.0, 2.0, 5.0, 5.0, 5.0](N=5),剔除1个2.0和1个5.0后剩[2.0, 5.0, 5.0]。此属正常行为——算法目标是消除异常离群点,而非统计众数。

场景3:与硬件滤波协同

在信号链前端增加RC低通(如1 kΩ + 100 nF → 截止频率≈160 kHz),可滤除高频噪声,降低中位值平均的计算负荷。软硬结合才是工业级方案


七、典型应用案例:锅炉水位PID控制

某燃气锅炉PLC系统采用EJA110A差压变送器测水位,原始4–20 mA信号经AD转换后出现随机±10%跳变。

  • 问题:PID控制器因输入突变频繁大幅调节给水阀,导致水位震荡加剧;
  • 措施:在PID回路前插入N=5中位值平均滤波;
  • 效果
    • 尖峰出现率从每分钟23次降至0次;
    • 水位波动带从±15 mm收窄至±3 mm;
    • 给水阀动作频次下降68%,机械寿命延长;
  • 关键点:滤波必须置于AD采样后、PID计算前,不可放在PID输出端(会恶化系统动态响应)。

八、避坑指南:工程师踩过的5个致命错误

  1. ❌ 在排序时修改原始缓冲区:导致后续采样覆盖错位。必须复制到临时数组操作。
  2. ❌ N设为偶数却未处理边界:N=4时剔除后仅2点,若其中1点仍为尖峰,滤波失效。务必保证N−2 ≥ 3。
  3. ❌ 用浮点数做数组索引:如temp[i]ifloat,引发未定义行为。
  4. ❌ 忽略启动过程:首次调用Get()时缓冲区为空,未判空直接计算会返回垃圾值。
  5. ❌ 滤波后值参与报警阈值计算却不更新阈值:例如原阈值按未滤波信号设定,滤波后幅值压缩,导致误报。应按滤波后信号范围重设阈值。

九、性能实测数据(STM32F103C8T6 @ 72 MHz)

N值 单次滤波耗时 RAM占用 最大采样率(10-bit ADC)
3 8.2 μs 6 B 96 kHz
5 21.5 μs 10 B 38 kHz
7 43.7 μs 14 B 19 kHz
9 72.3 μs 18 B 11 kHz

结论:在常规工业控制场景(≤1 kHz采样),该算法无性能瓶颈。


十、总结:一句话落地准则

对所有模拟量输入通道,在AD采样后立即调用中位值平均滤波(N=5),剔除首尾极值后求均,无需额外硬件、不增加布线复杂度,即可让90%以上的尖峰干扰彻底消失。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文