简介面向机器学习与故障诊断研究者的数字论文《基于卷积神经网络的滚动轴承故障诊断研究》针对滚动轴承振动信号特征难以准确表征的问题系统构建了从信号预处理到深度模型识别的方法体系。文档基于振动信号短时平稳性提出分帧、加窗、DFT与图像编码生成振谱图并设计深度卷积神经网络自动提取故障特征与分类弥补奇异值分解、多尺度模糊熵分析、经验模态分解等传统方法表征不完备的不足。内容涵盖振动信号预处理关键步骤、DCNN网络结构设计、训练流程以及美国凯斯西储大学轴承实测数据验证实验表明故障识别准确率达100%可完整复现故障诊断建模过程。文件为单份PDF文档大小仅1.28MB当前已有571人浏览学习适合机械工程、自动化及人工智能相关专业的学生与工程师参考也可作为深度学习故障诊断方向的入门与算法设计范本。1. 用CNN做滚动轴承故障诊断先搞清楚它到底在解决什么问题《基于卷积神经网络的滚动轴承故障诊断研究》这个标题把两个词绑在一起卷积神经网络CNN和滚动轴承故障诊断。前者是近年在工业状态监测里被反复验证的深度学习模型后者是设备维护里最头疼的问题之一——轴承一旦故障轻则停机重则产线事故。CNN在这里做的事情本质上是把“测一段振动波形判断轴承是正常还是内圈/外圈/滚动体故障”变成一个端到端的分类问题。不必手动算均方根、峭度、包络谱这些专家特征模型自己从原始信号里学特征。这篇文章适合三类人写论文需要复现模型的研究生、想用深度学习替代传统特征工程的算法工程师、以及现场维护设备但想试试AI诊断的可靠性工程师。接下来按我自己的落地路径从数据、模型到训练里的坑完整讲一遍。2. 振动信号怎么变成CNN能吃的数据数据集与预处理2.1 公开数据集怎么选CWRU仍是首选但不是唯一跑这个方向最常用的公开数据集是凯斯西储大学的CWRU轴承数据集。它包含正常和三种故障内圈、外圈、滚动体每种故障又按损伤直径分0.007、0.014、0.021英寸采样率有12kHz和48kHz两档。对这个标题来说CWRU足够验证“CNN能不能分出来”而且和大多数已发表论文有可比性。下载后是一个个.mat文件每个文件对应一个工况比如“97.mat”是正常数据“105.mat”是0.007英寸内圈故障。文件里还有负载、转速等元数据但训练时主要用到DE驱动端加速度那一列。如果你用自己的数据比如从现场通过加速度传感器采的振动信号也是一样的格式一列数值单位是g或m/s²采样率通常在10kHz以上。区别只是没有标准标签需要你根据检修记录自行标记。我一般会先做一次可视化的FFT看频谱确认确实有差异再开始建模。注意CWRU的下载地址和文件命名规则在不同镜像站会略有差异但mat格式内容基本一致如果你的网络下载不了也可以用Paderborn大学的数据集不过那个数据需要额外处理转速、负载信息新手容易被绕晕。选择数据集的另一个关键是采样率。同一个轴承在12kHz和48kHz下采出来的波形形态差异很大训练结果不会直接迁移。我的建议是论文里写清楚你用了哪一档采样率最好只用一个档位不要混用。实际项目中传感器的采样率是固定的所以尽量在项目开始就固定避免后面重采。2.2 滑窗采样一维信号变样本的三个参数原始振动信号是几十万点的连续序列不能直接一整段丢进CNN。常见做法是滑窗采样用固定长度的窗口把长序列切成短样本。窗口长度、步长、每类样本数这三个参数需要手动定。窗口长度建议覆盖至少两个旋转周期。比如CWRU在1797rpm下转频约30Hz周期约0.033s12kHz采样率对应约400个点取1024点大约覆盖2.5个周期。太短分不清脉冲特征太长样本数量少且训练慢。步长决定重叠率步长等于窗口长度就不重叠步长小于窗口长度则相邻样本有重叠。重叠能让样本数量翻几倍但会引入相关性后面避坑章节会细说。下面的代码用Python读取mat文件并做滑窗切分import numpy as np from scipy.io import loadmat # 读取CWRU mat文件取DE通道驱动端加速度数据 mat_data loadmat(105.mat) key [k for k in mat_data.keys() if DE in k][0] signal mat_data[key].flatten().astype(np.float32) # 一维数组 def sliding_window(signal, window_size1024, stride512): samples [] for start in range(0, len(signal) - window_size 1, stride): samples.append(signal[start:start window_size]) return np.array(samples) samples sliding_window(signal, 1024, 512) print(samples.shape) # 比如 (86, 1024)这段代码先把mat里的DE列取出来然后用for循环按步长512切窗口。窗口大小1024、步长512意味着相邻样本重叠一半。如果你发现某类数据切出的样本数量特别少可以把步长调小比如256让样本数量翻倍但每类样本最好控制在1万以内否则训练时间成倍增长。切完之后把所有类的样本纵向拼接再打标签。还有一点滑窗的顺序会影响数据分布如果一个文件的信号先慢变后快变最好把窗口顺序固定下来不要每次运行都换起始位置标准做法是设置一个随机种子或者干脆按顺序切。2.3 归一化与标签编码别让幅值范围坑了训练不同工况下振动信号幅值差异很大0hp负载时峰值可能只有0.5g2hp负载时能到2g。如果不做归一化CNN的卷积核会被大数值样本主导训练难以收敛。常见做法是标准化即(x - mean) / std。注意mean和std只从训练集计算不能把测试集也混进来算否则会造成数据泄漏。标签编码也很简单用整数0、1、2、3对应正常、内圈、外圈、滚动体故障。把样本和标签做成PyTorch的Dataset或TensorDataset即可。下面是归一化与标签编码的代码# 假设samples是二维数组 [N, window_size]sample_labels是对应的原始标签字符串列表 from sklearn.preprocessing import StandardScaler # 只对训练集拟合这里示意先拼好数据再手动拆 all_samples np.vstack([normal_samples, inner_samples, outer_samples, ball_samples]) # [N, 1024] all_labels np.concatenate([ np.zeros(len(normal_samples)), np.ones(len(inner_samples)), np.full(len(outer_samples), 2), np.full(len(ball_samples), 3) ]).astype(np.int64) # 归一化按样本自身做标准化更安全每个样本减去自己均值除以标准差 def normalize_per_sample(samples): return (samples - samples.mean(axis1, keepdimsTrue)) / (samples.std(axis1, keepdimsTrue) 1e-8) X normalize_per_sample(all_samples).reshape(-1, 1, 1024) # 增加通道维度 print(X.shape, all_labels.shape) # (N, 1, 1024) (N,)这里用的是“每个样本单独标准化”而不是全局标准化。因为振动信号的均值本该近似为0但实际采集会有直流偏置和传感器零点漂移逐样本扣除更稳健。1e-8防止除零。如果你坚持用全局scaler也建议只用训练集拟合然后用同一个scaler去变换测试集。之后把X转成torch.FloatTensorlabels转成torch.LongTensor就可以进入模型了。有些论文还会做带通滤波或重采样来消除转速波动和噪声干扰。那些属于额外的信号处理我不建议在一开始就加因为CNN自己有能力学滤波器的近似先跑一个干净的基线再逐步加预处理这样你能清楚每一步到底带来了多少提升。3. 搭一个能跑通的一维CNN模型结构设计与参数说明3.1 为什么一维卷积比二维更适合原始振动信号滚动轴承故障最关键的信息是故障冲击产生的周期性瞬态成分它在时间波形上表现为稀疏的尖峰在频谱上表现为以故障特征频率为中心的边带。一维卷积直接在时间轴上滑动卷积核相当于一个局部滤波器能自动学习“冲击-衰减”的时域模板。二维卷积则要求先把一维信号转成时频图比如STFT谱图或小波尺度图这一步引入了窗口函数、重叠率、小波基等额外参数调起来比CNN本身还麻烦。所以最小可行方案是用一维CNN直接吃原始波形。从计算量看一维卷积的参数和FLOPs也比二维小训练更快。当然时频图二维CNN在跨工况泛化上往往更好因为时频图能保留频带位置信息但这是后话。在这个标题的研究场景里先用一维CNN跑通基线再决定要不要升级。我在实际项目里见过很多同学一上手就花两周调STFT参数结果发现一维CNN跑出来的准确率已经够了时间全浪费在画图上。这里顺便说下卷积神经网络的原理骨架卷积层的核心是“局部连接权值共享”一个卷积核扫过整个时间轴提取的是同一个模式在所有位置的响应。池化层做降维丢掉对分类不重要的精确位置保留相对关系。多个卷积层堆叠后低层学的是边缘和冲击形状高层学的是这些冲击的组合方式。理解这一点你就知道为什么振动信号适合CNN故障冲击的波形模式在位置上会随机偏移而CNN的平移不变性正好能应对这种偏移。3.2 网络结构Conv1d BN ReLU MaxPool 的经典组合关于卷积神经网络结构图你可以不需要画得很复杂输入是[Batch, 1, 1024]经过三层卷积块每块包含一维卷积、批归一化、ReLU激活、最大池化最后接一个自适应平均池化和全连接分类头。下面是我常用的一版结构在CWRU四分类任务上准确率稳定在99%以上且参数量不到50万import torch.nn as nn class CNN1D(nn.Module): def __init__(self, num_classes4): super().__init__() self.features nn.Sequential( # Block1: 1 - 16 channels, kernel 5, keep length nn.Conv1d(1, 16, kernel_size5, padding2), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), # 1024 - 512 # Block2: 16 - 32 channels nn.Conv1d(16, 32, kernel_size5, padding2), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), # 512 - 256 # Block3: 32 - 64 channels nn.Conv1d(64, 64, kernel_size3, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), # 256 - 128 ) self.classifier nn.Sequential( nn.AdaptiveAvgPool1d(1), # 无论输入长度输出 64*1 nn.Flatten(), nn.Dropout(p0.3), nn.Linear(64, num_classes), ) def forward(self, x): x self.features(x) x self.classifier(x) return x说明一下参数设计第一层卷积核大小5padding2保证卷积不缩短序列长度。卷积核太大如9虽然感受野大但参数量增加对小数据集容易过拟合太小如3感受野不足。后面池化每次把长度减半三层池化后1024变成128最终用自适应平均池化直接把整个特征图压成一个64维向量再丢给全连接层。这里有一个容易忽略的点AdaptiveAvgPool1d(1)比Flatten后接多层全连接更省参数也更能抵抗过拟合。我早期用过两层256维全连接训练集轻松100%测试集掉到90%换成平均池化单层全连接后测试集直接拉回到98%以上。另外BatchNorm放在激活函数之前是常见做法也可以放在后面差别不大但一定要和ReLU配对否则卷积输出会饱和。3.3 训练配置学习率、batch size、优化器怎么定网络搭好了训练参数同样关键。优化器用Adam学习率1e-3batch size建议64或128。学习率太大训练震荡太小收敛慢。我习惯用余弦退火调度器初始1e-3最低降到1e-5。损失函数用交叉熵。还有一个必须做的操作固定随机种子。不固定的话每次跑结果都可能差0.5%甚至更多你会分不清是模型改进还是随机波动。代码放这里import torch import random import numpy as np def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True set_seed(42) model CNN1D(num_classes4) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50) # 训练循环示意省略数据加载和验证部分 for epoch in range(50): model.train() for x_batch, y_batch in train_loader: optimizer.zero_grad() out model(x_batch) loss criterion(out, y_batch) loss.backward() optimizer.step() scheduler.step() # 每5轮在验证集上评估一次准确率超过当前最佳则保存权重训练时建议开GPU并设置pin_memoryTrue样本量不大时CPU也能跑但会慢几十倍。batch size设大了如果显存不够报OOM时减小到32。如果你在调试阶段先只跑10轮确认loss在下降再上完整50轮。还有一点不要在一开始就用完整训练集留出10%作为验证集用来做早停。早停可以手动实现记录验证集准确率连续10轮不升就降低学习率或直接停止。很多新手把训练跑满50轮反而在过拟合区浪费了时间。4. 训练中的五个常见坑数据泄漏、样本不平衡、过拟合、随机性和评估偏差4.1 测试集准确率99%换一批数据就崩数据泄漏的坑现象在CWRU上随便切分测试集准确率能到99%甚至100%但你拿着模型去现场新采的数据上测准确率立刻掉到70%以下甚至错乱。原因滑窗切分时同一个连续振动信号被切成了大量重叠样本。如果你的划分方式是把所有样本随机打乱后按比例切训练/测试集那同一段原始信号的前后窗可能一个在训练集、一个在测试集模型相当于记住了相邻窗的相似波形测试时“开卷考试”。这就是典型的数据泄漏。解决按原始样本文件划分。比如CWRU里每个.mat文件是一条独立的连续记录先把文件打乱拿90%的.mat文件做训练10%做测试再从每个文件内部滑窗。这样训练和测试在时间上完全不重叠。代码上就是先划分文件列表再分别调滑窗函数生成各自样本。这是这个方向最常见的翻车原因没有之一。我见过一篇发表的论文就是这种错切测试集准确率99%复现时我换了个切分方式立刻掉到91%直接暴露了问题。4.2 故障类型识别都偏向多数类样本不平衡现象模型整体准确率95%但看混淆矩阵外圈故障的召回率只有60%大量外圈样本被识别成正常或内圈。原因CWRU里正常数据文件只有一个三种故障文件数量也不完全一样而且不同类别滑窗后样本数差异很大。如果正常类样本是故障类的5倍交叉熵会偏向把最大类学好因为把正常类判对的损失权重天然更高。解决最简单的办法是给损失函数加类别权重。在PyTorch里# 根据每类样本数量计算权重样本越少的类权重越大 class_counts np.bincount(train_labels) class_weights 1.0 / (class_counts 1e-8) class_weights torch.tensor(class_weights, dtypetorch.float32, devicecuda) criterion nn.CrossEntropyLoss(weightclass_weights)也可以用过采样从少数类文件里用更小的步长滑窗把样本数量补到和多数类接近。但注意过采样只是让训练时每个batch里各类数量均衡本质上和加权loss等价。我一般先试加权loss不行再上过采样。还有个细节如果你用了加权loss训练时的损失值会变高这正常只要验证集准确率在升就行别被绝对loss数值吓到。4.3 模型过拟合训练集100%测试集90%现象训练集loss降到0准确率100%测试集准确率只有90%左右差10个百分点。原因CNN参数量大而公开数据集样本量不大滑窗后也就几千到几万模型有能力记住训练样本的噪声细节。尤其是没有归一化、没有Dropout、用了多层全连接时过拟合概率很高。解决先把全连接层改成自适应平均池化单层全连接再在classifier里加nn.Dropout(p0.3)卷积层之间也可以加Spatial Dropout。如果还是过拟合可以做数据增强给训练样本加白噪声噪声幅度控制在信号标准差的0.05~0.1倍。下面的代码演示了如何在训练时实时加噪声def add_noise(x, std_ratio0.05): noise torch.randn_like(x) * torch.std(x, dim2, keepdimTrue) * std_ratio return x noise # 在训练循环里调用 x_batch add_noise(x_batch, std_ratio0.05)注意增强只加在训练集验证和测试集不要加。加了噪声之后模型被逼着学更鲁棒的形状特征而不是死记硬背。我踩过一次坑把噪声增加比例设成0.3结果训练集loss也下不去模型根本学不到清晰的冲击波形。噪声比例超过0.2就相当于把信号淹没在噪声里了0.05左右刚刚好。4.4 每次训练结果差很多随机性影响现象同一个数据集同一个脚本今天跑准确率98%明天跑变成96%“玄学”一样。原因PyTorch里卷积、dropout、数据加载器的shuffle都带随机性。如果随机种子没固定权重初始化、数据batch顺序、dropout mask都在变结果自然不同。解决在脚本最开头调用set_seed(42)并设置DataLoader(shuffleTrue, generatortorch.Generator().manual_seed(0))。对CUDNN设置torch.backends.cudnn.deterministic True和torch.backends.cudnn.benchmark False。这样同一份代码在同一GPU上跑出的结果可复现。这个步骤既是工程习惯也是写论文时让审稿人信服的基本要求。注意固定种子后在CPU上跑和GPU上跑结果仍可能不同因为浮点数运算顺序不同所以写明训练设备也有必要。4.5 只看准确率造成的误判混淆矩阵和F1才靠谱现象某类故障样本很少模型把它全都判成正常整体准确率仍然有95%你觉得效果很好。直到现场真的发生这类故障模型完全没报警。原因准确率是“所有样本中判对的比例”在类别不平衡时严重偏向样本多的类。故障诊断关心的是“把每一种故障都识别出来”所以召回率真正例率更重要。解决用sklearn.metrics.classification_report打印每类的precision、recall、F1并画出混淆矩阵。F1是precision和recall的调和平均能综合反映漏报和误报。在验收模型时我一般会要求每个故障类的召回率不低于90%如果某类掉到80%以下就说明模型没有真正学会这个故障的特征。另外混淆矩阵能帮你发现“模型把A类误判成B类”的系统性模式。比如滚动体故障经常被误判成外圈故障这是物理上合理的因为滚动体经过外圈时产生的冲击会和外圈故障相似。看到这种结果不需要强行调参而是考虑在输出层后加一个基于物理规则的修正如果滚动体类的概率低且外圈类概率高且故障特征频率计算结果显示滚动体故障概率更大则手动调整输出权重。这类后处理不在CNN结构内但对落地非常有帮助。5. 进阶让诊断结果更可信的验证技巧与部署思路训练完模型只是第一步你还需要回答两个问题模型到底是学到了物理特征还是刷了个高分能不能部署到现场我有三个小技巧可以分享。跨工况验证在CWRU上用0hp负载的数据训练用1hp、2hp的数据测试。如果准确率明显下降说明模型依赖的是幅值而不是波形形状。这是检验泛化能力最直接的方式。如果你想发论文这个实验几乎是必做项。实际操作时先按负载分组比如用0hp所有文件做训练用1hp所有文件做测试然后对比一张混淆矩阵。如果准确率从99%掉到85%不要急着加大模型先看看是不是归一化方式有问题——逐样本标准化通常能缓解幅值差异但跨工况的频带偏移仍然存在。Grad-CAM可视化对一维信号也可以做类似热力图的定位。用一个已训练好的模型取最后一个卷积层的梯度算出每个时间点的贡献权重然后画在原始波形上。你会发现模型注意力集中在故障冲击脉冲的位置上而不是随机噪声大、幅值高的地方。这个图放在报告里比准确率数字更有说服力。实现上可以复用torch.autograd.grad不用额外库。注意一维Grad-CAM的插值方式直接用双线性插值把权重上采样到和输入一样长不需要按二维图那样纠结。导出部署模型PyTorch训练好的模型可以导出为ONNX再用TensorRT加速。代码如下import torch.onnx model.eval() dummy_input torch.randn(1, 1, 1024, devicecuda) torch.onnx.export(model, dummy_input, bearing_cnn.onnx, input_names[signal], output_names[probs], dynamic_axes{signal: {0: batch}, probs: {0: batch}})导出时设置batch维度为动态这样现场每条推理可以不固定批大小。在嵌入式设备上跑时把归一化也写进模型或者放在前端处理保证推理时的输入统一。另一个容易踩的坑是torch.onnx.export的opset_version老版本ONNX Runtime不支持某些操作建议指定opset_version11兼容性最好。我自己的习惯是每次实验先固定随机种子、按文件切好数据然后把模型、预处理参数、脚本版本、结果指标一起存成实验记录方便回溯。这个方向并不神秘就是把信号处理好、模型别出bug、评估方式别自欺欺人。希望帮到你。本文还有配套的精品资源点击获取