频域入门与 DFT:从内积到离散傅里叶变换

前面几篇反复说”复杂声音 = 正弦波之和”,但一直没证明。这一篇就从零开始,把频域这扇门彻底打开:先复习三角函数和欧拉公式,再讲”内积匹配”的核心思想,然后一步步推出**离散傅里叶变换(DFT)**公式,最后用一个 N=4N=4 的例子完整手算一遍。


1. 为什么需要频域

1.1 时域看不出的信息

一段 1 秒的音频,时域波形只是一条上下跳动的曲线。你能看出它里面有 440 Hz 还是 880 Hz 吗?看不出。但人耳一秒钟就能分辨。

频域把信号按”频率”重新排列:横轴是频率,纵轴是”这个频率有多强”。就像把一道菜的食材按类别列出来:时域是”吃下去的感觉”,频域是”成分表”。

时域分解成频域

1.2 目标

给定离散信号 x[0],x[1],,x[N1]x[0], x[1], \dots, x[N-1],我们希望找到一组系数 X0,X1,,XN1X_0, X_1, \dots, X_{N-1},使得:

x[n]=1Nk=0N1Xkei2πkn/Nx[n] = \frac{1}{N}\sum_{k=0}^{N-1} X_k \, e^{i2\pi kn/N}

右边每个 ei2πkn/Ne^{i2\pi kn/N} 是一个”频率为 k/Nk/N 的复正弦”,XkX_k 是它的振幅(复数,含相位)。这个公式叫逆 DFT。我们要解决的问题是:已知左边,怎么求出 XkX_k


2. 三角函数基础

2.1 频率、角频率和周期

对离散信号,用 kk 表示”每 NN 个采样点转几圈”。频率为 k/Nk/N 的离散复指数是:

ei2πkn/N,n=0,1,,N1e^{i2\pi kn/N}, \qquad n = 0, 1, \dots, N-1

kk 每增加 NN,指数回到同一个值(因为 ei2π(n+N)n/Ne^{i2\pi(n+N)n/N}? 准确说是 ei2πk(n+N)/N=ei2πkn/Ne^{i2\pi k(n+N)/N} = e^{i2\pi kn/N},即 nnn+Nn+N 同值)。所以只需要 k=0,1,,N1k = 0, 1, \dots, N-1

2.2 正交性:频域的核心性质

两个不同频率的离散正弦在 NN 个点上”点积”为 0:

n=0N1ei2πk1n/Nei2πk2n/N={N,k1=k20,k1k2\sum_{n=0}^{N-1} e^{i2\pi k_1 n/N} \cdot \overline{e^{i2\pi k_2 n/N}} = \begin{cases} N, & k_1 = k_2 \\ 0, & k_1 \ne k_2 \end{cases}

这里 \overline{\cdot} 表示共轭(取相反虚部)。这个性质叫正交性。它意味着:不同频率的波互不干扰,就像三维空间里互相垂直的坐标轴。

这个公式是 DFT 一切推导的地基,值得多看几遍。直观理解:两个不同频率的波相乘再求和,正的部分和负的部分恰好抵消,结果归零;只有和自己相乘时全部同号,结果不为零。


3. 欧拉公式

3.1 从幂级数推导

复指数 eiθe^{i\theta} 用泰勒级数展开:

eiθ=1+iθ+(iθ)22!+(iθ)33!+e^{i\theta} = 1 + i\theta + \frac{(i\theta)^2}{2!} + \frac{(i\theta)^3}{3!} + \cdots

因为 i2=1i^2 = -1i3=ii^3 = -ii4=1i^4 = 1,整理实部虚部:

eiθ=(1θ22!+θ44!)+i(θθ33!+θ55!)e^{i\theta} = \left(1 - \frac{\theta^2}{2!} + \frac{\theta^4}{4!} - \cdots\right) + i\left(\theta - \frac{\theta^3}{3!} + \frac{\theta^5}{5!} - \cdots\right)

括号里分别是 cosθ\cos\thetasinθ\sin\theta 的泰勒展开,所以:

eiθ=cosθ+isinθ\boxed{e^{i\theta} = \cos\theta + i\sin\theta}

这就是欧拉公式。

3.2 几何意义

eiθe^{i\theta} 是单位圆上辐角为 θ\theta 的点:实部是 cosθ\cos\theta,虚部是 sinθ\sin\theta。当 θ\theta 随时间匀速增长,eiθe^{i\theta} 就在单位圆上匀速旋转。

由欧拉公式可以反解:

cosθ=eiθ+eiθ2,sinθ=eiθeiθ2i\cos\theta = \frac{e^{i\theta} + e^{-i\theta}}{2}, \qquad \sin\theta = \frac{e^{i\theta} - e^{-i\theta}}{2i}

DFT 用 ei2πkn/Ne^{-i2\pi kn/N} 做”探针”,本质就是在复平面上转着圈和信号做内积。


4. 内积匹配:DFT 的核心思想

4.1 类比:向量投影

在三维空间,想知道向量 v\vec{v}xx 方向有多少分量,就做点积 vx^\vec{v} \cdot \hat{x}。点积大说明 v\vec{v}x^\hat{x} 方向接近。

信号也一样:把 x[n]x[n] 看成一个 NN 维向量,把频率为 kk 的探针 ei2πkn/Ne^{-i2\pi kn/N} 看成另一个 NN 维向量,做”点积”:

Xk=n=0N1x[n]ei2πkn/NX_k = \sum_{n=0}^{N-1} x[n] \, e^{-i2\pi kn/N}

这就是 DFT 公式本身。

4.2 逐项解释

Xk=n=0N1x[n]ei2πkn/NX_k = \sum_{n=0}^{N-1} x[n] \cdot e^{-i2\pi kn/N}
  • x[n]x[n]:信号在第 nn 个采样点的值;
  • ei2πkn/Ne^{-i2\pi kn/N}:频率为 k/Nk/N 的探针(负号表示”匹配”时转的方向);
  • 求和:把所有采样点的乘积加起来;
  • XkX_k:复数结果。模 Xk|X_k| 是强度,辐角 argXk\arg X_k 是相位。

如果信号里恰好含有频率 k/Nk/N 的分量,乘积在所有 nn 上同向,和很大;如果没有,正交性让和接近 0。

4.3 为什么用复数

只用 cos\cos 或只用 sin\sin 都能做匹配,但会漏掉相位。复数探针同时包含余弦(实部)和正弦(虚部),一次计算就得到振幅相位。


5. 手算一个 N = 4 的例子

设信号只有 4 个点:

x=[1, 2, 0, 1]x = [1,\ 2,\ 0,\ -1]

旋转因子(N=4N=4W4=ei2π/4=iW_4 = e^{-i2\pi/4} = -i):

W40W_4^0W41W_4^1W42W_4^2W43W_4^3
11i-i1-1ii

N=4 DFT 手算

逐个计算:

X0=11+21+01+(1)1=2X_0 = 1\cdot1 + 2\cdot1 + 0\cdot1 + (-1)\cdot1 = 2

X0X_0 是所有样本之和,代表直流分量(0 Hz)。

X1=11+2(i)+0(1)+(1)i=13iX_1 = 1\cdot1 + 2\cdot(-i) + 0\cdot(-1) + (-1)\cdot i = 1 - 3i X2=11+2(1)+01+(1)(1)=0X_2 = 1\cdot1 + 2\cdot(-1) + 0\cdot1 + (-1)\cdot(-1) = 0 X3=11+2i+0(1)+(1)(i)=1+3iX_3 = 1\cdot1 + 2\cdot i + 0\cdot(-1) + (-1)\cdot(-i) = 1 + 3i

观察:

  • X1=X3=12+32=103.16|X_1| = |X_3| = \sqrt{1^2 + 3^2} = \sqrt{10} \approx 3.16:两个方向的强度相同,只是相位相反(共轭对称);
  • 对实信号,XkX_kXNkX_{N-k} 总是共轭对称,所以频谱图通常只画前半部分(0 到奈奎斯特);
  • 实际频率 fk=kfs/Nf_k = k f_s / Nk=1k=1 对应最低非零频率。

6. 逆变换:怎么还原

正向变换把信号变到频域,逆变换把频域变回时域:

x[n]=1Nk=0N1Xkei2πkn/Nx[n] = \frac{1}{N}\sum_{k=0}^{N-1} X_k \, e^{i2\pi kn/N}

验证:把 XkX_k 的表达式代进去,用正交性,最后只剩 x[n]x[n] 自己。两个变换互为逆运算,就像乘法和除法。


7. 从 DFT 到播放器的频谱

播放器里:

analyser.fftSize = 1024;             // N = 1024
analyser.getByteFrequencyData(data); // 得到 512 个 bin(0 到奈奎斯特)

每个 bin 对应:

fk=kfsNf_k = \frac{k f_s}{N}

fs=44100f_s = 44100N=1024N = 1024 时相邻 bin 间隔 43\approx 43 Hz。播放器把这些 bin 按频段分组求和(下篇会讲),再映射到地形高度。


8. 小结

  1. 频域是”成分表”,时域是”口感”;
  2. 不同频率的波正交:内积为 0;
  3. 欧拉公式 eiθ=cosθ+isinθe^{i\theta} = \cos\theta + i\sin\theta 把旋转写成紧凑形式;
  4. DFT 就是”信号 × 探针求和”的内积匹配;
  5. XkX_k 的模是强度,辐角是相位;
  6. 实信号的频谱共轭对称,通常只看一半;
  7. 逆变换用正交性还原原信号。

下一篇《FFT 与窗函数》讲怎样把 DFT 从 O(N2)O(N^2) 加速到 O(NlogN)O(N\log N),以及为什么直接截断信号会泄漏频谱、汉宁窗怎么解决。

8. 深入专题:旋转因子、矩阵形式、Parseval 与常见误区

DFT 的公式只是开始。这一节从几何、线性代数和能量守恒三个角度再深入一层,并手算更多例子。

8.1 旋转因子的几何

旋转因子 WNk=ei2πk/NW_N^k = e^{-i2\pi k/N} 在复平面上就是单位圆上的点,NN 个点把圆均分成 NN 份。

N=8 旋转因子

DFT 的探针 ei2πkn/Ne^{-i2\pi kn/N} 就是”以 kk 倍速在这些点上转圈”。k=0k=0 时探针不动(直流检测),k=N/2k=N/2 时探针每步跳半圈(奈奎斯特检测),kk 接近 NN 时其实和 kk 很小的负频率等价。

8.2 DFT 的矩阵形式

xxXX 都看成向量,DFT 可以写成矩阵乘法:

X=FNx\mathbf{X} = \mathbf{F}_N \mathbf{x}

其中矩阵元素:

(FN)k,n=ei2πkn/N=WNkn(\mathbf{F}_N)_{k,n} = e^{-i2\pi kn/N} = W_N^{kn}

例如 N=4N=4

[X0X1X2X3]=[11111i1i11111i1i][x0x1x2x3]\begin{bmatrix} X_0 \\ X_1 \\ X_2 \\ X_3 \end{bmatrix} = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & -i & -1 & i \\ 1 & -1 & 1 & -1 \\ 1 & i & -1 & -i \end{bmatrix} \begin{bmatrix} x_0 \\ x_1 \\ x_2 \\ x_3 \end{bmatrix}

这个矩阵是正交矩阵(差一个归一化因子),所以逆变换就是把矩阵取共轭再除以 NN。线性代数视角让 DFT 的很多性质(线性、可逆性、卷积定理)都变得直观。

8.3 两个典型信号的 N=4 手算

例一:直流信号 x=[1,1,1,1]x = [1,1,1,1]

X0=4,X1=X2=X3=0X_0 = 4, \qquad X_1 = X_2 = X_3 = 0

只有一个非零分量:直流。验证:任何常数信号和旋转探针的内积都为 0(除了 k=0k=0)。

例二:奈奎斯特信号 x=[1,1,1,1]x = [1,-1,1,-1]

X2=4,X0=X1=X3=0X_2 = 4, \qquad X_0 = X_1 = X_3 = 0

k=2k=2 对应频率 fs/2f_s/2,正好是能表示的最高频率:每个采样点翻转一次。

这两个例子说明:DFT 输出中哪个 kk 非零,就表示信号里含有哪个频率

8.4 Parseval 定理:时域能量 = 频域能量

DFT 是正交变换,所以能量守恒:

n=0N1x[n]2=1Nk=0N1Xk2\sum_{n=0}^{N-1} |x[n]|^2 = \frac{1}{N}\sum_{k=0}^{N-1} |X_k|^2

左边是时域能量,右边是频域能量(除以 NN 是归一化)。这个定理说明:

  • 频段能量 Eb=kBbXk2E_b = \sum_{k\in B_b}|X_k|^2 确实代表”这一频段的能量”;
  • 对信号做任何”只改频域不改能量”的处理,时域能量不变。

8.5 频率分辨率与补零

DFT 的频率分辨率:

Δf=fsN\Delta f = \frac{f_s}{N}

想要更细的 bin:

  • 增加 NN(采样更多点):分辨率 Δf\Delta f 变小;
  • 在数据末尾补零:Δf\Delta f 不变,但频谱被”插值”得更光滑(不是更清晰)。

补零的数学:补零相当于在频域做 sinc 插值,不会增加真实信息。这是初学者最容易踩的坑。

8.6 常见误区

误区一:“FFT 是 DFT 的近似,有精度损失”

FFT 是 DFT 的精确算法,结果完全一致,只是计算顺序不同、更快。

误区二:“补零能提高频率分辨率”

补零只让谱线更密,不会让两个原本重叠的峰分开。分辨率由 fs/Nf_s/N 决定,必须增加真实采样点。

误区三:”XkX_k 就是那个频率的振幅”

XkX_k 是复数的”强度”,振幅(峰值)与它相差归一化因子;而且单个 bin 的值受窗函数影响。

误区四:“DFT 只能处理周期信号”

DFT 对任何有限长序列都能算,只是”隐含周期化”——把序列当作周期的来对待,这也是窗函数话题的根源。

误区五:“频谱图每个竖线都是一个独立正弦”

对于非周期/含噪信号,bin 之间也有能量(泄漏),竖线图只是离散采样后的显示。

8.7 扩展练习

  1. 写出 N=2N=2 的 DFT 矩阵并验证 x=[a,b]x=[a,b] 的变换。
  2. 用 Parseval 定理验证 x=[1,2,0,1]x=[1,2,0,-1] 的时域能量和频域能量。
  3. 采样率 48 kHz、N=4096N=4096,频率分辨率是多少?要分辨 1 Hz 需要多少点?
  4. 为什么补零不会提高分辨率?给出 Δf\Delta fNN 的关系。

练习

  1. 证明 n=0N1ei2π(k1k2)n/N\sum_{n=0}^{N-1} e^{i2\pi(k_1-k_2)n/N}k1k2k_1 \ne k_2 时为 0(提示:等比数列求和)。
  2. x=[1,1,1,1]x = [1, 1, 1, 1] 手算 N=4 的 DFT,解释结果为什么只有一个非零分量。
  3. x=[1,1,1,1]x = [1, -1, 1, -1] 手算 DFT,它的频率是 fs/2f_s/2,想想为什么。
  4. 为什么播放器只显示 512 个 bin 而不是 1024 个?