基于深度学习和1D-CNN的滚动轴承故障诊断实战
简介基于Python的滚动轴承智能故障诊断系统开发资源适用于深度学习、机械故障诊断方向的毕业设计及课题研究。项目以完整代码和标准数据集为支撑覆盖振动信号采集、预处理、特征提取、混合神经网络建模到诊断结果可视化的全流程可用于学习CNN与GRU在时间序列故障识别中的应用。压缩包共50个文件以30个MAT数据文件为核心内含CWRU轴承数据不同工况样本另含4个Python脚本如模型构建、绘图等、7个Markdown说明文档及备份压缩包整体体积约39.63MB目录结构便于分模块查阅。目前已有42人学习下载。资源将网络公开研究方案整理为可直接运行的工程包含数据划分、小批量训练、早停策略、贝叶斯超参数优化等实现细节并配套可视化与报告生成模块读者可通过实际运行快速复现完整诊断流程作为毕设代码参考或算法改进基线。1. 滚动轴承故障诊断为什么值得用深度学习做机械设备里最容易出问题的旋转部件就是滚动轴承电机、泵、减速箱里全是它。传统的诊断方式靠人工看频谱、听声音经验门槛高而且早期微弱故障根本听不出来。用 Python 搭一套深度学习故障诊断系统本质上是把振动信号直接喂给模型让卷积神经网络自己学会区分正常状态和内圈、外圈、滚动体故障准确率能做到 95% 以上。这套资源面向的是做毕业设计的学生和刚接触故障诊断的工程师主线是用 Python 从零实现 1D-CNN 模型配合一份带标注的完整轴承振动数据集把数据预处理、模型训练、评估、可视化全流程打通。你不需要懂太多信号处理理论只要会基本的 Python 语法就能跟着复现。2. 数据集与预处理从原始振动信号到训练样本2.1 振动数据长什么样为什么不能直接喂给网络滚动轴承故障诊断的原始数据是时域振动信号本质是一个一维数组单位是加速度m/s²采样频率通常在 12kHz 到 48kHz 之间。单纯看波形很难区分故障类型因为正常轴承本身也有振动故障特征往往叠加在强噪声里。常见做法是先把连续信号切成长度固定的样本每个样本包含多个旋转周期的信息然后用滑动窗口法扩充样本量。这套资源里的数据集按故障位置和损伤尺寸分了类每类是独立的 CSV 或 mat 文件加载后第一步是确认采样频率和通道数这直接影响后续切窗长度。import numpy as np import pandas as pd from scipy.io import loadmat # 加载单个故障类别的数据文件 mat loadmat(data/InnerRace_0.007.mat) # 内圈故障损伤直径0.007英寸 signal mat[X097_DE_time].flatten() # 取出加速度通道并压成一维 fs 12000 # 采样频率单位Hz print(f信号长度: {len(signal)} 点时长: {len(signal) / fs:.2f} 秒)代码里X097_DE_time是数据文件里的字段名不同来源的数据集字段名不一样加载前先用mat.keys()查看实际键名。采样频率 fs 必须从数据说明文档里确认不能猜因为它决定了后续样本长度对应的物理时间。切窗长度选择有讲究一个经验值是每个样本包含 4 到 10 个旋转周期。假设轴的转速是 1800 r/min也就是 30Hz那么一个周期是 33ms在 12kHz 采样率下对应约 400 个点。切窗长度取 1024 或 2048 个点都能用窗口越长单样本包含的故障冲击次数越多模型更容易学到周期性特征但训练样本总数会变少。窗口太短则可能出现某段样本里压根没有故障冲击导致标签和实际内容不匹配。2.2 滑动窗口切分与训练集划分切窗的常用做法是设置一个重叠率重叠 50% 到 75%。比如窗口长度 1024 点步长 256 点这样相邻两个窗口有 75% 的重叠样本量能翻好几倍。但必须注意重叠率过高会让训练集和测试集之间存在大量同源数据评估结果虚高。我一般会先按连续时间段切分比如把每个文件的前 70% 信号划分到训练集、后 30% 划分到测试集再在各自区间里做滑窗避免同一个窗口的数据同时出现在训练和测试里。def sliding_window(signal, window_len1024, step_len256): samples [] n len(signal) for start in range(0, n - window_len, step_len): samples.append(signal[start:start window_len]) return np.array(samples) # 每个类别生成样本 all_x, all_y [], [] for class_id, path in enumerate(file_list): # file_list 按类别排序 data loadmat(path)[accel].flatten() split int(len(data) * 0.7) # 前70%做训练区后30%做测试区 train_sig, test_sig data[:split], data[split:] train_windows sliding_window(train_sig, 1024, 256) test_windows sliding_window(test_sig, 1024, 512) all_x.append(train_windows), all_y.append([class_id] * len(train_windows))切出来之后要做的三件事归一化、打乱顺序、转成 PyTorch 或 TensorFlow 需要的张量格式。归一化采用 z-score 标准化即减去均值除以标准差而不是简单的 min-max 归一化。原因是振动信号的幅值分布近似对称z-score 能保留故障冲击的相对形态min-max 则容易被偶尔出现的尖峰带偏。标准化系数只用训练集的均值和标准差测试集直接用同一组系数不能单独算否则相当于引入了测试集的信息这是评估严谨性的大忌。每类故障的样本量要基本均衡如果某个类别样本数量明显偏少模型会倾向把该类别预测成样本量大的类别最终导致混淆矩阵里某一行几乎全空。可以用一个简单的np.bincount打印各类别数量确认均衡性。3. 构建 1D-CNN 模型网络结构与参数设计3.1 为什么选一维卷积而不是先把信号转成频谱图故障诊断领域有两类主流做法一类把振动信号做短时傅里叶变换转成时频谱图再用二维 CNN比如残差网络识别另一类直接对原始信号做一维卷积。这套资源选择 1D-CNN核心原因有两条。第一原始信号保留的相位信息更加完整频谱图会丢失部分瞬态冲击的时序位置第二一维卷积的参数量比二维卷积小一个数量级在样本量不多的场景下更不容易过拟合。对小数据集来说1D-CNN 的性价比明显更高。网络设计参考了经典故障诊断论文里的结构整体由四个卷积块和一个全连接分类头组成。每个卷积块包含一维卷积层、批归一化、ReLU 激活和最大池化。批归一化在这类任务里很关键它能把每层输出拉到标准分布训练速度提升明显还有一定的正则化效果。卷积核大小选 32、64、128 这样逐层翻倍的做法通道数越多特征越抽象。3.2 具体的模型实现代码import torch import torch.nn as nn class BearingCNN(nn.Module): def __init__(self, num_classes10): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 16, kernel_size3, padding1), # 输入通道1输出16 nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(2), # 长度减半 nn.Conv1d(16, 32, kernel_size3, padding1), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size3, padding1), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2), ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(64 * (1024 // 8), 128), # 输入长度1024池化3次后128 nn.ReLU(), nn.Dropout(0.5), # 防止全连接层过拟合 nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x))代码里padding1是为了保持卷积分支的序列长度不变池化层每次对半降采样输入 1024 点经过三次池化后变成 128 点。全连接层的输入维度需要根据池化次数手动计算这是新手最容易写错的地方如果后面跑模型报维度不匹配优先检查这里。三个参数值得说明。kernel_size固定为 3相当于只看附近 3 个点的局部信息堆叠多层之后感受野足够覆盖一个旋转周期的长度没必要用更大的核参数还少。Dropout(0.5)只在最后一层全连接前用卷积特征层不drop避免把已经学到的局部特征随机丢弃。num_classes决定输出维度必须和数据集的类别总数一致常见配置是正常加 9 种故障共 10 类如果你的数据集类别数不同改这一个数字即可。3.3 损失函数与优化器选择多分类任务默认用交叉熵损失配合 Adam 优化器。学习率初始值设在 0.001 左右可以配合余弦退火策略让学习率在训练过程中逐步衰减前 30 个 epoch 快速收敛后面 20 个 epoch 精细调整。也可以用 SGD 加动量但调参成本更高对新手不友好。Adam 的一个已知缺点是初期收敛快后段可能震荡解决办法是加weight_decay1e-4做 L2 正则化。import torch.optim as optim model BearingCNN(num_classes10) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001, weight_decay1e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50, eta_min1e-5)T_max要设成总训练 epoch 数这样学习率会在训练结束时恰好降到eta_min。如果中途验证集准确率长时间不动先看是不是学习率退化得太快调到T_max60或者直接改成 StepLR 每隔 20 个 epoch 乘以 0.5。4. 训练与评估损失、准确率、混淆矩阵与可视化4.1 训练循环的标准写法训练代码本身不复杂但有几个细节直接影响最终效果。数据加载要用DataLoader并开启shuffleTrue确保每个 batch 里包含各类别的样本否则同一批数据全是同一类故障模型参数更新会来回震荡。batch size 一般取 32 或 64太小噪声大、收敛慢太大会把显存放爆1024 点输入的话 64 基本安全。from torch.utils.data import TensorDataset, DataLoader train_dataset TensorDataset(torch.tensor(train_x, dtypetorch.float32).unsqueeze(1), torch.tensor(train_y, dtypetorch.long)) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) for epoch in range(50): total_loss, correct, total 0, 0, 0 for x_batch, y_batch in train_loader: optimizer.zero_grad() out model(x_batch) loss criterion(out, y_batch) loss.backward() optimizer.step() total_loss loss.item() * x_batch.size(0) correct (out.argmax(1) y_batch).sum().item() total y_batch.size(0) scheduler.step() train_acc correct / total print(fEpoch {epoch1:02d} | Loss {total_loss/total:.4f} | Acc {train_acc:.4f})unsqueeze(1)这一步把形状从(batch, length)变成(batch, 1, length)对应 Conv1d 期望的输入格式漏掉会直接报维度错误。每个 epoch 结束打印平均损失和准确率观察损失曲线是否稳步下降。如果损失在早期下降后反弹多半是学习率偏大把初始 lr 降到 0.0003 重新跑。4.2 测试集评估与混淆矩阵训练结束后用测试集做一次整体评估不要只看准确率。准确率在类别均衡时能说明问题但故障诊断场景下更关心的是哪两类容易混淆。典型情况是内圈故障和滚动体故障特征相似模型经常互相误判这时候混淆矩阵能直观展示错误集中在哪个区块。from sklearn.metrics import confusion_matrix, classification_report model.eval() y_pred, y_true [], [] with torch.no_grad(): for x_batch, y_batch in test_loader: out model(x_batch) y_pred.extend(out.argmax(1).numpy()) y_true.extend(y_batch.numpy()) cm confusion_matrix(y_true, y_pred) print(classification_report(y_true, y_pred, target_namesclass_names))模型评估前必须调用model.eval()这会关闭 dropout 和批归一化的训练行为。批归一化在训练时用 batch 内统计量评估时用全局滑动平均不切换的话测试结果会偏弱。classification_report输出的 precision、recall、F1 指标里重点关注 F1 低于 0.9 的类别这通常是误判根源。4.3 特征可视化认识模型学到了什么用 t-SNE 把最后一层全连接的特征投影到二维平面能直观看到不同类别的样本在特征空间里是否分明。这一步不是必须的但对理解模型行为很有帮助也经常出现在毕业论文的图表里。from sklearn.manifold import TSNE model.eval() with torch.no_grad(): features [] for x_batch, _ in test_loader: feat model.features(x_batch) # 取卷积特征 features.append(feat.mean(dim2)) # 全局平均池化 feat_all torch.cat(features).numpy() tsne TSNE(n_components2, perplexity30, random_state42) embedded tsne.fit_transform(feat_all) # 按类别着色后绘制散点图即可观察聚类效果perplexity是 t-SNE 的核心参数通常设在 5 到 50 之间取 30 适用于中等样本量。如果聚类结果明显混杂先检查是不是某些类别训练样本太少再考虑增加网络宽度或加深层数。一般训练充分的话10 类样本在二维投影里能看到 10 个接近分离的簇。5. 避坑与排查故障诊断项目里最常翻车的四个问题5.1 测试集准确率虚高的元凶样本重叠现象训练准确率 95%测试准确率做到 99% 以上但换一批新采集的数据瞬间跌到 70%。原因切窗时没有先切分训练集和测试集而是先对完整信号滑窗、再随机划分窗口。滑窗重叠率通常不低同一个故障冲击会同时出现在相邻窗口里随机划分后训练集和测试集高度同源模型等于见过测试答案。解决严格按我前面写的方式先按时间段切分再分别滑窗。测试集样本完全由训练集没见过的信号段生成准确率会下落一些那才是真实水平。评估模型时如果测试准确率比训练准确率高基本可以断定数据泄漏了。5.2 损失不下降或直接发散现象loss 在 2.0 左右徘徊准确率始终徘徊在类别随机水平附近或者 loss 直接冲到几百随后变成 NaN。原因常见的有三种。数据预处理未归一化信号幅值在 ±50 的量级上传播梯度爆炸学习率设到 0.1 以上Adam 也不一定能救回来数据标签从 0 开始连续编号交叉熵要求y是[0, C-1]的整数如果类别编号出现断层loss 会异常。解决先打印train_x的均值和标准差确认在 0 附近、标准差接近 1。学习率从 0.001 起步出现 NaN 就降一个数量级重跑。检查np.unique(train_y)是否等于[0, 1, ..., C-1]。我一般会加一段断言训练前强制校验标签合法性省得排查半天。5.3 训练很快但泛化极差过拟合现象训练集准确率 100%测试集准确率只有 75%混淆矩阵里少数类几乎全错。典型公式是模型容量远超数据量。原因参数量和样本量比例失衡。比如 10 类故障每类只有 500 个样本参数几百万的模型必然硬记住训练样本。解决三个方向。减少num_classes不变的情况下把全连接层从 128 降到 64把 kernel_size 从 3 改成 5降低网络的表达冗余增大数据扩充用加噪声、幅值缩放、时间偏移三种方式做样本扩展。实际经验是 Dropout 从 0.3 提升到 0.6 往往最直接有效。5.4 换一台机器跑不动或环境冲突现象代码在自己电脑上正常到实验室服务器上跑直接报错常见提示是 CUDA out of memory 或者torch版本不兼容。原因PyTorch、CUDA、显卡驱动三者版本不匹配是最常见的环境坑其次是显存不足导致 OOM。这个现象在分组实验时非常常见不同机器的环境差异会导致同样的代码表现完全不同。解决代码里把所有.cuda()或.to(device)的调用改成靠设备检测动态分配。显存不够时把 batch size 从 64 降到 16输入长度从 1024 减到 512对应的全连接层维度同步调整。训练流程加入设备无关写法让模型自动在 CPU 或 GPU 上运行比每次手动改代码稳得多。device torch.device(cuda if torch.cuda.is_available() else cpu) model BearingCNN(num_classes10).to(device) # 训练循环中每个 batch 都要执行 x_batch x_batch.to(device)6. 进阶验证迁移学习到自有数据把模型用起来毕业设计到这一步基本可以收尾了但用过这套系统的同行都清楚公开数据集表现好不意味着真实设备有效。真正有价值的下一步是迁移学习用已有的 10 类故障模型作为预训练权重在自己采集的振动数据上微调。具体做法是冻结卷积层的前两个 block只训练后两个 block 和全连接层学习率设成 0.0001。冻结前几层的原因是底层卷积学到的都是通用的边缘和冲击特征这些特征在迁移任务里依然有效只微调高层语义特征可以大幅减少所需样本量。for param in model.features[0:4].parameters(): param.requires_grad False # 冻结前2个卷积块 optimizer optim.Adam( filter(lambda p: p.requires_grad, model.parameters()), lr0.0001, weight_decay1e-4 )迁移学习在实际项目里最大的价值在于你不需要重新标注上万条故障数据每类 100 个样本就能把测试准确率从 50% 拉到 85% 以上。另一个实用技巧是保留训练过程中验证集表现最好的模型权重而不是用最后一个 epoch 的权重。best_acc 0.0 if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_bearing_model.pth)从那以后我每次做这类故障诊断项目都强制走一遍这个流程先确认数据切分没有泄漏再训练基线模型最后用混淆矩阵定位易混类别。这套资源里的数据集、训练脚本和模型结构文件都是按这个流程组织的建议你完整跑通一遍再改参数。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →