简介:这份PDF文献面向机械工程、智能制造与设备运维方向的研究生及工程技术人员,聚焦滚动轴承故障诊断中特征提取困难、数据处理缓慢等痛点,系统梳理了机器学习在该领域的应用路径。全文围绕决策树、随机森林、支持向量机、K近邻与神经网络五种算法展开对比实验,并创新性地仅提取均方根、峰值、crest factor与形状因子四种简单时域特征输入分类模型,最终验证支持向量机诊断效果最优,为后续研究提供了可复用的特征工程与算法选型参考。资源包内含1个PDF文件,约1.42MB,内容完整涵盖摘要、引言、研究方法、实验对比与结论,结构规范,适合作为课题入门、论文写作或算法复现的参考文献。目前已有430人学习下载,读者可从中获取完整的故障诊断流程设计思路、多算法性能对比数据以及简单特征提取的落地方法,对开展轴承状态监测与智能制造相关研究具有直接参考价值。
1. 从一段振动信号说起:滚动轴承故障诊断到底在做什么
一台 7.5kW 的电机,转速 1470 r/min,驱动端轴承是 6205-2RS,你在轴承座上拧一个加速度传感器,采样频率 12 kHz,负载 0 马力。这套配置不是我编的,是凯斯西储大学轴承数据集的标准工况之一,也是绝大多数人做机器学习入门故障诊断时第一个跑通的数据。问题是:拿到一堆.mat文件之后,很多人卡在同一个地方——知道要分类,但不知道该把什么东西喂给模型。
滚动轴承故障诊断这件事,本质上是一条从「物理信号」到「故障标签」的映射链。传感器采到的是时域振动加速度,故障信息藏在冲击成分的周期性里:内圈故障会在转频的整数倍附近出现特征频率,外圈故障的冲击间隔由滚动体通过外圈的频率决定,滚动体故障则更复杂。但原始时域波形信噪比低,直接丢给分类器效果很差,所以中间必须做特征工程——把时域、频域、时频域的统计量抽出来,构成特征向量,再交给机器学习模型做分类。
这套流程适合谁?适合手上有一维振动信号、想做故障分类但不想一上来就搭深度网络的人。传统机器学习路线(SVM、随机森林、KNN、XGBoost)在样本量不大、特征设计合理的情况下,效果不比深度学习差,而且训练快、可解释、调参直观。如果你正在做课程设计、写小论文、或者想先跑通一个 baseline 再决定要不要上 CNN,这条路是性价比最高的起点。
2. 数据到手先别急着建模:CWRU 数据集的读取与标签对齐
2.1 为什么选 CWRU,以及它的目录结构长什么样
凯斯西储大学轴承数据中心公开的数据集,是故障诊断领域被引用最多的公开数据之一。它的实验台由一个 2 马力的电机、一个扭矩传感器和一个测功机组成,故障轴承通过电火花加工人为制造单点损伤,损伤直径分 0.007、0.014、0.021 英寸三档,故障位置分内圈(IR)、外圈(OR)、滚动体(B)三类,加上正常状态(Normal),构成基本分类体系。
下载下来通常是一堆.mat文件,命名类似97.mat、105.mat、130.mat。文件名本身不告诉你标签,需要对照官方文档里的映射表。常见做法是手动整理一张映射表,把文件编号、故障类型、损伤直径、电机负载对应起来。我一般会先写一个字典,把用到的文件编号和标签固定下来,避免后面反复查表。
2.2 用 scipy 读取 .mat 并构造标签
import scipy.io as sio import numpy as np import os # 文件编号到标签的映射,标签用整数编码 # 0=正常 1=内圈 2=外圈 3=滚动体 label_map = { '97': 0, '98': 0, '99': 0, '100': 0, # Normal '105': 1, '106': 1, '107': 1, '108': 1, # IR 0.007 '130': 2, '131': 2, '132': 2, '133': 2, # OR 0.007 '118': 3, '119': 3, '120': 3, '121': 3, # B 0.007 } def load_cwru(data_dir): signals, labels = [], [] for fname in os.listdir(data_dir): if not fname.endswith('.mat'): continue key = fname.split('.')[0] if key not in label_map: continue mat = sio.loadmat(os.path.join(data_dir, fname)) # DE 表示驱动端加速度信号,字段名通常是 Xxxx_DE_time for k in mat: if k.endswith('_DE_time'): sig = mat[k].flatten() signals.append(sig) labels.append(label_map[key]) break return signals, np.array(labels)这段代码做了三件事:遍历目录、按文件名前缀查标签、从.mat字典里取出驱动端信号。_DE_time是驱动端(Drive End)加速度的字段后缀,如果你要用风扇端信号,把后缀换成_FE_time。flatten()是因为.mat读出来是二维列向量,展平成一维方便后续切片。
注意:不同批次的 CWRU 文件字段名可能略有差异,有的文件里字段名带前缀数字,比如
X097_DE_time。用endswith匹配比硬编码字段名更稳。
2.3 滑窗切分:把一条长信号变成样本集
一条信号动辄十几万点,不能整条当一个样本。常见做法是滑窗切分,窗口长度和重叠率是两个关键参数。
def sliding_window(signals, labels, win=1024, step=512): X, y = [], [] for sig, lab in zip(signals, labels): for start in range(0, len(sig) - win, step): X.append(sig[start:start + win]) y.append(lab) return np.array(X), np.array(y)窗口长度 1024 在 12 kHz 采样下对应约 85 ms,覆盖了足够多的冲击周期;步长 512 即 50% 重叠,既能增加样本量,又不至于让相邻样本高度冗余。如果你的采样频率不同,窗口长度要按「至少覆盖 3~5 个故障特征周期」来估。步长太小会导致训练集和测试集高度相关,评估结果虚高,这是新手最容易踩的坑之一。
3. 特征工程:时域、频域、时频域到底该抽哪些量
3.1 时域统计量:最便宜也最容易被低估的一批特征
时域特征是最容易算的,但很多人只算均值和方差就完事,浪费了信息。对轴承故障敏感的有量纲特征包括均方根(RMS)、峰值、峰峰值、偏度、峭度;无量纲特征包括波形因子、峰值因子、脉冲因子、裕度因子、峭度因子。峭度对冲击成分特别敏感,正常轴承峭度接近 3,出现早期故障时会明显上升。
from scipy.stats import kurtosis, skew def time_features(x): rms = np.sqrt(np.mean(x ** 2)) peak = np.max(np.abs(x)) return [ np.mean(x), # 均值 np.std(x), # 标准差 rms, # 均方根 peak, # 峰值 peak / rms, # 峰值因子 kurtosis(x), # 峭度 skew(x), # 偏度 peak / np.mean(np.abs(x)), # 脉冲因子 ]这 8 个量构成一个基础时域特征向量。峰值因子和脉冲因子对早期冲击敏感,但稳定性差;RMS 稳定但灵敏度低。实践中把它们一起放进特征集,让模型自己选。
3.2 频域特征:包络谱比原始频谱更有用
直接对原始信号做 FFT,频谱上往往是转频和它的谐波占主导,故障特征频率容易被淹没。更有效的做法是先做包络解调,再对包络做 FFT,得到包络谱。包络谱上故障特征频率及其谐波会以明显峰值出现。
from scipy.signal import hilbert from scipy.fft import fft, fftfreq def envelope_spectrum(x, fs=12000): analytic = hilbert(x) envelope = np.abs(analytic) envelope = envelope - np.mean(envelope) # 去直流 n = len(envelope) yf = np.abs(fft(envelope))[:n // 2] xf = fftfreq(n, 1 / fs)[:n // 2] return xf, yfhilbert构造解析信号,取模得到包络。去直流这一步不能省,否则包络谱在 0 Hz 处会有一个巨大峰值,把其他成分压得看不见。得到包络谱后,可以抽取特征频率处的幅值、谱峰个数、谱重心等作为特征。
3.3 时频域:小波包能量比作为补充特征
时域和频域特征在变工况下稳定性有限,时频域方法能同时保留时间和频率信息。工程上常用小波包分解,把信号分解到若干频带,计算各频带能量占比。
import pywt def wavelet_packet_energy(x, wavelet='db4', level=3): wp = pywt.WaveletPacket(data=x, wavelet=wavelet, mode='symmetric', maxlevel=level) energies = [] for node in wp.get_level(level, order='natural'): energy = np.sum(node.data ** 2) energies.append(energy) energies = np.array(energies) return energies / (np.sum(energies) + 1e-12) # 归一化为能量比db4是轴承振动分析里常用的基函数,分解层数 3 意味着得到 8 个频带。归一化后的能量比是一个 8 维向量,反映信号能量在不同频带的分布。故障冲击会改变高频段能量占比,这个特征在负载变化时比单纯 RMS 更鲁棒。
提示:小波包分解的层数不是越多越好。层数太高,每个频带太窄,能量比反而对工况敏感。3~4 层是常见选择。
4. 模型选型与训练:从 SVM 到 XGBoost 的落地对比
4.1 为什么先用 SVM 跑 baseline
特征工程做完,特征维度通常在 20~50 之间,样本量几千到几万。这个规模下 SVM 是很好的 baseline:核函数能处理非线性边界,对小样本友好,调参维度低。用 RBF 核,主要调C和gamma两个参数。
from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.model_selection import GridSearchCV pipe = Pipeline([ ('scaler', StandardScaler()), ('svm', SVC(kernel='rbf', probability=True)) ]) param_grid = { 'svm__C': [1, 10, 100], 'svm__gamma': ['scale', 0.01, 0.001] } grid = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy', n_jobs=-1) grid.fit(X_train, y_train) print(grid.best_params_, grid.best_score_)StandardScaler必须放在 Pipeline 里,不能提前对全量数据做标准化,否则测试集信息会泄漏到训练过程。C越大对误分类容忍越低,容易过拟合;gamma越大决策边界越复杂。scale是1/(n_features * X.var()),通常是个合理起点。
4.2 随机森林和 XGBoost:特征重要性可以反哺特征工程
树模型的好处是能输出特征重要性,告诉你哪些特征真正在起作用。如果发现某个频域特征重要性极低,可以考虑删掉,降低维度。
from xgboost import XGBClassifier from sklearn.metrics import classification_report clf = XGBClassifier( n_estimators=300, max_depth=6, learning_rate=0.1, subsample=0.8, colsample_bytree=0.8, objective='multi:softmax', num_class=4, eval_metric='mlogloss' ) clf.fit(X_train, y_train) print(classification_report(y_test, clf.predict(X_test)))subsample和colsample_bytree都设 0.8 是常见的防过拟合配置。max_depth=6对几十维特征足够,再深容易记住训练集噪声。训练完用clf.feature_importances_看排序,如果时域峭度和包络谱特征排在前列,说明特征工程方向对了。
4.3 交叉验证怎么切:别用随机切分骗自己
滑窗切分产生的样本之间高度相关,如果直接train_test_split随机切,相邻窗口可能一个在训练集一个在测试集,评估结果会虚高。正确做法是按原始信号文件分组切分,同一个文件切出来的窗口要么全在训练集,要么全在测试集。
from sklearn.model_selection import GroupKFold groups = np.array(group_ids) # 每个样本对应的原始文件编号 gkf = GroupKFold(n_splits=5) for train_idx, test_idx in gkf.split(X, y, groups): X_train, X_test = X[train_idx], X[test_idx] y_train, y_test = y[train_idx], y[test_idx] # 训练和评估GroupKFold保证同一组的样本不会同时出现在训练和测试集。这一步做了和没做,准确率可能差十几个百分点。我见过太多人随机切分跑出 99% 然后换一组数据就崩掉,血泪经验。
5. 避坑与排查:故障诊断建模里最容易翻车的五件事
5.1 准确率 99% 但换负载就崩
现象:在 0 马力负载数据上训练和测试,准确率接近满分;换成 3 马力负载的数据,准确率掉到 60% 以下。
原因:模型学到的是特定负载下的信号幅值分布,而不是故障本身的特征。负载变化会改变振动幅值,时域特征首当其冲。
解决:训练时混入多种负载的数据,或者使用对幅值不敏感的无量纲特征(峰值因子、峭度、能量比)。更彻底的做法是做工况归一化,比如按 RMS 归一化后再抽特征。
5.2 测试集准确率远高于交叉验证
现象:交叉验证准确率 85%,换一个测试集跑出 97%。
原因:测试集和训练集来自同一段信号,滑窗重叠导致信息泄漏。
解决:按文件分组切分,用GroupKFold。如果已经切好了,检查训练集和测试集的样本是否来自同一原始文件。
5.3 特征数量远大于样本数量
现象:抽了 200 维特征,样本只有 500 个,SVM 训练极慢且效果差。
原因:维度灾难。特征多但样本少,模型容易过拟合。
解决:先做特征选择。用随机森林的feature_importances_排序,保留前 20~30 个;或者用SelectKBest配合f_classif。也可以先做 PCA 降维,但 PCA 后的主成分可解释性差,故障诊断里不太推荐。
5.4 包络谱峰值对不上理论故障频率
现象:算出来的包络谱峰值和理论计算的 BPFO、BPFI 对不上。
原因:要么采样频率设错了,要么理论频率计算公式里的参数(滚动体数、节径、接触角)用错了,要么信号里转频估计不准。
解决:先确认采样频率和轴承型号参数。6205-2RS 的滚动体数是 9,节径约 39.04 mm,接触角 0。用这些参数算 BPFO 和 BPFI,再和包络谱峰值对照。如果偏差在 1% 以内,说明对上了;偏差大就检查参数。
5.5 模型在正常样本上误报率高
现象:故障样本识别率很高,但正常样本经常被误判为故障。
原因:正常和故障样本数量不平衡,模型偏向多数类;或者正常样本的某些特征分布和故障样本重叠。
解决:用class_weight='balanced'或 SMOTE 过采样。另外检查正常样本里是否混入了异常工况(比如启停阶段),这些样本的特征和故障样本相似,应该剔除。
6. 进阶技巧:用特征重要性反推物理机理,再回头改特征
跑通 baseline 之后,真正拉开差距的不是换更复杂的模型,而是用模型反馈去改特征。我一般会做一件事:把 XGBoost 的特征重要性排序打印出来,对照物理机理看哪些特征排在前列、哪些排在末尾。
import pandas as pd feat_names = ['mean', 'std', 'rms', 'peak', 'crest', 'kurt', 'skew', 'impulse'] + \ [f'env_{i}' for i in range(10)] + \ [f'wp_{i}' for i in range(8)] importance = pd.Series(clf.feature_importances_, index=feat_names) print(importance.sort_values(ascending=False).head(15))如果排在前列的是峭度、峰值因子、包络谱某几个频带能量,说明冲击成分和调制特征确实被捕捉到了。如果排在前列的是均值、标准差这种对工况敏感的量,就要警惕模型可能学到了负载信息而不是故障信息。这时候可以做一个验证:把同一故障在不同负载下的样本混在一起,看模型还能不能分对。如果分不对,说明特征对工况的鲁棒性不够,需要引入工况归一化或者用对抗训练的思路剥离工况信息。
另一个技巧是看混淆矩阵。内圈故障和外圈故障容易混,因为两者的冲击特征在时域上相似,区别主要在频域的调制模式。如果混淆严重,可以针对性地增加包络谱在 BPFI 和 BPFO 附近的特征,比如取特征频率前 3 阶谐波的幅值比。
| 特征类型 | 对负载敏感度 | 对故障敏感度 | 建议 |
|---|---|---|---|
| 时域有量纲(RMS、峰值) | 高 | 中 | 配合归一化使用 |
| 时域无量纲(峭度、峰值因子) | 低 | 高 | 优先保留 |
| 包络谱特征频率幅值 | 中 | 高 | 取谐波幅值比更稳 |
| 小波包能量比 | 低 | 中 | 作为补充特征 |
最后说一个我自己的习惯:每次跑完模型,不只看准确率,一定把混淆矩阵和特征重要性一起看。准确率是黑匣子,混淆矩阵告诉你错在哪,特征重要性告诉你为什么错。这两样东西结合起来,才能从「调包」变成「做诊断」。希望帮到你。
本文还有配套的精品资源,点击获取