简介面向轴承故障诊断研究的一套完整实现方案采用高阶变分模态分解HO-VMD与卷积神经网络CNN联合建模针对西储大学公开振动数据进行特征提取与故障模式识别。HO-VMD负责将复杂信号分层分解为模态分量并抑制噪声CNN则自动学习空域特征完成磨损、裂纹等故障分类相比传统方法在准确率和鲁棒性上更有保障。压缩包共15个文件约60.73MB包括MATLAB源码.m、不同转速下的振动数据表格.xlsx、核心算法原始文献PDF以及运行环境安装说明TXT代码结构清晰主程序与子函数分模块存放便于直接复现实验。内容覆盖数据预处理、特征提取、适应度评估到CNN搭建与测试的完整流程尤其自带包络熵、排列熵、复合熵、信息熵、样本熵五种适应度函数及特征提取后的数据集可帮助研究者快速对比不同分解参数的影响。目前已有203人学习下载适合有一定信号处理基础、希望用深度学习开展轴承故障诊断的在校学生和工程师读者可按需修改输入数据与网络参数进一步迁移到其他旋转机械故障诊断场景。1. HO-VMD-CNN西储大学轴承故障诊断“优化-分解-分类”三段式为什么值得做直接拿原始振动信号扔给CNN做西储大学轴承故障诊断很多人在实验室里能跑到95%以上但换一个负载、换一段数据就掉回85%。原因不是CNN不行而是输入里混着大量转频、噪声和随机冲击卷积核把不该学的都学了。把HO-VMD-CNN这条链路搭起来本质是先把振动信号拆成一组有物理含义的模态再让分类器只盯着故障冲击所在的成分。HO是哈里斯鹰优化工程里常简写为HHO它负责把VMD里最头疼的模态数K和惩罚因子alpha自动搜出来避免人肉调参变成了玄学。这个方法适合正在做毕设或设备健康管理预研的人西储大学CWRU数据公开、故障类型齐全跑通一遍你就同时掌握了信号分解、群体优化和深度特征提取三块能力。2. 三个组件各自在干什么CWRU数据、VMD分解和HO寻优的选型逻辑2.1 西储大学数据集的真实组织方式别只盯着DE_time那列CWRU轴承数据是滚动轴承故障诊断的事实标准数据集。它包含正常状态以及内圈、外圈、滚动体三类故障每类故障又按损伤直径分成0.007、0.014、0.021英寸三种规格合起来正好是10类状态。每个状态有0、1、2、3马力四种负载工况采样率分为12kHz和48kHz两档。绝大多数论文用的是12kHz驱动端加速度信号也就是mat文件里的DE_time变量。文件名的数字编号只是数据编号不代表故障类别比如97号是正常基线105号是内圈0.007英寸故障但这种对应关系必须去CWRU官网的说明文档核对不能拿文件名顺序直接当标签用。样本组织方式也影响后面的泛化能力。原始信号是连续的几十万点长序列训练CNN前要切窗。切窗的窗口长度和步长决定了一个样本包含多少个轴承旋转周期也决定了训练集和测试集之间会不会发生信息重叠。我的经验是窗口取1024点、步长512点覆盖率50%这样的样本在VMD分解后能保留至少2到3个冲击周期CNN又能看到足够多的变化。窗口再短冲击特征会被噪声淹没再长样本数是下来了但训练样本量又不够。2.2 VMD相对EMD的本质区别带限约束避免了模态混叠经验模态分解EMD在轴承故障诊断里用了很多年但它的固有缺陷让它在CWRU这类强冲击信号上不太好用每一次分解递归地剥包络对噪声敏感而且模态混叠经常导致同一个冲击成分被拆到两三个IMF里谁都不干净。VMD换了一条路它把“分解”变成一个约束变分问题每个模态被定义为围绕一个中心频率的带限信号通过交替方向乘子法一次求解出全部K个模态。这个约束条件很重要它强制每个IMF的带宽尽量窄于是周期性冲击、工频成分、随机噪声会被分到不同频带模态混叠在源头被抑制。VMD有两个参数对结果影响最大K是模态个数alpha是惩罚带宽因子。K太小故障冲击和转频挤在一个模态里K太大一个完整的冲击又被拦腰切成碎片。alpha影响每个模态的带宽集中程度alpha越大模态中心频率附近保留的成分越纯但容错也越低。这两个参数没有固定解同一个数据集上不同工况搜出来的结果也不一样这就引出了标题里的HO。2.3 HO作为参数搜索器它不是锦上添花是结果可复现的前提在标题的缩写里HO通常指哈里斯鹰优化Harris Hawks Optimization。为什么要用群体优化算法来调两个参数因为VMD对K和alpha的响应不是线性的K5和K6之间可以出现结果突变网格搜索要跑几百次VMD才能粗略定位而VMD本身又是相对昂贵的操作一次分解几万点信号就要一两秒。哈里斯鹰优化有探索、软包围、硬包围三个阶段前期全局撒网、后期局部收敛20只鹰、30次迭代就能找到比较可靠的参数组合。这一点对设备健康管理场景特别重要因为现场更换传感器或转速变化后都要重新寻优迭代次数太多没法用太少又容易停在局部极值。HO在这里的定位不是比赛刷分而是把“人肉试K”变成“可复现的配置”。跑完HO之后会得到一组确定的K、alpha把它们保存下来作为整个诊断系统的固定配置。之后新来的振动数据都用同一组VMD参数分解保证训练和推断时特征分布一致。这是后面所有避坑讨论的基础。2.4 CNN卷积神经网络在这个环节里的真实角色模态图上的特征解释器很多人一听到CNN就条件反射地认为它是在“学习信号”实际上不是。把VMD分解出来的K个IMF按行堆叠成一张二维图像后卷积神经网络学的是这张图上的局部空间模式某一行的冲击波形态、相邻行之间的能量分布关系、不同频带在时间轴上的对齐情况。它本质是一个特征解释器负责把物理含义已经很明确的模态图映射到故障类别。所以CNN的结构不需要很深。在CWRU这种公开数据上两层卷积加全连接层往往就够了堆到十几层反而会因为训练样本量不足而过拟合。最后用两个Dense层把空间特征映射到类别得分也就是常说的全连接层。这个思路是VMD先做物理分工CNN再做模式识别HO在中间保证分工方式是优化的、确定性的。这也是HO-VMD-CNN和直接用绘制OHLC图像做股票预测的多尺度CNN这类“信号转图像再分类”方法在精神上共通的地方只是这里的图像来源是模态分解而非行情切片。3. 从mat文件到CNN训练可复现的HO-VMD-CNN最小工程3.1 CWRU数据读取与切窗先看清mat变量名再动手CWRU官方给的mat文件在Windows和不同MATLAB版本下导出后变量名并不完全统一。有的包里变量叫DE_time有的叫X105_DE_time还有的里面对应的是多个通道。直接用loadmat然后猜列索引是新手翻车的第一站。我一般先用whosmat看一眼变量列表再决定读哪个变量。import scipy.io as sio import numpy as np # 先列出mat里所有变量名不要跳过这一步 info sio.whosmat(105.mat) print(info) # 常见输出[(X105_DE_time, (121265, 1), double)] # 变量名里带DE_time的是驱动端加速度信号 def load_cwru_de(path: str) - np.ndarray: mat sio.loadmat(path) keys [k for k in mat.keys() if not k.startswith(__) and DE_time in k] if len(keys) ! 1: raise ValueError(fDE_time 变量不唯一: {keys}) return mat[keys[0]].ravel().astype(np.float64)这里的关键是用变量名而不是数组位置去锁定信号。DE_time是驱动端加速度传感器输出采样率12000Hz单位是g。变量名不唯一时立刻报错而不是蒙着往下走能省掉后面整个实验作废的风险。切窗时同样要小心窗口和步长直接决定样本数。SLICE_LEN 1024 STRIDE 512 def slice_samples(sig: np.ndarray, label: int): samples [] n len(sig) for start in range(0, n - SLICE_LEN 1, STRIDE): seg sig[start:start SLICE_LEN] samples.append((seg, label)) return samples切出来的每个样本是1024点的一维数组。1024点大约对应一个转频周期的10倍以上能覆盖多个冲击周期。步长设成512意味着相邻样本有50%重叠样本量扩大一倍但必须在划分训练集和测试集时按连续段分组否则同一个振动段的信息会同时出现在两边。3.2 VMD分解的最小调用vmdpy的输入输出先对齐VMD的常见实现是GitHub上搜索量很高的vmdpypip安装后即可调用。它的核心好处是函数签名固定只需要传原始信号和几个参数就能拿到分解结果不需要自己写交替方向乘子法。pip install vmdpyfrom vmdpy import VMD alpha 2000 # 惩罚带宽因子决定模态带宽集中度 tau 0 # 噪声容忍度0表示严格重构 K 6 # 模态个数HO优化对象 DC 0 # 0表示不把直流分量作为单独模态 init 1 # 中心频率初始化1表示均匀分布 tol 1e-7 # 收敛阈值 u, u_hat, omega VMD(sig, alpha, tau, K, DC, init, tol) # u: (K, N) 每个IMF的时域波形 # omega: (K, len(u_hat[0])) 各个模态中心频率随迭代收敛的轨迹u的第i行就是第i个固有模态函数。omega可以用来检查每个模态收敛到了什么频率比如外圈故障的冲击成分一般会落到一个较高频段如果发现两个模态中心频率挤在一起就该怀疑K取大了。这一阶段只把VMD当成工具用不需要改内部实现。3.3 包络熵目标函数让HO能比较哪组分解更好HO在搜索时需要知道某一组K和alpha到底好不好。在轴承故障诊断里最常见的适应度是包络熵。包络熵衡量的是分解后IMF包络信号的稀疏度如果某个IMF恰好包住了周期性冲击包络是一串尖锐脉冲熵值就小如果IMF里全是杂乱噪声包络接近均匀熵值就大。from scipy.signal import hilbert def envelope_entropy(imf: np.ndarray) - float: analytic hilbert(imf) env np.abs(analytic) prob env / (env.sum() 1e-12) return -np.sum(prob * np.log(prob 1e-12)) def vmd_fitness(params, sig): k int(round(params[0])) alpha params[1] u, _, _ VMD(sig, alpha, tau0, Kk, DC0, init1, tol1e-7) entropies [envelope_entropy(u[i]) for i in range(k)] # 加一个小惩罚防止K无限增大导致单脉冲被切碎 return np.mean(entropies) 0.05 * k0.05乘以K这个惩罚项是我自己的习惯。如果只取平均包络熵HO会发现把K加到很大时每个IMF只剩一小段冲击熵值反而更低于是拼命选大K最后分解结果没法看。加上这个惩罚项以后K增大的收益会被抵消一部分HO会收敛到“用最少模态覆盖主要冲击”的解。3.4 HO寻优核心循环简化版HHO也能给出可用配置完整的哈里斯鹰优化带四种包围策略但在工程落地时先跑一个简化版本把链路打通更重要。下面的代码保留了探索与开发的边界判断并且每次只比较新解的适应度。rng np.random.default_rng(42) def hho_optimize(sig, pop20, iters30): bounds ([3, 10], [500, 5000]) # K的搜索范围alpha的搜索范围 birds np.zeros((pop, 2)) for i in range(pop): birds[i, 0] rng.integers(bounds[0][0], bounds[0][1] 1) birds[i, 1] rng.uniform(bounds[1][0], bounds[1][1]) fits np.array([vmd_fitness(b, sig) for b in birds]) best_idx np.argmin(fits) best_bird birds[best_idx].copy() best_fit fits[best_idx] for t in range(iters): e1 2 * (1 - t / iters) # 逃逸能量基线从2降到0 for i in range(pop): e 2 * rng.random() * e1 - e1 r rng.random() if abs(e) 1: j rng.integers(pop) new birds[j] - rng.random() * np.abs(birds[j] - 2 * rng.random() * birds[i]) else: new best_bird - e * np.abs(best_bird - birds[i]) new[0] int(np.clip(np.round(new[0]), bounds[0][0], bounds[0][1])) new[1] np.clip(new[1], bounds[1][0], bounds[1][1]) f_new vmd_fitness(new, sig) if f_new fits[i]: birds[i] new fits[i] f_new idx np.argmin(fits) if fits[idx] best_fit: best_fit fits[idx] best_bird birds[idx].copy() return best_bird, best_fit逃逸能量e的绝对值大于等于1时鹰在全局范围内随机跳跃对应探索小于1时鹰围绕当前猎物收缩包围对应开发。代码里三种情况共用了一个更新公式实际学术版本还会根据随机数r判断软包围、硬包围和渐进俯冲但你这个阶段先跑通再替换成完整策略不迟。注意每次评估fitness都会调用一次VMD寻优30代、种群20理论上要跑600次VMD几万点信号大概十几分钟如果信号长度超过十万点时间会暴涨这时要缩小搜索范围。3.5 把IMF堆成图像固定长度、独立归一化是关键VMD输出的每个IMF长度和原始信号一样但不同样本的原始长度可能因为切窗而一致实际上每个样本内部各IMF长度都等于1024。为了统一输入格式并抑制绝对幅值影响我对每个IMF做重采样到固定长度并单独z-score。from scipy.signal import resample TARGET_LEN 512 def imf_to_matrix(u: np.ndarray, target_lenTARGET_LEN): k u.shape[0] mat np.zeros((k, target_len), dtypenp.float32) for i in range(k): seg resample(u[i], target_len) std seg.std() 1e-8 mat[i, :] (seg - seg.mean()) / std return mat为什么要在每个IMF内部单独归一化因为不同样本的振动能量差异很大如果按整幅图归一化能量小的样本会被压成一团黑CNN很容易靠绝对亮度分类而不是靠波形特征分类。独立归一化后每行IMF的幅值范围一致CNN学到的是形态而不是能量的绝对值这对跨负载泛化很重要。3.6 一个够用的CNN骨架与训练配置输入尺寸是(K, TARGET_LEN, 1)K由HO输出确定TARGET_LEN固定512。用两层Conv2D加两个Dense不要堆太深。import tensorflow as tf K, L best_bird[0], TARGET_LEN model tf.keras.Sequential([ tf.keras.Input(shape(K, L, 1)), tf.keras.layers.Conv2D(32, (3, 3), paddingsame, activationrelu), tf.keras.layers.BatchNormalization(), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(64, (3, 3), paddingsame, activationrelu), tf.keras.layers.BatchNormalization(), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dropout(0.4), tf.keras.layers.Dense(128, activationrelu), tf.keras.layers.Dense(10, activationsoftmax) ]) model.compile( optimizertf.keras.optimizers.Adam(1e-3), losscategorical_crossentropy, metrics[accuracy] )卷积核(3,3)在模态图上只关注相邻两三个IMF之间的局部关系正好对应相邻频带的联动。BatchNormalization放在激活之后能削弱样本之间幅值尺度差异的影响。Dropout取0.4防止Dense层在样本量有限时死记硬背。训练时配合EarlyStopping监控验证损失连续10个epoch不降就停学习率用ReduceLROnPlateau每5个epoch下降一半。tf.keras.callbacks.EarlyStopping(monitorval_loss, patience10) tf.keras.callbacks.ReduceLROnPlateau(monitorval_loss, factor0.5, patience5)4. 避坑清单CWRU VMD-CNN最容易翻车的5个环节4.1 随机切窗导致训练集和测试集互相串段现象模型在验证集上准确率接近99%现场部署到同一台设备上换一段数据准确率跌到80%出头。原因切窗时步长为512相邻样本重叠率50%如果你直接用train_test_split随机划分同一个连续振动段的两个高度相似样本被拆到了训练集和测试集两边。CNN实际上记住了这段信号的样子而不是学会了故障模式。解决按连续段为单位划分一段信号切出来的所有样本必须全部进同一个集合。更严格的做法是用不同负载的文件做训练和测试例如0马力训练、1马力测试完全杜绝串段。4.2 测试集也单独跑了一遍HO寻优现象单类故障识别率很高但整体结果每次跑都不一样。原因有些教程把VMD参数寻优写在了主流程里训练数据寻优得到一组参数测试数据又各自寻优得到另一组参数。测试集的特征提取规则和训练集不一致相当于考试时换了张考卷。解决HO寻优只在训练集上跑一次结束后把最优参数写成json配置测试和推断阶段读取配置直接分解不允许再优化。{ K: 6, alpha: 2380.5, tau: 0, DC: 0, init: 1, tol: 1e-7 }4.3 模态数K取2导致的先天性翻车现象VMD分解后只看到低频转频和一个高频噪声包CNN怎么调准确率都卡在70%以下。原因轴承故障冲击是宽频激励K2时冲击成分被迫和随机高频噪声挤在同一个模态里这个模态的包络熵反而很高CNN无法从中提取稳定的周期冲击。解决HO搜索区间下限不要设成2直接从3开始。另外不要只盯包络熵一个指标寻优结束后把最优参数对应的IMF包络谱打出来看一眼确认冲击频率及其倍频清晰可辨这一步能拦截大量纯数值上好看但物理上没意义的分解结果。4.4 mat文件变量名与标签映射错位现象加载一百个mat文件按文件名的数字下标从0到99给标签训练到一半发现正常样本和内圈0.021英寸样本彻底混在一起。原因CWRU文件名编号是采集顺序和故障类别没有规则对应关系直接拿迭代变量i当标签必然错。解决每个mat文件先打印whosmat结果再用一个manifest文件把文件名编号映射到故障类型、损伤直径和负载。# 手动整理一份manifest不要省 # 105 - ir_007_0hp # 106 - ir_014_0hp # 130 - or_007_3oclock_0hp这类映射表在GitHub上搜“CWRU bearing dataset manifest”能找到整理好的版本但下载后也要和官网说明核对一遍。4.5 TensorFlow在Windows上的显卡驱动导致训练慢十倍现象一个epoch要跑几分钟打印信息里看不到GPU。原因TensorFlow 2.10之后在Windows原生环境不再支持GPU需要WSL2或降级到2.10配CUDA 11.2和对应显卡驱动。你在win10上装好驱动实际训练却跑在CPU上这是相当常见的情况。解决训练前执行tf.config.list_physical_devices(GPU)确认能看到设备列表如果看不到要么装WSL2环境要么接受CPU训练并缩减样本量、把窗口步长拉大到1024减少样本数。不要在这个问题上死磕VMD寻优本身就是CPU密集型操作CNN部分用CPU跑小模型也能完成验证。5. 参数边界与调优手感K、alpha、tau和CNN超参数怎么设才不靠玄学5.1 VMD参数的工程区间一张表说清楚VMD参数在CWRU轴承数据上有一个相对集中且有效的取值范围。这不是官方规定而是大量论文和工程复现落在的区间。参数作用常用范围我的默认值与说明K模态个数310HO搜索域设为[3,10]大部分轴承信号收敛在57alpha惩罚带宽因子5005000alpha太大模态窄但容错低太小则模态混叠tau噪声容忍度0或1e-4默认0取非零值时允许重构误差实际提升有限DC是否单独提取直流0或1轴承振动默认0信号有明显偏置才考虑1init中心频率初始化1均匀分布初始化预知中心频率时可改0tol收敛阈值1e-7不用改越小迭代越多收益不明显alpha的数值要和你信号幅值放在一起看。CWRU的加速度数据范围大致在正负2galpha取2000左右是典型值如果你用的是g值放大了比如10倍的传感器alpha要相应调大否则分解出来的模态带宽会过宽。5.2 HO寻优参数的收敛手感HHO里面有三个数需要你来定种群大小pop、迭代次数iters、搜索边界。pop取20、iters取30是我的起点。迭代到后期如果适应度曲线在15代内下降不足1%就当成已经收敛不需要跑满。搜索边界里K的范围对结果影响很大K上限拉高到10以上后平均包络熵会因为惩罚项被拉回来但寻优时间会明显变长。若某个样本长度超过十万点建议先用前十万点做一次快速寻优确定大致区间再用完整信号精调否则一次实验要跑半个多小时。5.3 CNN超参数区间小模型优先CNN部分不需要追求大模型。输入图的宽是512点高是K一般在5到7之间这个尺寸本身很小大卷积核没有意义。经验配置如下。超参数推荐值说明卷积核(3,3)、paddingsame只修相邻模态关系步长固定1特征层数32→64两层足够更多层在千级样本下容易过拟合池化(2,2)两次池化把512压到128Dropout0.4最后一层全连接前加Dense128中间层再宽没有收益学习率1e-3 0.5衰减每5个epoch降一半监控验证损失Batch Size32样本少时32比64更稳EarlyStoppingpatience10防止训练迭代过多发生过拟合训练时输入维度是(K, L, 1)但K在每次寻优后可能变化所以模型第一层要根据最优K重建。如果两个不同工况跑出来的最优K不一样做法不是硬把K统一而是保持各自最优的K在输入前做插值重采样到同一个网格上再训练。5.4 评价指标准确率之外至少看四样东西CWRU数据集类别均衡准确率能到95%以上不代表方法可交付。我一般会固定一个模型后看四样混淆矩阵里哪些类互相混。滚动体故障和正常状态经常混因为滚动体缺陷的冲击方向和承载区角度相关不是每个旋转周期都撞上外圈冲击形状不稳定。再看每种故障的召回率尤其是0.021英寸大故障是否反而比0.007英寸难分若出现大故障更难分说明VMD把强冲击切成碎片了。还有跨负载准确率它直接反映模型是学了故障物理还是背了样本分布。最后用t-SNE把CNN倒数第二层的特征投影到二维平面故障类别如果聚成清晰的簇说明特征可靠如果混杂成一团先回去调VMD参数不要急着改网络结构。6. 让这套方案可交付留一规格、跨负载与特征可视化的验证套路我判断一套HO-VMD-CNN方案能不能真正交给现场不是看它在随机划分的测试集上的准确率而是看三个更严格的验证。第一个是留一规格验证只用0.007英寸和0.014英寸两种损伤规格训练拿0.021英寸的样本做测试。这种做法模拟了现场遇到新损伤程度的场景。如果准确率掉到90%以下说明模型在背损伤深度的分布而不是在学故障模式能保持在90%以上说明VMD分解出来的冲击周期性特征确实被CNN学到了。第二个是跨负载验证用0马力负载的数据训练拿1马力负载的数据测试。CWRU四种负载对应不同转速转速一变冲击周期和频率都会漂移这个验证能暴露你之前是否过度依赖某个特定转频。第三个是激活可视化。对测试集样本做Grad-CAM把CNN最后一层卷积的激活值叠加到IMF图上理想情况是激活区域集中在少数几个IMF行上也就是HO先分解出来的冲击成分所在的行。如果激活在全部K行上均匀铺开说明CNN没有找到关键频带模型的决策依据是高级噪声。这三个验证做完我还会坚持一个习惯把HO寻优参数、VMD参数、CNN结构和训练指标全部固化到配置文件里。下次采集新数据直接复用同一组参数做分解和推断不在现场重新寻优。早期我踩过最深的坑就是把优化器写进了部署脚本导致现场每次跑出来的结果都不一样最终还是靠配置化才把问题锁死。这种流程上的一致性往往比模型结构更重要。希望这篇笔记能帮你把CWRU这套流程真正跑通并且在跑到95%准确率的时候还有底气说一句我知道它为什么准。本文还有配套的精品资源点击获取