CWRU轴承数据集故障诊断实战:Python读取、特征提取与机器学习建模
发布时间:2026/10/14 9:30:57 锦皓数字建站

简介凯斯西储大学CWRU轴承数据集是机械故障诊断领域广泛使用的经典公开数据集面向设备健康监测与智能诊断研究人员。资源收录了正常状态及内圈、外圈、滚珠故障涵盖不同故障直径和多个测点位置的振动信号并配有整理好的Python程序、使用说明文本及预处理脚本。借助配套程序用户可完成从原始数据读取、噪声去除、滤波标准化到特征提取的全流程进而利用支持向量机、随机森林或神经网络等方法构建故障分类模型并进行交叉验证与性能评估。压缩包共172个文件其中165个mat格式为原始振动数据3个py脚本负责数据整理与特征提取2个txt文件说明使用方法和数据细节2个pyc为辅助模块整体大小约230.77MB。目前已有24538人学习适合机械、自动化专业师生及工程技术人员用于故障诊断算法验证、特征工程实践和模型训练。1. 凯斯西储轴承数据集故障诊断入门的第一份标准考题做设备健康管理和故障诊断的工程师练手数据大概率都绕不开凯斯西储大学CWRU的轴承数据集。它被很多人叫作故障诊断领域的 MNIST一份数据里同时覆盖正常、内圈、外圈、滚动体四种状态还按 0 到 3 hp 的负载和 0.007/0.014/0.021 英寸三种故障直径做了组合采样率有 12 kHz 和 48 kHz 两档。这意味着你在论文里看到的绝大多数诊断方法都能在这份数据上先跑通。本文会按实际动手的顺序把数据格式解析、Python 读取、特征提取、分类建模和常见坑完整走一遍新手可以照着复现熟手也能在参数和边界上找到对照。2. 数据格式与文件命名先弄懂 .mat 里的编号和采样参数再动手CWRU 数据以 .mat 格式提供每个文件是一段实验记录。很多初学者上来就直接loadmat一把梭结果发现变量名对不上、数据维度看不懂问题基本都出在没先搞清楚文件命名规则和信号的物理含义上。2.1 文件编号数字里藏着负载和故障类型文件名里的数字不是乱编的常见整理版本里编号与工况存在固定对应。以驱动端加速度计数据为例不同负载对应同一类故障的连续编号段故障直径决定编号区间。整段数据里能提取到的核心映射如下文件编号段故障类型故障直径英寸负载hp97–100正常无0–3105–108内圈0.0070–3118–121滚动体0.0070–3130–133外圈6 点钟方向0.0070–3169–172内圈0.0140–3197–200外圈6 点钟方向0.0140–3209–212内圈0.0210–3每个故障直径对应 4 个连续编号最后一个数字表示负载档位0 代表 0 hp空载1 代表 1 hp2 代表 2 hp3 代表 3 hp。所以你看到105.mat时第一反应应该是「内圈 0.007 英寸、0 hp 负载」。需要强调不同来源打包的编号可能存在差异以上映射以官网整理版本和多数公开教程的共识为准。拿到数据后第一件事是用代码把 .mat 内部变量名校验一遍确认它和文件名编号描述一致再进入后续处理。2.2 采样参数12 kHz 与 48 kHz 怎么选原始实验里有两套采样配置驱动端和风扇端加速度计通常以 12 kHz 采样部分驱动端实验用 48 kHz。这个参数直接决定你后续算特征频率时的分辨率和计算量选错会得出完全错误的频谱峰值位置。负载和转速的关系也是固定的电机实际转速随负载增加而下降0 hp 时约 1797 rpm1 hp 时约 1772 rpm2 hp 时约 1750 rpm3 hp 时约 1730 rpm。换算成转频就是除以 60空载约 29.95 Hz满载约 28.83 Hz。后面做包络谱找故障特征频率时这些数值是判断峰值是否匹配的基准。还有一点整个官网数据量不小单份 .mat 文件在 12 kHz 下大约包含 48 万个采样点约 10 秒48 kHz 下更是超过 120 万点。做滑窗分段时这个长度决定了你能切出多少训练样本也决定了特征提取的耗时量级。建议先用 12 kHz 的驱动端数据跑通流程再扩展其他工况。3. Python 读取与可视化从 .mat 到 numpy 数组的完整代码这一章解决最实际的问题把 .mat 文件变成能直接运算的 numpy 数组并画出时域波形和包络谱。代码用 Python 3 配合 scipy、numpy、matplotlib 三件套实验环境用 Jupyter 或 VS Code 都行。3.1 加载代码与结构体解包CWRU 的 .mat 文件结构并不统一有的文件直接存变量有的套了一层结构体数组变量名格式通常是X105_DE_time、X105_FE_time、X105_BA_time这类分别代表驱动端、风扇端和基座加速度计信号。写一个通用的解包函数import scipy.io as sio import numpy as np def load_cwru_mat(path): 读取 CWRU 的 .mat 文件兼容直接数组和结构体数组两种格式 raw sio.loadmat(path) # 过滤掉 __header__、__version__、__globals__ 等元数据键 keys [k for k in raw.keys() if not k.startswith(__)] out {} for k in keys: data raw[k] # 判断是否为结构体数组dtype.names 非空表示带字段 if isinstance(data, np.ndarray) and data.dtype.names: for name in data.dtype.names: arr data[name][0, 0] out[name] np.squeeze(arr) else: out[k] np.squeeze(data) return out data load_cwru_mat(105.mat) de_signal data[X105_DE_time] print(de_signal.shape, de_signal.dtype)这段代码先尝试直接取变量如果发现是结构体数组就遍历字段名把内部数组取出来做squeeze降维。这样无论官方原版还是网友二次整理的打包方式都能稳定拿到一维信号。3.2 时域波形先看幅值和冲击拿到信号后别急着算特征先画波形眼睛能看出很多问题。比如内圈故障的波形里会出现周期性冲击正常信号的幅值则平稳得多。import matplotlib.pyplot as plt fs 12000 # 采样率确认该文件是 12 kHz 实验 t np.arange(len(de_signal)) / fs plt.figure(figsize(12, 4)) plt.plot(t[:12000], de_signal[:12000]) # 只画前 1 秒 plt.xlabel(Time (s)) plt.ylabel(Amplitude (g)) plt.title(DE signal, 1s slice) plt.grid(True) plt.show()注意这里只取前 12000 个点画图也就是 1 秒窗口。全画出来 48 万点会糊成一片看不出冲击特征。波形图上如果能看到间隔均匀的尖峰先粗算一下尖峰间隔后面用包络谱验证。3.3 包络谱故障特征频率的计算与验证时域波形只能定性看定量判断靠包络谱。原理是对信号先带通滤波再用 Hilbert 变换取包络最后对包络做 FFT。这样能把高频冲击解调成低频的故障特征频率。from scipy import signal def envelope_spectrum(x, fs, bp_low500, bp_high3500): 带通滤波 Hilbert 包络 FFT返回频率轴和包络谱 # 带通滤波可以滤掉低频干扰和高频噪声突出冲击成分 b, a signal.butter(4, [bp_low / (fs / 2), bp_high / (fs / 2)], bandpass) x_f signal.filtfilt(b, a, x) # Hilbert 变换求包络 env np.abs(signal.hilbert(x_f)) # 对包络做 FFT freq np.fft.rfftfreq(len(env), 1 / fs) spec np.abs(np.fft.rfft(env)) return freq, spec freq, spec envelope_spectrum(de_signal, fs) plt.figure(figsize(12, 4)) plt.plot(freq[:2000], spec[:2000]) # 只画 0-2000 Hz 区间 plt.xlabel(Frequency (Hz)) plt.ylabel(Amplitude) plt.title(Envelope Spectrum) plt.grid(True) plt.show()判断峰值时要对照轴承参数。CWRU 用的 SKF 6205 深沟球轴承常见特征频率系数如下故障位置特征频率系数乘以转频内圈故障BPFI约 5.42外圈故障BPFO约 3.57滚动体故障BSF约 2.38以 3 hp 负载约 28.83 Hz 转频计算内圈故障基频约 156 Hz外圈故障基频约 103 Hz。在包络谱的对应位置找到明显峰值才能判断这个文件确实是内圈故障而不是文件名标错了。4. 特征提取与多分类构建一个能跑的故障诊断基线波形和频谱都能看了接下来解决工程里真正的问题把一段信号变成一条特征向量然后交给分类器输出故障类型。这一章会给出一个能直接跑出结果的完整流程特征不用多高级但切分和评估方式必须正确。4.1 滑窗分段把长信号切成训练样本一条 CWRU 信号有几十万点不能整条丢进模型。常见做法是滑窗分段窗口长度要覆盖至少几个故障冲击周期通常取 2048 到 4096 点。12 kHz 下 2048 点约 0.17 秒足够包含多次冲击。def make_samples(signal, label, win2048, step1024): 滑窗切分返回样本矩阵和标签数组 samples, labels [], [] for start in range(0, len(signal) - win, step): samples.append(signal[start:start win]) labels.append(label) return np.vstack(samples), np.array(labels) # 示例0正常, 1内圈, 2滚动体, 3外圈 normal load_cwru_mat(97.mat)[X097_DE_time] inner load_cwru_mat(105.mat)[X105_DE_time] X0, y0 make_samples(normal, 0) X1, y1 make_samples(inner, 1) print(X0.shape, X1.shape)步长设成窗口的一半1024时相邻窗口有 50% 重叠样本量翻倍但对特征区分度提升有限反而可能因为重叠导致数据泄漏。如果只是做分类基线窗口设 2048、步长也设 2048 不重叠样本量也够用。4.2 时域与频域特征一组能区分四种状态的特征每个窗口都要转成固定长度的特征向量。工程里常用的特征组合是时域统计量加频带能量计算成本低效果稳定。from scipy import stats def extract_features(x, fs12000): 从一段波形中提取时域和频域特征 feats {} feats[mean] np.mean(x) feats[std] np.std(x) feats[peak] np.max(np.abs(x)) feats[rms] np.sqrt(np.mean(x**2)) feats[kurtosis] stats.kurtosis(x) # 峭度对冲击敏感 feats[skewness] stats.skew(x) fetures stats.skew(x) feats[skewness] fetures # 频带能量把功率谱分成 4 段每段能量占比 freq, psd signal.welch(x, fs, nperseg256) total np.sum(psd) 1e-12 for i, band in enumerate(np.array_split(psd, 4)): feats[fband_{i}] np.sum(band) / total return feats # 把每个窗口转成特征向量 def build_feature_matrix(samples): rows [] for s in samples: rows.append(list(extract_features(s).values())) return np.array(rows) F0 build_feature_matrix(X0) F1 build_feature_matrix(X1) print(F0.shape, F1.shape)峭度对轴承冲击非常敏感正常信号的峭度接近 3出现故障后明显增大。频带能量则刻画了频谱形态不同故障的能量分布差异明显。这两个特征是区分度最高的建议在分析结果里重点看它们的取值。4.3 训练与评估随机森林做多分类切分方式决定结果可信度特征矩阵准备好后用随机森林做多分类是最稳的基线方案。但这里有一个关键陷阱不能把窗口随机打乱后切训练集和测试集因为同一个 .mat 文件里相邻窗口高度相关随机切分会导致测试集里混着训练集的近邻样本准确率虚高到 99% 以上换到新数据立刻崩掉。from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report from sklearn.model_selection import GroupShuffleSplit # 假设 all_X 是所有样本特征all_y 是对应标签groups 是文件编号 groups np.concatenate([ np.full(len(F0), 0), # 来自 97.mat np.full(len(F1), 1), # 来自 105.mat ]) all_X np.vstack([F0, F1]) all_y np.concatenate([y0, y1]) # 按文件分组切分同一个文件的所有窗口只能出现在同一侧 gss GroupShuffleSplit(n_splits1, test_size0.3, random_state42) train_idx, test_idx next(gss.split(all_X, all_y, groups)) clf RandomForestClassifier(n_estimators200, random_state42) clf.fit(all_X[train_idx], all_y[train_idx]) pred clf.predict(all_X[test_idx]) print(classification_report(all_y[test_idx], pred))GroupShuffleSplit保证同一个文件的所有窗口不会被拆到两侧。判断一个模型靠不靠谱看它按文件切分后的测试准确率而不是随机切分的结果。如果训练集和测试集来自不同负载比如用 0 hp 训练、3 hp 测试准确率会明显下降这才是真实场景下的诊断性能。5. 避坑与常见问题五个让新手翻车的真实场景CWRU 数据集看着简单实际跑起来翻车点非常多。下面五条是我在复现和教学过程中遇到频率最高的每一条都按现象、原因、解决三个层次说明。5.1 读取报错scipy.io.loadmat 提示不支持文件格式现象loadmat直接抛异常提示KeyError或无法解析但文件确认是 .mat 后缀。原因这份 .mat 是 MATLAB v7.3 格式内部用 HDF5 存储scipy 的loadmat只支持 v5 及更早版本。解决用h5py读取HDF5 格式的变量结构是引用关系需要额外处理import h5py def load_mat_v73(path): 读取 v7.3 格式的 CWRU mat 文件 with h5py.File(path, r) as f: for key in f.keys(): # v7.3 里数据以引用形式存储需要取出再转置 data np.array(f[key]) # CWRU 信号通常是 1 x N转置成一维 if data.ndim 2 and data.shape[0] 1: data data.squeeze() print(key, data.shape)判断文件属于哪种格式最快的方法用文本编辑器打开 .mat 文件开头出现HDF5字样就是 v7.3出现MATLAB 5.0就是老格式。5.2 变量名对不上不同打包版本的字段名不一致现象代码按X105_DE_time取变量结果KeyError。原因网上流传的 CWRU 数据有多个整理版本有的变量名去掉了time后缀有的把DE改成了d甚至同一文件内同时存在X105_DE_time和X105_DE两个数组。解决先打印load_cwru_mat返回的字典键名确认真实字段后再写死变量名或者写一个按关键词匹配的兜底逻辑比如字段名包含DE就认为是驱动端信号。5.3 外圈故障的负载方向标记现象有人发现130.mat和某个外圈故障样本的频谱峰值位置不一致怀疑数据有问题。原因CWRU 的外圈故障数据里标注了故障位置相对于负载区的角度常见的是 6 点钟、3 点钟和 12 点钟方向。不同角度下冲击的传递路径不同包络谱幅值有差异但特征频率不变。解决外圈故障文件先确认对照表里标注的方向做分类时把不同方向当成同一类没问题但做迁移学习时要注意训练集和测试集的方向分布是否一致。5.4 数据泄漏热切分准确率虚高现象随机切分后准确率 99.8%但换成按负载跨域测试后掉到 85%怀疑模型不行。原因滑窗重叠加上随机切分训练和测试样本高度相关模型记忆了窗口邻域的形态而不是真正的故障模式。解决按 4.3 的方法用GroupShuffleSplit按 .mat 文件分组切分更严格的做法是按负载切分用 0 hp 数据训练、1–3 hp 数据测试检验模型在不同工况下的泛化能力。5.5 采样率用错特征频率漂移现象包络谱峰值出现在 70 Hz对照特征频率计算却对不上。原因把 48 kHz 采集的文件用 12 kHz 的采样率去做频率轴所有频率都缩小到真实值的四分之一峰值位置完全错位。解决每个 .mat 文件在读取时把采样率作为元数据一起维护不要全局写死。加载后先检查信号长度48 kHz 的文件 10 秒信号长度约 48 万点12 kHz 约 12 万点长度也能反推采样率。6. 进阶验证把 CWRU 流程迁移到自己的实验台CWRU 数据跑通后真正的考验是把这套流程迁移到自己的实验台或现场设备数据上。我常用的做法是先做一次「跨域盲测」用 CWRU 全部数据按负载分组训练模型然后把你自采的一段已知故障信号丢进去。自采信号和 CWRU 的采样率、传感器安装位置都不一样如果直接分类准确率很低这不代表流程错了而是说明特征分布发生了偏移。一个实用技巧是先从包络谱入手做迁移适配。自采数据先算包络谱找到故障特征频率的峰值位置确认它和 SKF 6205或你实际轴承型号的理论特征频率对得上再去做特征提取和分类。这一步能过滤掉一大半传感器松动、转速波动导致的问题。如果包络谱里根本找不到对应峰值先回头检查轴承参数是否填错或者故障是否已经严重到信号形态发生变化。另一个值得做的验证是对比实验同一组样本一路直接提取时域特征分类另一路先做包络谱再取频谱峰值附近的能量特征对比两组准确率。实际测试中包络谱预处理在低信噪比数据上的优势很明显这是因为包络谱把故障信息集中到了低频段削弱了转速波动和背景噪声的影响。从那以后我每次拿到新的采集数据都强制先跑一遍包络谱对照特征频率再谈建模这个习惯帮我少走了很多弯路希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。