你手里只有一段
第二条限制:真机器不但只能一眼一眼地看,还只能看有限长的一段。这条限制的代价可以用一个公式说完,而且这个公式简单到有点可疑——分辨率 = 1/T。它简单,但它挡住的路比大多数人以为的多得多。
两个音同时响:1000 Hz 和 1002 Hz,振幅一样。采样率 8000 Hz,你手里有 1024 个采样点(也就是 0.128 秒)。做一次变换,频谱上只看到一个峰——两个音糊成了一坨。
问:下面哪个办法能让你看到两个峰?
先把答案量出来
同一对音(1000 Hz 和 1002 Hz),采样率固定 8000 Hz,只改点数:
点数 N 记录时长 T 谱线间隔 Δf 看到几个峰 ───────────────────────────────────────────────────────── 1024 0.128 s 7.8125 Hz 1 2048 0.256 s 3.9063 Hz 1 4096 0.512 s 1.9531 Hz 1 8192 1.024 s 0.9766 Hz 2 ←── 在这里分开了 16384 2.048 s 0.4883 Hz 2 ───────────────────────────────────────────────────────── 两个音相差 2 Hz。分开的门槛出现在 T 超过 0.5 秒的时候。
规律一眼就能看出来:谱线间隔 Δf 正好是记录时长 T 的倒数。而两个音要分得开,大致需要 Δf 小于它们的间距。
频率分辨率 = 1 / 记录时长。
Δf = 1 / T (T 是你观测了多久,单位秒) 要分开相差 δ 的两个频率,需要 T ≥ 1/δ 相差 2 Hz → 至少听 0.5 秒 相差 0.1 Hz → 至少听 10 秒 相差 1 mHz → 至少听 1000 秒
请注意这个式子里没有采样率,没有点数,没有算法,没有精度。它只有一个变量:你看了多久。
为什么是「看多久」
回到第 2 章那个转盘。两个频率相差 δ,意味着它们在转盘上会慢慢拉开距离——每秒钟拉开 δ 圈。
如果你只看了 T 秒,它们一共才拉开 δ·T 圈。要是这个数远小于 1,那么在你观测的整段时间里,这两个旋转基本上是步调一致的——它们缠出来的花纹几乎重合,质心也几乎在同一个方向上。你没有任何依据说这是两个东西。
只有当 δ·T ≥ 1,也就是它们在你的窗口里至少完整地错开了一圈,你才第一次看到「它们有时同相、有时反相」这个现象——那个现象就是拍频,而拍频周期正好是 1/δ。
换个说法:要区分两个转速,你必须至少等到它们分道扬镳一次。这件事没有任何算法上的捷径,因为在那之前,「它们是两个」这个信息还没有进入你的数据。
三个不管用的办法,逐个量一遍
提高采样率(选项 A)
采样率翻倍,点数不变——那么记录时长就减半:
同样 1024 个点: fs = 8000 → T = 0.128 s → Δf = 7.8125 Hz fs = 16000 → T = 0.064 s → Δf = 15.6250 Hz ← 更差了
提高采样率买到的是更高的频率上限(奈奎斯特),不是更细的频率刻度。这两件事经常被混为一谈,因为它们在直觉上都叫「看得更清楚」。采样率决定你能看到多高的频率,观测时长决定你能分辨多近的频率。两个正交的旋钮。
补零(选项 B)
这是最诱人的一个,因为它确实会让频谱看起来更细。把 1024 个真实采样后面补 7168 个零,再做 8192 点变换:
补零前:谱线间隔 7.8125 Hz 补零后:谱线间隔 0.9766 Hz ← 密了 8 倍,看起来精细多了 峰的个数:1 ← 一个都没多 主瓣宽度(−3 dB):6.84 Hz ← 和补零前一模一样
补零让你在同一条曲线上取了更多的采样点。曲线本身一点没变——那个又宽又胖的主瓣还是那么宽那么胖,只是现在你能看清它的形状了。
补零是频域的插值,不是频域的显微镜。它有用(能让峰的位置估得更准,能让画出来的谱线不那么锯齿),但它买不到分辨率,一点都买不到。
换更好的库或更高的精度(选项 D)
第 3 章测过:64 点的正交性残留是 1.6 × 10⁻¹³。精度早就富余了十几个数量级。这里缺的不是精度,是信息——1024 个点里就是没有「这是两个音」这条信息,用一千位精度去算也变不出来。
「分辨率」这个词在频谱这里被两个不同的东西共用了,混起来会出大事:
- 谱线间隔(bin spacing)= fs/N。这是你的频谱数组里相邻两个数之间隔多少 Hz。补零可以随便把它变小。
- 真实分辨率(resolution)≈ 1/T。这是你真正能分开的最小频率差。只有观测时长能改变它。
不补零的时候这两个数正好相等(fs/N = 1/T),所以很多人以为它们是一回事。一补零就露馅了。
另外还有第三个概念要分开:频率估计精度。如果你已经知道只有一个音,那么你可以用峰值附近几根谱线做插值,把这个音的频率估计到远远好于 Δf 的程度(千分之一个格子都做得到)。「估准一个」和「分开两个」是完全不同的两件事——电网频率监测靠的是前者,所以它能在 0.1 秒内报出 50.02 Hz。
把上面那张表跑出来,顺便亲手验证补零无效:
import numpy as np
fs = 8000
def peaks(N, pad=None):
n = np.arange(N)
x = np.cos(2*np.pi*1000*n/fs) + np.cos(2*np.pi*1002*n/fs)
M = pad or N
m = np.abs(np.fft.fft(x, M))[:M//2]
lo, hi = int(990*M/fs), int(1012*M/fs)
seg = m[lo:hi]
# 数一数半高以上有几个局部极大
return sum(1 for k in range(1, len(seg)-1)
if seg[k] > seg[k-1] and seg[k] >= seg[k+1] and seg[k] > 0.5*m.max())
for N in (1024, 2048, 4096, 8192, 16384):
print(f'N={N:6d} T={N/fs:.3f}s Δf={fs/N:8.4f}Hz 峰数={peaks(N)}')
print('补零 1024 → 8192:峰数 =', peaks(1024, pad=8192)) # 1
输出就是正文那张表,最后一行是 1。把 pad=8192 改成 pad=65536 试试——谱线密到 0.12 Hz,峰数还是 1。这一步比读十遍文字管用。
python3 -c "import numpy as np;fs=8000;n=np.arange(1024);x=np.cos(2*np.pi*1000*n/fs)+np.cos(2*np.pi*1002*n/fs);print(np.abs(np.fft.fft(x,8192))[:4096].argmax()*fs/8192)"
在线跑:Google Colab(自带 numpy)。手边只有浏览器的话,第 2 章那段绕圈代码改一下频率范围也能看到同样的结论。
- 吉他调音器为什么要「等一下」。要区分 440 Hz 和 441 Hz(差 1 Hz),至少得听 1 秒。这就是为什么再好的调音 App 也不能在 0.1 秒内告诉你「偏高 1 音分」——那需要区分 0.25 Hz 的差别,也就是 4 秒。它们实际用的是「估准一个」的路子,但那要求同时只有一根弦在响。
- 雷达的两种分辨率。雷达要分开两个挨得近的目标,靠的是带宽(距离分辨率 ≈ c/2B);要分开两个速度接近的目标,靠的是照射时长(多普勒分辨率 = 1/T)。后者就是这一章。这也是为什么合成孔径雷达要飞很长一段航线——它在用时间换分辨率。
- MRI 的扫描时间。核磁共振采集的是频域数据,图像的分辨率直接由「采了多大范围的频率」决定,而采得越多花的时间越长。「屏住呼吸 20 秒」这句话背后就是这本这一章。
- 引力波探测。LIGO 要从噪声里认出一次并合事件,用的是第 16 章的匹配滤波;但要把双星系统的质量估准,靠的是信号在探测器里停留了多少个周期。这也是为什么低质量的双星(并合慢、停留久)反而能被测得更准。
- 你的监控告警。「5 分钟粒度的 QPS 曲线上看到了一个 3 分钟的尖峰」——这句话本身就有问题。5 分钟采样只能可靠地描述周期大于 10 分钟的变化,3 分钟的东西已经落在混叠区了(上一章);而要在频谱上把「每小时一次的定时任务」和「每 55 分钟一次的另一个任务」分开,你需要至少 11 小时的数据。
「补零之后频谱明显变细了,所以补零能提高分辨率。反正是免费的,多补点总没坏处。」
前半句里「变细」的是谱线间隔,不是分辨率。上面实测过:补零到 8 倍,主瓣宽度 6.84 Hz,一点没动,峰数还是 1。
而「多补点没坏处」也不完全对,它有一个真实的坏处:补零会让人看到更多的旁瓣,从而看到更多「像是峰」的东西。下一章会讲这些旁瓣是怎么来的——在补零之前它们藏在谱线的缝隙里,补零之后你把缝隙也画出来了,于是频谱上突然长出一堆小尖尖。不知道来源的话,它们看起来非常像真实的频率成分。
补零真正该用的场合有两个:一是把峰的位置估准(已经知道只有一个峰,想读出它的中心);二是凑成 2 的幂好让 FFT 跑得快(第 17 章),或者做快速卷积时避免循环折叠(第 14 章)。判据是:补零之后如果你打算「数峰」,那就用错了;如果你打算「读一个峰的位置」,那就用对了。
正确答案是 C:继续录,凑够 8192 个真实采样点(1.024 秒)。
A 「采得更密细节更多」——采样率管的是频率上限。同样 1024 个点,采样率翻倍反而让记录时长减半,Δf 从 7.8125 Hz 变差成 15.6250 Hz。这两个旋钮互相正交,很值得在心里画一张表:采样率 ↔ 能看多高;观测时长 ↔ 能分多细。 B 「谱线密 8 倍」——密的是画笔,不是眼睛。补零之后你在同一条模糊的曲线上多取了几个点,曲线没变。这是频谱分析里最普遍的一个误会,普遍到很多商业软件的默认设置就在悄悄替你补零,让你看到一条漂亮而骗人的曲线。 D 「精度更高」——第 3 章测过,这台机器的数值精度早就富余了十几个数量级(残留 1.6 × 10⁻¹³)。这里缺的不是精度,是信息:那 1024 个点里根本没有「这是两个音」这条消息。没有任何算法能算出数据里不存在的东西,这是这本书里第二次遇到这句话(第一次是上一章的混叠),后面还会遇到第三次(第 10 章)。更进一步:「只录一段」这个动作,在数学上是拿信号去乘一个矩形窗(窗内是 1,窗外是 0)。而时域相乘 = 频域卷积(第 14 章会把这条定理讲透)——所以你的频谱其实是「真实频谱」和「矩形窗的频谱」卷出来的东西。矩形窗越短,它的频谱越胖,卷出来的东西就越糊。
这一句话同时解释了这一章和下一章:主瓣的宽度决定了你能分开多近的两个频率(分辨率),旁瓣的高度决定了一个强音会污染多远的地方(泄漏)。它们是同一个东西——矩形窗的频谱——的两个部位。
这一章的一句话
频率分辨率只由观测时长决定,Δf = 1/T;采样率、点数、补零、精度、算法,一个都帮不上忙——想分开两个近邻的频率,唯一的办法是多看一会儿。
上面最后那句话已经把下一章交代了一半:你的频谱是真实频谱和窗的频谱卷出来的。那么窗的频谱长什么样?下一章直接量出来,答案很难看:同一个绝对纯净的余弦,频率正好落在格子上的时候,1 根谱线装下 99% 的能量;把频率挪半个格子,需要 41 根。多出来的那四十根不是噪声,是你剪断信号那一刀切出来的。