4. 频域预处理:FFT频谱分析、功率谱密度估计、频谱泄露与窗函数
聊到IMU数据预处理,时域滤波是基本功。但说实话,有些噪声在时域里根本看不清楚。比如一个微弱的周期性振动,你盯着波形图看半天,可能只觉得「有点抖」,但具体抖在哪个频率上?说不准。
这时候就得请出频域分析这个大杀器了。我个人习惯,拿到一批IMU数据,先做一遍FFT看看频谱。这就像给信号做一次「体检」,哪里有问题一目了然。
4.1 FFT频谱分析——把时间信号拆成频率分量
FFT(快速傅里叶变换)说白了,就是把一段时域信号拆解成不同频率的正弦波组合。你想想看,任何一个复杂的波形,都可以看成是多个不同频率、不同幅值、不同相位的正弦波叠加而成。FFT就是帮我们找到这些「成分」的。
对于IMU数据,我们通常对加速度计和陀螺仪的每个轴分别做FFT。比如采样率是100Hz,采集了10秒数据,那就是1000个采样点。FFT之后,我们能得到从0Hz到50Hz(奈奎斯特频率)的频谱信息。
X[k] = Σ(n=0 to N-1) x[n] * e^(-j*2π*k*n/N)
其中N是采样点数,x[n]是时域序列,X[k]是频域序列。k对应频率f = k * fs / N。
我在项目中遇到过一件事:某款无人机悬停时高度总在漂移。时域上看加速度计Z轴数据,噪声水平正常。但一做FFT,发现0.8Hz附近有个明显的尖峰。后来排查发现是螺旋桨的振动通过机架传导到了IMU。这个频率正好和高度控制环的带宽接近,导致控制器一直在「误判」。
做FFT之前,先检查数据里有没有明显的直流偏置。如果有,建议先减去均值。否则0Hz处的能量会特别大,把其他频率分量都「压」得看不见了。
4.2 功率谱密度估计——噪声的能量分布
FFT给出的是频谱幅值,但有时候我们更关心「能量」在各个频率上的分布。这就是功率谱密度(PSD)的用武之地。
PSD的单位通常是 (m/s²)²/Hz 或者 (°/s)²/Hz。它告诉我们:在某个频率附近,每赫兹带宽内包含了多少「功率」。对于IMU选型或者噪声建模,PSD是绕不开的工具。
常用的估计方法有两种:
- 周期图法:直接对信号做FFT,取幅值的平方再除以N。简单粗暴,但方差大。
- Welch法:把信号分段,每段加窗,分别做FFT再平均。方差小,谱线更平滑。我个人更推荐这个方法。
from scipy import signal
import numpy as np
# 假设acc_z是加速度计Z轴数据,fs=100Hz
f, Pxx = signal.welch(
acc_z,
fs=100,
window='hann',
nperseg=256,
noverlap=128
)
# 绘制PSD曲线
plt.semilogy(f, Pxx)
plt.xlabel('频率 (Hz)')
plt.ylabel('功率谱密度 ((m/s²)²/Hz)')
plt.grid(True)
嗯,这里要注意:PSD曲线上的「噪声底噪」水平,直接决定了IMU的精度上限。比如加速度计的白噪声底噪在10⁻⁴量级,那你的系统就很难分辨低于这个水平的加速度变化。
4.3 频谱泄露——一个容易被忽视的坑
做FFT时,我们默认信号是「周期延拓」的。但实际采集的信号往往不是整周期截断。这就导致在边界处出现不连续,反映在频谱上就是能量「泄露」到了相邻的频率点上。
举个例子:你采集了一个50.3Hz的正弦波,采样率100Hz,采集1秒。理论上频谱应该在50.3Hz处有一个尖峰。但实际做FFT后你会发现,50Hz和51Hz附近都有能量分布,而且幅值还比真实值小了不少。
有一次做IMU振动台标定,振动台设定频率是20Hz。FFT结果出来,20Hz处幅值正常,但19Hz和21Hz处也有不小的能量。我一开始以为是振动台有问题,折腾了半天。后来才意识到是频谱泄露——采样时长不是20Hz周期的整数倍。加了个窗函数,问题就解决了。
频谱泄露的危害在于:
- 弱信号可能被强信号的泄露「淹没」
- 频率定位精度下降
- 幅值估计偏小
4.4 窗函数——对抗频谱泄露的利器
解决频谱泄露的办法,就是在做FFT之前给信号加一个窗函数。窗函数的作用是让信号在两端平滑地衰减到0,从而减少边界不连续带来的影响。
常用的窗函数有这些:
| 窗函数 | 主瓣宽度 | 旁瓣衰减 | 适用场景 |
|---|---|---|---|
| 矩形窗 | 最窄 | -13dB | 频率分辨率优先,幅值精度要求不高 |
| 汉宁窗 | 较宽 | -32dB | 通用场景,兼顾分辨率和泄露抑制 |
| 海明窗 | 较宽 | -43dB | 旁瓣衰减更好,适合弱信号检测 |
| 布莱克曼窗 | 最宽 | -58dB | 旁瓣衰减极佳,频率分辨率牺牲大 |
我个人习惯,处理IMU数据时首选汉宁窗。它能在频率分辨率和泄露抑制之间取得不错的平衡。如果你需要检测两个频率非常接近的信号,可以考虑矩形窗,但要接受泄露带来的误差。
加窗会降低信号的总能量,所以FFT幅值会比真实值偏小。需要乘以一个补偿系数:
# 汉宁窗的幅值补偿系数
coherent_gain = np.mean(np.hanning(N))
corrected_amplitude = fft_amplitude / coherent_gain
4.5 本章知识体系
下面这张图总结了频域预处理的完整流程和关键要点:
频域预处理不是孤立的一步。它和时域滤波是互补的——时域滤波处理「已知频率」的噪声,频域分析帮我们发现「未知频率」的问题。做IMU数据处理,两手都要硬。
FFT让你看到信号里有什么频率成分,PSD告诉你每个频率上噪声有多大,窗函数则保证你看得准、看得清。这三板斧用好了,IMU数据里的猫腻基本藏不住。