☰
CWRU轴承数据集整理与Python程序使用全攻略
2026/10/13 19:49:46 网站建设 项目流程

简介:凯斯西储大学(CWRU)轴承数据集是机械故障诊断领域的经典公开资源,面向故障诊断研究人员、设备维护工程师及机器学习学习者,用于滚动轴承内环、外环、滚珠等典型故障的模式识别与状态监测。压缩包共172个文件,以165个MAT格式振动信号数据为主,涵盖多种驱动端转速和故障直径下的正常与故障样本;另含3个Python数据整理分析脚本及2个TXT使用说明,可帮助用户完成数据加载、信号预处理和特征提取,规避下载与处理中的常见问题。资源整体约230.77MB,目录结构清晰,数据与脚本配套,便于直接开展对比实验;结合说明文档可快速理解数据组织方式。目前已有24538人学习下载,是入门轴承故障诊断、验证机器学习或深度学习分类算法时值得参考的高质量基准数据集。

1. CWRU轴承数据集是故障诊断入门的第一站:先看清它到底给你什么

做设备故障诊断或者学生研究旋转机械,绕不开一个公开数据集——凯斯西储大学(CWRU)轴承数据中心发布的振动数据。它把电机驱动端和风扇端的加速度计信号按故障类型、故障直径、转速和负载组织成文件,想验证算法、跑模型、写论文,很多人第一站就是它。可等你真的把数据包下载下来,会发现里面是一堆编号的 .mat 文件,点名和文档对不上,Python 读进来全是字典键,标签得自己建。这篇文章照着标题里“数据包+整理Python程序+使用说明”三个部分,讲清楚这个数据集怎么读、怎么整理成训练样本、参数怎么设、哪些地方最容易翻车。

2. 认识数据包结构:文件名编码、通道与负载是三个绕不开的门槛

CWRU 数据包网上有多个整理版本,常见结构是:根目录下按故障类型分子文件夹,或者直接摊开放一批 .mat 文件。不管哪种,第一步都不是写代码,而是把文件名和通道搞明白。否则整理出来的标签是错乱的,模型训得再准也没有工程意义。

2.1 文件名里的编码规则:DE、FE、BA和故障直径怎么读

CWRU 原始数据的常见命名方式是数字编号,比如105.mat、118.mat、130.mat,后面还有0.007、0.014、0.021这类直径描述。理解编号规则可以从官方说明文档里的表格入手,但更实用的是记住故障类型和直径之间的关系。

常见的故障代码格式有两种。一种是纯编号:105.mat对应内圈故障 0.007 英寸、负载 0 hp;118.mat对应外圈故障 0.007 英寸、负载 1 hp。另一种是带文字后缀的:IR007_0、OR014@6_2、B021_1,含义很直白,IR 是 inner race(内圈),OR 是 outer race(外圈),B 是 ball(滚动体),后面数字前三位是故障直径,@6表示外圈故障点所在负荷区的位置角度。整理程序里我一般会优先解析这种后缀,因为它直接告诉你标签。

读 mat 文件时,文件内部变量名也有约定。常见的有DE_time(驱动端加速度计)、FE_time(风扇端)、BA_time(基座加速度计),各个通道都是一维数组。所以标签信息只存在文件名里,信号本身不会自带故障类型。这一步要建立一个映射表,把每个文件名对应的故障类型、故障直径、负载、转速都收录进去。我的习惯是建一个字典,手动维护这些映射,而不是靠字符串切片,因为编号文件名的规律在不同版本里不完全一致。

2.2 12k与48k采样频率、驱动端与风扇端通道怎么选

数据包里的采样频率常见两档:12 kHz 和 48 kHz。驱动端轴承数据大多用 12k,实际文件命名里如果没写,可以从数组长度和记录时长反推。一个 10 秒的 12k 采样信号长度是 120000 个点左右,48k 则是 480000 个点左右。你先看DE_time的长度除以 2,基本就能确认采样频率档次。

选通道要看你的应用场景。做驱动端轴承故障诊断,优先用DE_time,因为故障冲击传递路径短,信噪比高;风扇端FE_time适合做负载传递研究或者想对比多个通道融合的场景。BA_time也不是没用,放在基座上能感知更全局的振动,但少了故障点的近场冲击特征,很多论文不推荐它做主要输入。

我在实际项目里会把 DE 和 FE 都导出来,分别存成独立文件,后续做通道融合或者单通道对比都方便。整理程序里不要只挑一个通道写死,留个参数channel让人切。常见做法是:

channel_map = { 'DE': 'DE_time', 'FE': 'FE_time', 'BA': 'BA_time', }

这样别人用你的整理程序时,改一个字符串就能切换通道,不用在代码里找半天哪里在赋值。

2.3 数据包里的文件夹、mat与csv格式:先盘点再动手

官方发布的数据最初以 mat 文件为主,后来社区也整理过 csv 版本。你拿到的“数据包”可能是三种情况:纯 mat 文件、mat 加 csv 混合、带 Excel 索引表。开始写程序之前,先跑一个文件清单:

find /path/to/cwru -name "*.mat" | sort | head -20

这样能快速看到命名风格。不同格式读取路径不一样,mat 用scipy.io.loadmat,csv 用pandas.read_csv,如果 mat 是 v7.3 格式还得走h5py。所以数据包整理程序的第一步不是处理信号,而是自动识别文件格式和版本,否则第一次跑就崩溃。

我用过的典型数据包里有三类文件:正常轴承数据97.mat这种不带故障的,故障数据带 IR/OR/B 后缀的,以及文档README。整理时把正常数据单独放一个文件夹,避免标签映射时混淆。把所有文件按名称排序后,打印前 10 个文件名和对应的标签,人眼确认一遍再往下走,这是血泪经验:标签错位的问题绝大多数出在排序规则和真实文件顺序不一致上。

3. 用Python把.mat数据整理成故障诊断标准格式:一个可复用的脚本思路

理解了命名规则,就可以写整理程序了。这一章不是给你一个黑匣子脚本,而是把每一步逻辑拆开,方便你按自己的数据包版本调整。

3.1 环境准备与依赖库:scipy、numpy、pandas的安装

读 mat 文件至少需要scipy和numpy,整理标签和最后存成规范数据集则用到pandas。环境干净的话直接装:

pip install numpy scipy pandas

如果是在国内网络环境,建议加-i https://pypi.tuna.tsinghua.edu.cn/simple,这个镜像比默认源快很多。装完后在 Python 里验证一下:

import scipy.io import numpy as np print(scipy.__version__) print(np.__version__)

这里有个容易踩的坑:scipy版本太旧可能不支持某些带压缩标志的 mat 文件。我的建议是scipy>=1.8.0,如果读取报ValueError: Unknown mat file version,说明要么文件损坏,要么碰到 v7.3 格式,后者要用h5py解决。具体排查放后面的避坑章节,这里先准备好环境就行。

3.2 按文件批量读取mat:从单一文件到完整数据集

以最常见的DE_time为例,先读一个文件:

from scipy.io import loadmat import numpy as np def load_single_mat(filepath, channel='DE'): mat = loadmat(filepath) key = f'X{filepath.stem}_{channel}_time' # 例如 X105_DE_time if key in mat: data = mat[key].flatten() else: # 兼容不同命名风格 candidates = [k for k in mat.keys() if channel in k and 'time' in k] if not candidates: raise KeyError(f'{filepath} 中找不到通道 {channel}') data = mat[candidates[0]].flatten() return data

这里filepath.stem是文件名去掉.mat后的部分,比如105。CWRU 原始 mat 的内部变量名很多是X105_DE_time这种格式,所以用变量名拼接最直接。但有些版本的文件变量名不带前缀,或者通道名是DriveEnd,这时候candidates那段兜底逻辑就起作用了。

批量读取时要注意:所有 mat 文件里通道的键名不一定统一。我会先在数据包根目录里随机抽三个文件,打印loadmat(...).keys()看看有没有共同规律,再决定用哪种解析。不要一上来就全量跑,否则遇到命名冲突报错会浪费一整轮时间。

批量循环一般这样写:

from pathlib import Path import pandas as pd data_root = Path('CWRU') files = sorted(data_root.glob('*.mat')) records = [] for f in files: de = load_single_mat(f, channel='DE') fe = load_single_mat(f, channel='FE') label = parse_label(f.name) # 从文件名提取故障类型 records.append({'file': f.name, 'label': label, 'de': de, 'fe': fe})

records直接放进内存可能很占空间,12k 采样下每个文件十几万点,几十个文件也就几百 MB,现代机器没问题。如果你处理的是 48k 和多个通道,一次性 load 全部会爆内存,那就改成边读边切窗边写盘,不要把所有原始信号都囤在列表里。

3.3 切窗与标签映射:把连续信号变成监督学习样本

原始信号是一维长序列,不能直接喂给 CNN 或者 Transformer。常见做法是滑窗切片,每个窗口是一个样本。窗口长度要覆盖至少一个机械旋转周期,转速 1797 rpm 时周期约 0.033 秒,对应 12k 采样就是约 400 个点,所以 1024 点是安全的;如果转速更低,建议用 2048 点。

切窗代码如下:

def sliding_window(data, window_len=1024, overlap=0.5): step = int(window_len * (1 - overlap)) n = len(data) if step <= 0: raise ValueError('overlap 必须小于 1') windows = [] for start in range(0, n - window_len + 1, step): windows.append(data[start:start + window_len]) return np.array(windows)

参数说明:overlap=0.5表示相邻窗口重叠 50%,这是故障诊断里比较常见的配置,既能增加样本量,又不至于让相邻窗口过于相似导致过拟合。如果样本量不够,可以提升到 0.75;如果追求独立样本,用重叠 0。切完窗后,每个窗口打上同样的标签:

def make_samples(data, label, window_len, overlap): windows = sliding_window(data, window_len, overlap) labels = np.full(len(windows), label, dtype=np.int32) return windows, labels

标签映射建议用整数常量,而不是字符串,方便模型计算交叉熵:

LABEL_MAP = {'normal': 0, 'inner': 1, 'outer': 2, 'ball': 3}

这里有个细节:CWRU 的原始文档里经典分类任务有时用三种故障加上正常,共四类。如果你想做更细的“故障直径 + 位置”分类,标签数会变成几十个,整理程序要支持从文件名解析出直径和负载。我的建议是先用四类跑通流程,再慢慢扩充细粒度标签。

3.4 训练集/测试集划分与数据增强:别把同源窗口切进两边

样本切好后,最容易犯的错误是直接train_test_split(X, y, test_size=0.2)。因为相邻窗口来自同一段信号,前后相关性极强,随机划分会让测试集里满是训练集的“近亲”,验证准确率虚高。正确做法是按文件分组划分,每个 mat 文件的所有窗口要么全进训练集,要么全进测试集。

代码可以这么写:

from sklearn.model_selection import GroupShuffleSplit groups = np.array([i for i, _ in enumerate(files) for _ in range(num_windows_per_file[i])]) # 更标准的方式是把每个样本所属的原始文件编号存起来 splitter = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42) train_idx, test_idx = next(splitter.split(X, y, groups=groups))

这里groups必须对应每个样本来自哪个文件。如果不做分组,准确率可能从 90% 直接虚高到 99% 以上,等到了实机数据就露馅。数据增强方面,常见做法是加一点高斯噪声或对窗口做小幅平移,但 CWRU 数据本身比较干净,我的经验是先不加增,等模型在测试集上稳定后再评估噪声鲁棒性,否则会把增强当成本文主题之外的一个大坑。

4. 使用说明里的关键设定:采样点数、重叠率、转速与故障直径怎么配

数据包自带的“使用说明”往往会写一些默认参数,但很多人直接照抄,最后发现结果和论文对不上。这里把真正影响结果的几个参数单独拎出来讲。

4.1 常用的1024/2048点窗长与重叠率选择

窗长和重叠率一起决定了样本量和每条样本包含的故障冲击次数。CWRU 的转速在 1730 到 1797 rpm 之间,故障特征频率通常不超过 300 Hz。以 1024 点窗长、12k 采样为例,每个窗口时长约 85 ms,约 2.5 个旋转周期,足够看到至少两次外圈故障冲击。如果窗长降到 512 点(42 ms),一个旋转周期都覆盖不全,故障特征容易被削弱。

常见参数组合如下表:

场景窗长重叠率样本时长建议
简单四分类102450%85ms首选
细粒度直径分类204875%170ms保证每窗3个以上周期
实时性要求高51250%42ms可能损失特征
深度模型训练204850%170ms样本量够就选

重叠率 50% 时,样本量大概为原始信号长度除以 512。一个 12k 采样、10 秒的信号能切出约 230 个窗口,四类数据几十个文件就上万样本,够一个小型 CNN 用了。如果你觉得样本量不够,拉高重叠率到 75% 样本量翻倍,但要小心模型记住窗口边界。

4.2 不同负载下转速变化对切窗的影响

CWRU 数据包里负载分为 0、1、2、3 hp,对应的转速分别是 1797、1772、1750、1730 rpm。转速变化直接影响故障特征频率,但不会剧烈影响一维振动波形的基本形态。切窗时如果能按文件记录转速,比如 105.mat 对应 1797 rpm,最好把转速和故障直径一起写入标签表,后续做可视化或计算特征频率时直接查表。

有一个常被忽略的点:不同负载下同一故障直径的信号幅值范围不同。比如 0 hp 下信号幅值偏小,3 hp 下幅值明显变大。如果切窗后不归一化,模型可能学到的是“负载差异”而不是“故障差异”。所以整理出的每个窗口建议做 z-score 标准化,而不是 min-max 到 [0,1]。z-score 能保留振动波形的相对形状,min-max 会被极端冲击点带偏。标准做法:

def zscore_window(win): mean = win.mean() std = win.std() if std < 1e-8: return win - mean return (win - mean) / std

4.3 数据标准化与归一化:哪种方法适合CNN/Transformer

对于 CNN 输入,z-score 后数据均值接近 0,标准差接近 1,卷积核初始化更容易收敛。对于 Transformer,输入是 1D 序列,同样用 z-score,因为 self-attention 对幅值敏感,如果输入特征范围差异太大,注意力权重会被某一维主导。CWRU 数据整理程序里我默认先做 z-score,存成 numpy 数组,等训练时再在Dataset类里按需调整。

如果做频域分析,标准化对象应该是幅值谱或者功率谱,而不是时域窗。程序里我会把时域 z-score 和频域标准化分开写,不要让一个函数同时处理两个域。一个简单的频域标准化是除以频谱的总能量,得到归一化频谱。使用说明文档里如果提到了滤波,比如带通 1kHz 到 10kHz,也属于预处理的一部分,但这一项不是必须的,CWRU 数据本身带比较干净的振动信号,直接用原始波形效果也可。

5. CWRU数据集避坑指南:四个常见的翻车现场与排查方法

整理 CWRU 数据的过程中,有几个坎几乎每个人都会踩。这里写四条最典型的,每条按现象、原因、排查的思路来。

5.1 mat文件版本太新导致读取失败

现象:scipy.io.loadmat报告ValueError: Unknown mat file version,或者读出来的数组 dtype 不对,全是mat73对象。

原因:CWRU 后期整理的数据包可能用 MATLAB R2015 以上版本导出,存成了 v7.3 格式(基于 HDF5),普通loadmat不支持。早期的 v5 格式才可以直接读取。

解决:先判断格式。v7.3 文件头包含 HDF5 的魔数,用h5py读取:

import h5py def load_mat_v73(filepath, key='DE_time'): with h5py.File(filepath, 'r') as f: data = f[key][()] return data.flatten()

读取后注意维度顺序,h5py 读出的数组通常需要转置,特别是多通道时。我的习惯是把 v73 数据转成 float32,再转成一维数组。整理程序里可以先用二进制读前 8 个字节判断版本,自动选择loadmat还是h5py,这样整个数据包就不会因为一个文件中断。

5.2 标签错位:文件名解析时的排序陷阱

现象:训练出来的混淆矩阵里,内圈故障的样本被预测成外圈,而且比例很均匀,不像随机错误。

原因:批量读取时用glob('*.mat')返回的文件列表顺序和文件名自然排序不一致,比如file10.mat排在file2.mat前面,导致按文件顺序赋标签时错位。

解决:按文件名排序时使用自然排序,不要用sorted(str)的字典序。用 Python 的natsort库:

pip install natsort
from natsort import natsorted files = natsorted(data_root.glob('*.mat'))

排序后打印一次文件名和对应标签映射表,人工核对前 20 行。尤其是带@6、@3这类位置后缀的外圈故障,文件名相似度很高,最容易看漏。

5.3 同源泄漏:训练测试集划分后准确率虚高

现象:测试集准确率 99% 以上,换成自己采集的轴承数据就只有 60%,怀疑模型泛化能力差。

原因:切窗后随机划分数据集,同一个 mat 文件的相邻窗口同时出现在训练集和测试集,信息高度重合。振动信号是连续时序,相邻窗口波形相似度可能超过 0.9,测试集相当于背答案。

解决:严格按文件划分,或者按不同的连续时间段划分。更稳妥的做法是:每个 mat 文件切窗后,把窗口按原始时间顺序分成三段,每段分给不同集合,从时间段层面避免泄漏。代码上就是上面 3.4 节里用GroupShuffleSplit,并把group设为文件名索引。

5.4 外圈故障的“静止载荷区”被切窗漏掉

现象:外圈故障分类准确率比内圈和滚动体低,尤其在某些负载下,外圈故障窗口混入正常样本。

原因:CWRU 外圈故障时,如果滚动体经过非承载区,冲击特征不明显;而且外圈故障位置相对负载区固定,有的 mat 文件里的信号包含大量冲击较弱的时间段。切窗后你不看波形分布,就会把冲击少的窗口当正常。

解决:切窗外圈故障时,先对整段信号做包络谱或均方根统计,挑出冲击明显的区间再切窗。具体做法是按小窗口计算 RMS,指数滑窗定位高能量段,只在高能量段上切。这个策略能显著提升外圈故障的分类效果。如果你发现某个外圈文件切出的窗 RMS 分布有两个峰,说明该文件混合了承载区和静止载荷区的信号,建议把低能量峰的部分舍弃或者单独标注。

6. 最后一步:验证你的整理程序是否正确的一个小技巧(用频谱校验)

整理完数据,不要急着训模型。先用一个简单的方法验证标签和信号是否匹配——计算轴承故障特征频率,看频谱峰值是否出现在理论位置。CWRU 数据有公开的轴承参数:驱动端轴承节径约 1.537 英寸,滚动体直径约 0.3125 英寸,滚子数 9,接触角 0。这些参数在官方文档里能查到,用它们和转速算出内外圈特征频率,和 FFT 谱峰值对照。

以 1797 rpm 为例,轴频约 29.95 Hz。外圈故障特征频率 BPFO 约 3.048 倍轴频,也就是 91.2 Hz;内圈故障 BPFI 约 5.048 倍轴频,约 151 Hz。程序里可以这样验证单个窗口:

def verify_by_fft(data, fs, target_freq): spectrum = np.fft.rfft(data) freqs = np.fft.rfftfreq(len(data), 1/fs) peak_idx = np.argmax(np.abs(spectrum[10:])) + 10 peak_freq = freqs[peak_idx] print(f'峰值频率: {peak_freq:.2f} Hz, 理论: {target_freq:.2f} Hz')

实际操作中,故障特征频率不一定落在幅值最大处,因为转速波动和转频谐波干扰,所以更稳的是去看目标频率附近 3Hz 范围内有没有明显峰。如果内圈标签的窗口,理论频率附近一点谱峰都没有,那大概率是标签映射错了。这个验证过程不用跑模型,几十秒就能在全部文件上扫一遍,是整理程序的一个强保险。

我自己每次整理完 CWRU 数据集,都会先把每个故障类别的第一个窗口画出来,做一次 RMS 和包络谱检查,再跑 FFT 对照特征频率。这个习惯帮我避免过至少三次标签错位的返工。希望这个技巧对你有用,也让你后续用 Python 折腾 CWRU 数据时少走一点弯路。

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

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

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

立即咨询