CWRU轴承振动信号导入包:从.mat到训练张量的完整指南
简介这份资源是面向机械故障诊断与信号处理方向的科研人员和学生准备的CWRU轴承振动信号导入包用于快速加载并分析凯斯西储大学经典轴承数据集支撑故障检测与健康监测实验。压缩包共6个文件约6KB以Python脚本为主辅以打包配置、许可协议和说明文档其中核心模块提供多变量振动数据的读取与组织方式便于直接接入后续的特征提取与分类流程。已有260人学习下载适合刚接触CWRU数据集、希望省去数据解析环节的研究者。借助该包读者可以跳过繁琐的数据格式整理把精力集中在滤波降噪、谱分析、峭度等特征计算以及异常检测与故障识别算法的验证上也能以此为基线复现和对比不同诊断方法对工业设备维护与健康管理研究具有实用参考价值。1. 拿到 CWRU 轴承振动信号导入包先搞清楚它到底解决什么问题很多人第一次接触 CWRU 轴承数据集卡住的地方不是算法而是数据读不进来。凯斯西储大学轴承数据中心的原始文件是 .mat 格式里面嵌套着 DE、FE、BA 多通道结构体采样频率还分 12kHz 和 48kHz 两套故障直径从 0.007 到 0.040 英寸不等。你兴冲冲下载完打开 MATLAB 一看变量名一堆乱码直接懵了。所谓 CWRU 轴承振动信号导入包本质上就是把这堆原始 .mat 文件规范化成统一数组或 DataFrame 的中间层工具让你不用每次写重复的解析逻辑。它适合三类人刚入门做轴承故障诊断的学生、需要快速搭 baseline 的算法工程师、以及想把 CWRU 当验证集但不想被数据格式拖住的研究者。这一章先把导入包要解决的边界讲清楚后面再动手。2. CWRU 数据集的目录结构与导入包的设计取舍2.1 原始 .mat 文件里到底存了什么CWRU 的每个 .mat 文件通常包含几个关键变量X 开头的编号变量如 X105_DE_time、采样频率标记、转速信息。以 12kHz 驱动端数据为例正常基线文件里会有 X097_DE_time、X097_FE_time、X097_BA_time 三条时间序列分别对应驱动端加速度计、风扇端加速度计和基座加速度计。故障文件则按故障类型和直径命名比如内圈故障 0.007 英寸对应 IR007_0外圈故障 0.014 英寸对应 OR0146。这些命名规则不统一有的带 符号表示故障相对载荷角位置有的直接数字编号这就是为什么裸读 .mat 经常翻车。导入包要做的第一件事就是把这些命名差异吃掉。常见做法是维护一张映射表把文件名前缀映射到标签和通道信息。我一般会在导入包里放一个 config 字典键是文件名模式值是 (故障类型, 故障直径, 通道) 三元组。这样新增文件时只改配置不动解析逻辑。2.2 导入包该暴露什么接口一个能用的 CWRU 导入包接口不需要多但必须稳定。我倾向于暴露三个函数load_single_file(path)返回单文件的时间序列和元信息load_by_label(fault_type, diameter)返回匹配的所有样本load_dataset(split_ratio)返回训练测试划分好的数组。参数上采样频率和通道选择必须可配因为 48kHz 数据和 12kHz 数据的后续处理逻辑不同混用会导致频谱分析时频率轴对不上。下面是一个最小可用的导入实现用 scipy 读 .mat用 numpy 做规范化import scipy.io as sio import numpy as np from pathlib import Path # 文件名到标签的映射按实际下载的文件名调整 LABEL_MAP { Normal: (Normal, 0.0), IR007: (InnerRace, 0.007), IR014: (InnerRace, 0.014), IR021: (InnerRace, 0.021), OR007: (OuterRace, 0.007), OR014: (OuterRace, 0.014), OR021: (OuterRace, 0.021), B007: (Ball, 0.007), B014: (Ball, 0.014), B021: (Ball, 0.021), } def load_single_file(mat_path, channelDE): 读取单个 CWRU .mat 文件返回 (signal, meta) mat_path Path(mat_path) data sio.loadmat(str(mat_path)) # 找到包含 _DE_time / _FE_time / _BA_time 的键 key None for k in data.keys(): if k.endswith(f_{channel}_time): key k break if key is None: raise KeyError(f未找到通道 {channel} 的数据可用键{list(data.keys())}) signal data[key].ravel().astype(np.float32) # 从文件名推断标签 stem mat_path.stem label, diameter Unknown, -1.0 for prefix, (l, d) in LABEL_MAP.items(): if prefix in stem: label, diameter l, d break meta {file: stem, channel: channel, label: label, diameter: diameter} return signal, meta这段代码的逻辑很直白先遍历 .mat 的键找通道后缀再按文件名前缀匹配标签。参数channel默认 DE因为驱动端信号信噪比最好做故障诊断时最常用。ravel()把列向量压平避免后续 reshape 时维度对不上。astype(np.float32)是习惯性操作CWRU 原始数据是 double转 float32 能省一半内存对深度学习训练没影响。2.3 采样频率与转速信息的处理CWRU 数据分 12kHz 和 48kHz 两套采样率转速有 1730、1750、1772、1797 rpm 四种。导入包如果不记录这些元信息后面做阶次分析或角域重采样时就得回去翻原始文档。我的做法是在 meta 里加fs和rpm字段从文件名或目录名解析。比如目录结构是12k_Drive_End/就设 fs1200048k_Drive_End/就设 fs48000。转速信息通常在文件名里没有需要根据载荷推断0 马力对应 1797 rpm1 马力对应 1772 rpm2 马力对应 1750 rpm3 马力对应 1730 rpm。这个映射关系写死在配置里比每次手动查表靠谱。提示如果你下载的数据集目录结构和本文不一致先别急着改代码用sio.loadmat打印一下 keys确认变量命名规律再动手。3. 从 .mat 到训练张量导入包的完整落地流程3.1 批量加载与标签编码单文件读取跑通后下一步是批量加载整个数据集。这里有个容易忽略的点CWRU 各类故障的样本数量不均衡正常样本和故障样本比例大概 1:3如果直接按文件切分训练测试集会出现某类故障在测试集里一个样本都没有的情况。我一般用分层抽样按 (label, diameter) 组合分层保证每类在训练和测试里都有代表。from sklearn.model_selection import train_test_split import numpy as np def load_dataset(root_dir, channelDE, test_size0.3, random_state42): 批量加载 CWRU 数据集返回分层划分后的数组 root Path(root_dir) signals, labels, metas [], [], [] for mat_file in root.rglob(*.mat): try: sig, meta load_single_file(mat_file, channelchannel) signals.append(sig) labels.append(f{meta[label]}_{meta[diameter]}) metas.append(meta) except KeyError: continue # 跳过不含目标通道的文件 # 按最短长度截断保证能堆成矩阵 min_len min(len(s) for s in signals) X np.stack([s[:min_len] for s in signals]) # 标签编码 unique_labels sorted(set(labels)) label_to_idx {l: i for i, l in enumerate(unique_labels)} y np.array([label_to_idx[l] for l in labels]) # 分层划分 X_train, X_test, y_train, y_test train_test_split( X, y, test_sizetest_size, random_staterandom_state, stratifyy ) return X_train, X_test, y_train, y_test, label_to_idxmin_len截断是无奈之举因为不同文件的采样点数可能差几个点不截断np.stack会报错。stratifyy保证分层random_state固定后结果可复现。label_to_idx返回出去后面画混淆矩阵时要用。3.2 信号预处理归一化与分段原始振动信号幅值范围在 ±5g 左右直接送进网络收敛慢。导入包可以顺带做 z-score 归一化按通道全局减均值除标准差。另一个常见操作是分段把长信号切成 1024 或 2048 点的窗口重叠率 50%。分段函数我一般放在导入包里因为窗口长度和重叠率是实验超参放在数据层比放在模型层更灵活。def segment_signal(signal, window_size1024, overlap0.5): 将长信号切分为重叠窗口 step int(window_size * (1 - overlap)) segments [] for start in range(0, len(signal) - window_size 1, step): segments.append(signal[start:start window_size]) return np.stack(segments) def normalize_signal(signal): z-score 归一化 return (signal - signal.mean()) / (signal.std() 1e-8)1e-8是防止除零虽然 CWRU 信号不会全零但养成习惯没坏处。分段后的形状是 (num_segments, window_size)可以直接喂给 1D-CNN 或 LSTM。3.3 与 PyTorch Dataset 对接如果你用 PyTorch导入包最后一步是包一个 Dataset 类。这样 DataLoader 的多进程加载和 shuffle 都能直接用。import torch from torch.utils.data import Dataset class CWRUDataset(Dataset): def __init__(self, X, y): self.X torch.tensor(X, dtypetorch.float32).unsqueeze(1) # 加通道维 self.y torch.tensor(y, dtypetorch.long) def __len__(self): return len(self.y) def __getitem__(self, idx): return self.X[idx], self.y[idx]unsqueeze(1)把 (N, L) 变成 (N, 1, L)符合 Conv1d 的输入要求。这一步如果忘了训练时第一个报错就是维度不匹配血泪经验。4. 导入 CWRU 数据时最容易踩的五个坑4.1 现象读取 .mat 报 “Unknown mat file type”原因CWRU 官网下载的某些文件是 MATLAB v7.3 格式底层是 HDF5scipy.io.loadmat 不支持。解决用h5py读或者用mat73库。我一般先试 scipy捕获异常后切 h5py。try: data sio.loadmat(path) except NotImplementedError: import h5py with h5py.File(path, r) as f: data {k: np.array(f[k]) for k in f.keys()}4.2 现象通道键名找不到比如没有 X105_DE_time原因不同批次的 CWRU 文件变量命名不一致有的用 “DE_time” 后缀有的用 “_DE_time”还有的用 “X105_DE_time”。解决不要硬编码键名用后缀匹配。上面load_single_file里的k.endswith(f_{channel}_time)就是干这个的。如果还找不到打印所有 keys 人工确认。4.3 现象训练集准确率 99%测试集 60%原因分段时把同一段信号的不同窗口分到了训练和测试集造成数据泄漏。解决分段要在划分之后做或者按文件划分而不是按窗口划分。我一般先按文件分 train/test再各自分段。4.4 现象48kHz 数据做 FFT 后频率轴对不上原因导入时没记录采样频率默认按 12kHz 算。解决meta 里必须带 fs画频谱时用np.fft.rfftfreq(len(signal), 1/fs)。4.5 现象内存爆了加载 48kHz 全量数据直接 OOM原因48kHz 文件单个就有几十万点全量加载成 float64 矩阵轻松超过 8GB。解决用 float32或者用生成器逐文件加载不要一次性 stack。如果做实验先拿 12kHz 子集跑通再上全量。注意CWRU 数据集里正常样本的文件名有的叫 “Normal”有的叫 “NORMAL”大小写敏感会导致标签匹配失败。统一转小写再匹配。5. 用导入包做一次可复现的基线实验5.1 最小验证加载 分段 训练一个 1D-CNN导入包写完后必须用一个端到端实验验证它没毛病。我一般用 12kHz 驱动端数据取 1024 点窗口50% 重叠搭一个三层 1D-CNN跑 20 个 epoch看测试集准确率能不能到 95% 以上。如果低于 90%先查数据泄漏和标签对齐再调模型。import torch.nn as nn class BaselineCNN(nn.Module): def __init__(self, num_classes): super().__init__() self.net nn.Sequential( nn.Conv1d(1, 16, kernel_size15, stride2, padding7), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(16, 32, kernel_size7, stride2, padding3), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size3, stride2, padding1), nn.ReLU(), nn.AdaptiveAvgPool1d(1), nn.Flatten(), nn.Linear(64, num_classes), ) def forward(self, x): return self.net(x)AdaptiveAvgPool1d(1)把任意长度压成 1这样换窗口大小不用改网络结构。kernel_size15是第一层大感受野对振动信号里的冲击成分敏感。5.2 验证导入包正确性的三个检查点第一标签分布检查打印每个类别的样本数确认没有某类为 0。第二信号可视化随机抽三类信号画时域波形正常信号幅值均匀故障信号有明显周期性冲击。第三频谱检查对正常和故障信号各做 FFT故障信号在轴承特征频率处应有峰值。这三个检查过了导入包基本可信。5.3 一个具体技巧用阶次分析替代 FFTCWRU 数据转速有波动直接 FFT 频谱会 smear。我习惯在导入包里加一个角域重采样函数用转速信息把时域信号转成角域再做 FFT 得到阶次谱。这样故障特征阶次固定不受转速影响。实现上需要先估计转速可以用希尔伯特变换提取包络再做 FFT 找转频。这个技巧在变转速工况下特别管用但代码量比普通 FFT 多建议先把基础导入跑通再上。我自己做 CWRU 相关实验时导入包改过至少五版每次都是因为遇到新的文件命名或格式问题。后来学乖了所有解析逻辑都写成配置驱动新增数据只改配置不改代码。如果你打算长期用 CWRU 做实验建议把导入包当基础设施维护别每次临时写脚本。希望帮到你。本文还有配套的精品资源点击获取