设计一个近似给定 IIR 滤波器幅度的线性相位 FIR 滤波器

信息处理 过滤器 过滤器设计 有限脉冲响应 无限脉冲响应 频率响应
2022-02-11 19:14:02

我有一个双二阶 IIR 滤波器,我想从中获得线性相位FIR。我看到了那个相关的问题,但 OP 不太关心阶段。

从 IIR 中提取 FIR 时,我很难获得完全相同的幅度曲线。

到目前为止,我的方法是:

  1. 在 1024 个样本缓冲区上应用我的 IIR 滤波器,该缓冲区只包含一个狄拉克作为第一个样本。
  2. 移动步骤 1 中获得的脉冲响应并使其对称(以获得相位线性度)

如果我在第 1 步之后停止,我的 IIR 和 FIR 将得到完全相同的幅度曲线,但我也会得到完全相同的相位曲线(这是非线性的,因此不感兴趣)。
如果我在第 2 步之后停止,我会得到一个线性相位,但幅度不完全相同。

在下图中,IIR 和 FIR(在步骤 2 之后)的传递函数(分别)为红色和蓝色:

在此处输入图像描述

我做错了什么?

2个回答

您在步骤 1 中所做的只是截断无限脉冲响应以通过 FIR 滤波器对其进行近似。如果您使用足够多的滤波器抽头,则近似值会变得任意准确。这意味着生成的 FIR 滤波器近似于原始 IIR 滤波器的幅度相位特性。所以用这种方法,相位永远不会变成线性的。

正如您在步骤 2 中所做的那样,使脉冲响应对称以获得相位线性,当然会改变幅度响应。

您应该做的是使用 IIR 滤波器的幅度作为(线性相位)FIR 滤波器设计例程中的所需响应。在这种情况下,您将得到一个具有完全线性相位和一定幅度近似误差的 FIR 滤波器。通过选择适当的滤波器阶数,可以使幅度误差足够小。最简单的方法可能是使用最小二乘近似,它只涉及求解线性方程组。

示例:我使用一个峰值 EQ 滤波器作为 IIR 原型。系数是(b是分子系数,a是分母系数):

b = [1.2223e+00, 0, 7.7775e-01];
a = [1.1250e+00, 0, 8.7502e-01];

您可以使用 IIR 滤波器频率响应的幅度并将其与线性相位相结合,以获得 FIR 滤波器设计例程所需的响应(N即滤波器长度)。代码是 Matlab/Octave 语法:

[H,w] = 频率 (b,a,256);
N = 61;
D = abs(H).*exp(-1i*w*(N-1)/2);

您可以使用名为的最小二乘 FIR 滤波器设计例程lslevin.m,您可以在此处找到该例程。

h = lslevin(N,w,D,ones(长度(w),1));
Hh = 频率 (h,1,256);

下图显示了两个频率响应(IIR 和 FIR)的幅度:

在此处输入图像描述

简单的解决方案:

  1. 对IIR的脉冲响应进行足够长度的采样,在这种情况下8192左右应该足够了
  2. 快速傅里叶变换
  3. 将相位设置为零
  4. 逆 FFT
  5. 时移并截断到所需的精度/过滤器长度

编辑:这是如何做的代码

%% get a filter target
%sos = audioEQ(6,5000,sqrt(.5),'para')
fs = 44100;
% paramtric: 6 dB, 5 kHz, Q = 1
a = [1.000000000000000  -1.216444449798070   0.533294672146362];
b = [1.232247112503961  -1.216444449798070   0.301047559642400];

%% Go through it step by step
% 1. Sample the impulse response
nx = 8192;
delta = zeros(nx,1); delta(1) = 1;
hiir = filter(b,a,delta);    
% 2. FFT
fh = fft(hiir);    
% 3. Set phase to zero
fhZeroPhase = abs(fh);    
% 4. inverse fft
hfir = ifft(fhZeroPhase);    
% 5. cut and shift to desired size. Let's go with 63 tabs
nFinal = 63;
hFinal = circshift(hfir,(nFinal-1)/2);
hFinal = hFinal(1:nFinal,:);

%% Plot the difference between the two spectra
freqAxis = (0:nx/2)'/nx*fs;
fDiff = fft(hFinal,nx)./fh;
semilogx(freqAxis,20*log10(abs(fDiff(1:nx/2+1))));
ylabel('Error in dB');
xlabel('Frequency in Hz');
set(gca,'xlim',[20 20000]);
% Note the scale: they magtnitude of the filters matches better than 1.2e-4
% dB. Depending on how good your match needs to be, you can probably get
% away with a much shorter filter