ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于深度学习1DCNN的轴承故障诊断:从振动信号到端到端分类实践

基于深度学习1DCNN的轴承故障诊断:从振动信号到端到端分类实践 简介基于深度学习的1DCNN轴承故障诊断源码包面向机械故障诊断、工业预测性维护领域的工程师与研究人员提供从振动信号预处理、1DCNN模型构建、训练优化到故障分类的完整实现方案。资源共50个文件包体仅3.64MB以Python源码.py为主涵盖数据读取与预处理、网络结构定义、训练测试以及t-SNE特征可视化另有17张PNG结果图、配置XML、说明TXT和混淆矩阵Excel表格便于对照实验结果与评估指标。源码基于TensorFlow/Keras编写支持CWRU数据集通过滑动窗口采样、归一化等操作对原始振动信号进行预处理可识别滚动体缺陷、内圈故障、外圈故障等典型模式同时提供多种优化器、损失函数与评估指标配置并包含模型保存与加载功能。目录包含data、models、utils等模块结构清晰适合入门学习和在此基础上进行二次开发。已有424人学习下载内容覆盖从数据准备到结果分析的完整链路是快速掌握深度学习驱动轴承故障诊断实践流程的实用资料。1. 基于深度学习1DCNN轴承故障诊断为什么直接把振动信号送进卷积网络遇到轴承故障诊断很多人的第一反应是FFT频谱、小波包分解或者时频图先做特征工程再训练分类器。但在公开的CWRU轴承数据集上跑过几组实验后会发现这些预处理环节引入的参数比网络本身还多窗函数选什么、频带切多宽、小波基用哪个每换一个数据集就得重新调一轮。这套基于深度学习1DCNN轴承故障诊断源码走的是另一条路把传感器采集到的原始振动加速度信号直接当作一维时间序列通过一维卷积网络自动提取冲击特征并完成故障分类。它解决的是预测性维护里最核心的问题——只有振动数据时如何区分正常、内圈故障、外圈故障和滚动体故障。适合正在复现论文、做设备健康管理平台或者在边缘设备上跑推理的工程师阅读从数据切分到网络落地都有可直接复用的代码。2. 数据准备CWRU数据加载、滑窗切分与防泄漏划分2.1 CWRU数据集的类别定义与文件组织CWRU是轴承故障诊断领域使用最广泛的公开数据集用SKF6205轴承在驱动端做故障注入采集采样率常用12kHz。故障由电火花加工产生分为内圈IR、外圈OR、滚动体B三种位置每种位置有0.007、0.014、0.021英寸三个损伤尺寸加上正常状态一共构成10个分类。这是绝大多数1DCNN复现实验采用的类别口径源码里也建议按10类做。实验台还带0到3马力四档负载对应转速从1797r/min降为1730r/min这个转速信息在后面验证跨工况泛化时很有用。数据以mat格式存储文件名中包含了故障位置和尺寸信息常见的读取方式是通过字段名过滤出驱动端振动信号。下面这个函数把mat文件读成一维数组并做基本的均值方差标准化import numpy as np from scipy.io import loadmat def load_cwru_signal(mat_path, sensorDE): mat loadmat(mat_path) key [k for k in mat.keys() if k.endswith(f_{sensor}_time)][0] sig mat[key].flatten().astype(np.float32) # 按整个文件做z-score消除不同实验间的基线漂移 sig (sig - sig.mean()) / (sig.std() 1e-8) return sig这里用endswith(f_{sensor}_time)匹配字段名而不是写死完整字段名是因为大多数CWRU版本的mat文件字段形如X097_DE_time但不同下载源头存在小差异。按归一化处理说明对整个连续信号计算均值和标准差相当于把每个实验的直流分量和幅值差异消掉。如果后续按滑动窗口切分且没有重叠这个操作不会引入标签泄漏但如果做的是窗口内归一化则下面一节还要再处理。2.2 滑窗切分窗口长度与重叠率怎么定连续振动信号不能整段塞进网络需要滑窗切成固定长度样本。窗口长度直接影响模型能观察到的物理范围。12kHz采样率下主轴转一圈大约400个采样点窗口取2048相当于连续观察约5个旋转周期足以覆盖一次故障冲击从发生到衰减的完整序列。窗口过短抓不全周期冲击过长则单个样本包含太多转速波动和无关噪声同时样本数量变少。所以2048是这个任务里比较稳妥的起点。重叠率解决的是样本量问题。0%重叠时每个文件只能切出几十个样本10类数据全部算上也不过几千条训练一个带BatchNorm的CNN显得捉襟见肘。50%重叠可以把样本量提升近一倍同时相邻窗口的相关性还处在可控范围内75%重叠会带来严重的样本冗余训练速度变慢且测试集容易出现虚高。以下函数实现滑窗切分返回一个二维数组每一行是一个窗口def sliding_window(sig, win_len2048, overlap0.5): step int(win_len * (1 - overlap)) if len(sig) win_len: return np.empty((0, win_len), dtypenp.float32) starts np.arange(0, len(sig) - win_len 1, step) return np.stack([sig[s:s win_len] for s in starts])参数说明win_len控制网络输入的序列长度overlap0.5表示相邻窗口重叠一半实际步长为1024个采样点。切分完成后一个长度约50万点的原始文件可以产出约490个窗口10类数据全量处理后在2万到4万样本量级训练一个1DCNN模型在普通GPU上几分钟就能跑完一个epoch。切分时注意保留每个样本对应的原始文件ID后续划分训练集和测试集时要用。2.3 归一化与防数据泄漏的数据集划分这一步是决定最终准确率是否可信的关键。两种常见做法有本质区别按窗口做归一化每个样本减去自身均值再除以自身标准差按全局统计归一化用整个文件或整个训练集的均值和标准差统一处理。推荐前者因为现场采集的振动信号经常存在基线漂移和幅值波动窗口内归一化可以消除这些设备相关变量让模型更关注冲击波形本身。def normalize_window(win): return (win - win.mean()) / (win.std() 1e-8)直接调用train_test_split随机打乱所有窗口是一个隐蔽但高频出现的数据泄漏问题。由于相邻窗口重叠且来自同一段连续记录随机划分后训练集里某几个窗口和测试集里另几个窗口在时间上几乎重合模型相当于“见过”测试数据的相邻片段测试准确率会虚高到很有误导性。正确做法是按原始文件分组保证同一段连续记录的全部窗口只出现在训练集或只出现在测试集中。一个更严格的验证方式是跨负载划分用0、1、2马力数据训练3马力数据测试此时转速和载荷都变了得到的准确率才是模型真实泛化能力的体现。源码里建议至少保留一组这样的划分结果。3. 1DCNN网络结构设计卷积核、批量归一化与参数推算3.1 为什么一维卷积适合振动信号从滤波器组到感受野从频域视角看一维卷积核本质上是一组可学习的带通滤波器卷积过程就是在原始信号上滑动计算局部加权和。轴承故障的特征频率如内圈BPFI、外圈BPFO在时域表现为周期性冲击这些冲击在卷积层看来就是局部波形的重复模式符合卷积权值共享的特性。相比把信号转成时频图再用二维CNN处理1DCNN省去了短时傅里叶变换的窗长、重叠和频带选择参数端到端直接从原始信号学习推理时也更适合部署在资源受限的采集设备上。卷积核尺寸选择是1DCNN设计里最重要的决定。第一层卷积感受野太小看到的只是几个采样点的瞬时幅值无法捕捉一个完整的冲击波形太大又增加参数和计算量。常见的做法是第一层用大卷积核配合大步长快速压缩序列长度后面几层换回小卷积核提炼局部细节。典型配置是第一层kernel为64、stride为2后面几层kernel为5、stride为2。设置第一层kernel大小为64对应约5.3毫秒的时间窗覆盖的物理范围足够辨认一次冲击。3.2 网络结构与PyTorch源码实现下面是一份可以直接用的PyTorch实现4层一维卷积加自适应池化最后接两层全连接分类器。输入张量形状为(batch_size, 1, 2048)输出形状为(batch_size, 10)。import torch import torch.nn as nn class Bearing1DCNN(nn.Module): def __init__(self, num_classes10): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 32, kernel_size64, stride2, padding32), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.Conv1d(32, 64, kernel_size5, stride2, padding2), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.Conv1d(64, 128, kernel_size5, stride2, padding2), nn.BatchNorm1d(128), nn.ReLU(inplaceTrue), nn.Conv1d(128, 128, kernel_size5, stride2, padding2), nn.BatchNorm1d(128), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool1d(1), ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(128, 64), nn.ReLU(inplaceTrue), nn.Linear(64, num_classes), ) def forward(self, x): h self.features(x).squeeze(-1) return self.classifier(h)3.2.1 参数量与输出维度推算各层输出序列长度变化如下表计算公式为L_out floor((L_in 2 * padding - kernel_size) / stride 1)。层操作kernelstride输入长度输出长度conv11→32通道64220481025conv232→64通道521025513conv364→128通道52513257conv4128→128通道52257129poolAdaptiveAvgPool1d--1291选第4层卷积输出为128维特征是因为这个维度对10类故障已经足够表达继续加宽到256收益有限且全连接层参数翻倍。全连接部分先压到64维再加Dropout最后映射到10个类别num_classes参数可以改成4做正常加三种故障的粗分也可以改成6不区分故障尺寸改的时候只需要换标签映射网络主体不用动。3.2.2 可选给特征层加SE通道注意力模块如果还想在准确率上再挤一点可以在每层卷积和BN之后插入一个SE模块用通道维度的注意力重新加权各个特征图。实现只有十行左右参数增加很少但对小样本故障特征有正向作用class SEBlock(nn.Module): def __init__(self, channels, reduction16): super().__init__() self.pool nn.AdaptiveAvgPool1d(1) self.fc nn.Sequential( nn.Conv1d(channels, channels // reduction, 1), nn.ReLU(inplaceTrue), nn.Conv1d(channels // reduction, channels, 1), nn.Sigmoid(), ) def forward(self, x): return x * self.fc(self.pool(x))使用方式是在每个ReLU后插入x se_block(x)通道数减半时SE的reduction注意不要整除出错比如32通道时reduction16会得到2个中间通道仍然合法。加了这个模块后模型参数会增加约几千个训练时间影响忽略不计推荐在最终实验版本里保留。4. 训练与调参损失函数、优化器、数据增强与过拟合防治4.1 损失函数、优化器与学习率策略10分类问题直接使用nn.CrossEntropyLoss()它内部已经把LogSoftmax和NLLLoss合并所以模型最后一层不需要手动加Softmax。训练时如果某个类别样本特别少比如现场只采集到正常数据和内圈故障数据可以给CrossEntropyLoss传weight参数权重与样本数成反比避免模型把所有样本都判成多数类。优化器选Adam学习率初始化为0.001设置weight_decay1e-4做L2正则。对这个小规模分类任务Adam比SGD收敛快对学习率的敏感度低更适合快速复现和调参。训练后期常见的做法是切换学习率调度器这里用ReduceLROnPlateau当验证集loss连续5个epoch不下降时把学习率乘0.5optimizer torch.optim.Adam(model.parameters(), lr0.001, weight_decay1e-4) criterion nn.CrossEntropyLoss() scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience5)学习率过大表现为loss剧烈震荡不收敛过小则loss下降非常缓慢。确认优化器和调度器配置后建议记录的指标是训练loss、验证loss和验证准确率其中验证loss是调度器判断的依据不能用训练loss替代。4.2 训练循环、检查点保存与早停训练循环的要点是每一轮结束时跑一次验证集同时把验证loss最小的权重单独保存。只保存state_dict而不是整个模型因为模型结构可能调整但权重文件在相同结构下不会失效。完整训练流程如下def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss, correct, total 0.0, 0, 0 for x, y in loader: x, y x.to(device), y.to(device) optimizer.zero_grad() out model(x) loss criterion(out, y) loss.backward() optimizer.step() total_loss loss.item() * y.size(0) correct (out.argmax(1) y).sum().item() total y.size(0) return total_loss / total, correct / total torch.no_grad() def evaluate(model, loader, criterion, device): model.eval() total_loss, correct, total 0.0, 0, 0 for x, y in loader: x, y x.to(device), y.to(device) out model(x) loss criterion(out, y) total_loss loss.item() * y.size(0) correct (out.argmax(1) y).sum().item() total y.size(0) return total_loss / total, correct / total训练主循环里记住三点每个epoch结束调用scheduler.step(val_loss)验证准确率不再提升时保存检查点跑够预定epoch数或连续15个epoch验证loss没刷新纪录就提前停止。检查点里同时保存epoch、模型权重、优化器状态便于中断后恢复训练best_loss float(inf) for epoch in range(50): train_loss, train_acc train_one_epoch( model, train_loader, optimizer, criterion, device) val_loss, val_acc evaluate(model, val_loader, criterion, device) scheduler.step(val_loss) if val_loss best_loss: best_loss val_loss torch.save({epoch: epoch, model_state: model.state_dict(), optimizer_state: optimizer.state_dict()}, best_ckpt.pt)注意模型在训练和验证之间的状态切换。model.eval()必须调用否则BatchNorm仍在使用训练批次统计量验证输出会不准。训练完成后加载检查点再评估一次确认保存的权重复现了验证准确率。4.3 数据增强、Dropout与常见调参坑CWRU数据干净且类别均衡模型可能很快在训练集上拿到99%以上准确率但真实振动信号含有噪声和转速波动。给训练集加高斯噪声是最贴近物理现实的数据增强手段按信噪比加噪比直接加随机扰动更容易控制强度。常见配置是信噪比20dB到10dB数值越小噪声越强默认取20dBdef add_noise(x, snr_db20.0): # x为窗口内信号, 先计算原始信号功率再生成对应噪声 sig_power x.pow(2).mean() noise_power sig_power / (10 ** (snr_db / 10.0)) noise torch.randn_like(x) * torch.sqrt(noise_power) return x noise另一个有效增强是随机时间平移。故障冲击的起始位置在每个窗口内不一定对齐用torch.roll把信号左右平移几十个采样点可以模拟触发位置偏移。注意验证集和测试集不要加任何增强只在训练循环内对输入张量调用。常见调参问题集中在三个现象。第一训练准确率接近100%但验证集只有七八成属于过拟合优先加大Dropout到0.6、增强噪声强度、把batch size从64降到32。第二loss完全不下降检查数据是否做了窗口内归一化、标签是否从0开始连续编码、学习率是否设置在合理区间。第三训练集和测试集同时出现某个类别准确率特别低通常是对应故障尺寸的数据量少或者和其他类别在波形上相似比如滚动体故障的冲击信号容易被噪声掩盖此时单独增加该类别的滑窗重叠率比改网络更有效。5. 从源码到现场混淆矩阵、TorchScript导出与跨负载验证5.1 用混淆矩阵与t-SNE验证诊断效果准确率只能说明整体水平工程上更关注哪些类别互相混淆。内圈故障和外圈故障在载荷变化时容易混淆滚动体故障误判成正常的比例通常最高这个现象需要在验证阶段用混淆矩阵确认。from sklearn.metrics import confusion_matrix torch.no_grad() def predict_labels(model, loader, device): model.eval() preds, gts [], [] for x, y in loader: x x.to(device) preds.extend(model(x).argmax(1).cpu().tolist()) gts.extend(y.tolist()) return confusion_matrix(gts, preds)t-SNE可以直观展示倒数第二层特征是否按类别聚拢。把model.features的输出作为特征向量输入T-SNEperplexity取30看10个类别在二维平面上是否明显分簇。如果正常类和某个故障类重叠说明该故障冲击微弱优先考虑数据增强而不是加深网络。5.2 跨负载验证从CWRU到现场数据的一道坎模型在同一负载内部划分训练集和测试集时准确率通常很高但用0马力数据训练、3马力数据测试时转速和载荷变化会让准确率明显下降这是1DCNN轴承故障诊断从论文走向落地时最大的问题。缓解的通用做法是在目标工况收集少量数据冻结前两层卷积只微调后面特征层和分类器。冻结参数数量少几百条样本就能做效果比重新训练整个网络稳定得多。导出推理模型用TorchScript方便脱离Python环境部署。注意导出前确定输入尺寸是固定的(1, 1, 2048)否则trace会失败model.eval() example_input torch.randn(1, 1, 2048) scripted torch.jit.trace(model, example_input) scripted.save(bearing_1dcnn.pt)现场推理时预处理必须和训练完全一致特别是窗口内归一化里的均值方差要从当前窗口独立计算不能拿训练集的统计量套用。把滑窗参数、归一化逻辑、类别字典和模型文件一起打包现场换人也能直接接手。本文还有配套的精品资源点击获取
返回列表