
说起FIR滤波器设计,好多人一听就头大,什么窗函数、转移函数,感觉像在啃硬骨头。真要上手做起来,也就那么回事儿,我今天就唠唠我是怎么把这玩意儿搞定的,特别简单。 我当时...
说起FIR滤波器设计,好多人一听就头大,什么窗函数、转移函数,感觉像在啃硬骨头。真要上手做起来,也就那么回事儿,我今天就唠唠我是怎么把这玩意儿搞定的,特别简单。
我当时接手一个项目,需要对采集到的信号做个干净的处理,要求不高,就是想把高频噪声给滤掉,搞个低通。本来想用IIR来着,但一想IIR那个相移问题,头疼,而且稳定性还得盯着,算了,还是用FIR,起码线性相位好搞。
设计滤波器,最开始你得知道自己想要啥效果。我对着需求看了半天,定了几个硬指标:
这几个参数定下来,后面所有的计算才有方向。说白了,就是搞清楚“哪里放行,哪里关门,关门要关多紧”。

FIR滤波器设计,核心就在于确定它的阶数N,也就是滤波器的“长度”。阶数越高,性能越但计算量越大,延迟也越大。我一开始不知道要多少,就凭经验先猜了个N=50试试水。
然后,根据我选的低通滤波需求,我对着一本老掉牙的数字信号处理书翻了翻,开始琢磨怎么用窗函数法。窗函数法是最简单粗暴的,适合快速出原型。
窗函数就是帮你“平滑”地截断理想滤波器的响应,避免一刀切带来的毛刺。常见的有矩形窗、汉宁窗(Hanning)、汉明窗(Hamming)、布莱克曼窗(Blackman)这些。
我寻思着,矩形窗过渡带太宽,效果不得选个带旁瓣衰减好的。我选了汉明窗,因为我需要的阻带衰减大概在-50dB左右,汉明窗能基本满足这个要求,而且计算起来简单。

确定了窗函数,接下来就是算理想滤波器的频率响应 $H_d(\omega)$。对于低通,它就是个简单的开关函数:在通带内是1,在阻带内是0。
有了理想响应,下一步就是根据窗函数和阶数N,算出滤波器的实际脉冲响应 $h[n]$。这就是把理想脉冲响应 $h_d[n]$ 和窗函数 $w[n]$ 乘起来。
我把所有频率指标都转成了数字频率 $\omega_c$(也就是截止频率除以采样频率再乘以 $2\pi$)。然后,我用公式迭代地把每个 $h[n]$ 的值都算出来。这个过程就是编程实现的核心,无非是循环套循环,根据 $n=0$ 到 $N-1$ 算出所有系数。
我就是把这些系数一个一个扔到Excel里算,算出来一组系数,然后发现阻带衰减还是差点意思。看来N=50有点不够用。
第一次算出来,用示波器看看效果,发现15kHz附近的衰减只有-30dB,差远了。我没有回头去找复杂的频域法或者最小二乘法,而是继续用窗函数法,直接把N给抬高了。我把阶数N直接提到了100。
重新计算,这回把窗函数换成了布莱克曼窗,因为它旁瓣衰减更虽然过渡带会略微变宽一点,但对于我的需求来说,只要阻带衰减够狠就行。
一轮跑下来,用MATLAB(当然你也可以用Python的SciPy)仿真一跑,频率响应曲线一下子就漂亮了。10kHz以下基本平坦,15kHz一过,曲线蹭蹭往下掉,到了20kHz,衰减已经到-60dB了。这效果我看着就满意了。
我把这101个(N=100,所以是101个系数)浮点数系数导出来,放到嵌入式平台上去实现。整个过程就是定指标、选方法、套公式、调参数,一步步把理想模型拉到现实中,没想象中那么玄乎。