はじめに
ウィーナーフィルタは、平均二乗誤差(MSE)を最小化する最適線形フィルタです。ノイズを含む観測信号から元の信号を推定する問題において、定常確率過程の枠組みで理論的に最適な解を与えます。
問題設定
ノイズを含む観測信号:
\[y(t) = s(t) + n(t) \tag{1}\]ここで \(s(t)\) は所望の信号、\(n(t)\) はノイズです。フィルタ \(h(t)\) を通した出力 \(\hat{s}(t)\) で \(s(t)\) を推定します。
\[\hat{s}(t) = \int_{-\infty}^{\infty} h(\tau) y(t - \tau) d\tau \tag{2}\]MSE 最小化
推定誤差の二乗平均を最小化します。
\[\min_h E\left[|s(t) - \hat{s}(t)|^2\right] \tag{3}\]ウィーナー・ホップ方程式
最適条件(直交性原理)から、以下の方程式が導かれます。
\[R_{yy}(\tau) * h(\tau) = R_{sy}(\tau) \tag{4}\]ここで \(R_{yy}\) は観測信号の自己相関、\(R_{sy}\) は所望信号と観測信号の相互相関です。
周波数領域での解
周波数領域では畳み込みが乗算になるため、ウィーナーフィルタは簡潔に表現できます。
\[H(f) = \frac{S_{sy}(f)}{S_{yy}(f)} \tag{5}\]信号とノイズが無相関の場合:
\[H(f) = \frac{S_{ss}(f)}{S_{ss}(f) + S_{nn}(f)} = \frac{\text{SNR}(f)}{\text{SNR}(f) + 1} \tag{6}\]ここで \(S_{ss}(f)\) は信号のパワースペクトル、\(S_{nn}(f)\) はノイズのパワースペクトル、\(\text{SNR}(f) = S_{ss}(f) / S_{nn}(f)\) は周波数ごとの信号対雑音比です。
直感的な理解: SNRが高い周波数帯域はそのまま通し、SNRが低い帯域は減衰させます。
Python実装
1次元信号のノイズ除去
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, ifft, fftfreq
np.random.seed(42)
# --- 信号生成 ---
fs = 1000 # サンプリング周波数
t = np.arange(0, 1, 1/fs)
signal = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 20 * t)
noise = np.random.normal(0, 0.8, len(t))
observed = signal + noise
# --- ウィーナーフィルタ(周波数領域) ---
N = len(observed)
Y = fft(observed)
freqs = fftfreq(N, 1/fs)
# パワースペクトル推定
S_yy = np.abs(Y)**2 / N
noise_power = np.var(noise) * N # ノイズのパワー(一様)
S_nn = noise_power / N * np.ones(N)
S_ss = np.maximum(S_yy - S_nn, 0) # 信号パワーの推定
# ウィーナーフィルタ
H = S_ss / (S_ss + S_nn + 1e-10)
S_filtered = ifft(Y * H).real
# --- 可視化 ---
fig, axes = plt.subplots(3, 1, figsize=(12, 8))
axes[0].plot(t, signal, 'b-', alpha=0.7, label='Original')
axes[0].set_title('Original Signal')
axes[0].set_ylabel('Amplitude')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].plot(t, observed, 'gray', alpha=0.5, label='Noisy')
axes[1].set_title('Observed (Signal + Noise)')
axes[1].set_ylabel('Amplitude')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
axes[2].plot(t, signal, 'b-', alpha=0.3, label='Original')
axes[2].plot(t, S_filtered, 'r-', alpha=0.8, label='Wiener filtered')
axes[2].set_title('Wiener Filter Output')
axes[2].set_xlabel('Time [s]')
axes[2].set_ylabel('Amplitude')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
mse_noisy = np.mean((signal - observed)**2)
mse_filtered = np.mean((signal - S_filtered)**2)
print(f"MSE (noisy): {mse_noisy:.4f}")
print(f"MSE (filtered): {mse_filtered:.4f}")
print(f"Improvement: {(1 - mse_filtered/mse_noisy)*100:.1f}%")
フィルタの周波数応答
plt.figure(figsize=(10, 4))
positive_freqs = freqs[:N//2]
plt.plot(positive_freqs, H[:N//2], 'g-', linewidth=2)
plt.xlabel('Frequency [Hz]')
plt.ylabel('|H(f)|')
plt.title('Wiener Filter Frequency Response')
plt.grid(True, alpha=0.3)
plt.xlim(0, 100)
plt.tight_layout()
plt.show()
カルマンフィルタとの関係
| 特徴 | ウィーナーフィルタ | カルマンフィルタ |
|---|---|---|
| 前提 | 定常過程 | 非定常でも可 |
| 処理方式 | バッチ(周波数領域) | 逐次(時間領域) |
| 最適性 | MSE最小(定常) | MSE最小(線形ガウス) |
| 適用範囲 | 信号処理 | 状態推定・制御 |
ウィーナーフィルタは定常過程における最適フィルタであり、カルマンフィルタはその非定常・逐次処理への拡張と見なせます。
関連記事
- 指数移動平均(EMA)フィルタの周波数特性 - EMAは簡便なローパスフィルタですが、ウィーナーフィルタはSNRに基づく最適な帯域設計です。
- 移動平均フィルタの種類と比較 - 移動平均とウィーナーフィルタの設計思想の違いを理解できます。
- バターワースフィルタの設計と実装 - カットオフ周波数を手動で設定するバターワースとの対比で、ウィーナーの自動的な帯域設計が際立ちます。
- FFTの基礎:高速フーリエ変換の理論と実装 - ウィーナーフィルタの実装に不可欠なFFTの解説です。
参考文献
- Haykin, S. (2014). Adaptive Filter Theory (5th ed.). Pearson. Chapters 2-3.
- Wiener, N. (1949). Extrapolation, Interpolation, and Smoothing of Stationary Time Series. MIT Press.
- Vaseghi, S. V. (2008). Advanced Digital Signal Processing and Noise Reduction (4th ed.). Wiley.