☰
截止频率全链路解析:从-3dB定义到模拟数字滤波器验证
2026/10/28 3:54:55 网站建设 项目流程

信号处理这行干久了,会发现一个挺有意思的现象:很多人能画出漂亮的幅频响应曲线,也能把滤波器系数算得清清楚楚,可一旦被问"你这个截止频率到底卡在哪个点上、为什么是这个数",回答就开始含糊。截止频率看着是最基础的一个参数,实际却是模拟电路、数字滤波器、采样率、阶数、相位响应几件事拧在一起的交汇点。我见过太多项目在这里栽跟头——电路焊出来了低频增益不对、代码跑起来了结果通带比预期窄一截、明明设了 100 Hz 实际测出来 70 多 Hz。这些问题追到根上,往往不是算法写错了,而是对"截止频率"这四个字的理解少了一层。

这篇内容想聊的就是这一层。从最基础的 -3dB 定义讲起,把模拟侧 RC 网络的公式推导、数字侧归一化频率和预畸变的换算关系、阶数与过渡带的权衡、以及最后怎么用实测数据验证自己设的截止频率是对的,一条线串下来。适合刚接触滤波器设计、正在做传感器信号调理或者音频处理的读者,也适合那些"会用 scipy 但说不清参数含义"的朋友。里面会给出可以直接跑的计算和代码,也会把我自己踩过的几个坑摊开讲。

1. 把"截止频率"这个词拆开:它究竟落在哪条线上

1.1 -3dB 这个数字不是随手定的

先说清楚一件事:理想低通滤波器的幅频响应是一条直角折线,通带内增益恒为 1,到了截止频率瞬间掉到 0。这种响应在数学上很干净,物理上造不出来。任何用电阻电容电感搭出来的网络,或者用有限阶差分方程实现的数字滤波器,通带到阻带之间必然有一段斜坡式过渡。既然没有"瞬间掉下去"的那个点,"截止频率"就必须人为定义在一个具体的幅度上。

工程上最通用的定义是幅频响应降到通带增益的 0.707 倍,也就是 -3.01 dB 的那个频率点。为什么是 0.707 而不是 0.5 或者 0.9?因为 0.707 对应的是半功率点。功率正比于幅度平方,0.707² ≈ 0.5,信号通过这个频率点时功率正好被砍掉一半。这个定义在电学里有明确的物理意义,也和一阶系统的极点位置天然吻合——一阶低通的传递函数是 H(s) = ω_c/(s+ω_c),把 s 换成 jω,令 ω = ω_c,模值恰好是 1/√2。换句话说,-3dB 点就是一阶系统的极点频率在频域上的投影,不是拍脑袋选的美学数字。

不过这里有个特别容易混淆的地方:不同滤波器类型、不同设计工具,对"截止频率"的默认定义并不一致。巴特沃斯的 -3dB 点确实落在标称截止频率上,因为它的幅频响应是 |H| = 1/√(1+(f/f_c)^(2n)),不管阶数 n 是多少,f = f_c 时模值恒为 1/√2。切比雪夫 I 型的定义就不一样了,它的标称截止频率指的是纹波带边缘,也就是响应第一次跌到通带纹波下限的那个点,这个点的衰减量等于纹波系数,可能是 0.5 dB、1 dB 或者 3 dB,取决于你设的纹波参数。椭圆滤波器更绕,它的截止频率通常定义为通带边缘,但阻带起点另有参数。

1.2 三种常被混着用的"截止"说法

我在帮别人看代码的时候,发现有三类表述经常被混为一谈,必须分开。

第一类是幅频 -3dB 点,就是上面说的半功率点,最通用。

第二类是阻带起始频率,这是滤波器开始提供足够衰减的位置。它和 -3dB 点之间隔着一整段过渡带,两者可能差得很远。一个 8 阶巴特沃斯低通,-3dB 点在 100 Hz,阻带起始点可能要到 200 Hz 才达到 40 dB 衰减。如果你把这两个当成一个东西,设计出来的滤波器通带会比预期宽或者窄很多。

第三类是数字滤波器设计工具里的归一化参数。这个最坑,后面单独开一节讲,因为它是实际项目里翻车率最高的地方。

下面这张表把常见的几种滤波器在标称截止频率处的实际衰减列出来,做设计选型的时候可以对着看:

滤波器类型标称截止频率处的衰减说明
巴特沃斯(任意阶)-3.01 dB最平坦通带,定义最直观
切比雪夫 I 型等于设定的通带纹波纹波 1 dB 时该点衰减 1 dB
切比雪夫 II 型通常按阻带边缘定义通带平坦,阻带有纹波
贝塞尔-3.01 dB通带幅度略逊,群时延最平坦
一阶 RC 无源-3.01 dB但后级负载会拉偏这个值
FIR 窗函数法(firwin)约 -6 dB理想矩形截断,半幅点

最后一行需要特别强调。用窗函数法设计 FIR 低通时,如果直接把 cutoff 设成想要的 -3dB 频率,实测会发现通带边缘比预期低不少,因为窗函数法逼近的是理想矩形,它的半幅点(-6 dB)才落在 cutoff 上。想得到 -3dB 点,得把 cutoff 往下调一点点,或者改用等波纹设计(remez)并直接控制通带边缘。

1.3 相位和群时延:被忽略的另一半

大部分人调截止频率只看幅频曲线,这是不够的。截止频率附近恰恰是相位变化最剧烈的地方,群时延在这里会出现明显起伏。对于音频、控制环路、生理信号这类对波形保真度敏感的场景,截止频率设得太贴近有用信号,即使幅频看起来还过得去,波形也会被相位失真搞得面目全非。

举一个具体的例子。某项目做电机电流采样,PWM 频率 20 kHz,采样率 40 kHz,为了压住开关噪声,在 ADC 前面加了一级二阶低通,截止频率定在 2 kHz。幅频上看,2 kHz 以内平坦,20 kHz 处衰减约 40 dB,看起来完美。但实际跑起来电流环震荡,查了半天发现是 2 kHz 处的群时延已经接近 100 μs,而电流环的带宽要求是 1.5 kHz。后来把截止频率抬到 5 kHz,噪声靠数字侧再做一级陷波解决,问题才消失。这个教训是:选截止频率不光要看"信号在不在通带内",还要看"这个频率上的相位延迟环路吃不吃得消"。

提示:评估截止频率是否合适,至少要同时看幅频响应、相频响应和群时延三条曲线。只看一条的决策,后期返工概率很高。

2. 模拟侧:从一阶 RC 到高阶网络,数值是怎么算出来的

2.1 一阶 RC 的 f_c = 1/(2πRC) 从哪来

这个公式几乎所有教材都有,但推导过程值得复述一遍,因为它解释了"为什么是 2π"这个很多人背下来却说不清的东西。

一阶 RC 低通的电路很简单:输入经过电阻 R,节点对地接电容 C,输出取自电容两端。把电容看成阻抗 1/(jωC),整个网络就是一个分压器,传递函数写成:

H(jω) = (1/(jωC)) / (R + 1/(jωC)) = 1 / (1 + jωRC)

模值是 |H| = 1 / √(1 + (ωRC)²)。要让这个值等于 1/√2,就得让 ωRC = 1,也就是 ω = 1/(RC)。注意这里的 ω 是角频率,单位是 rad/s。换成我们平时说的频率 f(单位 Hz),关系是 ω = 2πf,代入就得到:

f_c = 1 / (2πRC)

那个 2π 就是这么来的,它来自角频率和普通频率的换算,不是什么神秘的常数。

实际算一个:R = 1 kΩ,C = 100 nF。

f_c = 1 / (2 × 3.14159 × 1000 × 100×10⁻⁹) = 1 / (6.2832 × 10⁻⁴) ≈ 1591.5 Hz

如果反过来,想要 1 kHz 的截止频率,用 1 kΩ 电阻,需要 C = 1/(2π × 1000 × 1000) ≈ 159 nF,取标准值 150 nF 得到约 1061 Hz,取 220 nF 得到约 723 Hz。这就是为什么设计的时候经常要在标准值和目标频率之间做取舍。

高通的情况对称,把 R 和 C 位置对调,传递函数变成 H = jωRC/(1+jωRC),模值同样是 1/√(1+(1/ωRC)²),令 ωRC = 1 得到同样的 f_c = 1/(2πRC)。所以同一个 RC 组合,低通和高通的截止频率数值是一样的,只是斜率方向不同。

2.2 二阶之后,截止频率不再等于某个元件值

一阶的公式漂亮,但它有个硬伤:过渡带太缓。一阶低通的阻带衰减速率是 -20 dB/十倍频程,也就是说从截止频率往上走十倍,才衰减 20 dB。想要 40 dB 的衰减,得往上走一百倍频率。这个速率在很多场景下完全不够用。

于是就有了二阶、四阶、八阶。问题是,一旦阶数上去,截止频率和元件值的关系就不再是一对一的简单公式了。以最经典的二阶压控电压源(Sallen-Key)低通为例,它的传递函数是:

H(s) = K / (s²·R1·R2·C1·C2 + s·(R1·C2 + R2·C2 + R1·C2·(1-K)) + 1)

自然频率 ω_n = 1/√(R1·R2·C1·C2),品质因数 Q 则由电阻比值决定。而 -3dB 截止频率 f_c 和自然频率 f_n 的关系是:

f_c = f_n × √(1 - 1/(2Q²) + √((1 - 1/(2Q²))² + 1))

这个式子看着复杂,实际用起来可以记几个关键点。当 Q = 0.7071(巴特沃斯响应)时,f_c 恰好等于 f_n,这也是巴特沃斯最省心的原因。当 Q 大于 0.7071,f_c 略大于 f_n,通带会出现抬升(峰值);当 Q 小于 0.7071,f_c 小于 f_n,通带会提前下滑。

这也是为什么很多人照着网上的电路图焊了一个二阶低通,测出来的截止频率和计算值对不上。因为网上那些图可能用了 Q = 1 甚至更高的参数来换取更陡的过渡带,而你没注意到这个差别。

2.3 元件容差、寄生参数与实测偏差

理论算完只是开始,真正焊到板子上,偏差来源有一堆。

电容容差是最主要的一项。普通瓷片电容常见 X7R、X5R 材质,标称容差 ±10%,实际还会随直流偏压和温度漂移。给它加个 5 V 直流偏压,容量掉 20% 都不稀奇。我做过一个测试,同一批 100 nF 的 0805 瓷片电容,在 0 V 偏压下实测 96~104 nF,加上 10 V 偏压后掉到 78~85 nF。对应到截止频率上,理论 1591 Hz 的电路可能实际跑到 1900 Hz 以上。要做得准,要么换 C0G/NP0 材质(容差 ±5%,几乎没有电压系数,但容量做不大),要么换薄膜电容。

电阻的容差通常好一些,1% 精度很便宜,但要注意阻值大了以后寄生电容开始起作用。一个 1 MΩ 的电阻,几 pF 的寄生电容在几百 kHz 就开始影响响应了。

运放本身的带宽也是限制。用一颗增益带宽积只有 1 MHz 的运放做 100 kHz 的低通,运放自身的滚降会叠加在滤波器响应上,实测截止频率会比计算值低。经验上,运放的增益带宽积至少要是截止频率的 10 倍以上,20 倍更保险。

还有一个特别隐蔽的坑:测试方法本身会改变结果。用万用表测交流幅度,表的内阻和输入电容会并到电路里;用示波器探头,10× 探头是 10 MΩ 并联约 10 pF,1× 探头是 1 MΩ 并联约 100 pF。测一个 100 kΩ、100 pF 的 RC 网络(f_c ≈ 15.9 kHz),挂上 1× 探头,等于在电容上又并了 100 pF,截止频率直接掉一半。这时候你测出来的"实测值"其实是探头造成的,不是电路的问题。

提示:测量高阻节点的频率响应,一律用 10× 探头,并且要先把探头本身的电容算进去做修正。低阻节点(几百欧以下)可以放宽。

3. 数字侧:归一化截止频率是最容易翻车的地方

3.1 归一化到底是相对谁归一化

模拟电路里说截止频率,单位是 Hz,没有歧义。数字滤波器不一样,因为采样率的存在,所有频率都得先映射到一个无量纲的区间里。

数字频率的定义是 ω = 2πf/f_s,单位是 rad/sample,取值范围 0 到 π。它对应的物理频率范围是 0 到 f_s/2,也就是 0 到奈奎斯特频率。为什么上限是 f_s/2 而不是 f_s?因为采样之后,高于 f_s/2 的频率会和低于 f_s/2 的频率混叠在一起,无法区分,所以 0 到 f_s/2 是唯一有意义的区间。

很多设计工具(包括 scipy 的 signal 模块)支持两种传参方式:

  • 给 fs 参数:截止频率直接填 Hz,工具内部自己换算。
  • 不给 fs 参数:截止频率填归一化值,范围 0 到 1,这里的 1 对应的是奈奎斯特频率 f_s/2,不是采样率 f_s。

第二种方式是最容易出错的。我见过不少人以为归一化的 1 对应采样率,结果设了个 0.1,实际期望的是 0.1 × f_s,真实效果却是 0.1 × f_s/2 = 0.05 × f_s,截止频率只有预期的一半。

举个具体数字:采样率 1000 Hz,想要 100 Hz 的低通。正确写法是 Wn = 100/(1000/2) = 0.2。如果写成 100/1000 = 0.1,那实际设计的截止频率就是 50 Hz,通带窄了一半。这个错误在代码里不报错、不告警,只是默默地让结果不对,特别难查。

3.2 双线性变换的频率预畸变

数字滤波器通常的设计流程是:先设计一个模拟原型滤波器(在 s 域),再用某种映射把它搬到 z 域。最常用的映射是双线性变换,公式是:

s = (2/T) × (1 - z⁻¹) / (1 + z⁻¹)

这个映射有个副作用:它把模拟域的整条虚轴(0 到 ∞)压缩到了 z 域单位圆的上半圈(0 到 π),而且不是线性压缩,是畸变的。具体来说,模拟频率 Ω 和数字频率 ω 的关系是:

Ω = (2/T) × tan(ω/2)

在低频段,tan(ω/2) ≈ ω/2,所以 Ω ≈ ω/T = 2πf,基本是线性的。但频率越高,tan 的弯曲越明显。到 ω = π/2 时(即 f = f_s/4),tan(π/4) = 1,而线性近似给出的值是 π/4 ≈ 0.785,误差已经 27% 了。

这意味着,如果你直接按 s = jω 把模拟原型的截止频率设成 2π×f_c,再套双线性变换,得到的数字滤波器在 f_c 处的响应并不是 -3dB,而是偏移了。

解决办法叫频率预畸变(prewarping):在设计模拟原型之前,先把目标数字频率按 ω = 2×arctan(ΩT/2) 的逆关系换算成一个"虚拟的"模拟频率,这样经过双线性变换的畸变之后,刚好落回想要的位置。工程上常用的等效写法是:

f_prewarped = (f_s/π) × tan(π × f_c / f_s)

好消息是,scipy 的iirfilter、butter、cheby1这些函数,只要传了fs参数(或者直接用数字域的 Wn),内部已经自动做了预畸变,用户不需要自己算。坏消息是,如果你自己手搓双线性变换,或者从别处抄来一段没做预畸变的代码,这个偏差就得自己扛。0.1 × f_s 这种偏低的截止频率下,偏差可能只有 1%~2%,但在 0.4 × f_s 这种偏高位置,偏差能到 20% 以上。

3.3 一个能跑通全流程的实例

下面这段代码把设计、验证、画图整条链路走一遍,用的是 4 阶巴特沃斯低通,采样率 1000 Hz,目标截止频率 150 Hz。

import numpy as np from scipy import signal import matplotlib.pyplot as plt fs = 1000.0 # 采样率 Hz fc = 150.0 # 目标截止频率 Hz order = 4 # 方式一:传 fs,Wn 用 Hz b, a = signal.butter(order, fc, btype='low', fs=fs) # 方式二:不传 fs,Wn 归一化到奈奎斯特频率 # wn_norm = fc / (fs / 2) # = 0.3 # b, a = signal.butter(order, wn_norm, btype='low') # 计算频率响应 w, h = signal.freqz(b, a, worN=8192, fs=fs) mag_db = 20 * np.log10(np.abs(h)) # 找 -3dB 点 idx = np.argmin(np.abs(mag_db + 3.0103)) print(f"设计截止频率: {fc} Hz") print(f"实测 -3dB 点: {w[idx]:.2f} Hz") # 检查通带内是否有异常抬升 print(f"100 Hz 处增益: {mag_db[np.argmin(np.abs(w-100))]:.3f} dB") print(f"300 Hz 处增益: {mag_db[np.argmin(np.abs(w-300))]:.3f} dB")

跑出来的结果,实测 -3dB 点应该在 150 Hz 附近,误差在 0.5 Hz 以内。这里有个细节值得注意:freqz的worN参数设成 8192,是为了让频率分辨率足够细,否则用默认的 512 点,在 150 Hz 附近的采样点可能正好错过真正的 -3dB 位置,导致读数有 2~3 Hz 的偏差。这种"测量误差"经常被误当成"设计误差",浪费很多时间去调参数。

再补一个对比:如果不做预畸变会怎样。取同样的 fs 和 fc,但采样率改成 2000 Hz,目标截止频率抬到 800 Hz(即 0.4 × f_s,接近奈奎斯特),这时候预畸变的影响就很明显了。用 scipy 的signal.bilinear手动做一次不做预畸变的变换,会看到实测 -3dB 点跑到 900 Hz 以上,偏差超过 12%。

4. 阶数、过渡带与截止频率之间的三角关系

4.1 过渡带宽度反过来决定阶数

很多人设计滤波器的顺序是:先定截止频率,再随便选个阶数,跑一下看效果。这个顺序其实是反的,正确的逻辑是:截止频率和阻带起始频率一起决定了过渡带宽度,而过渡带宽度直接决定了需要多少阶。

巴特沃斯的阶数估算公式是:

n ≥ log₁₀[(10^(A_s/10) - 1) / (10^(A_p/10) - 1)] / [2 × log₁₀(ω_s/ω_p)]

其中 A_p 是通带允许的最大衰减(dB),A_s 是阻带需要达到的最小衰减(dB),ω_p 是通带边缘,ω_s 是阻带边缘。

算一个具体的:要求通带 100 Hz 内衰减不超过 1 dB,200 Hz 以上衰减至少 40 dB。

代入:分子是 log₁₀[(10⁴ - 1)/(10^0.1 - 1)] = log₁₀[9999/0.2589] = log₁₀(38622) ≈ 4.587。分母是 2 × log₁₀(2) = 0.602。n ≥ 7.62,向上取整得 8 阶。

如果把阻带要求放宽到 30 dB,分子变成 log₁₀[(1000-1)/0.2589] = log₁₀(3859) ≈ 3.586,n ≥ 5.96,6 阶就够。

如果把通带边缘从 100 Hz 拉到 150 Hz(过渡带从 100 Hz 缩到 50 Hz),分母变成 2 × log₁₀(200/150) = 2 × 0.1249 = 0.2499,n ≥ 18.4,得 19 阶。

这组数字说明一个很硬的道理:过渡带越窄,阶数需求爆炸式增长。从 100 Hz 过渡带缩到 50 Hz,阶数从 8 涨到 19,翻了一倍多。而在实际工程里,阶数每增加一阶,计算量、系数量化误差、定点实现的溢出风险都在增加。所以设计时的第一反应不该是"我要多陡的滤波器",而是"我的过渡带到底能容忍多宽"。

4.2 截止频率贴着奈奎斯特频率会发生什么

数字滤波器有个天然禁区:截止频率不能超过 f_s/2,而且越靠近它,各种问题越集中。

第一是系数精度问题。截止频率靠近奈奎斯特时,滤波器的极点会挤到单位圆靠近 z = -1 的位置,系数的动态范围变得极大。用双精度浮点还算能撑住,一旦转成 16 位定点,系数量化误差会让实际响应严重偏离设计值,甚至可能让极点跑到单位圆外,滤波器直接发散。

第二是预畸变放大效应。前面说过,双线性变换在接近奈奎斯特的位置畸变最严重。虽然 scipy 会自动预畸变,但如果截止频率设到 0.45 × f_s,预畸变后的"虚拟模拟频率"已经跑到无穷远附近了,设计出来的滤波器形状会变得很奇怪。

第三是数值稳定性。高阶 IIR 滤波器通常用二阶节级联(SOS)形式实现,而不是直接形式的 b/a 系数,就是为了缓解这个问题。用signal.butter拿到 b、a 之后,正常的做法是:

sos = signal.butter(order, fc, btype='low', fs=fs, output='sos') y = signal.sosfilt(sos, x) # 单向滤波 y0 = signal.sosfiltfilt(sos, x) # 零相位滤波

output='sos'这个参数,只要阶数超过 4,就该加上。用直接形式的 a 系数做 8 阶滤波,在高频端出现数值噪声几乎是必然的。

经验上,截止频率建议控制在 0.4 × f_s 以内,极端情况不超过 0.45 × f_s。真的需要更靠近奈奎斯特的截止频率,说明采样率选低了,应该先考虑提高采样率,而不是硬压滤波器。

4.3 抗混叠滤波器的截止频率与采样率配比

抗混叠是截止频率最经典的应用场景,也是配比关系最讲究的地方。

理论上,只要采样率大于信号最高频率的两倍就能无失真重建(奈奎斯特采样定理)。但实际上,没有任何低通滤波器能在 f_s/2 处突然切断,所以高于 f_s/2 的成分必然会有一部分漏进来,混叠到低频。这就要求抗混叠滤波器的阻带起始频率要低于 f_s/2,留出的余量取决于滤波器的过渡带宽度。

一个常用的配置是:信号最高频率 f_max,采样率取 f_s = 2.5 × f_max 到 4 × f_max,抗混叠滤波器的 -3dB 点设在 f_max 略高处,阻带起始点设在 f_s/2。

举个数字:信号带宽 1 kHz。

  • 采样率取 2.5 kHz,奈奎斯特频率 1250 Hz,过渡带只有 250 Hz,从 1000 Hz 到 1250 Hz 要衰减 60 dB 以上,需要非常高的阶数(巴特沃斯约 24 阶),不现实。
  • 采样率取 5 kHz,奈奎斯特 2500 Hz,过渡带 1500 Hz,从 1000 Hz 到 2500 Hz 衰减 60 dB,巴特沃斯约 6 阶,可行。
  • 采样率取 10 kHz,过渡带 4000 Hz,3 阶就够了。

这就是为什么实际系统里采样率经常远高于理论最小值。多出来的倍数不是浪费,是给抗混叠滤波器留的过渡带空间。

还有一点容易忽略:ADC 前面通常有两级滤波,一级是模拟 RC 或无源 LC,负责粗滤,把最强的干扰先压下去;一级是有源或数字滤波,负责精滤。模拟那级的截止频率不能设得太高,否则强干扰信号可能在进入 ADC 之前就把运放推饱和了,后面的数字滤波再精准也救不回来。这种情况在工业现场特别常见,几十伏的工频干扰串进来,运放早就削顶了。

5. 怎么验证你设的截止频率是真的

5.1 扫频法:最直接也最可靠

设计完、焊完、代码写完,最后一步永远是实测。扫频是最基础的方法:给系统输入一系列不同频率的正弦信号,记录每个频率下的输出幅度,画出来找 -3dB 点。

手工做的话,从 0.1 × f_c 扫到 10 × f_c,每个十倍频程取 20 个点以上(也就是对数均匀分布),能画出足够平滑的曲线。测量的时候有两个细节要注意:

一是每个频点要等系统响应稳定后再读数。滤波器有建立时间,一阶系统的时间常数是 RC,理论上要等 5 个时间常数才能到 99%。对于 f_c = 1 kHz 的一阶 RC,时间常数约 159 μs,等 1 ms 足够。但对于 Q 值高的高阶滤波器,建立时间会明显拉长,特别是接近截止频率附近,振铃可能持续很久。

二是输入幅度要控制。在截止频率附近,滤波器内部节点的电压可能远高于输入或输出,尤其是高 Q 的 Sallen-Key 结构,中间节点的电压可能达到输入的 Q 倍。输入给 1 V,中间节点可能摆动到 5 V,运放如果供电只有 5 V,直接削顶,测出来的曲线就是错的。

用代码做扫频验证,可以这样:

import numpy as np from scipy import signal fs = 48000.0 fc = 2000.0 sos = signal.butter(4, fc, btype='low', fs=fs, output='sos') freqs = np.logspace(1, 4.5, 200) # 10 Hz 到约 31.6 kHz results = [] for f in freqs: t = np.arange(0, 0.2, 1/fs) x = np.sin(2 * np.pi * f * t) y = signal.sosfilt(sos, x) # 取后 20% 的数据,避开启动瞬态 seg = y[int(0.8*len(y)):] amp = np.sqrt(2) * np.std(seg) results.append(amp) results = np.array(results) mag_db = 20 * np.log10(results / results[freqs < fc*0.1].mean()) idx = np.argmin(np.abs(mag_db + 3.0103)) print(f"扫频实测 -3dB: {freqs[idx]:.1f} Hz")

这段代码里,用后 20% 的数据而不是全部,是为了避开滤波器的启动瞬态;用标准差乘 √2 而不是直接取最大值,是为了抗噪,在有干扰的情况下更稳。

5.2 白噪声加功率谱:一次测全频段

扫频法准,但慢。如果只是想快速看个大概,白噪声激励加 FFT 分析效率高得多。

方法是:输入一段白噪声(幅度要控制,别让系统进非线性区),采集输出,算输入输出的互功率谱除以输入的自功率谱,得到频率响应。这在scipy.signal里有现成的函数signal.csd和signal.welch。

f, Pxy = signal.csd(x_in, y_out, fs=fs, nperseg=4096) f, Pxx = signal.welch(x_in, fs=fs, nperseg=4096) H = Pxy / Pxx mag_db = 20 * np.log10(np.abs(H))

用互谱而不是直接算输出的功率谱,好处是能排除输入信号本身不平坦带来的影响,也能抑制与输入不相关的噪声。

这个方法有个陷阱:nperseg决定了频率分辨率,分辨率是 fs/nperseg。fs = 48000,nperseg = 4096,分辨率约 11.7 Hz。要准确定位 2 kHz 附近的 -3dB 点,11.7 Hz 的分辨率还行;但如果截止频率只有 50 Hz,这个分辨率就太粗了,得把 nperseg 加大到 65536 甚至更多,或者降低采样率。频率分辨率和平均次数是一对矛盾,nperseg 大了,能平均的段数就少了,谱估计的方差变大。实操中需要根据目标截止频率和可用数据长度折中。

5.3 阶跃响应里藏着的额外信息

幅频曲线只反映幅度,阶跃响应能同时暴露幅度和相位的问题,有些毛病在幅频上根本看不出来。

给系统一个阶跃输入,看输出的上升时间、过冲量和振铃。一阶系统没有过冲,10%~90% 上升时间约等于 2.2 × RC,也就是 2.2/(2πf_c) ≈ 0.35/f_c。反推过来,如果测出上升时间是 350 μs,那 f_c 大约是 1 kHz。

二阶及以上的系统会有过冲和振铃,过冲量直接对应 Q 值:Q = 0.707 时过冲约 4.3%,Q = 1 时约 16%,Q = 2 时约 44%。振铃频率约等于自然频率 f_n。这几个数字很好用——如果你设计的巴特沃斯滤波器(Q = 0.707)实测过冲到了 15%,说明实际 Q 值偏高,很可能元件容差把阻尼比拉低了,顺带截止频率也会偏低。

零相位滤波(sosfiltfilt)在这种情况下要特别小心。它把正向和反向各滤一遍,幅频响应是单向滤波的平方。这意味着原来 -3dB 的点(模值 0.707),平方之后变成 0.5,也就是 -6dB。真正的 -3dB 点会往低频方向移。对 n 阶巴特沃斯,滤波两次之后的 -3dB 点约在原截止频率的 0.4142^(1/(2n)) 倍:

阶数filtfilt 后 -3dB 点与原截止频率之比
20.816
40.896
60.930
80.947

4 阶巴特沃斯设 100 Hz,做完 filtfilt 实际 -3dB 点只有约 90 Hz。做离线数据处理(不需要实时性)时,如果对截止频率有精确要求,这个偏移必须补偿,处理方法是在设计时把 cutoff 除以对应的系数。

6. 不同场景下截止频率的取值逻辑

6.1 传感器信号调理:卡在信号带宽和噪声之间

传感器前端的低通截止频率,本质上是"放多少有用信号"和"放多少噪声进来"之间的取舍。

以应变片测量为例,结构应变的变化频率通常不超过几十 Hz(除非测振动或冲击),但现场有 50 Hz 工频干扰、变频器的高频噪声、静电放电的瞬态。这种场景下,截止频率设 10~30 Hz 很合适,能把 50 Hz 压下去 20 dB 以上,同时保留全部有用的静态和准静态信号。

但如果测的是振动,带宽要求可能到几千 Hz,这时候 50 Hz 工频就落在通带里了,低通滤波根本解决不了,得换用陷波器或者提高共模抑制。这是选截止频率时最容易搞混的地方:低通不是万能的,它只能压掉截止频率以上的干扰,落在通带内的干扰得用别的办法。

还有一个容易被忽略的参数:传感器的输出阻抗。很多传感器(比如压电式、热电偶)输出阻抗高,直接接 RC 低通,电阻的热噪声会被放大,而且后级运放的输入偏置电流会在电阻上产生失调。这种场合要么用有源滤波(运放做缓冲),要么把 RC 的 R 值压小、C 值加大,但 C 加大了又会影响响应速度。

6.2 音频分频:交叉频率不是随便选的

音箱分频器是截止频率应用最直观的地方。两分频音箱,低音单元和高音单元各有一个通带,交叉点就是两个滤波器的截止频率重合处。

常见的交叉频率是 2~3 kHz,这个数字不是随口定的,它取决于两个单元各自的频率响应特性。低音单元在 2 kHz 以上开始出现分割振动(振膜不再整体运动,而是局部弯曲),失真急剧上升;高音单元在 1.5 kHz 以下受限于自身谐振频率,承受不了大功率。交叉点必须落在两个单元都能正常工作的重叠区间里。

分频器的阶数直接决定了交叉点附近的相位关系和叠加效果。一阶分频(-6 dB/oct)两个单元在交叉点处的相位差是 90°,叠加后会出现 +3 dB 的凸起或者抵消,取决于极性接法。二阶分频(-12 dB/oct)相位差 180°,需要把高音反相接才能平坦。四阶分频(-24 dB/oct)相位差 360°,同相接。这些细节决定了分频点附近的频响是否平坦,也是 DIY 音箱最常翻车的地方——很多人买了成品分频器直接接上去,忘了看相位,结果交叉点附近凹了一大块。

6.3 生理信号:极低频截止的讲究

心电、脑电这类生理信号,频率范围很低,但截止频率选起来反而更讲究。

心电信号的有效带宽通常说 0.05~150 Hz,但诊断用途和监护用途的要求不一样。监护场景(看心率、看节律)用 0.5~40 Hz 就够,这样能有效去掉基线漂移和肌电干扰;诊断场景(看 ST 段形态)需要 0.05 Hz 的低频截止,因为 ST 段的缓慢变化包含了重要信息,切高了会失真。

0.05 Hz 的截止频率意味着什么?意味着高通滤波器的时间常数是 1/(2π × 0.05) ≈ 3.18 秒。这个时间常数下,滤波器的建立时间要十几秒,而且对元件的容差极其敏感。更麻烦的是,这么低的截止频率用模拟电路实现,电容值要大到几百微法,只能用钽电容或者电解电容,漏电流和温漂都会带来问题。所以现代设备基本都是数字化之后做软件滤波,但软件滤波同样要面对定点运算下极低频系数精度不够的问题,通常要用二阶节级联加高精度系数。

脑电的情况更极端,感兴趣的频段(比如 α 波 8~13 Hz)本身就很窄,滤波器的过渡带要做得非常陡,但又不能引入相位失真(否则会影响波形分析)。这种场景下,前向后向零相位滤波几乎是标配,代价就是要接受前面说的截止频率偏移,或者用等波纹 FIR 并在设计时补偿。

7. 几个我自己踩过的坑

7.1 把 rad/s 和 Hz 混了

这个是新手坑,但栽进去的人真不少,包括工作几年的老手在赶工的时候。

signal.butter传 Wn 的时候,如果给了 fs,单位是 Hz;没给 fs,Wn 是归一化到奈奎斯特的比值。这两种都对。但如果你自己推导模拟原型,用 s 域公式算,很容易把 ω = 2πf 里的 2π 漏掉,或者多用一次。

我遇到过一次:某项目要从模拟原型迁移到数字,代码里设了Wn = 2 * np.pi * fc / fs,看着像模像样,实际上多乘了个 2π,导致截止频率只有预期的 1/6.28。查了两天才发现,因为幅频曲线形状是对的,只是整体往低频平移了,第一眼看不出来。

防这种错的办法很简单:设计完之后一定跑一次freqz,把 -3dB 点算出来打印,跟目标值对一下。多写三行代码,省两天调试。

7.2 滤波器设计对了,但数据预处理把它毁了

还有个更隐蔽的:滤波器本身没问题,但输入数据的预处理引入了偏差。

做过一个振动分析的项目,传感器信号里有个很大的直流偏置,预处理第一步是去均值。去均值用的是整段数据的平均值,看起来没问题。但后面接了 0.5 Hz 的高通,高通启动时的瞬态响应在头几秒内非常剧烈,振铃幅度比信号本身还大。后来改成先做高通、再单独处理前几秒的瞬态(用反射填充或者直接丢弃),问题才解决。

类似的还有归一化。有人习惯先把数据归一化到 ±1,再送进滤波器。如果归一化用的是整段数据的最大值,而这段数据里恰好有一个尖峰,那么整个信号的幅度都被压得很小,滤波后信噪比下降。正确的做法是先滤波、后归一化,或者用稳健的统计量(比如 99 百分位)做归一化。

7.3 定点实现里系数量化把截止频率拉偏了

浮点上跑得好好的滤波器,转到定点 MCU 上出问题,这个太常见了。

根本原因是 IIR 滤波器的极点位置对系数极其敏感,尤其当截止频率相对采样率很低的时候。比如 fs = 10 kHz,fc = 10 Hz,归一化频率只有 0.002,极点紧贴 z = 1 附近。用 Q15 定点表示系数,量化步长是 1/32768 ≈ 3e-5,这个精度相对于 0.002 的归一化频率还算够,但如果阶数高,误差会累积。

实操中的应对办法有几个:

一是用二阶节级联(SOS),每个二阶节单独量化,误差不会跨节累积。这是最有效的办法,几乎能解决 90% 的定点精度问题。

二是提高中间运算的位宽。Q15 系数配 Q31 累加器,是很多 MCU DSP 指令的标准配置。

三是把采样率降下来。如果信号带宽本来就只有几十 Hz,用 10 kHz 采样是浪费,改成 1 kHz,归一化截止频率从 0.002 变成 0.02,系数精度问题一下就缓解了。降采样通常比提高位宽更划算。

还有一个小技巧:如果滤波器系数是固定的(不需要在线调参),可以直接把系数算好烧进固件,用浮点系数配浮点运算,只要 MCU 有 FPU,性能完全够。很多 Cortex-M4 以上的芯片都有单精度 FPU,跑几个二阶节毫无压力,没必要硬上定点。

7.4 忽略群时延导致控制系统震荡

前面提过一次,这里再展开说说,因为这类问题在嵌入式控制里特别普遍。

控制环路的稳定裕度和相位延迟直接相关。滤波器的群时延在截止频率附近达到峰值,如果你的控制带宽接近滤波器的截止频率,那这部分额外的相位滞后可能直接把相位裕度吃光。

做一个粗略的估算:一阶低通在频率 f 处的相位滞后是 arctan(f/f_c)。当 f = f_c 时滞后 45°,f = 0.5f_c 时滞后 26.6°,f = 0.2f_c 时滞后 11.3°。如果控制环路的相位裕度只有 30°,而你在反馈路径上串了个截止频率等于环路带宽的低通,光这一项就吃掉 45°,环路必炸。

工程上的经验值是:反馈路径上的滤波器截止频率至少要是控制带宽的 5 倍,10 倍更保险。这意味着如果要做 1 kHz 带宽的电流环,反馈滤波的截止频率得在 5~10 kHz 以上。这时候就不得不权衡噪声抑制效果——截止频率抬高了,高频噪声压不住。解决办法通常是把滤波拆成两级:模拟侧一级截止频率较高的 RC(比如 20 kHz),数字侧一级截止频率较低但阶数高的滤波(比如 5 kHz 的 4 阶),两级串联得到陡峭的滚降,同时把群时延控制在可接受范围。

7.5 用错了滤波器类型,截止频率对但形状不对

最后说个概念性的。有次看到一段代码,要做带通滤波,用了signal.butter(4, [low, high], btype='band'),参数看着没问题。但跑出来的结果,通带中间凹下去了。

原因是butter的 band 类型,传两个截止频率时,得到的是一个 8 阶滤波器(4 阶原型映射到带通变成 8 阶),而这两个频率分别是下边带和上边带的 -3dB 点。如果 low 和 high 靠得很近(比如 100 Hz 和 120 Hz),过渡带极窄,对阶数的要求暴涨,4 阶原型根本不够,通带中间就凹了。

正确的做法是先用signal.buttord算出所需阶数:

N, Wn = signal.buttord(wp=[100, 120], ws=[80, 150], gpass=1, gstop=40, fs=1000) print(N, Wn) # 输出所需阶数和实际截止频率 sos = signal.butter(N, Wn, btype='band', fs=1000, output='sos')

buttord会根据通带、阻带指标反推最小阶数,避免手拍阶数拍小了。这个函数在窄带滤波场景下几乎是必用的,因为人的直觉很难判断"100 到 120 Hz 的带通要几阶",而公式能给出准确答案。

另外,带通滤波器的"中心频率"和"截止频率"是两个不同的量。中心频率是几何平均 √(f_low × f_high),不是算术平均 (f_low + f_high)/2。100 和 120 的几何平均是 109.5,算术平均是 110,差得不多;但如果频带更宽,比如 100 和 1000,几何平均是 316,算术平均是 550,差出一大截。用错这个会直接影响后续对滤波器特性的判断。


上面这些内容里,如果只能记住一句话,我希望是:截止频率从来不是一个孤立的数字,它是定义、阶数、采样率、实现方式共同决定的结果。算出那个数字只是开始,验证它落在预期位置、确认它在整个系统里没有带来副作用,才算真正把这件事做完。我自己现在的习惯是,任何滤波器上线之前,先跑一遍freqz打印 -3dB 点,再用一段合成信号做扫频复核,两关都过了才敢往真实数据上用。这个流程看着笨,但省下来的返工时间远比它多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询