尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于一维CNN与PyTorch的滚动轴承故障诊断实战

基于一维CNN与PyTorch的滚动轴承故障诊断实战 简介振动信号分析是旋转机械状态监测的核心技术传统频谱分析在变转速、强噪声环境下往往力不从心。深度学习为解决这一难题提供了新思路其核心原理是通过多层卷积网络自动从原始波形中学习故障特征替代人工特征工程。一维卷积神经网络尤其适合处理时序信号它能在不同时间尺度上提取局部冲击模式实现端到端的智能诊断。这一技术广泛应用于工业设备健康管理、预测性维护等场景可显著降低非计划停机风险。本文基于凯斯西储大学公开数据集使用PyTorch搭建一维CNN模型完整实现滚动轴承故障分类涵盖数据预处理、模型设计、训练调优与评估部署测试集准确率接近99%为工业故障诊断落地提供了可复用的工程实践参考。 做滚动轴承故障诊断这个方向我最怕听到的一句话是“直接用FFT看看频谱就行”。不是说频谱分析没用而是当设备转速波动、负载变化、背景噪声一上来人工看频谱这套活儿不仅耗时而且极度依赖老师傅的经验。我自己在这个坑里踩过很久后来把整套流程迁到深度学习上用Python实现了端到端的智能诊断系统从数据预处理到模型训练再到结果可视化全部一条龙跑通。这篇文章就是把这段完整经历拿出来分享包括数据集怎么选、模型怎么搭、训练参数怎么调、遇到哪些坑以及最终效果如何。适合正在做工业故障诊断、设备健康管理或者刚入坑深度学习想找个实际项目练手的读者。先交代一下项目的基本盘诊断对象是滚动轴承故障类型覆盖正常、内圈故障、外圈故障、滚动体故障四类振动信号作为唯一输入。核心思路是用一维卷积神经网络直接从原始振动波形中学习故障特征省掉人工构造特征这一步。整套代码基于Python完成深度学习框架选PyTorch数据集使用公开的凯斯西储大学(CWRU)轴承数据。项目最终在测试集上达到了接近99%的分类准确率。1. 项目全景设计从需求到方案选型1.1 项目要解决的核心问题滚动轴承是旋转机械里最容易出故障的部件之一一旦坏了轻则设备停机重则整条产线瘫掉。传统做法是定期停机检修但这既费钱又影响生产。拿我做过的一个电机轴承包案例来说一次非计划停机造成的损失抵得上好几套传感器加边缘计算设备的采购费用。所以大家越来越倾向于做在线监测和智能预警也就是设备还在转的时候就能通过振动信号判断它有没有故障、故障在哪个位置、严重程度如何。本项目的核心目标就是把“振动信号→故障类型”这一映射关系用深度学习模型自动建立起来。这里有个很关键的工程问题振动信号是非平稳、非线性的轴承不同位置的故障会在不同频段激起不同的共振响应。传统特征工程需要人工计算时域统计量均方根、峰值因子、峭度再做频域分析包络谱、小波分解这套流程研究价值高但落地时每个人提取的特征还不一样很难标准化复用。深度学习模型则直接把原始波形喂进去由卷积层自行学习多尺度特征。我整个项目的设计逻辑就是围绕“少做人工特征、多做数据驱动”来展开的。1.2 为什么选择深度学习而不是传统信号处理方法先给传统方法一个公道评价FFT包络谱在恒定转速、单一工况下确实好用尤其是对轴承内圈、外圈故障特征频率的判断物理意义非常清晰。但实际工业现场没这么理想转速波动会把特征频率抹开负载大范围变化会让幅值规律变得不稳定再加上齿轮啮合、不对中、松动等干扰源的信号混叠在一起人工包络分析经常做出矛盾的判断。深度学习路的优势在于它可以自动学习一个从原始信号到故障类别的高维非线性映射。模型内部数十个卷积核相当于在不同频段、不同时间尺度上做自适应特征提取这个过程比人肉调包络参数要稳定得多。我并不是说深度学习要完全取代传统方法在实际工程里两者可以互补——用传统方法做信号质量的快速校验用深度学习做最终分类。但在项目的主体框架里深度学习的端到端能力确实最适合做成一个统一的智能诊断系统。还有一个很现实的选型理由深度学习模型的推理速度极快。我训练好的模型在普通CPU上处理一段1024个点的信号只需要几毫秒完全能满足在线实时诊断的延迟要求。如果换成嵌入式设备和TensorRT加速甚至可以做到微秒级。这对后续往边缘端部署来说是非常大的优势。1.3 技术栈选型与版本说明Python在工业算法领域几乎是统治级的存在所以语言层面没有任何纠结空间。深度学习框架我在PyTorch和TensorFlow之间做了一轮对比最终选了PyTorch。原因包括三个方面第一PyTorch的动态计算图机制在调试模型结构时非常直观断点打进去能看到每个张量的shape第二社区生态现在明显向PyTorch倾斜很多预训练模型和论文代码都优先给PyTorch版本第三torchvision、torchaudio等配套库对信号处理相关的数据流水线支持很友好。我的开发环境如下表所示各位可以参照这个版本组合实测下来兼容性很稳组件版本说明Python3.9.13太老的版本对PyTorch 2.x支持不好PyTorch2.0.1使用CPU/GPU自动切换逻辑numpy1.24.3注意与PyTorch版本兼容scipy1.10.1用于信号读取与预处理scikit-learn1.2.2数据划分与评估指标计算matplotlib3.7.1结果可视化这里特别提醒一下Python版本不要用太新的比如3.12在部分情况下安装PyTorch会出兼容问题。你要是刚开始配环境建议直接用Anaconda创建独立环境避免把系统Python弄乱。后面第5章我会单独讲常见环境坑。2. 数据集准备完整训练的前提2.1 为什么选择凯斯西储大学CWRU轴承数据集做故障诊断的同行对CWRU数据集应该都不陌生它是这个领域最经典的公开基准数据。数据由美国凯斯西储大学轴承数据中心发布采集对象是电机驱动系统中的轴承振动信号。整个数据集的设备配置是在电机两端分别安装加速度传感器采样频率有12k和48k两档覆盖了负载0到3马力四种工况故障类型包括内圈、外圈、滚动体三类每类又有0.007英寸、0.014英寸、0.021英寸、0.028英寸等不同损伤尺寸。选择CWRU数据集有几个现实考量。一是公开好获取不需要自己搭实验台这对很多人来说是零成本起步的关键。二是样本量大每次实验采集的连续信号长度都足够滑窗切出大量独立样本为深度模型训练提供了充足的数据保障。三是标注信息完善每个文件名的后缀直接标明故障位置和损伤程度建标签非常方便。四是社区公认度高用这个数据集跑出来的结果可以跟文献横向对比验证模型的可靠性。当然CWRU数据集也有它的局限性比如采样环境比真实工业现场干净很多工况变化相对简单。我建议把它当作算法验证的基石而不是最终部署场景的完全仿真。我的项目主体流程是在CWRU上完成的后续如果采集到现场轴承信号只需要做同样的预处理流程再对模型做微调即可迁移应用。2.2 数据预处理滑窗切分、归一化与标签编码数据预处理的第一个决策点是样本长度。CWRU数据集的采样率是12kHz意味着每秒有12000个采样点。滚动轴承故障特征频率通常在几十赫兹到几百赫兹之间我按经验选择单个样本长度为1024个点约等于85毫秒的信号这个长度既能覆盖足够多的振动周期又不会因为过长导致计算量过大。接下来用滑动窗口切分连续信号窗口步长设为512个点也就是重叠率50%。这么做的好处是样本数量翻倍同时相邻样本之间仍有一定独立性不容易造成严重的数据泄漏。切分过程用一段简洁的代码就能实现import numpy as np def sliding_window_slice(raw_signal, window_size1024, stride512): samples [] total_length len(raw_signal) for start in range(0, total_length - window_size, stride): end start window_size samples.append(raw_signal[start:end]) return np.array(samples)切好样本后我做了两步重要处理。第一步是归一化这里我选择z-score标准化公式为$$x_{\text{norm}} \frac{x - \mu}{\sigma}$$其中$\mu$和$\sigma$是每个样本自身的均值和标准差。这样做的目的是消除不同实验工况下信号幅值绝对水平的差异让模型专注于波形形态而不是被整体幅值带偏。第二步是给每个样本打标签四类目标对应0、1、2、3然后用PyTorch内置的交叉熵损失函数标签直接用整数索引即可不需要手动做one-hot编码。数据切分完成后我统计了一下样本数量分布正常情况下每个类别能切出几千个样本完全够训练一个中等规模的卷积网络。有一点需要特别注意统计样本时不要只看总数要确认每个类别的样本量基本均衡如果某个故障类别样本明显偏少后续训练很容易出现偏向多数类的问题。2.3 数据集划分策略与防泄漏要点很多初学者在做数据划分时习惯直接random split但在时序信号场景里这个做法隐患很大。原因是同一段连续信号滑窗切出来的相邻样本之间高度相关如果简单随机划分训练集和测试集之间可能出现“近亲样本”导致测试准确率虚高。说白了就是模型作弊了它可能已经记下了测试集样本的邻近片段。我采用的做法是先按“实验文件”级别分组再在文件组层面划分数据集。假设某个故障类型的原始信号长度为20秒我先把它切成100个文件组每个组内再做滑窗采样划分时保证同一文件组的所有样本全部进入训练集或者全部进入测试集不让它们跨集出现。这样可以最大限度避免时间邻近导致的数据泄漏。实际划分比例我用了60%训练、15%验证、25%测试如下表所示数据集样本数用途训练集约12000模型权重学习验证集约3000超参数调优与早停判断测试集约5000最终泛化性能评估这里比例没有采用常见的8:1:1是因为我宁可训练数据稍少一点也要保证测试集足够大让最终的准确率置信区间更窄。数据加载部分我用了PyTorch的Dataset和DataLoader把预处理后的样本打包成张量设置batch_size为64num_workers根据机器配置设为4。这套流程跑下来数据加载不再是训练瓶颈。3. 核心模型设计一维CNN网络的搭建与训练3.1 模型架构设计思路针对一维振动信号最自然的网络结构就是一维卷积神经网络。它的卷积核在时间轴上滑动相当于用不同长度的滤波器去匹配信号中的局部模式这跟传统带通滤波器的思想有异曲同工之处但参数完全由数据驱动学习。我的模型结构经过了多轮迭代最终确定为四个卷积模块加一个分类头的结构。具体每层参数配置如下层卷积核数卷积核大小池化输出维度输入层---1024×1Conv1d_1163, padding1MaxPool(2)512×16Conv1d_2323, padding1MaxPool(2)256×32Conv1d_3643, padding1MaxPool(2)128×64Conv1d_41283, padding1MaxPool(2)64×128分类头---4每一层都带上BatchNorm和ReLU激活函数。BatchNorm在这里起到两个作用一是缓解梯度消失让深层网络的训练更稳定二是对每批数据的分布做归一化让模型对输入幅值波动更鲁棒。分类头用全局平均池化将特征图压缩成一维向量再接一个线性层输出四个类别的logits。全局平均池化比直接Flatten参数量更少也不容易过拟合。模型定义代码如下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_size3, padding1), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(16, 32, kernel_size3, padding1), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size3, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(64, 128, kernel_size3, padding1), nn.BatchNorm1d(128), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), ) self.classifier nn.Sequential( nn.AdaptiveAvgPool1d(1), nn.Flatten(), nn.Linear(128, num_classes) ) def forward(self, x): x self.features(x) x self.classifier(x) return x这里要解释一下为什么卷积核大小选了3而不是更大的5或7。小卷积核配合深层堆叠能获得更大的感受野同时参数量更少。而且工业信号里的局部冲击特征通常在几个采样点内就完成一次起落卷积核过大反而会把它抹平均。实测下来kernel_size3的效果比kernel_size7高大约1个百分点。3.2 训练配置与超参数选择训练配置的每个细节都会影响最终性能。我用的损失函数是nn.CrossEntropyLoss优化器选择Adam而不是SGD。Adam对学习率的敏感度低收敛速度快特别适合从零开始训练的实验阶段。当然Adam有个缺点是可能收敛到sharp min泛化略有折扣所以我把weight_decay设成1e-4对模型参数做L2正则化相当于在优化目标里加一个参数范数的惩罚项抑制过拟合。学习率的初始值设为0.001批量大小64。训练轮数设定为50但其实到第30轮左右模型就开始趋于收敛。我在训练中加入了余弦退火学习率调度器做法是让学习率随训练轮数从0.001平滑衰减到接近0这样可以在训练后期让权重落到更平滑的极小值点泛化性能更好。训练循环的核心代码如下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(), lr0.001, weight_decay1e-4) scheduler CosineAnnealingLR(optimizer, T_max50) def train_one_epoch(): model.train() total_loss, correct, total 0.0, 0, 0 for x_batch, y_batch in train_loader: x_batch x_batch.to(device) y_batch y_batch.to(device) optimizer.zero_grad() outputs model(x_batch) loss criterion(outputs, y_batch) loss.backward() optimizer.step() total_loss loss.item() pred outputs.argmax(dim1) correct (pred y_batch).sum().item() total y_batch.size(0) return total_loss / len(train_loader), correct / total训练之前有个容易被忽略的步骤确认输入张量的维度。卷积神经网络期望输入是(batch, channel, length)的形状而原始切分出来的样本是(batch, length)所以需要先通过unsqueeze(1)加一个通道维度。任何channel数不对的问题模型第一层就会报错。3.3 训练过程监控与模型保存训练不是模型定义完就万事大吉过程监控非常关键。我在每个epoch结束时同时记录训练集和验证集的损失值与准确率并打印出来。训练集loss持续下降而验证集loss开始反弹这是过拟合的经典信号需要提前停掉。我在代码里设置了early stopping机制如果验证集准确率连续8个epoch没有刷新历史最优就自动终止训练恢复历史最优权重。模型保存方面要区分两个概念只存权重还是存整个模型。我推荐只存state_dict也就是模型的参数字典。这样文件小加载灵活后续如果改了模型类再加载权重也不容易出bug。保存和加载的代码如下# 保存模型 torch.save(model.state_dict(), best_bearing_cnn.pt) # 加载模型 model BearingCNN(num_classes4) model.load_state_dict(torch.load(best_bearing_cnn.pt, map_locationcpu)) model.eval()训练完成后的效果让我比较满意50轮跑下来验证集准确率稳定在98.5%以上。整个训练过程用GPU大约十来分钟用CPU大约需要四十分钟左右。如果机器没有独立显卡用小数据集把epoch缩到20也能先跑通流程把精力放在数据部分这个我会在常见问题里展开。4. 系统评估与可视化部署4.1 混淆矩阵与分类报告解读准确率只是一个笼统的指标真正要看的是混淆矩阵。我习惯把所有测试样本的预测结果汇总画出4x4的混淆矩阵行代表真实标签列代表预测标签。对于CWRU数据集来说最常发生的混淆是在内圈故障和外圈故障之间因为两者的特征频率在数值上可能比较接近模型容易犹豫。我用scikit-learn的classification_report来计算每个类别的精确率、召回率和F1分数。指标定义上精确率真正例数/(真正例数假正例数)召回率真正例数/(真正例数假负例数)F1是两者的调和平均数。多分类问题中只看准确率会有盲点比如总体准确率很高但某个小众类别识别很差F1就能把这层皮剥开。我实测下来F1均分接近99%说明模型没有严重偏科。可视化混淆矩阵的代码如下生成的图可以直接贴到项目报告里import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay y_pred [] y_true [] model.eval() with torch.no_grad(): for x_batch, y_batch in test_loader: x_batch x_batch.to(device) outputs model(x_batch) preds outputs.argmax(dim1).cpu().numpy() y_pred.extend(preds) y_true.extend(y_batch.numpy()) cm confusion_matrix(y_true, y_pred) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labels[Normal, Inner, Outer, Ball]) disp.plot(cmapBlues) plt.show()4.2 特征可视化t-SNE降维观察模型分类准确率高不代表特征就很干净。为了验证模型确实学到了可分的特征空间我把训练好的模型当作特征提取器取全局平均池化层的输出作为每个样本的特征向量再用t-SNE降维到二维平面做散点图。这里我是手动取特征向量只需要在模型forward里接一个临时输出。实操时我把模型倒数第二层的输出保存下来代码大致逻辑先拿到与这个分支对应的特征然后调用sklearn.manifold.TSNE降维。由于t-SNE计算复杂度随样本数几何增长我随机抽取每个类别各200个样本参与可视化。最终散点图上四类样本呈现非常清晰的聚类同类紧挨异类远离。这个图不仅适合放在论文里做支撑材料也能直观证明网络的表征能力。需要提醒的是t-SNE对超参数特别是perplexity比较敏感我习惯把perplexity设为30和默认值一致展示效果相对稳定。如果发现聚类图一团糟先别急着改网络先检查是不是类别标签错乱。4.3 模型导出与推理代码示例训练完成后要把模型用到实际诊断场景就需要一个简洁的推理脚本。推理阶段最关键的一点是先调用model.eval()切换成评估模式这会关闭Dropout和BatchNorm的统计更新保证推理结果稳定。我把单条样本的推理逻辑封装成函数输入一段1024点的振动信号输出预测类别和对应置信度。推理代码如下def infer_bearing(model, signal, devicecpu): model.eval() if len(signal) 1024: raise ValueError(Signal length must 1024) # 截取最近1024个点并做z-score标准化 segment signal[-1024:] segment (segment - segment.mean()) / (segment.std() 1e-8) tensor torch.FloatTensor(segment).unsqueeze(0).unsqueeze(0).to(device) with torch.no_grad(): logits model(tensor) probs torch.softmax(logits, dim1) pred_idx logits.argmax(dim1).item() conf probs[0][pred_idx].item() return pred_idx, conf这个推理函数在嵌入式设备上也能跑因为模型本身参数量很小整体不到几十KB。边缘部署时可以把模型导出为TorchScript或者ONNX格式再丢到对应的推理引擎里执行。我在项目里导出ONNX在嵌入式设备上实测单次推理耗时比原PyTorch版本快约一倍。5. 常见问题与排查技巧实录5.1 Python环境与依赖包问题新手最容易卡在环境配置上。最常见的报错是安装PyTorch时下载慢或者装完版本不匹配。我的建议是直接到PyTorch官网根据操作系统和CUDA版本选择安装命令别自己在pypi上乱装。CUDA版本的选择上如果没有独立显卡就选CPU版本训练时把device设成cpu即可。另一个高频坑是numpy版本冲突。PyTorch 2.0对numpy有一份最低版本要求如果系统装了太老的numpy运行时会报类似“module compiled against API version”的错误。解决办法是升级到1.24以上版本。再有就是scipy在处理CWRU原始.mat文件时需要用到loadmat如果你的scipy版本过新个别.mat文件格式反而支持有问题这时候可以降级到1.10试试或者用h5py库手动读取。5.2 数据加载与内存不足问题故障诊断的数据集虽然不大但滑窗切分后样本数量会上涨很快。我调试初期曾试图把全部样本一次性读入内存化成数组结果在样本量超过5万时内存占用直接拉满。后来我改用PyTorch的Dataset方案把样本以npy文件或者原始索引形式保存DataLoader在迭代时才动态加载到内存。这样内存占用只取决于batch_size不会随着数据集总量线性增长。如果数据文件本身特别大可以考虑用内存映射mmap_moder读取或者先把原始信号处理成特征向量再保存减少I/O压力。还有一个小技巧将预处理后的样本批量存成多个chunk文件每个chunk里包含若干样本训练时按顺序加载chunk。这个方案我后面在更大规模的风机轴承数据上复用效果很不错。5.3 模型泛化与过拟合问题深度学习模型一旦训练样本量不够很容易在训练集上跑出100%准确率但测试集表现平平。我处理过拟合有三个招式第一是数据增强对振动信号做随机幅值扰动、随机噪声叠加、随机缩放这些在信号领域跟图像领域的平移旋转增强一个道理能有效扩充训练集。第二是调整模型容量比如把卷积核数从128减到64或者在全连接层加Dropout(0.5)。第三是早停加正则化就是前面提到的weight_decay和early stopping。跨工况泛化是另一个容易被忽略的问题。CWRU数据集有0到3马力四种负载工况如果你用0马力训练直接拿3马力测试准确率通常会有明显下降。这是正常的分布偏移现象。想提升跨工况泛化能力一个实用做法是训练时混合多种工况的数据让模型学会对转速与负载变化不敏感的特征。我在项目中做了消融实验混合全部工况训练后在单独工况测试集上的准确率比单工况训练平均高出2到3个百分点。结尾回头再复盘这个项目我最深的体会是深度学习模型的训练其实没有想象中那么玄真正的门槛在数据和工程细节。数据选型、预处理、划分方式、防泄漏这几件事没做好后面模型再花哨也是空中楼阁。我自己从一开始用频谱图配合二维CNN到后来直接使用一维原始信号进去再到现在把模型裁剪后部署到边缘设备每一步都是被真实问题推着往前走。如果你也想做轴承故障诊断我建议先用公开数据把整条流程跑通然后找一台实际设备做数据采集验证。有了自己的数据模型才有工业落地的底气。最后留一个小建议训练日志不要随手删后面调参和写报告时翻旧日志往往能省下大量重复实验的时间。本文还有配套的精品资源点击获取
返回列表