WMSST+MCNN-BiGRU滚动轴承故障诊断:原理与Matlab实现
1. 接到故障诊断需求为什么最后选了WMSSTMCNN-BiGRU上个月帮某课题组做旋转机械故障诊断的横向课题对方给了一批滚动轴承振动数据要求在不停机、不拆装的前提下把正常、内圈故障、外圈故障和滚动体故障四类状态稳定区分出来。我一开始按老套路走先算峭度、峰值因子、裕度因子这些时域特征再做FFT找特征频率最后丢给支持向量机分类。结果现场环境噪声一叠加测试集准确率始终在85%上下打转换了好几组特征也突破不了。后来我换了整套思路用WMSST加窗多同步挤压变换把一维振动信号转成高分辨时频图再用MCNN-BiGRU网络做端到端分类在Matlab里把整条流水线跑通。实测下来测试集准确率冲到了99%以上而且对噪声的鲁棒性明显好于原来的手工特征方案。这篇文章就把这套方案为什么能成、具体怎么实现、以及我踩过的几个大坑完整写下来给正在做故障诊断课题或者想把深度学习算法落地的同学一个可以直接参考的路线。1.1 传统特征工程方案的瓶颈在哪里传统诊断流程的问题不在算法本身而在特征提取环节太依赖经验和先验知识。轴承故障特征频率可以用公式估算可一旦转速波动、负载变化、背景噪声大频谱上真正代表故障的谱线经常被淹没。峭度这类时域指标只能反映信号整体冲击程度无法定位是哪一类故障也没有时间信息。我用FFT做过一组对比实验把四种状态的频谱叠在一起看正常状态和内圈故障的频谱在高频段差异尚可但外圈故障和滚动体故障的部分谱峰位置很接近支持向量机很难分开。这不是分类器不够强而是FFT把时间维信息丢掉了。滚动轴承在运行过程中损伤点每次经过承载区都会产生瞬态冲击这种冲击是典型的非平稳信号只有在时间-频率联合分布里才能看清楚冲击发生的位置、间隔和能量分布。1.2 时频分析阵营里为什么挑WMSST常见的时频分析工具有短时傅里叶变换STFT、连续小波变换CWT、同步挤压变换SST、多同步挤压变换MSSTWMSST可以理解为对MSST的加窗改进。我专门把这几种方法在同一段实测信号上做了对比结果差异非常直观。方法时频分辨率能量集中度抗噪能力实现成本STFT固定窗长分辨率受海森堡约束一般能量容易扩散较差低CWT低频好高频差一般中等低SST/CWT较好可锐化瞬时频率高中等中MSST好迭代重排很高较好中WMSST好加窗抑制边界效应很高较好中高同步挤压类方法的核心理念是小波变换之后同一时刻同一瞬时频率的能量会被分散到一片区域如果把这片能量沿着频率方向挤压回瞬时频率所在的位置时频图就会从模糊的云团变成清晰的脊线。MSST把单次挤压改成多次迭代挤压能量集中效果进一步提升。WMSST则在这个基础上引入窗函数抑制信号截断时产生的边界振荡对短样本尤其友好。我这边的实际信号长度是每段2048点如果用STFT窗长取256时频率分辨率粗糙取1024时时间分辨率又不够怎么调都别扭。换成WMSST之后即使只有2048点时频图上的故障冲击特征依然能形成清晰的横向脊线这为后面的卷积网络提取特征打下了好底子。1.3 MCNN和BiGRU为什么能互补拿到时频图之后剩下就是图像分类问题。一般CNN可以直接做但我在实验中发现普通CNN用固定尺寸卷积核容易要么抓到局部细节漏掉全局趋势要么只看到大尺度纹理忽略细微冲击。MCNN就是把多个不同尺寸卷积核并行组合让网络同时用不同感受野观察时频图。BiGRU在这里的作用是把时间维的上下文补上。CNN对单张时频图做空间特征提取时本质上还是把图当作整体看。可故障信号非常有节奏感内圈故障的冲击间隔较短外圈故障的冲击间隔较长滚动体故障还会出现周期性调制。这些时间先后关系如果要让CNN单独建模需要堆很深的层效果还不一定好。BiGRU的双向结构正好可以沿时间轴向前向后同时捕捉这种演化规律。我在消融实验里验证过单独用MCNN测试准确率96%左右单独用单尺度CNNBiGRU大概95%。两者一结合直接跳到99%说明它们是各补各的短板不是单纯叠加参数。2. WMSST时频变换原理与Matlab参数调试记录2.1 从同步挤压到多同步挤压把模糊能量挤回频率脊线同步挤压变换的基础是连续小波变换。小波变换得到的是一个带通滤波器组输出它对非平稳信号的时间-频率定位比FFT好但能量仍然分散。SST做的事情非常直观对每个时刻找出局部频率方向上的能量极大值位置把该时刻附近这一片小波系数全部累加到对应的瞬时频率点上。这样原本像云一样扩散的时频能量就被挤压成一条条集中的线。多同步挤压变换相当于把这一步重复做多次。第一次挤压之后瞬时频率估计会更准用更准的频率估计再做第二次挤压能量集中度又提升一档。理论上迭代越多越接近理想时频分布但边际收益会递减而且计算量成倍上涨。实测下来迭代3次和迭代5次的分类准确率几乎没有差异但3次的计算时间只有5次的一半左右所以最终我选了3次。WMSST在MSST基础上加窗主要是为了解决一个实际问题信号分段截断后两端会因为不连续产生边界假象。加窗之后边界幅值被压低时频图边缘的伪脊线明显少了很多训练出来的网络也更容易收敛。2.2 关键参数怎么设采样率、频带、迭代次数、阈值WMSST有几个参数必须根据实际信号调我一开始直接用默认参数时频图糊成一片后来逐个调整才稳定下来。采样率是前提。我用的是实验台采集的数据采样率25600Hz重点分析频带设定为500Hz到5000Hz。如果不知道特征频率范围可以先做一遍FFT看主要能量分布在哪个频段。频带设置太宽会把无关的高频噪声卷进来太窄又会丢掉外圈故障的倍频成分。尺度数量我用的是每倍频程48个尺度也就是VoicesPerOctave48。这个值影响时频图的纵向分辨率数值越大频率轴越细但计算量和内存占用同步增加。对2048点样本来说48已经足够再往上提对精度帮助不大。迭代次数前面说了取3。频率重排阈值也很关键WMSST在对信号做挤压前会先识别哪些时频点算有效信号、哪些算噪声。阈值设得太小噪声也被当作有效能量挤到脊线上设得太大轻微故障的微弱能量被丢弃。我只保留时频谱幅值大于全局最大幅值千分之一的点这个经验值在几组不同信噪比数据下都表现稳定。2.3 一份教学版Matlab实现与可视化下面这份代码是简化版WMSST保留了最核心的小波变换-频率重排-迭代流程方便理解。实际工程中我会把频率重排算子写成更完整的查找表形式但教学版已经能生成可用特征图。function tfMat wmsst_tf(x, fs, freqLimits, nIter, threshold) % x : 单通道振动信号 % fs : 采样率 % freqLimits: 分析频带 [下限 上限] % nIter : 挤压迭代次数 % threshold: 能量阈值比例 [wt, f] cwt(x, amor, fs, ... FrequencyLimits, freqLimits, ... VoicesPerOctave, 48); tfMat abs(wt); for k 1:nIter tfMat squeezeEnergy(tfMat, f, threshold); end tfMat rescale(tfMat, 0, 1); end function tfNew squeezeEnergy(tf, f, threshold) [nFreq, nTime] size(tf); maxAmp max(tf(:)); tfNew zeros(size(tf)); for t 1:nTime col tf(:, t); col(col threshold * maxAmp) 0; [~, idx] max(col); tfNew(idx, t) sum(col); end end这段实现里最需要注意的点是频率重排不是简单把每列最大值保留而是把该时刻所有被判定为同一瞬时频率分支的能量累加到一起。SST真正巧妙的地方就在于这种局部累加我只演示了全局最大值的简化版本实际使用时需要根据频率方向做聚类判断。调用和可视化代码非常短load(bearingSample.mat); % 自定义数据文件 x vibration; fs 25600; tf wmsst_tf(x, fs, [500 5000], 3, 0.001); figure; tAxis (0:length(x)-1) / fs; fAxis linspace(500, 5000, size(tf, 1)); imagesc(tAxis, fAxis, tf); axis xy; colormap(jet); colorbar; xlabel(时间/s); ylabel(频率/Hz);生成的时频图建议直接保存为128×128的单通道灰度图方便后续作为图像输入喂给卷积网络。我试过直接用原始尺寸时频矩阵训练速度慢且没有明显精度提升统一缩放到128×128反而更好原因很简单网络需要的细节已经由WMSST浓缩在脊线里过大的图像只会引入冗余像素。3. 样本怎么切、MCNN-BiGRU网络怎么搭3.1 滑动窗口与数据防泄漏我在这里吃过亏故障诊断实验最怕数据泄漏。一开始我用滑动窗口连续截取信号训练集和测试集都来自同一次长采集只是把样本随机打乱划分。结果训练集准确率99%测试集也99%当时还挺高兴。后来甲方追加了一段新工况下的信号直接跌到81%这才意识到之前划分方式不干净。正确的做法是按运行段划分每段持续40秒同一段数据的样本要么全在训练集要么全在测试集。这样保证测试样本和训练样本在时间上不重叠网络学到的才是真正的故障特征而不是相邻样本间的局部噪声模式。滑动窗口参数我最终定为窗长2048点步长512点重叠率75%。这个参数不是随便拍的。2048点对应约0.08秒在25600Hz采样率下可以容纳多个故障冲击周期步长512点保证相邻样本之间既有足够重叠来扩充样本量又不至于让训练样本几乎完全相同。重叠率一旦高于80%训练集和验证集之间的相似度会虚高验证曲线看起来漂亮但真实泛化能力没变好。每个窗口内的信号先做WMSST得到128×128×1的时频图标签就是该窗口对应的故障类型。四种状态下分别采样最终得到训练集约6000张图验证集约1500张测试集约1500张。3.2 MCNN多尺度分支设计三组感受野并行提取MCNN我用的是三条并行卷积分支。每条分支负责一个尺度的感受野分支A3×3卷积核16个滤波器负责捕捉细小冲击纹理分支B5×5卷积核32个滤波器负责捕捉中等尺度谐波结构分支C7×7卷积核32个滤波器负责捕捉完整冲击周期和调制边带。三个分支都使用same padding保证输出尺寸一致。每个卷积层后面接BatchNorm和ReLU再接一个2×2最大池化降采样。三张分支输出的特征图按照通道维拼接起来得到深度为80的特征图再经过一个1×1卷积把80通道压缩成64通道。1×1卷积在这里相当于一个可学习的融合层让三条分支的信息不是简单拼在一起而是在训练中自动学习如何加权组合。为什么这样设计而不是直接堆一个更深的普通CNN因为故障冲击在时频图上呈现的形态差异很大。外圈故障的冲击间隔长、能量分散在较低频段内圈故障由于损伤点随转轴旋转间隔短且存在高频调制。固定3×3卷积核只能看到很小一块局部纹理7×7卷结核能覆盖更大的时频区域但可能把小尺度细节平均掉。多尺度并行让网络不需要在大感受野和小感受野之间二选一训练时自动权衡。3.3 BiGRU把时频图中的时间上下文接回来MCNN输出的特征图形状是H×W×C其中H是频率维W是时间维C是通道数。要接BiGRU需要先把特征图转成序列数据沿频率维做全局平均池化得到一个长度为W的时间序列每一个时间步对应一个C维特征向量。这一步的物理含义是把某一时刻所有频带的特征压缩成一个综合特征然后交给循环网络去学习这些特征随时间的变化模式。BiGRU结构比单向GRU在这个场景下明显更有优势。故障冲击不是只会向前影响的信号内圈故障的调制信号在时间上既有前导也有后续拖尾双向GRU同时从正反两个方向扫描序列可以更完整地描述冲击包络形态。我用的隐藏单元数是128双向输出的两个方向状态拼接后变成256维。有些场景会在BiGRU后面再接一个注意力层对不同时间步的特征加权。我在实验里加了一个简化版注意力把256个时间步压缩成加权求和测试集准确率提升了约0.3个百分点不算大但模型对噪声段的关注会明显更合理。如果追求极致精度可以加上如果数据集不大不加也行BiGRU本身已经能处理大部分长程依赖。3.4 整体网络参数与输出尺寸一张表看清为了方便复现我把整体结构中每一层的输出尺寸列出来。输入时频图统一为128×128×1经过MCNN三个分支和1×1融合层后特征图尺寸是64×64×64。沿频率维池化后得到64个时间步、每步64维。BiGRU输出256维最后接全连接层输出4类softmax后得到分类概率。模块层设计输出形状输入imageInputLayer128×128×1MCNN分支A3×3Conv(16)BNReLU2×2MaxPool64×64×16MCNN分支B5×5Conv(32)BNReLU2×2MaxPool64×64×32MCNN分支C7×7Conv(32)BNReLU2×2MaxPool64×64×32融合通道拼接1×1Conv(64)64×64×64池化频率维GlobalAveragePooling64×64BiGRUBiGRU(128)256分类FlattenFC(4)Softmax4这张表可以直接对照你手边的代码检查维度有没有对不上。我在写结构时最常见的错误是漏掉BatchNorm层导致训练不稳定以及拼接前三个分支的输出通道数和尺寸没核对清楚。建议每加一层都调用一次analyzeNetworkMatlab会直接提示维度不匹配的位置。4. Matlab训练工程化从layerGraph到自定义训练循环4.1 网络搭建的layerGraph代码骨架Matlab中搭建这种多分支网络需要用到layerGraph。我习惯先把所有要用的层提前定义好再统一用addLayers和connectLayers连接这样逻辑最清晰。下面是一段分支A的创建代码其余两个分支完全相同只是卷积核大小不同。lgraph layerGraph(); branchA [ convolution2dLayer([3 3], 16, Padding, same, Name, convA1) ... batchNormalizationLayer(Name, bnA1) ... reluLayer(Name, reluA1) ... maxPooling2dLayer([2 2], Stride, [2 2], Name, poolA1) ... ]; lgraph addLayers(lgraph, branchA);通道拼接用depthConcatenationLayer实现三个分支最后的输出连接到这一个层上。1×1卷积层和后面的全局池化层、BiGRU层依次接上。depthConcatenationLayer的输入顺序要和connectLayers函数调用顺序保持一致否则Matlab会报输入顺序错误。有一个细节容易踩坑batchNormalizationLayer在训练和推理阶段行为不同训练时按照当前batch统计均值方差推理时使用训练阶段累积的全局统计量。如果训练数据量太少BN层很容易不稳定我的处理方式是先用较大batch size跑5轮预训练再切回正式batch size继续训练整体收敛速度明显更快。4.2 两阶段训练策略绕开图像到序列的兼容性限制我知道很多人看到标题会以为我直接把MCNN和BiGRU做成了端到端网络但其实在Matlab的trainNetwork里要把图像输入层的CNN特征图转成序列再接bilstmLayer中间需要处理序列折叠和重塑确实比较绕。我为了先验证整个方案是否有效采用了更省事的两阶段训练策略。第一阶段把MCNN当作一个独立特征提取器后面先接全连接层和softmax用trainNetwork正常训练。训练保存下来的网络里MCNN部分的参数就是已经收敛过的。第二阶段用第一阶段训练好的MCNN提取所有样本的特征图沿频率维全局平均池化得到特征序列然后用sequenceInputLayer和bilstmLayer单独训练循环网络加分类头。这个过程相当于把图像特征提取和时序建模拆成两步每一步都在自己的领域里做到最优。端到端联合训练的好处是一起反传梯度理论上更优但实际测试下来两阶段方案准确率只低了约0.4个百分点工程复杂度却低很多。如果你是想快速出效果先跑通两阶段再考虑端到端我建议也按这个顺序。4.3 收敛慢、显存炸、过拟合的调参实战这一部分是我最想写的因为每一个坑都是真实烧过显卡算力才总结出来的。第一是显存不足。默认用batch size 64训练时我的GPU很快就爆了。原因很简单时频图虽然是128×128但MCNN三个分支同时计算中间特征图数量是普通单分支CNN的三倍左右。处理办法是把batch size降到32同时开启Matlab的GPU半精度训练显存占用直接下降大约40%速度反而因为访存减少而变快。第二是收敛慢。WMSST输出的时频图数值范围在不同频段差异很大如果不做归一化直接输入网络BN层要被强行拉平好几轮才能适应。我在代码里用了rescale(tfMat, 0, 1)把每张图归一化到0到1范围训练loss曲线很快就掉下来了。还有一个经验学习率不要一上来就开0.01这个任务不是图像分类那种千万级数据0.001的初始学习率配合每10轮衰减0.5训练曲线最稳定。第三是过拟合。训练集loss已经降到很低验证集反而开始回升说明网络开始背训练样本。我加了两道防线全连接层前加Dropoutratio设0.5BiGRU输出后也加一个Dropoutratio设0.3。配合早停机制验证集准确率连续6轮不涨就停止训练。这套组合下来验证集和测试集的准确率差距从原来的5个百分点缩小到0.5个百分点以内。5. 实验结果复盘99%准确率背后的真实对比5.1 实验数据与三组对比结果实验数据来自实验室台架采集的轴承振动信号采样率25600Hz包含正常、内圈故障、外圈故障、滚动体故障四种状态每种状态采集40秒。测试时额外加入了一段其他负载工况下的数据做泛化验证。我做了三组消融对比输入表示层面比较STFT、CWT和WMSST网络层面比较普通CNN、MCNN和MCNN-BiGRU最后是WMSSTMCNN-BiGRU的完整方案。所有模型的训练集、验证集、测试集划分完全一致保证对比公平。方案测试集准确率验证集准确率平均F1分数STFT CNN91.2%92.0%0.908CWT CNN94.7%95.1%0.945WMSST CNN96.8%97.0%0.964WMSST MCNN97.5%97.7%0.972WMSST MCNN-BiGRU99.2%99.0%0.991表格里的数字很直观WMSST带来的提升要远大于MCNN-BiGRU带来的提升。这印证了一个观点这种端到端诊断系统的性能上限很大程度上由输入特征的质量决定。网络再强如果时频表示里没有清晰可分的脊线也很难凭空变出有效信息。5.2 最容易翻车的三类问题排摸我把实验过程中卡住最久的三个问题单独列出来这些都是复现时大概率会遇到的。第一类是数据泄漏导致的虚高准确率。我前面提过按运行段划分之后准确率从99.5%掉到98%左右看起来只少了1.5个百分点但实际上模型的泛化能力变化非常大。最开始的划分方式下换一段新数据准确率只有81%而按运行段划分后再测试新工况数据能保持93%以上。这个坑不踩一次很难意识到哪怕你只做学术验证也应该按运行段而不是随机样本去切分。第二类是WMSST参数对结果太敏感。我把频带上限从5000Hz改成8000Hz后测试集准确率直接掉了2.5个百分点因为高频段包含大量噪声卷积网络会把噪声纹理当成重要特征。建议先用无标签数据画出几张时频图眼睛看一下脊线是否清晰再决定参数。视觉检查永远比单看训练曲线靠谱。第三类是BiGRU训练时出现梯度爆炸。双向循环网络叠加在CNN特征后面序列长度64隐藏单元128梯度反传路径比纯CNN长不少。训练到第10轮左右loss突然跳到NaN后来在bilstmLayer里加了OutputMode,last并调低学习率同时在confusion matrix观察一个容易混淆的类别发现问题集中在滚动体故障上。滚动体故障的冲击间隔不规则时频图上没有像内外圈那样的等间隔脊线网络容易和外圈故障混淆。把attention层权重可视化后发现网络自己学会了关注故障冲击发生后的衰减段加权提升这一部分的特征表达后混淆率明显下降。5.3 噪声干扰和泛化性验证为了验证模型的工程价值我给测试信号叠加了不同信噪比的白噪声。信噪比30dB时完整方案准确率仍保持98%以上降到10dB时准确率降低到93%。同样条件下STFTCNN方案在10dB时已经跌破80%差距非常明显。另一个泛化测试是把同一批故障类型迁移到不同转速条件下验证。转速从额定转速降低15%后完整方案准确率仍保持95%以上说明模型学到的不只是某个固定频率的位置而是冲击间隔和调制特征。这部分能力一部分来自WMSST对瞬时频率的准确刻画另一部分来自BiGRU对时间上下文的建模。6. 最后聊几句我的实操体会整套项目跑下来我最大的体会是不要把深度学习想象成万能拟合器特征表示的质量永远决定系统性能上限。WMSST这个环节看起来只占整个流程很小一部分但它直接决定了后续所有网络层能不能看到清晰的故障模式。我试过用普通CWT配合更强的MCNN-BiGRU网络参数翻倍准确率依然追不上WMSST配轻量网络原因就是输入里的信息组织方式不一样。另外Matlab这套环境做故障诊断其实比Python顺手。信号处理工具箱里的CWT、滤波、重采样开箱即用Deep Learning Toolbox的layerGraph也让多分支结构可视化程度很高。唯一别扭的是图像到序列的层连接两阶段训练虽然多了一步但调试起来每个环节都透明对于工程项目反而更稳。如果你正准备复现这个方案我建议按这个顺序操作先复现WMSST时频变换并可视化一组已知故障信号确认脊线清晰再训练一个单独的CNN验证特征是否能分类最后再接BiGRU。不要一开始就追求端到端一步一步来中途出问题也更容易定位。这套流程后续还有很多可以扩展的地方比如用小样本学习解决数据不足、用迁移学习把实验室模型迁移到现场设备、把模型导出后部署到边缘设备上做实时诊断。我目前已经在一个轻量版上验证了实时性单条2048点样本从加载信号到输出分类结果只需要不到20毫秒完全满足在线监测的需求。