傅里叶变换入门:从时域到频域
说实话,我刚接触信号处理那会儿,最困惑的就是——为什么非要把好好的波形拆成一堆正弦波?
时域信号多直观啊,横轴是时间,纵轴是幅度,一眼就能看出信号怎么变化。但后来我遇到一个实际项目:要分析一段振动数据,找出设备故障的频率特征。时域图上密密麻麻的波形,根本看不出门道。直到我做了傅里叶变换,频谱图一出来,故障频率清清楚楚。
嗯,从那天起,我就再也不敢小看频域了。
为什么需要频域?
时域描述的是信号随时间的变化。但很多信号的本质特征,藏在频率里。
举个例子:
- 一段音乐,时域上看是振幅起伏的波形
- 但你想知道它有哪些音符(频率成分),时域就帮不上忙了
- 频域分析能告诉你:信号由哪些频率的正弦波组成,每个频率的幅度和相位是多少
核心思想:任何周期信号都可以分解为一系列不同频率、不同幅度、不同相位的正弦波之和。这就是傅里叶级数。非周期信号呢?用傅里叶变换。
傅里叶级数:周期信号的分解
先看周期信号。假设一个信号周期是 T,频率 f₀ = 1/T。
傅里叶级数说:这个信号可以写成
x(t) = a₀ + Σ[aₙ·cos(2πnf₀t) + bₙ·sin(2πnf₀t)]
其中:
- a₀ 是直流分量(平均值)
- aₙ、bₙ 是各次谐波的系数
- n 是谐波次数(n=1 是基波,n=2 是二次谐波……)
我刚开始学的时候,总觉得这些公式很抽象。后来我写了个小脚本,用方波做实验——只取前 3 次谐波合成,波形还差得远;取到 10 次,已经有点样子了;取到 50 次,几乎完美。亲眼看到这个过程,比背十遍公式都管用。
我的经验:理解傅里叶级数,最好的办法就是动手合成波形。取一个方波,分别用 1、3、5、10、50 次谐波去逼近,观察误差怎么变化。你会直观感受到「级数收敛」是什么意思。
傅里叶变换:从周期到非周期
周期信号用级数,非周期信号怎么办?
思路其实很巧妙:把非周期信号看作周期无穷大的周期信号。当 T → ∞,频率间隔 Δf → 0,离散的频谱就变成了连续的频谱。
这就是傅里叶变换:
X(f) = ∫ x(t)·e^(-j2πft) dt
从 -∞ 到 +∞ 积分。结果 X(f) 是一个复数,包含了幅度和相位信息。
逆变换呢?
x(t) = ∫ X(f)·e^(j2πft) df
说白了,正变换是把时域信号拆成频率成分,逆变换是把频率成分重新合成时域信号。
注意:傅里叶变换存在的条件是信号绝对可积——∫|x(t)|dt < ∞。实际中大部分信号都满足,但像纯正弦波(无限长)就不满足。不过别担心,工程上我们处理的是有限长信号,没问题。
时域 vs 频域:一张表说清楚
| 对比项 | 时域 | 频域 |
|---|---|---|
| 横轴 | 时间 t | 频率 f |
| 纵轴 | 幅度 x(t) | 幅度 |X(f)| 或相位 ∠X(f) |
| 直观信息 | 信号随时间的变化 | 信号包含哪些频率成分 |
| 典型应用 | 波形显示、触发检测 | 频谱分析、滤波设计 |
| 数学工具 | 微分方程 | 傅里叶变换 |
我个人的习惯是:先看时域波形有没有明显特征(比如脉冲、突变),如果没有,直接上频谱。很多问题在频域里一目了然。
相位谱:容易被忽略的关键信息
很多人只关注幅度谱,觉得相位不重要。我曾经也这么想。
直到有一次做信号重构实验:我保留了幅度谱,把相位谱全部置零。结果重构出来的信号面目全非,完全不是原来的样子。
为什么会这样?因为相位决定了各频率分量之间的相对位置。没有正确的相位,正弦波叠加出来的波形就完全错位了。
记住:幅度谱告诉你「有哪些频率」,相位谱告诉你「这些频率怎么对齐」。两者缺一不可。
知识结构图:傅里叶变换的核心逻辑
下面这张图概括了从时域到频域的完整路径:
Python 实战:用代码感受傅里叶变换
光说不练假把式。我们写一小段代码,生成一个合成信号,然后做傅里叶变换看看频谱。
import numpy as np
import matplotlib.pyplot as plt
# 参数设置
fs = 1000 # 采样率 1000 Hz
T = 1 # 时长 1 秒
t = np.linspace(0, T, fs, endpoint=False)
# 合成信号:50Hz 正弦波 + 120Hz 正弦波 + 噪声
f1, f2 = 50, 120
x = 0.7 * np.sin(2 * np.pi * f1 * t) + 1.2 * np.sin(2 * np.pi * f2 * t)
x += 0.3 * np.random.randn(len(t)) # 加点噪声
# 傅里叶变换
X = np.fft.fft(x)
freqs = np.fft.fftfreq(len(x), 1/fs)
# 只取正频率部分
half = len(x) // 2
X_mag = np.abs(X[:half])
freqs = freqs[:half]
# 找到峰值频率
peak_idx = np.argsort(X_mag)[-5:] # 取前5个峰值
print("主要频率成分 (Hz):", freqs[peak_idx])
print("对应幅度:", X_mag[peak_idx])
运行这段代码,你会看到频谱在 50Hz 和 120Hz 处出现明显峰值。噪声虽然存在,但不会掩盖主要频率成分。
建议:把上面代码里的频率、幅度、噪声强度改一改,看看频谱怎么变化。我当初就是这么练的——改参数、看结果、想原因。比死记硬背公式有效十倍。
避坑指南
- 采样率要够:根据奈奎斯特定理,采样率必须大于信号最高频率的两倍。我曾经用 100Hz 采样率去采 60Hz 信号,结果频谱里出现了 40Hz 的假峰——这就是混叠。
- 频谱泄露:如果信号周期和采样窗口不匹配,频谱会「漏」到相邻频率上。解决办法是加窗函数(汉宁窗、海明窗等)。
- 相位展开:傅里叶变换得到的相位在 -π 到 π 之间,有跳变。如果需要连续相位,要做 unwrap 处理。这个在后面的章节会详细讲。