资讯详情 HO-VMD-CNN轴承故障诊断:参数优化与西储大学数据实战
📅 2026/10/12 13:20:08
简介这份资源面向从事旋转机械状态监测与深度学习故障诊断的研究生、工程师及竞赛选手提供一套基于西储大学轴承数据的HO-VMD-CNN完整实现方案。方法先用高阶变分模态分解对驱动端振动信号做多尺度分解提取本征模式函数并抑制噪声再由卷积神经网络自动学习空间特征完成磨损、裂纹等故障模式的分类识别兼顾信号处理的精细度与深度学习的特征提取能力。压缩包共15个文件约60.73MB以9个m脚本文件为核心涵盖主流程、特征提取、空间Dropout层及五种适应度函数包络熵、排列熵、复合熵、信息熵、样本熵另含4个xlsx数据表、1份算法PDF与1份环境说明txt便于直接复现与调参。目前已有203人学习下载适合希望快速搭建HO-VMD-CNN诊断流程、对比不同熵指标效果并完成论文实验的读者参考。1. 从“HO-VMD-CNN西储大学轴承故障诊断”说起这套组合拳到底在解决什么问题如果你手头有一台旋转机械的振动监测数据想做一个能落地的故障诊断模型大概率绕不开三个关键词VMD、CNN、以及那个被用烂了但确实好用的西储大学轴承数据集。HO-VMD-CNN这个标题拆开看就是“优化算法HO调参的变分模态分解VMD 卷积神经网络CNN”目标场景是轴承故障诊断。它要解决的核心痛点很具体原始振动信号里噪声大、故障特征被淹没直接丢给CNN效果不稳定而VMD的惩罚因子α和模态数K如果拍脑袋定分解结果要么模态混叠要么过分解后面CNN再强也白搭。我第一次接触这套流程时最直观的感受是VMD像一把手术刀切得好不好全看K和αCNN像显微镜但前提是切片本身得干净。HO常见的是蜣螂优化、粒子群、鲸鱼优化这类群智能算法在这里的角色就是自动搜这两个参数把“玄学调参”变成可复现的流程。这套方案适合谁适合已经拿到西储大学轴承数据、跑通过基础CNN分类、但卡在准确率上不去或者换工况就翻车的从业者。新手也能跟但前提是你得先理解VMD在干什么而不是直接抄代码。2. HO-VMD-CNN的完整链路拆解从振动信号到分类标签2.1 为什么非得用VMDEMD和STFT差在哪轴承故障振动信号是非平稳、非线性、多分量的。STFT受限于固定窗长高频和低频没法同时兼顾EMD虽然自适应但模态混叠和端点效应是血泪经验级别的坑分解出来的IMF分量经常物理意义不明。VMD的优势在于它把分解问题变成了变分约束问题每个模态围绕中心频率紧凑分布数学上更严谨。但VMD不是没有代价K值选大了一个故障特征被拆到两个模态里K选小了不同故障特征挤在一个模态里。α控制带宽α太大模态带宽窄容易丢信息α太小模态之间串扰。我一般会先做一次频谱预览看主要共振峰大概有几个K的搜索范围就定在3到8之间α在1000到5000之间搜。这个范围不是拍脑袋是西储大学数据在12kHz采样率下内圈、外圈、滚动体故障特征频率及其谐波的大致分布决定的。2.2 HO优化算法在VMD参数搜索中的适配逻辑HO以常见的蜣螂优化DBO为例本质是启发式搜索它的适应度函数决定了搜出来的K和α好不好。这里最容易翻车的地方是很多人直接拿CNN的分类准确率当适应度跑一次VMDCNN要几十秒优化算法迭代几十次一天都跑不完。我的做法是用包络熵或者样本熵作为适应度VMD分解完直接算毫秒级搜出来的参数再交给CNN验证。import numpy as np from vmdpy import VMD from scipy.stats import entropy def fitness_function(params, signal): params: [K, alpha] signal: 原始振动信号一维数组 返回: 包络熵越小越好 K int(params[0]) alpha params[1] # VMD分解tau0表示无噪声约束DC0表示无直流分量 u, u_hat, omega VMD(signal, alpha, 0, K, 0, 1, 1e-7) # 计算所有模态的包络熵均值 env_entropy [] for mode in u: # 希尔伯特变换取包络 analytic np.abs(np.fft.ifft(np.fft.fft(mode) * 2)) # 归一化后算熵 p analytic / (np.sum(analytic) 1e-12) env_entropy.append(entropy(p)) return np.mean(env_entropy)这段代码里VMD函数的参数顺序是(signal, alpha, tau, K, DC, init, tol)别搞反。包络熵越小说明模态越稀疏、故障冲击特征越明显。用这个当适应度DBO迭代50次大概两三分钟就能收敛比直接上CNN快一个数量级。2.3 用HO搜出来的K和α怎么喂给CNN搜到最优K和α之后VMD分解得到K个模态分量。不是所有模态都有用我一般会算每个模态和原始信号的相关系数取相关系数大于0.3的前几个模态或者直接取包络熵最小的前3个模态。然后把这些模态堆成二维矩阵行是模态数列是时间点做成类似灰度图的格式喂给CNN。def build_cnn_input(signal, K, alpha, top_n3): 构建CNN输入选取top_n个模态归一化后堆叠 u, _, _ VMD(signal, alpha, 0, K, 0, 1, 1e-7) # 计算每个模态与原始信号的相关系数 corrs [np.corrcoef(mode, signal)[0, 1] for mode in u] # 按相关系数排序取前top_n idx np.argsort(corrs)[-top_n:] selected u[idx] # 逐模态归一化到[0,1] normalized [] for mode in selected: mode_min, mode_max mode.min(), mode.max() normalized.append((mode - mode_min) / (mode_max - mode_min 1e-12)) return np.array(normalized) # shape: (top_n, signal_length)CNN结构不用太复杂三层卷积加两层全连接足够。输入尺寸是(top_n, 1024)如果原始信号长度是2048可以截断或者降采样。卷积核用3×3和5×5混合池化用最大池化。西储大学数据量不大每个故障类别几百个样本记得做数据增强加高斯白噪声和随机幅值缩放。3. 西储大学轴承数据集的预处理与样本构造别在第一步就埋雷3.1 数据加载和故障类别映射西储大学数据集的文件命名有规律比如12kDriveEndBearingFault下面按转速和故障直径分文件夹。我一般写个脚本批量读取把内圈故障、外圈故障、滚动体故障分别映射成0、1、2正常样本映射成3。注意外圈故障有6点钟、3点钟、12点钟三个位置初期做三分类就够了别一上来搞九分类样本不均衡会让你怀疑人生。import os import scipy.io as sio import numpy as np def load_cwru_data(root_dir, fault_types, window_size2048, overlap0.5): root_dir: 数据集根目录 fault_types: 字典键是文件夹关键词值是标签 window_size: 每个样本的长度 overlap: 滑动窗口重叠比例 X, y [], [] step int(window_size * (1 - overlap)) for key, label in fault_types.items(): folder os.path.join(root_dir, key) for file in os.listdir(folder): if file.endswith(.mat): mat sio.loadmat(os.path.join(folder, file)) # DE_time是驱动端加速度数据不同文件变量名可能不同 signal mat[X file[:3] _DE_time].flatten() for start in range(0, len(signal) - window_size, step): X.append(signal[start:start window_size]) y.append(label) return np.array(X), np.array(y)overlap0.5意味着相邻样本有一半重叠这是为了在小样本下增加数据量。但注意训练集和测试集划分要按原始文件划分不能按样本随机划分否则同一个文件切出来的样本会同时出现在训练和测试里准确率虚高到99%以上实际部署直接翻车。3.2 训练集测试集划分的坑按文件划分而不是按样本我见过太多人在这里翻车。正确做法是先把所有文件列出来随机选80%的文件作为训练文件20%作为测试文件然后分别对训练文件和测试文件做滑窗。这样测试集里的样本和训练集来自不同文件更接近真实场景。def split_by_file(root_dir, fault_types, test_ratio0.2): train_files, test_files [], [] for key in fault_types: folder os.path.join(root_dir, key) files [f for f in os.listdir(folder) if f.endswith(.mat)] np.random.shuffle(files) split int(len(files) * (1 - test_ratio)) train_files.extend([(os.path.join(folder, f), fault_types[key]) for f in files[:split]]) test_files.extend([(os.path.join(folder, f), fault_types[key]) for f in files[split:]]) return train_files, test_files拿到文件列表后再分别滑窗构造样本。这个细节决定了你的模型是实验室玩具还是能上产线的工具。3.3 归一化方式的选择z-score还是min-maxVMD分解后的模态幅值范围差异很大有的模态幅值在10的负三次方量级有的在10的零次方量级。如果直接min-max归一化小幅值模态的信息会被压缩到几乎为零。我的习惯是先对每个模态单独做z-score标准化再统一缩放到[-1,1]。这样既保留了模态内部的相对变化又让CNN的输入分布一致。def normalize_modes(modes): modes: shape (n_modes, length) 先z-score再缩放到[-1,1] normalized [] for mode in modes: mean, std mode.mean(), mode.std() z (mode - mean) / (std 1e-12) # 缩放到[-1,1]用tanh更平滑 normalized.append(np.tanh(z)) return np.array(normalized)用tanh而不是线性缩放是因为tanh对异常值不敏感振动信号里偶尔的冲击尖峰不会把整个模态拉偏。4. 避坑与排查HO-VMD-CNN落地时最容易翻车的五个地方4.1 现象VMD分解报错“Input signal must be real”原因从.mat文件读出来的信号可能是复数格式或者维度不对。西储大学数据里有些文件存的是包络谱而不是原始时域信号。解决读出来后先np.real()取实部再flatten()压成一维最后检查len(signal) 100太短的信号直接丢弃。4.2 现象HO优化算法收敛到K3α2000就不动了原因适应度函数用了包络熵但包络熵对K不敏感K3和K5的包络熵差异很小优化算法没有梯度信息。解决适应度函数改成包络熵和模态间相关系数的加权模态间相关系数越小越好这样K太小会被惩罚。def fitness_combined(params, signal): K int(params[0]) alpha params[1] u, _, _ VMD(signal, alpha, 0, K, 0, 1, 1e-7) # 包络熵部分 env_entropy [] for mode in u: analytic np.abs(np.fft.ifft(np.fft.fft(mode) * 2)) p analytic / (np.sum(analytic) 1e-12) env_entropy.append(entropy(p)) # 模态间相关系数部分 corr_matrix np.corrcoef(u) # 取上三角非对角线元素的均值 upper corr_matrix[np.triu_indices(K, k1)] return np.mean(env_entropy) 0.5 * np.mean(np.abs(upper))4.3 现象CNN训练准确率99%测试准确率60%原因按样本随机划分了训练测试集同一个文件的滑窗样本泄漏了。解决按文件划分并且训练时加Dropout和L2正则别让模型记住样本。4.4 现象换一个转速工况模型完全失效原因西储大学数据有0hp、1hp、2hp、3hp四种负载不同负载下故障特征频率会偏移。解决训练时把不同负载的数据混在一起或者用迁移学习先在源负载上预训练再在目标负载上微调最后两层。4.5 现象HO搜出来的参数每次都不一样原因群智能算法本身有随机性初始种群随机生成。解决固定随机种子并且把种群规模和迭代次数设大一点DBO用30个个体、50代基本能稳定收敛到相近的K和α。5. 进阶技巧用包络谱峰值信噪比验证VMD分解质量最后一章说一个我常用的验证技巧。HO-VMD-CNN跑通之后怎么判断VMD分解是真的有效而不是碰巧分类准确率高我的做法是算包络谱峰值信噪比。具体来说对每个模态做希尔伯特变换取包络再对包络做FFT得到包络谱然后看故障特征频率处的峰值和背景噪声均值的比值。def envelope_spectrum_snr(mode, fs, fault_freq): mode: 一个模态分量 fs: 采样率 fault_freq: 理论故障特征频率 返回: 包络谱峰值信噪比(dB) analytic np.abs(np.fft.ifft(np.fft.fft(mode) * 2)) env_spectrum np.abs(np.fft.fft(analytic)) freqs np.fft.fftfreq(len(mode), 1/fs) # 取正频率部分 pos_mask freqs 0 freqs, env_spectrum freqs[pos_mask], env_spectrum[pos_mask] # 找故障频率附近的峰值 idx np.argmin(np.abs(freqs - fault_freq)) peak env_spectrum[max(0, idx-2):idx3].max() # 背景噪声取中位数 noise np.median(env_spectrum) return 20 * np.log10(peak / (noise 1e-12))这个指标大于8dB说明故障特征被有效提取出来了小于5dB即使CNN分类准确率高也可能是过拟合。我一般会把这个指标和CNN的混淆矩阵一起看两个都过关才认为方案靠谱。另外HO的适应度函数可以换成这个包络谱峰值信噪比直接最大化它比包络熵更贴近最终目标。但计算量会大一些因为要对每个模态做FFT。折中方案是先用包络熵粗搜再用包络谱信噪比精搜。最后说个习惯每次跑完HO-VMD-CNN我都会把最优K、α、包络谱信噪比、测试集准确率四个数记在一个表格里换数据集或者换工况时对比着看。有一次K6、α2500在0hp负载下准确率98%换到3hp直接掉到72%后来发现是3hp的故障特征频率偏移了VMD的K没跟着变。从那以后我每换一个工况就重新跑一次HO不偷懒。希望帮到你。本文还有配套的精品资源点击获取