信号处理学习笔记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绘图演示:

相关推荐
NULL指向我2 小时前
信号处理学习笔记1:软件RC一阶高通\低通滤波
学习
NPUQS3 小时前
【Unity 3D学习】Unity 与 Python 互通入门:点击按钮调用 Python(超简单示例)
学习·3d·unity
电子云与长程纠缠10 小时前
Godot学习05 - 播放与分离FBX动画
学习·游戏引擎·godot
蒸蒸yyyyzwd10 小时前
day3学习笔记
笔记·学习
red_redemption11 小时前
自由学习记录(143)
学习
楼田莉子12 小时前
MySQL数据库:MySQL的数据类型
数据库·学习·mysql
小陈phd13 小时前
系统架构师学习笔记(三)——计算机体系结构之存储系统
笔记·学习·系统架构
Rsun0455114 小时前
AI智能体学习路线
人工智能·学习
charlie11451419114 小时前
通用GUI编程技术——Win32 原生编程实战(十六)——Visual Studio 资源编辑器使用指南
开发语言·c++·ide·学习·gui·visual studio·win32