傅里叶变换可视化 —— 从波形到频谱的每一步
你说得对——第 3 课把傅里叶变换一句"用 FFT 问每帧有哪些频率"带过去了,等于让你直接信了一个魔法。今天把它拆到不能再拆:零公式,两台可以动手操作的机器,每一步都看得见。
第 0 步:先认识主角——正弦波,以及"2Hz"是什么意思
正弦波是自然界最"默认"的波动形状:秋千的摆动、音叉的颤动、绳子轻甩出的涟漪,都是它。它长得像平滑的波浪,一遍遍重复同一个形状。
看懂它只需要两件事:
- 一个完整周期(循环):从平衡位置出发 → 升到波峰 → 落回平衡 → 降到波谷 → 再回到起点。这一整个来回,算"振动了一次"。
- 频率(Hz,赫兹):每秒振动多少个完整周期。"2Hz" = 这条正弦波每秒完成 2 个完整来回,仅此而已。Hz 不是高度、不是长度,是"每秒几次"。
认识主角之后,马上让三条不同 Hz 的正弦波合体——拖下面机器里的滑块,注意 9Hz 那条最密、2Hz 那条最疏:
第一步:复杂波 = 简单正弦波的叠加
傅里叶的全部出发点只有一句话:任何复杂的波,都可以由若干个简单的正弦波相加得到。就像一杯混合果汁:喝到的是混合味道,但配方是"苹果 3 勺 + 橙子 2 勺 + 芹菜 1 勺"。
下面这台机器里,三种"果汁"的量给你拖。拖动滑块,看上面三个简单波怎么叠成下面的复杂波:
玩一下你会发现:把某个滑块拖到 0,复杂波立刻"变简单"一点。所以反过来问:给定一团复杂的波,能不能查出它是哪几个简单波、各多少成分配成的?——查配方的过程,就是傅里叶变换;"配方表"就是频谱。
第二步:怎么测某个频率"藏没藏"?拿尺子对拍
思路简单到好笑:拿一个已知频率的标准正弦波当尺子,去和信号逐点相乘,再取平均:
- 尺子频率对上了:乘积的每个波峰都撞在信号波峰上 → 乘积几乎全是正的 → 平均值很大
- 尺子频率没对上:乘积一会儿正一会儿负,互相抵消 → 平均值 ≈ 0
像推秋千:顺着秋千的节奏推,越推越高(共振);胡乱推,推了也白推。
别信我,自己算一遍:8 个数字看清测量全过程
规则只有一条:信号值 × 参考波值,逐点相乘,再把所有乘积加起来除以点数(取平均)。我们只取 8 个采样点(每 0.125 秒一个,铺满 1 秒),尺子固定用 1Hz 参考波。(表里的 0.71 是 cos45° ≈ 0.707 四舍五入。)
情形 A:信号本身就是 1Hz 正弦波(和尺子对上了)
先看图:紫色的信号和灰色的尺子(参考波)上下对齐画好,8 个采样时刻各画一个点、连一条竖虚线。竖线颜色就是那一列乘积的符号——绿 = 正,红 = 负,灰 = 零:
| 时间点 | 信号值 | 参考波(1Hz) | 乘积 |
|---|---|---|---|
| 0s | 1.00 | 1.00 | 1.00 |
| 0.125s | 0.71 | 0.71 | 0.50 |
| 0.25s | 0.00 | 0.00 | 0.00 |
| 0.375s | −0.71 | −0.71 | 0.50 |
| 0.5s | −1.00 | −1.00 | 1.00 |
| 0.625s | −0.71 | −0.71 | 0.50 |
| 0.75s | 0.00 | 0.00 | 0.00 |
| 0.875s | 0.71 | 0.71 | 0.50 |
关键观察:信号和尺子是同一形状,同起同落——两个负数相乘也得正。于是8 个乘积全部 ≥ 0,一个负的都没有。合计 = 1.00+0.50+0+0.50+1.00+0.50+0+0.50 = 4.00,平均 = 4.00 ÷ 8 = 0.50。判定:平均远大于 0 → 信号里藏着 1Hz!
情形 B:信号换成 2Hz 正弦波(尺子还是那把 1Hz,没对上)
再看图:信号振得比尺子快一倍,节奏错开——盯住 t=0 和 t=0.5 两列,点的位置一上一下互换:
| 时间点 | 信号值(2Hz) | 参考波(1Hz) | 乘积 |
|---|---|---|---|
| 0s | 1.00 | 1.00 | +1.00 |
| 0.125s | 0.00 | 0.71 | 0.00 |
| 0.25s | −1.00 | 0.00 | 0.00 |
| 0.375s | 0.00 | −0.71 | 0.00 |
| 0.5s | 1.00 | −1.00 | −1.00 |
| 0.625s | 0.00 | −0.71 | 0.00 |
| 0.75s | −1.00 | 0.00 | 0.00 |
| 0.875s | 0.00 | 0.71 | 0.00 |
关键观察:信号振得比尺子快一倍,节奏错开——0 秒时同号(乘积 +1),0.5 秒时信号在峰、尺子在谷,符号相反(乘积 −1)。合计 = +1.00 + (−1.00) = 0,平均 = 0 ÷ 8 = 0.00。判定:正负精确抵消 → 信号里没有 1Hz。
下面这台探测器,做的就是你刚才手算的事——只是把"8 个点"换成"800 个点"(2 秒 × 每秒 400 点),尺子(滑块)可以随意换。信号里藏着两个频率(保密!):先手动拖滑块,看乘积曲线什么时候整条趴在正半轴、什么时候上下乱穿;再点 自动扫描,看扫过每个频率、柱子一根根长出来——那就是频谱诞生的瞬间:
找到那两个藏起来的频率了吗?(提示:信号是归一化的,柱高和成分强度成正比——哪根柱子粗壮,哪个成分就多。)
第三步:把每次测量的结果,画进频谱
单次测量你已经会算了。频谱就是把这件事对每个频率各做一遍,再把结果排成一张图,规则只有三条:
- 横轴位置 = 测试频率:按固定间隔(步长)一根根排开,如 0.5、0.75、1.0、1.25…
- 柱高 = 该频率的测量结果(2 × |平均|,正好还原成分强度)
- 没藏的频率 → 平均抵消 → 柱高 ≈ 0
拿你问的例子直接实验:信号 = 1Hz(强度 0.8)+ 2.5Hz(强度 0.5)。先在心里猜频谱长什么样,再点自动扫描:
结果:只在 1.00Hz 和 2.50Hz 两处立起柱子(约 0.62 和 0.38),其余全部贴地。读图就是读配方——
| 频谱特征 | 告诉你的事 |
|---|---|
| 柱子立在哪个频率 | 信号里藏着什么频率(1Hz、2.5Hz) |
| 柱子多高 | 该成分有多强(探测器把信号整体缩放到最大 1,所以柱高同比缩成 0.62/0.38,但相对比例仍是 0.8 : 0.5) |
| 没有柱子的地方 | 这些频率不存在(测量精确抵消为 0) |
还有一个终极验证:你一根根柱子扫出来的东西,数学家早就造好了一步到位的机器——FFT。下面把"你的自动扫描"和 np.fft.rfft 放在同一张图上对拍(信号就是上面那个 1Hz + 2.5Hz):
np.fft 发生的事,没有本质区别。加 4.5Hz 的练习跑完,两条谱会一起长出第三根柱——现在信号配方表归你管了。
看到了吗?2.5Hz 凭空消失了——它落在 2Hz 和 3Hz 两根柱子的缝里,哪次测量都没对上它,两次都精确抵消为 0。频谱只剩 1Hz 孤柱一根。所以"网格多细"决定了你能看清多细的频率差别(真实 STFT 里由帧长决定:帧越长,频率网格越细——第 5 课看 Qwen3-ASR 前端参数时会再遇到这件事)。补一句真话:真实世界的信号往往不恰好整周期,漏网的能量会摊到邻近柱上(叫"频谱泄漏"),不会像这里一样消失得这么干净,但结论不变——网格不够细,就看不清。
第四步:从频谱到语谱图,只差一个"逐帧"
现在你已经会读频谱这张"配方表"了。但语音是变化的——1 秒内音节都换了好几个。所以:
- 把波形切成小片(帧,约 25 毫秒一片,每 10 毫秒前移);
- 对每一帧单独做一遍刚才的"扫描 → 柱状图";
- 把每帧的柱状图竖起来、按时间横向排开 → 就是第 3 课看到的语谱图。
检索练习
本课主看材料
3Blue1Brown:But what is a Fourier transform? A visual introduction
它用"把波形缠绕到圆上求质心"的视角讲同一个道理,和今天"对拍取平均"互为印证(链接打不开就在 B 站/YouTube 搜这个标题,有官方中文版)。看完你会拥有两个互补的直觉,傅里叶从此不再是黑盒。