简介这份PDF论文面向机械故障诊断方向的研究生、工程师及深度学习入门者聚焦滚动轴承故障特征难以准确表征的痛点提出基于振谱图与深度卷积神经网络的诊断方案。资源包共1个PDF文件大小约1.28MB内容为完整期刊论文含摘要、引言、基本原理、实验验证与结论等章节便于系统研读方法细节。论文核心思路是先对轴承振动信号做分帧、加窗、DFT与图像编码构建表征故障的振谱图再设计深度卷积神经网络用已知故障模式的振谱图及标签训练网络最后以美国凯斯西储大学公开实测振动信号验证识别准确率达100%。文中还对比了奇异值分解、多尺度模糊熵、经验模态分解及深度信念网络等既有方法梳理了从数据采集、预处理、特征提取到故障建模的完整流程并给出网络训练与故障识别两阶段原理适合作为深度学习落地故障诊断的参考范例。目前已有571人学习。1. 从振动信号到故障标签卷积神经网络做滚动轴承诊断到底在解决什么产线上那台电机最近又开始“嗡嗡”作响老师傅拿听针贴上去听了几秒说“外圈有点问题”。这种靠经验听音的判断在单台设备上管用一旦扩展到几十上百台旋转机械就彻底不够用了。滚动轴承故障诊断要解决的就是把这种模糊的听觉经验变成可复现、可批量部署的自动判断。卷积神经网络在这里扮演的角色是直接从原始振动信号里学出故障特征省掉人工设计特征那一步。传统做法要先算时域指标、再做包络谱、手动挑峰一套流程走下来换个工况就得重新调参。CNN 把特征提取和分类揉进一个网络端到端训练对变工况的适应能力明显更强。这篇笔记面向的是想把这条路走通的工程师——不管你是刚接触深度学习还是已经在用 CNN 做一维信号分类都能从下面找到能直接抄的配置和踩过的坑。2. 一维卷积神经网络处理轴承振动信号的原理与数据准备2.1 为什么轴承故障诊断偏爱一维 CNN 而不是二维轴承振动信号本质是一维时序采样率通常在 12kHz 到 48kHz 之间。有人会先把信号转成时频图再套用图像分类那套二维 CNN。这条路能走但多了一步时频变换就多了一层信息损失和参数选择——窗长、重叠率、用什么变换每个选择都会影响最终结果。一维 CNN 直接在原始时序上做卷积卷积核沿时间轴滑动学到的就是局部波形模式比如冲击脉冲的上升沿、衰减振荡的周期。这些恰恰是轴承故障最本质的特征。从计算量看一维卷积的参数量远小于二维。一个卷积核大小为 64 的一维卷积层参数量是 64×输入通道数×输出通道数换成二维同样感受野需要 8×8 的核参数量直接翻到 64 倍。在嵌入式设备或边缘计算盒子上部署时这个差距直接决定能不能跑起来。我一般会先试一维 CNN只有在数据量特别大、故障模式极其复杂时才考虑二维方案。2.2 振动数据的采集、分段与标签制作数据准备这一步翻车的人比模型调参翻车的还多。常见做法是加速度传感器安装在轴承座径向采样率至少是轴承故障特征频率的 10 倍以上。以常见的 6205 轴承为例外圈故障特征频率在转频的 3 到 5 倍左右转频 30Hz 时特征频率约 100-150Hz采样率 12kHz 完全够用。拿到连续振动信号后要切成固定长度的样本。切法有两种重叠切分和不重叠切分。重叠切分能增加样本数量但要注意训练集和验证集之间不能有重叠否则验证结果会虚高。我一般用 50% 重叠做训练集增强验证集和测试集用不重叠切分。import numpy as np def segment_signal(signal, window_size, overlap0.5): 将连续振动信号切分为固定长度样本 signal: 一维振动信号数组 window_size: 每个样本的采样点数如 1024 overlap: 重叠比例0 到 1 之间 返回: shape 为 (样本数, window_size) 的数组 step int(window_size * (1 - overlap)) segments [] for start in range(0, len(signal) - window_size 1, step): segments.append(signal[start:start window_size]) return np.array(segments) # 示例采样率 12kHz取 1024 点约 85ms覆盖多个冲击周期 samples segment_signal(raw_signal, window_size1024, overlap0.5) print(f切分出 {samples.shape[0]} 个样本每个样本 {samples.shape[1]} 点)这段代码的关键参数是window_size。取太小一个样本里连一个完整的冲击周期都装不下取太大样本里混入太多正常段故障特征被稀释。经验值是让窗口至少覆盖 3 到 5 个故障冲击周期。对于内圈故障冲击间隔等于转频倒数30Hz 转频对应 33ms1024 点在 12kHz 下是 85ms能覆盖两到三个周期基本够用。如果发现模型学不动先把窗口加大到 2048 试试。标签制作要严格对应。每个样本来自哪一段信号、那段信号对应哪种故障、故障尺寸多大都要记录清楚。常见做法是用文件夹名做标签比如inner_race_007表示内圈故障、直径 0.007 英寸。这样加载数据时直接解析路径就行不容易出错。2.3 归一化与数据增强的取舍振动信号幅值受负载影响很大同一故障在不同负载下幅值可能差好几倍。不做归一化模型会偏向幅值大的样本。常见做法是每个样本单独做 z-score 归一化减均值除标准差。注意是对每个样本自己做不是对整个数据集做这样能消除负载带来的幅值差异。数据增强在轴承诊断里要谨慎。图像分类那套旋转、裁剪、加噪声在振动信号上不一定成立。加高斯噪声是安全的相当于模拟传感器噪声。幅值缩放也可以模拟不同负载。但时间轴上的拉伸或压缩要小心那会改变故障特征频率把外圈故障变成别的故障。我一般只用加噪声和幅值缩放两种增强倍数控制在 0.8 到 1.2 之间。3. 搭建一维 CNN 故障分类网络结构、参数与训练脚本3.1 网络结构设计几层卷积、卷积核多大、池化怎么选一维 CNN 做轴承故障分类结构不需要太深。我试过 3 层卷积和 8 层卷积在公开数据集上准确率差距不到 1 个百分点但 8 层训练时间翻了三倍。常见做法是 3 到 5 个卷积块每个块包含一维卷积、批归一化、ReLU 激活、最大池化。卷积核大小是关键参数。第一层卷积核要大一些比如 64 或 128因为原始振动信号里故障冲击的上升沿比较宽小卷积核感受野不够。后面几层可以逐步减小到 16 或 8提取更精细的模式。池化层用最大池化窗口大小 2 或 4逐步降低时间维度。import torch import torch.nn as nn class BearingCNN1D(nn.Module): def __init__(self, num_classes10, input_length1024): super().__init__() self.features nn.Sequential( # 第一层大卷积核捕捉冲击波形 nn.Conv1d(1, 32, kernel_size64, stride2, padding32), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), # 第二层中等卷积核提取局部模式 nn.Conv1d(32, 64, kernel_size32, stride1, padding16), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2), # 第三层小卷积核组合特征 nn.Conv1d(64, 128, kernel_size16, stride1, padding8), nn.BatchNorm1d(128), nn.ReLU(), nn.AdaptiveAvgPool1d(1) # 全局平均池化输出 128 维 ) self.classifier nn.Sequential( nn.Dropout(0.3), nn.Linear(128, num_classes) ) def forward(self, x): # x: (batch, 1, input_length) x self.features(x) # (batch, 128, 1) x x.squeeze(-1) # (batch, 128) return self.classifier(x) model BearingCNN1D(num_classes10, input_length1024) print(f参数量: {sum(p.numel() for p in model.parameters()):,})这段网络定义里第一层stride2是为了快速降低时间维度1024 点经过第一层卷积和池化后变成 256 点左右。padding设置为卷积核的一半保证输出长度不因卷积而缩小。全局平均池化替代了全连接层参数量大幅减少过拟合风险也低。Dropout(0.3)放在分类层前训练时随机丢弃 30% 神经元。参数量算下来大概 5 万左右在树莓派上都能跑推理。如果类别更多、数据更复杂可以把通道数翻倍但要注意参数量增长是平方级的。3.2 训练脚本损失函数、优化器与学习率调度轴承故障分类是典型的类别不均衡问题。正常样本多故障样本少某些故障类型可能只有几十个样本。直接用交叉熵损失模型会偏向多数类。常见做法是给每个类别加权权重和样本数成反比。from torch.utils.data import DataLoader, TensorDataset import torch.optim as optim # 假设 X_train: (N, 1024), y_train: (N,) X_tensor torch.FloatTensor(X_train).unsqueeze(1) # 增加通道维 y_tensor torch.LongTensor(y_train) dataset TensorDataset(X_tensor, y_tensor) loader DataLoader(dataset, batch_size64, shuffleTrue) # 类别权重样本少的类别权重大 class_counts np.bincount(y_train) class_weights 1.0 / class_counts class_weights class_weights / class_weights.sum() * len(class_counts) criterion nn.CrossEntropyLoss(weighttorch.FloatTensor(class_weights)) optimizer optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) scheduler optim.lr_scheduler.StepLR(optimizer, step_size20, gamma0.5) for epoch in range(60): model.train() total_loss 0 for batch_x, batch_y in loader: optimizer.zero_grad() output model(batch_x) loss criterion(output, batch_y) loss.backward() optimizer.step() total_loss loss.item() scheduler.step() if (epoch 1) % 10 0: print(fEpoch {epoch1}, Loss: {total_loss/len(loader):.4f}, LR: {scheduler.get_last_lr()[0]:.6f})weight_decay1e-4是 L2 正则化防止权重过大。学习率初始 1e-3每 20 个 epoch 减半。这个调度策略在轴承数据上比较稳不会出现损失震荡。如果发现训练损失下降但验证损失上升先把 dropout 调到 0.5再考虑减小网络容量。3.3 验证策略按工况划分还是随机划分这是最容易翻车的地方。很多人把数据随机打乱后划分训练集和验证集结果验证准确率 99%一上新工况就掉到 60%。原因很简单同一个工况下的样本被同时分到了训练集和验证集模型只是记住了这个工况的分布没有学到真正的故障特征。正确做法是按工况划分。比如数据集有 0hp、1hp、2hp、3hp 四种负载那就用 0hp 和 1hp 做训练2hp 做验证3hp 做测试。这样验证准确率才反映模型在新工况下的泛化能力。如果按工况划分后准确率掉得厉害说明模型过拟合了工况需要加数据增强或者用域适应方法。我一般会同时看两个指标随机划分的准确率和按工况划分的准确率。两者差距在 5 个百分点以内说明模型学到的特征比较鲁棒差距超过 15 个百分点就得回头检查数据划分和增强策略了。4. 滚动轴承故障诊断的避坑与排查清单4.1 准确率虚高但实际部署失效现象验证集准确率 98% 以上但把模型部署到新设备上误报率极高。原因数据泄露。最常见的是同一个样本的重叠切分片段同时出现在训练集和验证集里。重叠 50% 时相邻两个样本有 512 个点完全一样模型相当于在验证集上看到了训练集的副本。解决切分样本前先按时间顺序划分训练、验证、测试三段再在各自段内做重叠切分。或者直接用不重叠切分牺牲一些样本数量换真实评估。4.2 损失不下降或震荡剧烈现象训练几个 epoch 后损失卡在某个值不动或者上下大幅震荡。原因学习率太大或太小。太大导致震荡太小导致下降缓慢。另外如果输入信号没有归一化梯度尺度差异大也会震荡。解决先用 1e-4 到 1e-3 之间的学习率试。如果震荡降到 1e-4如果下降太慢升到 3e-3。同时检查每个样本是否做了 z-score 归一化。批归一化层也能缓解这个问题但输入归一化是前提。4.3 某些故障类别始终分不对现象外圈故障和正常样本分得很准但内圈故障和滚动体故障混淆严重。原因内圈故障和滚动体故障的特征频率在频谱上可能重叠尤其是故障尺寸较小时。另外如果这两类样本数量少模型学不充分。解决先看混淆矩阵确认是哪两类在混。如果是样本少加类别权重或做过采样。如果是特征重叠考虑增加网络深度或引入频域特征作为辅助输入。我试过把原始信号和包络谱拼接成双通道输入对内圈和滚动体故障的区分度有明显提升。4.4 模型在边缘设备上推理太慢现象PC 上推理没问题部署到嵌入式设备后单次推理超过 100ms满足不了实时性要求。原因模型参数量或计算量太大。全连接层是参数量大户卷积层是计算量大户。解决用全局平均池化替代全连接层参数量能降一个数量级。卷积层用深度可分离卷积替代标准卷积计算量降到三分之一左右。如果还不行把输入窗口从 1024 降到 512但要注意故障特征是否还完整。4.5 不同批次数据分布不一致导致模型失效现象同一台设备换了传感器或调整了采样率后模型准确率大幅下降。原因模型学到了传感器相关的特征而不是故障本身的特征。采样率变化会直接改变故障特征频率在频谱上的位置。解决训练时加入不同采样率和不同传感器的数据做增强。如果做不到至少保证部署时的采样率和训练时一致。另外在输入端做重采样把所有数据统一到同一个采样率再送进模型。5. 从分类到可解释用卷积核激活定位故障冲击模型告诉你“这是外圈故障”但你不知道它凭什么这么判断。在工业场景里这个“凭什么”往往比分类结果本身更重要——维修人员需要知道故障发生在哪个时间点、哪个冲击周期才能定位到具体位置。一维 CNN 的卷积核激活可以给出这个答案。做法很简单取一个测试样本前向传播到第一层卷积把每个卷积核的输出拿出来。哪个卷积核在哪个时间点激活最强就说明那个时间点有它学到的模式。把激活值画出来和原始振动信号叠在一起就能看到模型关注的是哪些冲击。def visualize_conv_activation(model, sample, conv_layer_idx0): 提取指定卷积层的激活并返回 sample: (1, 1, 1024) 的单个样本 activations [] x sample for i, layer in enumerate(model.features): x layer(x) if i conv_layer_idx: activations.append(x.detach().squeeze().numpy()) return activations[0] # 取一个外圈故障样本 sample X_test[0:1] act visualize_conv_activation(model, torch.FloatTensor(sample).unsqueeze(1)) # act shape: (32, 时间点数)32 个卷积核各自的激活拿到激活矩阵后对每个卷积核的激活做归一化然后找激活峰值对应的时间点。如果多个卷积核的峰值都集中在某个周期附近说明模型确实学到了周期性的冲击特征。如果激活是散乱的说明模型可能学到了噪声或工况相关的伪特征这时候就要回头检查数据质量了。我一般会抽 10 个测试样本做这个可视化如果 8 个以上的激活峰值和故障冲击周期对得上就认为模型学到的特征是可信的。这个方法比单纯看准确率更有说服力尤其是在向产线人员解释模型决策时。还有一个进阶用法把激活峰值的时间间隔算出来反推故障特征频率和理论计算值对比。如果对得上说明模型学到的物理意义是对的如果对不上但分类准确率很高那就要警惕模型是不是在走捷径。这个习惯帮我避免了好几次“准确率虚高但实际不可用”的翻车。希望帮到你。本文还有配套的精品资源点击获取