news 2026/10/2 14:18:49

基于深度学习和1D-CNN的滚动轴承故障诊断实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于深度学习和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_len=1024, step_len=256): 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_classes=10): super().__init__() self.features = nn.Sequential( nn.Conv1d(1, 16, kernel_size=3, padding=1), # 输入通道1,输出16 nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(2), # 长度减半 nn.Conv1d(16, 32, kernel_size=3, padding=1), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size=3, padding=1), 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))

代码里padding=1是为了保持卷积分支的序列长度不变,池化层每次对半降采样,输入 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_decay=1e-4做 L2 正则化。

import torch.optim as optim model = BearingCNN(num_classes=10) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50, eta_min=1e-5)

T_max要设成总训练 epoch 数,这样学习率会在训练结束时恰好降到eta_min。如果中途验证集准确率长时间不动,先看是不是学习率退化得太快,调到T_max=60或者直接改成 StepLR 每隔 20 个 epoch 乘以 0.5。

4. 训练与评估:损失、准确率、混淆矩阵与可视化

4.1 训练循环的标准写法

训练代码本身不复杂,但有几个细节直接影响最终效果。数据加载要用DataLoader并开启shuffle=True,确保每个 batch 里包含各类别的样本,否则同一批数据全是同一类故障,模型参数更新会来回震荡。batch size 一般取 32 或 64,太小噪声大、收敛慢,太大会把显存放爆,1024 点输入的话 64 基本安全。

from torch.utils.data import TensorDataset, DataLoader train_dataset = TensorDataset(torch.tensor(train_x, dtype=torch.float32).unsqueeze(1), torch.tensor(train_y, dtype=torch.long)) train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) 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(f"Epoch {epoch+1: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_names=class_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(dim=2)) # 全局平均池化 feat_all = torch.cat(features).numpy() tsne = TSNE(n_components=2, perplexity=30, random_state=42) 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_classes=10).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()), lr=0.0001, weight_decay=1e-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')

从那以后我每次做这类故障诊断项目都强制走一遍这个流程:先确认数据切分没有泄漏,再训练基线模型,最后用混淆矩阵定位易混类别。这套资源里的数据集、训练脚本和模型结构文件都是按这个流程组织的,建议你完整跑通一遍再改参数。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 14:17:59

嵌入式Linux开发入门:从交叉编译到系统构建的21天实战路径

1. 嵌入式Linux为什么劝退率这么高:先搞清楚难点在哪做嵌入式开发这些年,我见过太多人从单片机转Linux,或者在大学里学了C语言和操作系统原理,但一碰到真正的嵌入式Linux项目就完全蒙住。资料买了一堆,教程收藏了几百个…

作者头像 李华
网站建设 2026/10/2 14:17:51

从组合导航毕设到交稿:我愿这样给 AI 论文工具排座次

先把场景说具体:导航与信息工程专业很常见的一类毕设,是做 “城市复杂环境下 GNSS/INS 组合导航定位算法设计与验证”。你要读卫星导航、惯性器件、卡尔曼滤波、松耦合/紧耦合相关文献,建立误差模型,写仿真或数据处理代码&#xf…

作者头像 李华
网站建设 2026/10/2 14:17:18

SSM+Flask双引擎架构:商城系统设计与实战全解析

做商城类系统,我前后折腾过好几个版本。最开始图省事,一个单体JSP项目硬扛所有模块,结果用户管理、商品库存、订单状态机全挤在一起,改一个BUG牵一发动全身。后来换成SpringSpringMVCMyBatis这套组合,也就是大家常说的…

作者头像 李华
网站建设 2026/10/2 14:16:33

ADS入门实战:微带贴片天线原理图仿真与S11调参全流程

最近后台不少同学问我,ADS到底该怎么入门。我给的答案一直是同一个:别一上来就碰PA、碰混频器,先拿微带贴片天线原理图仿真练手。原因很简单,微带贴片天线几乎涵盖了ADS里最核心的几个操作——工程创建、衬底设置、微带线元件调用…

作者头像 李华
网站建设 2026/10/2 14:16:32

eChain数字钥匙串:本地优先的卡包应用设计与实现

eChain 这个名字,是我给自己做的一个手机应用起的“花名”,直译过来就是“电子钥匙扣”。起因特别朴素:我裤兜里的实体卡实在太多了。小区门禁卡、图书馆借书卡、健身房会员卡、楼下打印店的储值卡、超市会员卡,再加上偶尔发的临时…

作者头像 李华
网站建设 2026/10/2 14:16:26

AUTOSAR TM模块详解:全局时间同步原理、Vector配置与实战避坑

做AUTOSAR开发这几年,要说哪个模块最容易被低估,我第一个提名TM——Time Management,时间管理。底盘域控和智驾域控联调的时候,一个常见故障现象就是:明明两个控制器都在跑同样的控制周期,一上CANoe看时间戳…

作者头像 李华