我正在研究频域中的维纳滤波器,由
$$ W(\omega) = \frac{S_{ud}(\omega)}{S_{uu}(\omega)}$$
where $S_{xy} (\omega)$是交叉功率谱密度 (CPSD),请参见scipy.signal.csd或MATLAB cpsd。我使用 Pythonscipy.signal.csd
来估计$S_{xy}$。
这里$d[n]$是所需信号(“mic”),$u[n]$(“ref”)是参考信号。
现在,在这里我描述了如何生成信号(在 Python 中)。我生成了一个 10 秒的白噪声,还生成了一个 FIR 传递函数true_h
(有两种生成方法:一种是设计 FIR 带通滤波器并使用它,第二种是从真实房间实验中测量的)。
from scipy import signal
# Generate filter
true_L = 150 # filter size for simulation
true_h = signal.firwin(true_L, cutoff=[1000, 3000], pass_zero="bandpass", fs=FS)
fft_h = np.fft.rfft(true_h)
# Generate 10 seconds of white noise
np.random.seed(0)
ref = np.random.randn(10*FS)/100
mic = signal.lfilter(true_h, 1, ref)
目标是估计$w = \mathcal{F}^{-1}(W)$其中$W$是估计的 Wiener 滤波器频率,$\mathcal{F}^{-1}$是逆离散傅里叶变换。
频域中的维纳计算如下:
from scipy import signal
# ref = reference signal
# mic = desired signal
freqs, Pxy = signal.csd(ref, mic, fs=FS, nperseg=nfft)
freqs, Pxx = signal.csd(ref, ref, fs=FS, nperseg=nfft)
wiener = Pxy / Pxx
估计滤波器$w$的应用如下:
# Inverse FFT for Wiener filter
wiener_taps = np.fft.irfft(wiener)
# Apply Wiener
error_wiener = mic - signal.lfilter(wiener_taps, 1, ref)
期望的结果是$e[n] = d[n] - w[n] \ast u[n]$几乎是$0$。但这种情况并非如此。我得到的残差只有 -10 到 -20 dB 左右。
当我使用频率/快速块 LMS(FBLMS 或 FLMS)时,我得到一个残差$e[n]$,它约为 -40 dB,即 的估计true_h
值几乎是完美的。
我还绘制了与真实传递函数(在抽头和频域中)相比的 Wiener 和 FLMS 估计值,true_h
并获得了非常好的匹配,但 FLMS 中的匹配比 Wiener 中的更好)。
这很奇怪,因为 Wiener 被认为是 MSE(均方误差)意义上的最佳滤波器。但在这个例子中,Wiener 的表现比 FLMS 差。请参阅以下结果:
结果:
图 1:回波回波损耗增强 (ERLE)。黑色 = 原始,绿色 = Wiener 之后,蓝色 = LMS 之后。
图 2:残差 (e = d - w * u)。绿色:维纳滤波器,红色:FLMS 滤波器。蓝色:自适应 FLMS(与此问题无关)。
编辑时添加: 在这里我进一步研究维纳。我生成了一个 150 抽头的 FIR,并将其应用于白噪声信号(由正态分布生成):
# Generate filter
true_L = 150 # filter size for simulation
true_h = signal.firwin(true_L, cutoff=[1000, 3000], pass_zero="bandpass", fs=FS)
# Generate 10 seconds of white noise
np.random.seed(0)
ref = np.random.randn(10*FS)/100
mic = signal.lfilter(true_h, 1, ref)
然后通过以下方式计算维纳:
- scipy 的默认 CSD 参数。
f1, Pxy = signal.csd(ref, mic, fs=FS)
f1, Pxx = signal.csd(ref, ref, fs=FS)
wiener_freq = Pxy/Pxx
- 对整个信号(一个“帧”)进行一次长 FFT:
f1, Pxy = signal.csd(ref, mic, fs=FS, nperseg=len(ref))
f1, Pxx = signal.csd(ref, ref, fs=FS, nperseg=len(ref))
wiener_freq = Pxy/Pxx
正如我们所见,第二种方法产生了更好的结果,但仍然不是一个完美的 0 残差(即残差 < -100 dB)。
在编辑中添加:
当信号是合成的时,似乎就是这种情况:即参考是由 生成的白噪声,numpy.random.randn
而传递函数是由scipy.signal.firwin
大约 50-250 个抽头生成的。对于真实数据,维纳似乎表现更好。
在另一个编辑中添加:
图 5:合成白噪声信号和 FIR 传递函数的 Wiener(橙色)与 FBLMS(绿色)。
图 6:Wiener(橙色)与 FBLMS(绿色)的真实信号(使用音频设备录制)
可以看出,对于合成情况,FBLMS 优于 Wiener,而对于真实情况,反之亦然,Wiener 更好,FBLMS 收敛于 Wiener。
参考: