☰
WMSST+MCNN-BiGRU滚动轴承故障诊断:原理与Matlab实现
2026/10/20 9:33:30 网站建设 项目流程

1. 接到故障诊断需求:为什么最后选了WMSST+MCNN-BiGRU

上个月帮某课题组做旋转机械故障诊断的横向课题,对方给了一批滚动轴承振动数据,要求在不停机、不拆装的前提下,把正常、内圈故障、外圈故障和滚动体故障四类状态稳定区分出来。我一开始按老套路走:先算峭度、峰值因子、裕度因子这些时域特征,再做FFT找特征频率,最后丢给支持向量机分类。结果现场环境噪声一叠加,测试集准确率始终在85%上下打转,换了好几组特征也突破不了。

后来我换了整套思路,用WMSST(加窗多同步挤压变换)把一维振动信号转成高分辨时频图,再用MCNN-BiGRU网络做端到端分类,在Matlab里把整条流水线跑通。实测下来测试集准确率冲到了99%以上,而且对噪声的鲁棒性明显好于原来的手工特征方案。这篇文章就把这套方案为什么能成、具体怎么实现、以及我踩过的几个大坑完整写下来,给正在做故障诊断课题或者想把深度学习算法落地的同学一个可以直接参考的路线。

1.1 传统特征工程方案的瓶颈在哪里

传统诊断流程的问题,不在算法本身,而在特征提取环节太依赖经验和先验知识。轴承故障特征频率可以用公式估算,可一旦转速波动、负载变化、背景噪声大,频谱上真正代表故障的谱线经常被淹没。峭度这类时域指标只能反映信号整体冲击程度,无法定位是哪一类故障,也没有时间信息。

我用FFT做过一组对比实验:把四种状态的频谱叠在一起看,正常状态和内圈故障的频谱在高频段差异尚可,但外圈故障和滚动体故障的部分谱峰位置很接近,支持向量机很难分开。这不是分类器不够强,而是FFT把时间维信息丢掉了。滚动轴承在运行过程中,损伤点每次经过承载区都会产生瞬态冲击,这种冲击是典型的非平稳信号,只有在时间-频率联合分布里才能看清楚冲击发生的位置、间隔和能量分布。

1.2 时频分析阵营里为什么挑WMSST

常见的时频分析工具有短时傅里叶变换STFT、连续小波变换CWT、同步挤压变换SST、多同步挤压变换MSST,WMSST可以理解为对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%左右;单独用单尺度CNN+BiGRU,大概95%。两者一结合,直接跳到99%,说明它们是各补各的短板,不是单纯叠加参数。

2. WMSST时频变换原理与Matlab参数调试记录

2.1 从同步挤压到多同步挤压:把模糊能量"挤"回频率脊线

同步挤压变换的基础是连续小波变换。小波变换得到的是一个带通滤波器组输出,它对非平稳信号的时间-频率定位比FFT好,但能量仍然分散。SST做的事情非常直观:对每个时刻,找出局部频率方向上的能量极大值位置,把该时刻附近这一片小波系数全部累加到对应的瞬时频率点上。这样原本像云一样扩散的时频能量,就被"挤压"成一条条集中的线。

多同步挤压变换相当于把这一步重复做多次。第一次挤压之后,瞬时频率估计会更准;用更准的频率估计再做第二次挤压,能量集中度又提升一档。理论上迭代越多越接近理想时频分布,但边际收益会递减,而且计算量成倍上涨。实测下来,迭代3次和迭代5次的分类准确率几乎没有差异,但3次的计算时间只有5次的一半左右,所以最终我选了3次。

WMSST在MSST基础上加窗,主要是为了解决一个实际问题:信号分段截断后,两端会因为不连续产生边界假象。加窗之后,边界幅值被压低,时频图边缘的伪脊线明显少了很多,训练出来的网络也更容易收敛。

2.2 关键参数怎么设:采样率、频带、迭代次数、阈值

WMSST有几个参数必须根据实际信号调,我一开始直接用默认参数,时频图糊成一片,后来逐个调整才稳定下来。

采样率是前提。我用的是实验台采集的数据,采样率25600Hz,重点分析频带设定为500Hz到5000Hz。如果不知道特征频率范围,可以先做一遍FFT看主要能量分布在哪个频段。频带设置太宽会把无关的高频噪声卷进来,太窄又会丢掉外圈故障的倍频成分。

尺度数量我用的是每倍频程48个尺度,也就是VoicesPerOctave=48。这个值影响时频图的纵向分辨率,数值越大频率轴越细,但计算量和内存占用同步增加。对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我用的是三条并行卷积分支。每条分支负责一个尺度的感受野:

  • 分支A:3×3卷积核,16个滤波器,负责捕捉细小冲击纹理;
  • 分支B:5×5卷积核,32个滤波器,负责捕捉中等尺度谐波结构;
  • 分支C:7×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×1
MCNN分支A3×3Conv(16)+BN+ReLU+2×2MaxPool64×64×16
MCNN分支B5×5Conv(32)+BN+ReLU+2×2MaxPool64×64×32
MCNN分支C7×7Conv(32)+BN+ReLU+2×2MaxPool64×64×32
融合通道拼接+1×1Conv(64)64×64×64
池化频率维GlobalAveragePooling64×64
BiGRUBiGRU(128)256
分类Flatten+FC(4)+Softmax4

这张表可以直接对照你手边的代码检查维度有没有对不上。我在写结构时最常见的错误是漏掉BatchNorm层导致训练不稳定,以及拼接前三个分支的输出通道数和尺寸没核对清楚。建议每加一层都调用一次analyzeNetwork,Matlab会直接提示维度不匹配的位置。

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已经降到很低,验证集反而开始回升,说明网络开始背训练样本。我加了两道防线:全连接层前加Dropout,ratio设0.5;BiGRU输出后也加一个Dropout,ratio设0.3。配合早停机制,验证集准确率连续6轮不涨就停止训练。这套组合下来,验证集和测试集的准确率差距从原来的5个百分点缩小到0.5个百分点以内。

5. 实验结果复盘:99%准确率背后的真实对比

5.1 实验数据与三组对比结果

实验数据来自实验室台架采集的轴承振动信号,采样率25600Hz,包含正常、内圈故障、外圈故障、滚动体故障四种状态,每种状态采集40秒。测试时额外加入了一段其他负载工况下的数据做泛化验证。

我做了三组消融对比:输入表示层面比较STFT、CWT和WMSST;网络层面比较普通CNN、MCNN和MCNN-BiGRU;最后是WMSST+MCNN-BiGRU的完整方案。所有模型的训练集、验证集、测试集划分完全一致,保证对比公平。

方案测试集准确率验证集准确率平均F1分数
STFT + CNN91.2%92.0%0.908
CWT + CNN94.7%95.1%0.945
WMSST + CNN96.8%97.0%0.964
WMSST + MCNN97.5%97.7%0.972
WMSST + 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%。同样条件下,STFT+CNN方案在10dB时已经跌破80%,差距非常明显。

另一个泛化测试是把同一批故障类型迁移到不同转速条件下验证。转速从额定转速降低15%后,完整方案准确率仍保持95%以上,说明模型学到的不只是某个固定频率的位置,而是冲击间隔和调制特征。这部分能力一部分来自WMSST对瞬时频率的准确刻画,另一部分来自BiGRU对时间上下文的建模。

6. 最后聊几句我的实操体会

整套项目跑下来,我最大的体会是:不要把深度学习想象成万能拟合器,特征表示的质量永远决定系统性能上限。WMSST这个环节看起来只占整个流程很小一部分,但它直接决定了后续所有网络层能不能看到清晰的故障模式。我试过用普通CWT配合更强的MCNN-BiGRU,网络参数翻倍,准确率依然追不上WMSST配轻量网络,原因就是输入里的信息组织方式不一样。

另外,Matlab这套环境做故障诊断其实比Python顺手。信号处理工具箱里的CWT、滤波、重采样开箱即用,Deep Learning Toolbox的layerGraph也让多分支结构可视化程度很高。唯一别扭的是图像到序列的层连接,两阶段训练虽然多了一步,但调试起来每个环节都透明,对于工程项目反而更稳。

如果你正准备复现这个方案,我建议按这个顺序操作:先复现WMSST时频变换并可视化一组已知故障信号,确认脊线清晰;再训练一个单独的CNN验证特征是否能分类;最后再接BiGRU。不要一开始就追求端到端,一步一步来,中途出问题也更容易定位。

这套流程后续还有很多可以扩展的地方,比如用小样本学习解决数据不足、用迁移学习把实验室模型迁移到现场设备、把模型导出后部署到边缘设备上做实时诊断。我目前已经在一个轻量版上验证了实时性,单条2048点样本从加载信号到输出分类结果只需要不到20毫秒,完全满足在线监测的需求。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询