基于DWVD与MCNN的旋转机械故障诊断方法与实践
1. 故障诊断这条路为什么我最后选了 DWVD MCNN搞旋转机械故障诊断这几年我试过不少方案。早期做频谱分析拿 FFT 看幅值谱简单是简单但遇到变转速、冲击性强、信噪比低的工况频谱图上一片糊特征频率根本找不准。后来转向时频分析小波变换、经验模态分解都折腾过一阵各有各的毛病——小波基函数选起来看缘分EMD 模态混叠折腾得人头疼。直到把离散韦格纳分布DWVD和多尺度卷积神经网络MCNN搭到一起才算是找到一条效果稳定、流程清晰、代码也好复现的技术路线。这套 DVMCNN 方案解决的核心问题说白了就是两件事一是怎么把一维振动信号里藏着的故障特征干净地“捞”出来二是怎么让网络不用靠人工调特征就能自动把不同故障分得明明白白。DWVD 负责前者它的时频聚集性比短时傅里叶变换强不少能把信号的能量在时频平面上怼成一条细线故障特征一眼可见MCNN 负责后者用多尺度卷积核并行提取不同粗细的特征比单一尺寸卷积核更能适配复杂振动信号里“大趋势里有小毛刺”这种特点。这篇文章适合谁看正在做故障诊断课题的学生、刚接触时频分析加深度学习的工程师以及想在 MATLAB 里完整跑通“信号处理 网络训练 识别评估”这条链路的人。我会把 DWVD 的原理、交叉项抑制办法、MCNN 结构设计逻辑、完整 MATLAB 实现流程和调试中踩过的坑一次说清楚。代码部分我会挑核心段落贴出来解释完整的工程文件可以按文中思路自行搭建几分钟就能跑起来。2. 为什么是 DWVD时频分析方案选型的逻辑2.1 从 STFT 到 WVD差的不是一点半点先说一个基本问题滚动轴承或齿轮箱的故障信号本质上是非平稳信号。故障引起的冲击会调制出周期性脉冲这些脉冲在频谱上表现为以某个共振峰为中心、以故障特征频率为间隔的边带族。如果只用传统频谱分析这些边带容易被噪声淹没而且变转速工况下频谱是“糊”的特征会平移、变形没法直接对比。时频分析的意义就在于把时间、频率两个维度同时展开。短时傅里叶变换STFT是加窗的 FFT窗长定了时间分辨率和频率分辨率就互相掐架——窗短了频率看不清窗长了时间定位不准。小波变换虽然能自适应分辨率但小波基的选择没有统一标准换了数据就得重新试。Wigner-Ville 分布走的是另一条路。它不切窗而是把信号和信号的延迟副本做相关运算再对延迟做傅里叶变换。数学形式是[ WVD(t,f) \int x(t\tau/2) x^*(t-\tau/2) e^{-j2\pi f\tau} d\tau ]这个分布的理论时频聚集性极佳对单分量线性调频信号它能给出理想的“能量脊线”这是 STFT 和小波变换都做不到的。理论上讲它的时频分辨率不受不确定性原理约束这也是它被称为“高分辨率时频表示”的原因。2.2 DWVD 的实际计算过程离散化之后输入信号为 x[n]长度 N离散韦格纳分布在 MATLAB 里的核心计算可以写成function [tfr, f] myDWVD(x, fs) N length(x); if mod(N, 2) ~ 0 x x(1:end-1); N length(x); end tfr zeros(N, N); t (0:N-1) / fs; f (0:N/2-1) * fs / N; for n 1:N tau_max min([n-1, N-n, round(N/2)-1]); tau -tau_max:tau_max; x_shift1 zeros(1, length(tau)); x_shift2 zeros(1, length(tau)); for k 1:length(tau) if (n tau(k)) 1 (n tau(k)) N x_shift1(k) x(n tau(k)); end if (n - tau(k)) 1 (n - tau(k)) N x_shift2(k) x(n - tau(k)); end end R x_shift1 .* conj(x_shift2); tfr(n, :) abs(fft(R, N)) .^ 2; end tfr tfr(:, 1:N/2); end别急着复制去跑这个版本是裸的 WVD双分量信号跑出来交叉项能盖过真实特征。我当初第一次跑出这种图的时候满屏幕都是震荡条纹真实故障频率完全看不清整个人是崩溃的。2.3 交叉项问题与核函数抑制交叉项是 WVD 的固有缺陷。当信号包含两个分量时WVD 会在两个分量之间的时频位置产生振荡的伪能量幅度甚至能超过真实分量。生活化类比就是双人合唱时录音里除了两个人的歌声还有一个“第三个人”的混响声音而这个混响不是任何人唱出来的。抑制交叉项的标准做法是在模糊域乘核函数也就是把 WVD 变成 Cohen 类分布的一种。我实际用的是平滑伪 WVDSPWVD通过时域窗和频域窗同时平滑来压制交叉项。MATLAB 里有现成函数可以做时频分析不过我习惯自己控制核函数的参数因为默认参数不一定适合自己的信号。关键参数就这么几个参数作用我的取值调整经验时域平滑窗长度控制时间维度的平滑程度5~11越长交叉项越弱但时间分辨率下降频域平滑窗长度控制频率维度的平滑程度5~9过长会把真实窄带特征抹掉信号长度 N时频图尺寸512 或 1024建议 2 的幂次后续送 CNN 也方便我的经验是先拿一段已知故障类型的数据跑一遍把交叉项压下去但不明显损失主频带的锐度然后固定参数整个数据集统一用这套参数免得每个样本单独调参导致特征分布不一致。DWVD 的输出是一张二维时频图横轴是时间纵轴是频率像素亮度代表能量强度。这张图就是后面 MCNN 的输入。所以时频变换这一步做得好不好直接决定网络的天花板——输入已经糊了网络再强也白搭。3. MCNN 多尺度卷积网络的结构设计逻辑3.1 为什么单尺度 CNN 不够用故障振动信号的时频图有个特点不同故障类型对应的特征尺度差异很大。比如滚动体故障的冲击特征比较窄、局部性强在时频图上表现为细碎的亮点簇而保持架故障的特征往往更宽表现为大范围的能量团块。如果只用单一尺寸的卷积核要么捕捉局部细节但丢了全局上下文要么看到全局轮廓但漏了细小的冲击特征。标准 CNN 靠堆层数来扩大感受野但层数越深参数越多训练数据不够就容易过拟合。而且深层网络的梯度传播路径长小数据集下训练不稳定。多尺度卷积网络解决这个问题的思路更直接在同一层里并行使用不同尺寸的卷积核各看各的尺度最后把特征拼起来。3.2 网络结构具体设计我用的 MCNN 结构是这样的层类型具体配置输出尺寸输入层64×64×1归一化后的时频图64×64×1多尺度卷积块11×1 卷积32通道3×3 卷积32通道5×5 卷积32通道并行64×64×96BatchNorm ReLU逐通道归一化 激活64×64×96最大池化2×2步长232×32×96多尺度卷积块21×1 卷积64通道3×3 卷积64通道5×5 卷积64通道并行32×32×192BatchNorm ReLU同上32×32×192全局平均池化每通道取平均1×1×192全连接层192 → 6464Dropout概率0.564输出层Softmax类别数 故障类型数类别数个这里每个多尺度卷积块里的三个卷积核是并行关系输出在通道维度上直接 concatenate。1×1 卷积负责逐像素的特征变换和降维3×3 是标准感受野5×5 提供更大的上下文范围。三个尺度互补比单独用 3×3 堆叠的效果明显好。3.3 参数选择的理由和教训通道数这块我一开始为了追求效果把第一层通道设成 64/64/64结果训练集只有几千张图网络直接过拟合训练准确率 100%验证集只有 80%。后来把通道数降到 32/32/32配合 Dropout 和数据增强验证集反而上去了。这个教训说明小数据集上网络容量不是越大越好够用就行。输入尺寸我选定 64×64而不是直接把原始时频图全尺寸塞进去。原因有二一是 DWVD 输出矩阵是 N×N/2N 取 512 时就是 512×256直接送网络参数太多二是 64×64 已经能保留足够分辨率的故障特征计算量小好几个量级。这里需要说明输入尺寸的确定是基于我自己的实验数据特性做的常见选择不同数据集可能需要微调但 64×64 是个性价比很高的起点。4. 完整工程流程从原始信号到诊断结果4.1 数据准备与预处理我手里的数据是轴承故障模拟实验台采集的振动信号采样率 12kHz包含正常、内圈故障、外圈故障、滚动体故障四种状态。原始数据是连续的长信号不能直接丢给网络要切样本。我的切法是这样fs 12000; sampleLen 1024; % 每个样本点数 stepLen 512; % 重叠步长 samples {}; labels {}; for i 1:numel(allSignals) sig allSignals{i}; label labelsRaw{i}; numSamples floor((length(sig) - sampleLen) / stepLen) 1; for j 1:numSamples startIdx (j-1) * stepLen 1; samples{end1} sig(startIdx:startIdxsampleLen-1); labels{end1} label; end end重叠 50% 是为了数据增强样本量直接翻倍。对于深度学习来说样本量永远是多多益善只要保证训练集和测试集来自不同时间段的数据重叠取样不会造成信息泄漏。每个样本长度 1024 点在 12kHz 采样率下大约是 85ms对于轴承故障特征频率通常在几十到几百赫兹来说足够包含多个完整故障周期。4.2 DWVD 时频图批量生成与预处理对每个样本做 DWVD得到的是一个二维复数矩阵取模之后是能量分布。这一步我单独写了个批处理脚本一次性把全数据集转成图像并保存为 .mat 文件避免每次训练都重算一遍时频变换。for i 1:numel(samples) x samples{i}; [tfr, ~] myDWVD(x, fs); tfrImg mat2gray(abs(tfr)); % 归一化到 [0,1] tfrImg imresize(tfrImg, [64, 64]); % 统一尺寸 imgs(:,:,1,i) tfrImg; end注意 mat2gray 这一步很关键直接把幅值线性归一化到 0~1送入网络之前不需要再做标准化。做过归一化之后不同样本之间的亮度差异被抹平网络学起来更容易收敛。4.3 MCNN 网络搭建与训练配置用 MATLAB 深度学习工具箱网络搭建可以直接用 layerGraphlgraph layerGraph(); % 输入层 lgraph addLayers(lgraph, imageInputLayer([64 64 1], Name, input)); % 多尺度卷积块1 conv1_1 convolution2dLayer(1, 32, Name, conv1_1, Padding, 0); conv1_3 convolution2dLayer(3, 32, Name, conv1_3, Padding, 1); conv1_5 convolution2dLayer(5, 32, Name, conv1_5, Padding, 2); lgraph addLayers(lgraph, conv1_1); lgraph addLayers(lgraph, conv1_3); lgraph addLayers(lgraph, conv1_5); % 并联拼接 concat1 concatenationLayer(3, 3, Name, concat1); lgraph addLayers(lgraph, concat1); lgraph connectLayers(lgraph, input, conv1_1); lgraph connectLayers(lgraph, input, conv1_3); lgraph connectLayers(lgraph, input, conv1_5); lgraph connectLayers(lgraph, conv1_1, concat1/in1); lgraph connectLayers(lgraph, conv1_3, concat1/in2); lgraph connectLayers(lgraph, conv1_5, concat1/in3); % 后面跟上BN、ReLU、池化、第二个多尺度块、全局平均池化、全连接、softmax训练配置我推荐这样设options trainingOptions(adam, ... InitialLearnRate, 1e-3, ... MaxEpochs, 30, ... MiniBatchSize, 32, ... ValidationData, {valImgs, valLabels}, ... ValidationFrequency, 10, ... Plots, training-progress, ... Shuffle, every-epoch, ... LearnRateSchedule, piecewise, ... LearnRateDropFactor, 0.5, ... LearnRateDropPeriod, 10);初始学习率 1e-3 是经验值对于图像分类任务来说比较稳。如果发现 loss 震荡不收敛先降到 5e-4 试试而不是去改网络结构。MiniBatchSize 32 是内存和梯度稳定性之间的平衡我的显卡 6G 显存跑这个尺寸没压力。4.4 结果评估与可视化训练完之后我习惯先看混淆矩阵而不是只看准确率。准确率只能告诉你整体行不行混淆矩阵能告诉你哪两类容易互相认错。我遇到的情况是滚动体故障和内圈故障的混淆率比较高原因也合理——两者产生的冲击特征在时频图上有相似性尤其是当滚动体故障进入早期阶段时特征幅度弱和噪声的边界更模糊。评估代码核心部分YPred classify(net, testImgs); accuracy mean(YPred testLabels); figure; plotconfusion(testLabels, YPred);另外我还习惯把网络最后一层全连接输出的特征向量拿出来用 t-SNE 降维到二维画散点图。如果同一类聚成一团、不同类分得开说明特征学得好如果混在一起说明网络没学到有效区分这时候就该回头看看时频图质量和网络结构了。5. 调试过程实录那些年我踩过的坑5.1 DWVD 计算速度慢到怀疑人生裸写的双重循环 DWVD 跑 1024 点信号一个样本要好几秒。几千个样本跑下来得几个小时。后来我优化了中间的相关运算部分改成了矩阵化操作速度提升明显。对于时间紧的项目建议直接用 MATLAB 的 DSP 工具箱里的现成函数或者先降采样缩短信号长度再算。时频图的尺寸和频率分辨率本身就是权衡不需要无脑追高分辨率。5.2 交叉项没压住网络学了一堆“幻觉特征”最开始我直接拿裸 WVD 的输出去训练网络训练曲线很好看但一换测试数据立刻拉胯。原因就是模型把交叉项的伪特征当成了故障特征来学换了数据交叉项的位置和形态全变了模型自然就失效了。后来加上核函数平滑把交叉项压低之后测试准确率明显回升。这一条我建议好好记住时频分析这一步的输出质量再怎么强调都不过分宁可在预处理阶段多花时间调核函数参数也别拿脏数据硬训网络。5.3 类别不均衡导致的“假准确率”早期实验里正常样本远多于故障样本网络学出来的结果看着准确率有 95%实际上故障样本几乎全分错准确率是被多数类拉高的。后来我用混淆矩阵发现问题重新对少数类做了过采样保证每类样本数量基本一致才拿到真正可用的模型。评估故障诊断模型一定要分清楚“整体准确率”和“各类准确率”后者才是工程上真正关心的指标。5.4 时频图分辨率的取舍我试过 128×128 的输入准确率只提升了一两个百分点训练时间翻倍。也试过 32×32准确率掉了五六个点。64×64 是性价比的甜点位。这个结论针对的是我实验里的数据和故障类型如果你的故障特征本身非常细微可能需要更高分辨率但这个思路值得参考——先从小到大试一遍找到准确率变化曲线变平缓的位置就是合适的分辨率。6. 换数据、换场景时这套方案怎么调整没有一套方案是放之四海而皆准的。DWVD MCNN 这套方法用的场景是旋转机械振动信号但如果要迁移到其他场景有几个地方需要重新考虑。第一个是采样率和信号长度的匹配。我的样本是 1024 点 12kHz约 85ms。如果你的设备转速很低故障特征频率只有几赫兹85ms 里可能连一个完整周期都装不下样本长度就得加长。反过来高频故障信号样本长度太长反而让时频图上的特征被压缩也要调。第二个是故障类型的区分度。如果不同故障类型在时频图上的表现本身就非常接近单纯靠图像分类网络很难区分这时候可以考虑在信号预处理上下功夫比如先做带通滤波突出共振频带再做包络解调最后再送 DWVD。包络分析是轴承故障诊断的老牌方法和时频分析结合能进一步提升信噪比。第三个是网络的迁移学习。如果换了新的设备或新的故障类型但和原有数据集有一定的相似性可以复用之前训练好的网络作为预训练模型只冻结前面的卷积层重新训练后面几层。这样能大幅减少训练数据需求收敛也快得多。我试过一次数据量只有原来的三分之一微调之后也能达到接近从头训练的效果。7. 实操中的一点个人体会这套 DVMCNN 方案我跑过轴承数据、也尝试迁移到齿轮箱数据上整体感受是时频分析和深度学习的组合拳确实比“手工特征 分类器”的老路子省心也比“裸信号直接送网络”的效果好——因为时频图等于把人类对故障机理的理解提前缝进了特征里网络不用自己去摸索这个先验知识。给后来者几个实在的建议一不要跳过核函数参数调优这一步这是 DWVD 能不能用的分水岭二第一次跑通后先别急着调网络结构把数据预处理、样本划分、评估指标这几件事搞严谨效果提升往往比换网络架构更显著三训练果不好先看混淆矩阵别只看整体准确率四严格区分训练集和测试集的时间段同一段信号重叠取样会让测试集失去意义。最后分享一个小细节。我在生成时频图像集时顺手把每张图的能量均值、峰值频率也存了一份。虽然 MCNN 自己能学特征但这些人工统计量在后续做模型可解释分析、找误分类原因的时候特别有用。有一次排查误分类样本就是靠这些统计量发现某个样本的时频图能量峰值异常低再追查下去发现是这段数据采集时传感器松动。这些边缘信息有时候比网络本身更能说明问题。