☰
HO-VMD-CNN轴承故障诊断:从信号分解到CNN分类的落地实践
2026/10/13 4:12:02 网站建设 项目流程

简介:这份资源面向轴承故障诊断方向的研究生、算法工程师与信号处理学习者,提供一套将高阶变分模态分解与卷积神经网络结合的完整Matlab实现方案,用于解决西储大学驱动端振动信号中故障特征提取难、噪声干扰强、分类准确率不稳定的问题。压缩包共15个文件,约60.73MB,包含9个m脚本、4个xlsx数据表、1个txt说明与1个pdf文献,脚本覆盖HO-VMD分解、特征提取、空间Dropout层与五种适应度函数(包络熵、排列熵、复合熵、信息熵、样本熵),数据表则对应1750、1772、1797三组转速下的驱动端振动记录及分解后数据集。已有203人学习下载,适合希望复现HO-VMD-CNN流程、对比不同熵指标效果或直接套用西储大学数据做实验的读者,可据此快速搭建从信号分解到故障分类的完整链路。

1. HO-VMD-CNN西储大学轴承故障诊断:一条被低估的落地路径

西储大学轴承数据集几乎是每个做旋转机械故障诊断的人绕不开的公开数据,但真正把它跑出可复现结果的人并不多。HO-VMD-CNN 这套组合,本质上是把信号分解、模态优化和卷积网络串成一条流水线:先用 VMD 把振动信号拆成若干本征模态,再用 HO 策略去优化 VMD 的关键参数,最后把模态分量喂给 CNN 做分类。它解决的是原始振动信号信噪比低、单一 CNN 直接吃原始信号容易过拟合的问题。适合已经跑过基础 CNN 分类、想往信号处理方向深挖的从业者,也适合需要一套可解释、可调参的故障诊断流程的工程团队。这条路径不算新,但参数没调对就翻车的情况非常普遍。

2. HO-VMD-CNN 的选型逻辑:为什么不是直接上 CNN

2.1 原始振动信号直接喂 CNN 的三个硬伤

西储大学数据集的驱动端加速度计信号,采样频率常见 12kHz 和 48kHz 两档。以 12kHz 为例,一个 0.1 秒的窗口就是 1200 个点。直接把这段时序丢进一维 CNN,理论上可行,但实际跑起来有三个绕不开的问题。

第一是噪声主导。轴承早期故障的冲击成分往往淹没在背景噪声里,CNN 的卷积核在浅层只能学到局部波形,噪声一多,梯度方向就被带偏。第二是样本量矛盾。西储大学每个故障直径档位(0.007、0.014、0.021 英寸)的样本有限,直接切窗做数据增强,窗口之间高度相关,验证集精度虚高。第三是频域信息丢失。时域卷积对周期性冲击的捕捉依赖感受野,而故障特征频率(如内圈 BPFI、外圈 BPFO)是频域概念,纯时域网络学起来效率低。

VMD 在这里的作用,是把信号按中心频率自适应地拆成 K 个模态。每个模态对应一个窄带分量,故障冲击往往集中在某个或某几个模态里。这样 CNN 的输入从“一锅粥”变成“几盘菜”,学习难度下降一个量级。

2.2 VMD 的参数为什么需要 HO 来优化

VMD 有两个核心参数:模态数 K 和惩罚因子 α。K 决定拆几个模态,α 决定每个模态的带宽。K 太小,故障模态和噪声模态混在一起;K 太大,出现过分解,同一个故障被拆到多个模态,CNN 反而学到冗余特征。α 太小,模态带宽过宽,噪声进来;α 太大,模态带宽过窄,故障冲击被削掉。

工程上常见的做法是固定 K=4 或 K=5,α=2000,然后手动试。但西储大学不同故障类型、不同负载(0 到 3 马力)下,最优 K 和 α 并不一致。HO 策略——无论是用粒子群、遗传算法还是贝叶斯优化——本质上是把 K 和 α 当作超参数,用某个目标函数(常见的是包络熵或样本熵)去搜索。包络熵越小,说明模态里周期性冲击越明显,故障特征越干净。

我一般会先用包络熵做目标,搜索范围 K 取 2 到 8,α 取 500 到 5000。这个范围覆盖了西储大学绝大多数工况。搜索算法本身不挑,粒子群收敛快但容易早熟,贝叶斯优化样本效率高但实现稍重。如果只是复现,粒子群 20 个粒子迭代 30 代足够。

2.3 CNN 输入该用哪个模态

VMD 拆完 K 个模态,不是全部塞给 CNN。常见做法是计算每个模态的包络熵或峭度,选指标最优的前 2 到 3 个模态。峭度对冲击敏感,但受个别尖峰影响大;包络熵更稳,适合作为筛选依据。选出来的模态可以拼接成多通道输入,也可以分别做特征提取再融合。前者实现简单,后者精度上限更高但调参麻烦。新手建议先走多通道拼接,把流程跑通再优化。

3. 用 Python 跑通 HO-VMD-CNN 的最小闭环

3.1 数据准备与标签映射

西储大学数据集的文件命名有规律,比如12kDriveEndBearingFault下面按转速和故障直径分文件夹。常见做法是读.mat文件,取DE_time通道。标签按故障类型和直径映射成整数。

import scipy.io as sio import numpy as np import os def load_cwru_mat(filepath, channel='DE_time'): """读取西储大学 .mat 文件,返回指定通道的振动信号""" mat = sio.loadmat(filepath) # 常见键名如 X123_DE_time,这里做模糊匹配 key = [k for k in mat.keys() if channel in k][0] signal = mat[key].flatten() return signal def build_dataset(root_dir, window=1024, overlap=0.5): """按窗口切分,返回 X 和 y""" X, y = [], [] label_map = {} # 根据实际文件夹结构填充 for subdir in os.listdir(root_dir): label = label_map.get(subdir) if label is None: continue for fname in os.listdir(os.path.join(root_dir, subdir)): if not fname.endswith('.mat'): continue sig = load_cwru_mat(os.path.join(root_dir, subdir, fname)) step = int(window * (1 - overlap)) for i in range(0, len(sig) - window, step): X.append(sig[i:i+window]) y.append(label) return np.array(X), np.array(y)

窗口长度 1024 是 12kHz 采样下的常用值,对应约 85ms,覆盖了中低频故障特征。重叠 50% 是为了增加样本量,但要注意同一段信号切出的窗口不能同时出现在训练集和验证集,否则精度虚高。我一般按文件划分训练验证,而不是按窗口随机划分。

3.2 VMD 分解与 HO 参数搜索

VMD 的 Python 实现常用vmdpy包。HO 搜索部分这里用粒子群做示例,目标函数用包络熵。

import numpy as np from vmdpy import VMD from scipy.signal import hilbert def envelope_entropy(modal): """计算包络熵,越小说明周期性冲击越明显""" env = np.abs(hilbert(modal)) env = env / (np.sum(env) + 1e-12) return -np.sum(env * np.log(env + 1e-12)) def vmd_objective(signal, K, alpha): """VMD 分解后取最小包络熵作为目标""" u, _, _ = VMD(signal, alpha, 0, K, 0, 1, 1e-7) return min(envelope_entropy(u[i]) for i in range(K)) def pso_search(signal, n_particles=20, n_iter=30): """粒子群搜索 K 和 alpha""" bounds = np.array([[2, 8], [500, 5000]]) pos = np.random.uniform(bounds[:, 0], bounds[:, 1], (n_particles, 2)) vel = np.zeros_like(pos) pbest = pos.copy() pbest_val = np.array([vmd_objective(signal, int(p[0]), p[1]) for p in pos]) gbest = pbest[np.argmin(pbest_val)] gbest_val = np.min(pbest_val) for _ in range(n_iter): r1, r2 = np.random.rand(), np.random.rand() vel = 0.5 * vel + 1.5 * r1 * (pbest - pos) + 1.5 * r2 * (gbest - pos) pos = np.clip(pos + vel, bounds[:, 0], bounds[:, 1]) for i in range(n_particles): val = vmd_objective(signal, int(pos[i, 0]), pos[i, 1]) if val < pbest_val[i]: pbest[i] = pos[i] pbest_val[i] = val if val < gbest_val: gbest = pos[i] gbest_val = val return int(gbest[0]), gbest[1]

粒子群参数里,惯性权重 0.5、学习因子 1.5 是保守设置,收敛慢但不容易早熟。K 取整是因为 VMD 要求模态数为整数。包络熵计算前做了归一化,避免幅值影响。实际跑的时候,VMD 对长信号很慢,建议先取一段 4096 点的信号做参数搜索,再用搜到的参数处理全部数据。

3.3 CNN 模型搭建与训练

选前三个模态拼接成三通道输入,CNN 结构不用太深,三层卷积加全局平均池化就够。

import torch import torch.nn as nn class VMDCNN(nn.Module): def __init__(self, n_classes=10): super().__init__() self.features = nn.Sequential( nn.Conv1d(3, 32, kernel_size=7, padding=3), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size=5, padding=2), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(64, 128, kernel_size=3, padding=1), nn.BatchNorm1d(128), nn.ReLU(), nn.AdaptiveAvgPool1d(1) ) self.classifier = nn.Linear(128, n_classes) def forward(self, x): # x: (batch, 3, window) feat = self.features(x).squeeze(-1) return self.classifier(feat)

输入通道 3 对应选出的三个模态。卷积核从大到小,第一层 7 是为了捕捉冲击的局部波形,后面逐层缩小感受野做抽象。全局平均池化替代全连接,减少参数量,对小样本更友好。训练时学习率 1e-3,Adam 优化器,batch size 32,早停 patience 设 10。如果验证集精度波动大,先把 batch size 降到 16,或者对每个模态做 z-score 归一化。

4. 避坑与排查:HO-VMD-CNN 落地时最容易翻车的五件事

4.1 模态数 K 搜出来是小数或越界

现象:粒子群返回的 K 是 5.7 这种小数,或者跑到边界 8 不动。原因:目标函数里没有对 K 取整,或者搜索范围设得太窄,最优值在边界外。解决:在vmd_objective里对 K 做int()取整,搜索范围根据信号长度调整,一般 K 不超过 8。如果总在边界,把上限放到 10 再试。

4.2 包络熵越搜越小但分类精度不涨

现象:HO 搜索收敛得很好,包络熵从 0.8 降到 0.3,但 CNN 验证精度卡在 70%。原因:包络熵只衡量模态的周期性,不衡量模态之间的冗余。K 太大时,多个模态包络熵都低,但内容重复,CNN 学不到互补特征。解决:在目标函数里加一个惩罚项,比如模态间相关系数超过 0.8 就扣分,或者直接限制 K 不超过 5。

4.3 训练集精度 99% 验证集 60%

现象:典型过拟合。原因:窗口重叠切分导致训练集和验证集有同源样本,或者 CNN 参数量相对样本量太大。解决:按文件划分数据集,确保同一文件的窗口只出现在一个集合。CNN 加 Dropout 或权重衰减,西储大学小样本场景下 weight decay 设 1e-4 通常有效。

4.4 VMD 分解长信号内存爆掉

现象:处理 48kHz 采样、10 秒以上的信号时,VMD 返回的 u 矩阵占几个 G。原因:VMD 内部做频域变换,信号越长矩阵越大。解决:分段处理,每段 4096 到 8192 点,段间重叠 25%,分解完再拼接。或者先降采样到 12kHz 再分解,西储大学故障特征频率一般在 5kHz 以下,降采样不丢关键信息。

4.5 换负载工况后精度断崖下跌

现象:0 马力训练,3 马力测试,精度从 95% 掉到 50%。原因:不同负载下转速变化,故障特征频率偏移,VMD 搜到的 K 和 α 不再最优。解决:要么在每个负载下单独搜参数,要么在训练集里混入多负载样本。工程上更稳的做法是加一个转速归一化步骤,把信号重采样到统一角度域,但这会引入额外复杂度,新手先做多负载混合训练。

5. 把 HO-VMD-CNN 用稳的一个习惯:先看模态再调网络

很多人跑这套流程,习惯是先把 CNN 调到位,再回头调 VMD。我的血泪经验是反过来:VMD 拆出来的模态如果本身看不出周期性冲击,CNN 再深也没用。具体做法是,HO 搜索完参数后,把选出的模态画出来,用包络谱看一眼故障特征频率有没有明显峰值。西储大学内圈故障的 BPFI 在 0 马力下大约 162Hz,外圈 BPFO 大约 107Hz,如果包络谱里这些频率的幅值不突出,说明 VMD 参数还没到位,回去调搜索范围或换目标函数。

验证方法上,我一般留一个“ sanity check ”:取一个已知故障的样本,用搜到的参数分解,看前三个模态的包络谱峰值是否落在理论故障频率附近。这个检查花不了几分钟,但能避免后面几小时的无效训练。CNN 那边,不要一上来就堆层,三层卷积加全局池化在西储大学上足够,精度上不去先查数据划分和归一化,而不是加层。

最后一个习惯:每次实验记录 K、α、包络熵值和验证精度,四个数放一行。跑上十几组之后,你会发现自己对“什么样的 K 和 α 组合容易出好结果”有了直觉,这比任何自动搜索都可靠。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询