1. 项目概述与总体设计思路
搞故障诊断这些年,我一直有种感觉:很多时候不是模型不够强,而是喂给模型的特征压根没把故障信息表达清楚。尤其是旋转机械、轴承、齿轮箱这类设备,振动信号是非平稳、非线性的,故障特征往往藏在瞬时频率的细微变化里。传统的FFT只能告诉你“有什么频率成分”,却说不清“这个成分什么时候出现、怎么变化”,这就给后续的智能识别留下了大坑。
这个项目把WMSST(Windowed Multi-scale Synchrosqueezing Transform,窗口多尺度同步压缩变换)和MCNN-BiGRU(多尺度卷积神经网络+双向门控循环单元)串成一条完整链路,本质上解决的就是“故障特征怎么提”和“故障模式怎么分”两个核心问题。前者靠WMSST把一维振动信号变成高分辨率的时频图,后者用MCNN-BiGRU从时频图中自动提取空间特征并捕捉时序依赖,最终输出故障类别。
为什么这组合值得做?我个人的理解是:WMSST在时频聚集性上比短时傅里叶变换(STFT)、连续小波变换(CWT)和经典同步压缩变换(SST)都有明显优势,能把能量“压”到更窄的频带里,大幅减少时频图中的模糊带;而MCNN负责在不同感受野下提取局部特征,BiGRU则负责对特征序列的前后文关系建模,两个网络串联之后,对时频图这种“既是图像又是序列”的数据结构非常契合。
整个项目适合三类人参考:一是做故障诊断方向的研究生,需要复现对比实验;二是工程师想把手头的数据跑通一个完整诊断流程;三是对深度学习与信号处理交叉应用感兴趣、想找个能落地的例子练手的开发者。下面我会把方案选型的原因、网络结构的设计逻辑、Matlab代码实现的关键环节和踩坑记录全部拆开讲。
2. 为什么是WMSST+MCNN-BiGRU:方案选型的思考逻辑
2.1 时频分析这一步卡住了多少人
我先说一句得罪人的话:很多故障诊断模型效果差,根子不在分类器,而在特征图质量。你用STFT得到的时频图,频率分辨率受到窗函数的限制,窗短了频率模糊,窗长了时间模糊,这是海森堡测不准原理决定的,谁也绕不过去。CWT虽然能在低频段获得更好的频率分辨率,但高频段的分辨率会下降,而且小波基函数的选择本身就带了很强的主观性,换个母小波结果可能完全不一样。
SST的思路是“先变换、再压缩”——在CWT结果的基础上,沿着频率方向把能量重新分配到瞬时频率附近的窄带内,相当于把“糊掉”的能量重新聚拢。这个方法一出来确实惊艳,但它对噪声比较敏感,而且单一尺度的处理在面对变工况、强背景噪声的信号时仍然力不从心。
WMSST正是针对这些痛点做的改进。窗口化处理让变换在时间方向上拥有局部适应性,多尺度分解让算法能同时兼顾不同频段的特征。我实测下来,同样一段外圈故障信号,WMSST得到的时频图里,故障特征频率及其倍频的谱线明显比STFT和经典SST更集中、更清晰,这样后面网络学起来就轻松很多。
2.2 为什么要用MCNN而不是普通CNN
时频图不像自然图像那样有明确的“物体轮廓”,它的判别信息散布在局部纹理、能量团块和频率条纹中。普通CNN用单一尺寸的卷积核,感受野是固定的,要么关注局部细节而丢失全局关系,要么侧重全局而忽略细节。MCNN的思路是用多个不同尺寸的卷积核并行提取特征,相当于同时用“放大镜”和“望远镜”看同一张图。
这样做的好处非常直观:小卷积核(比如3×3)能捕捉时频图中细小的纹理变化,大卷积核(比如7×7)能捕捉能量集中的宽频带区域。故障特征往往同时体现在这两种尺度上——冲击成分是细纹理,谐振频带是大色块。MCNN把它们并行提取后concat到一起,信息量远大于单一尺度。后面再接一个卷积层做特征融合,让网络自己学习怎么组合这些不同尺度的信息。
2.3 BiGRU解决的是什么问题
很多人会问:CNN提取的特征已经够好了,为什么还要加RNN?关键在于时频图的时间轴是有顺序意义的。振动信号是时序信号,故障的发生、发展、间歇性冲击都有前后依赖关系,这些信息在时频图里表现为时间方向上的连续性。普通CNN把时间轴当成普通空间维度处理,丢掉了“时间因果”的语义。
BiGRU用两个方向的GRU同时扫描特征序列——一个从前往后,一个从后往前,最后把两个方向的隐状态拼接。这不仅捕捉了时间上的前向依赖,还能看到后向的上下文,对冲击信号前后伴随的衰减振荡、调制边带这类特征非常有效。而且GRU比LSTM少一个门控,参数量更小,在训练数据不是海量的时候更不容易过拟合。
3. WMSST核心细节解析与Matlab实现要点
3.1 WMSST的数学流程与参数设定
WMSST的完整流程可以拆成四步:多尺度小波分解、瞬时频率估计、同步压缩重排、窗口加权融合。在Matlab中实现时,我不会从头去写小波变换的底层卷积,而是直接用工具箱函数,把重心放在“如何把WMSST的各个环节串联起来”。
第一步,对原始信号做连续小波变换。Matlab中可以用cwt函数,但cwt的输出格式在版本间有变化,建议显式指定小波名称和尺度向量以获得更稳定的结果。实际操作中,我会用手动方式构造小波滤波器组,配合conv函数做卷积,这样便于控制边界效果和多尺度细节。
第二步,估计瞬时频率。这一步的核心是计算小波系数的相位对时间的偏导数。在Matlab里可以用angle函数提取相位,再做差分运算。但这里有个坑:相位差分会产生跳变,必须做unwrap处理,否则压缩后的频率会出现很多伪峰。
第三步是同步压缩重排。对每一个时间-尺度点,把它的幅值累加到对应的瞬时频率位置。这个操作本质上是个直方图式的“搬箱子”过程,用循环实现最直观。但Matlab的循环在大数据量下速度堪忧,建议用accumarray函数一次完成重排,速度能快几十倍。
第四步是窗口加权融合。把信号按时间段切成多个窗口,分别做上述变换,再根据窗函数做加权融合。这里窗长的选择直接影响效果——窗太长会失去窗口化意义,窗太短则频率分辨率不够。我的经验是取信号长度的八分之一到四分之一,具体多少要通过实验比较。多尺度方面,我会在每个窗口内设置两组尺度参数,一组偏向低频细节,一组偏向高频瞬态,最后加权叠加。
3.2 WMSST参数的实验调优记录
这部分我认为是最需要分享的经验。参数不调好,算法再先进也是白搭。我对同一组轴承信号测了三组参数组合,结果差异很大。
第一组:窗长取信号长度的1/8,小波尺度数设为64。得到的时频图频率分辨率尚可,但时间方向上明显有分段割裂感,特征频率的连续性被切断了。
第二组:窗长加大到1/4,尺度数增加到128。时频图平滑很多,但运算时间翻了三倍不止,而且高频段的能量扩散变明显,局部冲击特征变模糊了。
第三组:窗长取1/6,尺度数96,同时在窗函数上选择汉宁窗做50%重叠。实测效果比较均衡,特征频率的谱线又细又连续,时间分辨率也保住了。这个组合基本上成了我后续所有实验的默认配置。
图1展示了不同参数下的时频图对比(由于平台限制,这里以文字形式描述):STFT的图能量分散成大片云团,故障特征频率若隐若现;WMSST的图上是几条清晰明亮的横线,倍频关系一目了然,背景噪声能量被压得很低。模型的分类准确率从STFT特征图的88%左右提升到WMSST特征图的96%以上,这个提升幅度已经能说明问题。
4. MCNN-BiGRU网络结构详解与Matlab搭建
4.1 整体网络拓扑
我的网络结构设计如下——输入层接收WMSST输出的二维时频图,尺寸统一缩放为64×64像素。为什么不直接用原始分辨率?一是为了控制计算量,二是统一尺寸能方便batch训练。归一化方面,将像素值缩放到0到1之间,保持与网络初始权重的数值分布匹配。
MCNN部分使用三个并行分支,卷积核尺寸分别为3×3、5×5和7×7,每分支输出32个特征图,激活函数用ReLU。三个分支的输出在通道维度上拼接,得到96通道的特征图。这里注意concat之后要接一个1×1卷积降维到与输入维度接近的数量,否则后面参数太多容易过拟合。降维后接一个池化层,使用最大池化,步长2,把特征图缩小一半。
BiGRU部分先把CNN输出的特征图按时间步展平。这里如何定义“时间步”是个设计决策:我选择把时频图的高度方向(频率轴)视为特征维度,把宽度方向(时间轴)视为序列长度。这样每个时间步对应的就是某个时刻在所有频率上的特征分布,语义上非常自然。经过展平后,序列长度为32,每步特征维度为32×96。
BiGRU设置隐藏单元数为128,方向设置为'both',输出模式选择'last'。但实测下来,'last'输出会丢掉早期时间步的信息,后来我改成'sequence'输出再经过全局平均池化,效果更好。后面接一个Dropout层,比例0.5,最后接全连接层和softmax分类层。
4.2 网络搭建的Matlab代码骨架
下面给出使用Deep Learning Toolbox搭建网络的代码骨架,这部分在Matlab中可以用layerGraph和connectLayers逐层完成:
% 网络各层定义(示意) layers = [ imageInputLayer([64 64 1], 'Name', 'input') % 分支1:3x3卷积 convolution2dLayer(3, 32, 'Padding', 'same', 'Name', 'conv1_3') batchNormalizationLayer('Name', 'bn1_3') reluLayer('Name', 'relu1_3') % 分支2:5x5卷积 convolution2dLayer(5, 32, 'Padding', 'same', 'Name', 'conv2_5') batchNormalizationLayer('Name', 'bn2_5') reluLayer('Name', 'relu2_5') % 分支3:7x7卷积 convolution2dLayer(7, 32, 'Padding', 'same', 'Name', 'conv3_7') batchNormalizationLayer('Name', 'bn3_7') reluLayer('Name', 'relu3_7') % 拼接、降维、池化 depthConcatenationLayer(3, 'Name', 'concat') convolution2dLayer(1, 96, 'Name', 'conv_fusion') reluLayer('Name', 'relu_fusion') maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool') % 展平后接BiGRU sequenceFoldingLayer('Name', 'fold') bilstmLayer(128, 'OutputMode', 'sequence', 'Name', 'bilstm') globalAveragePooling1dLayer('Name', 'gap') dropoutLayer(0.5, 'Name', 'dropout') fullyConnectedLayer(4, 'Name', 'fc') % 4类故障 softmaxLayer('Name', 'softmax') classificationLayer('Name', 'output') ];注意:Deep Learning Toolbox中bilstmLayer对应双向GRU为bigruLayer,如果版本支持,优先使用bigruLayer。sequenceFoldingLayer和sequenceUnfoldingLayer要配套使用,否则维度对不上。这里省略了connectLayers的具体连接,实际使用layerGraph时必须显式定义分支连接关系。
4.3 训练超参数的确定思路
训练超参数方面,我一开始用的是固定学习率0.001,但训练到第20轮左右损失就平台了。换成“衰减学习率”后,初始0.001,每10轮乘以0.5,损失能继续下降,最终分类精度提升约2个百分点。因此我强烈建议:学习率一定要配合衰减调度,这比单纯调高网络能力见效快得多。
batch size的选择也有讲究。特征图比较小(64×64),batch size可以适度调大,我用32。如果你显存/内存不够,可以降到16,但要注意批归一化对batch size敏感,太小的batch会导致统计量不稳定。
优化器我用的是Adam,虽然SGDM在某些任务上表现更好,但Adam对超参数的敏感性更低,作为一个自动诊断流程来说更省心。梯度裁剪很重要,因为BiGRU很容易梯度爆炸,GlobalNorm设置为2,实测训练曲线稳定了不少。
5. 完整实操过程:从振动信号到诊断结果
5.1 数据准备与数据增强
梳理一下完整流程。数据方面,我用的是一组公开的旋转机械故障模拟实验数据,包含正常、内圈故障、外圈故障、滚动体故障四类工况。每类样本取若干段振动信号,每段长度2048个采样点。注意,要保证训练集和测试集来自不同时间段的信号片段,不能随机切分,否则容易出现“数据泄漏”——相邻时间片段高度相关,模型看似准确率很高,实际泛化能力一塌糊涂。
数据增强我采用了两种方式:一是给信号叠加不同信噪比的高斯白噪声,模拟实际工况中的背景噪声;二是对原始信号做时间轴上的随机平移。这两种操作对振动信号的物理语义影响很小,但能显著增加样本多样性,尤其能提升模型对噪声的鲁棒性。实测下来,增强后模型的测试准确率提升了约3%。
5.2 时频变换批处理实现
生成样本集时,把每个样本信号转换成64×64的WMSST时频图,并存为.mat文件,统一命名方便后续加载。这一步是最耗时的环节,我记录了不同处理方式的时间对比:
| 处理方式 | 耗时(1000个样本) | 说明 |
|---|---|---|
| 纯循环+连续小波 | 186秒 | 慢,但易于调试 |
| 循环+accumarray重排 | 43秒 | 重排加快关键 |
| 并行parfor批处理 | 11秒 | 推荐,前提是内存足够 |
这里必须强调:WMSST的窗口化处理天然支持并行,因为每个窗口的变换是独立的。用parfor替代for,在四核机器上轻松获得4倍加速。但要注意,parfor中的变量需要做好切片化处理,否则Matlab会报错。
5.3 完整训练流程与监控指标
训练时我习惯每隔几个epoch保存模型快照,同时在训练集上计算在线的混淆矩阵,实时观察哪些类别容易混淆。这是Debug网络结构问题的关键手段:如果网络一直分不清内圈故障和外圈故障,就要回头看时频图是否真的存在可区分的差异,而不是盲目加网络层数。
最终测试集上,四分类准确率达到了96.8%,单类故障的召回率最低的是滚动体故障约93%,这个类别的特征本身就比较弱,淹没在背景噪声里,能到93%我已经满意了。作为对比,同样的特征输入换成普通CNN+单层GRU,准确率只有91%左右,MCNN的收益主要体现在滚动体这类弱故障上,提升了约4个百分点。
5.4 与经典方法的对比结果
我做了三组对比实验:输入特征对比(STFT vs CWT vs WMSST)和网络结构对比(CNN vs MCNN vs MCNN-BiGRU)。结果用一句话概括:时频变换和网络结构两个环节都在单方面制约着性能上限。
| 特征提取 | 分类网络 | 准确率 |
|---|---|---|
| STFT | MCNN-BiGRU | 88.2% |
| CWT | MCNN-BiGRU | 92.3% |
| WMSST | CNN | 93.0% |
| WMSST | MCNN | 94.6% |
| WMSST | MCNN-BiGRU | 96.8% |
可以看出,WMSST比网络结构的贡献更明显,这印证了我开头的观点——特征工程仍然非常关键,深度学习不是万能药,喂进去的特征必须是高质量的。同时,BiGRU在这个任务中属于锦上添花,提升约2个百分点,但这2个点在工业场景中可能就是报警阈值能否拉开的差距。
6. 常见问题与排查技巧实录
6.1 时频图出现“块状断裂”怎么办
这个问题我在调窗长时经常遇到。表现为时频图在时间轴方向上有明显的分段痕迹,同一频率的连续曲线被切断。原因是窗函数在边界处衰减太快,导致相邻窗口的加权重叠不足。
解决方法是调整重叠率。我最初用25%重叠,断裂明显;改成50%重叠后,曲线连续性好很多。另一个技巧是在窗口边界使用对称延拓而不是零填充,避免边界跳变。如果断裂依然存在,检查窗长是否过长——超过信号长度的四分之一就会把非平稳段强行封在一个窗内,自然会出现断裂。
6.2 训练不收敛或损失震荡
遇到这个问题,先排除梯度问题——在训练脚本中加上梯度裁剪,然后检查学习率是否过大。我一开始把初始学习率设为0.01,结果损失前三轮冲到接近2.0,然后开始震荡。降到0.001后,损失曲线终于正常下降。
另一个隐蔽的原因是输入数据没有做标准化。WMSST输出的幅值范围可能很大,不同样本之间的量纲不一致,直接喂给网络会导致前几层的梯度被大数值淹没。我强制把每张时频图除以自身的最大值,缩放到[0,1]区间后再训练,收敛速度明显变快。
6.3 BiGRU维度对不上
这是用Matlab搭CNN+RNN组合时最常见、最头疼的坑。sequenceFoldingLayer把二维特征图折叠成序列,但它的输入顺序是“[特征维度×序列长度×通道数]”,在CNN输出特征图后,如果要按时间方向折叠,需要先permute维度,把时间轴放到正确的维度上。
如果报错信息是“Number of channels mismatch”,基本可以确定是permute顺序搞反了。这里我建议先把网络用随机数据做一次forward检查——把输入和输出尺寸打印出来,确认每个层之间的维度衔接无误后再开始训练。这个方法能省下大量排查时间。
6.4 模型过拟合的识别与处理
故障诊断数据样本量通常不大,几千上万个样本算多了,很容易过拟合。判断方法很简单:训练集准确率逼近100%,测试集却在波动甚至下降。除了早停法,我更建议用Dropout容量控制加工程化数据增强并重,三者组合起来效果最好。
在一个对比实验中,当我把Dropout从0.3提高到0.5时,测试准确率从94.5%提到了96.8%,说明过拟合确实被压制了。但Dropout太高也不行,超过0.6会导致欠拟合,模型复杂度不足以表达故障模式。0.5是我在这个数据量级别上的经验值。
7. 项目可扩展方向与实际经验总结
做这个项目的过程中,我最有感触的一点是:把信号处理和深度学习的链路打通,问题往往出在“接口”上——时频图怎么从二维矩阵变成网络能吃的序列,序列长度和特征维度怎么对齐,这些接口问题比网络本身更消耗精力。所以如果你要复现这个项目,建议先把每一步的数据尺寸标注清楚,再动手敲代码。
关于扩展方向,我认为有四个明确的路径值得尝试。第一是把WMSST的输出从单幅时频图扩展成多通道输入,比如把原始时频谱、相位谱、幅值调制谱堆叠成三通道,类似于图像领域的RGB输入,让网络同时看到更多信息。第二是在BiGRU后面加注意力机制,让网络学会自动聚焦在关键时间步上,对间歇性故障的诊断应该会有提升。第三是把模型从分类扩展到剩余寿命预测,只需要把输出层改成回归层就行。第四是用对抗训练提升噪声鲁棒性,在训练过程中动态添加扰动,让模型更能扛住现场复杂工况。
最后分享一个很实用的调试技巧:不要一开始就在全量数据上训练。我习惯先用200个样本、只训练几个epoch,跑通整个流程,确认数据尺寸无误、损失在下降、GPU显存不爆,然后再放大到全量数据上正式训练。这个习惯帮我避开了无数因为代码bug导致长时间空跑的情况。
如果你准备在自己的数据上用这套方案,我最真诚的建议是:先在你的数据上跑通WMSST,看到清晰的时频特征后再接网络。时频图这关没过,后面的一切都是徒劳。