
简介本资源聚焦MATLAB平台下的信号时频分析核心技术与工程实践面向电子信息、通信、机械振动及生物医学等领域的本科生、研究生与工程师解决非平稳信号在时间-频率联合域中特征提取与可视化分析的实际问题。压缩包为精简型工具包仅1KB含2个文件核心MATLAB脚本stran.m实现短时傅里叶变换或小波类时频分析功能license.txt明确使用授权条款m文件可直接调用或二次开发txt文件保障合规使用。已有465人学习下载体现其在课程设计、毕业课题及科研预研中的实用价值。读者可即刻获得可运行的时频分析脚本、典型变换方法的MATLAB实现逻辑、以及配套许可说明无需额外配置即可开展STFT谱图绘制、瞬时频率估计等基础分析任务是入门时频分析并衔接signal processing toolbox与wavelet toolbox的轻量级实践入口。 做信号处理这些年我接过的实测数据一多就越发觉得时频分析是个绕不开的坎。你拿一段振动信号、一段语音、或者一段电力暂态波形直接做FFT得到的是一个全局频谱——信号里有哪些频率成分一目了然但每个频率是什么时候出现的、怎么随时间变化的完全看不出来。而实际工程问题问得最多的恰恰是什么时候。轴承故障特征频率是间歇出现的语音的音调是连续变化的变频器的谐波是随转速走的——这些场景下固定频谱不够用必须上时频分析。MATLAB在这方面做得相当成熟短时傅里叶变换、小波变换、经验模态分解这些方法都有现成函数工具箱封装得也不错。这篇就聊聊我在项目里实际用MATLAB做时频分析的方法、参数怎么选、坑在哪里以及怎么把这些分析做成能反复使用的应用程序。内容偏实践适合刚接触时频分析的工程师、做故障诊断和信号处理的学生以及需要在MATLAB里把分析流程固化成工具的朋友参考。1. 为什么非做时频分析不可先搞清楚它解决什么问题1.1 傅里叶变换在非平稳信号前的短板我不止一次在评审会上听到这样的质疑信号分析用FFT不就行了写论文、出报告FFT确实是最常用的工具但很多人忽略了FFT的一个前提假设——它把信号当作无限长的平稳信号来处理。工程实测数据几乎都不满足这个假设。举个例子一台设备启动过程中采集的振动信号转速从500转每分钟升到3000转每分钟振动频率跟着转速一路上扬。你用FFT看这个信号只能得到一条从低频到高频都有能量的频谱至于转速是什么时候升上来的、升速过程中有没有某个频率的共振峰值频谱上完全看不出时间顺序。再举个更直观的例子。两个信号第一个是0到5秒为50Hz正弦、5到10秒为100Hz正弦第二个是0到10秒同时包含50Hz和100Hz正弦。这两个信号的FFT频谱几乎一模一样都是从10Hz到100Hz某段有两条谱线。但听感、物理过程、故障机理完全不同。这就是傅里叶变换的致命短板——它把时间信息彻底丢弃了。时频分析要解决的就是在什么时间、出现什么频率这样一个二维问题。1.2 时频分析到底能告诉你什么时频分析的本质是构造一个时间-频率平面把一维信号映射到这个二维平面上能量在平面上的分布就是时频谱。通俗点说FFT给你一张配料表时频分析给你一张烹饪过程记录——哪个环节加了什么料、加了多少、持续多久清清楚楚。我自己的经验是时频分析在几个场景下是不可替代的。一是旋转机械的转速波动信号分析变转速下振动频率跟着转频走只有时频图能看出频率随时间的变化轨迹二是语音和生物医学信号处理比如心音信号中S1、S2心音的时频分布能辅助判断瓣膜病变三是电力系统暂态分析谐波、间谐波在故障瞬间的涌起和衰减过程只有时频图能完整呈现四是地震信号和探地雷达数据分析反射波的到达时刻和频率衰减特征直接依赖时频信息。当然时频分析不是万能的。它增加了时间维度也就引入了时间和频率分辨率的矛盾——这个矛盾贯穿了所有时频分析方法后面我会详细讲。这里先记住一个结论如果你的信号是平稳的直接做FFT就够了千万别为了炫技非上时频分析只有信号包含调频、调幅、突变等非平稳特征时时频分析才有真正的用武之地。2. MATLAB时频分析方法选型四种主流方案怎么选2.1 短时傅里叶变换STFT最稳妥的入门方案短时傅里叶变换的思路非常朴素把长信号切成小段每段近似平稳然后分别做FFT。数学上就是对信号加一个窗函数平移窗的位置逐段计算频谱。MATLAB里核心函数是spectrogram我在R2016a之后一直用它接口没怎么变过用起来很稳定。STFT最大的优点是直观、计算量可控、结果解释简单。时频图横轴是时间、纵轴是频率、颜色深浅代表能量大小工程人员上手就会看。它对单分量信号和多分量分离较好的信号效果都不错是我做故障诊断时的默认首选。但STFT有个绕不开的问题窗长一旦确定整个时间-频率平面上的分辨率就固定了。窗长越长频率分辨越好时间分辨越差窗长越短则反之。这是海森堡测不准原理在信号处理里的体现没有免费午餐。所以STFT适合那些频率变化不太剧烈的信号如果信号里有高频突变又有低频缓变它往往顾此失彼。2.2 连续小波变换CWT分辨率取舍更灵活连续小波变换换了个思路用一组可伸缩、可平移的小波基函数去匹配信号。高频段用窄的小波时间分辨率高低频段用宽的小波频率分辨率高。这样就打破了STFT分辨率恒定的限制自动适应信号在不同频段的特征。MATLAB里cwt函数在R2016b之后做了重构原先直接指定小波名称和尺度的写法变成了默认使用Morse小波、自动计算尺度。我最早用旧版cwt(x, scales, cmor1-1.5)升级后改成了cwt(x, fs)结果简单很多但自定义能力反而需要查文档才能搞清楚。所以如果你升级了MATLAB版本之前跑得好好的小波分析代码可能要调整。CWT的缺点是计算量大、结果解释不如STFT直观而且小波基函数的选择有主观性——Morlet小波、墨西哥帽小波、Morse小波选哪个合适没有统一标准。我在实际项目中通常把小波当做第二意见STFT上看不清的信号再用CWT确认一下细节。2.3 Wigner-Ville分布高分辨率但不能直接用Wigner-Ville分布的理论分辨率是所有方法里最高的它不丢窗、不丢尺度直接对信号的瞬时自相关函数做傅里叶变换。理想情况下一个线性调频信号在WVD时频图上会显示为一条无限细的斜线效果非常漂亮。但代价也很明显交叉项。如果信号包含多个分量WVD结果中会出现各个分量之间的虚假能量项位置在两个真实分量之间的时间、频率中点附近幅度还很大经常把真实特征淹没。我试过用WVD分析两个频率接近的正弦叠加时频图上一片混乱跟水渍一样不仔细看根本分不清真伪。MATLAB主工具箱没有内置WVD需要用Time-Frequency Toolbox里的tfrwv函数或者自己根据定义写。我的建议是除非你分析的单分量信号且需要极致分辨率否则慎用WVD。如果非要用可以考虑加核函数的平滑伪WVDSPWVD来压制交叉项但它会牺牲分辨率算下来优势就没那么明显了。2.4 Hilbert-Huang变换HHT处理非线性非平稳信号HHT的核心分两步先用经验模态分解EMD把信号分解成若干个固有模态函数IMF再对各IMF做Hilbert变换求瞬时频率和瞬时幅值。它不像STFT和小波那样有固定的基函数而是完全数据自适应的所以对非线性、非平稳信号特别友好。MATLAB里emd函数在R2018a之后进入主工具箱可以直接调用。我拿它分析过一段包含间歇振荡的加速度信号EMD能把间歇成分分离成单独的IMF再对IMF做Hilbert谱时间-频率-幅值关系非常清楚这是STFT很难做到的。但HHT有几个固有的痛点EMD的分解结果受停止条件、端点效应影响大同一段信号不同参数设置可能得到不同IMF模态混叠问题也常见两个频率接近的成分容易混在一起瞬时频率对噪声敏感需要先做平滑。另外emd的计算速度比STFT慢很多不适合处理超长信号。我的定位是HHT适合短数据的精细分析不适合批量看数据概况。2.5 四种方法对比小结方法核心函数分辨率特点主要局限适用场景STFTspectrogram全平面固定分辨率窗口长度难取舍常规非平稳信号分析默认首选CWTcwt高频时间分辨率好低频频率分辨率好计算量大基函数选择主观频率跨度大的信号、突变检测WVD需第三方工具包理论分辨率最高交叉项严重单分量信号、理论分析HHTemd完全自适应分解不稳定、模态混叠非线性非平稳短信号选型没有绝对的对错我习惯先问自己三个问题信号有几个主要分量分量频率随时间怎么变我需要实时性还是离线精细分析答案不同选的方法就不同。3. 核心函数实操与关键参数解析3.1 spectrogram的窗口、重叠与FFT点数怎么定spectrogram函数的典型调用是[s, f, t] spectrogram(x, window, noverlap, nfft, fs)。前三个参数往往是影响结果最直接的因素我见过不少同学一把梭用默认值出来的图糊成一团还以为是数据问题。窗口长度决定频率分辨率和时间分辨率的平衡。物理上频率分辨率约等于fs除以窗长窗越长频率分辨率越高也就是两个相近频率越容易分开。但窗长了参与平均的时间范围就大时间分辨率变差突变时刻会被抹平。实际工程中我通常先把信号的大致频带范围估一下再反过来选窗长。比如采样率1000Hz需要区分间隔10Hz的两个峰那么窗长至少要1000/10100点实际取窗长的整数倍或者略大用128或256点比较稳妥。noverlap是相邻窗口的重叠点数。很多人忽略这个参数实际上它对时频图的平滑度影响非常明显。如果不重叠时间轴上每隔一个窗长取一列频谱图像会呈现明显的块状纹理像马赛克一样。增大重叠后相邻帧的谱互相交叉视觉上连续很多时间定位也更准。经验做法是取窗长的50%到75%比如256点的窗noverlap取128到192之间。超过75%之后计算量增加明显视觉改善却不大了。nfft是FFT点数。这里要注意nfft并不提高物理分辨率只是对频谱做频域插值让谱线更密、图像更精细。物理分辨率由窗长决定nfft再大也分不开原本重合的两个频率峰。一般取nfft为窗长的2到4倍即可比如窗长256nfft取512或1024既够精细又不会太慢。我见过有人把nfft设成32768的结果图是细腻了速度慢了好几个数量级物理分辨率一点没变纯属浪费。3.2 频率轴设置与时间刻度处理spectrogram的输出频率轴f在指定fs参数后范围是0到fs/2。实际使用时有时候信号主要能量集中在一个窄带内直接看全频段图会压扁细节这时候可以用xlim或ylim对频率轴做裁剪。比如分析旋转机械的振动信号关心10到500Hz频段就在画图时设置ylim([10 500])。时间轴的刻度默认是从0开始用输出变量t来标注。但如果你之前对信号做过切片、滤波、去趋势等预处理时间轴可能产生了偏移绘图时要注意做对应的坐标修正。我习惯在预处理阶段就保留原始时间戳数组画图时直接用它做横轴刻度避免事后对不上。另外颜色轴很重要。spectrogram返回的s是复数谱直接画有实部虚部的问题一般要画幅值谱或功率谱。早期版本我用imagesc搭配abs(s)后来发现直接指定输出类型power或psd更方便。颜色映射用colormap(jet)是最常见的但我自己更偏爱colormap(parula)MATLAB默认色系对色弱读者更友好而且没有jet那种高亮色块带来的视觉误导。颜色范围注意用clim旧版叫caxis控制否则默认的自动范围可能让低能量细节全部被压成背景色。3.3 cwt的尺度与小波选择新版cwt的调用格式cwt(x, fs)确实方便它会自动选择尺度矩阵和小波参数我日常快速看数据时就用这种偷懒写法。但如果要精细控制还是得了解尺度向量的作用。尺度对应频率尺度越小小波越窄对应频率越高尺度越大小波越宽对应频率越低。如果想要一个线性或对数分布的频率轴可以自己构造尺度向量再用cwt(x, scales, wavelet, fs)这样指定。需要注意的是尺度向量的范围决定了分析的最高和最低频率。下限太高会漏掉低频成分上限太低会丢失高频细节实际中可以先跑一版默认参数看频率范围再针对性地调整尺度。小波基函数的选择我用得最多的是Morlet小波也就是复值调制高斯小波。它对频率分析比较友好能得到平滑的时频曲线而且参数cmor1-1.5中的1和1.5分别代表中心频率和带宽直接影响小波在频域的宽度——带宽越大频率分辨率越差、时间分辨率越好跟STFT窗长的权衡一模一样。如果信号突变比较剧烈可以考虑用墨西哥帽小波它对跳变更敏感但频率轨迹看起来会粗一些。3.4 emd的调用和IMF选择emd函数用起来简单imf emd(x)返回一个矩阵每一行是一个IMF分量最后一列通常是残差。但实际分析中关键的步骤在于选哪几个IMF来解释物理过程。我的做法是先画出所有IMF的时域波形观察哪些IMF的幅值和信号主要成分量级吻合然后对关心的IMF做Hilbert变换用hht函数生成Hilbert谱。MATLAB的hht(imf, fs)函数我常用它会返回三个输出包括瞬时频率、瞬时幅值和Hilbert谱封装得很完整。要说避坑要点我提三个。第一做EMD前最好剔除信号中的直流分量和明显趋势项否则第一个IMF经常把趋势当成一个模态分解出来干扰后续分析第二端点效应是EMD的老毛病信号两端会出现发散的大幅摆动分析时最好先用padarray或信号延长工具处理端点或者舍弃靠近两端的时频谱段第三如果信号特别长先做分段EMD再拼接的切面包做法可以救急但每段之间的IMF不一定对齐拼接处会有跳变只适合大致观察能量分布不适合做精细量化。4. 一个完整应用案例从信号生成到时频图解读4.1 模拟一个带故障特征的非平稳信号为了把前面的方法串起来我构造一个模拟信号一个线性调频信号从50Hz扫到200Hz叠加一个120Hz的固定频率正弦再叠加一点白噪声。这种信号在工程上很常见——变频驱动设备的启动过程就类似振动频率随转速上升同时可能存在某个恒定频率的共振或电气特征。fs 1000; % 采样率 1000 Hz t 0:1/fs:10-1/fs; % 10 秒时问轴 N length(t); f0 50; f1 200; % 线性调频起止频率 x_chirp chirp(t, f0, t(end), f1); x_tone 0.3 * sin(2*pi*120*t); x_noise 0.05 * randn(1, N); x x_chirp x_tone x_noise;这个信号里既有随时间连续变化的成分又有恒定的分量非常适合演示不同时频分析方法的差异。如果直接用FFT你只会看到50到200Hz之间一大片连续谱峰和120Hz一条谱线无法知道chirp是从什么时间开始扫频的。4.2 用STFT绘制时频图并解读我对这个信号做STFT参数按前面讲的经验来选。频带范围是50到200Hz区分度要求不算苛刻窗长256点对应频率分辨率约3.9Hz足够分开120Hz和附近的chirp成分。重叠取75%让图像平滑nfft取1024做频域插值。window hann(256); noverlap 192; nfft 1024; [s, f, t_out] spectrogram(x, window, noverlap, nfft, fs); figure; imagesc(t_out, f, 10*log10(abs(s))); axis xy; xlabel(时间 (s)); ylabel(频率 (Hz)); title(STFT 时频图); colormap(parula); clim([-60 10]);我特意对幅值取了10*log10的单位转换因为原始信号有源有噪声直接画线性幅值时噪声底会显得很干净低能量成分看不见。转成dB后动态范围更清晰背景噪声基底也能看到。时频图上应该能看到一条从50Hz到200Hz的斜线那是chirp成分120Hz附近的水平亮线是固定正弦背景的蓝色基底就是噪声。解读时频图有个技巧先看背景基底判断噪声水平再看亮线轨迹判断主成分最后重点关注轨迹的突变和断裂。如果某个时刻轨迹突然加宽或断裂往往对应信号突变——这在故障诊断里非常有用比如轴承内圈故障特征频率会出现周期性间歇时频图上表现为水平亮线变细变亮、再消失的重复模式。4.3 用CWT看同一信号的分辨率差异同样一段信号我用cwt再跑一遍对比。新版cwt直接指定采样率即可[wt, f_cwt] cwt(x, fs); figure; imagesc(t_out, f_cwt, abs(wt)); axis xy; xlabel(时间 (s)); ylabel(频率 (Hz)); colormap(parula); clim([0 5]);CWT时频图上高频段的chirp轨迹会比STFT更细时间定位更精准但低频端轨迹会粗一些因为低频小波的时域宽度变大。这正是小波高频高时间分辨率、低频高频率分辨率特性的直观体现。用CWT时有个容易踩的坑注意纵轴的频率范围。新版cwt自动选择的频率上限到奈奎斯特频率fs/2有时候信号的高频段没什么能量纵轴还会拉到500Hz结果低频细节被压缩到很小一块区域。这时候建议用ylim把感兴趣的频段放大再画一版。我在实际项目里往往同一信号分别跑STFT和CWTSTFT看全貌CWT看细节两个图配合着解读效果最好。5. 常见问题与排查技巧实录5.1 时间分辨率和频率分辨率打架怎么办这是被问得最多的一个问题。有人想要同时看清一个短暂冲击的精确发生时刻和两个很接近的频率峰结果发现怎么调参数都做不到。这不是MATLAB的问题而是物理极限——窗越短越能定位时间但频率就越看不清。我的处理思路是分层分析先拿一个较短的窗跑STFT快速找到所有关注的事件时刻再对这些时刻周围做进一步细化用更长的窗分析片段把频率细节看清楚。比如先窗长128点跑全段发现0.5秒有个冲击再提取0.4到0.6秒的片段用512点窗长做精细频谱。这种粗看全貌、细看局部的两阶段法比试图用一个窗长解决所有问题高效得多。如果你确实需要同时获得好分辨率和好时间定位那就应该换CWT或HHT而不是在STFT参数里死磕。CWT在高频段天然时间分辨率好HHT对突变时刻的定位也比较准。记住一点方法选型和参数调优是一体两面的别非要拿锤子拧螺丝。5.2 边界效应和频谱泄漏STFT在做分段FFT时窗口边缘的数据被窗函数弱化了所以信号开头和结尾的时频图边缘常常颜色偏暗、能量偏低。CWT和EMD同样有端点问题只是表现形式不同。这不是代码bug是算法的固有特性。解决边界的常用办法有两个。一是对信号做延拓比如在信号两端各补一段镜像数据分析完再裁掉对应的时频图区域。MATLAB里wextend函数就是干这个的补镜像比补零效果好很多。二是在画图时直接裁剪边缘区域不显示时间轴最前最后大约半个窗长的范围。我自己在写分析程序时习惯把这两种方式都做成可选项默认用裁剪简单且不会引入虚假数据。频谱泄漏则是另一个老生常谈。如果信号频率不是FFT频率网格的整数倍能量就会泄漏到邻近频点表现为时频图上出现横纹。加窗能抑制泄漏但会加宽主瓣。汉宁窗是折中方案频率精度和泄漏抑制都比较均衡如果信号里有个很强的单频分量可以考虑用布莱克曼窗或凯塞窗旁瓣抑制更彻底但主瓣更宽。这个取舍没有标准答案我一般按先看有没有泄漏横纹再看有没有需要分辨的相近频率来选窗。5.3 大数据量时频分析太慢怎么优化实测数据动不动几百万甚至上千万点直接跑spectrogram确实卡到怀疑人生。我踩过不少坑最后总结出几条实用经验。第一降采样。很多传感器采样率远高于信号本身的最高频率比如振动信号采样率50kHz但关注频带只有0到5kHz直接降采样到fs_new12500Hz就够了。一次降采样计算量立刻减少到原来的四分之一甚至更多。降采样前记得抗混叠滤波MATLAB里resample函数自带FIR抗混叠滤波比直接x(1:4:end)安全得多。第二分块加并行。如果你的信号必须保留全部分辨率考虑用stft函数配合for循环分帧处理。spectrogram函数本身很高效但一次处理整个信号内存吃紧。实测下来把信号切成长度20万点左右的块每块独立跑spectrogram再把结果按时间顺序拼接速度和内存占用都更可控。拼接时注意每块重叠区域的处理别让块边界在时频图上留下接缝。MATLAB的parfor在这里能派上用场但多块之间的调度开销有时候会吃掉并行收益数据量特别大时才值得用。第三降低时间输出密度。noverlap设小一点时间轴的分辨率会降低但图像点数减少、显示速度加快。如果最终只看趋势不必追求过高的时间精度。5.4 时频图的颜色映射与可视化细节同样的数据不同的颜色映射画出来视觉结论可能天差地别。默认jet的高饱和颜色容易让人把噪声当信号我一般优先用parula或viridis这类顺序色图。如果特意要突出高能量区域可以设置clim的下限把低能量区域压成同一色形成能量阈值效果。还有一个小细节imagesc默认的坐标轴是y轴向下画时频图时注意用axis xy翻转过来否则纵轴会从高频到低频倒着排列看久了容易出错。输出高分辨率图片时print(fig, -dpng, -r300)会比直接截图好很多。如果你要把时频图嵌入论文或报告建议把FontSize调到14以上TickDir设成out图片里的坐标字才不会被缩小到看不清。6. 把时频分析做成应用程序的几个经验6.1 用App Designer封装分析流程分析做出来后如果只在自己手里跑脚本每次都要改参数、改路径效率太低。我在项目后期通常会把时频分析封装成一个小工具用MATLAB App Designer做交互界面加载数据、选参数、看时频图都在一个窗口里完成。封装时的关键不是界面多花哨而是把参数做成可配置且带默认值的。比如窗长、重叠率、nfft、频率范围上下限都设置成输入框并给一组合理的默认值。这样既能快速跑通也能在需要精细分析时调整。另一个要点是做好数据导出一键把时频矩阵s、频率轴f、时间轴t保存成.mat文件后续做量化分析和统计就方便了。6.2 批处理里的常见坑批量处理一批文件时最怕每个文件的最优参数不一样。我的经验是先用一个典型文件确定参数模板再用模板批量跑。如果某些文件质量差比如有效数据太短或噪声过大程序里要加异常判断数据长度小于某个阈值时跳过信噪比太低时给出警告并跳过。这样批处理跑完输出一个汇总表哪些文件成功、哪些文件被跳过一目了然。还有一个容易忽略的坑不同型号传感器的采样率可能不同。批量处理时fs应该从数据文件的元信息里读取而不是写死。我见过有人因为fs写错所有频率轴全部偏移分析成果全部作废。批量脚本里务必把fs作为输入参数并在每个文件的输出图标题里标出fs方便复核。6.3 代码组织与复用建议我把时频分析相关的代码按功能拆成函数信号预处理、参数计算、时频分析、画图、结果导出各一个文件。这样做最大的好处是换新项目时只要改参数调用不用重新设计流程。比如我维护了一套getOptimalWindowLength函数根据信号采样率和目标频率分辨率自动推荐窗长省去了每次手动算的麻烦。对于需要交付给同事或客户使用的工具建议把核心分析逻辑封装成统一入口函数输出标准化结构体。我习惯用结构体组织输出包含s、f、t、method、params这些字段后续无论是画图、存文件还是喂给机器学习分类器都从同一个结构体取数据代码风格统一也方便别人接手。6.4 最后的个人体会做了几年时频分析我最大的体会是方法本身并不难难的是理解信号背后的物理过程。同样一幅时频图有人只看到花花绿绿的色块有人能看出设备的故障模式差别就在于对信号来源的理解深度。所以每次分析前我建议先问清楚数据怎么来的、关注什么频段、什么时间尺度、判据是什么再决定用STFT、CWT还是HHT。参数调优的过程记录也值得保留下来下次遇到类似信号直接套用能少走很多弯路。如果你刚接触时频分析我的建议是从STFT入手把spectrogram的各个参数吃透对照已知信号比如chirp加正弦把时频图看出来再逐步尝试CWT和HHT。先跑通再追求复杂度这条路是最稳的。本文还有配套的精品资源点击获取