在嵌入式音频、软件无线电、传感器信号调理这些场景里FIR 滤波器几乎是绕不开的一环而只要动手设计 FIR 滤波器第一个撞上的岔路口就是窗函数怎么选。滤波器设计软件里那个下拉框——矩形、汉宁、汉明、布莱克曼、凯泽——看起来只是个选项实际上它直接决定了过渡带有多少宽、阻带能压多深、阶数要堆到多少、后期定点化会不会翻车。这篇内容不讲教科书式的推导专讲在真实项目里怎么从一句“阻带 80dB、过渡带 2kHz”的需求一步步反推出该用哪种窗、长度取多少、参数怎么定以及哪些坑是手册上不会写、只有自己调过才知道的。无论你是刚开始接触数字滤波的学生还是已经在做音频或射频链路的工程师下面这些手算流程和参数陷阱都能直接拿去用。1. 窗函数在 FIR 滤波器里到底修的是什么1.1 理想低通截断后的振铃是绕不过去的理想低通的冲激响应是 sinc 函数它在时间轴上是无限长的向两侧缓慢衰减。要把它变成可以写进代码、跑进 FPGA 的有限长系数最直觉的做法就是直接从中间截一段出来两侧全部置零这在数学上等价于乘了一个矩形窗。问题也随之而来时域的突然截断在频域就是与矩形窗的频谱做卷积把原本干净的砖墙响应抹成了一片带波纹的形状通带内出现起伏阻带内出现一长串旁瓣第一旁瓣只比主瓣低 13dB 左右。这就是吉布斯现象。它的本质不是程序写错了也不是采样率不够而是“有限长度”这个约束本身带来的代价。工程上没法消除它只能压制它窗函数就是用来压制这件事的工具。所谓选窗本质上是在挑一条衰减曲线让 sinc 两端平滑地衰减到零而不是一刀切下去振铃就会被削下去。我见过不少刚上手的人看到阻带压不下去第一反应是加大阶数实际上阶数只影响过渡带的陡峭程度对阻带最低能到多深几乎没帮助。阻带能压多深主要由窗的形状决定这个认知上的区分非常关键后面所有选型判断都建立在这上面。1.2 主瓣管过渡带旁瓣管阻带任何窗函数的频谱都可以拆成两部分来看中间那个又高又胖的主瓣以及两侧一排排逐渐降低的旁瓣。这个拆法不是学术游戏它直接对应两个工程指标。主瓣的宽度决定了卷积之后响应被抹开的范围换句话说就是过渡带有多宽。主瓣越窄从通带到阻带的切换越干脆。矩形窗的主瓣最窄所以同样阶数下它的过渡带最陡这也是它唯一的优势。旁瓣的高度决定了阻带能被压到多低。旁瓣越高泄漏进阻带的能量越多阻带衰减就越差。矩形窗的第一旁瓣只有 -13dB 的相对高度设计出来的滤波器阻带衰减也就卡在 -21dB 左右这个数字在绝大多数场景里都不够看。汉宁窗把旁瓣压到 -31dB汉明窗压到 -43dB布莱克曼窗直接压到 -57dB代价是主瓣被撑宽过渡带跟着变宽。这两者是一对天然的矛盾压旁瓣必然撑主瓣撑主瓣必然拖宽过渡带拖宽过渡带就必须加阶数来补。所有的窗函数设计都是在这条权衡曲线上选一个自己舒服的点没有哪个窗能同时做到过渡带最窄和阻带最深。谁要是跟你说某个窗“全面最优”那基本是不了解这条曲线。1.3 选窗之前先摆清四个指标很多选型讨论之所以吵不出结果是因为大家手里的指标不一样。动手之前先把这四个数字白纸黑字写下来选型会变得非常机械。第一是过渡带宽度也就是从通带边缘到阻带边缘差多少赫兹。这个数字决定阶数是最贵的一个指标。过渡带收窄一半阶数基本翻倍运算量和存储都跟着翻。第二是阻带最小衰减单位 dB。这个数字决定窗型80dB 以下汉明窗勉强再深就得上布莱克曼或凯泽。第三是通带允许的纹波。窗法有个先天特点通带纹波和阻带衰减近似对称阻带压到 80dB通带纹波也就大致在 0.01dB 这个量级。如果你的指标是通带纹波 0.001dB、阻带只要 40dB那窗法就浪费了应该换等波纹设计。第四是采样率和截止频率的位置。所有公式都是归一化频率下的实际算的时候必须换算换算错了整个设计就白做。把这四条写在纸上再去看窗函数对照表选择范围通常只剩一两个。我个人的习惯是先按阻带要求筛窗口类型再按过渡带要求算长度最后仿真验证不达标就加 5% 到 10% 的长度重来比反复纠结理论公式省事得多。2. 六大常用窗横向评测把数字摊开看2.1 一张对照表看清主瓣、旁瓣与过渡带不同资料给出的过渡带系数常常对不上原因是它们对“过渡带”的定义不同有的按主瓣宽度算有的按 -6dB 到 -60dB 的实际跌落宽度算有的按满足特定阻带指标的等效宽度算。下面这张表我用的是最常见的归一化表述过渡带以 Δω kπ/N 给出N 为滤波器长度这样换算到实际频率时最不容易出错。窗类型主瓣宽度双侧最高旁瓣可达阻带衰减过渡带系数 k适用场景矩形窗4π/N-13dB约 -21dB1.8频谱分析、瞬态检测滤波器里极少用巴特利特窗8π/N-27dB约 -25dB6.1教学演示实际几乎被汉宁替代汉宁窗8π/N-31dB约 -44dB3.1通用低通、抗混叠、频谱分析汉明窗8π/N-43dB约 -53dB3.3语音通信、对第一旁瓣敏感的场合布莱克曼窗12π/N-57dB约 -74dB5.5高阻带要求、测量仪器前端凯泽窗可调可调可调至 -100dB 以上由 β 决定指标明确的工程实现首选看这张表要抓两个要点。第一过渡带系数和阻带衰减是绑在一起动的从汉宁到布莱克曼阻带深了 30dB过渡带系数从 3.1 涨到 5.5等于同样过渡带要求下阶数要多花 77%。第二汉宁和汉明的过渡带系数几乎一样但阻带差了 9dB这 9dB 是白捡的所以只要不是对远端旁瓣有特殊忌讳汉明在多数场景里比汉宁更划算。2.2 矩形窗唯一能给出最陡过渡带的窗矩形窗在所有窗里是异类。它的主瓣最窄同样长度下过渡带最陡理论上没有任何一个窗能在这项指标上赢过它。但它的第一旁瓣高达 -13dB设计出来的滤波器阻带衰减卡在 -21dB 附近实际能用的场合少得可怜。那它什么时候还有价值我遇到过两类。一类是本身阻带要求就极低的场景比如某些只做粗略平滑的预处理20dB 衰减足够另一类是频谱分析用的窗这里矩形窗反而是首选因为它的频率分辨率最高能把靠得很近的两个谱线分开代价是泄漏大需要用别的手段补偿。还有一个容易忽略的场合当滤波器长度已经由硬件资源锁死而过渡带又实在做不到时有人会退而求其次用矩形窗换陡度然后在后面串一级模拟滤波或者用自适应算法补阻带。这种做法我不推荐因为 -21dB 的阻带意味着高频分量会原封不动地漏进来数字域后面怎么补都补不干净。真要做这种妥协至少也要用汉宁把阻带先拉到 -44dB 再谈其他。顺带提一句巴特利特窗也就是三角形窗。它的旁瓣衰减比矩形好但主瓣宽了一倍阻带只改善 4dB性价比极差现在基本只在教学里出现工程实现里直接跳过就行。2.3 汉宁窗与汉明窗一个字的差别在阻带这两个窗名字只差一个字公式也只差一个系数但频域表现差别不小值得单独拎出来讲。汉宁窗的时域表达式是 0.5 - 0.5cos(2πn/(N-1))两端严格归零频谱上旁瓣以 18dB/倍频程的速度快速衰减第一旁瓣 -31dB远端旁瓣掉得很快。这个特性让它在抗宽带噪声时表现很好因为远端泄漏少。汉明窗的表达式是 0.54 - 0.46cos(2πn/(N-1))两端不归零停在 0.08 的位置。这个不归零的设计让它的第一旁瓣被压到 -43dB但代价是远端旁瓣不再快速衰减形成一个大约 -43dB 的平台一直延伸到很远。所以选型逻辑很清晰干扰主要集中在靠近过渡带的位置用汉明干扰是宽带的、遍布高频用汉宁。语音通信里常见的情况是邻近信道干扰靠得比较近汉明更合适而抗混叠滤波器面对的是整个高频段的折叠能量汉宁的快速远端衰减反而更有优势。我自己的默认选择是汉明理由是 -53dB 的阻带在实际项目里刚好卡在很多指标的及格线上而且过渡带系数只比汉宁大 0.2几乎不花额外阶数。如果指标要求 60dB 以上两个都不够直接跳到布莱克曼或凯泽不要在汉宁汉明之间反复试。2.4 布莱克曼和平顶深阻带与幅度精度的两种取向布莱克曼窗由三个余弦项叠加而成主瓣宽度达到 12π/N是汉宁的 1.5 倍但阻带能压到 -74dB 左右。这个数字让它成为高阻带要求场景的默认答案测量仪器前端、高精度 ADC 的抗混叠、需要压制强邻道干扰的接收链路基本都在这个量级。布莱克曼的另一个好处是旁瓣衰减速度快远端泄漏很低适合处理那种“近端干扰不强但远端杂散很多”的场合。缺点是过渡带太宽同样指标下阶数比汉明多六七成运算量吃紧的嵌入式平台要掂量一下。平顶窗则是另一个方向的极端。它的通带极其平坦幅度误差可以压到 0.01dB 以内专门用于幅度测量比如校准信号源、功率测量。但它的主瓣极宽过渡带是布莱克曼的两三倍用在滤波器设计里基本等于自杀。我见过有人把平顶窗误用到低通设计上结果两千阶都做不出想要的过渡带排查了半天才发现窗选错了。记住一句话平顶窗是给频谱分析用的不是给滤波用的。介于两者之间还有 Blackman-Harris、Nuttall 这些四项、五项窗阻带能到 -90dB 以上过渡带系数在 7 到 9 之间。它们的存在意义是当你需要 90dB 阻带但又不想用凯泽窗那套数值求解时直接调库里的现成窗更省事。实际项目里我用得不多因为凯泽窗在同等指标下过渡带更窄参数也更直观。2.5 凯泽窗与切比雪夫窗把旁瓣电平变成可调旋钮前面那些窗都是固定形状选定了阻带就定了。凯泽窗不一样它有一个参数 β可以连续调节主瓣和旁瓣的分配比例等于把“选窗”变成了“调参”。β 越大旁瓣压得越低主瓣越宽β 等于 0 时退化成矩形窗。这个连续可调的特性让凯泽窗成为工程实现的万能解指标说 62dB就调 β 到刚好 62dB 多一点不多花一点阶数也不留太多余量。配合经验公式从指标到阶数和 β 可以一步算出来不需要试错。切比雪夫窗又叫 Dolph-Chebyshev 窗走的是另一条路它让所有旁瓣高度完全相等形成等波纹。这个特性在需要精确控制最大旁瓣电平时很有用比如某些雷达脉冲压缩场景。但对普通滤波器设计来说等波纹旁瓣并不比凯泽的递减旁瓣更有优势而且参数只有“旁瓣电平”一个过渡带的对应关系不如凯泽直观所以我用得更少。实际操作中如果项目里已经有成熟的凯泽窗设计流程就别折腾别的了如果指标里有明确的“最大旁瓣不得高于 X dB”这种表述再考虑切比雪夫。3. 从指标反推窗型与阶数的手算流程3.1 完整案例48kHz 采样率下 80dB 抗混叠滤波器拿一个真实需求走一遍完整流程。某音频采集链路采样率 48kHz要求通带 0 到 8kHz 内纹波不超过 0.01dB10kHz 以上衰减不低于 80dB。第一步确定过渡带。通带边缘 8kHz阻带起点 10kHz过渡带宽度 Δf 2kHz。归一化到采样率是 0.04167换算成角频率 Δω 2π × 2000 / 48000 0.2618 rad/sample。第二步选窗型。80dB 这个数字直接排除汉宁-44dB和汉明-53dB布莱克曼的 -74dB 也不够只剩凯泽窗或者 Blackman-Harris 这类高阶窗。选凯泽。第三步算阶数。凯泽窗的经验公式是 N ≈ (A - 8) / (2.285 × Δω)其中 A 是阻带衰减的 dB 数。代入 A 80Δω 0.2618N ≈ (80 - 8) / (2.285 × 0.2618) 72 / 0.5982 ≈ 120.4取整到 121 个系数奇数长度保证 Type I 线性相位低通设计用奇数长度最省心。阶数是 120 阶。第四步算 β。A 80 大于 50用公式 β 0.1102 × (A - 8.7) 0.1102 × 71.3 ≈ 7.86。第五步定截止频率。这里有个关键细节截止频率要放在过渡带的正中间也就是 (8000 10000) / 2 9000Hz。原因在于窗法设计的低通响应在截止频率处的幅度大约是 -6dB如果直接把截止频率设在 8kHz那么 8kHz 处的衰减就已经是 -6dB 了通带边缘指标立刻失败。第六步生成并验证。用 Python 的 scipy 一行就能出系数import numpy as np from scipy import signal fs 48000 numtaps 121 fc 9000 beta 7.86 taps signal.firwin(numtaps, fc, window(kaiser, beta), fsfs, scaleTrue)这段代码跑出来的系数实测阻带通常在 -80dB 到 -85dB 之间通带偏差在 1e-4 量级指标达标。如果实测差一点点把长度从 121 加到 127 或 129 重跑一般就够了。3.2 窄过渡带的代价1kHz 到 1.2kHz 要多少阶换个更极端的场景感受一下过渡带宽度对阶数的影响有多剧烈。同样是 48kHz 采样率通带 0 到 1kHz阻带从 1.2kHz 开始要求衰减 60dB。过渡带 Δf 200Hz归一化后是 0.004167Δω 0.02618。A 60落在 21 到 50 之上用第一个 β 公式。先算阶数N ≈ (60 - 8) / (2.285 × 0.02618) 52 / 0.05982 ≈ 869取 871 个系数。β 0.1102 × (60 - 8.7) 5.65。从 121 个系数到 871 个翻了三倍多只因为过渡带从 2kHz 收到了 200Hz。这就是为什么工程师总在跟系统那边争过渡带指标过渡带每收窄一半乘法器资源基本翻倍。如果这个滤波器要跑在 48kHz 实时链路上871 阶意味着每秒约 4200 万次乘加对小型 MCU 已经不轻松得上 DSP 或者降采样后再滤。所以实际项目里有一个常用套路先降采样再滤波。如果信号带宽只有 1kHz先在 48kHz 下用几十阶的粗滤波器滤一遍抽取到 4.8kHz再在低采样率下做窄过渡带滤波。同样的绝对过渡带宽度在低采样率下归一化后宽了十倍阶数直接降到十分之一。这个思路比死磕窗函数选型有用得多。3.3 凯泽窗 β 的三种取值方法与我的习惯β 的取值有三种常用途径适用场合不太一样。第一种是公式法就是上面用的两个分段公式。A 大于 50 时用 0.1102(A - 8.7)A 在 21 到 50 之间用 0.5842(A - 21)^0.4 0.07886(A - 21)A 小于 21 时 β 取 0。这个公式误差在 0.1dB 量级工程上完全够用。第二种是查表法经典文献里给了一张 β 和阻带衰减的对照表21dB 对应 030dB 对应 1.540dB 对应 2.550dB 对应 3.460dB 对应 4.570dB 对应 5.780dB 对应 7.090dB 对应 8.5100dB 对应 10.0。查表比公式快但两点之间还是要插值实际精度不如公式。第三种是仿真迭代法先按公式估一个 β跑一次频响看实测阻带差多少就微调。这个方法最笨但最可靠尤其是当你的指标定义比较特殊比如阻带从某一段开始算、或者要求某一段相对衰减时公式的假设可能不成立只能靠仿真兜底。我的习惯是先用公式算然后故意把 β 加大 3% 到 5%宁可阻带深一点、过渡带宽一点点也不要在量产批次的边角料上卡线。因为实际器件的频率响应会随温度、时钟抖动变化设计时刚好卡在 80dB实测掉到 78dB 是很常见的事留点余量比返工便宜得多。3.4 窗法做不到的事独立控制通带与阻带这一点必须单独说清楚因为它决定了什么时候该放弃窗函数改用等波纹设计。窗法设计出来的滤波器通带纹波和阻带衰减是近似对称的都是 δ 的量级。阻带压到 80dB意味着 δ ≈ 1e-4通带纹波也就在 ±0.0001 左右。这个特性对大多数场景是好事通带白送一个高平坦度。但如果你的指标是通带纹波 0.01dB、阻带只要 45dB窗法就亏了。0.01dB 对应 δ ≈ 0.001同样的 δ 给出 60dB 阻带多出来的 15dB 全浪费在阻带上而这些余量本来可以换成更窄的过渡带。这种“通带严、阻带松”的指标用 Parks-McClellan 等波纹算法能省下 30% 以上的阶数。反过来“通带松、阻带严”的指标窗法也不划算因为通带精度被强行拉到和阻带一样高。所以判断标准很简单**通带纹波要求和阻带衰减要求相差在 10dB 以内用窗法相差超过 20dB考虑等波纹。**这个经验值不是理论推导出来的是我在几个项目里对比实测结果之后总结的比较粗糙但好用。等波纹算法也有自己的问题设计过程是迭代的某些极端指标下会不收敛通带和阻带都是等波纹某些场合比如音频听起来不如窗法的平滑滚降自然系数的解析性差出了问题不好解释。所以我的一般顺序是先试窗法不满足再上等波纹而不是一上来就用最优算法。4. 从系数到验证一条可复现的实现链路4.1 生成系数的三种写法与参数陷阱同一个滤波器用 scipy 有三种常见写法每种都有坑。第一种用signal.firwin这是最常用的taps signal.firwin(numtaps121, cutoff9000, windowhamming, fs48000)这里第一个坑是截止频率的归一化方式。老版本 scipy 用nyq参数传奈奎斯特频率新版本用fs传采样率两者混用会导致频率差一倍。如果你接手的是别人的老代码看到firwin(N, 0.1875, nyq0.5)这种写法说明它用的是归一化到奈奎斯特频率的老接口0.1875 对应的实际频率是 0.1875 × 24000 4500Hz。新版要写成firwin(N, 4500, fs48000)。这个换算错一次滤波器中心频率整个偏掉而且频响图看起来还挺正常特别难发现。第二个坑是scale参数默认是 True会把系数归一化到直流增益为 1。低通设计这没问题但如果做的是高通或者带阻直流增益本来就该是 0这个归一化会做出奇怪的结果。做高通时务必设scaleFalse自己按通带中心频率归一化。第三个坑是window参数传元组的形式window(kaiser, beta)注意 beta 是位置参数不能写成关键字。写成windowkaiser会用默认 β出来的阻带跟算好的对不上。第二种写法是直接构造窗再乘 sincn np.arange(numtaps) m n - (numtaps - 1) / 2 h_ideal np.sinc(2 * fc / fs * m) * 2 * fc / fs w np.kaiser(numtaps, beta) taps h_ideal * w这种写法透明每一步都能改适合做变体设计比如变截止频率、加窗后再做微调。缺点是容易漏掉归一化或者把 sinc 的定义搞错numpy 的 sinc 是 sin(πx)/(πx)不是 sin(x)/x代入时要乘 π。第三种是手写查表系数用于极低资源的定点平台。这种情况一般只在系数固定、代码空间紧张时用把系数存成常量数组配合移位加法实现乘法。要注意的是量化后的系数必须保持对称否则线性相位就没了。4.2 频响验证把主瓣旁瓣量出来系数生成之后必须验证不能只信公式。验证的核心是三件事通带偏差、阻带最低点、过渡带实际宽度。w, h signal.freqz(taps, worN1 16, fsfs) mag_db 20 * np.log10(np.abs(h) 1e-15) # 通带最大偏差 passband mag_db[w 8000] print(通带偏差:, np.abs(passband).max(), dB) # 阻带最强点 stopband mag_db[w 10000] print(阻带衰减:, -stopband.max(), dB) # 找 -3dB 和 -60dB 点看实际过渡带 idx_3db np.argmax(mag_db -3) idx_60db np.argmax(mag_db -60) print(实际过渡带:, w[idx_60db] - w[idx_3db], Hz)这段代码里worN取 2 的 16 次方是有讲究的。**FFT 点数不够会导致频率采样太粗阻带的尖峰可能被跳过量出来的衰减值偏乐观。**我见过有人用 512 点 FFT 验证一个 800 阶滤波器看到阻带 -90dB 就交付了实际上用细网格一测只有 -70dB。经验做法是取至少 8 倍于滤波器长度的点数再向上取到 2 的幂。还有一个细节np.argmax(mag_db -60)找的是第一个满足条件的点如果通带里就有低于 -60dB 的凹陷比如设计出错这个结果会完全不对。严谨一点的做法是先定位到通带外再在阻带区间内搜索。4.3 系数量化与线性相位对称性浮点系数直接搬到定点硬件上必须过量化这一关。这里最容易出的问题是对称性被破坏。理想情况下线性相位 FIR 的系数满足 h[n] h[N-1-n]严格对称。但浮点转定点时如果两个对称位置的系数分别做四舍五入有可能一个进位一个舍去得到一个不对称的系数对。这种不对称在频域表现为相位响应出现轻微弯曲音视频里可能表现为轻微的相位失真在通信解调里可能直接导致误码率上升。稳妥的做法是量化后强制对称q 15 # Q15 格式 taps_q np.round(taps * (1 q)).astype(np.int64) # 强制镜像对称 taps_q (taps_q taps_q[::-1]) // 2 # 再检查一遍 assert np.array_equal(taps_q, taps_q[::-1])这里用整除 2 而不是取平均后取整是为了避免再引入一次舍入。做完之后把系数还原成浮点重新跑一次频响看指标掉了多少。Q15 量化通常会让阻带衰减恶化 3 到 6dB如果原始设计只留了 2dB 余量量化后就不达标了。这就是前面强调留 5% 余量的原因。量化位宽怎么选我的经验是阻带要求 60dB 以下用 Q1260 到 80dB 用 Q1580dB 以上用 Q17 或者干脆保留浮点。Q15 理论动态范围约 96dB扣掉滤波器自身的增益余量实际能保证 80dB 左右再往上就吃紧了。4.4 群延迟补偿与定点实现的溢出检查线性相位 FIR 的群延迟是 (N-1)/2 个采样点。121 个系数对应 60 个采样点48kHz 下是 1.25ms。这个延迟在很多场景里必须补偿比如多通道同步采集几个通道的滤波器长度不一致不补偿就会出现通道间相位差又比如闭环控制系统1.25ms 的延迟可能直接影响稳定性。补偿方法很简单在数据流前面补相应数量的零或者在时间戳上做偏移。麻烦的是当滤波器长度动态变化时比如自适应滤波延迟也跟着变这时候要么固定长度要么在输出端做可变延迟线。定点实现的溢出检查是另一件常被忽略的事。滤波器输出的最大绝对值不会超过输入最大值乘以系数绝对值之和也就是 sum(|h[n]|)。低通滤波器的这个值在 1 到 1.1 之间看起来不大但中间累加过程是逐项相加的如果累加器位宽不够中间结果会溢出。计算方法输入 16 位有符号数最大值 32767累加器至少需要 16 log2(sum|h| × 2^Q) 位。以 Q15 系数、121 阶为例sum|h| × 2^15 ≈ 1.05 × 32768 ≈ 34406log2 约等于 15.07所以累加器需要 16 15 1 32 位。用 32 位累加器是安全的用 24 位就要小心得先算清楚最坏情况。还有一个坑系数的直流增益归一化之后sum(h) 等于 1但 sum(|h|) 不等于 1。对于某些窗比如旁瓣很高的sum(|h|) 可能达到 1.3 甚至更高溢出余量要按这个算不能按 1 算。5. 踩过的坑与排查速查5.1 过渡带估算偏差的三个常见来源公式算出来的阶数实测往往不够差个 10% 到 20% 是常态。原因主要有三个。第一是公式本身的定义差异。前面说过凯泽窗的 N 公式里Δω 的定义是阻带边缘减通带边缘的角频率差但有些资料里用的是主瓣宽度有些用的是 -6dB 到 -60dB 的宽度这几个定义之间能差 30%。混用不同来源的公式结果自然对不上。第二是截止频率的位置。把截止频率设在过渡带中点是理论最优但实际指标往往只约束通带边缘和阻带起点中点位置稍微偏一点实测指标就会在某一侧富余、另一侧不够。我的做法是中点定位之后分别检查两侧哪侧不够就往哪侧挪一点截止频率通常挪 5% 就能补齐。第三是指标的定义方式。有些指标说“10kHz 以上衰减 80dB”实际是指从 10kHz 开始算有些是说“10kHz 处衰减 80dB之后不限”。前者宽松后者严格按后者设计出来的滤波器要比前者长不少。跟提需求的人确认清楚这几个字能省下几百阶。5.2 通带边缘掉下去的那些情况通带边缘衰减超标是我遇到过最多的问题。典型表现是设计要求 8kHz 处衰减不超过 0.1dB实测 8kHz 处掉了 0.5dB怎么调都不行。原因通常是截止频率设错了。窗法设计的响应在截止频率处是 -6dB从截止频率往通带方向走衰减逐渐减小但减小的速度取决于过渡带的陡峭程度。过渡带越窄通带边缘离 -6dB 点越近掉得越多。解决办法有两个。一是把截止频率往阻带方向挪让通带边缘离 -6dB 点远一些代价是过渡带实际变窄需要加阶数。二是直接加阶数让整个过渡带变陡同样的频率偏移对应更小的衰减。还有一个隐蔽的情况多级滤波器级联。单级设计时每级都刚好达标级联之后通带边缘的衰减是累加的两级各掉 0.05dB加起来 0.1dB 就超标了。级联设计时每级都要留够余量我一般按总指标除以级数再打个八折来分配。5.3 多带、半带与多相结构里的窗选型不是所有 FIR 都是简单的低通。多带滤波器、半带滤波器、多相抽取滤波器窗的选择逻辑都不太一样。多带滤波器比如同时通过两个频段用窗法设计时各个带的指标是耦合的。如果两个带的阻带要求不一样窗型要按最严的那个选结果就是要求松的那个带浪费了阶数。这种情况我更倾向用等波纹设计能独立分配各带的误差权重。半带滤波器是个特例它的系数有一半是零除中心点外偶数位置全为零运算量减半非常适合 2 倍抽取。设计时通常直接用专门的半带设计函数不用窗法。但如果非要手算要注意半带滤波器对系数的对称性和零值分布有额外约束加窗之后可能破坏这些约束得手动清理。多相抽取滤波器的每个支路都是原滤波器的子集窗型选择跟原型滤波器一样但要注意各支路的系数动态范围可能差别很大定点化时不能共用一套缩放因子否则小系数支路的量化噪声会很大。稳妥做法是每个支路单独评估取最保守的那个缩放。5.4 常见问题速查表下面这张表是我这些年攒下来的排查清单遇到问题先对一遍能省不少时间。现象最可能的原因排查动作解决方式阻带只到 -21dB 左右用了矩形窗检查 window 参数是否为默认换成汉明或凯泽阻带比预期浅 10dB 以上FFT 验证点数不足把 worN 提到 2 的 16 次方以上提高验证精度后重测通带边缘衰减超标截止频率设在通带边缘检查 fc 是否在过渡带中点fc 向阻带方向偏移截止频率整体偏一倍归一化方式混用查 nyq 与 fs 参数统一到 fs 参数高通滤波器直流不为零scale 默认归一化检查 scale 参数设 scaleFalse 后手动归一化相位响应不是直线量化破坏对称性检查 h[n] 与 h[N-1-n]量化后强制镜像对称实测阻带比仿真差 5dB系数量化位宽不足核对 Q 格式与指标升到 Q15 或 Q17时域输出有直流偏移系数和不为 1 或定点溢出算 sum(h) 和累加器位宽重新归一化、加宽累加器级联后指标不达标各级误差累加单独测每级指标按级数分配余量过渡带比算的宽 20%公式定义不一致核对 Δω 的定义统一用角频率差重算表里最后一条特别值得说。不同资料给的过渡带系数差异极大我见过同一款窗在三个来源里分别标注 3.1、3.3、3.5 的。遇到这种情况不要纠结哪个对直接用仿真验证按某个系数算一个长度跑频响测实际过渡带反推出真实系数再按这个系数重算。一轮迭代就能校准到你自己的工具链上比查十份资料都快。我个人在实际操作中的体会是窗函数选型这件事理论公式只能给你一个起点真正决定成败的是“算完就测、测完再调”这个循环跑得够不够快。我现在的习惯是把整个流程写成一个脚本输入通带边缘、阻带起点、衰减要求三个数自动算阶数、生成系数、跑频响、打印实测指标不达标就自动加长重跑。这个脚本不到一百行但省下来的时间比读十篇论文都多。另外提醒一句别在项目后期才想起来优化滤波器长度那时候架构已经定死改一个参数牵动一大片前期多花半天把指标和阶数算清楚后面能少加很多班。
阅读完成 · 觉得有帮助?