资讯详情

Matlab fdesign滤波器设计:从规格到实现的全流程指南

📅 2026/10/2 22:33:28 | 华诺云谱 👁 阅读
Matlab fdesign滤波器设计:从规格到实现的全流程指南
做信号处理这么多年Matlab 里的滤波器设计工具我换了一茬又一茬。刚入门那会儿用的是 fir1、butter、cheby1 这些函数一个项目里滤波器种类一多函数签名记到怀疑人生。后来开始用 fdesign才算把这些零散的接口统一收拢起来。这篇文章就把我实际使用 fdesign 的体会、踩过的坑以及一整套能直接复用的流程整理出来给同样被滤波器设计折腾的朋友做个参考。fdesign 是什么简单说它是 Matlab 信号处理工具箱里一套面向对象的滤波器设计接口。你不需要再纠结 fir1 的第几个参数是截止频率、butter 的频率归一化到底怎么算只需要把设计需求——比如通带频率、阻带频率、通带纹波、阻带衰减——写成规格字符串交给 fdesign 对象再调用 design 方法就能拿到一个可以直接用的滤波器对象。整个过程统一、可读、可复用特别适合需要反复比较不同设计参数、不同设计方法的场景。这篇文章适合用过 Matlab 但还没系统接触过 fdesign 的人也适合已经在用但想优化设计流程的工程师。我会从设计思路讲起再给一个带噪信号滤波的完整案例最后把常见报错和排查方法整理成速查表。文里的代码我都实际跑过可以直接抄。1. fdesign 到底解决了什么问题1.1 用传统函数设计滤波器时我踩过的坑在 fdesign 出现之前设计滤波器最常用的就是一组以函数名命名的散装接口。做低通用 fir1(b, Wn)敲完还得算一下 Wn 是不是归一化频率换 butter 又变成 [b,a] butter(n, Wn)频率定义似乎差不多但阶数和截止频率的组合完全换个思路到了 cheby1、cheby2、ellip 更麻烦通带纹波是多少、阻带衰减怎么传每个函数的约定都不一样。这些函数单看都不复杂放在一起就容易出事。我印象最深的一次是给一个振动信号做带通先用 butter 试了几组参数后来想对比一下切比雪夫的效果结果把通带纹波的单位搞混了——cheby1 的 Rp 单位是 dB而我在传参时按线性值传了 0.1设计出来的滤波器频响直接是歪的。当时对着图看了半天最后才发现是参数理解错位。第二个坑是阶数全靠试。用传统函数设计滤波器时你给多少个阶数它就给你多少阶的结果频响满不满足要求在出图之前完全没概念。尤其用 fir1 的时候想要把过渡带做到一定陡峭程度阶数只能从 50 试到 200每次都要手动改参数重新画图耗时不说还容易陷入再加两阶看看的无效循环。等波纹设计对应的 remez 函数虽然可以按指标自动算阶数但参数顺序复杂一直没成为主流用法。第三个坑是验证和对比很别扭。传统函数设计完滤波器想看幅频响应得自己调用 freqz再手动写 subplot 对比。真要同时对比三个滤波器的效果代码就越堆越长。后来我意识到问题的根源不是某个函数难用而是缺少一层统一的设计语义层——你需要先想清楚指标再让工具去匹配算法而不是自己去记每一个函数的细节。fdesign 就是冲着这个痛点来的。1.2 fdesign 的设计思路把需求交给对象fdesign 的核心思路是把设计需求和实现算法分开。通俗地讲它就像你去饭店点菜你不用关心后厨是蒸是炒还是炸只需要把口味需求说清楚——少辣、多放葱、要快——然后等着菜上桌就行。fdesign 就是这个传菜的伙计你把通带频率、阻带频率、纹波、衰减这些规格写清楚它自己去找合适的算法把滤波器做出来做完还帮你端到面前。用代码来感受一下这个流程。设计一个常规低通滤波器需求是通带截止 4000Hz、阻带起始 5000Hz、通带纹波 1dB、阻带衰减 60dB采样率 44100Hz传统写法可能是分别查 fir1、kaiserord 或者 remez 的文档然后把参数换算好。用 fdesign 是这样写的d fdesign.lowpass(Fp,Fst,Ap,Ast, 4000, 5000, 1, 60, 44100); Hd design(d, equiripple);第一行把需求写成规格字符串 Fp,Fst,Ap,Ast后面依次跟上具体数值。第二行选择设计方法并执行设计返回的 Hd 就是可直接使用的滤波器对象。整个过程没有多余的参数换算也没有不知道阶数该填多少的烦恼——因为 Fp,Fst,Ap,Ast 这种规格属于最小阶设计design 方法会自动计算满足这些指标所需的最低阶数。这种设计方式带来两个很明显的好处。第一个是可读性规格字符串本身就是文档半年后回来看代码一眼就知道当初的指标是什么。第二个是可替换性想换一种设计算法只需要改 design 的第二个参数从 equiripple 改成 ellip指标需求完全不用动对比不同算法的效果变得非常轻松。2. fdesign 核心对象体系与标准设计流程2.1 fdesign 家族常用滤波器对象速览fdesign 不是一个孤零零的函数而是一整套对象家族。除了最常见的低通 fdesign.lowpass还有高通、带通、带阻、任意幅频响应、任意群延迟、微分器、希尔伯特变换器、分数延迟滤波器等。常用的几个我整理成了表格对象主要用途常见规格字符串示例fdesign.lowpass低通滤波Fp,Fst,Ap,Ast、N,Fc、N,F3dBfdesign.highpass高通滤波Fp,Fst,Ap,Ast、N,Fcfdesign.bandpass带通滤波Fst1,Fp1,Fp2,Fst2,Ast1,Ap,Ast2fdesign.bandstop带阻滤波Fp1,Fst1,Fst2,Fp2,Ap1,Ast,Ap2fdesign.arbmag任意幅频响应N,F,A、F,Afdesign.differentiator微分器设计N,Fp,Apfdesign.hilbert希尔伯特变换器N,TW,Ap看到这些规格字符串第一感觉可能是又多了一套规则但它们的逻辑高度统一。F 表示频率A 表示幅度Ap 表示通带纹波Ast 表示阻带衰减N 表示阶数。频率参数既可以用归一化频率0 到 11 对应奈奎斯特频率也可以在最后一个参数传入采样率 Fs 后直接用 Hz 表示灵活性很强。这套对象体系的价值在于按需选择而不是按函数选择。以前你想设计一个带通首先得想自己该用 butter 还是 cheby2现在你只需要创建 fdesign.bandpass 对象把指标写清楚设计方法反而是后面才决定的事。对象封装了指标指标和算法解耦这是 fdesign 和传统函数最大的区别。2.2 三段式设计流程从规格到滤波器fdesign 的标准用法可以拆成三步创建对象、配置选项可选、执行设计。第一步创建对象时规格字符串和参数值一起传入。比如设计一个低通滤波器通带截止 0.4归一化到奈奎斯特频率、阻带起始 0.5、通带纹波 1dB、阻带衰减 60dBd fdesign.lowpass(Fp,Fst,Ap,Ast, 0.4, 0.5, 1, 60);这里频率 0.4 和 0.5 是归一化到奈奎斯特频率后的值也就是实际频率除以 fs/2。如果你觉得归一化算起来麻烦可以直接传采样率d fdesign.lowpass(Fp,Fst,Ap,Ast, 4000, 5000, 1, 60, 44100);最后这个 44100 就是采样率此时 4000 和 5000 的单位就是 Hz。这一步的本质是把我想滤掉什么、保留什么翻译成机器能读的规格字符串。第二步是可选的配置用 designopts 查看当前设计方法支持的选项。比如你打算用 equiripple 方法设计可以先看一下它能调哪些参数opts designopts(d, equiripple);返回的 opts 是一个结构体里面包含了设计方法相关的所有可调项比如是否使用最小阶、阻带形状、阻带衰减斜率、是否要求最小相位、迭代上限等。这一步有点像点菜时跟服务员说少放盐——不调也行但调了能让结果更贴合你的特殊需求。第三步就是执行设计Hd design(d, equiripple, opts);省略 opts 时使用默认选项。如果省略第二个参数直接写 design(d)Matlab 会根据你的规格字符串自动选一个合适的设计方法。我个人的习惯是显式指定设计方法这样代码的可复现性更好别人看的时候也知道你当初是拿什么算法设计的。设计完成后返回的 Hd是滤波器系统对象dfilt 系列可以直接参与信号处理。你可以用 filter(Hd, x) 完成滤波也可以用 freqz(Hd) 画幅频相位响应它和其他滤波器对象没有区别。唯一需要注意的是Hd 的具体类型取决于设计方法和规格可能是直接型结构 dfilt.df2sos也可能是级联结构后面小节会展开讲。2.3 设计完成之后measure、info、fvtool设计出滤波器只是第一步能不能用、满不满足指标还需要验证。fdesign 配套的验证工具有三个我几乎每次都会用到。measure 是检验设计结果是否达标的直接工具measure(Hd)它会输出实际达到的通带纹波、阻带衰减、过渡带宽度、阶数等一系列参数。比如你设计指标是阻带衰减 60dBmeasure 显示 Actual Stopband Attenuation 是 60.2dB这就说明达标了如果只有 52dB说明某种原因导致设计没达到预期需要调整设计方法或参数。这个工具比画图判断准确得多毕竟人的眼睛很难从对数刻度图里精确读出一个 53dB 和 60dB 的区别。info 用来查看滤波器结构和系数信息info(Hd)它会在命令行打印出滤波器类型、阶数、是否稳定、系数格式等文本说明。当你拿到一个别人传过来的 Hd 对象想知道它到底是个什么滤波器时info 是最快的查看方式。fvtool 则提供了可视化的滤波器分析窗口fvtool(Hd);一句命令就能打开滤波器可视化工具幅频响应、相位响应、群延迟、零极点图、冲激响应全部以交互面板的形式呈现。最方便的是fvtool 支持同时传入多个滤波器对象进行对比fvtool(Hd1, Hd2, Hd3); Legend {滤波器A, 滤波器B, 滤波器C};这样几组设计方案的曲线叠在同一张图上优劣一眼就能看出来。做方案选型时这个特性非常实用。3. 实操从带噪信号到可用的滤波器3.1 第一步生成测试信号并看清频谱理论讲再多不如一个能跑的实例。下面这个案例是典型的信号处理场景一段采样率 44100Hz 的音频信号里面混着 1kHz 的有用信号、5kHz 和 12kHz 的干扰信号还叠加了白噪声目标是设计一个低通滤波器把 1kHz 保留下来把高频干扰滤掉。先构造测试信号fs 44100; % 采样率单位 Hz t (0:4095) / fs; % 约 93ms 的时域数据 x sin(2*pi*1000*t) ... % 1kHz 有用信号 0.6 * sin(2*pi*5000*t) ... % 5kHz 干扰 0.4 * sin(2*pi*12000*t) ... % 12kHz 干扰 0.1 * randn(size(t)); % 白噪声设计滤波器之前先用 FFT 看一下信号的频谱组成这样做完滤波后能有一个清晰的对照基准。频谱分析的代码我通常这样写N length(x); X fft(x); f (0:N-1) * fs / N; % 构造频率轴单位 Hz plot(f(1:N/2), abs(X(1:N/2))); xlabel(频率 (Hz)); ylabel(幅度); title(原始信号频谱);画出来之后应该在 1kHz、5kHz、12kHz 三个位置看到明显谱峰白噪声表现成一条平坦的底噪。这一步很关键它帮你确认信号的频域特征也决定了滤波器的通带和阻带到底该定在哪里。如果不看频谱直接拍脑袋定截止频率很可能是白忙一场。3.2 第二步用 fdesign 设计低通滤波器现在明确了需求保留 1kHz滤掉 5kHz 及以上的成分留一点过渡余量。我把低通指标定为通带截止 10kHz、阻带起始 12kHz、通带纹波 1dB、阻带衰减 60dB这样 1kHz 稳稳落在通带内5kHz 和 12kHz 落在阻带内指标余量充足。用 fdesign 创建设计对象并执行设计d fdesign.lowpass(Fp,Fst,Ap,Ast, 10000, 12000, 1, 60, fs); Hd design(d, equiripple);equiripple 方法就是等波纹 FIR 设计它在通带和阻带内把误差均匀分布相同阶数下能得到更陡的过渡带。设计完成后用 measure 看一下实际指标measure(Hd);实测下来等波纹设计为了满足 1dB 通带纹波和 60dB 阻带衰减需要的阶数大概在 90 阶左右这个数量级在音频处理里完全可以接受。从设计日志里会看到每段滤波器的阶数并且因为等波纹设计默认是线性相位的所以相位响应也是线性的信号通过后波形不会发生相位失真。如果你对相位不敏感更在意计算效率可以换一个 IIR 设计方法试试Hd_iir design(d, ellip);elliptic 滤波器在满足同样衰减指标时阶数会大幅下降到个位数但相位响应是非线性的通带边缘会有明显的相位变形。这就是用相位特性换计算量的取舍。在我的实际经验里音频和振动信号一般优先选线性相位的 FIR控制系统的实时性要求高时才会考虑 IIR。3.3 第三步验证设计结果并完成滤波滤波器设计好了先在频域里确认它的形状fvtool(Hd);从幅频响应图里能看到通带范围内纹波始终在 1dB 以内阻带衰减在 12kHz 之后迅速达到 60dB 以上过渡带也正好在 10kHz 到 12kHz 之间符合预期。接着对信号做实际滤波y filter(Hd, x);filter 函数对 Hd 这个滤波器对象是直接支持的不需要像传统方式那样先提取 b、a 系数再调用 filter(b, a, x)。滤波完成后同时画出滤波前后的频谱做对比Y fft(y); plot(f(1:N/2), abs(X(1:N/2)), b); hold on; plot(f(1:N/2), abs(Y(1:N/2)), r); xlabel(频率 (Hz)); ylabel(幅度); legend(滤波前, 滤波后);图上应该能看到 5kHz 和 12kHz 的谱峰被大幅压低1kHz 的谱峰几乎不受影响白噪声底也整体下降了。再对比一下时域波形figure; plot(t, x, b); hold on; plot(t, y, r); xlabel(时间 (s)); ylabel(幅度); legend(滤波前, 滤波后);滤波后的波形会明显干净很多高频毛刺基本消失只剩下平滑的 1kHz 正弦波。实际项目中我会再加一步用 snr 函数对比滤波前后的信噪比用数字说明处理的提升幅度。这个案例虽然简单但完整覆盖了从信号分析、滤波器设计到效果验证的全流程拿它当模板换成高通、带通、带阻也一样能用。3.4 进阶用一个带通案例搞定频段提取带通滤波器在工程里更常见比如从传感器的宽频信号里提取某个特定频段的特征。假设采样率 1000Hz需要提取 10Hz 到 30Hz 的有效信号同时抑制 5Hz 以下的低频漂移和 35Hz 以上的高频噪声。带通指标定义为下阻带截止 5Hz下通带边界 10Hz上通带边界 30Hz上阻带起始 35Hz下阻带衰减 40dB通带纹波 1dB上阻带衰减 40dB。用 fdesign.bandpass 创建对象时规格字符串要按顺序把所有参数写全d_bp fdesign.bandpass(Fst1,Fp1,Fp2,Fst2,Ast1,Ap,Ast2, ... 5, 10, 30, 35, 40, 1, 40, 1000); Hd_bp design(d_bp, ellip);注意这里我选了 ellip 方法因为这个带通的过渡带相对于通带宽度来说比较窄用 FIR 需要很高的阶数IIR 椭圆滤波器能以更低的阶数满足同样指标。设计完用 measure 验证一下再看一眼 fvtool确认通带 10~30Hz 内是平的阻带衰减达到设定值。实际滤完波之后你会发现低频漂移被压平了、高频噪声也被滤掉了时域波形里只剩下 10~30Hz 的有效成分。这种从宽频信号里抠出窄带信号的需求在振动分析、生物电信号处理、结构健康监测里都很常见fdesign.bandpass 一套流程就能搞定。4. 常见问题与排查技巧实录4.1 规格字符串写错与边界值问题fdesign 用多了之后我发现报错很大一部分出在规格字符串和参数数值上报错信息往往又不够直白新手看了容易懵。最常见的问题是频率参数和采样率的关系没理清。如果你创建对象时传入了 Fs那频率就用实际频率如果没传 Fs频率就按归一化频率解释。有个同学曾拿着一段代码问我为什么设计出来的低通滤波器把 1kHz 的信号也滤掉了我一看他的代码fdesign.lowpass(Fp,Fst,Ap,Ast, 0.4, 0.5, 1, 60) 没传 Fs而他的信号采样率是 44100Hz0.4 和 0.5 对应的实际频率是 8820Hz 和 11025Hz1kHz 当然被滤掉了。所以凡是设计结果不对第一步先检查频率单位是不是我理解的那个。规格参数本身的合法性也容易踩坑。通带频率必须小于阻带频率低通场景阻带起始频率不能超过奈奎斯特频率纹波 Ap 必须是正值、单位 dB衰减 Ast 也是正值。比如 Fp 和 Fst 传成相等Matlab 会直接报错。遇到这类报错把 d 对象直接打印出来看是最快的排查方式disp(d)对象会明确列出 Frequency constraints 和 Magnitude constraints一眼就能看出你设置的参数到底长什么样哪里不合理基本一目了然。4.2 设计方法不可用与收敛问题另一个高频报错是设计方法和规格不匹配。比如你写 fdesign.lowpass(N,Fc, 50, 0.4) 创建了一个固定阶数 50、截止频率 0.4 的低通对象然后想用 ellip 方法设计就会报错——因为椭圆滤波器设计需要通带纹波、阻带衰减这类指标信息单独一个 Fc 和阶数无法支撑椭圆设计。这不是 fdesign 的 bug而是不同设计方法对规格的约束条件不同。遇到这种问题我的习惯是先跑一下 designopts 看看该设计方法到底需要什么规格d fdesign.lowpass(N,Fc, 50, 0.4); designopts(d, ellip);如果设计方法不支持当前规格这里就会有提示。更稳妥的做法是换一种更适合当前规格的设计方法或者回头改规格字符串让它包含所选设计方法需要的全部信息。固定阶数加窗设计就老老实实用 window 或 kaiserwin最小阶约束设计才适合 equiripple、ellip 这些方法。等波纹设计偶尔还会遇到不收敛的问题尤其是指标定得非常苛刻时比如通带纹波 0.01dB、阻带衰减 120dB 这种不切实际的组合。等波纹本质上是迭代优化算法指标太激进会比较难收敛。遇到这种情况先检查指标是否被写错了比如把阻带衰减的 dB 值写成了线性值指标确实需要这么低时可以通过 designopts 调整迭代上限、阻带形状等参数或者放宽某个不那么关键的指标。4.3 从 fdesign 到 Simulink 和代码生成的关键坑在实际工程里滤波器设计完很少只停在 Matlab 脚本里更多是要放进 Simulink 模型或者生成 C 代码。这一步有一个很典型的坑dfilt 对象在 Simulink 和代码生成场景下并不是全部直接支持。在 Simulink 里使用有两个思路。一个是直接把设计好的 Hd 传到 DSP System Toolbox 提供的相关模块里具体做法是先在 Matlab 工作区把滤波器对象准备好然后在 Simulink 模块参数里指定这个变量名。另一个思路更通用一点先把 fdesign 设计结果转成系数再用支持代码生成的滤波器 System object 去承载。比如 FIR 滤波器可以取出 Numerator 系数b Hd.Numerator;然后把 b 传给 dsp.FIRFilter再做定点转换和代码生成。IIR 滤波器则通过 Hd.sosMatrix 和 Hd.ScaleValues 取出级联二阶节的系数矩阵和增益再传给 dsp.BiquadFilter。这样一套操作下来滤波器就从分析用对象变成了可以上工程的对象。导出系数还有一个很省事的办法在 fvtool 窗口的菜单栏里直接选择导出可以把滤波器系数保存成 MAT 文件、文本文件甚至生成 C 头文件。做嵌入式移植的时候这个功能能省不少手动抄系数的功夫也避免了人工复制造成的误差。4.4 多方案对比与设计速度技巧用 fdesign 做方案选型是非常爽的场景因为它把对比不同设计的成本降得很低。我经常写一个循环脚本一次性对比不同截止频率、不同设计方法的滤波器效果cutoffs [8000, 10000, 12000]; methods {equiripple, kaiserwin, ellip}; count 0; for c cutoffs for m methods d fdesign.lowpass(Fp,Fst,Ap,Ast, c*0.8, c, 1, 60, fs); Hd design(d, m{1}); count count 1; h(count) Hd; end end fvtool(h);半分钟不到9 个候选滤波器的频响曲线就全部叠在一张图上了。配合 measure 输出每个滤波器的实际阶数、纹波、衰减情况都能拿到选型的依据就非常充分了。不过循环里频繁调用 measure 会拖慢速度因为 measure 要做不少数值计算。我的经验是循环里只记录滤波器对象和阶数信息选型阶段用 fvtool 可视化判断确定方案后再针对候选滤波器单独跑 measure 获取精密指标。另一个小技巧是先用椭圆滤波器算一遍看看理论上最少需要多少阶才能满足幅频指标然后再决定要不要用 FIR 等波纹方案换取线性相位——有了这个参照你对 FIR 阶数的合理性就有底了不会设计出阶数离谱的滤波器还不自知。我自己用 fdesign 最大的感受是它把设计滤波器这件听起来很专业的事变成了描述需求、点一下、拿结果的三步操作。第一次用的时候总觉得绕但真正把规格字符串、设计方法、验证工具这三件套用顺之后效率提升非常明显。最后再分享一个小习惯在脚本里调试时我会把 fdesign 对象直接 disp 出来看一眼它会清楚地列出 Frequency constraints、Magnitude constraints 和当前设计方法一旦结果不对这一步能帮你快速确认是不是规格写错了。这个习惯帮我省了无数次重复设计的时间希望你也能用上。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑