ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CWRU轴承数据集实战:Python故障诊断全流程详解

2026/9/17 2:53:23 拓冰建站 浏览量
CWRU轴承数据集实战:Python故障诊断全流程详解 作为常年跟旋转机械故障诊断打交道的人我深知拿不到一份干净好用数据集的痛苦。CWRU凯斯西储大学轴承数据集在这个圈子里算是难得的“标准答案”几乎每一篇关于轴承故障诊断的论文里都有它的影子。但很多刚开始接触这个领域的朋友包括几年前的我面对那一堆.mat文件都是一脸懵——数据怎么读信号怎么切故障类别怎么对应模型怎么跑起来更别说后期的部署了。这篇文章我打算用一次完整的Python实战把从CWRU原始数据加载、预处理、特征工程、模型训练到最终部署的整个链路走一遍。无论你是刚入门的学生、转行做设备健康管理的工程师还是想快速搭一个故障诊断Demo的算法岗同学这篇都能给你一条直接“抄作业”的路线。我会把代码、参数、踩过的坑都摊开讲保证你跟着做完能拥有一套属于自己的轴承故障诊断系统。1. 项目准备先把CWRU数据集和实验环境弄明白1.1 为什么首选CWRU数据集做故障诊断入门CWRU轴承数据集由凯斯西储大学电气与计算机工程系提供数据通过实验台架采集包含了正常轴承、内圈故障、外圈故障、滚动体故障四类主要状态每种故障又按损伤直径区分严重程度0.007英寸、0.014英寸、0.021英寸部分还有0.028英寸。采样频率常见为12kHz和48kHz电机负载涵盖0到3马力对应转速1797到1720 r/minSKF和NTF两种品牌轴承都有覆盖。这套数据最大的价值在于它提供了标准的、被广泛验证的故障模式标签并且数据采集条件可控、噪声环境相对理想。这意味着你可以把精力完全集中在算法建模上而不用花大量时间去清洗一份“脏乱差”的工业现场数据。我的经验是用它练手一套完整的机器学习或深度学习流程再迁移到自己的实际项目数据上会顺畅非常多。1.2 Python环境搭建与关键依赖库做这个项目Python版本我推荐3.9到3.11太老或太新的版本在部分库的兼容性上会有些小麻烦。核心依赖库如下numpy和scipy负责处理.mat数据文件和数值计算pandas做数据管理matplotlib画时域波形和频谱图scikit-learn提供机器学习模型和评估指标tensorflow或pytorch用于构建深度学习模型onnxruntime和flask用于模型部署。如果你本机还没有装好Python基础环境建议直接安装Anaconda发行版它会帮你把大部分科学计算库一并装好。pip install numpy scipy pandas matplotlib scikit-learn tensorflow onnxruntime flask joblib2. 数据加载与处理吃透.mat文件才能喂饱模型2.1 从.mat文件到numpy数组的完整解析CWRU数据集的原始文件是MATLAB格式每个.mat文件内包含多个变量通道比如驱动端加速度计数据DE、风扇端加速度计数据FE、基座加速度计数据BA以及转速等。用scipy.io.loadmat读取时需要注意MATLAB的struct类型会被转换成numpy的structured array直接取数据会得到一层“套娃”结构。from scipy.io import loadmat import numpy as np data loadmat(12k_Drive_End_B007_0_118.mat) print(data.keys()) # 查看文件内所有变量名实际读取时变量名通常是DE、FE、BA这样的键。关键是取出真实信号数组de_signal data[DE][0] # 驱动端加速度数据 fs 12000 # 采样频率文件命名里有12k标识我建议把数据加载封装成函数方便批量处理。对于文件较多的情况提前做一个映射表把每个文件对应的故障类型、损伤直径、负载情况记录下来形成一个DataFrame这样后面做特征工程和训练集划分时就会非常方便。2.2 数据切分、样本增强与标签映射原始信号长度很长直接丢进模型既慢又容易过拟合。行业里的通用做法是把连续信号切成固定长度的样本比如每个样本2048个点或4096个点。以12kHz采样频率为例2048个点对应约0.17秒足够覆盖多个轴承旋转周期能保留故障冲击特征。切分时我有两个建议一是采用重叠切分比如步长设为样本长度的一半能有效增加样本量二是切分前按故障类型分组确保训练集和测试集没有同源样本混入。代码片段如下def slice_signal(signal, sample_len2048, stride1024): samples [] for start in range(0, len(signal) - sample_len 1, stride): samples.append(signal[start:startsample_len]) return np.array(samples)标签映射是另一个关键点。CWRU数据集的故障类型和损伤程度组合很多你不要把所有组合都当作独立类别那样类别数太多模型学习难度大也没有必要。我按“正常”、“内圈故障”、“外圈故障”、“滚动体故障”四分类来做把不同损伤直径合并到大类里。如果你有精细化诊断需求再细分也不迟。label_map { Normal: 0, Inner_Race: 1, Outer_Race: 2, Ball: 3 }2.3 数据探索看一眼波形和频谱心里才有底在建模之前一定要花时间去看看不同状态下的信号长什么样。我见过很多同学上来就训练结果模型精度上不去回去看数据才发现标签对应错了。时域波形上正常轴承的振动信号幅值小且随机内圈故障会出现明显的周期性冲击外圈故障的冲击特征在负载较大时容易调制滚动体故障因为故障位置随转动变化冲击幅值会有波动。频谱上故障特征频率往往与外圈通过频率BPFO、内圈通过频率BPFI、滚动体自转频率BSF有关这些特征频率对应着明确的轴承几何参数公式。我强烈建议你画一下正常信号和故障信号的对比如下import matplotlib.pyplot as plt plt.figure(figsize(12, 6)) plt.subplot(2, 1, 1) plt.plot(normal_signal[:2048]) plt.title(Normal Signal) plt.subplot(2, 1, 2) plt.plot(inner_race_signal[:2048]) plt.title(Inner Race Fault Signal) plt.tight_layout() plt.show()这一步不仅能帮你确认数据加载是否正确也能让你对故障特征形成直观认识为后面特征选择提供方向。3. 特征工程与模型训练从“信号”到“诊断”的关键跨越3.1 时域、频域与熵特征的组合策略信号切好后直接喂给一维卷积神经网络1D-CNN是可行的但如果你更倾向于用经典机器学习模型比如随机森林、XGBoost或支撑向量机就需要手动提取特征。特征的质量直接决定模型上限这步不能凑合。我常用的特征组合分三块时域统计特征包括均值、方差、峰值、均方根、峭度、波形因子、峰值因子、脉冲因子、裕度因子频域特征包括频谱重心、均方频率、频率方差以及几个主要频带的能量占比信息熵特征包括近似熵和样本熵它们对信号的复杂性与不规则性很敏感在轴承退化诊断中表现不错。import numpy as np from scipy import stats def extract_features(signal): features [] # 时域特征 features.append(np.mean(signal)) features.append(np.var(signal)) features.append(np.max(np.abs(signal))) features.append(np.sqrt(np.mean(signal**2))) features.append(stats.kurtosis(signal)) features.append(np.max(np.abs(signal)) / np.sqrt(np.mean(signal**2))) # 频域特征 f np.fft.fft(signal) magnitude np.abs(f[:len(f)//2]) freqs np.fft.fftfreq(len(signal), 1/fs)[:len(f)//2] features.append(np.sum(magnitude * freqs) / np.sum(magnitude)) # 返回 return np.array(features)实际项目中我会用上述函数遍历所有样本生成一个特征矩阵再做标准化和降维。特征数量不宜太少否则模型区分度不够也不宜太多否则引入冗余和噪声。我一般保持在20到40维之间再通过特征重要性评估做筛选。3.2 模型选型对比随机森林、XGBoost与1D-CNN的取舍模型选择没有绝对“最好”只有“最适合”。我在CWRU数据集上对比过几类方案随机森林训练快抗过拟合能力强适合特征工程做得好、样本量中等的场景。在四分类任务上我测试的准确率能达到99%以上这已经足够大多数场景使用。XGBoost比随机森林稍慢但对小样本和类别不平衡的鲁棒性更好调参空间大值得尝试。1D-CNN端到端学习不需要手动特征工程性能上限高。但训练时间长需要调整的网络结构参数多对小数据集容易过拟合。如果你的目标是快速验证算法流程先用随机森林打底是最稳妥的。如果追求最终效果展示或者有充足算力和数据直接上1D-CNN。我在实际项目中常这样分层推进先用经典机器学习模型做基准再逐步尝试深度学习模型对比提升幅度决定是否值得增加复杂度。3.3 训练流程、交叉验证与模型评估模型训练前要做的最重要一件事是划分数据集。我用train_test_split把数据按7:3分成训练集和测试集并设置stratifyy来保证每个类别的样本比例一致。训练时再做一次5折交叉验证进一步验证模型的稳定性。from sklearn.model_selection import train_test_split, cross_val_score from sklearn.ensemble import RandomForestClassifier X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.3, random_state42, stratifyy ) model RandomForestClassifier(n_estimators200, max_depth20, random_state42) model.fit(X_train, y_train) cv_scores cross_val_score(model, X_train, y_train, cv5) print(fCross-validation accuracy: {cv_scores.mean():.4f} ± {cv_scores.std():.4f})评估指标上不要只看准确率要结合混淆矩阵、精确率、召回率和F1-score一起看。特别是在故障诊断场景中漏报的代价往往比误报更大。比如在实际工业场景中把内圈故障误判为正常可能导致设备继续运转直至彻底失效把正常误判为故障则会造成不必要的停机检修。因此要重点关注召回率。from sklearn.metrics import classification_report, confusion_matrix y_pred model.predict(X_test) print(classification_report(y_test, y_pred, target_names[Normal, Inner_Race, Outer_Race, Ball]))从我实测的经验来看正常类别通常最好识别故障类别中滚动体故障的区分度略差可能需要调整特征或增加该类别样本量。这两种情况都能在混淆矩阵中清楚地体现出来方便你做针对性的优化。4. 模型部署把训练好的诊断模型变成可用的服务4.1 模型导出与格式选择Pickle、ONNX还是TensorFlow SavedModel训练好的模型不能只活在Jupyter Notebook里要部署到实际系统中才有价值。部署方案根据使用场景大致分三种如果只是本地脚本或小工具调用直接用joblib.dump把模型保存为.pkl文件即可。如果需要跨语言调用、边缘设备推理或性能优化导出为ONNX格式是更专业的选择。ONNX是一个开放的模型表示标准支持PyTorch、TensorFlow、scikit-learn等多种框架的模型转换在CPU推理速度上通常有不错的优化。如果是深度模型并部署在服务端TensorFlow SavedModel或PyTorch的TorchScript/Vitual也都能胜任。我的建议是经典机器学习模型优先ONNX或Pickle深度学习模型优先ONNX或SavedModel。既然标题里强调了“模型部署”我就把ONNX作为重点来讲因为它可以把你训练的随机森林、XGBoost或者1D-CNN统一导出后续无论对接Java、Go还是C服务都能无缝衔接。import onnx from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType initial_type [(float_input, FloatTensorType([None, n_features]))] onnx_model convert_sklearn(model, initial_typesinitial_type, target_opset15) onnx.save_model(onnx_model, bearing_fault_model.onnx)这里有个小坑skl2onnx对部分模型和参数的支持有限比如某些自定义特征提取函数就不能直接转换。所以特征提取和标准化步骤要在模型导出前单独封装好推理时先做同样的预处理再输入ONNX模型。4.2 基于Flask快速搭建推理服务为了让模型“活”起来最好的方式就是用Flask写一个轻量级REST API服务。客户端传入一段振动信号服务端完成特征提取、标准化、模型推理返回诊断结果。代码结构很清晰from flask import Flask, request, jsonify import numpy as np import onnxruntime as ort app Flask(__name__) # 加载ONNX模型 sess ort.InferenceSession(bearing_fault_model.onnx) input_name sess.get_inputs()[0].name app.route(/predict, methods[POST]) def predict(): data request.get_json() signal np.array(data[signal], dtypenp.float32) features extract_features(signal).reshape(1, -1) # 注意这里需要和训练时保持一致的标准化 features_scaled scaler.transform(features) pred sess.run(None, {input_name: features_scaled.astype(np.float32)})[0] label int(np.argmax(pred)) return jsonify({prediction: label, confidence: float(np.max(pred))}) if __name__ __main__: app.run(host0.0.0.0, port5000)注意几个容易出错的点一是ONNX模型的输入名字要和转换时定义的一致不用死记运行时动态读取就好二是标准化器的参数要在训练完成后保存下来推理时直接加载三是输入数据必须转成float32否则ONNX Runtime会报类型错误。正如我上面代码中显式调用.astype(np.float32)那样这一步看似细节实则避免浪费大量排查时间。如果你希望每个推理请求处理更快可以给Flask加上多线程模式或者用gunicorn做多进程部署。CWRU这种2048个点的样本在普通CPU上特征提取加推理耗时通常在几十毫秒以内完全能够满足实时性要求不高的场景。4.3 性能优化与边缘部署的思路如果要把诊断模型部署到边缘设备比如树莓派、NVIDIA Jetson或工控机上需要额外考虑资源限制和推理延迟。我的建议是模型轻量化优先换用MobileNetV3或轻量级1D-CNN结构推理引擎换用ONNX Runtime或TensorRT后者在NVIDIA GPU上能获得数倍速度提升必要时做INT8量化把模型体积压缩到原来的四分之一精度损失在1%到2%以内很多场景可以接受。整体部署架构可以参考这样的流程传感器信号采集设备通过Modbus/OPC UA等工业协议上传数据边缘计算网关运行推理服务网关将原始信号送入模型推理将诊断结果写入时序数据库同时推送告警到监控大屏或手机短信形成从感知到决策的完整闭环。这样在数据不落中心服务器的前提下实现近设备端实时诊断也有利于数据隐私保护。5. 常见问题与排查技巧实录5.1 数据加载报错与标签混乱处理问题1loadmat读取后数据维度不对比如取出了二维矩阵而不是一维信号。 原因MATLAB文件中可能存储了多通道时间序列或者数据本身是二十维度的“行向量”和“列向量”问题。 排查打印data[DE].shape如果发现是(1, N)或(N, 1)用np.ravel()或.flatten()转成一维再处理即可。问题2不同文件采样频率不同混在一起训练导致模型混乱。 原因CWRU数据集合集里既有12kHz也有48kHz采样频率的文件统一按文件名称区分即可。 解决在加载时解析文件名字符串把采样频率显式记录到DataFrame列里训练时单独用同一采样频率的数据或按比例重采样。问题3标签映射时出错把内圈轻微故障和严重故障标签搞混。 解决建议建一个配置文件或字典用文件名的标准编码规则来映射比如文件名中的“B007”表示滚动体0.007英寸故障“IR014”表示内圈0.014英寸故障。不要在代码里手写每个文件路径对应什么标签而是写一个解析函数自动处理。5.2 模型训练过拟合与精度上不去的实用对策在CWRU数据集上我发现部分类别样本量不均衡会导致模型偏向多数类。对策有三种一是用SMOTE做少数类过采样二是在损失函数里为少数类设置更高权重三是通过重叠切分增加少数类样本量。三者可以组合使用我通常先用方案三成本最低。如果模型训练精度很高但测试精度很低大概率是过拟合。这时优先减少模型复杂度比如降低随机森林的深度或增加min_samples_leaf同时增加正则化强度Dropout和L2正则化都要用上还可以扩充数据量在切分信号时用较小的步长来增加样本数。如果你看到训练集和测试集精度差异在5个百分点以上我建议先从这三方面检查。另一个容易被忽视的问题训练集和测试集划分时如果直接对整个原始信号文件进行随机切分同一个文件中切出来的样本可能同时出现在训练集和测试集导致信息泄漏模型实际表现被高估。这在实际项目中是个常见的“陷阱”因为信号相邻样本之间具有很强的相关性。正确的做法是先把原始信号按文件或按时间段分组划分时确保同一个原始信号段的所有样本要么全在训练集要么全在测试集。5.3 部署阶段推理结果异常排查顺序部署完成后如果发现API返回结果与训练时测试结果不一致先别急着怀疑模型坏了。我总结了一套排查顺序输入数据检查接口收到的信号是否正确有没有空值、NaN或异常大值。预处理一致性特征提取的参数、标准化器的均值和方差是否和训练时完全一致。类型与维度输入数组的dtype是否为float32维度是否为(1, N)而非(N,)。模型输出解释ONNX的输出可能是二维数组索引[0]才对应当前样本的各类别概率。如果以上都正确但结果还是不对就用一个训练集的已知样本直接走一遍API和本地脚本跑出的结果对比如果还是一样就说明只是客户端传参问题如果不一样基本能定位是哪个环节出了偏差。这种逐层定位的方法比盲目调模型高效得多。6. 从CWRU到真实工业场景实践经验与扩展建议6.1 数据集局限性与迁移学习的必要性CWRU数据集的学术价值无需多言但直接把它训练出的模型搬到实际产线效果通常会打折扣。原因包括实际工况负载和转速变化更复杂、工业现场的噪声和干扰成分更大、不同设备相同故障类型的振动特征也存在差异此外传感器安装位置和采样参数也不同。有效的方法是迁移学习在CWRU上预训练特征提取器用实际现场的一小部分带标签数据做微调再用大量无标签数据配合自监督或半监督方法做持续迭代。我在一个风电齿轮箱项目里就尝试过这样的思路从CWRU迁移来的预训练模型经过数百条现场数据微调后诊断准确率从直接套用时的70%出头提升到了90%以上。迁移学习绝不是锦上添花而是从实验室到产线的必经之路。6.2 放大到故障诊断平台数据管道与模型生命周期管理单个模型能解决单一设备的问题但做工业设备健康管理平台还需要结构化地组织数据和模型的生命周期。数据管道层面传感器数据采集后要经过清洗、对齐、持久化存储、特征工程和样本切分这个过程建议用Airflow或Prefect做定时调度和监控。模型层面每一轮训练要记录模型版本、训练数据批次、参数配置、评估指标和部署时间用MLflow管理会非常顺手。API服务要支持多模型热切换方便A/B测试。这样一套系统搭出来后从数据采集到模型部署就形成了一个完整的闭环。新的数据进来模型定期重新训练和评估再一键发布新版本诊断能力会随着数据积累持续提升。CWRU数据集最大的意义就是让你能在真实系统验证这一整条链路为以后接手更复杂的工业项目打下基础。根据我个人的经验来说做工业AI项目最忌讳的就是只盯着算法和指标而忽略了数据和工程化的完整闭环。从CWRU这个小而规范的数据集开始完整地走一遍数据加载、特征工程、模型训练和部署上线的流程你收获的不仅是一个演示Demo更是一整套可以复用到任何设备故障诊断场景的方法论。数据、模型和服务三者的衔接才是这类项目真正的门槛。最后再补充一点完整代码和注释我建议按模块组织数据加载部分单独一个文件特征工程一个文件模型训练一个文件部署服务一个文件这样后续维护和扩展都会舒服很多。动手把流程跑通一遍很多细节才能真正被掌握。希望这篇实战记录能给你省下一些摸索的时间。