搞轴承故障诊断的朋友,十有八九都绕不过“振动信号怎么处理,故障特征怎么提,分类怎么分”这三件事。传统的时域指标、频域谱线分析做了很多年,但放到复杂的工业现场,设备转速波动、背景噪声干扰、多点位耦合振动一叠加,人工找特征越找越头疼。最近我把 VMD 和 CNN-BiLSTM 串成一条完整的诊断链路,在标准公开轴承数据集上跑了分类实验,效果比自己单独用 CNN 或者单独用 BiLSTM 都要稳。这套基于 VMD-CNN-BiLSTM 的组合方案,特别适合正在做故障诊断相关毕设或者想落地预测性维护的工程师参考,整套流程用 Matlab 就能实现,从数据读取到模型训练再到结果评估,不用去折腾跨语言的工程环境。
先把这个项目到底干了什么事讲清楚。VMD 负责把原始振动信号分解成若干个有物理含义的模态分量,相当于先把混在一起的信号“分拣”干净;CNN 负责从模态分量里扫出局部冲击模式;BiLSTM 再沿着时间维度把前后文关系串起来。很多朋友一听到“ VMD+CNN+BiLSTM ”就以为是三个模块无脑堆叠,其实每个模块都有明确分工,少了任何一环都会让最终的分类精度打折扣。接下里按我的实际踩坑经历,把原理、参数、代码和排错一次性说透。
1. 项目到底在做什么:VMD-CNN-BiLSTM 解决什么问题
1.1 轴承故障诊断的痛点
轴承振动信号本质上是一个强非平稳、强噪声背景下的调制信号。内圈故障、外圈故障、滚动体故障产生的冲击会周期性出现,但这些冲击往往淹没在齿轮啮合、轴系不对中、基础松动带来的低频大能量振动里。直接拿原始信号喂给神经网络,模型当然也能学,但噪声和干扰分量会占用大量网络容量,学到的特征不够干净,换一组工况数据就掉链子。
传统方案里,大家喜欢先算均方根值、峭度、峰值因子这些时域指标,再配合包络谱、倒频谱做频域分析,最后丢给 SVM 或者随机森林分类器。这套流程能用,但有两个硬伤:一个是特征工程严重依赖人的经验,换一台设备、换一种转速,好用的特征可能就失效了;另一个是轴承故障早期阶段信号能量很弱,手工特征根本分不开正常状态和轻微故障。
深度学习方案的好处是可以端到端地从数据里自动学特征,不用人肉设计指标。但轴承振动信号是高维时序数据,普通 CNN 在时序依赖建模上能力有限,普通 RNN 又有梯度消失问题,所以实际项目里很少有人直接用单一网络硬扛。这也正是我选择 VMD-CNN-BiLSTM 组合方案的根本原因。
1.2 三条技术线组合的逻辑
为什么是 VMD 而不是别的分解方法?轴承振动信号本质上是多个模态的叠加,VMD 能把信号自适应地分解成若干个有限带宽的本征模态函数(IMF),每个模态对应一个中心频率附近的振荡成分。分解完后,故障冲击所在的频带被单独分离出来,噪声被分摊到其他模态里,后续网络的输入信噪比就高了很多。
为什么要加 CNN?VMD 分解出的模态并不是“纯”故障信号,里面还残留着随机干扰。一维 CNN 本质上是一组可学习的滑动模板,能够在短时间窗口内捕捉冲击的局部形态。比如内圈故障冲击波形通常是一个陡峭的衰减振荡,CNN 的卷积核能自动学习到这个模板,然后通过池化层把位置微小的偏移容忍掉。这一步把“时域局部特征”提得非常稳健。
为什么要加 BiLSTM?轴承故障信号不是孤立的一个个冲击,冲击之间有固定的间隔,而且故障演化过程中前后状态是有关联的。BiLSTM 相当于同时从正向和反向读取整个序列,能捕捉到“前一个冲击来了之后,后续信号会怎么变化”这类长距离依赖关系。单纯用 CNN 时,卷积核感受野有限,很难覆盖多个冲击周期之间的关联;加了 BiLSTM 之后,时序建模能力补上了,分类准确率会有明显提升。
打个比方,VMD 是垃圾分类员,把可回收物、厨余垃圾、有害垃圾分开;CNN 是质检员,负责检查每一袋垃圾里有没有可疑的磨损碎片;BiLSTM 是调度员,根据垃圾连续出现的时间规律判断整个生产线是不是出了问题。三个角色缺一个,诊断链条都不完整。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. VMD 部分:信号分解的原理与参数选择
2.1 从 EMD 到 VMD:为什么不用传统分解
搞故障诊断的人最早接触的分解方法是 EMD,也就是经验模态分解。EMD 的思路是反复筛分,把信号逐层剥出本征模态函数。但它有两个很头疼的问题:一是模态混叠,同一个频率成分可能被拆到多个 IMF 里;二是端点效应,信号两端在包络拟合时会产生虚假振荡。
VMD 是另一种思路,它不搞递归筛分,而是把分解问题构造成一个变分问题:寻找 K 个模态函数,让所有模态的估计带宽之和最小,同时保证所有模态叠加起来能还原原始信号。求解过程用交替方向乘子法(ADMM),天然具备数学上的收敛保证,频带划分比 EMD 清晰得多。
在实际项目中,VMD 最直观的优势是能把“频带”切得很干净。比如说外圈故障特征频率大概在一两百赫兹附近,直接看原始频谱是看不出峰的,但 VMD 分解后,故障频带被单独拉成一个模态,整个包络谱就是一根很锋利的谱线。这个优势在信噪比低的早期故障样本上尤其重要。
2.2 VMD 关键参数怎么定
VMD 的主要参数有五个:模态个数 K、惩罚项系数 alpha、噪声容忍度 tau、直流分量标志 DC、初始中心频率方式 init。其中 K 和 alpha 对结果影响最大。
K 是模态个数,选小了会欠分解,多个频率成分挤在一个模态里,故障特征和强干扰混在一起;选大了会过分解,本来一个完好的模态被劈成两半,甚至把噪声单独分解成虚假模态。我惯用的做法是:先用 K=4 跑一遍,画出各模态的中心频率和频谱图,观察相邻模态的中心频率差;如果两个中心频率非常接近且波形轮廓相似,就减少 K;如果第一个模态里还能看到明显的故障冲击特征,就增加 K。
alpha 是带宽惩罚系数,它的作用类似于“松紧带”。alpha 越大,模态带宽越窄,频率分辨率越高,但容易把真实信号的频谱细节磨掉;alpha 越小,模态带宽越宽,容忍度更高,但可能把相邻频带的成分混进来。轴承振动信号我一般从 1000 到 3000 之间试,优先用 2000,观察分解结果再微调。
tau 是噪声容忍度,当信号本身噪声很大时,适当增大 tau 能让分解结果更稳健,但增大会引入更多噪声成分到模态里。init 一般用均匀初始化(1),DC 通常选 0,因为我们关心的是交流振动成分。
为了省事,我也见过有人用优化算法自动选 K 和 alpha,比如以包络谱峭度最大为目标做网格搜索。这个方法放在论文里很漂亮,但在工程现场,我更推荐先凭经验确定一个大致的范围,再用一次小批量实验定位最优参数,速度快且可控性强。
2.3 分解效果怎么看
参数调完之后,一定要多看几张图,不要只看一个数字指标。我自己的验收标准有三条:第一,各模态的中心频率在频谱图上有明显分离,不能出现两个峰挤在同一个频段;第二,故障特征频率所在的模态波形要能看出周期性冲击,而不是一片毛刺;第三,分解后所有模态之和能基本还原原始信号,残差很小。
举个例子,某组驱动端轴承信号的采样率是 12kHz,我取 K=6、alpha=2000 分解后,第一个模态对应低频轴转频及其谐波,第二到第四个模态对应不同的共振频带,第五六个模态包含高频噪声和残余冲击。故障冲击能量主要落在第三个模态里。后续送入 CNN-BiLSTM 的输入,就重点保留故障分量所在的模态,噪声为主的模态可以弱化或者丢弃,这样网络的学习负担小很多。
很多初学者问能不能不丢弃模态,把全部 K 个模态都堆成 K 通道输入给网络。这样也是可行的,网络有能力自动区分有用模态和噪声模态。但我个人建议还是做一点筛选,至少把明显是直流偏置和纯噪声的模态排除掉,输入维度越低,网络越小,训练越快,过拟合风险越低。
3. CNN-BiLSTM 部分:模型架构详解与训练策略
3.1 一维 CNN 到底在提取什么
很多人在故障诊断里用 CNN 都会纠结一个问题:到底用一维卷积还是二维卷积?我的观点很明确:如果输入是原始时域波形或者 VMD 分解后的时域模态,用一维卷积就够了。二维卷积通常需要用连续小波变换或短时傅里叶变换把信号转成时频图,虽然可视化效果好,但多了一步变换和处理,计算量大,而且对采样率和窗口长度很敏感。
一维 CNN 的核心操作是对序列做滑动卷积。一个卷积核可以看作一个可学习的局部模式模板,比如长度为 7 的卷积核,它会在信号上每 7 个点扫一次,计算两者的匹配程度。第一层卷积核学习的是简单的冲击形态,第二层卷积核就能组合多个简单形态形成更复杂的模式,比如“一个冲击后紧跟一段衰减振荡”。
池化层的作用是降维和抗偏移。轴承冲击发生的位置会有微小的相位偏移,池化取局部最大值后,这种偏移的影响被稀释了,模型对信号起点不敏感。这就好比验收产品时不是逐帧严格对齐,而是允许局部一定范围内的位移。
在设计 CNN 部分时,我有几个经验值:第一层卷积核大小取 5 到 7,偏大一点能覆盖完整的冲击波形;滤波器数量从 32 起步,每加深一层乘 2 到 64,太宽了训练慢,太窄了表达不足;池化层核大小取 2,步长 2,既降一半维度,又不至于丢失太多细节。卷积层和全连接层之间一定要加 BatchNorm 和 Dropout,否则深网络训起来稳定性很差。
3.2 BiLSTM 在时序建模中的不可替代性
VMD 加 CNN 已经能提取很好的局部特征了,为什么还要 BiLSTM?核心原因是轴承故障特征在时间轴上有明确的先后依赖。正常轴承的振动是平稳的随机过程,故障轴承的振动则是周期性的冲击串,这个周期性正是区分故障类型的重要线索。
普通 LSTM 只能从前往后读取数据,BiLSTM 则同时用正向和反向两个 LSTM 分支读取序列,最后把两个方向的输出拼接或加和。对于“冲击—衰减—沉寂—再冲击”这样的模式,正向分支能记住上一次冲击的特征,反向分支能利用后续信号对当前判断进行修正,双向信息综合下来,对周期性的建模能力比单向强很多。
在实际网络设计里,BiLSTM 的隐藏单元数我一般设为 64 到 128。太小了记不住长距离依赖,太大了参数爆炸,训练集小的话非常容易过拟合。另外要注意 OutputMode 的设置,如果是做整段序列的分类,通常用 'last' 只拿最后一个时间步的输出;如果后面还要接注意力机制或者逐点预测,才考虑用 'sequence'。
3.3 网络结构与训练参数参考
我在这套方案里用的网络结构大致如下:一个序列输入层,接两层一维卷积加池化,然后一个 BiLSTM 层,最后是全连接加 Softmax 分类层。分类数量根据具体任务定,比如只做四分类(正常、内圈、外圈、滚动体),或者更细一点把损伤尺寸也分出来做成十分类。
训练参数方面,优化器用 Adam,初始学习率 0.005 到 0.01,配合 piecewise 下降策略,每若干轮乘以 0.1。MiniBatchSize 常用 64,在显存不足时降到 32。MaxEpochs 设在 30 到 50 之间,配合早停策略防止过拟合。类别不均衡时,优先用加权交叉熵损失,而不是盲目降低分类阈值。
这里给一份我实际跑过的参考配置表:
| 参数 | 参考值 | 说明 |
|---|---|---|
| VMD 模态数 K | 4~8 | 观察中心频率分离度后确定 |
| VMD 惩罚系数 alpha | 1000~3000 | 一般从 2000 开始试 |
| 输入序列长度 | 1024~2048 | 至少覆盖 3 个冲击周期 |
| 一维卷积核大小 | 5~7 | 第一层偏大,后续偏小 |
| 卷积滤波器数 | 32~64 | 逐层倍增 |
| BiLSTM 隐藏单元 | 64~128 | 根据样本量调整 |
| Dropout 比率 | 0.2~0.4 | 放在 BiLSTM 之后 |
| 初始学习率 | 0.005~0.01 | 配合步进下降 |
| MiniBatchSize | 64 | 显存不足则降到 32 |
这套配置不是凭空拍脑袋定的,是在多组公开数据集上反复试验后的折中方案。样本量大、数据干净时,可以适当加大滤波器数;样本量少、噪声大时,建议减少隐藏单元并提高 Dropout。
4. Matlab 实操全流程:从原始数据到诊断结果
4.1 数据集加载与样本划分(特别强调不泄漏)
项目用的数据来自一个广泛使用的标准轴承故障公开数据集,文件按故障类型、损伤尺寸、负载条件分门别类存放。Matlab 里直接用 load 命令加载 mat 文件,取驱动端加速度信号(DE)这一通道作为分析对象,采样率通常是 12kHz。
加载完数据后,第一件事不是直接喂网络,而是先做滑窗截取。假设每条原始信号约 10 秒,12 万多个点,按 2048 点一个窗口、无重叠切割,能拿到约 60 个样本。一个故障类型下往往有好几条不同工况的记录,累计起来样本量并不大,所以很多论文里会采用重叠滑窗,步长 512 或 1024,把样本量扩充到几百甚至上千。
这里有一个特别容易翻车的地方:样本扩充之后,绝不能把所有窗口随机打乱再划分训练集和测试集。因为同一个原始记录里相邻窗口高度相关,重叠越多相关性越强,如果这些相似窗口被分到训练集和测试集两边,模型相当于提前偷看了答案,测试准确率会虚高,换到实际新数据上立刻露馅。正确做法是先按原始记录文件分组,把整段记录划分为训练段、验证段和测试段,然后再在各段内部滑窗切样本。我见过不止一个人在这个细节上翻车,审稿人或验收老师一眼就能看出来数据划分不合理。
4.2 VMD 分解与特征构造
Matlab 里运行 VMD 有两种常见方式:一是用网上流传的开源 VMD 函数脚本,调用格式类似:
matlab复制[u, u_hat, omega] = VMD(signal, alpha, tau, K, DC, init, tol);
二是自己写 ADMM 迭代求解,但没必要重复造轮子,开源脚本足够成熟。我用的是第一种,稳定且效率不错。
批量处理时,我会先对每个窗口信号单独做 VMD,然后把筛选后的模态堆叠成多通道序列。比如 K=6,选出其中 4 个有效模态,则每个样本变成一个 4×2048 的矩阵。在 Matlab 中,这就是一个 numFeatures=4、sequenceLength=2048 的序列样本。
要注意的是 VMD 分解对每个窗口单独做,参数一旦确定就不要随便改。我在初期调试时犯过一个错误,对前几百个样本用的 K=5,后面为了效果改成 K=6,结果所有样本都得重新分解,白白浪费了一晚上时间。所以正式跑之前,一定要用少量样本把参数彻底定下来,再批量处理全部数据。
分解完成后,可以做一步数据标准化。标准化的统计量要在训练集上计算,测试集沿用训练集的均值和标准差,千万不能对每个样本单独标准化,那样会破坏样本间的相对尺度信息。
4.3 模型定义与训练
模型定义用 Matlab Deep Learning Toolbox 的层数组写法,核心代码如下:
matlab复制numFeatures = 4; % VMD 筛选后的模态数
numClasses = 4; % 四分类任务
layers = [
sequenceInputLayer(numFeatures)
convolution1dLayer(7, 32, 'Padding', 'same')
batchNormalizationLayer
reluLayer
maxPooling1dLayer(2, 'Stride', 2)
convolution1dLayer(5, 64, 'Padding', 'same')
batchNormalizationLayer
reluLayer
maxPooling1dLayer(2, 'Stride', 2)
bilstmLayer(64, 'OutputMode', 'last')
dropoutLayer(0.3)
fullyConnectedLayer(numClasses)
softmaxLayer
classificationLayer
];
有些旧版 Matlab 不支持 maxPooling1dLayer,可以用 globalAveragePooling1dLayer 替代,作用类似,都是把序列维度降下来再接 BiLSTM。
训练选项的设置参考如下:
matlab复制options = trainingOptions('adam', ...
'MiniBatchSize', 64, ...
'MaxEpochs', 40, ...
'InitialLearnRate', 0.01, ...
'LearnRateSchedule', 'piecewise', ...
'LearnRateDropFactor', 0.1, ...
'LearnRateDropPeriod', 15, ...
'ValidationData', {valFeatures, valLabels}, ...
'ValidationFrequency', 20, ...
'Shuffle', 'every-epoch', ...
'Plots', 'training-progress', ...
'Verbose', true);
net = trainNetwork(trainFeatures, trainLabels, layers, options);
训练时如果你的机器有 NVIDIA 显卡,并且装好了 CUDA 支持,trainingOptions 里默认就会用 GPU,速度比 CPU 快一个量级。没有 GPU 也别慌,把 MiniBatchSize 调到 32,MaxEpochs 降到 20,耐心等也能出结果,就是 BiLSTM 那一步比较吃时间。
4.4 评估与可视化
模型训练完,最直观的评估是混淆矩阵。Matlab 里可以用 confusionchart 直接出图,代码很简单:
matlab复制ypred = classify(net, testFeatures);
figure;
confusionchart(testLabels, ypred);
除了总体准确率,还要看每一类的召回率。轴承故障诊断中最怕的是把“内圈故障”误判成“外圈故障”,这在实际检修里会导致完全相反的拆卸方案。如果混淆矩阵中某两类经常混在一起,就需要回看 VMD 参数,确认这两个故障类型的特征频带是不是被拆到了同一个模态里。
我还会用 t-SNE 对网络倒数第二层的特征做降维可视化。如果同一类样本在二维图里聚成紧凑的一团,不同类之间界限清晰,说明网络提取的特征是可分的;如果各类特征完全重叠,哪怕训练集准确率再高,也要怀疑模型过拟合了。Matlab 里可以用 tsne 函数,输入特征向量输出二维坐标,画散点图非常方便。
另外一定要保存训练好的模型和参数记录,方便复现和继续调参。Matlab 里直接 save('trainedNet.mat', 'net') 就行,不要每次重新训练,太费时间。
5. 常见问题排查与避坑实录
5.1 和数据划分有关的坑
我前面提过按原始记录分组划分,但还有一个更隐蔽的坑:滑窗时用了重叠,但划分时把相邻窗口切到了训练和测试两侧。即使你按文件分组,文件内部依然有大量重叠窗口相邻问题,正确做法是先按文件切段,再在段内滑窗,并且保证训练段、验证段、测试段之间至少有 1 秒以上的间隔。
另一个常见坑是样本不平衡。公开数据集中,正常样本往往远多于各种故障样本,如果直接把全部样本喂进去,模型会倾向把所有样本都判成正常类。我一般会统计每个类别的样本数,然后用 loss 层的类权重来加权,或者对少数类做重采样,保证每个 batch 里各类比例相对均匀。
5.2 和 VMD 有关的坑
K 值不合适的现象很有辨识度:如果训练出来准确率很高,但测试泛化差,别急着怀疑网络,先回看 VMD 分解是否过拟合了噪声。K 过大的时候,VMD 会把噪声也当作独立的模态,网络学到的是噪声的随机模式,而不是故障的本质特征。这时候把 K 调小 1 到 2,重新分解再训一遍,效果往往立竿见影。
alpha 太小时,模态带宽过大,相邻模态会相互混叠,输入到网络的多通道之间存在明显的冗余信息;alpha 太大时,模态过于窄,真实冲击的能量可能被割裂到多个小频带里,导致没有某个单独模态能承载完整的故障特征。判断方法很简单,对比原始信号和分解后模态累加还原的波形,残差越大说明分解参数越不合理。
5.3 和训练有关的坑
BiLSTM 训练不稳定是高频问题,典型表现是 loss 在一轮内剧烈震荡,或者到某一步突然变成 NaN。常规排查顺序是:先降低学习率到 0.001,再检查输入是否有 NaN 或 Inf,最后确认标签是否从 1 开始连续编号。如果都没问题,就给 trainingOptions 增加 'GradientThreshold', 1,限制梯度范数,能解决大部分梯度爆炸问题。
过拟合则是另一个高频问题。VMD-CNN-BiLSTM 组合模型参数多,而轴承数据集即使扩充后样本量也只是几千到一万级别,很容易出现过拟合。我的经验是 Dropout 一定要放在 BiLSTM 输出之后,而不是只在最后一个全连接层之前;另外早停策略很关键,用验证集准确率作为监控指标,连续若干轮不提升就停止训练并恢复最佳模型。
这里整理一份我实测过的排查速查表,遇到问题可以直接对照:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 训练和测试准确率差距过大 | 数据划分泄漏 | 按原始记录段预留测试集 |
| 各类别准确率严重不均衡 | 样本不平衡 | 加权损失或重采样 |
| 训练 loss 震荡或 NaN | 学习率过大或梯度爆炸 | 降学习率,设梯度阈值 |
| 测试集准确率低于 90% | VMD 模态数不匹配 | 调整 K 和 alpha,观察频谱 |
| 混淆矩阵相邻类别混叠 | 故障频带特征被拆散 | 检查 IMF 频谱中心频率分离度 |
| 模型推理时间过长 | BiLSTM 隐藏单元过多 | 减到 32 或 64 |
| 更换工况后性能骤降 | 单工况过拟合 | 增加多工况混合训练数据 |
换工况掉点这件事,我多说一句。轴承故障诊断最终要落地到现场,现场转速、负载和采集系统可能跟公开数据集完全不同。如果只在一个工况的数据上训练,模型基本只能记住那个工况的模式。我后来把不同负载条件下的样本全部混进来训练,模型的跨工况泛化能力明显提升,代价是训练时间稍长。如果条件允许,建议在训练集里刻意混入不同转速和负载下的数据,这是提升模型实用价值性价比最高的一招。
最后再分享一个个人习惯。每次调完参数我都会把分解图、训练曲线、混淆矩阵和模型参数存成一个带日期的文件夹,方便回溯。有一次我调好一组 VMD 参数后只记了 K 和 alpha,隔了两周想复现当时的分解结果,死活找不到具体配置,只能从头再试。从那以后,我所有的实验记录都强制写成一张参数表,包括窗口长度、重叠步长、标准化统计量、VMD 参数、网络结构和训练选项。故障诊断项目最大的成本不在跑模型,而在调参和排错,把这些过程记录下来,后续不管是写论文还是接新项目,都能少走很多弯路。
