第2章:时域分析基础——均值滤波、中值滤波的原理与Python实现
各位同学,欢迎来到实战课的第二讲。今天咱们聊聊时域分析里最基础、也最实用的两个工具——均值滤波和中值滤波。
说实话,我刚开始做信号处理那会儿,总觉得滤波这事儿得靠什么高级算法。后来在项目里摔过几次跟头才明白:基础方法用好了,能解决80%的工程问题。你想想看,现场采集的高频数据,噪声往往就那么几种——要么是随机白噪声,要么是脉冲尖刺。对付它们,均值和中值滤波就是最趁手的兵器。
2.1 均值滤波:平滑噪声的“平均主义”
均值滤波的原理,说白了就是用窗口内所有点的平均值,来代替中心点的值。我习惯把它想象成“拉帮结派”——让每个数据点都看看周围邻居的平均水平,别太出格。
核心公式:
y[n] = (1/M) * Σ x[n + k] (k = -L 到 L)
其中 M = 2L + 1 是窗口长度,L 是窗口半径。
举个简单的例子。假设我们有一段传感器数据,混入了高频噪声。用均值滤波走一遍,噪声会被“抹平”。但代价是什么?信号的边缘也会变模糊。我在做振动信号分析时就吃过这个亏——为了降噪把窗口设得太大,结果故障特征全被平滑掉了。
Python实现
import numpy as np
import matplotlib.pyplot as plt
def mean_filter(signal, window_size):
"""均值滤波实现"""
half = window_size // 2
result = np.zeros_like(signal)
for i in range(half, len(signal) - half):
result[i] = np.mean(signal[i-half : i+half+1])
# 边界处理:直接复制原值
result[:half] = signal[:half]
result[-half:] = signal[-half:]
return result
# 生成测试信号
t = np.linspace(0, 1, 500)
clean = np.sin(2 * np.pi * 5 * t)
noise = 0.3 * np.random.randn(500)
noisy = clean + noise
# 应用均值滤波
filtered = mean_filter(noisy, window_size=5)
# 可视化
plt.figure(figsize=(10, 4))
plt.plot(t, noisy, alpha=0.5, label='含噪信号')
plt.plot(t, filtered, 'r-', linewidth=2, label='均值滤波后')
plt.legend()
plt.show()
我的经验:窗口大小选3或5通常够用。超过7,信号失真会明显增加。我曾经在ECG信号处理中试过窗口=15,结果QRS波群都快认不出来了。
2.2 中值滤波:对抗脉冲噪声的利器
中值滤波的思路完全不同——它取窗口内所有点的中位数,而不是平均值。为什么要这么做?
你想想看,如果信号里突然蹦出一个尖峰(比如传感器受干扰),均值滤波会被这个尖峰“带偏”,而中值滤波直接忽略它。因为中位数对异常值不敏感。
核心公式:
y[n] = median( x[n-L], ..., x[n], ..., x[n+L] )
我记得有一次做工业现场的数据采集,电机启动瞬间会产生强烈的电磁干扰,信号上全是毛刺。均值滤波怎么调都不行,换成中值滤波后,干净得跟教科书一样。
Python实现
def median_filter(signal, window_size):
"""中值滤波实现"""
half = window_size // 2
result = np.zeros_like(signal)
for i in range(half, len(signal) - half):
result[i] = np.median(signal[i-half : i+half+1])
result[:half] = signal[:half]
result[-half:] = signal[-half:]
return result
# 加入脉冲噪声
impulse_noise = np.zeros(500)
impulse_noise[100:105] = 5.0
impulse_noise[300:303] = -4.0
noisy_impulse = clean + impulse_noise
# 对比两种滤波
mean_result = mean_filter(noisy_impulse, 5)
median_result = median_filter(noisy_impulse, 5)
注意:中值滤波对高斯白噪声的效果不如均值滤波。它擅长的是“点状”噪声,而不是“面状”噪声。选哪个,得看你的噪声长什么样。
2.3 滑动窗口的选择策略
窗口怎么选?这是新手最容易纠结的问题。我给大家总结了一套实用策略:
| 场景 | 推荐窗口大小 | 说明 |
|---|---|---|
| 高频噪声(白噪声) | 3~7 | 均值滤波优先,窗口越大越平滑 |
| 脉冲噪声(尖刺) | 3~5 | 中值滤波优先,窗口太大丢失细节 |
| 缓慢变化的趋势信号 | 11~21 | 可以大窗口,但要小心相位延迟 |
| 边缘检测场景 | 3 | 窗口越小越好,保留跳变沿 |
我个人习惯是从最小窗口开始试。先设窗口=3,看效果;不行再逐步增大。千万别一上来就搞个大窗口,否则信号特征被抹平了都不知道。
避坑指南:我曾经在金融时间序列上用过均值滤波,窗口设了20。结果发现滤波后的序列跟原序列有肉眼可见的相位偏移——因为均值滤波本质上是低通滤波器,会引入延迟。后来我改用零相位滤波(比如scipy的filtfilt)才解决。
2.4 知识体系总览
下面这张图,是我梳理的本章核心逻辑。你可以把它当作一个“决策树”——遇到噪声时,按图索骥就行。
嗯,这张图的核心逻辑就是:先看噪声长什么样,再选滤波方法,最后定窗口大小。别搞反了顺序。
2.5 实战建议
最后,我给大家三个实战中总结出来的建议:
- 先可视化,再滤波。拿到数据别急着写代码,先画个图看看噪声形态。我见过太多人上来就套滤波,结果把有用信号滤没了。
- 组合使用效果更好。如果信号里既有高斯噪声又有脉冲噪声,可以先中值滤波去尖刺,再均值滤波平滑。我在雷达信号处理中常用这个组合。
- 注意边界效应。滤波时窗口会超出信号边界,处理不好会在两端产生假信号。我习惯用“镜像延拓”或“零填充”来处理边界,别直接扔掉。
一句话总结:均值滤波抹平噪声,中值滤波剔除尖刺。窗口大小决定平滑程度,也决定信号失真程度。选对了,事半功倍;选错了,事倍功半。