☰
轴承故障时频图调参:先定窗口尺度,再选CWT小波
2026/10/27 2:14:07 网站建设 项目流程

简介:围绕凯斯西储大学轴承故障数据集展开的时频分析文档,面向机械故障诊断与信号处理方向的学习者和研究人员,帮助理解轴承振动信号的故障特征提取方法。文档共1个docx文件,压缩包约1.05MB,内容集中于数据集说明、短时傅里叶变换与连续小波变换三部分,可作为课程设计、论文复现或实验入门的参考资料。正文梳理驱动端、风扇端和基座振动信号的差异,并以正常及内圈、滚珠、外圈故障数据为例,对比STFT在重叠比例0.5下尺度16、32、64的时频图,讨论时间分辨率与频率分辨率的权衡。连续小波部分比较morl、cmor1-1、cmor1.5-2、cgau8等小波函数及32至256尺度的效果,说明参数选择依据,并附Python代码片段,便于对照理解数据读取、时频图绘制与故障辨识度评价。已有1582人学习。

1. 先定小波还是先定尺度:一份基准数据集的时频图踩坑顺序

调时频图这件事,我一开始的顺序是反的:拿到这份公开的轴承故障基准数据集,第一反应是去找“最好的小波”,把 morl、cmor1-1、cmor1.5-2、cgau8 轮着换了一遍,图看着都差不多,于是怀疑数据有问题。后来把窗口尺度摊开来对比才发现,真正决定四类故障能不能分开的是窗口长度和尺度序列,小波函数只排在第二位。正确的顺序是:先定通道和截窗长度,再用 STFT 把尺度试出来,然后用 CWT 挑小波,最后回头校一次尺度序列。这篇就按这个顺序,把每一步的判据、能直接跑的代码和翻过的车写清楚,给同样要出轴承振动时频图的人抄作业。

2. 摸清手里的数据:通道、截窗、字典读取

2.1 三向加速度计决定你该读哪一路

这份基准数据集的台架结构很朴素:一台 1.5 kW(2 马力)电机,中间接扭矩传感器和译码器,右侧挂功率测试计,另有电子控制器。加速度计贴在三个位置——电机壳体驱动端、风扇端、基座。这三路数据不是冗余备份,物理含义完全不同:驱动端主要吃转子和传动链的激励,轴承的内圈、外圈、滚动体故障特征在这一路最干净;风扇端更多反映风扇叶片失衡、风扇轴承一类的异常;基座拿到的是整机振动叠加,不平衡、底座松动这类问题在这里更容易看出来。

我一般这么选:做轴承单点故障辨识,优先驱动端;做整机状态监测或者多传感器融合,三路一起上。新手最容易犯的错是把三路当成“多测几次取平均”,结果把驱动端那条最有辨识度的冲击序列稀释掉了。

通道在数据文件里的体现是键名后缀,驱动端加速度数据一般带DE,风扇端带FE,基座带BA。读之前先print(data.keys())看一眼,别硬编码猜键名。

2.2 截窗 1024 点不是随口定的

原始记录动辄十几万点,直接整段做时频变换,出来的图横向拉到几十厘米,冲击串挤成一条线,什么都看不出来。工程上常规做法是滑窗切片,每片 1024 点,这也是这类数据集论文里最常见的取法。

1024 点的物理含义要按采样率换算。驱动端常见的是 12 kHz 采样(也有 48 kHz 批次,动手前先确认文件对应的采样率,别默认)。12 kHz 下 1024 点约 85 ms,按 1797 r/min 折算转频约 30 Hz,一个窗口里能装下两三个旋转周期,内圈故障的冲击重复间隔也能露出好几次——这个长度刚好够“看见冲击串”,又不会把不同工况混进同一张图。

提示:截窗长度直接决定后面尺度的可选范围。窗口只有 1024 点,nperseg设到 512 以上时每张图的时间帧所剩无几,横向基本没有分辨率可言。

2.3 读文件和 reshape 的两个细节

scipy.io.loadmat返回的是字典,不是数组。有人直接拿返回值去plot,报一串错,卡半天。

import numpy as np from scipy.io import loadmat import matplotlib.pyplot as plt import matplotlib matplotlib.rc("font", family='Microsoft YaHei') # 中文标题不乱码 # loadmat 返回 dict,键名形如 X***_DE_time normal = loadmat('0_0.mat')['X097_DE_time'].reshape(-1) # 正常基线 inner = loadmat('21_1.mat')['X209_DE_time'].reshape(-1) # 0.021 英寸 内圈 ball = loadmat('21_2.mat')['X222_DE_time'].reshape(-1) # 0.021 英寸 滚动体 outer = loadmat('21_3.mat')['X234_DE_time'].reshape(-1) # 0.021 英寸 外圈 # 统一截 1024 点,四类样本对齐后再画 WIN = 1024 normal, inner, ball, outer = [x[:WIN] for x in (normal, inner, ball, outer)] fig, axes = plt.subplots(2, 2, figsize=(20, 10)) for ax, sig, name in zip(axes.ravel(), (normal, inner, ball, outer), ('正常', '内圈', '滚动体', '外圈')): ax.plot(sig) ax.set_title(name) plt.tight_layout() plt.show()

reshape(-1)的作用是把 loadmat 读出来的列向量拉平成一维,不然stft和pywt.cwt会按二维处理,直接翻车。文件名里的21_1 / 21_2 / 21_3对应 0.021 英寸故障直径下的内圈、滚动体、外圈,同一故障尺寸横向对比才有意义;跨尺寸(0.007 / 0.014 / 0.021 英寸)对比是另一个实验设计,别混在一张图里。

这一段跑完,先看时域波形。四张波形图如果长得几乎一样,先别急着上时频,多半是通道选错或者截窗落在无冲击段,这一点在第 5 章还会展开。

3. STFT 窗口尺度:16、32、64、128 四张图怎么比出 32

3.1 尺度换的到底是什么

短时傅里叶变换的思路很直白:拿一个窗函数在时间轴上滑,滑到哪就在哪一小段上做傅里叶变换,于是频谱有了时间标签。窗口长度nperseg就是那个“尺度”。

它是硬约束,不是玄学:频率分辨率约等于fs / nperseg,时间分辨率约等于nperseg / fs。窗口越长,频率分得越细但时间上糊;窗口越短,时间上锐利但频率糊成一片。重叠比例控制滑动步长,取 0.5 意味着每步移动半个窗口,是兼顾计算量和图面平滑的常见折中。

按 12 kHz 采样折算,四个尺度对应的指标如下:

nperseg频率分辨率时间分辨率一张图看得见什么
16约 750 Hz约 1.33 ms冲击时刻很锐,但故障特征频率的谐波全糊在一起
32约 375 Hz约 2.67 ms冲击串和频带位置都能看,四类故障可区分
64约 187 Hz约 5.33 ms频带更清楚,但冲击间隔开始粘连
128约 94 Hz约 10.67 ms频带最细,1024 点窗口下时间帧太少,横向几乎没信息

3.2 判读的顺序:先看冲击串,再看频带

看这类图有个固定顺序,我一般这么走:第一步横向看有没有周期性竖直亮条,那就是冲击;第二步量亮条间隔,换算成冲击重复频率;第三步纵向看能量集中在哪个频带,内圈和外圈的能量分布位置通常不同;第四步才是横向比对四类故障图,看能不能一眼分开。

16 点窗口的问题在于第二步做不了——竖条够锐,但纵向频带糊成一坨,量不出中心频率。128 点窗口的问题在第一步——频带漂亮,但横向帧太少,冲击串被抹平。项目里最后定在 32,理由就是这个折中位置:冲击串清楚,频带位置也能读。

这里有个真实的矛盾要摊开说:如果采样率确实是 12 kHz,375 Hz 的频率分辨率其实压不住内圈故障特征频率的谐波间隔(基频在 160 Hz 上下),理论上 64 点更稳。选 32 的隐含前提是“图面可读性优先、后续靠分类器补”,不是“32 在频率上最优”。做诊断实验和做论文插图,这两个目标的答案可以不一样,别把结论当定理。

3.3 可复现的 STFT 对比代码

from scipy.signal import stft def stft_compare(sig, win, fs=12000, overlap=0.5): """win: 窗口点数; overlap: 重叠比例""" noverlap = int(win * overlap) # 重叠样本数,不是比例 f, t, Z = stft(sig, fs=fs, nperseg=win, noverlap=noverlap) return f, t, np.abs(Z) fig, axes = plt.subplots(2, 2, figsize=(20, 10), dpi=100) for ax, win in zip(axes.ravel(), (16, 32, 64, 128)): f, t, mag = stft_compare(inner, win) # pcolormesh 比 imshow 更适合非均匀坐标轴 ax.pcolormesh(t * 1000, f, mag, shading='gouraud', cmap='jet') ax.set_title(f'内圈 nperseg={win}') ax.set_xlabel('时间 / ms') ax.set_ylabel('频率 / Hz') plt.tight_layout() plt.show()

几个参数容易被写错。noverlap要传样本数,不是 0.5 这个比例,所以必须先int(win * overlap)算一遍;fs一定要显式传,不传的话频率轴变成 0~0.5 的归一化值,图上标的“Hz”其实不是 Hz;shading='gouraud'让色块过渡平滑,不加的话低分辨率下会出现明显的马赛克格。还有个小细节——循环里用变量win拼标题,标题和参数永远一致;手写死了标题的代码,改了参数忘记改标题,看图的人会被误导,这种坑我踩过不止一次。

判读的时候把四张图放同一色标下比。pcolormesh默认每张图独立归一化,视觉上“看起来差不多”,实际能量差一个数量级。要横向比就得统一vmin/vmax,或者对幅度取np.log1p再画,压缩动态范围后冲击串更显眼。

4. CWT 小波函数:morl、cmor1-1、cmor1.5-2、cgau8 怎么挑

4.1 复数小波为什么在包络上更好看

连续小波变换把小波函数按尺度伸缩、按时间平移,和信号做内积,得到一张尺度-时间图(纵轴换成频率也行)。和 STFT 的固定窗口不同,小波在高频用短窗、低频用长窗,天然适配振动信号这种“高频冲击 + 低频调制”的结构。

小波函数的差别主要在两点:是不是复数、中心频率和带宽怎么配。复数小波(cmor、cgau)返回的是复数系数,取模之后相当于同时拿到包络信息,冲击的幅度变化更平滑;实数小波(morl)只给实部,相位信息丢了,图上容易出现正负交替的条纹,读起来更费劲。项目里拿morl、cmor1-1、cmor1.5-2、cgau8四个做对比,尺度统一设成 128,这个设计是对的——只变一个变量。

cmor1.5-2的命名规则值得记一下:cmor是复 Morlet,前面的1.5是带宽参数,后面的2是中心频率参数。带宽越大,时域包络越窄、频率选择性越差;中心频率越高,同一尺度下对应到频率轴的位置越靠上。cmor1-1和cmor1.5-2的差别就在这两个数的配比上,出图效果差异往往比“换一个完全不相关的小波”更微妙,得盯着低频段看。

4.2cparam = 2 * fc * totalscal这行的来历

这段代码里最容易被抄错又最不该抄错的就是这一行。pywt.cwt的第二个参数要的是尺度序列scales,不是“我要 128 个尺度”这个数字。尺度序列和实际频率的换算关系是f = fc / (scale * sampling_period),反过来推,要让尺度覆盖到目标频段,就得先把尺度按cparam / arange(totalscal, 0, -1)生成,cparam里带上中心频率fc和总尺度数。

import pywt import numpy as np fs = 12000 # 采样率,必须和你手上的文件一致 sampling_period = 1.0 / fs # 注意:传的是周期,不是频率 totalscal = 128 # 尺度个数 wavenames = ['cgau8', 'morl', 'cmor1-1', 'cmor1.5-2'] fig, axes = plt.subplots(2, 2, figsize=(20, 10)) for ax, wn in zip(axes.ravel(), wavenames): fc = pywt.central_frequency(wn) # 每个小波的中心频率不同,必须各自算 cparam = 2 * fc * totalscal # 尺度序列常数项 scales = cparam / np.arange(totalscal, 0, -1) # 递减生成,保证频率轴单调 coeffs, freqs = pywt.cwt(inner, scales, wn, sampling_period=sampling_period) ax.contourf(np.arange(len(inner)) / fs, freqs, np.abs(coeffs), 100, cmap='jet') ax.set_title(f'内圈 - {wn}') ax.set_xlabel('时间 / s') ax.set_ylabel('频率 / Hz') plt.tight_layout() plt.show()

np.arange(totalscal, 0, -1)是从大到小生成,因为尺度大对应频率低,这样freqs才是递增的,画出来纵轴方向才对。central_frequency必须对每个小波单独调用,不同小波的fc不一样,直接共用一个值,频率轴整体偏移,图上看不出问题但量出来的特征频率全错。

四个小波在 128 尺度下的实际表现,按我这边出图的观感整理:

小波类型时频局部化四类故障辨识度主要问题
cgau8复数高斯时域很窄,频率上拖尾长高,冲击起跳点很锐低频段能量发虚,背景噪声容易被放大
morl实 Morlet均衡中,正负条纹干扰判读相位信息丢失,包络不连续
cmor1-1复 Morlet(窄带)频率选择性强偏低,四类图差异不明显时域太宽,冲击被抹平
cmor1.5-2复 Morlet(宽带)时域收得住高,频带位置和冲击串都在低频细节不如大尺度配置

结论取cmor1.5-2,理由和项目一致:cgau8的辨识度也高,但低频段发虚,做低频调制分析时不利;cmor1.5-2在高频冲击和低频调制之间更平衡。这个选择没有绝对对错,取决于你后面要提取什么特征——做冲击时刻检测,cgau8反而更顺手。

4.3 尺度序列在小波里的作用比 STFT 更直接

STFT 里换窗口,时间和频率是一对一的硬交换;小波里换尺度序列,改变的是整张图的频率采样分布。尺度数从 128 提到 256,不是“图更清楚”,而是低频段被采样得更密——这一点在第 6 章用实测图说话。

5. 排查:时频图看着像噪声时的五个检查点

5.1 图整片深蓝或整片红,看不出结构

现象:contourf或pcolormesh出来一片平色,只有零星亮点。原因:小波系数或 STFT 幅度里有离群极值,色标被拉到极高,正常区域全被压到低端。解决:画之前对幅度做np.log1p,或者手动设vmin/vmax,取np.percentile(mag, 99)当上限。这一步不做,再好的参数也白搭。

5.2 换了小波,图几乎没变化

现象:四个小波轮着跑,出图看起来一模一样。原因:scales序列是用同一个totalscal和近似的fc生成的,尺度覆盖范围太窄,几个小波的有效差异区被裁掉了。解决:把totalscal提到 256 再比一次,同时打印每个小波的freqs首尾值,确认频率轴真的不一样。如果频率轴范围相同但图形相同,检查是不是循环里变量没跟着换。

5.3 CWT 直接报 NameError

现象:跑到pywt.cwt(data_list2, scales2, wavename2, sampling_period)这一行报name 'fc2' is not defined或者sampling_period未定义。原因:中心频率那行被注释掉了,或者采样周期变量只在别的分支里定义过。解决:每个小波分支都完整写fc = pywt.central_frequency(wn),sampling_period = 1.0 / fs放在循环外统一定义。这类错误和算法无关,纯粹是复制粘贴的代价。

5.4 频率轴是 0 到 0.5,不是 0 到几千 Hz

现象:纵轴刻度只有 0~0.5。原因:sampling_period没传,或者传成了采样频率。pywt.cwt的第四个位置参数是采样周期,传12000进去等于周期 12000 秒。解决:显式用关键字sampling_period=1.0/fs,别用位置参数,一眼就能看出单位对不对。

5.5 四类故障的时频图长得几乎一样

现象:正常、内圈、滚动体、外圈四张图肉眼分不出差别。原因有三个常见来源:通道选错(读了风扇端却当成驱动端分析)、截窗落在无冲击段(前 1024 点恰好是平稳段)、频带没对齐(四张图各自独立归一化)。解决:先回时域波形确认冲击存在,统一色标范围,再确认四类样本来自同一故障尺寸、同一负载工况。四类图必须同条件对比,跨工况混在一起,看不出差别是正常的。

6. 把尺度摊开看:32、64、128、256 的实测与固定动作

6.1 大小尺度各自盯着什么

用cmor1.5-2固定小波,只改totalscal,取 32、64、128、256 四档,同一段滚动体故障信号。出图后我的观察是:尺度数越大,图底部那条低频带被采样得越密,低频调制结构越清楚;高频段的竖直冲击条反而在小尺度数下更锐利,因为同样的尺度序列长度被分配到了更宽的频率范围上,高频分到的采样点更多。

这就印证了小波的双尺度性质,落到操作上是两条规则:想看清高频冲击的起跳时刻,尺度数往下调;想看清低频调制和边频带,尺度数往上调。128 是在 1024 点截窗下的舒适区,再往上加,图会变厚但可读信息不增。

尺度数低频段表现高频段表现适用目标
32底部频带粗,调制看不清冲击条最锐冲击时刻检测
64低频开始分层冲击条仍清楚快速巡检
128低频调制可读冲击条适中四类故障分类对比
256低频分层最细冲击条变钝边频带与调制分析

6.2 我现在每次都走的一遍流程

参数定了之后,我把这套流程固化成固定动作,省得每次重新纠结:

步骤动作判据
1打印字典键名,确认通道后缀键名带 DE / FE / BA
2画四类时域波形,截 1024 点冲击串肉眼可见
3STFT 扫 16/32/64/128,统一色标冲击间隔可量、频带可读
4CWT 扫四个小波,totalscal=128四类图能一眼分开
5固定小波,扫 32/64/128/256按目标选尺度数
6记录参数组合,写进实验日志别人能复现

第 6 步最容易被跳过。参数组合记不下来,一周后回头看自己的图,完全不知道当时用的什么窗口、什么尺度、采样率假设是多少,只能从头再跑一遍。这种事我干过不止一次,后悔药没处买。

6.3 一个能省半小时的技巧

如果你只是想快速判断“这份数据能不能分出四类故障”,别急着做完整对比。先固定nperseg=32的 STFT,把四类信号的图放同一vmin/vmax下横排,看三件事:冲击条的存在与否、冲击间隔的差异、能量集中的频带位置。这三件事里只要有两件能分开,后面的 CWT 细化就有意义;三件全都一样,问题在数据侧不在参数侧,回去查通道和工况。

回过头看,这套流程里真正值钱的不是哪个具体参数,而是顺序:先确认数据、再定尺度、后挑小波、最后记录。我最早反过来做,先花两天研究小波函数的数学性质,图还是看不清;顺序理顺之后,同样的活半天就出结果。从那以后我每次动时频分析,都强制先把时域波形和通道确认走一遍,再谈变换参数。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询