频域分析入门:傅里叶变换(FFT)的核心思想,频谱泄露与窗函数

各位同学,今天我们聊一个绕不开的话题——傅里叶变换。

说实话,我刚入行那会儿,看到FFT的公式就头疼。一堆复数、求和、指数,感觉像天书。后来做项目做多了,才慢慢悟出来:FFT说白了就是把信号从时间轴掰到频率轴上去看。你想想看,一个振动信号在时域里乱糟糟的,但到了频域,几个峰值一出来,问题就清楚了。

一、傅里叶变换到底在干什么?

我习惯用一个比喻来理解:傅里叶变换就像一台“频率分光镜”。白光通过三棱镜,分解成红橙黄绿蓝靛紫。信号通过FFT,分解成不同频率的正弦波分量。

数学上,连续傅里叶变换的公式长这样:

X(f) = ∫ x(t) · e^(-j2πft) dt

但在实际工程中,我们处理的是离散采样信号。所以用的是离散傅里叶变换(DFT),而FFT是它的快速算法。

DFT公式:

X[k] = Σ x[n] · e^(-j2πkn/N)   (n=0 to N-1)

这里N是采样点数,k对应频率索引。每个X[k]代表该频率分量的幅度和相位

核心要点:FFT的输出是复数数组。取模得到幅度谱,取角度得到相位谱。我们平时看频谱,看的都是幅度谱。

二、频谱泄露——一个让人头疼的问题

我在项目中遇到过这样一个情况:采集一个50Hz的纯正弦波,采样率1000Hz,采样1秒。理论上频谱应该只在50Hz处有一个尖峰。但实际做FFT后,发现50Hz附近出现了很多“小尾巴”,能量散开了。

这就是频谱泄露

为什么会这样?

原因很简单:FFT假设信号是周期延拓的。如果你的采样长度不是信号周期的整数倍,那么首尾连接处就会产生“突变”。这个突变在频域里表现为额外的频率分量。

举个例子:

  • 你采了1000个点,信号正好是50个完整周期 → 完美,频谱干净
  • 你采了1000个点,信号是50.3个周期 → 首尾不连续,频谱泄露

注意:频谱泄露不是算法错误,而是有限长截断带来的固有现象。你无法完全消除它,只能尽量抑制。

三、窗函数——给信号“加个框”

既然泄露是因为截断引起的,那能不能让截断变得“温柔”一点?

可以。加窗就是干这个的。

窗函数的核心思想:让信号在两端平滑地衰减到零,减少首尾突变。

我常用的窗函数有几种:

窗函数 主瓣宽度 旁瓣衰减 适用场景
矩形窗 最窄 -13dB 频率分辨率要求高,泄露不敏感
汉宁窗 较宽 -31dB 通用场景,平衡分辨率与泄露
海明窗 较宽 -41dB 旁瓣抑制更好,适合弱信号检测
布莱克曼窗 最宽 -58dB 强干扰下的微弱信号提取

嗯,这里要注意:加窗不是免费的午餐。它抑制了泄露,但会降低频率分辨率(主瓣变宽)。所以选窗函数是个权衡。

四、汉宁窗 vs 海明窗——我个人的选择

这两个窗长得像,公式也像:

汉宁窗:w[n] = 0.5 - 0.5·cos(2πn/N)
海明窗:w[n] = 0.54 - 0.46·cos(2πn/N)

区别在哪?

  • 汉宁窗:两端严格归零,旁瓣衰减快,但主瓣稍宽
  • 海明窗:两端不归零(0.08左右),旁瓣更低,但衰减慢

我个人的习惯是:

  • 通用频谱分析,用汉宁窗。它最“老实”,不会引入额外假象
  • 弱信号检测,用海明窗。它的旁瓣更低,能压住强信号的干扰

小技巧:如果你不确定用哪个窗,先试试汉宁窗。它在大多数场景下表现都不错。我曾经在一个振动分析项目中,用汉宁窗成功分离了两个间隔仅0.5Hz的模态频率。

五、实战代码——加窗FFT怎么做

下面给一段Python代码,演示加窗FFT的完整流程:

import numpy as np
import matplotlib.pyplot as plt

# 生成测试信号:50Hz + 120Hz,加噪声
fs = 1000  # 采样率
t = np.arange(0, 1, 1/fs)
x = 0.7*np.sin(2*np.pi*50*t) + np.sin(2*np.pi*120*t)
x += 0.3*np.random.randn(len(t))

# 加汉宁窗
window = np.hanning(len(x))
x_windowed = x * window

# FFT
X = np.fft.fft(x_windowed)
freqs = np.fft.fftfreq(len(x), 1/fs)

# 只取正频率部分
half = len(x)//2
X_mag = np.abs(X[:half]) * 2 / len(x)
freqs = freqs[:half]

# 绘图
plt.figure(figsize=(10, 4))
plt.plot(freqs, X_mag)
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('加汉宁窗后的频谱')
plt.grid(True)
plt.show()

这段代码里有个细节:np.abs(X[:half]) * 2 / len(x)。为什么要乘以2?因为FFT输出是双边谱,我们只取正频率,幅度要加倍才能恢复真实幅值。另外除以N是为了归一化。

避坑指南:我曾经犯过一个错误——加窗后忘记做幅度补偿。结果频谱的幅度比实际小了一半。汉宁窗的幅度恢复系数是2,海明窗是1.85左右。记得查一下你用的窗函数的恢复系数。

六、知识体系总览

下面这张图总结了本章的核心逻辑:

频域分析核心知识体系 时域信号 x(t) FFT 频域 X(f) 频谱泄露问题 非整数周期截断 加窗函数 矩形窗(无窗) 汉宁窗(通用) 海明窗(弱信号) 核心权衡:频率分辨率 vs 旁瓣抑制

这张图把整个流程串起来了:时域信号 → FFT → 频域 → 遇到泄露问题 → 加窗解决 → 不同窗函数的选择。

七、总结与避坑

最后,我把自己踩过的坑总结一下:

  1. 不要直接用矩形窗(也就是不加窗)。除非你确定采样长度是周期的整数倍,否则泄露会让你怀疑人生。
  2. 加窗后记得幅度补偿。不同窗的补偿系数不同,查表或计算一下。
  3. 窗函数会影响频率分辨率。如果你需要分辨两个很近的频率,用矩形窗或汉宁窗;如果信号很弱,用海明窗或布莱克曼窗。
  4. FFT点数不够时,补零不会提高分辨率。补零只是插值,真正的分辨率由采样时长决定。

一句话总结:FFT让你看到信号的频率成分,窗函数让你看得更清楚。选对窗,事半功倍;选错窗,事倍功半。

好了,这一章就到这里。记住,理论是死的,信号是活的。多动手试试,你会有自己的体会。


公众号:蓝海资料掘金营,微信deep3321