ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FIR滤波器完全指南:从原理到工程实践

FIR滤波器完全指南:从原理到工程实践 1. FIR滤波器到底是什么——从名字里读懂它的底气先聊聊这个标题。No Fear with FIR字面意思是“有FIR不用怕”。在数字信号处理这个圈子里敢这么说的东西不多。IIR滤波器性能好但稳定性让人揪心FFT性能强但窗函数和频谱泄漏一堆讲究自适应滤波更不用说收敛性、步长因子调得让人头秃。唯独FIR你用起来可以非常踏实。这不是玄学是它的数学结构决定的。FIR的全称是Finite Impulse Response有限冲激响应滤波器。它做的事情本质上就是一句话当前的输出是过去N个输入样本的加权和系数是固定的权值就是滤波器的抽头系数。用公式表示就是y[n] b0*x[n] b1*x[n-1] b2*x[n-2] ... bN*x[n-N]没有反馈项没有递归结构。这一点跟IIRInfinite Impulse Response无限冲激响应滤波器有本质区别。IIR长这样y[n] b0*x[n] b1*x[n-1] ... - a1*y[n-1] - a2*y[n-2] - ...看到那个负号了吗输出还依赖于过去的输出这就是“反馈”。一有反馈事情就开始复杂了可能发散可能振荡相位特性也很难控制。而FIR没有反馈输出只跟输入有关这就带来了几个非常重要的好性质——后面我会逐个拆开讲。先说结论FIR这玩意儿从算法结构上就注定了它适合用在音频处理、通信基带、生物电信号处理、传感器数据清洗这类对稳定性和相位要求高的场景。你要是做实时控制系统、音频均衡器、心电信号滤波FIR几乎是首选。这篇博文不会讲太多数学推导主要聊实际操作中你真正需要知道的东西以及我这些年踩过的坑。1.1 FIR的“有限”二字到底值多少钱很多人把“有限冲激响应”当成一个名词背下来就完了其实这两个字是理解FIR所有优点的钥匙。什么叫有限冲激响应就是你在输入端打一个很短很短的脉冲理想情况下就是一个单位冲激信号滤波器的输出在有限的N个样本之后会彻底归零。这跟IIR有着天壤之别。IIR的冲激响应理论上永远不会完全归零它只是不断衰减、趋近于零这在数学上就对应着极点的存在。极点在单位圆内且不碰边界系统就稳定一旦设计参数不对或者量化误差积累极点偏移到单位圆外系统直接发散——你听到的就是刺耳的爆音或者控制系统里的剧烈振荡。FIR没有极点它的所有零点都随便放系统的稳定性完全不需要担心。这一点在嵌入式定点实现里特别省心。用IIR做定点化你得反复验证量化后的极点位置稍微收敛不住就完蛋用FIR只要输入不溢出输出一定不会发散。说白了IIR是走钢丝FIR是走平地。1.2 FIR还有一个隐藏天赋严格线性相位这是很多人忽略但实际工程中极度重要的性质。FIR滤波器只要抽头系数满足对称性奇对称或偶对称就会拥有严格的线性相位特性。什么是线性相位就是滤波器对不同频率成分的延迟时间是相同的。我举个实际的例子方便你理解。你录了一段吉他solo经过一个滤波器处理。如果滤波器是非线性相位的比如IIR那不同频率成分被延迟的时间不同波形就会在时间上被“扭曲”。低频可能晚到0.5毫秒高频可能早到0.2毫秒——你听到的效果就是声音“糊了”瞬态不清晰甚至有一种奇怪的“咝咝”瑕疵感。FIR因为能精确保证输出和输入之间的延迟是固定值等于(N-1)/2个采样周期所以波形形状不会失真只是整体平移了一下。这对于音频母带处理、通信系统里的符号同步、心电图的波形诊断都是刚需。心电信号滤波你去掉基线漂移的时候如果滤波造成QRS波群形状改变医生真会骂人的。IIR再优秀在这类场景也得靠边站。2. FIR和IIR正面交锋——选型前必须搞清的底层差异既然标题热搜里带“fir和iir滤波器区别”那这一节我得好好聊聊。很多新手在选型时很痛苦网上有人说FIR好有人说IIR阶数低效率高到底听谁的我的建议很简单把两类滤波器的核心差异列出来对着自己的需求一项项打勾答案自然就出来了。先看几个最关键的对比维度。对比维度FIRIIR冲激响应长度有限N个样本后归零理论上无限实际不断衰减系统结构非递归无反馈递归有反馈稳定性天然稳定无条件稳定需要保证极点在单位圆内相位特性可设计为严格线性相位非线性相位除非特殊设计同等性能下的阶数高通常几十到几百阶低通常几到几十阶计算量较大较小设计方法窗函数法、频率采样法、最优化法双线性变换法、冲激响应不变法定点实现难度容易较难容易溢出或振荡这个表格基本就是选型的核心依据。不是说IIR就不好IIR在计算资源受限、对相位要求不高的场景下非常香。举个典型的例子你要做一个简单的电池电压低通滤波把50Hz工频纹波滤掉只要看个趋势那用二阶巴特沃斯IIR三下五除二就搞定了计算量小到可以忽略。但你要是做的是一个多通道音频均衡器用户能直接听到处理效果那线性相位就比那点计算量重要得多。2.1 稳定性问题怎么强调都不过分关于稳定性我多说几句。IIR的稳定性不是“设计出来就完事”的它在你实现的那一刻才是真正见分晓的时候。为什么因为你设计滤波器用的是浮点运算但很多嵌入式平台只能用定点运算或者浮点精度有限。你设计时极点位置在0.9823量化之后可能变成1.001——越过单位圆系统就炸了。我之前做过一个电机控制项目用IIR做电流环的滤波仿真阶段一切正常一上真机就偶发振荡。查了两天才发现问题出在某个极端温度下ADC采样的噪声变大导致IIR内部状态变量数值波动加上定点舍入误差极点附近的等效响应开始变得敏感。后来换成FIR同样功能稳定性问题彻底消失代价只是CPU占用多了大概30%。但30%的算力换一个绝不振荡的系统怎么说都值。FIR的稳定性用一句话概括就是它没有反馈过去输入的贡献终会归零不可能凭空产生无穷大的输出。真要出问题也只可能是输入溢出或者中间累加溢出这是工程实现问题不是算法原理问题可控得多。2.2 相位线性为什么能决定你听到的声音我已经说了线性相位很重要再往深一层讲原因。一个信号比如一段语音是由无数个不同频率的基音和泛音组成的。这些成分在时间轴上共同构成一个完整的波形。假若一个滤波器对低频成分延迟5个采样点对高频成分延迟3个采样点那么输出波形中各频率成分的相对时间关系就乱了波形形状不再复现输入。人耳对这种相位失真虽然不敏感的频段有但在瞬态信号上很敏感。打击乐的“啪”一下、语音中的爆破音、吉他拨弦的起振瞬间这些瞬态如果相位乱了声音就失去了原有的“锐利感”变得松散、模糊。做音频效果器尤其是压缩器、均衡器这种对声音质感要求极高的设备IIR总有一种“打了一层纱”的感觉FIR则能保持声音的原始轮廓。在通信领域这件事更严肃。数字解调器里发射端和接收端的滤波器如果相位非线性会直接导致符号间干扰ISI就算均衡器也很难完全挽回。所以很多通信标准里干脆规定了必须用满足线性相位的滤波器——比如根升余弦滤波器它本质上就是个定制的FIR。4G/5G基带里那些成型滤波器用FIR实现是标准操作。2.3 计算量这笔账算清楚才不慌FIR容易被诟病的一点就是计算量大。同样达到-60dB阻带衰减、0.1dB通带纹波的性能指标IIR可能只要8阶FIR可能要101阶或者更高。每个输入样本要做101次乘加运算实时系统里如果采样率是48kHz那一秒钟就是48000 * 101 ≈ 484.8万次乘加。听起来挺吓人但现在的MCU和DSP算这点东西真的不算什么。我实测过在一颗主频168MHz的Cortex-M4上跑一个101阶FIR滤波器48kHz采样率每秒钟要处理101 * 48000 4848000次乘加。用CMSIS-DSP库的arm_fir_f32函数开启FPU和循环展开优化后每次乘加大概在几个周期内完成总CPU占用通常在10%到15%之间浮动。这完全在可接受范围内。更骚的操作是用多相结构或者FFT快速卷积。当滤波器阶数非常高上千阶时把时域卷积换成频域乘法用FFT处理后做IFFT计算复杂度可以从O(NM)降到O(NlogN)。不过这是后话一般工程里面阶数不超过256时直接时域卷积反而更简单、延迟更低。你千万别一上来就整FFT卷积代码复杂不说分组带来的延迟在某些实时控制系统里可能就是不可接受的。3. 从零设计一个FIR滤波器——完整实操过程聊完理论来点硬核的。这一节我会带你完整走一遍FIR滤波器的设计流程。我的目标不是给你一堆PPT式的理论而是让你看完之后能自己动手设计出一个可用的滤波器直接丢到工程里跑。3.1 第一步把指标定清楚别急着写代码滤波器的设计指标不是拍脑袋定的它来自你的应用场景。设计之前你必须回答这几个问题采样率是多少信号里哪些频率成分是有用的哪些是要滤掉的两种频率之间有没有过渡带空间通带容许的最大波动是多少通常用dB表示比如±0.1dB或者等效为通带纹波系数。阻带需要衰减多少比如要-60dB这个指标直接决定了滤波器的阶数。信号的延迟是否可以接受FIR的固有群延迟是固定的(N-1)/2个采样周期注意不是平均是固定。举个例子。假设我有一个心电信号采集系统采样率是500Hz。ECG信号的有效频带大致在0.5Hz到40Hz之间高频噪声主要来自肌电干扰大概30Hz以上就变得明显了。我需要滤掉30Hz以上的成分但又要保留QRS波群的形态那就把通带截止频率设为30Hz阻带截止频率设为45Hz留一点过渡带的余量通带纹波0.1dB阻带衰减60dB。这么一算我用Kaiser窗估算阶数的公式N ≈ (A - 7.95) / (2.285 * Δω)。A是阻带衰减dBΔω是过渡带宽度归一化到π。A60Δω (45-30) / (500/2) 15 / 250 0.06归一化角频率π对应250Hz代入算一下N ≈ (60 - 7.95) / (2.285 * 0.06) ≈ 52.05 / 0.1371 ≈ 379.5取整到奇数为了线性相位对称性方便取奇数比较常见不过偶数也行大概381阶。这个阶数在常规MCU上跑起来有点吃力但在PC或者高性能DSP上完全没问题。如果觉得阶数太高可以适当放宽过渡带比如把阻带起始频率推到50Hz或者降低阻带衰减到40dB阶数就能降下来不少。工程就是妥协的艺术。3.2 第二步用Python快速出设计方案现在我们用Python动手设计。我通常用scipy.signal里面的firwin和remez是最常用的两个函数。firwin适合用窗函数法快速得到一个能用的设计remez适合做等纹波最优设计在同等阶数下能实现更低的纹波/更高的衰减。import numpy as np from scipy.signal import firwin, freqz, remez import matplotlib.pyplot as plt # 参数设定 fs 500.0 # 采样率 Hz f_pass 30.0 # 通带截止 Hz f_stop 45.0 # 阻带起始 Hz A_stop 60.0 # 阻带衰减 dB # 用 firwin 设计低通滤波器 numtaps 381 taps firwin(numtaps, f_pass, fsfs, windowblackman) # 用 freqz 看频率响应 w, h freqz(taps, worN4096, fsfs) plt.figure(figsize(10, 6)) plt.plot(w, 20 * np.log10(np.abs(h))) plt.axvline(f_pass, colorred, linestyle--, labelPassband edge) plt.axvline(f_stop, colorgreen, linestyle--, labelStopband edge) plt.axhline(-60, colorgray, linestyle:, label-60 dB target) plt.xlabel(Frequency (Hz)) plt.ylabel(Magnitude (dB)) plt.title(FIR Low-pass Filter Frequency Response) plt.legend() plt.grid() plt.show()跑完这个代码你会看到一条从通带到阻带平滑下降的曲线在45Hz附近应该能达到-60dB以下。如果曲线没达标就调整numtaps或者换用更陡峭的窗。如果想用最优化设计可以换成remeztaps remez(numtaps, [0, f_pass, f_stop, fs/2], [1, 0], fsfs, weight[1, 100])这里weight参数很关键它表示通带和阻带的权重比。如果你特别在意阻带衰减就把阻带权重调大。remez出来的滤波器在同等阶数下通常比窗函数法性能更好但缺点是通带和阻带纹波是等幅的不像窗函数法那样越靠近边缘纹波越大。选哪种我的经验是如果没过硬的性能需求firwin简单省事如果追求同样阶数下的最优性能那就上remez。3.3 第三步把设计好的系数落地到工程仿真通过了代码写好了接下来就是落地。落地环节有几个容易踩的坑。第一系数量化。你在Python里拿到的是64位浮点系数到了嵌入式平台可能是16位定点数。假设用Q15格式1位符号位15位小数位你应该先把系数归一化到[-1, 1)再乘以32768四舍五入取整。关键检查点量化后的滤波器频率响应是否还满足指标。很多情况下16位量化对60dB阻带衰减的滤波器是够用的但如果你需要80dB以上的衰减16位就不太够了得上24位或32位。第二中间累加精度。FIR计算的核心是乘累加MAC多个乘法结果累加时可能会超过单精度浮点或定点数的表示范围。举个典型例子一个100阶的FIR输入信号幅值在满量程附近系数也接近满量程累加结果可能达到单个样本的几十倍如果累加器用的是16位直接溢出。正确做法是用32位或更宽的累加器或者在输入进滤波器之前做一次缩放。第三C语言实现。如果不想手写CMSIS-DSP的arm_fir_f32是Cortex-M平台的首选极其高效。如果平台没有现成库那就老老实实写typedef struct { float *coeffs; // 系数数组长度numTaps float *history; // 历史输入缓冲 int numTaps; int pos; // 环形缓冲当前指针 } FIRFilter; float fir_process(FIRFilter *fir, float input) { int i; float acc 0.0f; // 写入新的输入到环形缓冲 fir-history[fir-pos] input; // 乘累加 int idx fir-pos; for (i 0; i fir-numTaps; i) { acc fir-coeffs[i] * fir-history[idx]; idx (idx 0) ? (fir-numTaps - 1) : (idx - 1); } // 更新指针 fir-pos (fir-pos 1) % fir-numTaps; return acc; }这段代码是教材级别的实现简洁清晰但性能一般因为内层循环做乘法的时间可能还有优化空间。如果你做实时音频或高频采集强烈建议用现成的DSP库或者至少把循环展开写并开启编译器的自动向量化选项。4. 实际工程中的坑与经验——No Fear的真正底气这一节是我最想写的。理论、设计方法网上到处都有但是工程里踩过的坑才是花钱和时间买来的经验。我按踩坑频率从高到低排个序。4.1 频率采样法为何容易翻车很多教材讲FIR设计时会提到频率采样法但我实际用下来觉得它是最容易“看上去很美用起来翻车”的方法。频率采样法的思路是在频率轴上等间隔取N个点在这些点上精确匹配你期望的频率响应然后通过IDFT得到时域系数。听起来很美好但问题在于两点之间没有约束你只保证了采样点上的响应两个采样点之间可能出现很大的纹波这就是所谓的“不确定区域”。比如输入信号有一个频率落在两个采样点之间它的增益可能是你想都没想过的-10dB而不是平坦的0dB。这种情况在宽带信号处理里简直致命。我的建议频率采样法只适合做非常规整、频率间隔跟采样点对齐的场景或者纯粹用来教学真正工程上还是老老实实窗函数法或remez/parks-mcclellan算法。4.2 定点化之坑——系数一量化性能就缩水有人觉得浮点算出来效果不错定点化不就取整吗能差多少我告诉你16位量化一个60dB阻带衰减的FIR实测阻带衰减可能掉到50dB甚至更低。这个问题在窄带滤波器中尤其严重因为窄带滤波器系数动态范围很大几个大系数就占满了16位的整数部分那些小系数只有几个bit的精度量化误差直接映入阻带响应里。解决办法有几个一是提高量化位数用24位或32位定点数二是对系数做归一化后再量化保证最大系数用满15位小数精度三是对滤波器的系数做“裂化”处理把一组高动态范围的系数拆成两组低动态范围的系数并联这样每一组量化精度都够用。第三种方法听着复杂但很好用我做过一个窄带带通滤波器就是靠这个技保住阻带衰减指标的。还有一个特别容易忽略的坑定点数乘法溢出。假设Q15格式两个Q15数相乘结果需要32位才能完整保存。如果你在16位MCU上用16位乘16位的指令乘法结果的低16位被丢弃滤波器就可能输出大量“嘶嘶”噪声这其实就是量化噪声被直接注入到了信号。正确做法是确保乘累加在32位里进行最后再一次性截断或舍入回16位。4.3 延迟预算——实时控制系统必须算清的一笔账FIR的线性相位特性也意味着一个代价固定的群延迟。对一个N阶对称FIR任何频率成分的延迟都是(N-1)/2个采样周期。如果N101、采样率48kHz那延迟大约是100/2/48000 ≈ 1.04毫秒。在音频处理里1毫秒延迟基本感知不到。但在某些工业控制场景比如电流环、位置环延迟每增加一点系统的相位裕度就少一点轻则性能下降重则震荡。前年帮人做一个伺服驱动器的电流采样滤波采样率20kHzFIR做了129阶固定延迟是64/20000 ≈ 3.2毫秒。听起来不多但电流环的响应时间要求通常在1毫秒以内3.2毫秒的延迟直接把环路整成震荡了。后来方案改成FIR保留因为要滤波质量但在环路前向通路里加用预测补偿或者把采样率提到40kHz阶数不变的情况下同样的频带设计延迟减半才把性能拉回来。这个项目给我的教训是在设计阶段就要把滤波器的延迟预算算进去不要等系统调不通了再回头找原因。一个可以快速估算的公式群延迟 (N-1) / (2 * fs)。你接手一个实时系统第一件事就要确认这个延迟是否可接受。4.4 别忽略输入信号的有效位数和量化噪声还有一个很多人会忽略的点FIR的性能上限受限于ADC和DAC的量化噪声。如果你的ADC是12位的动态范围理论上限约72dB就算你用了一个80dB阻带衰减的FIR系统的整体性能还是被ADC卡在72dB。这个时候盲目提高滤波器阶数毫无意义反而浪费算力。我在音频产品上见过不少人把FIR从128阶换成512阶结果听感改善微乎其微因为瓶颈在解码芯片的模拟输出端。做完一个滤波器先算一道“链路预算”输入量化噪声、中间处理量化噪声、输出量化噪声三者要平衡。滤波器自己的指标再漂亮也只是整个链路的一部分不是全部。5. 常见问题与排查技巧实录这一节我把这些年遇到的典型问题汇总成一个速查表再逐个讲排查思路。现象可能原因排查方法滤波器输出完全不对/变成噪声系数数组顺序颠倒检查卷积方向很多实现里是系数跟历史输入反向相乘方向错基本等于白做滤波器频响远达不到设计指标系数量化位数不够用高精度仿真对比量化前后频响低频输出有微弱波动累加器溢出检查MAC累加长度改用32位或64位累加信号被延迟特别严重滤波器阶数过高重算延迟预算考虑用多相分解或换成IIR实时系统偶发爆音中间状态未清干净使能时出现瞬态在滤波器输出使能前先跑一段零输入把状态清干净高频噪声去不掉滤波器系数没有落到位或设计时阻带起始频率设太高重新检查频响曲线确认阻带位置逐个展开讲。第一个问题系数顺序。这是我的老本行当年第一次用CMSIS-DSP库写FIR忘记查它的系数顺序约定直接用自己的顺序塞进去结果高频全被滤没了低频反而乱成一团。CMSIS-DSP的arm_fir_f32规定系数数组按时间顺序存放第0个是当前输入对应的系数最后一个是最早输入的系数。你处理的历史缓冲反向遍历就对了。这个问题排第一是因为它最容易发生又最隐蔽肉眼看到频谱乱了你根本没想到是顺序。第二个问题量化精度。当你感觉“设计仿真很完美一上真机就拉胯”的时候先用Python读回实际固件里的系数做一次仿真对比。如果仿真复现了问题基本就是量化精度的问题。我之前做24位ADC的系统采样率256kHzFIR做到255阶用16位系数阻带衰减本来设计有75dB实测只有58dB查出来就是量化精度不够换成Q31格式立马恢复。第三个问题累加器溢出。这个在音频开发和控制系统里都常见特征很诡异低幅值信号正常一旦信号幅度大一点输出就出现畸变。你查系数查半天屁用没有因为问题压根不在系数在于内部累加结果超过了表示范围。我试过一个48阶FIR输入是±1.0满量程累加时如果每个系数也是接近1.0最坏情况下累加结果可能达到48如果中间累加只用Q15范围-1.0到0.9999那必炸。《德州仪器应用笔记SPRA491》专门讲过这个问题核心建议就是MAC累加用40位或32位输出前再做截断。第四个问题延迟。前面讲过了不多说。只加一句你要真对群延迟敏感可以用最小相位FIR它在放弃线性相位的同时能大幅缩短延迟代价是相位响应变成非线性的了。这是一条折中路线适合那些既想要FIR稳定性和灵活设计能力、又对延迟敏感的应用。第五个问题爆音。这个在音频设备里尤其常见。你程序上电时滤波器状态数组里的值全是垃圾第一帧输出就带着这些垃圾的历史输入计算产生一个巨大的初始瞬态听起来就是“噗”一声。解决方法是初始化时把history数组清零并且在真正启用输出之前先让滤波器吞掉几百个零样本让内部状态充分“洗”一遍。第六个问题高频噪声去不掉。有时候不是滤波器没设计对而是你设计的时候通带和阻带的边界没选好。比如你要滤掉50Hz工频但有用信号里刚好有一个45Hz的慢波你把阻带截止频率设在45Hz以下可能连有用信号一起削了效果看着就是“该滤的没滤干净不该滤的也没了”。排查方法直接画滤波器的频响曲线对照你的信号频谱看通带覆盖范围是不是正确。再补一个很多人没用过的骚操作在线调整滤波器系数。FIR的系数是可以热更新的因为结构里没有反馈理论上你随时换一组系数不会引起不稳定。IIR就做不到你换系数的时候如果状态变量还是旧系数对应的值很容易产生瞬态甚至振荡。所以在实现“自适应滤波”或者“动态均衡”的时候FIR有着天然的优势。我之前做一个音频均衡器的项目用户界面上拖动频率滑块底层就是实时修改FIR系数。每次拖动时先把新系数预计算好然后在一个信号过零点的时刻一次性切换配合一点插值平滑完全听不到切换的咔哒声。IIR的均衡器要做出同样的平滑切换你得做系数插值或者状态保持复杂度高出不少。6. 工具选型与我的最终建议最后聊聊我日常工作里围绕FIR的一套工具链。首先是设计端Python scipy.signal是我的绝对主力。firwin、remez、kaiserord这几个函数覆盖了九成需求。偶尔我会用MATLAB的Filter Designer以前叫FDATool它的图形界面非常直观可以拖拖滑块就生成一个滤波器但说实话在自动化批处理和版本管理上不如Python脚本。仿真验证用Python把频响、相位、群延迟全画出来。复杂场景会用Simulink做整个信号链路的仿真但模型基本上还是在跑同样的FIR算法。真正上嵌入式平台之后调试手段主要是逻辑分析仪配合DAC输出中间节点看信号波形。或者直接在固件里留一个调试模式把滤波前后的数据通过串口发到PC上用Python做离线分析。给新手的一个建议不要一上来就追求“最优设计”先用窗函数法做一个简单、能用的FIR跑通整个链路。你有了一个能跑的起点再来优化性能指标这样心态和方法论都好很多。一上来就remex 多相分解 循环优化容易让你迷失在细节里最后连基础功能都跑不通。从设计流程的角度我的标准代码里一定会有下面两个函数一个是设计滤波器的一个是验证滤波器的def design_fir_lowpass(fs, f_pass, f_stop, A_stop, methodfirwin): 设计低通FIR滤波器返回系数 # 估算阶数Kaiser公式 delta_f (f_stop - f_pass) / fs N_min int((A_stop - 7.95) / (14.36 * delta_f)) 1 N N_min if N_min % 2 1 else N_min 1 # 保证奇数 if method firwin: taps firwin(N, f_pass, fsfs, windowblackman) elif method remez: taps remez(N, [0, f_pass, f_stop, fs/2], [1, 0], fsfs, weight[1, 100]) return taps def verify_fir_response(taps, fs, f_pass, f_stop, A_stop): 验证滤波器频率响应是否达标 w, h freqz(taps, worN8192, fsfs) h_db 20 * np.log10(np.abs(h) 1e-12) passband_ok np.all(h_db[(w 0) (w f_pass)] -0.1) stopband_ok np.all(h_db[(w f_stop) (w fs/2)] -A_stop) return passband_ok, stopband_ok这两个函数我几乎每个项目都会用已经形成了肌肉记忆。改一下参数就能适配高通、带通、带阻滤波器方便得很。对我个人来说FIR滤波器是整个DSP领域里最值得先熟练掌握的工具之一原因无他它稳定、可靠、设计流程成熟几乎没有什么暗坑能让你调试一个礼拜。你只要把指标定清楚、实现时注意系数顺序和累加精度基本就能发挥它应有的性能。No Fear with FIR这句话与其说是口号不如说是所有被IIR搞过心态的工程师的共同感悟。以后你在工程里再遇到滤波需求先从FIR开始评估大概率不会错。
返回列表