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再平均。方差小,谱线更平滑。我个人更推荐这个方法。
Welch法PSD估计(Python示例):
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频谱分析 功率谱密度估计 共同问题:频谱泄露 → 解决方案:窗函数 核心流程:时域数据 → 预处理 → 频域分析(FFT/PSD)→ 识别噪声特征 → 针对性滤除

频域预处理不是孤立的一步。它和时域滤波是互补的——时域滤波处理「已知频率」的噪声,频域分析帮我们发现「未知频率」的问题。做IMU数据处理,两手都要硬。

一句话总结:

FFT让你看到信号里有什么频率成分,PSD告诉你每个频率上噪声有多大,窗函数则保证你看得准、看得清。这三板斧用好了,IMU数据里的猫腻基本藏不住。