信号处理学习笔记3:限幅 + 中值 + 一阶 RC 三合一

中值滤波 专门干掉突然蹦出来的超大尖峰、脉冲毛刺,别的滤波干不掉的突发跳变

目录

中值滤波:

中值滤波C代码演示:

C结果展示:

为何结果像没排序

中值滤波窗口填充过程

毛刺连续堆无法中值滤波

先限幅,再中值

[限幅 + 中值 + 一阶 / 二阶 RC搭配:](#限幅 + 中值 + 一阶 / 二阶 RC搭配:)

程序运行结果:

python绘图演示:


中值滤波:

中值滤波作用: 对连续采样数据排序后取中间值

完全剔除孤立尖峰毛刺,对电磁干扰、电机脉冲、ADC 跳变有效。

取最近 N 个数据 → 从小到大排序 → 扔掉最大、最小,只拿中间值

离谱的爆值直接被踢出队伍,根本参与不到结果里。

中值滤波 = 排序 → 取中间值

中值滤波C代码演示:

cpp 复制代码
#include <stdio.h>

#define WIN 5  // 窗口大小5

// 缓冲区
float buf[WIN];

// 初始化缓冲区(全部 = 第一个数据,不会出现0)
void median_init(float v) {
    for (int i = 0; i < WIN; i++) buf[i] = v;
}

// ===================== 【真正正确】中值滤波 =====================
float median_filter(float val)
{
    // 1. 先进先出滑动窗口(标准写法)
    for (int i = 0; i < WIN - 1; i++) {
        buf[i] = buf[i + 1];  // 所有数据左移
    }
    buf[WIN - 1] = val;       // 新数据放最后


    // 2. 复制数组
    float tmp[WIN];
    for (int i = 0; i < WIN; i++) tmp[i] = buf[i];

    // 3. 冒泡排序(从小到大)
    for (int i = 0; i < WIN - 1; i++) {
        for (int j = 0; j < WIN - 1 - i; j++) {
            if (tmp[j] > tmp[j + 1]) {
                float t = tmp[j];
                tmp[j] = tmp[j + 1];
                tmp[j + 1] = t;
            }
        }
    }

    // 4. 返回中间值
    return tmp[2]; // WIN=5,中间位置是 2
}

// ===================== 测试 =====================
int main()
{
    float data[] = { 10.2, 10.3, 99.9, 10.1, 10.5, 88.8, 10.4, 10.6 };
    int len = sizeof(data) / sizeof(data[0]);

    median_init(data[0]);

    printf("原始数据\t中值滤波后\n");
    printf("----------------------------\n");

    for (int i = 0; i < len; i++) {
        float res = median_filter(data[i]);
        printf("%.2f\t\t%.2f\n", data[i], res);
    }

    return 0;
}

C结果展示:

99.9 和88.8这样的毛刺直接被消掉:

为何结果像没排序

中值滤波窗口 = 5 必须收集满 5 个数据才会开始正常排序输出!

  • 第 1 个数据 → 窗口:[10.2, 10.2, 10.2, 10.2, 10.2] → 输出 10.2
  • 第 2 个数据 → 窗口:[10.2, 10.2, 10.2, 10.2, 10.3] → 输出 10.2
  • 第 3 个数据 → 窗口:[10.2, 10.2, 10.2, 10.3, 99.9] → 输出 10.2
  • 第 4 个数据 → 窗口:[10.2, 10.2, 10.3, 99.9, 10.1] → 输出 10.2
  • 第 5 个数据开始,窗口满了 → 排序完全正常!

中值滤波窗口填充过程

  1. 抓最近 5 个数
  2. 从小到大排队
  3. 拿中间那个当结果

只要某一个值大得离谱 / 小得离谱排序一定会被挤到最左 or 最右

永远当不了中间值

永远影响不到输出
窗口越大,越不怕单个毛刺,但越怕信号真变化(拖影严重)

窗口再大,扛不住连续 3 个以上扎堆毛刺 → 必须靠先限幅守门

偶数窗口几乎没人用,都用奇数:3/5/7/9,好取正中间值

毛刺连续堆无法中值滤波

极端例子(窗口 = 5):

正常:10.1 10.2 10.3 10.4 10.5

突然连续进来 3 个大毛刺 99.9

窗口慢慢变成:99.9 99.9 99.9 10.4 10.5

排序:10.4 10.5 👉99.9👈 99.9 99.9

中间值直接变成 99.9

滤波直接被毛刺带偏、沦陷!

先限幅,再中值

限幅逻辑

比如正常数据就在 9 ~ 11 之间:

大于 11.5 → 直接丢弃,不让进缓存

小于 8.5 → 直接丢弃,不让进缓存

把连续大毛刺离谱炸值,直接拦在窗口外面,不让它们进缓存堆积,从根源防止中值滤波被攻破、带偏

限幅 + 中值 + 一阶 / 二阶 RC搭配:

第一层: 限幅 把连续大毛刺离谱炸值,直接拦在窗口外面 防止中值滤波被攻破、带偏

第一层:中值 → 守门,干掉突发大毛刺

第二层:RC 低通 → 精细平滑,稳住小幅噪声、稳住基线

cpp 复制代码
#include <stdio.h>

#define MEDIAN_WIN     5
#define FILTER_MAX     13.0f
#define FILTER_MIN     8.0f
#define ALPHA          0.18f

float median_buf[MEDIAN_WIN];
float rc_last = 0.0f;

void filter_init(float val)
{
    for(int i=0; i<MEDIAN_WIN; i++) median_buf[i] = val;
    rc_last = val;
}

float limit_amplitude(float val)
{
    if(val > FILTER_MAX) return FILTER_MAX;
    if(val < FILTER_MIN) return FILTER_MIN;
    return val;
}

float median_filter(float val)
{
    for(int i=0; i<MEDIAN_WIN-1; i++)
        median_buf[i] = median_buf[i+1];
    median_buf[MEDIAN_WIN-1] = val;

    float tmp[MEDIAN_WIN];
    for(int i=0; i<MEDIAN_WIN; i++) tmp[i] = median_buf[i];

    for(int i=0; i<MEDIAN_WIN-1; i++)
        for(int j=0; j<MEDIAN_WIN-1-i; j++)
            if(tmp[j] > tmp[j+1]){float t=tmp[j];tmp[j]=tmp[j+1];tmp[j+1]=t;}

    return tmp[MEDIAN_WIN/2];
}

float rc_filter(float val)
{
    float out = ALPHA * val + (1.0f - ALPHA) * rc_last;
    rc_last = out;
    return out;
}

float ultimate_filter(float val)
{
    float s1 = limit_amplitude(val);
    float s2 = median_filter(s1);
    float s3 = rc_filter(s2);
    return s3;
}

int main(void)
{
    // ========== 50个:缓慢从10.0 涨到12.5 + 穿插超大毛刺 ==========
    float data[50] = {
    // 1~10
    10.0f, 10.05f, 99.9f, 10.10f, 10.15f,
    10.20f, 10.25f, 88.8f, 10.30f, 10.35f,
    //11~20
    10.40f, 10.45f, 77.7f, 10.50f, 10.55f,
    10.60f, 10.65f, 66.6f, 10.70f, 10.75f,
    //21~30
    10.80f, 10.85f, 55.5f, 10.90f, 10.95f,
    11.00f, 11.10f, 44.4f, 11.15f, 11.20f,
    //31~40
    11.25f, 11.30f, 33.3f, 11.35f, 11.40f,
    11.50f, 11.60f, 22.2f, 11.70f, 11.80f,
    //41~50 继续稳步涨到12.5
    11.90f, 12.00f, 12.05f, 12.10f, 12.15f,
    12.20f, 12.30f, 12.35f, 12.40f, 12.50f
    };

    filter_init(data[0]);

    printf("序号\t原始数据\t滤波输出\n");
    printf("-----------------------------------------\n");

    for(int i=0; i<50; i++)
    {
        float res = ultimate_filter(data[i]);
        printf("%2d\t%.2f\t\t%.2f\n", i+1, data[i], res);
    }

    return 0;
}

程序运行结果:

cpp 复制代码
序号    原始数据        滤波输出
-----------------------------------------
 1      10.00           10.00
 2      10.05           10.00
 3      99.90           10.00
 4      10.10           10.01
 5      10.15           10.03
 6      10.20           10.05
 7      10.25           10.08
 8      88.80           10.10
 9      10.30           10.13
10      10.35           10.16
11      10.40           10.19
12      10.45           10.23
13      77.70           10.26
14      10.50           10.29
15      10.55           10.33
16      10.60           10.37
17      10.65           10.41
18      66.60           10.45
19      10.70           10.48
20      10.75           10.52
21      10.80           10.56
22      10.85           10.61
23      55.50           10.64
24      10.90           10.68
25      10.95           10.72
26      11.00           10.76
27      11.10           10.80
28      44.40           10.84
29      11.15           10.89
30      11.20           10.93
31      11.25           10.98
32      11.30           11.03
33      33.30           11.07
34      11.35           11.11
35      11.40           11.15
36      11.50           11.20
37      11.60           11.25
38      22.20           11.30
39      11.70           11.35
40      11.80           11.41
41      11.90           11.48
42      12.00           11.56
43      12.05           11.62
44      12.10           11.69
45      12.15           11.75
46      12.20           11.82
47      12.30           11.88
48      12.35           11.93
49      12.40           12.00
50      12.50           12.06

python绘图演示:

相关推荐
爱喝水的鱼丶16 分钟前
SAP-ABAP:条件判断与循环控制语句(7篇)第七篇:性能优化:条件与循环代码的常见性能瓶颈与优化方案
学习·算法·性能优化·sap·abap
小新同学^O^9 小时前
简单学习 --> 模型参数
学习·llm·大模型参数
cdbqss110 小时前
VB2026 菜单生成基类 BqGetMenuStrip
数据库·经验分享·学习·oracle·vb
吃好睡好便好11 小时前
创建魔方矩阵和单位矩阵
开发语言·人工智能·学习·线性代数·matlab·矩阵
星夜夏空9912 小时前
STM32单片机学习(21) —— I2C通信
stm32·单片机·学习
searchforAI13 小时前
B站视频转笔记用哪个工具?2026年四款AI笔记工具对比实测
人工智能·经验分享·笔记·gpt·学习·视频总结·ai笔记
爱上好庆祝14 小时前
学习JS第十一天(JS的进阶)
前端·javascript·学习
yeiweilan15 小时前
AI应用学习
学习
吃好睡好便好15 小时前
矩阵的加减运算
开发语言·人工智能·学习·线性代数·算法·matlab·矩阵
Mister西泽15 小时前
C++ Primer Plus 第六版 编程练习题及详细答案
开发语言·c++·学习·visual studio