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

目录
[限幅 + 中值 + 一阶 / 二阶 RC搭配:](#限幅 + 中值 + 一阶 / 二阶 RC搭配:)
中值滤波:
中值滤波作用: 对连续采样数据排序后取中间值,
完全剔除孤立尖峰毛刺,对电磁干扰、电机脉冲、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 个数据开始,窗口满了 → 排序完全正常!
中值滤波窗口填充过程
- 抓最近 5 个数
- 从小到大排队
- 拿中间那个当结果
只要某一个值大得离谱 / 小得离谱排序一定会被挤到最左 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绘图演示:

