
简介基于深度学习的轴承故障诊断平台面向机械故障诊断、深度学习方向的开发者、毕业生及课程设计学生旨在解决传统轴承诊断对专家经验依赖强、效率低和复杂工况适应性差等问题。平台提供从数据预处理、特征提取到模型训练与诊断识别的全流程实现。压缩包共63个文件包括11个Python脚本、10个MAT数据文件、34张训练与评估图片、1个UI界面文件及说明文档整体大小14.85MB。源码模块划分清晰涵盖主程序、数据处理、特征提取、模型训练、诊断、结果可视化等功能数据来自凯斯西储大学支持1D-CNN、LSTM、GRU、MCNN-LSTM等多种网络模型。同时提供UI界面支持交互式操作便于调试与演示。目前已吸引59人学习适合作为毕业设计、课程设计的参考实现也可为智能故障诊断研究提供基础工具。1. 轴承故障诊断平台到底是什么一个振动信号分类任务在产线上见过一次典型的“假正常”减速箱温度、油液、噪声全在指标内拆开一看轴承内圈已经剥落了一大片。原因是早期损伤产生的冲击能量太小淹没在齿轮啮合和背景噪声里靠人工听音和温度巡检根本抓不到。基于深度学习的轴承故障诊断平台做的就是把这个“听音辨故障”的过程自动化采集振动信号用卷积网络从信号里自动学出故障指纹输出“正常、内圈故障、外圈故障、滚动体故障”这类结论。它不是一个只能看可视化大屏的演示系统而是一条能落地复现的链路从原始振动数据、滑窗切片、模型训练、评估一直到部署推理每一步都有明确输入输出和可调参数。适合设备健康管理工程师、算法工程师也适合做深度学习相关毕设和项目验证的学生去搭一套属于自己的诊断流程。2. 为什么选深度学习特征工程上限、迁移学习破局与模型选型2.1 传统特征法为什么不够用时域/频域特征的天花板传统轴承诊断的主流做法是先提取特征再丢给分类器。时域指标包括均方根值、峭度、峰值因子、波形因子频域指标包括频谱峰值、包络谱故障特征频率幅值这些特征在“轴承已经明显损伤”的阶段确实有效因为故障冲击已经把信号能量和谱线结构改变得足够大。问题出在两个环节。第一个是阈值难定。同一台设备在不同转速、不同负载下时域指标的分布会整体平移。早班 1800 转时峭度可能稳定在 3 左右晚班降到 1500 转同样健康的轴承峭度就跑到 4 以上阈值没法写死。第二个是故障模式一多特征工程就失控。正常、内圈点蚀、外圈点蚀、滚动体剥落、复合故障每种模式需要的特征组合不同专家经验只能覆盖已知故障遇到没见过的模式基本要重新做一轮分析。我早期做过一个方案对每段信号算 20 多个时域频域特征用随机森林分类在单一工况下准确率能到 95% 以上但一换负载直接掉到 80% 以下而且排查不出原因。后来才想明白不是分类器不行是手工特征承载不了跨工况的分布变化。2.2 深度学习不是玄学卷积如何提取冲击特征迁移学习解决工况漂移深度学习在轴承诊断里的作用是把“手工找特征”换成“让网络自己找特征”。振动信号进入一维卷积网络后第一层的卷积核本质上就是一组可学习的滤波器网络训练过程中会自己去适配共振频带深层卷积再把不同尺度上的冲击特征、调制特征组合起来形成故障判据。这就不需要你预先知道轴承的故障特征频率是多少也不需要做包络解调只要数据里有可学习的模式网络就有机会抓住它。那迁移学习在诊断场景里解决什么问题还是工况漂移。在 1797 r/min、0 HP 负载下训练好的模型放到 1772 r/min、3 HP 负载的现场数据上信号分布已经变了模型输出往往不可靠。迁移学习的常见做法是在源工况上训练好一个特征提取器再拿目标工况的小样本数据去微调最后的分类层或者把预训练模型的浅层冻结、只解冻靠近输出的层。这样比从零训练稳定得多数据量要求也低一点。我一般会把“源工况训练目标工况微调”当成默认方案除非目标工况数据量足够大才直接从头训练。2.3 一维信号还是二维时频图两条路线的取舍与预实验模型选型第一条分岔路一维卷积直接处理原始振动波形还是用短时傅里叶变换把信号转成二维时频图再进二维卷积。两条路线我都在实际数据上试过。一维 CNN 的优点是推理延迟低一条 4096 点的样本在 CPU 上毫秒级出结果适合做边缘盒子或实时监测缺点是网络对冲击特征的提取不如二维时频图直观。二维时频图的优点是能复用 ImageNet 预训练的 ResNet 等模型做迁移学习在样本量小的场景下收敛快缺点是时频变换本身需要调窗函数、窗长、重叠率而且推理开销大一截。一个很实用的判断标准先做小规模预实验目标工况有几千条样本以上优先走一维 CNN 路线数据量只有几百条才考虑时频图加预训练模型。无论走哪条路第一步都建议先把数据读出来看看波形和包络谱里有没有周期性冲击确认数据集的故障特征真实存在。写一个最简化的检查脚本import scipy.io as sio import numpy as np import matplotlib.pyplot as plt from scipy import signal from scipy.fft import rfft, rfftfreq # 读取 CWRU 格式的 .mat 文件采样率 12 kHz data sio.loadmat(data/CWRU/1797_IR007_6.mat) sig data[X1797_DE_time].reshape(-1)[:4096] fs 12000 # 带通滤波后做希尔伯特解调突出冲击包络 b, a signal.butter(4, [800, 4000], btypebandpass, fsfs) filtered signal.filtfilt(b, a, sig) env np.abs(signal.hilbert(filtered)) f, Pxx signal.periodogram(env, fsfs, nfft4096) plt.figure(figsize(12, 4)) plt.subplot(121) plt.plot(sig) plt.title(raw waveform) plt.subplot(122) plt.plot(f, Pxx) plt.xlim([0, 2000]) plt.title(envelope spectrum) plt.tight_layout() plt.show()这段脚本有两个重点。带通滤波选 800–4000 Hz 是为了避开低频轴频和大质量结构共振同时保留轴承高频共振带具体频率范围需要根据测点位置和传感器频响调整希尔伯特包络谱则能把周期冲击转换成谱线上的峰值方便肉眼确认故障特征频率。如果画出来一条干净的时域正弦波、包络谱上也没有明显尖峰那要么是故障太轻要么是采集通道本身就有问题这时候没必要急着训练模型先把数据质量解决掉。3. 数据准备与切分从原始振动信号到干净训练集3.1 公开数据集选型与组织CWRU 数据集的目录规划做轴承故障诊断绕不开凯斯西储大学轴承数据中心的数据集行业内几乎拿它当基准。这个数据集包含正常、内圈故障、外圈故障、滚动体故障四类故障损伤直径有 0.007、0.014、0.021 英寸三档采样率 12 kHz 和 48 kHz负载从 0 到 3 HP 都覆盖了。目录怎么组织直接影响后续分组的正确性我习惯按“转速_故障位置_损伤直径”命名 mat 文件例如1797_IR007_6.mat表示 1797 r/min、内圈故障、损伤直径 0.007 英寸、负载约 0 HP。如果这个信息记录得不完整后面做工况外推测试会彻底抓瞎。自己的采集数据也有同样的问题。我一般会在数据采集阶段就把转速、负载、采样率、测点位置、传感器灵敏度、故障部位这些字段写进文件名或附带一个 CSV 清单不做这一步后面做分组划分时只能靠猜。数据集目录的推荐结构大致是这样data/ CWRU/ 1797_IR007_6.mat 1797_IR014_6.mat 1772_OR007_5.mat 1750_B007_4.mat custom/ normal_1800rpm_pump1.csv inner_1500rpm_pump1.csv强烈建议保留原始 mat 或 CSV 文件不要直接把处理完的 npy 覆盖原始文件。原始信号是唯一可追溯的物理事实一旦覆盖后面想重新评估新模型就没有后悔药了。3.2 滑窗切片与标签编码窗口/步长怎么给原始振动信号通常长达几十秒甚至几分钟不能整段丢给网络需要切成长度固定的样本。窗口长度怎么选是最容易被新手糊弄过去的参数我一般用采样率换算12 kHz 采样率下1800 r/min 的转频是 30 Hz一圈约 33 ms窗口至少要覆盖 2 到 3 个旋转周期也就是 66 到 100 ms对应 800 到 1200 点保险起见取 2048 或 4096 点。窗口太短会截断冲击周期网络看不到完整的故障节律窗口太长则每条样本包含太多周期计算开销大而且故障特征被平均掉。步长决定样本重叠率。步长等于窗口长度时样本互不重叠数据量最小步长取窗口一半时样本量翻倍但相邻样本高度相似训练时会引入大量冗余。我通常取窗口的 25% 到 50%既能扩样本又不至于让模型见到几乎一样的输入。切片的实现不复杂import numpy as np from pathlib import Path def sliding_windows(signal, win_len4096, stride2048): n len(signal) indices range(0, n - win_len 1, stride) return np.stack([signal[i:i win_len] for i in indices]) def label_by_filename(name: str) - int: # 0正常, 1内圈故障, 2外圈故障, 3滚动体故障 if IR in name: return 1 if OR in name: return 2 if B in name: return 3 return 0 # 遍历目录把每个 mat 文件切窗后连同标签存入字典 all_windows {X: [], y: []} for mat_path in Path(data/CWRU).glob(*.mat): sig sio.loadmat(mat_path)[mat_path.stem _DE_time].reshape(-1) win sliding_windows(sig, win_len4096, stride2048) label label_by_filename(mat_path.stem) all_windows[X].append(win) all_windows[y].append(np.full(win.shape[0], label))这里label_by_filename按文件名里的关键字映射标签注意 CWRU 文件格式里滚动体故障的文件名用B开头正常样本用Normal开头映射表必须和文件名约定严格一致。标签必须编码成整数不要直接拿文字做 one-hot 之外的处理否则后面混淆矩阵、分类报告都不好画。切完之后记录每段窗口对应的源文件名和起始位置这对排查坏样本特别关键别省。3.3 数据集划分红线按实验工况切分别让信息泄露数据划分是轴承诊断项目里最隐蔽的坑。如果先把所有窗口打乱再按 70/30 随机分成训练集和测试集测试准确率会虚高到离谱。原因是同一个 mat 文件切出来的窗口之间高度相似消息已经通过重叠窗口泄漏到测试集里了模型等于背过答案再考试。正确的做法是按实验维度切分。一个 mat 文件代表一次实验、一个具体工况切分时要把整个 mat 文件对应的全部窗口放在同一个集合里保证训练集和测试集不共享任何原始信号片段。常用工具是 scikit-learn 的GroupShuffleSplitgroup 参数传入每个窗口所属的源文件名from sklearn.model_selection import GroupShuffleSplit X all_windows[X] # shape: [n_samples, 4096] y all_windows[y] groups all_windows[source] # 每个窗口对应的 mat 文件名 gss GroupShuffleSplit(n_splits1, test_size0.3, random_state42) train_idx, test_idx next(gss.split(X, y, groups)) X_train np.concatenate([X[i] for i in train_idx]) y_train np.concatenate([y[i] for i in train_idx]) X_test np.concatenate([X[i] for i in test_idx]) y_test np.concatenate([y[i] for i in test_idx])分组的粒度也值得说明。只按文件名分组已经能避免大部分数据泄露但更严格的做法是按物理工况分组比如同一种故障位置、同一种损伤直径、同一种负载算一组验证时整个工况都在测试集里这样评估的是模型面对“没见过的工况”的能力。线上跑起来之后最需要的就是这种评估而不是在相似数据上的自嗨分数。4. 模型训练与调参一维 CNN 的完整训练流程与关键参数4.1 网络结构适合振动信号的轻量一维 CNN一维 CNN 的结构设计我踩过不少坑。一开始套用过二维 ResNet 的通道数直接把输入改成 (1, 4096) 就开训结果收敛慢还过拟合。振动信号和图像的关键区别是图像局部纹理尺度相对一致而振动冲击既可能是几十个采样点的瞬态冲击也可能是包络层面几百个点的调制周期第一层卷积核太小抓不到冲击太大又丢失细节。我的常用结构是三到四层一维卷积加全局平均池化输入 4096 点第一层卷积核取 64 到 128后面逐层减半。贴一个可以直接跑的轻量结构import torch import torch.nn as nn class VibrationCNN(nn.Module): def __init__(self, num_classes4, dropout0.3): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 16, kernel_size64, stride4, padding32), # 4096 - 1024 nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.Conv1d(16, 32, kernel_size16, stride2, padding8), # 1024 - 512 nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.Conv1d(32, 64, kernel_size8, stride2, padding4), # 512 - 256 nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), ) self.pool nn.AdaptiveAvgPool1d(1) self.classifier nn.Sequential( nn.Dropout(dropout), nn.Linear(64, num_classes) ) def forward(self, x): x self.features(x) x self.pool(x).squeeze(-1) return self.classifier(x)第一层kernel_size64, stride4是关键设计。64 个采样点在 12 kHz 下约 5.3 ms能包住一个完整的冲击前沿stride4 把 4096 点降采样到 1024降低后续计算量同时保留频率信息。BatchNorm 放在卷积之后、激活之前对振动信号这种均值和方差会随工况漂移的输入非常重要不然网络很难收敛。分类器只用了一层全连接没有隐藏层因为特征已经是全局平均池化后的紧凑向量再加隐藏层只会增加过拟合风险。4.2 训练超参与优化器选择在过拟合和欠拟合之间找平衡模型结构确定后训练参数直接决定最终效果。我常用的初始配置是Adam 优化器学习率 1e-3batch size 64训练 40 个 epoch配合ReduceLROnPlateau在验证 loss 连续 5 个 epoch 不下降时把学习率乘 0.2。数据量大到上万条时用 128 的 batch size每类样本量偏少时降到 32 并用加权采样。这些不是拍脑袋定的训练阶段一边看训练集准确率一边看验证集准确率如果训练集准确率接近 100 而验证集只有 80是过拟合直接加 dropout、降学习率、减少卷积通道如果两边都不到 70是欠拟合加深网络或增加卷积核宽度。训练过程不应只盯着准确率要同时记录 loss。验证 loss 先降后升是过拟合的典型曲线EarlyStopping 的 patience 我一般设 8 到 10避免在验证集上反复横跳的消耗。还有一个容易翻车的地方类别权重。轴承数据集的 mat 文件里正常样本往往比故障样本多得多不处理的话模型会把所有样本都判成正常。我习惯给每个类别算一个权重故障类别权重是正常类别的两到三倍用torch.nn.CrossEntropyLoss(weight...)传进去比手工复制故障样本方便得多。4.3 迁移学习备选方案用预训练权重做时频图分类如果目标工况的数据量实在太小一维网络从头训练会抖得厉害这时可以走时频图加预训练模型路线。做法是把原始振动信号做短时傅里叶变换得到 224x224 的灰度时频图复制成三通道输入 ImageNet 预训练的 ResNet18替换最后一层全连接输出为四类。迁移学习落地时的关键是冻结策略先冻结前四层只训练分类头和最后两个残差块学率设到 1e-4 量级跑 10 个 epoch 再看效果如果验证准确率还在涨再解冻全部层用更小学习率微调。这条路线我只在目标样本少于五百条时使用样本量超过一千条时一维 CNN 更容易控制过拟合也更容易部署到边缘设备。5. 避坑清单五条最容易让诊断平台翻车的踩坑记录5.1 压缩包解压报错或文件损坏现象拿到训练数据压缩包后Windows 下双击解压到一半提示“文件意外结束”Linux 下unzip报invalid zip archive: could not find eocd但压缩包明明看着有几百兆。 原因下载传输导致文件不完整少数情况是压缩包本身用了伪加密——修改了加密标志位却没真加密普通解压器检测到加密标志就会报错。 解决先校验再解压。Linux 下执行unzip -t file.zip会列出测试结果Windows 下可以用 7-Zip 的“测试压缩文件”功能。文件不完整就用下载工具重新拉一次伪加密的情况直接用 7-Zip多数情况下能直接解如果 7-Zip 也无法正常打开检查文件大小是否和发布页一致不一致基本就是传输过程丢包了。5.2 数据泄露导致验证准确率虚高现象训练时验证准确率 0.99测试准确率 0.98自己都觉得好得不真实。遇到真实采集数据一跑准确率只有 0.75。 原因划分数据集时把同一个 mat 文件切出的高度相似窗口同时塞进了训练集和测试集模型“背题”了。窗口重叠率越高泄漏越严重。 解决按实验文件分组切分。用GroupShuffleSplit把同一个源文件的所有窗口归入同一个集合重新评估准确率回落是正常的那才是真实水平。这个坑我栽过不止一次现在固定流程是先做数据划分、再谈模型顺序反不得。5.3 归一化偷看了测试集现象训练前对全部数据先做了StandardScaler再切分训练集准确率正常但实际部署到现场后输出完全错乱。 原因StandardScaler在全局数据上 fit测试集和现场数据的均值、方差混进了训练时使用的统计量训练过程已经间接看过测试集分布。 解决scaler.fit(X_train)只让训练集参与统计量计算然后transform训练集、验证集和测试集。更稳妥的做法是每条样本按自身统计量做 z-score 归一化避免测点或工况之间的全局均差干扰模型我在多测点场景下默认用这条。5.4 样本不均衡导致模型全都判成正常现象损失已经降到很低但混淆矩阵显示几乎所有故障样本都被判成正常只有正常类的召回率很高。 原因正常样本在数据集中占比过高CrossEntropy 优化方向被正常类主导模型发现全判正常也能拿到低 loss。 解决给损失函数加类别权重故障类的权重按样本数反比放大或者用WeightedRandomSampler对故障类做上采样。加了权重之后可以观察每个类别的召回率是否拉开差距如果正常类召回率下降百分之几但故障类召回率大幅上涨这是健康的平衡。5.5 窗口长度选错模型看不见完整冲击周期现象网络收敛很快准确率不错但可视化注意力发现模型只认高频噪声纹理换一个采集位置的信号立刻失效。 原因窗口太短只包含半个旋转周期甚至更少冲击节律无法表达窗口太长又让冲击特征被淹没在长时间信号里。 解决按转频换算窗口长度至少覆盖 2 到 3 个旋转周期。12 kHz 采样率、1800 r/min 的工况下4096 点约 0.34 秒、约 10 圈是比较稳妥的取值6000 转的高速主轴可以缩短到 2048 点。调参时把切出来的窗口波形画出来逐屏看有没有周期性冲击比只盯准确率靠谱得多。6. 落地验证与部署不只跑通准确率还要能上线复现6.1 先过外推测试同一模型在不同负载下的表现离线数据集上准确率再高也不等于现场能用。常见做法是拿训练时完全没见过的工况数据做外推验证用 0 HP 负载数据训练模型再看它在 1 HP、2 HP、3 HP 负载数据上的分类表现。如果准确率掉得厉害说明模型把负载工况特征当成了分类依据故障特征学得并不本质。这时候先别急着换模型结构可以尝试在训练集里加入多负载样本或者对源域信号做速度扰动增强把转速变化模拟进训练过程。外推测试这段一定要记录基线数据不然上线后现场性能退化根本没法定位是模型问题还是采集问题。6.2 阈值校准与置信度过滤生产环境里的判据生产环境不能直接拿argmax当报警依据。一条样本 softmax 输出 0.9 和 0.35虽然都是“内圈故障”这个 argmax 结果但置信含义完全不同。我一般给每个类别设一个置信度阈值低于阈值的样本丢到“不确定类”转人工复核只有置信度高于阈值的才触发报警。阈值可以从验证集上画 ROC 曲线取约登指数对应的点不要靠感觉定。如果模型在目标工况上整体偏保守可以少量采集现场负样本做一次温度标定校准把 softmax 输出重新映射到更接近真实概率的分布上。6.3 模型导出与部署形态onnx 落地与最小推理验证训练完成后最终交付的不能是一个 PyTorch 权重文件。工业现场最常见的部署形态是导出成 ONNX 格式跑在服务端 CPU 或边缘盒子上由一段推理脚本接收振动序列、返回故障类型和置信度。导出时要把输入长度固定写死避免部署端因为动态 shape 翻车import torch import onnxruntime as ort import numpy as np model VibrationCNN(num_classes4) model.load_state_dict(torch.load(best_model.pt, map_locationcpu)) model.eval() dummy torch.randn(1, 1, 4096) torch.onnx.export( model, dummy, bearing_cnn.onnx, input_names[signal], output_names[logits], dynamic_axesNone, opset_version13 ) sess ort.InferenceSession(bearing_cnn.onnx, providers[CPUExecutionProvider]) sig np.random.randn(1, 1, 4096).astype(np.float32) logits sess.run([logits], {signal: sig})[0] label np.argmax(logits, axis1)[0]这段导出代码里有几个实用细节。dynamic_axesNone表示输入 shape 固定推理端不用处理变长序列输入必须是 float32PyTorch 默认的 float64 在 ONNX Runtime 里会报 dtype 不匹配导出前一定model.eval()否则 BatchNorm 的统计量还是训练模式导出后的推理结果会和训练不一致。部署端跑通最小推理后再用一条已知故障类型的真实信号验证输出输入长度、dtype、归一化方式全部校验通过平台才算出货。说一个我自己的习惯每次做这类平台我都会在测试集里故意留一个“现场换传感器后重新采集的样本组”它和训练数据分布只有细微差别。跑通模型的那一刻很兴奋但如果这种样本上准确率掉了 10 个点以上我会先停下来重新检查数据切分和预处理管线而不是急着调模型结构。数据分布漂移是这类系统上线后最难缠的黑匣子提前设一个对照组能省下大量现场排查的时间。希望帮到你。本文还有配套的精品资源点击获取