VMD+排列熵+ELM:小样本轴承故障诊断实战
2026/9/23 14:48:57 网站建设 项目流程

简介:这份资源面向机械故障诊断方向的研究生、工程师及算法初学者,提供一套基于VMD排列熵与ELM的滚动轴承故障诊断Python实现方案,帮助读者快速搭建从振动信号分解、特征提取到状态分类的完整流程。压缩包共407个文件,以404个txt振动信号样本为主,另含2个py脚本与1个csv特征数据,整体约4.8MB,结构紧凑便于直接运行与二次开发。其中py脚本分别完成VMD分解与排列熵计算、ELM分类训练,txt文件对应不同工况下的轴承振动数据,csv则用于特征汇总。目前已有633人学习下载,适合希望理解变分模态分解、排列熵特征构造及极限学习机分类的读者参考,可据此复现故障识别实验、对比不同特征组合效果,并在此基础上迁移到其他旋转机械的健康监测任务中。

1. 从一段振动信号说起:VMD、排列熵和ELM到底怎么串起来做轴承诊断

手里只有一段轴承振动信号,怎么判断它到底是内圈裂了、外圈剥落了,还是滚动体出了问题?很多人第一反应是上深度学习,但真到产线边上,样本少、标注贵、算力紧,训练一个几十层的网络往往不划算。我后来稳定用的一套组合是:先用 VMD 变分模态分解把原始振动信号拆成若干本征模态分量,再对每个分量算排列熵 PE 作为特征,最后喂给 ELM 极限学习机做分类。整条链路在 Python 里几十行就能跑通,训练阶段几乎不耗时,特别适合小样本、要快速迭代的故障诊断场景。

这套方案解决的核心问题是:把非平稳、噪声重的振动信号,转成维度低、区分度高、且对样本量不敏感的特征向量。适合谁?适合手上有一台试验台、采了几组正常和故障数据、想快速验证诊断思路的工程师,也适合做课程设计或论文复现的同学。它不追求 SOTA 精度,追求的是可复现、可解释、能在普通笔记本上跑完。下面我按“信号怎么拆、特征怎么提、分类器怎么用、坑在哪”的顺序讲透。

2. VMD 变分模态分解:把一段混叠振动拆成干净分量

2.1 为什么选 VMD 而不是 EMD

轴承振动信号在故障冲击下是非平稳的,直接做 FFT 只能看到全局频谱,冲击发生的时刻和周期被抹掉了。经验模态分解 EMD 曾经是主流,但它有两个老毛病:模态混叠和端点效应,分解出来的 IMF 经常一个分量里混着好几个频段,后面算熵就失真了。VMD 变分模态分解的思路完全不同,它把分解写成一个变分优化问题,假设每个模态是围绕中心频率的窄带信号,通过交替方向乘子法迭代求解,把各模态的中心频率和带宽同时约束住。

结果就是 VMD 出来的分量频带更干净,模态混叠明显减轻,中心频率也稳定。代价是要预先指定模态个数 K 和惩罚因子 alpha,这两个参数选不好,要么过分解要么欠分解。我的经验是:轴承故障特征频率通常集中在几个频段,K 取 3 到 6 之间先试,alpha 默认 2000 起步,再根据分量中心频率是否分散来微调。

2.2 用 Python 跑通 VMD 的最小代码

网上 vmd 的 Python 实现有好几个版本,我一般用 vmdpy 这个包,接口清晰。先装依赖:

pip install vmdpy numpy scipy scikit-learn matplotlib

然后是最小可运行示例,输入一段一维振动信号:

import numpy as np from vmdpy import VMD # signal: 一维振动信号,长度建议 >= 1024 # alpha: 带宽约束,默认 2000 # tau: 噪声容限,0 表示严格保数据 # K: 模态个数 # DC: 是否含直流分量,振动信号一般 False # init: 中心频率初始化,1 表示均匀分布 # tol: 收敛容差 u, u_hat, omega = VMD(signal, alpha=2000, tau=0, K=4, DC=False, init=1, tol=1e-7) # u 形状为 (K, N),每一行是一个模态分量 print(u.shape) # (4, len(signal))

逻辑说明:VMD 函数返回三个东西,u 是时域模态分量,u_hat 是频域谱,omega 是各模态中心频率。真正拿来做特征的是 u,每一行代表一个分解出来的分量。参数说明:alpha 越大,每个模态带宽越窄,容易把有用信息切碎;alpha 越小,带宽越宽,模态之间容易串。K 是最关键的,K 太小会把故障冲击和背景噪声混在一个分量里,K 太大则会出现中心频率相近的冗余分量。我一般先画 omega,看中心频率有没有挤在一起,挤了就减 K。

2.3 K 和 alpha 怎么定:一个可操作的判断流程

参数没有万能值,但有一套稳定的试法。第一步,固定 alpha=2000,K 从 3 试到 6,每次记录 omega。第二步,看相邻中心频率的间隔,如果两个模态中心频率差不到信号采样率的 5%,说明过分解,减 K。第三步,对每个分量算峭度,轴承故障冲击会让某个分量的峭度明显偏高,如果所有分量峭度都差不多,说明分解没抓住冲击,回去调 alpha 或检查信号本身。

提示:VMD 对信号长度敏感,太短的信号(比如少于 512 点)分解不稳定,建议先做分段或补零到 1024 以上再分解。

3. 排列熵 PE:把每个模态分量压成一个数

3.1 排列熵为什么适合振动信号

分解完得到 K 个分量,如果直接把整段波形当特征,维度太高,而且对时间平移敏感。排列熵 PE 的好处是:它只看信号里相邻数值的大小排列模式,对幅值变化不敏感,对突变和复杂度敏感,计算量还小。轴承正常时振动相对规则,排列熵偏低;出现故障冲击后,信号复杂度上升,排列熵升高。所以每个模态分量算一个 PE 值,K 个分量就得到 K 维特征向量,维度低、物理意义清楚。

PE 的核心参数是嵌入维数 m 和延迟时间 tau。m 一般取 3 到 7,太小区分度不够,太大计算量涨且对噪声敏感。tau 通常取 1,因为振动信号采样率高,相邻点已经足够。我常用 m=5、tau=1,这个组合在轴承数据上比较稳。

3.2 排列熵的 Python 实现与参数

import numpy as np from math import factorial def permutation_entropy(x, m=5, tau=1): n = len(x) # 构造所有长度为 m 的嵌入向量 idx = np.arange(m) * tau patterns = [] for i in range(n - (m - 1) * tau): seg = x[i + idx] # 用 argsort 得到排列模式 order = tuple(np.argsort(seg)) patterns.append(order) # 统计每种排列出现概率 from collections import Counter counts = Counter(patterns) probs = np.array([c / len(patterns) for c in counts.values()]) # 归一化排列熵 pe = -np.sum(probs * np.log(probs)) / np.log(factorial(m)) return pe # 对 VMD 的每个分量算 PE features = np.array([permutation_entropy(u[k], m=5, tau=1) for k in range(u.shape[0])]) print(features) # 长度等于 K 的特征向量

逻辑说明:先按嵌入维数 m 和延迟 tau 把信号切成重叠片段,每段用 argsort 得到大小排列模式,统计各模式频率,再算香农熵并除以 log(m!) 归一化到 0 到 1。参数说明:m 越大,能区分的排列模式越多,但需要的数据点也越多,经验上信号长度至少要是 m! 的几倍才稳定。tau 大于 1 会引入跳点,振动信号一般不需要。归一化很重要,否则不同 m 下的 PE 没法比较。

3.3 特征向量的组织方式

K 个 PE 值组成一个 K 维向量,这就是一条样本的特征。实际做的时候,我会把正常、内圈故障、外圈故障、滚动体故障四类各采若干段,每段都走一遍 VMD+PE,最后拼成一个矩阵,行是样本、列是特征。这里有个细节:不同样本的 VMD 分解是独立做的,所以每个样本的 K 个分量顺序可能不一致,但 PE 值本身不依赖分量顺序,直接按行拼就行。如果担心顺序问题,可以对 PE 值排序后再拼,我试过,对 ELM 分类结果影响很小。

4. ELM 极限学习机:小样本下的快速分类器

4.1 ELM 相比 SVM 和 BP 的取舍

特征有了,接下来是分类。SVM 在小样本上表现好,但核函数和惩罚系数调起来费劲;BP 神经网络要反复迭代,样本少容易过拟合。ELM 的思路是:隐层权重和偏置随机生成后不再调整,只求输出层权重,而输出层权重可以通过一次最小二乘解析求解。这意味着训练阶段没有迭代,速度极快,而且随机隐层在高维特征上反而有不错的泛化。

代价是 ELM 对隐层节点数敏感,节点太少欠拟合,太多过拟合,而且随机初始化带来结果波动。我的做法是:隐层节点数取特征维度的 5 到 20 倍之间试,多跑几次取平均,或者固定随机种子保证可复现。

4.2 用 Python 实现 ELM 并完成分类

import numpy as np from sklearn.preprocessing import OneHotEncoder from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score class ELM: def __init__(self, n_hidden=100, activation='sigmoid', seed=42): self.n_hidden = n_hidden self.activation = activation self.seed = seed def _act(self, x): if self.activation == 'sigmoid': return 1 / (1 + np.exp(-x)) return np.maximum(0, x) # relu def fit(self, X, y): rng = np.random.RandomState(self.seed) n_samples, n_features = X.shape # 随机输入权重和偏置,训练中不再改变 self.W = rng.randn(n_features, self.n_hidden) self.b = rng.randn(self.n_hidden) H = self._act(X @ self.W + self.b) # 输出层权重解析解,加正则提升数值稳定性 self.beta = np.linalg.pinv(H.T @ H + 1e-6 * np.eye(self.n_hidden)) @ H.T @ y def predict(self, X): H = self._act(X @ self.W + self.b) return H @ self.beta # X: 样本特征矩阵 (n_samples, K) # y: one-hot 标签 (n_samples, n_classes) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) elm = ELM(n_hidden=80, activation='sigmoid', seed=42) elm.fit(X_train, y_train) y_pred = elm.predict(X_test) acc = accuracy_score(np.argmax(y_test, 1), np.argmax(y_pred, 1)) print('accuracy:', acc)

逻辑说明:fit 里随机生成输入权重 W 和偏置 b 后固定,计算隐层输出 H,再用伪逆求输出权重 beta。predict 用同一套 W、b 算 H,乘 beta 得到输出。参数说明:n_hidden 是隐层节点数,我一般从 50 起试到 200;activation 用 sigmoid 或 relu 都行,sigmoid 更平滑;正则项 1e-6 是防止 H.T@H 奇异,样本极少时可以调大。seed 固定后结果可复现,这点在写论文或做对比实验时很重要。

4.3 训练集划分与结果评估的注意点

轴承数据往往同一类样本来自连续采集,如果随机划分训练测试集,相邻样本高度相似,测试精度会虚高。更严谨的做法是按时间段划分:前 70% 时间做训练,后 30% 做测试。另外类别要不均衡就分层抽样。评估别只看准确率,混淆矩阵能看出哪两类容易混,比如内圈和外圈在早期故障时 PE 特征接近,混淆矩阵会暴露这个问题。

5. 避坑与排查:这套链路最容易翻车的五个地方

5.1 现象:VMD 分解后所有分量看起来都差不多

原因:alpha 太小或 K 太大,模态带宽过宽导致分量之间高度相关。解决:先把 alpha 提到 2000 以上,K 降到 3 或 4,画各分量频谱确认中心频率是否分开。如果还是混,检查信号是否做了去均值,直流分量会干扰分解。

5.2 现象:排列熵值全部接近 1,区分不开故障

原因:m 太小或信号噪声太大,排列模式趋于随机。解决:把 m 提到 5 或 6,先对分量做一次平滑或带通滤波再算 PE。另外确认信号长度足够,长度不足 m! 的几倍时 PE 估计偏差大。

5.3 现象:ELM 训练精度很高但测试精度崩了

原因:隐层节点过多导致过拟合,或者训练测试集随机划分造成数据泄漏。解决:减 n_hidden,加正则,改用按时间划分。我踩过这个坑,随机划分下测试集精度 99%,按时间划分掉到 85%,后者才是真实水平。

5.4 现象:每次跑 ELM 结果都不一样

原因:输入权重随机初始化。解决:固定 seed,或者跑 10 次取平均并报告标准差。如果标准差很大,说明特征区分度不够,回去优化 VMD 参数或 PE 参数,而不是怪 ELM。

5.5 现象:换一台设备数据,整套参数全失效

原因:采样率、转速、故障特征频率都变了,VMD 的 K 和 PE 的 m 需要重新标定。解决:把 K 和 m 当作可调超参,换数据后先做一轮小网格搜索,别指望一套参数打天下。这是血泪经验,我早期直接套用旧参数,结果诊断全错。

6. 进阶:把 PE 换成多尺度排列熵,以及怎么验证特征真的有用

基础版每个分量只算一个 PE,信息量有限。进阶做法是多尺度排列熵 MPE:对每个分量先做粗粒化,得到不同时间尺度序列,每个尺度算一个 PE,拼起来特征维度变成 K 乘尺度数。这样能同时捕捉高频冲击和低频调制信息,对早期微弱故障更敏感。代价是计算量涨,且尺度数太多会引入冗余。我一般取尺度 1 到 10,再对特征做一次方差筛选或 PCA,把维度压回来。

验证特征有没有用,别只看最终准确率。我习惯做两件事:一是画特征散点图,用前两个主成分看四类样本是否分得开,分不开说明特征不行,换分类器也白搭;二是做消融,分别用原始信号直接分类、只用 VMD 分量能量分类、用 PE 分类,对比准确率,确认 PE 确实带来了增益。下面这个小表是我在某次试验台上的对比,供参考:

特征方案特征维度ELM 测试准确率
原始信号统计量672%
VMD 分量能量481%
VMD + 排列熵493%
VMD + 多尺度排列熵4095%

可以看到 PE 的贡献很明显,多尺度再涨一点但维度翻了十倍,是否值得看你的算力预算。最后说个习惯:我每次做完都会把 VMD 的 omega、PE 参数、ELM 的 seed 和 n_hidden 记在一个配置字典里,连同数据划分方式一起存下来。这套链路参数多,不记录的话两周后自己都复现不出来。希望帮到你。

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

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

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

立即咨询