ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

小波时频图与Swin Transformer的轴承故障诊断实战指南

小波时频图与Swin Transformer的轴承故障诊断实战指南 简介一份基于小波时频图WTFP与移位窗口视觉TransformerST的轴承故障诊断完整项目实例面向具备Python与深度学习基础的研究生、科研人员及工业运维工程师重点解决非平稳振动与工况变化下传统诊断方法性能不佳的问题。项目从原始振动信号切片与标准化出发经连续小波变换生成二维时频图利用复Morlet小波提取多分辨率特征并保留故障冲击信息随后以移位窗口视觉Transformer为核心分类网络通过局部窗口注意力降低计算复杂度结合窗口移位实现跨区域特征交互构建了含WTFP生成、数据增强、模型训练、测试评估的端到端链路同时涵盖GUI设计及部署应用包括GPU推理、服务接口与监控维护。资源包内共1个docx文档约147KB内含完整Python程序代码与代码详解并分模块讲述项目背景、挑战与解决方案、模型架构、代码示例及应用领域。目前已有63人学习浏览适合风力发电、工业电机、数控机床等领域的轴承故障诊断与智能运维系统原型开发。1. 轴承故障诊断项目里小波时频图 移位窗口视觉 Transformer 这条路线为什么值得做做轴承故障诊断项目时把振动信号先画成小波时频图wtfpWavelet Time-Frequency Plot再交给移位窗口视觉 TransformerST也就是 Swin Transformer分类是 Python 方案里少有的「信号处理 深度学习 桌面工具」一条龙路线。传统做法要先手工算均方根、峭度、包络谱特征设计得好不好直接决定准确率而时频图把一维振动信号变成二维图像同时保留时间和频率两个维度正好把故障冲击的细节交给深度网络去学。这套方案适合手里有振动数据、不想在特征工程上反复折腾、又希望最终交付一个带 GUI 的诊断工具的工程人员。下面从信号怎么变成图讲起一路写到模型训练、GUI 集成和最容易翻车的位置。2. 用 Python 生成小波时频图wtfpCWT 参数怎么设才能看清故障冲击2.1 短时傅里叶变换做不好这件事窗口固定是硬伤轴承故障信号本质是周期性瞬态冲击叠加高频共振冲击发生的时刻很短暂共振频带又相对集中。短时傅里叶变换STFT只有一个固定窗口窗短了频率分辨率差窗长了时间分辨率差无论怎么调都很难同时看清「冲击在哪一刻出现」和「冲击激励起了哪个频带」。连续小波变换CWT的思路是让母小波做伸缩和平移分析低频成分时把尺度拉大用较长的时窗换频率分辨率分析高频冲击时把尺度缩小在时间上定位更准。这种多分辨率特性正好匹配轴承故障信号里「宽频冲击 窄带共振」的结构。实际项目里很多团队一开始图省事直接算 STFT 谱图送到模型里也能跑出不错的准确率但换到负载变化较大的数据上往往扛不住根源就是 STFT 把瞬态冲击的时间信息抹平了。这条路线里还有一个隐藏收益CWT 是线性变换不引入交叉项干扰不像 Wigner-Ville 分布在多分量信号上会出现虚假能量生成的时频图特征更「干净」。代价是计算量比 STFT 大不少但现代 CPU 处理几万点一段信号也就是几十毫秒放到训练流程里完全可接受。2.2 复小波 cmor3-3 与尺度范围的选型理由小波函数选什么直接影响时频图长什么样。常用选择是复 Morlet 小波PyWavelets 里的名字是cmorB-CB是带宽C是中心频率。cmor3-3是复 Morlet 的一个常见配置带宽为 3中心频率为 3。复小波能同时给出幅度和相位计算出来的时频图纹理比实小波比如morl、mexh更丰富对模型学习更友好。实小波丢掉相位信息后同一个故障在正半周和负半周的冲击会互相抵消图上容易出现空洞这个坑不踩一次很难体会。尺度范围和采样率必须一起看。CWT 的伪频率大约等于母小波中心频率乘以采样率再除以尺度所以尺度从 1 往上取时覆盖的频率是从高到低递减的。一般做法是先扫一遍 1 到 256 的尺度画出图看一眼能量集中的频带再截取一个合适范围。做 12 kHz 采样率的 CWRU 轴承数据时我一般取 128 个尺度按对数间隔均匀分布既不会漏掉低频调制成分也控制住了特征矩阵大小。尺度太多会让时频图长宽比失衡深度网络输入还得缩放反而浪费显存。2.3 把振动信号转成 wtfp 的 Python 代码最小可用实现import numpy as np import pywt def signal_to_wtfp(signal, fs12000, waveletcmor3-3, scalesNone): 把一维振动信号转成二维小波时频图。 Args: signal: 一维 numpy 数组振动加速度数据。 fs: 采样率HzCWT 伪频率折算必须用它。 wavelet: pywt 小波名常见 cmor3-3。 scales: 尺度数组默认 1~128。 Returns: wtfp: 归一化到 0~1 的二维功率谱图shape (len(scales), len(signal))。 if scales is None: scales np.linspace(1, 128, 128) coef, freqs pywt.cwt(signal, scales, wavelet, sampling_period1.0 / fs) power np.abs(coef) ** 2 power_db 10.0 * np.log10(power 1e-12) power_db (power_db - power_db.min()) / (power_db.max() - power_db.min() 1e-8) return power_db.astype(float32), freqs逻辑说明pywt.cwt返回复数小波系数和对应的伪频率序列。先对系数取模求功率再取对数压缩动态范围最后做 min-max 归一化。如果不做对数压缩强共振频带会把弱故障成分直接压成黑色模型只能学到「有没有共振」学不到「共振里藏着什么故障」。参数说明sampling_period1.0/fs必须传否则伪频率是按归一化频率算的换了采样率后同一张图对应的物理频率完全对不上。scales数组越密图的纵向分辨率越高但计算耗时线性增长128 是个性价比很高的默认值。1e-12是防止 log 里出现零值的保护项换成1e-8也行但别去掉。绘图时还有一个很常见的观感问题就是横轴时间刻度太密、纵轴频率刻度挤成一团。用 matplotlib 的MaxNLocator可以限制定点数量import matplotlib.pyplot as plt from matplotlib.ticker import MaxNLocator wtfp, freqs signal_to_wtfp(signal) plt.figure(figsize(8, 4)) plt.imshow(wtfp, aspectauto, cmapjet, extent[0, len(signal) / fs, freqs[-1], freqs[0]]) plt.gca().yaxis.set_major_locator(MaxNLocator(nbins6)) plt.xlabel(时间 (s)) plt.ylabel(频率 (Hz)) plt.colorbar(label归一化功率) plt.tight_layout() plt.savefig(wtfp_sample.png, dpi150)这段的重点是extent参数前两个是横轴起止时间后两个是纵轴起止频率。因为尺度从小到大对应频率从高到低所以纵轴下限填freqs[-1]、上限填freqs[0]。注意如果在训练管线里直接喂数组而不是存图片就不要走savefig再读图这条路中间多一次 JPEG 压缩会损失小能量纹理等于自己引入了噪声。3. 移位窗口视觉 TransformerST在时频图上看到的是什么3.1 Swin 的 W-MSA 和 SW-MSA窗口注意力为什么适合局部纹理Swin Transformer 的核心设计是两阶段交替的窗口自注意力。第一步把输入图像切成不重叠的小窗口在每个窗口内部做标准的自注意力计算这一步叫 W-MSA下一步把窗口整体移位半个窗口大小重新划分窗口后再做一次自注意力叫 SW-MSA。这么做的直接好处是计算复杂度从整图注意力的平方量级降成与图像尺寸线性相关同时移位窗口让相邻窗口边界的像素有机会互相感知信息不会一直被锁在局部。放到轴承时频图这个场景里故障特征其实是一小块一小块的周期纹理内圈故障在图上表现为近似等间隔的竖直亮条带外圈故障也有周期性分布但相位和调制方式和内圈不同。窗口自注意力天然适合提取这种局部周期性结构因为注意力机制可以直接建模「这个亮条和上一个亮条相隔多远」而不是像卷积那样靠堆层数慢慢扩大感受野。3.2 为什么不直接用 ResNet卷积归纳偏置与时频纹理的错位对比维度ResNet50Swin-TST基础操作3×3 卷积堆叠窗口内自注意力 移位窗口建模范围依赖层数堆叠扩大感受野局部窗口注意力 跨窗口传播对周期性纹理能提取但需要较深网络注意力直接比较窗口内任意两个位置参数量约 25M约 28M调参难度低收敛稳定对学习率和数据量更敏感ResNet 在图像分类上非常成熟但它的卷积核尺寸固定想看到「隔一段距离的另一个冲击条」必须靠深层特征逐层传递。Swin 的窗口注意力在同一个窗口内可以直接比较任意两个 patch 的关系对周期、间隔这类特征更敏感。一批实验里常见的现象是数据量足够时两者准确率都能到 95% 以上但换到低负载工况或噪声更强的数据上Swin 的下降幅度明显更小。代价是训练时更挑学习率收敛也慢一些后面第 4 章会专门讲训练配置。3.3 用 timm 搭一个 Swin-T 分类模型Python 代码与参数说明import timm import torch.nn as nn def build_swin_model(num_classes4, img_size224, pretrainedFalse): model timm.create_model( swin_tiny_patch4_window7_224, pretrainedpretrained, num_classesnum_classes, img_sizeimg_size ) return model逻辑说明timm库把 Swin-T 的完整结构封装好了模型名swin_tiny_patch4_window7_224表示了关键配置序列patch size 为 4×4、注意力窗口为 7×7、输入尺寸为 224×224。num_classes4对应正常、内圈故障、外圈故障、滚动体故障四分类如果按故障直径细分这个数字改成对应类别数即可。参数说明pretrained一般设False。ImageNet 上预训练的权重学的是自然图像纹理时频图是温度图风格的能量分布两者分布差异很大迁移收益有限。只有当你有大量同分布无标注时频图可以做自监督预训练时才值得考虑预训练权重。img_size建议保持 224这是模型默认值减小到 192 虽然省显存但窗口划分的边界条件会变效果可能要重新调。timm创建的模型默认带一层分类头显存不够可以换swin_small_patch4_window7_224但轴承故障诊断用 Tiny 通常已经够用。重点提醒拿到模型后先打印一下结构确认最后分类头的输入维度是 768Swin-T 的 stage 4 输出维度免得到处找维度不匹配的问题。4. 训练一个能用的轴承故障诊断模型数据划分与训练配置4.1 数据泄漏是最大的坑用 GroupShuffleSplit 按信号分组切分训练数据怎么划分往往比选什么模型更能决定测试集分数的真实性。轴承振动数据的常规做法是把长信号按滑动窗口切段窗口长度取 2048 点步长取 512 点每段送进前置处理生成时频图。问题就出在这同一条原始信号切出来的相邻窗口重叠部分高达 75%如果随机打乱后划分训练集和验证集同一个原始信号的窗口一部分进了训练、一部分进了验证集模型等于提前看到了答案。验证集准确率虚高到接近 100%一上现场就崩。解决办法是按信号来源分组划分。CWRU 这类公开数据集中每个.mat文件是一条独立的长信号给每个样本记录一个sample_id用分组划分保证同一条信号的窗口不会跨集合import numpy as np from sklearn.model_selection import GroupShuffleSplit def split_by_source(sample_ids, labels, test_size0.25, seed42): gss GroupShuffleSplit(n_splits1, test_sizetest_size, random_stateseed) train_idx, val_idx next(gss.split(sample_ids, labels, groupssample_ids)) return train_idx, val_idx逻辑说明GroupShuffleSplit的groups参数传样本来源 ID划分时会把同一组的样本全部放进同一侧从机制上杜绝窗口间数据泄漏。next()取第一折因为这里只需要一次划分。验证集只是用来调参真正的最终评估要用独立采集的另一批数据这点在第 6 章再展开。参数说明test_size取 0.25 是经验值四分类问题这个比例足够稳定。random_state42固定下来保证多次实验可以复现。注意不要用train_test_split替代它不认groups这是最容易踩的坑。4.2 训练配置速查表Swin 不按 ResNet 那套调参配置项推荐值原因优化器AdamW配合 weight decay 效果稳定初始学习率5e-4Swin 从头训练比 ResNet 更怕大学习率weight decay0.05抑制过拟合Swin 默认配置batch size32视显存调整太小会放大训练波动学习率调度CosineAnnealing收敛后期更平滑早停 patience10 epoch防止过拟合学习率是 Swin 训练里最「玄学」的变量。用 1e-3 跑 ResNet 可能没事换到 Swin 上 loss 直接飞掉低于 1e-4 又收敛太慢。建议先用 5e-4 跑 10 个 epoch 观察 loss 曲线如果前期震荡剧烈就降一半。batch size 和图像尺寸直接决定显存占用224 输入、batch 32 大概需要 12 GB 显存不够就先降 batch优先保输入分辨率。4.3 最小训练循环与评估代码一次跑通的骨架import torch import torch.nn as nn def train_one_model(model, train_loader, val_loader, epochs50, devicecuda): model.to(device) optimizer torch.optim.AdamW(model.parameters(), lr5e-4, weight_decay0.05) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_maxepochs) criterion nn.CrossEntropyLoss() best_acc 0.0 for epoch in range(epochs): model.train() total_loss 0.0 for x_batch, y_batch in train_loader: x_batch, y_batch x_batch.to(device), y_batch.to(device) optimizer.zero_grad() loss criterion(model(x_batch), y_batch) loss.backward() optimizer.step() total_loss loss.item() scheduler.step() val_acc evaluate(model, val_loader, device) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_swin.pt) print(fepoch {epoch1}: loss{total_loss/len(train_loader):.4f}, val_acc{val_acc:.4f})逻辑说明每个 epoch 先跑完整训练集再用验证集计算准确率验证集准确率提升才覆盖保存权重这就是「后悔药」机制。如果只看最后一个 epoch 的权重很可能正好落在过拟合段前面前面表现最好的权重反而被丢掉。CosineAnnealingLR的T_max设为总 epoch 数让学习率从头到尾平滑衰减避免后期在最优解附近来回震荡。评估函数要输出混淆矩阵和各类别召回率准确率只是一个及格线import numpy as np from sklearn.metrics import classification_report, confusion_matrix def evaluate(model, loader, device): model.eval() y_true, y_pred [], [] with torch.no_grad(): for x_batch, y_batch in loader: x_batch x_batch.to(device) pred model(x_batch).argmax(dim1).cpu().numpy() y_true.extend(y_batch.numpy()) y_pred.extend(pred) print(classification_report(y_true, y_pred)) cm confusion_matrix(y_true, y_pred) return (np.array(y_pred) np.array(y_true)).mean()代码说明里有几个细节值得留意y_true.extend(y_batch.numpy())能这么写是因为DataLoader默认把标签留在 CPU 上如果标签也搬到了 GPU记得先.cpu()再转 numpy。argmax(dim1)取的索引就是类别编号和标签编码一一对应。轴承四分类里最容易混的是外圈故障和滚动体故障因为两者的冲击间隔在时频图上有时非常接近如果classification_report里这两类的召回率明显低于其他类不要急着堆模型先回第 2 章确认时频图频带设置有没有把两个故障的区分度抹掉。5. 从训练到 GUI 的 5 条避坑记录数据泄漏、时频图相似与 Windows 环境5.1 训练集准确率 99%测试集只有六成现象训练过程很顺利训练集准确率一路涨到 99%验证集也有 95% 以上但拿另一组负载工况的数据测试准确率直接掉到 60% 上下。原因这是最典型的数据泄漏滑动窗口切段后直接随机划分同一条原始信号的窗口同时出现在训练集和验证集里模型背下了窗口内容而不是故障规律。另外负载工况单一也会造成「工况泄漏」只拿 0 hp 数据训练模型学到的是这套负载下的振动幅值特征换到 2 hp 自然失效。解决训练集、验证集按sample_id分组划分最终评估时用完全不参与训练的负载工况数据比如 CWRU 数据里拿 0 hp 和 1 hp 做训练2 hp 和 3 hp 做最终测试这才是真实水平的数字。5.2 时频图肉眼看不出差别模型却强行分类现象把内圈故障和滚动体故障的时频图打印出来对比肉眼几乎找不到稳定差异但模型在训练集上依旧能分到 99%验证集却忽高忽低。原因要么是样本量太少且类别不平衡模型记住了个别样本的噪声要么是滑动窗口步长太小大量高度重叠的窗口让同类样本几乎复制训练集的信息量被高估了。解决先检查每个类别的窗口数量确认没有出现某类比别的类少 5 倍以上的情况。再看训练集里随机抽 20 个同类样本的时频图如果两两之间相似度高到像同一张图就把窗口步长从 512 加大到 1024减少重复样本。验证集准确率忽高忽低是典型的「数据信息量不足」信号和模型本身关系不大。5.3 Windows 下 GUI 双击闪退或中文乱码现象程序在 PyCharm 里跑得好好的打包成 exe 后双击闪退或者界面上中文全部变成方块。原因开发环境里 Python 解释器能找到的路径打包后未必存在。最常出问题的是模型权重文件路径写成了绝对路径换了机器就找不到中文乱码则是打包时没把字体资源打进去tkinter 默认字体在无中文字体的精简系统上会退化。解决模型权重用相对路径读取并把best_swin.pt放在和程序同级的weights/目录下打包命令里显式声明包含字体文件和权重目录。开发阶段就用纯相对路径写不要图省事拖绝对路径。另外检查打包机器的 Python 环境变量配置是否完整很多闪退其实只是环境变量没配对造成的依赖缺失。5.4 换了采样率后模型彻底失效现象训练时用的是 12 kHz 采样率的数据现场采集设备是 48 kHz模型在线诊断时准确率骤降甚至所有样本都输出同一个类别。原因CWT 伪频率折算依赖采样率参数模型学到的是「这张图长什么样」而不是「这个频率是多少」。采样率翻 4 倍后同一故障在时频图上的纹理位置整个偏移模型自然不认。解决两条路选一条。要么在线诊断前把信号重采样到训练时的采样率用 scipy 的resample_poly做整数倍重采样要么训练时做数据增强把训练集信号按不同采样率各生成一份时频图让模型学会对频率缩放不敏感。前者见效快后者更彻底工程上我一般先做前者确认准确率恢复后再考虑要不要做增强。5.5 GUI 推理卡死点一下按钮转半天现象点「开始诊断」后整个界面卡住标题栏显示「未响应」等十几秒才恢复体验非常差。原因模型推理放在了 GUI 主线程里forward是同步计算显存和 CPU 数据搬运期间窗口消息循环被阻塞界面自然假死。解决推理放进子线程用queue把结果传回主线程刷新界面。这是一个老生常谈但相当多新手会忽略的点代码如下def run_inference(self, signal): result_queue queue.Queue() def worker(): model.eval() with torch.no_grad(): wtfp, _ signal_to_wtfp(signal) tensor torch.from_numpy(wtfp[None, None]).to(device) prob torch.softmax(model(tensor), dim1).cpu().numpy()[0] result_queue.put(prob) threading.Thread(targetworker, daemonTrue).start() self.after(100, self.poll_result, result_queue)逻辑说明worker函数里完成从信号到时频图再到模型推理的全过程完成后把概率数组放进队列主线程用after(100, ...)轮询队列拿到结果再更新界面。这套写法把耗时操作和界面刷新解耦GUI 不会卡死。参数说明daemonTrue保证主程序退出时子线程不会阻止进程结束概率数组从 GPU 搬回 CPU 后再放队列避免跨线程持有 CUDA tensor 引发报错。6. 把模型做成现场能用的诊断工具GUI 设计与留出验证技巧6.1 GUI 只做三件事选文件、跑模型、亮结果诊断软件的界面设计不要往「功能丰富」上堆现场操作人员要的是十秒内能上手。核心控件就三类文件选择按钮、启动诊断按钮、结果展示区。结果展示区同时显示三样东西预测故障类别、各类别置信度条形图、当前信号的时频图缩略图。置信度条形图用 tkinter 的 Canvas 画就行不需要引第三方绘图控件。模型加载放到 GUI 初始化时做一次不要每次点按钮都重新加载权重。用torch.load(weights/best_swin.pt, map_locationcpu)加载到 CPU如果机器有独立显卡也建议先在 CPU 上跑一版确认没有依赖 GPU 的隐藏逻辑再决定要不要切 CUDA。现场部署环境的一大特点是变量不可控CPU 推理能稳定工作比快几毫秒重要得多。6.2 最终验证拿没见过的工况说话训练过程中的验证集准确率只是参考交付前一定要做一次更狠的测试。拿完全没有参与训练的另一批负载或转速数据按第 5.1 节的方法完整跑一遍记录混淆矩阵看正常样本有没有被误报成故障、外圈故障有没有被分到滚动体。这一步如果不过关前面所有训练指标都只能算自嗨。我自己的习惯是在项目里单独写一个eval_ood.py专门加载现场采集的新数据和训练时固定的预处理配置一键出报告。这样每次拿到新数据不用改任何代码就能复测模型漂移程度。这个脚本比 GUI 本身更重要因为轴承故障诊断最怕的不是模型不够准而是模型什么时候悄悄失效了都不知道。最后提醒一件事不要迷信任何一个单一指标也不要迷信某一次的测试集高分。把训练、留出验证、现场复测三套流程固定下来每次更新数据或调参都走一遍这套方案才算真正落地。那次我被 97% 的验证集准确率骗上线结果现场数据只有七成多就是吃了没做留出验证的亏。希望帮到你。本文还有配套的精品资源点击获取
返回列表