资讯详情 基于MATLAB的模拟与数字滤波器设计实战:IIR/FIR完整流程
📅 2026/10/5 4:39:54
做滤波器设计这件事我用MATLAB折腾了整整一个项目周期从模拟滤波器到数字滤波器从IIR到FIR中间踩了不少坑也总结出一套可以快速落地的设计流程。这篇文章就围绕基于MATLAB的模拟滤波器和数字滤波器设计这个主题把我在实际项目中用到的设计思路、关键函数、参数计算方法和调试技巧完整梳理一遍尤其是数字滤波器部分IIR和FIR都在里面了希望能帮你绕开我当初走过的弯路。先简单交代一下背景。当时的需求是做一套信号预处理链路前端传感器采集到的信号混入了工频干扰和高频噪声需要先做模拟抗混叠滤波再进ADC之后在数字域里做一次精确的带通滤波提取目标频段的特征信号。整个过程既要保证实时性又要把幅频响应做得尽量平坦群延迟不能太离谱。这个需求拆开来看恰好覆盖了今天这篇文章的全部内容——模拟滤波器设计、数字IIR滤波器设计、数字FIR滤波器设计以及MATLAB下的整套验证方法。如果你正准备做课程设计、毕业设计或者在实际工程里需要搭一套滤波方案这篇文章可以直接当操作手册用。每个关键函数我都标注了背后的原理和参数含义每一步都给出了可以直接抄作业的代码模板最后还整理了我在实际调试中遇到的高频问题和排查方法这些在官方文档里基本找不到。1. 滤波器设计方案的整体拆解先想清楚再动手1.1 模拟滤波器和数字滤波器到底该怎么选很多初学者一上来就打开MATLAB随便选一个函数开始跑这是大忌。滤波器设计的第一步不是写代码而是根据你的系统架构决定用模拟滤波器还是数字滤波器或者两者都用。模拟滤波器直接处理连续信号使用电阻、电容、电感或运放搭建在数字系统里的位置通常在ADC之前作用很明确抗混叠、滤除带外大干扰、保护后级ADC不饱和。数字滤波器处理离散信号靠算法实现精度高、稳定性好、灵活性大同一个系数向量就能轻松改变滤波特性这在硬件上换一套元件才能实现的效果在MATLAB里只是一条命令的事。实际工程中绝大多数系统走的是混合架构模拟端做粗滤波数字端做精滤波。比如我的这个项目前级用一个二阶有源低通把采样率以上的噪声压下去防止ADC混叠后级在FPGA里跑一个数字带通滤波器把目标频段之外的成分全部干掉。这两者的设计在MATLAB里都是完全可控的而且可以用同一套指标去校验。如果你只是学习仿真不在意真实硬件实现也可以只做数字滤波器。但我的建议是模拟滤波器的设计过程一定要走一遍它能帮你建立起对极点、零点、频带变换这些基本概念的直观理解这些理解在数字域里同样适用可替代的学习路径几乎不存在。1.2 设计指标怎么定通带、阻带、纹波和阶数是核心无论做模拟还是数字滤波器首先要有一组量化的指标。MATLAB里几乎每个设计函数都要求你提供通带截止频率也就是你希望信号基本无损通过的频率点阻带起始频率也就是你希望信号被明显抑制的频率点通带最大波纹单位是dB表示通带内幅度的允许波动范围阻带最小衰减单位是dB表示阻带内需要达到的最小抑制量这四个指标直接决定滤波器阶数。阶数越高过渡带越窄、阻带衰减越深但实现复杂度、相位延迟、数值稳定性都会变差甚至出现不可实现的极端参数。我自己习惯的做法是先按需求把指标列成一张小表再根据这张表去算阶数最后才进入具体设计。这不是形式主义而是避免你调了一下午参数最后发现电路根本没法实现或者数字滤波器需要几千阶才算得动。以我项目里的数字带通滤波器为例我当时的目标是通带20Hz到200Hz通带波纹0.5dB阻带在10Hz和400Hz处衰减至少40dB采样率是2kHz。这个需求做FIR等纹波设计阶数大约在几百量级做IIR椭圆设计阶数只需要个位数。从这组指标就能看出对于实时性敏感且硬件资源受限的系统IIR几乎是必然选择这也是文章标题里数字滤波器部分重点覆盖IIR的原因。1.3 MATLAB滤波器设计的整体工具箱思路MATLAB做滤波器设计最常用的工具箱是Signal Processing Toolbox里面按设计方法可分为三大类一是基于经典模拟原型变换的滤波器设计函数如butter、cheby1、cheby2、ellip二是专门做FIR设计的函数如fir1、fir2、firls、remez三是交互式设计工具如fdatool或新版中的filterDesigner。此外还有freqz、fvtool、zplane这些验证和可视化函数它们是你判断滤波器是否符合要求的眼睛。我个人的建议是第一遍设计先尝试交互式工具Filter Designer它能把参数、幅频响应、零极点图全部摆在一个界面上直观但是效率不高真正做项目时要用脚本方式把设计流程固化成一套函数这样换一组参数就能重新生成一组滤波器。下文所有示例代码我都按这个思路来写运行环境是MATLAB R2020a及以上版本更老的版本里绝大部分函数同样可用。2. 模拟滤波器设计实战从原型到频带变换2.1 经典模拟原型滤波器巴特沃斯、切比雪夫、椭圆与贝塞尔模拟滤波器的经典设计路径是原型频带变换。所谓原型就是一个归一化的低通滤波器截止频率为1 rad/s然后通过数学变换变成你需要的低通、高通、带通或带阻。MATLAB里的buttap、cheb1ap、cheb2ap、ellipap就是用来生成这些原型的函数分别对应巴特沃斯、切比雪夫I型、切比雪夫II型和椭圆滤波器。这几种原型各有性格。巴特沃斯滤波器的幅频特性在通带内最平坦单调无纹波代价是过渡带相对宽相同指标下阶数往往最高切比雪夫I型把误差分配到通带通带内等波纹过渡带比巴特沃斯陡但通带边缘会有可见的起伏切比雪夫II型反过来通带平坦、阻带等波纹缺点是阻带内的衰减不是单调加深的椭圆滤波器在通带和阻带都有等波纹但它的过渡带最窄相同指标下阶数最低代价是相位非线性最严重。如果你对相位有要求贝塞尔滤波器值得关注它的群延迟在通带内最平坦但牺牲的是幅频的陡峭度。实际选择时我一般遵循这个原则对幅频特性要求高且阶数无所谓的选巴特沃斯对阶数敏感、硬件资源紧的选椭圆信号是脉冲或方波类需要保形那就优先考虑贝塞尔。切比雪夫I和II型卡在中间主要用于那些不想用椭圆但又能容忍一定波纹的场景。2.2 用MATLAB完成模拟低通、高通、带通设计的完整示例直接看代码比空谈原理更有用。假设我要设计一个通带截止频率为1kHz的模拟低通滤波器阻带起始频率2kHz通带波纹1dB阻带衰减40dB。第一步先估算阶数% 设计指标 Wp 2*pi*1000; % 通带边界角频率单位 rad/s Ws 2*pi*2000; % 阻带边界角频率单位 rad/s Rp 1; % 通带最大波纹单位 dB Rs 40; % 阻带最小衰减单位 dB % 求最小阶数和截止频率 [n, Wn] buttord(Wp, Ws, Rp, Rs, s); % 用butter直接设计模拟滤波器 [b, a] butter(n, Wn, s);这里有一个关键点模拟域设计时频率参数必须是角频率rad/s这就是最后一个参数s的含义。很多初学者直接用Hz去调用butter结果响应完全对不上这是模拟滤波器设计最常见的错误。buttord返回的Wn是修正后的截止频率它已经考虑了波纹和衰减的折中不是简单地等于Wp或Ws。设计完低通之后如果要做高通或带通最优雅的办法是从一个已设计好的低通原型出发使用lp2hp、lp2bp、lp2bs做频带变换。实现高通只需要加一条命令% 把1kHz低通原型变换成截止频率1kHz的高通 [bt, at] lp2hp(b, a, 2*pi*1000);带通则要指定中心频率和带宽两个参数实际中带通滤波器用的最多因为它可以把信号限制在目标频段内有效规避直流偏置和带外干扰。下面是一个带通设计的完整代码模拟的是中心频率500Hz、带宽200Hz的场景% 带通设计中心频率500Hz带宽200Hz W0 2*pi*500; % 中心角频率 Bw 2*pi*200; % 带宽角频率 % 先从低通原型出发 [z, p, k] buttap(n); [b, a] zp2tf(z, p, k); % 频带变换 [bt, at] lp2bp(b, a, W0, Bw);lp2bp把归一化低通原型变换为中心频率W0、带宽Bw的带通滤波器。得到b和a之后你可以用freqs直接观察频响特性用bode命令看幅频和相频。这里我建议养成一个习惯每设计完一个滤波器马上用freqs把它画出来并且对照设计指标逐项检查。亲测有效的检查顺序是通带边界是否符合、阻带衰减是否达标、过渡带是否陡峭、带内是否出现异常尖峰。2.3 模拟滤波器转换成数字滤波器双线性变换法的正确打开方式模拟滤波器设计完后如果目标是数字系统还需要将它离散化。MATLAB提供了impinvar脉冲响应不变法和bilinear双线性变换法。脉冲响应不变法只适合低通滤波器和带通滤波器而且会产生频谱混叠应用场景很受限双线性变换法通过非线性频率压缩避免混叠是工程中的主力方法但代价是频率轴被压缩高频部分会失真所以通常需要先做频率预畸变。用bilinear完成转换时需要注意给到它的原型截止频率必须是预畸变后的数字角频率而不是原来的模拟频率。一个完整的带通模拟转数字示例如下% 假设模拟带通原型为bt, at目标数字采样率 Fs 8000 Hz Fs 8000; % 由于双线性变换存在频率弯曲需预畸变 Wp1 2*Fs*tan(2*pi*f_low/(2*Fs)); % 下边界预畸变 Wp2 2*Fs*tan(2*pi*f_high/(2*Fs)); % 上边界预畸变 [bd, ad] bilinear(bt, at, Fs, [Wp1, Wp2]);这段代码的最后一行的第四个参数是预畸变后的边缘频率必须依次给出。如果不做预畸变直接使用设计出来的数字滤波器截止频率会和设计目标偏差很大尤其在采样率不高的情况下差距肉眼可见。我自己最早做项目时栽过这个跟头后面才发现官方文档里这个参数的意义被很多教程忽略了。不过如果你的应用不是特别苛刻直接使用数字域的butter、ellip等函数按数字指标设计会更省事因为MATLAB自动帮你处理了频率变换和预畸变。模拟设计更大的价值在于理解原理以及当你确实需要先设计模拟电路比如有源滤波器硬件实现时使用。3. 数字滤波器设计核心环节IIR与FIR两线并行3.1 IIR数字滤波器用低阶实现陡峭过渡带的秘诀数字IIR滤波器是从模拟原型出发设计的本质上就是把模拟滤波器的传输函数映射到数字域。MATLAB里最常用的设计命令是butter、cheby1、cheby2、ellip它们支持直接设计低通、高通、带通和带阻数字滤波器区别在于频率参数要做归一化处理。这里的归一化是指相对于奈奎斯特频率采样率的一半的比值范围是0到1。直接设计带通IIR滤波器的代码非常简单这是我最常用的一个模板% 数字带通IIR滤波器采样率Fs2000Hz通带20Hz-200Hz Fs 2000; Wp [20 200] / (Fs/2); % 通带归一化边界 Ws [10 400] / (Fs/2); % 阻带归一化边界 Rp 0.5; % 通带波纹dB Rs 40; % 阻带衰减dB % 用cheby1自动估算阶数并设计 [n, Wn] cheb1ord(Wp, Ws, Rp, Rs); [b, a] cheby1(n, Rp, Wn, bandpass);cheb1ord返回的n是根据所有指标综合出的最小阶数它保证了你的滤波器既满足衰减要求又不会过于冗余。当你系统对阶数特别敏感时这个自动阶数估算的价值非常大。我用这套逻辑对比过巴特沃斯和椭圆同样的指标巴特沃斯需要8阶切比雪夫I型需要6阶椭圆只需要4阶。在FPGA上实现时每一阶IIR都对应一组乘法器和延时单元少两阶的差距非常可观。不过要注意IIR滤波器存在一个天然缺陷——相位非线性。阶数越高、过渡带越陡相位扭曲就越明显。如果你的应用对波形形状敏感比如要求滤波后的波形不能有太大失真那就要考虑filtfilt这种零相位处理方法但filtfilt只能离线处理在线实时流处理时无法使用。这一点需要在系统设计时就做出取舍不能等滤波器设计完再纠结。3.2 FIR数字滤波器设计线性相位与任意幅频响应当相位成为硬指标时FIR滤波器是绕不开的选择。FIR自带线性相位特性这意味着它对所有频率成分的延迟都一样波形不会因为滤波而产生相位失真这对振动信号、生物电信号这类需要保形分析的场景特别重要。FIR的代价是需要更高的阶数才能达到和IIR相同的幅频陡峭度。MATLAB里设计FIR有两个主流函数fir1是标准窗函数法适合设计低通、高通、带通和带阻这样的标准频率响应firls和remez使用优化算法适合设计任意形状的幅频特性其中remez等纹波设计法能让误差在频带上均匀分布在同样阶数下获得更窄的过渡带。fir1的用法非常固定比如设计一个截止频率100Hz的10阶低通FIR滤波器Fs 1000; fc 100; b fir1(10, fc/(Fs/2)); % 10阶FIR等效于11个抽头默认使用的是Hamming窗。如果你想获得更陡的过渡带或更深的阻带衰减可以换成Kaiser窗并使用kaiserord来估算阶数。Kaiser窗的beta参数直接控制主瓣宽度和旁瓣高度的权衡这是一个很实用的参数工程中用得非常频繁。如果要对幅频响应做更精细的定制比如设计一个波峰在某个频段特别凸出的滤波器就得使用firls或remez。以remez为例它的频率向量和幅度向量可以精确刻画出你想让滤波器呈现的形状非常适合做音频均衡器或补偿滤波器% 设计一个60Hz到200Hz通带增益1300Hz以上增益为0的带通滤波器 f [0 50 60 200 300 400]/(Fs/2); a [0 0 1 1 0 0]; b remez(100, f, a);这里f是归一化频率点a是对应的期望幅度。100阶的remez设计在过渡带表现很好但需要注意的是remez在过渡带边界会产生等波纹震荡设计时要留出足够的过渡带宽度否则可能出现预期之外的通带波纹。3.3 IIR与FIR的量化对比到底该选哪一个我把这两种数字滤波器放在一张表格里对比帮你做决断对比维度IIR滤波器FIR滤波器阶数与资源消耗阶数低运算量小适合实时系统阶数高运算量大对硬件资源要求高相位特性非线性阶数越高越明显严格线性相位适合保形应用稳定性若极点设计不当可能不稳定全零点结构天生稳定设计灵活性只能设计标准频响可设计任意幅频响应数值敏感度系数量化后容易产生偏差系数量化误差影响小我的设计习惯是如果系统允许离线处理且信号形状重要优先用FIR如果系统对实时性和资源消耗敏感且对相位要求不高果断选IIR。很多项目里还会出现预滤波用IIR粗处理、后处理用FIR精修的组合这个思路在工程实践中验证过很多次效果相当稳。4. 实操过程实录一套完整的滤波器设计流程复盘4.1 从需求指标到代码落地一个带通滤波器的完整搭建为了让整个流程更直观我完整复盘一下项目里那个2kHz采样率下的数字带通滤波器设计过程。第一步明确采样率。采样率决定了可处理的最高频率也就是奈奎斯特频率1kHz。目标通带是20Hz到200Hz这属于低频信号按说采样率可以更低但考虑到前级传感器输出信号的上升沿需要被如实还原我把采样率定在了2kHz留出足够裕量。第二步确定滤波器类型和设计参数。由于是在FPGA里做实时流处理资源有限我选择了IIR椭圆滤波器。椭圆滤波器在相同阶数下过渡带最窄实际操作上这是满足40dB阻带衰减指标里阶数最低的方案。设计代码Fs 2000; Wp [20 200]/(Fs/2); Ws [10 400]/(Fs/2); Rp 0.5; Rs 40; [n, Wn] ellipord(Wp, Ws, Rp, Rs); [b, a] ellip(n, Rp, Rs, Wn, bandpass);运行之后得到n4这是一个很舒服的结果四阶IIR带通在FPGA上几乎可以忽略运算开销。然后我立刻用freqz做验证[H, f] freqz(b, a, 4096, Fs); figure; subplot(2,1,1); plot(f, 20*log10(abs(H))); grid on; xlabel(频率 (Hz)); ylabel(幅度 (dB)); subplot(2,1,2); plot(f, unwrap(angle(H))*180/pi); grid on; xlabel(频率 (Hz)); ylabel(相位 (度));从幅频响应图上可以清楚看到通带覆盖了20到200Hz阻带衰减在10Hz和400Hz处都超过了40dB。使用grid on之后可以很方便地读取曲线上的具体数值验证是否达标。第三步做零极点检查。IIR滤波器的稳定性完全取决于极点是否都在单位圆内。我习惯用zplane快速查看zplane(b, a);只要所有极点落在圆内滤波器就是稳定的。这一步看似简单但很多人在系数切换或量化后经常忽略它结果系统运行时偶发振荡排查起来极其痛苦。4.2 滤波器的效果验证freqz、fvtool、filter与filtfilt的配合设计出系数只是第一步滤波器是否真的能在系统中正常表现需要用信号实测。我当时的验证流程分三步。先构造验证信号。一个混合了目标频段和无用频段的信号是必要的而且比直接拿真实数据更容易定位问题。我用三段正弦波叠加20Hz、100Hz是想要的800Hz是不想要的干扰全部叠在一起t (0:4095)/Fs; sig sin(2*pi*20*t) 0.8*sin(2*pi*100*t) 2*sin(2*pi*800*t);再调用filter做流式滤波观察时域波形和频谱。接着用filtfilt做一次零相位滤波对比看看相位影响有多大。两步做完可以非常直观地感受到两种处理方式的区别filter出来的波形在时间轴上有明显延迟而filtfilt输出的波形和原始信号在特征点位置上是对齐的。造成这个区别的原因在于filtfilt把信号正向滤波一遍、反向再滤波一遍两次延迟方向相反互相抵消了。不过filtfilt有副作用就是信号的首尾段会有幅度畸变因为反向滤波时信号开头变成了结尾边界效应被放大了。处理长信号时这个影响可以忽略处理短片段比如一个脉冲时就要格外小心。最后是fvtool这个工具可以把幅频、相频、群延迟、零极点全部画在一个窗口里适合做综合评估。我每次设计完滤波器都会看一眼群延迟曲线如果通带内群延迟波动过大说明相位失真比较严重需要考虑换FIR或者增加通带宽度作为代价。4.3 滤波器性能的额外优化实现效率与数值稳定性仿真通过不等于真正能用滤波器落实到固件或代码里时还有两个坑需要提前踩平。第一是系数格式问题。MATLAB默认算出的浮点系数在移植到定点环境比如单片机、FPGA时会因为量化产生明显偏差。一个典型的解决路径是把浮点系数转成定点格式比如用MATLAB的fixed-point designer或者手动量化然后重新画freqz观察量化后的指标是否还能满足要求。我遇到过四阶IIR浮点转16位定点后阻带衰减从40dB退化到33dB的情况这在实际应用中可能就是不可接受的只能靠提高量化位数或改用级联型结构来缓解。第二是滤波器结构问题。直接型Direct Form II的IIR实现方便但高阶时系数动态范围大量化误差容易放大。改进手段是把高阶滤波器分解为多个二阶节级联每一节都单独实现、单独检查。MATLAB里用tf2sos可以把传递函数转为二阶节矩阵sos tf2sos(b, a);这个操作在数字滤波器工程化落地时几乎必用。二阶节级联不仅数值稳定性好而且方便直接映射到硬件上。我的习惯是IIR滤波器只要阶数超过4一律用sos形式传递和存储。5. 常见问题与排查技巧实录这些都是真金白银踩出来的5.1 指标差一点不达标优先调整阶数与过渡带设计结果和指标差一点是常态比如阻带衰减只有38dB要求却是40dB。这时候不要盲目提高阶数因为每多一阶都会带来额外的相位延迟和资源开销。更好的办法是适当放宽阻带起始频率或通带截止频率让过渡带变宽就能在不加阶数的情况下提高阻带衰减。原理很简单相同阶数下过渡带越宽滤波器越容易实现陡峭的幅频切换阻带衰减自然更深。如果你已经在使用自动阶数估算函数直接加严一档指标再看阶数变化往往能得到意想不到的效果。比如cheb1ord算出来是6阶你把通带波纹从0.5dB放宽到1dB、阻带衰减降到35dB阶数可能直接降到4阶。这些都是可以接受的设计权衡关键在于想清楚系统对哪些指标更敏感。5.2 频率归一化错误新手申请造成的重灾区我见过最多的错误就是把实际频率值直接塞进数字滤波器设计函数而没有除以奈奎斯特频率。结果得到的滤波器响应频率和预期完全不对。MATLAB的数字滤波器设计函数凡是输入参数里带有frequency的默认都是归一化频率范围在[0,1]其中1对应Fs/2。如果不做归一化程序不会报错但结果必定错误。排查方法非常简单设计完马上用freqz看横轴频率值确认通带位置是否和设计目标一致。如果你觉得每次手动归一化太麻烦可以自己封装一个函数把实际频率转成归一化频率内部调用butter或ellip。封装不仅能统一逻辑还能减少低级错误的发生概率。5.3 滤波器输出出现异常瞬态或振荡从零极点与量化误差找答案滤波后的信号前几个点出现较大的跳变这是正常的瞬态响应属于滤波器初始化问题用filter的初始状态参数zi可以减小这种跳变。更严重的问题——输出持续振荡——往往出自两个原因一是设计出来的滤波器极点落在单位圆外滤波器不稳定二是定点量化后极点偏移导致失稳。排查步骤依次是zplane看图、检查系数、检查量化格式。我记得有一次排查一个看起来毫无问题的滤波器最后发现是浮点转定点时用了round函数而没有做饱和处理导致某个极点推出了单位圆。这个教训告诉我定点化之后必须重新验一遍稳定性不能只盯着浮点仿真结果。5.4 大作业和毕设场景下的滤波器设计建议如果你是在做课程作业或毕业设计MATLAB滤波器设计可以是一个很好切入的题目因为它的成果可视化强、验证清晰、扩展性强。最简单的版本是设计一个IIR低通滤波器和FIR高通滤波器用合成信号验证滤波效果进阶版本可以加入真实信号、做定点量化、对比不同窗函数效果再进一步可以做GUI界面把滤波器的参数调节、波形显示都放进去。很多学校虽然要求不同但核心考核点依然离不开阶数选择、指标验证和结果分析这三件事。只要这三个方面讲清楚分数基本不会差。6. 一些关于设计习惯的零散但关键的经验到这儿从模拟到数字、从IIR到FIR的滤波器设计路径已经完整过了一遍。最后想分享几个零散的小点都是我在实际操作中反复验证过的心得。第一滤波器的设计过程一定要脚本化。不要每次都在命令行里敲个没完把设计流程封装成函数参数化输入输出这才是能复用的工程做法。尤其在做对比实验时脚本化让你只改一行参数就能重新生成一组滤波器。第二验证永远比设计重要。滤波器设计函数本身很少出错但你对需求的理解、归一化的处理、参数的取舍都可能出问题。所以每完成一个滤波器的第一步永远是画图观察幅频响应、相频响应、零极点图一个都不能少。第三涉及到数字域实现时提前想好定点量化问题。MATLAB仿真做得很完美不等于硬件实现也能这么完美。建议在设计的最后阶段专门花时间模拟定点环境下的滤波效果而不是等移植到硬件之后再发现问题。按这套流程走下来无论你是在做模拟滤波器、数字IIR还是数字FIR都能在一个下午之内设计、验证、优化完一轮。把代码和参数换成你自己的应用场景这套方法论依然成立。最后再提醒一句任何滤波器设计都要以真实的信号应用场景为准指标是死的系统是活的灵活调整才是正经事。