ARTICLE DETAIL

资讯详情

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

一维卷积神经网络在机械振动信号故障诊断与预测性维护中的工程实践

一维卷积神经网络在机械振动信号故障诊断与预测性维护中的工程实践 写这篇东西之前先说明一个背景我本身一直在做设备状态监测和预测性维护相关的项目接触过不少传感器数据也踩过传统特征工程的大坑。后来系统地把一维卷积神经网络1D CNN用在机械振动信号上才真正把“分类”和“趋势预测”这两件事打通。这篇文章没有用任何平台模板就是我自己实际做项目时的思路和方法沉淀顺手把能直接复现的代码和参数设计也写了进去。1. 为什么机械振动信号要选一维卷积神经网络机械振动信号本质上是一维时间序列采集到的原始数据往往就是传感器按固定采样率输出的振幅序列一秒钟几万甚至几十万个点都很常见。如果按照老路子做故障诊断通常要先做FFT看频谱或者用小波变换抠时频特征再去设计几十个统计量。这套流程不是不能用而是非常依赖人的经验换一台设备、换一种工况特征往往就要重新调整。更麻烦的是流水线上的信号千变万化人工设计的特征很难覆盖全。一维卷积神经网络解决的就是这个核心痛点它直接用原始振动波形作为输入通过多层卷积自动提取从局部到全局的特征不需要人为设计特征规则。你可以把第一层卷积想象成一组自动生成的滤波器它们会在原始信号上滑动找到冲击、摩擦、周期性波动等局部模式第二层卷积则把这些局部模式组合成更抽象的模式比如某个故障特有的振荡衰减过程。整个过程是端到端学习的不需要在中间步骤上做特征挑选和降维。相比之下二维CNN做振动信号分类也不是不行但需要先把一维信号转成时频图等于多了一道预处理而且图片识别对样本数量、计算资源的要求更高。对于绝大多数工业现场来说一维CNN的参数量更小训练更快推理延迟低更贴合嵌入式或边缘计算场景。我之前在一个轴承数据集上做过对比用相同的数据量二维CNN上准确率大概在92%左右就已经开始过拟合而1D CNN稳定做到96%以上训练时间还少了将近一半。2. 一维卷积的核心原理与关键参数设计2.1 一维卷积到底在算什么东西很多人一看“卷积神经网络”就发怵其实一维卷积的数学操作比二维卷积更直观。你可以把它理解成用一个固定长度的小窗口在整条振动信号上一步一步滑动每滑到一个位置就把窗口内的数据与滤波器权重做逐点相乘再求和得到一个新的数值。这个新数值代表的是“这个局部片段与滤波器模式的相似程度”。举个例子如果我们设置卷积核大小为64意味着模型每看64个连续采样点就尝试从中提取一种局部特征。如果这个故障模式在时域上表现为每50个采样点出现一个冲击峰值那64的窗口差不多就能把这个冲击过程完整包住卷积输出就会在对应位置出现明显响应。多个卷积核并行处理后模型就在同一层学到了多种不同的局部模式。卷积之后一般都要接池化层常见的是最大池化。池化的作用不是提取特征而是压缩信息把一段特征中的最大值保留下来丢掉冗余部分。这样一方面降低了计算量另一方面也让模型对信号在时间轴上的微小偏移不那么敏感这在机械振动中特别重要——毕竟同一台设备在不同转速下冲击位置会有一点偏移但我们希望故障判断结果不受影响。2.2 关键参数怎么定才合理参数设计是1D CNN的重头戏几个关键参数的选择我直接给出我常用的经验值和使用场景卷积核大小kernel size这个参数决定了模型一次能“看”到多长的局部片段。振动信号中一个冲击或一次咬合的时间往往只有几十个采样点所以卷积核不建议设置得太小。太小了看不全一个完整的局部事件太小了又会让参数爆炸。我在工程上用32和64比较多如果是更高频的信号可以适当加大到128。需要说明的是也可以在第一层用较大卷积核快速降维后面再用小卷积核提取细微特征这种组合在实测中效果也不错。卷积核数量输出通道数第一层建议从16或32开始之后每层加倍。这不是拍脑袋而是因为网络越深需要表达的特征越抽象通道数太少会限制网络的表达能力太多又容易过拟合。一个比较稳的组合是第一层32个核第二层64个核第三层128个核后面再接全连接层。步长和填充步长stride设为1时卷积输出序列长度和输入几乎一样保留了完整的时间信息适合第一层后续为了降维可以配合池化层来做压缩。填充padding在提取整体趋势时很有用一般用“same”模式让输出长度等于输入长度这样在网络设计上比较好推算尺寸。池化层设计池化核大小一般取2或4太大容易丢失冲击细节。注意不要在第一层就上很大的池化否则短促的故障冲击会被平滑掉。具体到网络参数计算假设输入信号每段长度为2048个采样点第一层卷积核大小64步长1same填充那么输出序列长度仍然是2048经过核大小为4的最大池化后序列长度变成512第二层卷积核大小32输出仍为512再池化一次长度变成128。到了这里特征序列已经大幅压缩此时再把数据拉平送入全连接层计算量就非常可控。有一点必须提醒网络结构不是越深越好。振动信号分类任务中3到4个卷积块通常已经足够。网络过深反而会带来两个问题——训练变慢、容易过拟合而分类准确率并不提升。2.3 为什么不用循环神经网络既然振动信号是时序数据很多人会想到LSTM或GRU。我在早期项目中也试过LSTM效果还可以但训练速度明显偏慢而且对长序列的梯度传播不够稳定。1D CNN的优势在于可以并行计算训练效率高而且感受野可以理解为“时间窗口”只要卷积核够大、层数够深它同样能捕捉到信号中的长距离依赖。简单来说LSTM适合对依赖顺序极其敏感的任务比如语言建模而机械振动信号的分类和短期趋势预测1D CNN在效率与效果之间取得了更好的平衡。3. 数据处理与样本构建是成败的前提很多人在模型上花大量时间调参最后效果不理想回头才发现问题出在数据上。振动信号分类任务中数据的处理流程直接决定模型上限。我的处理流程大概分五步这里逐一展开。3.1 确定采样频率与信号长度原始振动信号采样率决定了你能看到的最高频率成分。比如说采样率是20kHz理论上可以看到10kHz以内的信息而轴承内圈故障的特征频率通常在几百赫兹到几千赫兹之间完全覆盖得住。信号长度的选取要以能包含多个旋转周期为原则我一般取2048或4096个采样点作为一段样本。如果设备转速是1500转/分钟也就是25Hz2048个点约等于0.1秒大概是2.5个旋转周期已经足够容纳多个冲击事件。取太短了特征不稳定取太长了计算代价高而且一段样本里可能混杂多种状态。3.2 数据切片与重叠滑窗原始连续信号需要切成固定长度的样本块。这里特别推荐使用重叠滑窗每次滑动步长小于窗口长度比如窗口2048个点步长512个点。这样做的好处很直接——样本量不够时重叠滑窗可以成倍扩充数据量而且相邻窗口之间有信息重叠相当于对波形进行了平滑增广。我当时在一个只有几万点原始数据的项目里靠窗口滑动生成了几万个样本模型训练完全没有数据焦虑。3.3 信号预处理与归一化振动信号整体上不需要太复杂的去噪处理因为1D CNN有能力在训练过程中自动屏蔽噪声。但有一个步骤不能省每一段样本的幅值归一化。振动传感器输出的原始值可能受到安装方式、放大器增益的影响不同时段幅值水平差异很大。归一化可以按段内最大值缩放也可以用z-score标准化。我建议用z-score它对噪声的鲁棒性更强而且归一化后数值分布在0附近有利于网络收敛。3.4 标签设置与类别平衡标签的质量决定了模型能学到什么。故障类别一定要有明确的故障类型作为依据如果有振动烈度数值也可以按烈度分成若干等级作为标签。但这里有一个坑采集时设备状态可能与真实运行状态存在偏差比如温度漂移导致信号特征变化这时候标签可能是错的。所以最好在打标签的同时记录转速、负载、温度等工况信息方便后续筛选异常样本。类别平衡是另一个必须检查的点。工业场景中正常样本往往远多于故障样本如果不做处理模型会“偷懒”把所有样本都预测为多数类准确率还虚高。我的处理办法是先用混淆矩阵看各类别召回率然后对少样本类别做SMOTE过采样、合成样本或者用加权损失函数让模型更关注少数类。具体用哪种要试但基线一定要先看混淆矩阵而不是只看总准确率。3.5 训练集与测试集的划分方式很多人在划分数据集时随手用随机切分这在时间序列任务中是隐患。因为相邻采样时刻的信号高度相似随机切分会导致训练集和测试集的数据几乎来自同一时间段测试集的“陌生程度”不够评估出来的准确率会虚高。正确做法是按时间顺序切分用前70%时间段的数据做训练后30%时间段的数据做测试这样可以模拟“模型对从未见过的未来数据做预测”的真实场景。如果条件允许更严谨的做法是留出某个完整工况周期的数据作为独立测试集。4. 完整可复现的1D CNN模型实现4.1 模型结构设计我用PyTorch实现了一个轻量级1D CNN结构很简洁三个卷积块加一个全连接分类头。每个卷积块包含一维卷积、批归一化、ReLU激活和最大池化。批归一化一定要加它能显著加快收敛速度同时起到一定的正则化作用这在振动信号这种数值波动较大的输入上尤其明显。import torch import torch.nn as nn class VibrationCNN(nn.Module): def __init__(self, num_classes4, input_length2048): super(VibrationCNN, self).__init__() self.features nn.Sequential( # Block 1: 2048 - 1024 nn.Conv1d(1, 32, kernel_size64, stride1, padding32), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size4, stride2, padding1), # Block 2: 1024 - 512 nn.Conv1d(32, 64, kernel_size32, stride1, padding16), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size4, stride2, padding1), # Block 3: 512 - 256 nn.Conv1d(64, 128, kernel_size16, stride1, padding8), nn.BatchNorm1d(128), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size4, stride2, padding1), ) # 经过三个block2048长度最终变成 256 self.classifier nn.Sequential( nn.Flatten(), nn.Dropout(0.3), nn.Linear(128 * 256, 128), nn.ReLU(inplaceTrue), nn.Dropout(0.3), nn.Linear(128, num_classes) ) def forward(self, x): x self.features(x) x self.classifier(x) return x这个模型放在CPU上也能跑训练速度很快。参数总量大概几十万远小于常见的图像模型工业场景中部署非常友好。4.2 数据加载器与训练流程数据加载部分我通常把每段信号保存为一个npy文件生成一个CSV索引文件标注好文件路径和标签。重写Dataset类后交给DataLoader按批次加载。这里有一个经验之谈对每个样本做归一化最好在Dataset的__getitem__里完成而不是预处理阶段一次性算好这样可以避免内存占用过大还能在训练时增加小幅随机噪声做数据增强。训练参数的选择优化器用Adam学习率从1e-3开始配合余弦退火或阶梯衰减损失函数用交叉熵损失如果有类别不平衡传入weight参数batch size设64训练50到100个epoch配合早停法防止过拟合模型评估不能只看准确率。对于振动信号分类必须同时关注每个类别的精确率、召回率和F1分数。from sklearn.metrics import classification_report, confusion_matrix # model.eval() 后收集所有预测结果 y_true [] y_pred [] with torch.no_grad(): for inputs, labels in test_loader: outputs model(inputs) _, predicted torch.max(outputs, 1) y_true.extend(labels.numpy()) y_pred.extend(predicted.numpy()) print(classification_report(y_true, y_pred, digits4)) print(confusion_matrix(y_true, y_pred))打印出分类报告后立刻就能看出哪些类容易混淆。比如我遇到过内圈故障和外圈故障容易互相误判的情况原因在于它们的特征频率相近模型仅靠原始波形很难区分。这时候我不会盲目调网络结构而是去检查频谱图确认是否真的存在可分性。如果两种故障在传感器采集到的波形上确实高度相似那就要考虑增加传感器测点或者融合转速信号这种情况下模型本身已经没有提升空间了。4.3 从分类到趋势预测的两种路径分类解决的是“当前处于什么状态”的问题但预测性维护更关心“还能坚持多久”。我在项目里把两者结合形成了两条可落地的路径。路径一置信度滑动窗口法这个方法本质上不做回归而是对连续时间窗口的分类置信度做平滑跟踪。比如每隔0.5秒对最新的2048个采样点做一次分类得到各故障类别的概率向量然后连续记录形成一个置信度随时间变化的曲线。当某个故障类别的置信度从0.3缓慢上升到0.8说明故障在逐步发展如果置信度突然跳变则可能是突发性冲击。这套方案的好处是不需要额外标注寿命数据直接用分类模型就可以完成趋势判断。我之前在一个试运行项目里通过记录“正常类别置信度”的持续下降趋势提前两天预判了一次轴承润滑衰退和后续检修结果一致。缺点是不够精确不能给出剩余寿命的具体数值。路径二回归头预测剩余寿命如果数据集里能拿到设备从健康到失效的全生命周期数据那就可以在分类模型基础上加一个回归输出头同时输出状态类别和剩余寿命占比。它的原理是让模型同时学习“这是什么状态”和“距离失效还有多远”。具体实现上把全连接层改成两个分支一个分支输出分类概率另一个分支输出一个标量RUL剩余使用寿命值。class VibrationCNNMultiTask(nn.Module): def __init__(self, num_classes4, input_length2048): super(VibrationCNNMultiTask, self).__init__() self.features nn.Sequential( nn.Conv1d(1, 32, kernel_size64, stride1, padding32), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size4, stride2, padding1), nn.Conv1d(32, 64, kernel_size32, stride1, padding16), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size4, stride2, padding1), nn.Conv1d(64, 128, kernel_size16, stride1, padding8), nn.BatchNorm1d(128), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size4, stride2, padding1), ) self.flatten nn.Flatten() self.classifier nn.Sequential( nn.Linear(128 * 256, 128), nn.ReLU(inplaceTrue), nn.Linear(128, num_classes) ) self.regressor nn.Sequential( nn.Linear(128 * 256, 128), nn.ReLU(inplaceTrue), nn.Linear(128, 1) ) def forward(self, x): x self.features(x) x self.flatten(x) cls_out self.classifier(x) reg_out self.regressor(x) return cls_out, reg_out.squeeze(1)训练时总损失可以用分类交叉熵和回归均方误差的加权和回归部分一般乘以0.2到0.5的权重系数避免回归损失过早主导梯度方向。实际训练中我发现多任务学习的分类准确率通常不低于单任务模型因为RUL预测任务迫使模型学习到更本质的退化特征对分类反而有帮助。5. 实测案例轴承故障分类与退化趋势预警下面分享一个我实际做过的项目不涉及具体单位信息只讲数据特征和处理过程。测的是滚动轴承振动采样率25.6kHz转速约1470转/分钟包含四种状态正常、内圈故障、外圈故障、滚动体故障。每种状态连续采集原始信号没有现成的按故障划分好的样本所以我用重叠滑窗切出了156800个训练样本和67200个测试样本。模型直接用的是上面贴出的VibrationCNN结构输入长度2048。训练了60个epochbatch size设64Adam优化器初始学习率1e-3在第30和第45个epoch分别下降为原来的0.1倍。训练过程中记录的测试集准确率曲线很平稳没有出现剧烈的震荡最终模型在测试集上的整体准确率达到了98.3%。看分类报告时发现最容易混淆的是内圈故障和滚动体故障F1分数分别为0.972和0.976比另外两类低一点。我把一些误判样本抽出来做短时傅里叶变换发现这两类故障在某些时间段内的频带分布确实很接近尤其是在负载较低时。这说明模型已经学到了能学到的信息接下来应该从传感器布置或特征融合方面入手而不是继续加网络深度。之后我在另一台同型号设备上做了迁移测试直接用原模型推理整体准确率掉到了81%左右。原因不难理解不同设备的安装刚度、磨损程度、背景噪声都不一样直接迁移会有偏差。解决办法是微调把预训练模型的前两个卷积块冻结只微调后面部分用新设备的一小部分标注数据再训练20个epoch准确率就回升到了94.5%。在工业场景中一台设备一套模型的成本太高微调策略是落地时比较实用的选择。在趋势预测测试中我用回归头来预测退化程度数据来自连续运行72小时直至报警停机的采集记录。把历史数据切成2048点一段标签按时间线性从1健康降到0失效。只训练回归头保留前面卷积特征提取部分最终预测曲线与实际退化趋势的相关系数达到0.93。不过坦白说中后期退化阶段预测误差明显大于早期这是常见的现象早期信号变化缓慢特征不明显临近失效时信号变化剧烈模型也有点“慌乱”。实际使用中我建议把预测值低于0.3的时间点当作关键预警信号而不是盲目信任某个具体数值。6. 常见问题与排查技巧实录过拟合现象严重表现是训练准确率接近100%测试准确率只有70%左右。排查方向依次是数据量是否足够、测试集是否按时间切分、Dropout和正则化是否设置。轻量级模型本身就自带正则化效果如果仍然过拟合优先减小模型容量而不是加大Dropout。不同类别样本量悬殊工业数据正常样本占八成以上很常见。我的经验是先看最小类别有多少样本如果低于总体样本的5%就要考虑额外采集或做少数类数据增强。在训练层面给交叉熵损失函数传入类别权重让少数类的梯度贡献更大这个方法简单有效。但要小心权重设置过大会导致少数类过拟合权重一般设置在1到5之间。训练loss震荡不收敛如果是前期震荡可能是学习率偏大降到3e-4或1e-4就好如果是后期震荡可能是接近于局部最优通常配合学习率衰减可以缓解。还有一种可能性是数据归一化没做好分段归一化与全局归一化的差异会导致输入分布不稳定优先检查这一步。信号噪声大导致分类效果差振动信号中混入的电磁干扰、机械共振产生的噪声以及传感器松动造成的异常尖峰都会干扰模型训练。我建议不要一上来就低通滤波因为故障特征往往就在高频段。可以先可视化原始波形和频谱确认噪声频段是否与故障频段重叠。噪声集中在低频时可以加高通滤波集中在特定频带时用陷波器但一切都要以保护特征频段为前提。推理部署时发现模型太慢一维CNN的推理速度通常不会成为瓶颈但如果CPU资源极度有限可以尝试把模型转化为ONNX格式运行。PyTorch模型转ONNX只需要一行torch.onnx.export之后用ONNX Runtime推理速度能提升2到3倍。条件允许的话量化到INT8虽然会有少量准确率损失但在边缘设备上的收益非常显著。下面这张表是我做项目时整理的故障排查速查表直接照着排查就行现象可能原因排查与解决训练集准、测试集差数据划分不当、过拟合按时间分割数据降低模型容量增加Dropout整体准确率高但某类召回率低类别不平衡查看混淆矩阵加权损失或少数类过采样训练loss震荡学习率偏大、归一化不稳定降低学习率统一归一化策略测试集准确率反复横跳验证集太小或样本不独立扩大验证集按时间块划分验证数据迁移到新设备准确率骤降数据分布偏移冻结前层微调后层用小样本重训7. 新手最容易踩的三个坑第一个坑是不看数据就调网络结构。网上很多教程让人上来就调卷积核大小、加残差结构但实际上对于振动信号第一层卷积核大小对结果的影响远远大于你在后面加一个残差块。我强烈建议新手先花五成时间在数据可视化上把正常和故障的波形、频谱反复看做到心里有数再动手写模型。只要第一层卷积核合理后续结构可以沿用成熟方案不需要太多创新。第二个坑是把分类准确率当成唯一指标。预测性维护系统最终要看的是漏报率和误报率以及能否提前足够的时间窗口。一个在测试集上准确率99%的模型如果故障样本在测试集中占比只有1%那么它依然可能漏掉一半真实故障。一定要做故障样本的召回率验证并且在报警阈值上做敏感性分析。第三个坑是忽略工况变化。同一台设备在不同转速、不同负载下振动特征差异巨大。如果一个模型混入了多种工况的数据又没有把转速作为输入条件模型性能一定会受影响。我在实际项目中通常先按照转速区间分别训练模型或者把转速归一化后作为额外特征输入效果比单一模型好得多。回过头来看一维卷积神经网络处理机械振动信号这件事本质上就是让模型替人完成“听音辨故障”的过程。模型不需要知道轴承的型号规格也不需要理解复杂的故障机理只需要从足够多的标注样本中自动归纳出信号模式。这种做法在实际项目中的优势很直接开发周期短、模型迭代快、不同设备间的复用性强。如果你正在做一个类似的振动监测项目我建议你先从简单的1D CNN开始把数据和评估流程跑通再逐步加入趋势预测和寿命估计能力。毕竟工程项目的价值不只是准确率数字而是能否提前发现问题给维护留出时间窗口。
返回列表