资讯详情 基于深度学习的滚动轴承故障诊断:从数据切分到一维CNN实战
📅 2026/10/2 18:02:07
简介这份资源是面向计算机相关专业毕业设计与项目实战学习者的滚动轴承故障诊断完整方案基于深度学习框架实现可直接作为毕设、课程设计或期末大作业使用。项目围绕CWRU轴承数据集展开涵盖数据预处理、模型搭建、训练与可视化分析等环节帮助读者理解从原始振动信号到故障分类的完整流程。压缩包共41个文件约34.87MB其中30个mat文件为实验数据4个py脚本负责数据生成、CNN与DNN模型构建及散点图绘制6个md与1个txt提供说明文档目录结构清晰便于按模块查阅。目前已有970人学习下载说明该方案在同类毕设选题中具有较高参考价值。读者可获得经过严格调试、下载即用的源码与全部数据直接运行即可复现实验同时能借鉴数据划分、模型对比与结果展示的写法为撰写论文和答辩提供扎实的工程支撑。1. 从一份轴承振动数据说起深度学习故障诊断到底在做什么一台电机在试验台上连续跑 72 小时加速度传感器贴在轴承座上采样率 12 kHz每小时存一个 CSV。数据拿到手横轴是时间纵轴是振动幅值肉眼看就是一条抖动的曲线。可当内圈出现剥落时这条曲线里会混进周期性冲击成分频率大约等于转频乘以一个跟滚珠数、接触角有关的系数。人耳听不出来示波器也未必看得清但把它转成频谱或时频图故障特征就浮出来了。基于深度学习的滚动轴承故障诊断本质就是让模型自动从这些振动信号里学出「正常 / 内圈故障 / 外圈故障 / 滚动体故障」的判别边界替代过去靠人工设计特征加 SVM、随机森林的老路子。这份「项目源码 全部数据」的毕业设计包通常包含几类东西原始振动信号或已切好的样本、Python 训练脚本、模型定义、评估代码可能还有一份说明文档。它解决的是「我不想从零采数据、也不想自己搭 CNN但我要跑通一条完整链路并写进论文」这个诉求。适合两类人一是做机械、自动化方向毕业设计、需要可复现实验的学生二是刚转深度学习、想找一个信号类任务练手的工程师。前提是你得会装 Python、能看懂张量维度剩下的可以边做边补。2. 数据怎么读、标签怎么对把 CWRU 风格的目录变成能训练的数组2.1 先认清你手里的是哪种数据组织方式滚动轴承公开数据集里最常见的是凯斯西储大学CWRU那套按「故障位置 故障直径 负载」分文件文件名里带 0.007、0.014、0.021 这类数字代表故障尺寸英寸。毕业设计包里的数据大概率是它的变体或者作者自己用试验台采的。不管哪种你第一步不是写模型而是把目录结构打印出来看清楚。import os root data # 换成你解压后的实际目录 for dirpath, dirnames, filenames in os.walk(root): # 只打印有文件的目录避免刷屏 if filenames: print(dirpath, -, len(filenames), files) print( 示例:, filenames[:3])这段代码的作用是「侦察」确认数据是按类别分文件夹还是所有文件平铺、靠文件名区分标签。参数上root指向解压后的顶层目录filenames[:3]只取前三个避免输出过长。跑完你会得到两种典型结果一种是data/normal/、data/inner/这种标签直接来自文件夹名另一种是data/97.mat、data/105.mat这种标签藏在文件名或配套的说明表里。前者好办后者必须找到作者给的标签映射否则后面全错。注意如果目录里混着.mat、.csv、.txt三种格式先统一读成同一种别在训练循环里临时判断否则 DataLoader 会报维度不一致。2.2 用滑动窗口把长信号切成样本原始信号往往是一条几万到几十万点的长序列模型不能直接吃。常见做法是滑动窗口切分窗口长度取 1024 或 2048 点步长取窗口的一半这样既有重叠增强样本量又不至于让相邻样本几乎一样导致验证集泄漏。import numpy as np def make_samples(signal, label, win1024, step512): 把一条长信号切成 (N, win) 的样本并复制标签 xs, ys [], [] for start in range(0, len(signal) - win 1, step): seg signal[start:start win] # 去均值消除直流分量这一步对振动信号很关键 seg seg - np.mean(seg) xs.append(seg) ys.append(label) return np.array(xs, dtypenp.float32), np.array(ys, dtypenp.int64) # 假设 signal 是长度为 120000 的一维数组label1 表示内圈故障 X, y make_samples(signal, label1) print(X.shape, y.shape) # 期望类似 (232, 1024) (232,)逻辑说明win是窗口长度决定单个样本覆盖多长时间step是步长越小样本越多但冗余越大一般取win // 2。去均值那行不是可选项振动信号常带零点漂移不去掉会让模型学到跟故障无关的偏置。参数怎么改采样率 12 kHz 时1024 点约 85 ms足够覆盖一个冲击周期如果你的采样率只有 1 kHz窗口要相应放大到 4096 以上否则一个故障冲击都装不下。2.3 标签编码与训练集划分的两个硬约束标签必须从字符串转成从 0 开始的整数且训练集和测试集的类别顺序要一致。更关键的是划分方式不能随机打乱所有样本再切因为同一段长信号切出来的相邻窗口高度相似随机切会让测试集里混进训练集的「近亲」准确率虚高到 99% 但换台设备就崩。from sklearn.model_selection import train_test_split # 正确做法按原始信号文件划分而不是按切好的窗口划分 # 假设 file_ids 记录每个窗口来自哪个文件 file_ids np.array([...]) # 与 X 等长 unique_files np.unique(file_ids) train_files, test_files train_test_split(unique_files, test_size0.3, random_state42) train_mask np.isin(file_ids, train_files) X_train, y_train X[train_mask], y[train_mask] X_test, y_test X[~train_mask], y[~train_mask]参数说明test_size0.3是常见起点数据少时可以 0.2random_state固定住保证你论文里的数字别人能复现。这一步是很多毕业设计翻车的地方——用随机切分跑出 99.8%答辩老师一问「换负载还准吗」就答不上来。按文件划分虽然数字会掉到 90% 出头但那是真实水平。3. 模型怎么搭一维 CNN 够用别一上来就上 Transformer3.1 为什么振动信号首选一维卷积而不是二维图像模型把振动信号转成时频图再喂给二维 CNN是另一条路但它多了一步 STFT参数窗长、重叠选不好反而丢信息而且计算量翻倍。一维 CNN 直接在原始时序上卷卷积核沿时间轴滑动天然匹配「冲击是局部事件」这个先验。常见做法是堆 3 到 5 个卷积块每块「Conv1d BatchNorm ReLU MaxPool」最后全局池化接全连接分类头。这个结构在 CWRU 上做到 95% 以上不难关键是别把模型堆太深——几千个样本撑不起几十层网络。import torch import torch.nn as nn class BearingCNN(nn.Module): def __init__(self, n_classes4): super().__init__() self.features nn.Sequential( # 输入 (B, 1, 1024) nn.Conv1d(1, 16, kernel_size15, stride2, padding7), nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(16, 32, kernel_size7, stride1, padding3), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size5, stride1, padding2), nn.BatchNorm1d(64), nn.ReLU(), nn.AdaptiveAvgPool1d(1), # 全局平均池化输出 (B, 64, 1) ) self.classifier nn.Linear(64, n_classes) def forward(self, x): x self.features(x).squeeze(-1) return self.classifier(x)逻辑说明第一层卷积核取 15、步长 2是为了在早期就压缩时间维度、抓住宽一点的冲击包络后面核变小、步长变 1做精细特征组合。AdaptiveAvgPool1d(1)把任意长度压成 1好处是输入窗口长度变了也不用改全连接层。参数怎么改n_classes按你的实际类别数填三分类就写 3如果欠拟合训练准确率都上不去把通道数 16/32/64 翻倍如果过拟合训练高验证低先加 Dropout 再考虑减通道。3.2 训练循环里必须盯住的三个量训练脚本谁都会写但毕业设计里真正决定成败的是三个量学习率、批大小、以及验证指标选什么。学习率从 1e-3 起步配 Adam 是稳妥选择批大小 64 或 128太小梯度噪声大太大泛化差。验证指标别只看准确率类别不均衡时看宏平均 F1。from torch.utils.data import DataLoader, TensorDataset from sklearn.metrics import f1_score train_ds TensorDataset(torch.from_numpy(X_train).unsqueeze(1), torch.from_numpy(y_train)) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) model BearingCNN(n_classes4) opt torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() for epoch in range(30): model.train() for xb, yb in train_loader: opt.zero_grad() loss criterion(model(xb), yb) loss.backward() opt.step() # 每个 epoch 结束后在测试集上算宏 F1 model.eval() with torch.no_grad(): pred model(torch.from_numpy(X_test).unsqueeze(1)).argmax(1).numpy() print(epoch, macro-F1:, f1_score(y_test, pred, averagemacro))参数说明unsqueeze(1)是把 (N, 1024) 变成 (N, 1, 1024)因为 Conv1d 要求通道维。lr1e-3是 Adam 的常用起点若 loss 震荡就降到 3e-4。epoch30不是定数看验证 F1 连续 5 轮不涨就可以停这就是最朴素的早停。跑完你会看到 F1 从 0.6 左右爬到 0.95 上下如果一直卡在 0.25四分类的随机水平八成是标签没对上或数据没归一化。3.3 归一化放在切窗之后还是之前这是个容易被忽略但影响很大的细节。正确顺序是先切窗再对每个窗口单独做 z-score 归一化而不是对整条长信号归一化后再切。原因是对整条信号归一化时均值和方差里混进了故障冲击的极端值正常段的幅值会被压得偏小模型反而更难区分。def zscore_per_window(X): # X: (N, win) mean X.mean(axis1, keepdimsTrue) std X.std(axis1, keepdimsTrue) 1e-8 # 防止除零 return (X - mean) / std X_train zscore_per_window(X_train) X_test zscore_per_window(X_test)1e-8是数值稳定项防止某个窗口恰好恒定导致除零。注意测试集的归一化只能用测试集自己的统计量不能借用训练集的均值和方差——虽然对 z-score 影响不大但养成这个习惯换到需要全局统计的标准化方法时就不会犯数据泄漏的错。4. 避坑与排查那些让准确率从 99% 掉到 60% 的细节4.1 现象训练准确率 99%测试准确率 60% 出头原因按窗口随机划分数据集同一段信号切出的相邻窗口同时进了训练和测试模型其实在「背答案」。解决改成按原始文件划分确保测试集里的信号片段在训练阶段完全没见过。改完后准确率会降但那是真实泛化能力。4.2 现象换一个负载比如从 0 马力换到 3 马力准确率暴跌原因模型学到了跟负载相关的幅值或转速特征而不是故障本身的冲击模式。解决训练时做幅值归一化每窗口 z-score并尽量把不同负载的数据都放进训练集如果只想用单一负载训练那论文里就别声称跨负载泛化。4.3 现象loss 变成 NaN原因学习率太大或者输入里有 NaN / Inf。解决先把学习率降到 1e-4 试一轮再在切窗后加一行assert np.isfinite(X).all()定位脏数据。振动数据里偶尔会有传感器掉线产生的全零段全零窗口做 z-score 会除零前面加的1e-8就是防这个。4.4 现象验证集 F1 忽高忽低每次跑结果都不一样原因批大小太小、没设随机种子、或者数据量本身太少。解决固定torch.manual_seed(42)和np.random.seed(42)批大小提到 128如果数据只有几百个样本考虑用交叉验证代替单次划分报告均值而不是某一次的数字。4.5 现象模型在测试集上很好但拿一段新采的信号预测全是同一类原因新信号的采样率、量纲或安装位置跟训练数据不一致归一化后分布对不上。解决确认新数据采样率与训练一致如果不一致先重采样到同一频率再检查传感器灵敏度系数是否相同。这个坑在答辩现场被问到「实际怎么用」时最容易暴露。5. 把准确率再往上推一点时频图融合与迁移的取舍模型跑通、论文数字够看之后如果你还想再进一步有两条路值得试但都要清楚代价。第一条是时频图融合。把同一段振动信号同时送进一维 CNN 和一条「STFT 转图 二维 CNN」的支路最后拼接特征。做法上STFT 用n_fft256、hop_length64得到约 129×N 的时频矩阵再当单通道图像喂给一个小型二维 CNN。融合后准确率通常能涨 1 到 3 个百分点但训练时间翻倍而且 STFT 参数需要调。我的习惯是先把一维 CNN 的 baseline 做扎实再考虑融合否则两个分支都调不好你分不清涨点来自哪里。第二条是迁移学习。如果你手头只有几百个样本可以从一个在大量振动数据上预训练过的一维 CNN 出发冻结前几层只微调分类头。但要注意预训练模型的输入长度、采样率必须和你的一致否则卷积核学到的频率响应对不上。没有合适预训练权重时别硬套 ImageNet 的二维模型——轴承信号和自然图像差得太远迁移过去基本是负收益。验证方法上我一般会做两件事一是画混淆矩阵看错分集中在哪两类之间内圈和滚动体最容易混二是做一次「加噪声测试」往测试信号里叠 SNR5dB 的高斯白噪声看准确率掉多少。掉得少说明模型学的是结构特征掉得多说明它依赖了脆弱的幅值细节。这两个动作花不了十分钟但能让你在论文里多写一段有说服力的分析。最后说个我自己的教训刚做这类项目时我总想一步到位上最复杂的模型结果调了两周还不如同学一个三层 CNN 跑得好。后来才明白轴承故障诊断这个任务数据划分和归一化做对了简单模型就能到 95%这两步做错了再深的网络也是白搭。先把第 2 章那几行切窗和划分的代码抠明白比换模型有用得多。希望帮到你。本文还有配套的精品资源点击获取