基于深度学习的滚动轴承故障诊断毕设项目:从CWRU数据到CNN/DNN模型实战
发布时间:2026/10/18 20:51:13 锦皓数字建站

简介这份资源是面向高校学生与深度学习入门者的滚动轴承故障诊断毕业设计完整项目包围绕振动信号采集、预处理、模型构建、训练调优、故障诊断预测及结果可视化六个环节展开适合需要完成相关课题或复现经典实验的读者。压缩包共41个文件约34.88MB以30个mat数据文件为核心配套4个py源码脚本、6个md说明文档和1个txt使用说明覆盖数据加载、模型定义与绘图等模块目录结构清晰便于按流程查阅。目前已有241人学习下载。读者可借助完整源码与全部数据直接跑通CNN、DNN等模型训练流程理解从原始振动信号到故障类型输出的完整链路并参考说明文档快速定位数据预处理与模型调优的关键细节减少自行搜集CWRU数据集与搭建实验环境的时间成本。1. 从一段振动信号说起这套轴承故障诊断毕设到底能跑出什么如果你手头正好有一段 CWRU 轴承振动信号采样频率 12kHz正常、内圈、外圈、滚动体四种状态混在一起想用深度学习把它分成四类那这套「python毕业设计-基于深度学习的滚动轴承故障诊断方法项目」基本就是照着这个场景做的。它给的不是一份论文模板而是一套能直接跑起来的工程骨架creat_data.py负责把原始信号切成样本pre_data放预处理产物cnn.py和dnn.py分别对应卷积网络和全连接网络两条路线plot_scatter.py把特征分布画出来看聚类效果last_layer_data存的是模型倒数第二层的输出方便你做 t-SNE 或者散点观察。适合谁正在做深度学习方向毕业设计、需要一份能改能跑、数据齐全的轴承故障诊断代码包的人。它解决的核心问题就一个让你不用从零写数据加载和模型搭建把精力放在调参和写论文上。2. 数据管线拆解从 CWRU 原始信号到模型能吃的样本2.1 为什么先切样本再谈模型滚动轴承故障诊断的输入不是一张图而是一段一维时序振动信号。CWRU 数据集里正常和故障状态下的信号是连续采集的长度动辄几十万点。深度学习模型没法直接吃这么长的序列必须切成固定长度的样本。常见做法是滑动窗口窗口长度取 1024 或 2048 个采样点步长取窗口的一半或者更小这样既能保证每个样本包含足够的故障冲击特征又能通过重叠切分扩充样本量。这套项目里creat_data.py干的就是这件事它把原始信号读进来按窗口切分打上标签输出成模型能直接读的格式。窗口长度怎么定1024 点对应 12kHz 采样率下约 85 毫秒足够覆盖一个完整的轴承故障冲击周期。如果你用的采样率不同比如 48kHz窗口可以相应放大到 4096。步长太小会导致样本高度相似训练集和验证集之间信息泄漏准确率虚高步长太大又会让样本数不够模型欠拟合。我一般取窗口的 50% 作为步长样本量能翻一倍重叠带来的冗余在深度学习里通常不是坏事。2.2 预处理脚本里藏着的几个关键参数pre_data目录下放的是预处理后的数据但预处理本身在creat_data.py里完成。你需要关注的参数有这么几个归一化方式、标签编码、训练集验证集划分比例。归一化常见的是对每个样本做 z-score 标准化也就是减均值除标准差让每个样本的均值为 0、方差为 1。这一步不做模型收敛会慢很多甚至不收敛。标签编码就是把正常、内圈故障、外圈故障、滚动体故障映射成 0、1、2、3方便后面做交叉熵损失。训练集验证集划分比例一般取 7:3 或者 8:2。这里有个坑不能随机打乱后再划分因为滑动窗口切出来的样本在时间上是相邻的随机打乱会让相邻样本同时出现在训练集和验证集里验证准确率会偏高。正确做法是按时间段划分前 70% 时间段的样本做训练后 30% 做验证。这套项目如果没做时间隔离你跑出来的 99% 准确率可能只是过拟合的假象。import numpy as np import scipy.io as sio from sklearn.model_selection import train_test_split def load_cwru_signal(file_path, key): 读取 CWRU 的 .mat 文件提取振动信号 mat_data sio.loadmat(file_path) signal mat_data[key].flatten() # DE 时间序列 return signal def sliding_window(signal, window_size1024, step512): 滑动窗口切分返回样本矩阵 samples [] for start in range(0, len(signal) - window_size, step): samples.append(signal[start:start window_size]) return np.array(samples) def normalize(samples): 逐样本 z-score 标准化 mean samples.mean(axis1, keepdimsTrue) std samples.std(axis1, keepdimsTrue) 1e-8 return (samples - mean) / std # 以正常状态为例实际要循环读取四种状态 signal load_cwru_signal(CWRU/normal.mat, X097_DE_time) samples sliding_window(signal, window_size1024, step512) samples normalize(samples) print(f正常状态样本数: {samples.shape[0]}, 每个样本长度: {samples.shape[1]})上面这段代码展示了数据管线的核心三步读信号、切窗口、归一化。window_size和step是最需要根据你的数据调整的参数normalize里的1e-8是防止除零。实际项目中你需要对四种状态分别调用这套流程然后把样本和标签拼在一起。标签可以用np.full生成正常状态全 0内圈故障全 1以此类推。2.3 数据增强在振动信号上怎么做样本不够是毕设常见问题。CWRU 每种故障状态下的数据量有限切完窗口可能只有几百个样本。数据增强在图像上很成熟在振动信号上也有几种常用手段加高斯白噪声、幅值缩放、时间平移。加噪声最直接给每个样本加上信噪比 20dB 到 30dB 的高斯噪声相当于模拟不同工况下的传感器噪声。幅值缩放就是把样本乘以一个 0.8 到 1.2 之间的随机系数模拟负载变化。时间平移就是循环移位让故障冲击出现在窗口的不同位置。注意增强只能用在训练集上验证集和测试集必须用原始样本。否则验证准确率会虚高论文里写出去容易被答辩老师问住。我一般会在训练集上做 2 到 3 倍增强验证集保持原样。3. 模型选型与训练CNN 和 DNN 两条路怎么选3.1 CNN 处理一维振动信号的卷积核怎么设cnn.py是这个项目的核心模型文件。一维 CNN 处理振动信号第一层卷积核通常设得比较大比如 64 或 128目的是捕捉信号里的局部冲击特征。轴承故障的振动信号里故障冲击表现为周期性的高频衰减振荡大卷积核能覆盖一个完整的冲击周期。后面几层卷积核可以缩小到 3 或 5用来提取更抽象的特征。池化层用最大池化池化窗口取 2 或 4。全连接层之前通常会接一个全局平均池化把特征图压成一维向量这样能减少参数量降低过拟合风险。输出层用 softmax四个神经元对应四种状态。损失函数用交叉熵优化器用 Adam学习率从 1e-3 开始试。import torch import torch.nn as nn class BearingCNN(nn.Module): def __init__(self, num_classes4): super().__init__() self.features nn.Sequential( # 第一层大卷积核捕捉冲击特征 nn.Conv1d(1, 16, kernel_size64, stride2, padding32), nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(2), # 第二层小卷积核提取抽象特征 nn.Conv1d(16, 32, kernel_size5, padding2), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size3, padding1), nn.BatchNorm1d(64), nn.ReLU(), nn.AdaptiveAvgPool1d(1) # 全局平均池化 ) self.classifier nn.Linear(64, num_classes) def forward(self, x): # x 形状: (batch, 1, 1024) x self.features(x) # (batch, 64, 1) x x.flatten(1) # (batch, 64) return self.classifier(x) model BearingCNN(num_classes4) print(model)这段代码里kernel_size64是第一层的关键参数它决定了模型能看多宽的信号片段。stride2是为了降低序列长度减少计算量。BatchNorm1d放在卷积和激活之间能加速收敛。AdaptiveAvgPool1d(1)把任意长度的特征图压成 1这样输入窗口长度变化时模型不用改结构。如果你把窗口从 1024 改成 2048这个模型照样能跑。3.2 DNN 作为基线模型的价值dnn.py是全连接网络输入是展平后的振动信号或者手工提取的特征。直接用原始信号展平输入 DNN效果通常不如 CNN因为 DNN 没有局部感受野学不到冲击的局部模式。但 DNN 有两个用处一是作为基线证明 CNN 的改进确实有效二是输入手工特征时DNN 反而更合适。手工特征包括时域特征均方根、峰值、峭度和频域特征频谱峰值、包络谱幅值。这些特征维度低通常几十维用 DNN 分类足够。如果你在论文里想对比「手工特征 DNN」和「原始信号 CNN」两条路线这个项目正好给了你两个脚本。DNN 的结构简单输入层、两到三个隐藏层、输出层。隐藏层神经元数量从 256 往下递减到 64激活函数用 ReLUDropout 率设 0.3 到 0.5。学习率和 CNN 保持一致方便对比。3.3 训练循环里必须监控的三个量训练脚本里除了损失和准确率还有三个量值得盯学习率、梯度范数、验证集损失。学习率用余弦退火或者阶梯下降从 1e-3 降到 1e-5。梯度范数如果突然变大说明可能遇到了梯度爆炸需要加梯度裁剪。验证集损失如果连续几个 epoch 不降反升就是过拟合的信号该早停了。import torch.optim as optim from torch.optim.lr_scheduler import CosineAnnealingLR device torch.device(cuda if torch.cuda.is_available() else cpu) model BearingCNN(num_classes4).to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) scheduler CosineAnnealingLR(optimizer, T_max50) for epoch in range(50): model.train() for x, y in train_loader: x, y x.to(device), y.to(device) optimizer.zero_grad() logits model(x) loss criterion(logits, y) loss.backward() # 梯度裁剪防止爆炸 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm5.0) optimizer.step() scheduler.step() # 验证阶段省略重点看验证损失是否回升weight_decay1e-4是 L2 正则化抑制过拟合。clip_grad_norm_的max_norm5.0是经验值振动信号训练时梯度偶尔会冲高加上这个更稳。CosineAnnealingLR的T_max50对应总 epoch 数学习率会按余弦曲线降到接近零。4. 避坑与排查跑不通、准确率异常、可视化翻车4.1 现象训练损失不降准确率停在 25%原因通常是标签没对齐或者输入数据没归一化。CWRU 的.mat文件里正常状态的键名和故障状态的键名不一样比如X097_DE_time和X105_DE_time如果读文件时键名写错读出来的信号可能是空的或者错的。另外如果忘了做 z-score 标准化信号幅值差异大模型第一层就饱和了。解决先打印每个样本的均值和标准差确认归一化生效。再检查标签数组的长度是否和样本数一致。用np.unique(labels, return_countsTrue)看每个类别的样本数如果某一类为 0说明读文件那步就错了。4.2 现象验证准确率 99%但测试集只有 60%这是典型的样本泄漏。滑动窗口切出来的样本在时间上相邻如果划分数据集时随机打乱训练集和验证集里会有几乎相同的样本。模型记住了这些样本验证准确率虚高但测试集用的是不同时间段的信号分布有差异准确率就掉下来了。解决按时间顺序划分前 70% 做训练后 30% 做验证和测试。或者用不同负载下的数据做交叉验证比如 0 负载训练1 负载测试这样更能反映模型的泛化能力。4.3 现象plot_scatter.py画出来的散点图所有点挤在一起last_layer_data里存的是模型倒数第二层的输出通常是 64 维或 128 维。直接取前两维画散点图如果这两维的方差很小点就会挤在一起。常见做法是先做 PCA 降到 50 维再用 t-SNE 降到 2 维。t-SNE 的困惑度参数设 30 到 50学习率设 200迭代 1000 次。解决在plot_scatter.py里加一步 PCA 预处理再送进 t-SNE。如果还是挤检查last_layer_data是不是在模型eval()模式下生成的训练模式下的 BatchNorm 会引入噪声。4.4 现象GPU 显存不够batch size 只能设 8振动信号样本长度 1024第一层卷积核 64通道数 16中间特征图占的显存不小。如果显卡只有 4GBbatch size 设 64 会直接 OOM。解决把第一层卷积的stride从 2 改成 4特征图长度直接减半。或者把窗口长度从 1024 降到 512。再不行就用梯度累积batch size 设 16累积 4 次再更新一次参数等效 batch size 64。4.5 现象换了台机器同样的代码报ModuleNotFoundError项目依赖的库版本没锁。torch、numpy、scipy、sklearn、matplotlib这几个库版本不兼容时报错五花八门。解决在 README 里补一个requirements.txt把版本号写死。常见组合是torch1.13.1、numpy1.24.3、scipy1.10.1、scikit-learn1.2.2、matplotlib3.7.1。用pip install -r requirements.txt一次性装齐。5. 把准确率从 95% 推到 99%几个我反复验证过的技巧先说一个反直觉的结论在这个项目上换更深的网络带来的提升往往不如把数据管线改对。我见过太多人把 ResNet 搬过来准确率还是 95%因为样本泄漏和归一化的问题没解决。数据管线对了一个三层 CNN 就能到 98% 以上。第一个技巧是多尺度窗口融合。不要只用 1024 一种窗口长度同时切 512、1024、2048 三种分别送进三个 CNN 分支最后把特征拼接起来分类。512 窗口对高频冲击敏感2048 窗口覆盖更完整的周期融合后模型能看到不同时间尺度的模式。这个改动在验证集上通常能涨 1 到 2 个百分点。第二个技巧是标签平滑。把硬标签 0 和 1 换成 0.1 和 0.9防止模型对某一类过度自信。交叉熵损失里加一个label_smoothing0.1参数就行PyTorch 的CrossEntropyLoss直接支持。这个改动对最终准确率影响不大但能让模型的置信度更合理论文里画混淆矩阵时好看很多。第三个技巧是测试时增强。测试阶段对同一个样本做多次加噪和平移把多次预测结果平均。相当于用增强后的多个版本投票能降低单次预测的随机性。代价是推理时间翻几倍但毕设场景下完全可接受。def test_time_augment(model, x, n_aug5): 测试时增强对同一个样本加不同噪声预测结果平均 model.eval() preds [] with torch.no_grad(): for _ in range(n_aug): noise torch.randn_like(x) * 0.05 # 小幅噪声 logits model(x noise) preds.append(torch.softmax(logits, dim1)) return torch.stack(preds).mean(dim0) # 使用时对每个测试样本调用取 argmax 作为最终类别n_aug5是增强次数噪声标准差0.05是经验值太大会破坏信号特征。这个函数返回的是平均后的概率分布比单次预测更稳。最后一个技巧是保存倒数第二层特征做可视化。last_layer_data目录就是干这个的。训练完模型后把验证集样本送进模型取全连接层之前的输出存下来。用 t-SNE 降到 2 维画散点图如果四类分得很开说明模型学到了判别性特征如果混在一起说明模型还没训好回去调学习率或者加数据。这个图放在论文里比准确率数字更有说服力。从那以后我每次跑完训练都会强制走一遍「看损失曲线、看混淆矩阵、看 t-SNE 散点图」这三步少一步都不敢写进论文。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。