简介:这份资源面向机械工程研究人员、设备维护与故障诊断工程师及智能诊断方向学者,围绕滚动轴承故障诊断这一工业场景,给出基于改进灰狼优化算法(IGWO)与支持向量机(SVM)的完整研究方案。内容涵盖EEMD信号分解、峭度与相关系数筛选敏感IMF分量重构、时域频域特征提取、PCA降维,以及用IGWO优化SVM参数构建诊断模型,并在凯斯西储大学轴承数据集上验证准确性与稳定性。资源包为1个PDF文件,约482KB,内含论文理论分析与可运行代码及逐段解释,便于读者理解方法细节并复现实验流程。目前已有77人学习下载,适合希望将智能优化与机器学习结合应用于设备状态监测的读者参考,也可作为相关课题复现与工程实践的技术资料。
1. 从一段振动信号说起:这套 IGWO-SVM 故障诊断代码到底能干什么
轴承故障诊断这件事,真正上手过的人都知道,难的不是“知道要用 SVM”,而是信号里那点冲击成分被噪声和转频调制埋得死死的,特征提不准,后面分类器再花哨也是白搭。这份资源给了一条完整链路:EEMD 分解振动信号、PCA 降维挑主成分、再用改进灰狼优化算法(IGWO)去搜 SVM 的惩罚因子 C 和核参数 g,最后输出故障类别。它不是一篇纯理论推导,而是带详细代码和逐行解释的工程实现,适合手里有 CWRU 或类似轴承数据集、想跑通“特征提取 + 参数寻优 + 分类”全流程的人。如果你之前用网格搜索调 SVM 调到怀疑人生,或者 EEMD 分解完不知道 IMF 怎么选,这套东西能直接拿来改。
2. EEMD+PCA 特征工程:把振动信号变成 SVM 能吃的向量
2.1 为什么是 EEMD 而不是直接上原始信号
滚动轴承故障的振动信号本质上是非平稳、非线性调制的,内圈、外圈、滚动体故障对应的冲击重复频率不同,但原始时域波形里这些冲击往往被转频和噪声淹没。直接拿原始序列丢给 SVM,维度高、冗余大,分类边界根本画不出来。常见做法是先做时频分解,把信号拆成若干本征模态函数(IMF),再从每个 IMF 里提统计量。
EEMD 是在 EMD 基础上加白噪声辅助的改进版,解决 EMD 的模态混叠问题。它的逻辑是:往原始信号里多次加入不同幅值的白噪声,每次做 EMD,最后对多组 IMF 取平均。白噪声把原本可能混在一起的频率成分“打散”到不同尺度上,平均之后噪声抵消,模态更干净。代价是计算量成倍增加,但轴承信号长度通常几千到几万点,工程上完全扛得住。
这份代码里 EEMD 的关键参数是集成次数 NE 和噪声幅值 ratio。NE 一般取 50~100,ratio 取 0.2 左右。NE 太小,平均效果不够,模态混叠还在;NE 太大,跑一次要几分钟,调参阶段很拖节奏。我一般先用 NE=50 快速验证,确定 IMF 数量后再加到 100 出最终结果。
# EEMD 分解核心调用(基于 PyEMD 库) from PyEMD import EEMD import numpy as np def eemd_decompose(signal, NE=50, noise_ratio=0.2): """ signal: 一维振动信号 NE: 集成次数,越大越稳但越慢 noise_ratio: 添加白噪声幅值比例 返回: IMF 矩阵,形状 (n_imfs, len(signal)) """ eemd = EEMD() eemd.trials = NE eemd.noise_width = noise_ratio imfs = eemd.eemd(signal) return imfs逻辑说明:eemd.eemd()返回的是二维数组,每一行是一个 IMF,按频率从高到低排列。前几个 IMF 通常对应高频冲击成分,后面几个对应低频趋势。参数noise_width直接决定加入噪声的强度,设太小起不到打散作用,设太大残留噪声会污染 IMF。
2.2 IMF 筛选与统计特征构造
分解完不是所有 IMF 都有用。轴承故障的冲击特征主要集中在前几个高频 IMF 里,后面的低频 IMF 往往是转频或趋势项,带进去反而增加噪声。常见做法是计算每个 IMF 与原始信号的相关系数,或者用峭度值筛选——峭度对冲击成分敏感,峭度大的 IMF 优先保留。
这份代码里用的是相关系数法:算每个 IMF 和原始信号的皮尔逊相关系数,设一个阈值(比如 0.1),低于阈值的丢弃。然后对保留的 IMF 分别提取时域统计量:均方根、峭度、偏度、峰值因子、裕度因子。每个 IMF 提 5 个特征,假设保留 6 个 IMF,就是 30 维特征向量。
from scipy.stats import kurtosis, skew from scipy.signal import find_peaks def extract_features(imfs, corr_threshold=0.1): """ imfs: EEMD 分解得到的 IMF 矩阵 corr_threshold: 相关系数筛选阈值 返回: 特征向量 (n_selected_imfs * 5,) """ features = [] for imf in imfs: # 相关系数筛选 corr = np.corrcoef(imf, imfs.sum(axis=0))[0, 1] if abs(corr) < corr_threshold: continue # 时域统计量 rms = np.sqrt(np.mean(imf**2)) kurt = kurtosis(imf) sk = skew(imf) peak = np.max(np.abs(imf)) crest = peak / rms if rms > 0 else 0 features.extend([rms, kurt, sk, peak, crest]) return np.array(features)参数说明:corr_threshold设 0.1 是经验值,设太高会丢掉有用 IMF,设太低会引入噪声分量。峭度和峰值因子对早期微弱故障最敏感,均方根反映能量水平,偏度反映波形不对称性。这五个量组合起来,基本能覆盖轴承不同故障类型的特征差异。
2.3 PCA 降维:去掉冗余,留下主成分
30 维特征里肯定有相关性高的,比如均方根和峰值因子都反映幅值信息。PCA 的作用是把这些相关特征投影到正交空间,按方差贡献率排序,取前几个主成分。这份代码里保留累计贡献率 95% 的主成分,通常 30 维会降到 8~12 维。
PCA 之前必须做标准化,否则量纲大的特征(比如峰值)会主导方差方向。标准化用 z-score:减均值除标准差。注意标准化参数只能从训练集算,然后应用到测试集,不然就是数据泄露。
from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA def pca_reduce(X_train, X_test, var_keep=0.95): """ X_train, X_test: 原始特征矩阵 var_keep: 累计方差贡献率阈值 返回: 降维后的训练/测试特征,以及 PCA 对象 """ scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 用训练集的均值和方差 pca = PCA(n_components=var_keep) X_train_pca = pca.fit_transform(X_train_scaled) X_test_pca = pca.transform(X_test_scaled) return X_train_pca, X_test_pca, pca逻辑说明:fit_transform只在训练集上做,测试集用transform,这是避免数据泄露的铁律。n_components=0.95表示自动选择保留 95% 方差所需的主成分数,不用手动指定维度。跑完可以看pca.explained_variance_ratio_确认前几个主成分贡献了多少。
3. IGWO 优化 SVM:为什么不能继续用网格搜索
3.1 SVM 参数对轴承分类的影响到底有多大
SVM 做轴承故障分类,核心参数就两个:惩罚因子 C 和核函数参数 g(RBF 核的 gamma)。C 控制对误分类的容忍度,C 越大越不允许错分,但容易过拟合;C 太小则欠拟合,分类边界模糊。g 控制核函数的宽度,g 太大决策边界过于复杂,g 太小则过于平滑,区分不开不同故障类型。
网格搜索是最笨但最稳的办法:给 C 和 g 各设一组候选值,笛卡尔积组合,每组训练一次 SVM 看交叉验证准确率。问题是计算量随候选值数量指数增长。如果 C 取 10 个值、g 取 10 个值,就是 100 次训练;每次训练还要做 5 折交叉验证,就是 500 次。轴承数据集样本量几百到几千,跑一轮可能十几分钟,调参阶段根本等不起。
灰狼优化算法(GWO)是群智能算法的一种,模拟灰狼群体的等级制度和狩猎行为。它把每只狼的位置当作一组 (C, g) 候选解,通过 Alpha、Beta、Delta 三只头狼引导整个群体向最优解移动。相比网格搜索,GWO 不需要遍历所有组合,通常 20~30 只狼迭代 50~100 次就能收敛到不错的解。
3.2 标准 GWO 的毛病和 IGWO 的改进点
标准 GWO 有两个问题。第一,初始种群随机生成,如果运气不好全落在差区域,收敛慢甚至早熟。第二,收敛因子 a 线性递减,前期探索和后期开发的切换不够灵活,容易陷入局部最优。
这份代码里的 IGWO 做了两处改进。一是用混沌映射初始化种群,常见的是 Logistic 映射或 Tent 映射,让初始解在搜索空间里分布更均匀,避免随机扎堆。二是把线性收敛因子改成非线性递减,前期 a 下降慢一点,多探索;后期下降快一点,加速收敛到最优解附近。
import numpy as np def igwo_optimize(X_train, y_train, X_val, y_val, n_wolves=20, max_iter=50, lb=[0.01, 0.01], ub=[100, 100]): """ X_train, y_train: 训练集 X_val, y_val: 验证集,用于计算适应度 n_wolves: 狼群规模 max_iter: 最大迭代次数 lb, ub: C 和 g 的搜索下界/上界 返回: 最优 C, g 和收敛曲线 """ dim = 2 # 混沌初始化(Logistic 映射) chaos = np.random.rand(n_wolves, dim) for i in range(1, n_wolves): chaos[i] = 4 * chaos[i-1] * (1 - chaos[i-1]) positions = lb + chaos * (ub - lb) alpha_pos, alpha_score = None, float('inf') beta_pos, beta_score = None, float('inf') delta_pos, delta_score = None, float('inf') convergence = [] for t in range(max_iter): for i in range(n_wolves): C, g = positions[i] # 用验证集准确率的负值作为适应度(越小越好) clf = SVC(C=C, gamma=g, kernel='rbf') clf.fit(X_train, y_train) score = 1 - clf.score(X_val, y_val) if score < alpha_score: delta_score, delta_pos = beta_score, beta_pos beta_score, beta_pos = alpha_score, alpha_pos alpha_score, alpha_pos = score, positions[i].copy() elif score < beta_score: delta_score, delta_pos = beta_score, beta_pos beta_score, beta_pos = score, positions[i].copy() elif score < delta_score: delta_score, delta_pos = score, positions[i].copy() # 非线性收敛因子 a = 2 - 2 * (t / max_iter) ** 2 for i in range(n_wolves): for d in range(dim): r1, r2 = np.random.rand(), np.random.rand() A1 = 2 * a * r1 - a C1 = 2 * r2 D_alpha = abs(C1 * alpha_pos[d] - positions[i][d]) X1 = alpha_pos[d] - A1 * D_alpha r1, r2 = np.random.rand(), np.random.rand() A2 = 2 * a * r1 - a C2 = 2 * r2 D_beta = abs(C2 * beta_pos[d] - positions[i][d]) X2 = beta_pos[d] - A2 * D_beta r1, r2 = np.random.rand(), np.random.rand() A3 = 2 * a * r1 - a C3 = 2 * r2 D_delta = abs(C3 * delta_pos[d] - positions[i][d]) X3 = delta_pos[d] - A3 * D_delta positions[i][d] = (X1 + X2 + X3) / 3 convergence.append(alpha_score) return alpha_pos[0], alpha_pos[1], convergence逻辑说明:适应度用验证集错误率,因为训练集准确率会过拟合,验证集更能反映泛化能力。混沌初始化那段用 Logistic 映射生成分布更均匀的初始位置。非线性收敛因子a = 2 - 2*(t/max_iter)**2让前期 a 下降慢、后期下降快,平衡探索和开发。三只头狼的位置更新公式是标准 GWO 的核心,每只狼根据 Alpha、Beta、Delta 的位置加权移动。
参数说明:n_wolves设 20 够用,设太大计算量线性增长;max_iter设 50~100,看收敛曲线是否平稳;lb和ub是 C 和 g 的搜索范围,C 一般 0.01~100,g 一般 0.01~100,具体看数据尺度。如果特征做过标准化,g 的有效范围会偏小,可以适当调低上界。
3.3 用优化后的参数训练最终 SVM 并评估
IGWO 跑完拿到最优 C 和 g,用这两个参数在完整训练集上训练 SVM,然后在测试集上评估。评估不能只看准确率,轴承故障诊断里各类样本可能不均衡,要看混淆矩阵和各类的召回率。
from sklearn.svm import SVC from sklearn.metrics import classification_report, confusion_matrix def train_final_svm(X_train, y_train, X_test, y_test, best_C, best_g): clf = SVC(C=best_C, gamma=best_g, kernel='rbf') clf.fit(X_train, y_train) y_pred = clf.predict(X_test) print(confusion_matrix(y_test, y_pred)) print(classification_report(y_test, y_pred)) return clf逻辑说明:classification_report会输出每类的精确率、召回率和 F1,比单一准确率更有参考价值。如果某类召回率明显低,说明该类特征区分度不够,可能要回去检查 EEMD 的 IMF 筛选或补充特征。
4. 避坑与排查:跑这套代码最容易翻车的五个地方
4.1 EEMD 分解结果每次不一样
现象:同样的信号跑两次 EEMD,IMF 数量和波形有差异。原因:EEMD 内部加的是随机白噪声,没有固定随机种子。解决:在调用 EEMD 之前设np.random.seed(42),或者用eemd.noise_seed参数固定种子。如果做对比实验,种子必须固定,否则结果不可复现。
4.2 PCA 降维后准确率反而下降
现象:不做 PCA 时 SVM 准确率 92%,做完 PCA 降到 85%。原因:PCA 是无监督的,它保留方差大的方向,但方差大不等于分类判别力强。有些故障区分信息在方差小的维度里,被 PCA 丢掉了。解决:不要盲目用 PCA,先看不做降维的基线准确率。如果降维后掉点,改用 LDA 或者基于互信息的特征选择,这些是有监督的,会考虑类别标签。
4.3 IGWO 收敛曲线震荡不收敛
现象:迭代 50 次,适应度上下跳,没有明显下降趋势。原因:狼群规模太小或搜索范围设得太宽,狼群在巨大空间里乱飞。解决:先把n_wolves加到 30,max_iter加到 100;同时缩小lb和ub,比如 C 限制在 0.1~50,g 限制在 0.001~10。如果还不行,检查适应度函数是不是用了训练集准确率——必须用验证集。
4.4 测试集准确率远低于验证集
现象:IGWO 优化时验证集准确率 95%,最终测试集只有 80%。原因:过拟合。IGWO 在验证集上反复优化,相当于在验证集上“调参”,验证集信息泄露到了参数选择里。解决:把数据分成训练集、验证集、测试集三份,IGWO 用验证集,最终评估用测试集。如果数据量小,用交叉验证代替固定验证集,但要注意交叉验证的折数不能太少。
4.5 SVM 训练报错“类标签必须从 0 开始”
现象:SVC.fit()报错,提示标签不是连续的整数。原因:轴承故障标签可能是 1、2、3 或字符串,SVM 要求标签从 0 开始连续整数。解决:用LabelEncoder把标签转成 0 到 n-1 的整数,训练完再用inverse_transform转回来。这个坑很小但很常见,尤其是从 CSV 读标签的时候。
5. 进阶技巧:用交叉验证 + 多随机种子把结果钉死
这套代码跑通之后,真正要拿去做对比实验或者写论文,单次运行的结果说服力不够。我的习惯是:IGWO 优化阶段用 5 折交叉验证代替固定验证集,每折跑一次 IGWO,取平均适应度作为该组参数的得分。这样能避免验证集划分的偶然性。
具体做法是把训练集分成 5 份,每次拿 4 份训练、1 份验证,循环 5 次。IGWO 的每只狼对应一组 (C, g),适应度是 5 折验证错误率的均值。这样每只狼要训练 5 次 SVM,计算量是原来的 5 倍,但结果稳得多。
from sklearn.model_selection import StratifiedKFold def fitness_cv(C, g, X, y, n_folds=5): """5 折交叉验证错误率作为适应度""" skf = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=42) errors = [] for train_idx, val_idx in skf.split(X, y): clf = SVC(C=C, gamma=g, kernel='rbf') clf.fit(X[train_idx], y[train_idx]) errors.append(1 - clf.score(X[val_idx], y[val_idx])) return np.mean(errors)另一个技巧是固定多个随机种子跑 10 次完整流程,记录每次的测试集准确率,报告均值和标准差。如果标准差超过 2%,说明流程不稳定,要回去检查 EEMD 种子、IGWO 初始化和数据划分。我一般会跑 10 次,去掉最高最低各 2 次,取中间 6 次的均值作为最终结果。这样即使审稿人要求重复实验,数据也拿得出手。
还有一点:IGWO 的收敛曲线要保存下来,横轴迭代次数、纵轴适应度,画出来看是否单调下降。如果曲线在最后几次还在明显下降,说明max_iter不够,要加大。如果曲线早早平了但准确率不高,说明狼群多样性不够,要增大n_wolves或调整混沌初始化。
从那以后我每次跑群智能优化算法,都强制先画收敛曲线、再跑多随机种子、最后用交叉验证兜底,这三步少一步都不敢把结果写进报告。希望帮到你。
本文还有配套的精品资源,点击获取