
做信号分析的这些年我收到过最多的一个疑问就是波形明明把时间、幅度都画得清清楚楚为什么大家还要多此一举去看频谱“频谱分析”这个词在设备振动、音频处理、电力谐波这些场景里几乎天天出现但不少人用FFT只是机械地点一下按钮对背后发生了什么、时谱分析又补上了哪块短板其实并不清楚。这篇文章就把这两件事放在一起讲透频谱分析告诉我们一段信号里“有什么频率”时谱分析则告诉我们“这些频率在什么时候出现”。适合刚接触信号处理、以及已经在跑FFT但总感觉结果有些说不清的朋友。1. 频谱分析到底在分析什么1.1 时域里的“信息压缩”一段原始信号通常以时间为横轴、幅值为纵轴。以电机振动信号为例电机转起来之后传感器采到的波形是一串密密麻麻的、看起来几乎没有规律的曲线。你把这段曲线发给一个不懂信号处理的人看他大概率只能说“好像有点重复、又好像不太规则”。但同一段信号经过频谱分析之后结果会变成几根清晰的谱线转频一根、轴承外圈特征频率一根、齿轮啮合频率一根旁边再带一些边带。这就是频域带来的“信息压缩”——把成千上万个时间采样点压缩成若干个主要频率成分的幅度和相位。你可能会问时域波形难道没有这些信息吗有只是藏得太深。比如一个缓慢变化的压力信号上叠加了一个50Hz的工频干扰你在时域里看到的是“线条变粗了”很难精确说出干扰到底是多少赫兹、幅度多大。但频谱分析可以非常直观地回答50Hz处有一根谱线幅度多少毫伏一目了然。这正是频谱分析最核心的价值把一个复杂信号拆解成不同频率分量的集合让你能对“能量去了哪里”这件事心里有数。1.2 傅里叶变换的两面性频谱分析的数学基础是傅里叶变换。它的直觉解释可以这样理解任何一段有限长的平稳信号都可以看作若干个正弦波、余弦波的叠加每个正弦波有自己的频率、幅度和相位。傅里叶变换就是把信号按这几张“模板”逐一做内部匹配算出来每个频率对应的分量有多大。这个过程完全可靠但它有一个经常被忽略的前提——信号在观察窗口内必须是平稳的或者至少是近似平稳的。这就是傅里叶变换的两面性它能给你极干净的频率信息却会毫不留情地把时间信息抹掉。变换完之后的频谱图上你只知道信号里有120Hz这个成分却不知道它是在第1秒出现还是第5秒出现。对于长时间监测、非平稳工况、瞬态冲击这类场景单靠一张频谱图远远不够所以才有了后文要展开的时谱分析。这也是我主张先把频谱分析吃透、再上手时谱分析的原因时谱里那张二维图的每个切片本质上就是一段短信号的频谱。2. 频谱分析落地工程的四个关键参数2.1 采样率先定下“能否看清频谱”的前提做频谱分析第一步不是写FFT代码而是确认你手里的数据是怎么采上来的。采样定理大家都背过采样率至少要大于信号最高频率的2倍否则高频成分会“折叠”回低频产生混叠。工程上我从来不会只卡2倍一般留4到10倍余量。比如轴承振动分析里常见的特征频率在几kHz到几十kHz选择采样率时我会先估算最高关心的频率再按至少5倍去设置采样率之后硬件端还会加抗混叠滤波器。很多人的混叠问题其实发生在这一环。我就见过有人拿10kHz采样率去测一个几十MHz的干扰源结果干扰没有消失而是变成了一段低频“幻影”出现在频谱里看起来像真实的故障频率实际上完全不是。判断混叠的一个土办法是改变采样率重新采集一次如果某条谱线的频率跟着变了那就是混叠。真正的信号频率不会因为采样率改变而改变。2.2 频率分辨率Δf Fs / N 到底怎么算FFT分析频率的分辨率公式是Δf Fs / N其中Fs是采样率N是FFT点数。注意提高分辨率只有两条路降低采样率或者增加FFT点数。我举个具体的例子假设采样率Fs 1000Hz一次分析取N 1024点那么Δf 1000 / 1024 ≈ 0.98Hz。这意味着频谱图上相邻两根谱线间隔约1Hz两根间隔小于1Hz的谱线会被糊成一根肉眼无法分辨。如果你想分辨出0.5Hz的差异需要的点数就是Fs / 0.5 2000点以上。实际做的时候通常取2的整数次幂比如2048点。这里有一个容易被误解的地方增加N的本质是增加观察时长T因为N Fs × T。也就是说要分辨出两个非常接近的频率你必须观察足够长的时间。频率分辨率和时间长度之间的这种绑定关系是整个傅里叶分析里最基础的物理约束后文讲时谱分析时它还会以另一种形式再次出现。2.3 窗函数给信号“剪裁”也要讲究手法FFT处理的数据必然是有限长的相当于从原始无限长信号里截了一段。这种硬生生的截断会在频谱上产生“泄漏”——本来应该是一根细谱线的能量扩散到了旁边几十甚至几百根谱线上。这就是窗函数出场的原因。可以这样理解不加窗相当于用矩形窗去截窗边缘突然从有变到无信号被强行剪断高频分量就出来了加窗则是让信号边缘慢慢衰减到零减小这种突变。窗函数的选择有一个公共的取舍主瓣越窄频率分辨能力越强旁瓣越低泄漏抑制越好。这两者通常互相矛盾。我用过的几种常用窗做个对比窗函数主瓣宽度-3dB旁瓣衰减典型使用场景矩形窗0.89 × Fs / N 附近-13dB瞬态信号、校准但泄漏最明显汉宁窗1.44 × Fs / N 附近-31dB通用分析首选兼顾分辨率与泄漏汉明窗1.30 × Fs / N 附近-43dB频率接近时略优于汉宁布莱克曼窗2.00 × Fs / N 附近-58dB强调振幅精度分辨率牺牲较大工程上我最常用汉宁窗。比如对一台减速机做振动频谱分析时齿轮啮合频率附近往往有多个边带用矩形窗容易泄漏得一塌糊涂换成汉宁窗后边带一下子清晰了。需要注意的是加窗会改变信号的总能量如果要精确测量幅值就得做幅值校正或能量校正否则测出来的峰值会比真实值低。这个“测出来偏小”的坑几乎每个初学者都踩过。2.4 功率谱还是幅值谱不同关注点不同选择同样做FFT最后画图的纵轴选择其实有讲究。直接对FFT结果取绝对值得到的是幅值谱它最直观50Hz处幅值是1.5V那就是1.5V。但幅值谱有方差大的毛病特别是噪声较多时谱线会上下乱跳夹在中间的真信号反而不突出。这时用功率谱密度PSD更合适。功率谱密度在工程上通常用Welch方法计算把长信号分成多段重叠的子段每段加窗做FFT再对功率谱取平均。平均段数越多方差越小谱线越平滑。比如采样长度只有2秒我的处理方式是nperseg取512重叠50%这样可以得到约7段做平均频谱曲线比单次FFT稳得多。做故障诊断和振动状态监测时我几乎只推荐PSD配对数坐标低频故障特征在宽动态范围下才不会被高频噪声淹没。3. 时谱分析让频谱跟着时间动起来3.1 一维频谱的局限前面说了很多频谱分析的优势但它的短板也非常明显它默认信号在观察窗内是平稳的。现实中大量场景不满足这个条件。举个最常见的例子一段录音前半段背景安静中间突然有人说话后半段传来喇叭啸叫。你做整段FFT只会看到一整片频率能量堆积在一起完全看不出来这些声音的先后关系。再比如设备启动过程电机从停止到额定转速转频从0逐渐升到50Hz你如果对着整个启动过程做一次FFT只会看到一条模糊的“频率带”启动阶段和稳态阶段混在一起毫无分析价值。这时就需要时谱分析。所谓时谱工程界通常指时频分布谱图也就是把信号在“时间-频率”二维平面上展示颜色深浅表示某个时刻、某个频率上的能量强度。它在论文和报告里常被称为spectrogram中文也叫语谱图但用在振动、电力、生物医学信号上时大家更习惯叫时谱图。它解决的核心问题就一句话频谱在什么时间发生变化以及变化是怎么演进的。3.2 STFT的核心窗口长度与重叠率实现时谱分析最常用的工具是短时傅里叶变换STFT。思路非常简单在时间轴上滑动一个固定长度的窗口每到一个位置就对窗口内的信号做一次FFT把结果按时间顺序排列拼成一张二维图。你可以把它想象成给信号“分段听诊”每一小段内假设信号近似平稳段与段之间的变化就能被捕捉到。但这里有个没有办法绕开的矛盾窗口越长频率分辨率越好窗口越短时间分辨率越好。两者是反比关系因为不确定性原理在信号分析里的体现。我的实战经验是这样取舍的如果目的是看慢变趋势比如齿轮箱温度升高过程中振动频谱的整体变化窗口可以开长一点比如4096点把频率分得细一点如果目的是抓瞬时冲击比如轴承出现剥落时那个短促的“咯噔”声窗口必须短256点甚至128点都不过分频率分辨率牺牲一点没关系但要能看清冲击发生的准确时刻。重叠率是另一个容易被忽略但非常重要的参数。窗口滑动时如果一帧一帧首尾相接时间轴就会比较粗糙两个窗口之间变化的细节会丢失。我一般设置50%到75%的重叠率。以Fs 1000Hz、窗口256点为例窗口长度是0.256秒50%重叠意味着每次只前进128个采样点时间步长是0.128秒这样即使用短窗时谱图的时间连续性也会好很多。75%重叠更平滑但计算量随之增加。3.3 从STFT到完整时谱图一次实际计算理论知识讲完直接给一段可以复现的代码。还是用Python生态工具是numpy、scipy和matplotlib。import numpy as np import matplotlib.pyplot as plt from scipy.signal import spectrogram fs 1000 t np.arange(0, 4, 1 / fs) # 构造一个0到4秒的频率变化信号1-2秒之间有一个从50Hz扫到150Hz的chirp x np.zeros_like(t) mask (t 1) (t 2) x[mask] np.sin(2 * np.pi * (50 100 * (t[mask] - 1)) * t[mask]) # 1.5秒到1.7秒加一个瞬时冲击 imp (t 1.5) (t 1.7) x[imp] 1.2 * np.sin(2 * np.pi * 200 * t[imp]) # 加一点噪声 rng np.random.default_rng(42) x 0.05 * rng.standard_normal(len(t)) f, t_spec, Sxx spectrogram( x, fsfs, nperseg256, # 窗口长度256点对应0.256秒 noverlap128, # 50%重叠 windowhann, # 汉宁窗 scalingspectrum ) plt.figure(figsize(10, 5)) plt.pcolormesh(t_spec, f, 10 * np.log10(Sxx 1e-12), shadinggouraud, cmapinferno) plt.ylim(0, 250) plt.xlabel(时间 (s)) plt.ylabel(频率 (Hz)) plt.colorbar(label功率谱密度 (dB)) plt.tight_layout() plt.show()输出图上你能看到三条清晰的痕迹1秒处开始一条斜向上的亮带是那根从50Hz扫到150Hz的chirp信号1.5秒处竖直方向的一条短线对应那个200Hz的冲击背景淡淡一层是噪声。这就是时谱分析的完整价值频率随时间变化的轨迹、瞬时冲击的出现时刻全部一目了然。第三步中还有一个细节值得说为什么谱图纵轴用10 × log10(Sxx)而不是直接画Sxx因为信号能量动态范围可能非常大可能从1倍到1000倍线性轴会让小的特征完全看不见。转成dB之后大信号和小信号之间的差异被压缩整个谱图的可读性会好很多。这是我在实际项目里养成的习惯凡是看时谱图一律用dB坐标。4. 常见问题与排查技巧实录4.1 频谱泄露为什么谱线旁边多了很多“裙边”我做轴承故障诊断时曾经对着一张频谱图犯过很久的嘀咕转频50Hz那根谱线旁边有一连串慢慢衰减的“裙边”看起来像故障特征但频率位置又对不上。后来才确认这就是典型的频谱泄露——50Hz不是FFT频率栅格的整数倍能量泄到了相邻谱线上。矩形窗最严重汉宁窗能压住一部分但不可能完全消除。处理这个问题有两个方向。第一个方向是选择窗函数这是最常规的第二个方向是调整FFT点数让关注的频率尽量落在栅格上。比如采样率1000Hz、N 1024时栅格间隔是约0.98Hz50Hz恰好比较接近第51根栅格但如果换成N 1000栅格间隔就是1Hz整50Hz正好落在栅格中心。有些分析软件里有“频率栅格对齐”的功能原理就是反推一个合适的FFT长度。要注意的是改变N也会改变观测时长最终仍要服从分辨率公式。4.2 栅栏效应与补零补零不等于提高分辨率另一个常见坑是“补零”。很多人以为在FFT之前给数据后面补一排零把N从1024补到8192频谱图上的谱线就会变密两个相近的频率就能分开了。实测下来谱线确实变密了但两个峰值如果本来靠得太近补零之后它们依然是一个鼓包不会分裂成两个峰。根本原因前面已经说了真实分辨率取决于观测时长T和时间窗内的有效信号长度有关补零只是在两个真实栅格之间做了插值让曲线看起来更光滑。那补零到底有没有用有用但不是用来提分辨率而是用来找峰值。当某个真实频率正好落在两个栅格之间时能量会平分到两条谱线上看起来有两个差不多的矮峰真实幅值被低估。补零之后能更好地估计出真实峰值位置和幅值。我处理齿轮箱边带频率时常常先做原始长度FFT再补零到4倍长度做二次精细搜索定位精度会明显提升。4.3 时谱图里那些“不干净”的干扰从哪来时谱图上最常见的干扰是横竖条纹。横向一整条亮线通常意味着某个固定频率的持续干扰比如50Hz工频、PWM开关频率这种干扰源在频域上很好识别。竖向的亮线则代表某个时刻突然出现、频带很宽的瞬态冲击比如电磁干扰、开关动作、设备碰撞。这两种干扰一眼就能从时谱图上分辨出来这也是时谱分析比单纯频谱分析更适合做“环境诊断”的原因。还有一类干扰来自参数设置窗口长度太短时频率轴模糊谱图看起来像蒙了一层雾窗口太长时时间轴模糊短冲击被拉成一条长线。如果你发现时谱图“该圆的不圆、该尖的不尖”先回头检查nperseg和noverlap别急着怀疑信号。我的调参经验是先用默认窗口长度跑一遍确认目标特征的时间跨度大概多长再按“时间跨度覆盖3到5个窗口”的原则去反向调整窗口长度。这样反复调两三次基本能找到一个既保留时间细节、又不牺牲频率分辨能力的好参数。最后再说个小技巧也是我个人最常用的一条实操心得在做设备状态监测或者语音信号分析时永远把频谱图和时谱图同时打开看。频谱图用来判断“有没有某个频率成分”时谱图用来判断“这个成分什么时候出现、怎么变化”。两图对照很多看似奇怪的异常都能几十秒内定位出来比单看任何一种图都要高效得多。既然你已经把这两个工具都掌握在手下一步不妨找个真实采集的信号试一遍踩过几个参数坑之后你会突然发现频谱分析和时谱分析就像一对组合拳一块用才顺手。