滤波方法(一):移动平均滤波

滤波,在最广泛的意义上,是从含噪声的观测序列中估计感兴趣信号的过程。这个概念横跨信号处理、控制理论、时间序列分析和统计学,但核心目标高度统一:从杂乱无章的测量值中还原出隐藏其后的真实状态或趋势。

问题设定

考虑一个最简场景:你在一个有噪声的秤上反复称量同一个物体,每次读数都围绕真实值上下浮动。直觉告诉你取几次读数的平均值——这便是最朴素的滤波思想。形式化地描述,每一个离散时刻 $k$ 有一个不可直接观测的系统状态 $x_k$,以及一个受噪声污染的观测 $z_k$,滤波器的任务是根据到时刻 $k$ 为止的全部观测 $z_1, z_2, \dots, z_k$,给出 $x_k$ 的最优估计 $\hat{x}_{k|k}$。

移动平均的定义

移动平均(Moving Average)是最古老也最直观的滤波器。第 $k$ 时刻的估计定义为最近 $W$ 个观测值的算术平均:

$$\hat{x}k = \frac{1}{W} \sum$$}^{W-1} z_{k-j

唯一的参数是窗口宽度 $W$。$W$ 越大,滤波输出越平滑,但对真实变化的响应越迟缓——平滑度与响应速度之间存在着基本权衡,这一权衡贯穿此后所有的滤波方法。

频域特性

移动平均在本质上是有限脉冲响应(FIR)滤波器,其频率响应为

$$H(\omega) = \frac{1}{W} \cdot \frac{\sin(\omega W/2)}{\sin(\omega/2)} \cdot e^{-j\omega (W-1)/2}$$

其中 $\sin(\omega W/2)/\sin(\omega/2)$ 项是 Dirichlet 核,在主瓣之外有逐渐衰减的旁瓣。这意味着移动平均虽能抑制高频噪声,但会引入振铃效应,且对突变信号的响应呈现线性斜坡。

可视化

下图的 GIF 演示了窗口宽度从 1 逐步增加到 30 的过程中,滤波输出如何在平滑与滞后之间变化。

滤波01-图1 移动平均滤波:窗口宽度从1到30的变化

Python 实现

import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

os.makedirs('images', exist_ok=True)

np.random.seed(42)
k = np.arange(200)
true = np.sin(2 * np.pi * k / 50) + (k > 100).astype(float)
obs = true + np.random.randn(200) * 0.5

def moving_average(x, W):
    kernel = np.ones(W) / W
    return np.convolve(x, kernel, mode='same')

W3 = moving_average(obs, 3)
W8 = moving_average(obs, 8)
W20 = moving_average(obs, 20)

fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(k, true, 'k-', label='True Signal', linewidth=1.5)
ax.plot(k, obs, '.', color='gray', alpha=0.4, label='Observations')
ax.plot(k, W3, label='MA W=3', linewidth=1.2)
ax.plot(k, W8, label='MA W=8', linewidth=1.2)
ax.plot(k, W20, label='MA W=20', linewidth=1.2)
ax.set_xlabel('Time Step k')
ax.set_ylabel('Value')
ax.set_title('Moving Average Filtering')
ax.legend(loc='upper right')
ax.grid(True, alpha=0.3)
fig.tight_layout()
fig.savefig('images/moving_average.svg')
fig.savefig('images/moving_average.png', dpi=150)
plt.close(fig)

fig, ax = plt.subplots(figsize=(10, 5))
frames = list(range(1, 31))
line_true, = ax.plot(k, true, 'k-', label='True Signal', linewidth=1.5)
line_obs, = ax.plot(k, obs, '.', color='gray', alpha=0.3, label='Observations')
line_ma, = ax.plot(k, W3, 'r-', label='Moving Average', linewidth=1.5)
title = ax.set_title('Effect of Window Width W=3')
ax.set_xlabel('Time Step k')
ax.set_ylabel('Value')
ax.legend(loc='upper right')
ax.grid(True, alpha=0.3)
ax.set_ylim(-2.5, 3.0)

def update(i):
    W = frames[i]
    line_ma.set_ydata(moving_average(obs, W))
    title.set_text(f'Effect of Window Width W={W}')
    return line_ma, title

ani = animation.FuncAnimation(fig, update, frames=len(frames), interval=200, blit=False)
writer = animation.PillowWriter(fps=5)
ani.save('images/滤波01-图1.gif', writer=writer)
plt.close(fig)

import csv
with open('images/moving_average.csv', 'w', newline='') as f:
    w = csv.writer(f)
    w.writerow(['k', 'true', 'obs', 'W3', 'W8', 'W20'])
    for i in range(200):
        w.writerow([i, true[i], obs[i], W3[i], W8[i], W20[i]])

演化与局限

移动平均有三个内在局限。其一,它假设信号在窗口内近似恒定,对趋势和周期信号的跟踪天然滞后。其二,它平等对待窗口内的所有观测,不给近期数据更高的权重——而直觉上,越近的观测越能反映当前状态。其三,它需要存储完整的窗口长度历史数据,内存开销随窗口宽度线性增长。

这三个局限中,第二个和第三个可以被一个优雅的递推公式同时解决:让新观测获得更高权重,旧观测的权重指数衰减,且只需存储上一个估计值。这就是指数平滑——下一篇的主题。

参考文献

[1] arXiv:2410.21184 — Shannon-like Interpolation with Spectral Priors and Weighted Hilbert Spaces.

Category
Tagcloud
Junck Kalman Tool Science Moving Average Exponential Smoothing UKF Nonlinear Filtering FckZhiHu Wiener Filter Code GIS Probability Code Generation Bayesian Story Architecture Non-Gaussian Nonlinear Hackintosh Hadoop Matplotlib OSX-KVM Optimal Estimation History Lens Cellular Automata Translate Unscented Transform Discuss Sequential Monte Carlo Bayesian Estimation Photo OpenWebUI Time Series Windows AI PVE Computability State Space Algorithm QGIS Kalman Filter Photography CUDA Signal Processing Pyenv RX590 PHD C Radio Communicate Prerequisites Ubuntu Sigma Point Simulation LLM Tools Geology Ollama Visualization GlumPy Programming University Scholar Particle Filter ChromeBook Hack RTL-SDR Kivy Guide Qwen3 Camera Math EKF Turing LlamaFactory AMD Jacobian Book Data Game Ventoy Hardware VirtualMachine Mac Frequency Domain Memory Computer GPT-OSS Learning Linux Filtering ML Python Life NumPy Mathematical Modeling Poem Microscope