
简介面向现代工业设备健康管理场景基于机器学习的机械故障诊断资料包旨在帮助机械工程师、数据科学从业者及专业学生掌握利用机器学习实现设备异常识别与故障预警的完整路径。整包共38个文件以20个Python脚本与18个编译后的pyc文件为主压缩包约75KB脚本代码贯穿数据预处理、特征工程、模型训练与评估环节便于直接运行和二次修改。已有618人学习下载。内容结合设备振动与声音信号的去噪清洗、频谱及时域统计特征提取覆盖SVM、随机森林、CNN等经典与深度模型的选择策略并配套训练数据集与模型脚本可帮助读者快速搭建一套故障诊断基线流程理解训练集、验证集与测试集划分、交叉验证以及准确率、F1等评估指标的实际应用。整体结构简洁清晰适合作为从入门到进阶的实践参考。1. 机械故障诊断先问数据这份 ML 资源包里到底装了什么工厂里最怕的不是设备忽然停机而是停机后拆开轴承才看到滚珠已经碎裂。其实振动数据早就在异常只是没人把它翻译成故障信号——这正是基于机器学习的机械故障诊断要解决的核心问题。这个压缩包以一套完整的故障诊断项目为主线目录结构清晰dataset 存放原始振动数据model 用于保存训练产物train.py 是贯穿全流程的训练脚本覆盖从信号预处理、特征提取、模型训练到评估的完整闭环。它适合两类人一是刚接手设备健康管理、想用算法替代人工巡检的工程师二是正在做故障诊断课程设计或毕业设计、需要一套能跑通全流程代码的开发者。跟着这套资源走一遍你会发现难点不在模型本身而在数据整理和评估口径。2. 信号预处理与特征工程把轴承振动变成模型能学的特征向量机器学习模型不直接吃原始波形它吃的是特征。这是整套故障诊断流程的第一个分水岭特征工程做得好的项目用随机森林也能到 95% 以上准确率特征乱来的项目换成 Transformer 也救不回来。这一章先讲数据侧怎么做再讲 train.py 里最常用的特征提取逻辑。2.1 原始振动信号的问题噪声、直流偏移与工频干扰工业现场采集的振动信号远没有教科书那么干净。传感器安装位置靠近电机时50Hz 工频及其谐波会叠加在轴承故障特征频率上齿轮啮合产生的边带也会掩盖早期的微弱冲击。更麻烦的是直流偏移——ADC 采样时如果有零点漂移时域幅值整体抬高直接算均值、方差都会被带偏。所以预处理的第一步不是滤波而是去均值。常见做法是import numpy as np raw np.load(dataset/sample_001.npy) # 一维振动信号 signal raw - np.mean(raw) # 去直流分量消除零点漂移去掉均值后下一步是带通滤波。轴承故障信号通常集中在几千赫兹以内但具体频带随转速和传感器安装方式变化。我一般会先用功率谱密度图看一眼能量集中在哪再决定滤波器上下限。切忌一上来就套默认参数不同设备的最优频带差异很大。常见做法是# 用 scipy 做 500Hz-5000Hz 带通滤波的典型写法 python -c from scipy.signal import butter, filtfilt import numpy as np sig np.load(dataset/sample_001.npy) b, a butter(4, [500, 5000], btypebandpass, fs25600) filtered filtfilt(b, a, sig) np.save(processed_data/sample_001_filtered.npy, filtered) 这里的滤波参数有两个关键点一是 butterworth 滤波器阶数设 4 通常够用阶数越高相位畸变越明显二是 filtfilt 做零相位滤波输出的信号不会产生延迟。如果你用的是 lfilter普通滤波输出的波形会整体偏移后续提取的峰峰值、峭度都会失真这点在故障诊断里非常容易翻车。2.2 时域特征与频域特征峭度、有效值、边频带能量的组合滤波之后就是特征工程的核心环节。时域特征里最常用的是峭度Kurtosis和有效值RMS。滚动轴承早期故障时峭度对冲击类信号非常敏感往往在有效值还没明显变化时峭度已经显著升高——这正是早期预警的价值。频域特征则重点看故障特征频率处的幅值以及边频带能量。from scipy.stats import kurtosis from scipy.fft import rfft, rfftfreq def extract_features(signal, fs): rms float(np.sqrt(np.mean(signal ** 2))) kurt float(kurtosis(signal)) peak float(np.max(np.abs(signal))) spectrum np.abs(rfft(signal)) / len(signal) freqs rfftfreq(len(signal), 1 / fs) # 取 500-3000Hz 频段平均能量 band_mask (freqs 500) (freqs 3000) band_energy float(np.mean(spectrum[band_mask])) return { rms: rms, kurtosis: kurt, peak: peak, band_energy: band_energy }这段代码里的参数取 500-3000Hz 只是一个占位实际操作中应该根据频谱图调整。注意 rfft 的幅值归一化是除以信号长度如果你用 fft双边频谱再取一半幅值会差一倍这个坑会在特征对比时造成系统性偏差。特征的组合策略我一般遵循一个原则只保留与故障机理相关的特征。峭度对应冲击、有效值对应能量损耗、频带能量对应共振频段激发这三个维度已经能覆盖大部分滚动轴承故障场景。2.3 滑动窗口切分长度与重叠率怎么定机械设备处于变工况时振动信号是非平稳的直接用整段信号提取一个特征向量会稀释局部故障信息。滑动窗口是标准解法把长信号切成若干短段每段提取一组特征一个段就是一个样本。def sliding_window(signal, win_len1024, step512): samples [] for start in range(0, len(signal) - win_len 1, step): segment signal[start:start win_len] samples.append(segment) return sampleswin_len 取多少取决于故障特征频率和你希望样本覆盖的转数。比如主轴转速 1500 RPM 时1 转是 40ms若采样率 25600Hz一转对应 1024 个采样点窗口至少要覆盖 2-3 转才能捕捉到周期性的冲击特征。step 一般取窗口的一半重叠 50% 是折中方案重叠太少样本不够重叠太多比如 90%相邻样本高度相关训练时会有信息泄漏风险。这块没有绝对标准我通常先按 50% 重叠建立基线再看训练集和验证集的指标差距决定是否调整。3. 模型选型与 train.py 实战SVM、随机森林与 CNN 的适用边界特征工程做完数据已经变成表格形式的特征向量二维数组行是样本列是特征这时才进入模型选择环节。很多初学者喜欢一上来就堆深度学习但机械故障诊断场景往往面临的是小样本、强噪声、类别不平衡盲目上深度模型反而容易过拟合。3.1 三种模型的定位小样本 SVM、稳健随机森林、复杂非线性 CNN先看适用边界对比模型样本量需求多分类能力可解释性训练成本典型场景SVMRBF核百级样本即可需一对多策略中等低单工况小样本、类别均衡随机森林千级样本起步原生支持高特征重要性低多特征融合、类别有倾斜CNN万级样本起原生支持低高原始波形端到端、复杂非线性模式在 train.py 的结构里这三种模型通常是三个独立的分支通过命令行参数切换。SVM 的优势在小样本场景特别明显因为 RBF 核把原始特征映射到高维空间后几十个正样本也能画出清晰边界。随机森林则是默认先跑的选择——它的特征重要性输出能直接反馈你的特征工程做得好不好如果某个特征重要性特别低说明它和故障类别几乎无关。而 CNN 在这个场景里一般不是从零设计大网络而是用小核宽、浅层数的一维卷积网络输入直接是滤波后的波形切片。它的价值在于能自动学习特征减少人工特征提取的工作量但它需要足够多的样本支撑否则在测试集上的表现反而不如随机森林。3.2 训练脚本参数拆解从数据划分到早停打开 train.py核心逻辑一般是这样一段import argparse import numpy as np from sklearn.model_selection import train_test_split from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier parser argparse.ArgumentParser() parser.add_argument(--model, choices[svm, rf, cnn], defaultrf) parser.add_argument(--kernel, defaultrbf) parser.add_argument(--C, typefloat, default1.0) parser.add_argument(--n_estimators, typeint, default200) parser.add_argument(--test_size, typefloat, default0.3) parser.add_argument(--random_state, typeint, default42) args parser.parse_args() X np.load(processed_data/features.npy) y np.load(processed_data/labels.npy) # 先划分再归一化顺序不能反 X_train, X_test, y_train, y_test train_test_split( X, y, test_sizeargs.test_size, stratifyy, random_stateargs.random_state )这里有个隐藏的关键点train_test_split 传入了 stratifyy保证划分后训练集和测试集的类别比例与原数据集一致。如果数据集本身类别不平衡比如正常样本占 90%、故障样本占 10%不 stratify 的话随机划分可能让测试集里完全没有故障样本指标再漂亮也没有意义。SVM 的 C 参数控制误分类惩罚力度C 越大模型越努力把训练样本分对但容易过拟合C 太小边界太平滑欠拟合。RBF 核的 gamma 参数一般在 train.py 里默认 auto控制单个样本的影响范围gamma 太大容易导致决策边界贴着每个样本画训练集满分、测试集崩盘。启动训练时python train.py --model svm --kernel rbf --C 1.0 --test_size 0.3训练完成后train.py 通常会把模型序列化保存到 model 目录SVM 存的是支持向量和系数随机森林存的是整棵树的节点分裂信息。我习惯保存模型的同时把 scaler归一化器也存下来——后面部署时你会发现如果只存分类器不存归一化器新数据的特征分布和训练时不一致模型输出的置信度会完全失真。3.3 归一化位置和交叉验证数据泄漏的高发区特征归一化是故障诊断里最容易做错的环节。标准做法是先在训练集上 fit 归一化器计算均值和标准差再用同一个归一化器去 transform 验证集和测试集。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) # 只 fit 训练集 X_test_scaled scaler.transform(X_test) # 只 transform 测试集如果写成 scaler.fit_transform(X)即把训练集和测试集合并后再归一化测试集的信息就悄悄泄露进了训练阶段模型在测试集上的表现会被高估。这是故障诊断论文里最容易被审稿人抓到的硬伤。交叉验证方面5 折交叉验证是最常用的配置。它的核心不是让指标好看而是评估模型在不同数据子集上的稳定性。如果 5 折的准确率分别是 97%、74%、96%、75%、97%说明某些折上出现了极端情况问题可能出在数据划分或特征提取的时序依赖性上——这时应该回头查滑动窗口是否跨越了设备工况切换的边界。4. 评估指标与阈值调优准确率是最会骗人的指标模型训练完成后train.py 会输出一堆评估数字但准确率在这类场景里往往最不可信。机械设备大多数时间处于正常状态故障样本天然稀缺。假设正常样本占 95%模型什么都不学只要永远输出正常准确率就有 95%——这个模型毫无价值。4.1 精确率、召回率、F1故障诊断里先想清楚代价故障诊断更关注的是故障能不能被查出来和报警是不是误报。精确率是报警里有多少是真故障召回率是真故障里有多少被报出来了。这两个指标的取舍取决于误报和漏报哪个代价更高。在滚动轴承场景漏报的代价是设备继续带病运行直到彻底损坏可能引发产线停机和连带损坏误报的代价是维修人员白跑一趟或者被频繁的假报警搞得麻木之后真报警也不当回事。多数情况下设备维护场景更倾向于提高召回率宁可多报不可漏报。from sklearn.metrics import precision_score, recall_score, f1_score y_pred model.predict(X_test_scaled) precision precision_score(y_test, y_pred, pos_labelfault, zero_division0) recall recall_score(y_test, y_pred, pos_labelfault, zero_division0) f1 f1_score(y_test, y_pred, pos_labelfault)zero_division0 这个参数很重要。当某个类别在测试集中完全没有出现时精确率会变成除零默认行为是返回 nan 并输出警告。故障诊断的测试集一般会包含所有类别但如果一次划分翻车导致某类样本缺失这个参数能帮你快速暴露问题。4.2 混淆矩阵与 AUC-ROC不看全貌就看不清问题混淆矩阵是评估故障诊断模型最直观的工具。四格分别对应 TP故障样本被正确判为故障、FN故障被判为正常也就是漏报、FP正常被判为故障也就是误报、TN正常被判为正常。我每次跑完模型都会先看这四个数而不是只盯准确率。AUC-ROC 曲线用于评估模型的排序能力它回答的是如果我把所有样本按故障概率从高到低排列故障样本是不是排在前面。AUC 的优点是均衡考虑每个类别不受阈值影响适合对比不同模型的选型。但 AUC 高不代表实际部署就好用——部署时你需要选一个具体的判定阈值这个阈值怎么定就是下一节的内容。4.3 阈值移动把默认 0.5 改成业务需要的值默认情况下分类器的阈值是 0.5即概率大于 0.5 判为故障。但如果数据不平衡这个默认值通常不是最优解。比如正常样本的特征分布和故障样本有重叠0.5 阈值下误报率偏高可以把阈值上调到 0.7用一部分召回率换取更少的误报。常见的调阈值方式是在验证集上遍历 0.1-0.9 的所有可能阈值画出 F1、精确率、召回率随阈值变化的曲线然后根据业务代价选点。比如设备停机损失极大就应该选召回率掉头下降之前的那个阈值点。这个操作不改变模型本身只改变最终决策边界是成本最低的调优手段。注意调阈值和调模型参数必须分开。先用默认阈值评估模型本身的性能再单独在验证集上选阈值。混在一起调你无法判断提升来自模型变强还是阈值更匹配。5. 常见问题与避坑五个实战翻车现场与排查思路故障诊断项目的坑大多不在算法理论而在数据处理细节。这一章整理五条高频踩坑记录每一条都是我在跑实际项目时真实遇到过的问题。5.1 数据泄漏归一化顺序错了测试集指标虚高现象模型在测试集上的准确率达到 99%但部署到现场后完全失灵误报率居高不下。原因在合并所有数据后统一做归一化测试集的均值和标准差参与到了训练特征的计算里模型相当于提前看过答案。更隐蔽的情况是滑动窗口的 step 太小相邻窗口的样本高度重叠训练集和测试集里出现了几乎相同的样本。解决先把数据按时间顺序划分为训练集、验证集、测试集再在训练集上 fit 归一化器。滑动窗口的重叠率控制在 50% 左右切分数据时用时间戳或者数据源 ID 做 group 划分避免同一时刻的窗口被拆分到两个集合。5.2 类别不平衡模型学会了躺平全预测正常也一堆高分现象训练完成后准确率 95%但看混淆矩阵发现故障类别的召回率是 0模型把所有样本都判成了正常。原因故障样本占整体数据不足 5%决策树或 SVM 的默认目标是最小化整体错误率把少数类全部牺牲掉是对模型最划算的选择。解决切分数据时用 stratifyy 保持类别比例再在训练时调整类权重。SVM 可以通过 class_weightbalanced 实现自动逆向权重随机森林里调整 class_weight 参数同样有效。如果故障样本实在太少先用 SMOTE 这类过采样方法合成少数类样本但合成样本要放在训练集里不能混进验证集。5.3 时序混洗随机打散数据后跨工况泛化能力归零现象训练集、验证集指标都很好但拿到另一台设备的同型号轴承上就完全失效。原因train_test_split 默认随机打乱数据。一台设备的数据包含多个工况段随机打散后同一工况的样本被分到训练集和测试集两边模型学到的是背下了该工况的模式而不是理解了故障的物理特征。解决用 GroupShuffleSplit 替换 train_test_split以每一个连续运行工况段为分组单位保证同一组的样本不会被拆开。换句话说训练集和测试集必须来自不同的时间段或不同的设备运行工况这样测试集的指标才反映真实泛化能力。5.4 峭度特征偏高却误报频发冲击信号被误当成故障现象某台设备的峭度值明显升高模型持续报警但停机检查后发现轴承完好只是隔壁车间的天车经过带来了地面振动冲击。原因峭度只对冲击敏感不区分冲击来源。环境干扰、电磁噪声、甚至安装松动都会产生高峭度信号它不是轴承故障的特异性指标。解决把频率成分纳入判断而不是只看时域冲击。轴承外圈故障的特征频率是明确的——根据转速计算 BPFO如果 2kHz 共振频段内有对应特征频率的峰值且伴生边带才判为故障。只看峭度不看频谱是这类误报的主要来源。5.5 训练脚本随机种子不固定同样的数据两次跑出不同结果现象同一套数据、同一个脚本今天跑 96.5%明天跑 94.2%怀疑代码有 bug。原因随机森林在构建子树时使用随机抽样神经网络的权重初始化也是随机的。没有设置随机种子每次训练的启动状态都不一样。解决在 train.py 入口处设置固定随机种子需要同时设置 Python 随机、numpy 随机和 sklearn 的随机状态。注意train_test_split 里也要传入 random_state模型构造器里也要传入 random_state只有全部固定才能复现实验结果。import random import numpy as np random.seed(42) np.random.seed(42) # sklearn 内的随机性由其 estimator 各自的 random_state 控制6. 部署与模型更新从离线训练到滚动窗口实时推理模型训练完只是第一步真正落地是把它接进实时监测系统。故障诊断部署和常规互联网模型部署有个关键差异推理输入不是一张独立的图片或一行文本而是不断滑动的振动信号流。我习惯用滚动窗口做实时推理每采集到新的 1024 个采样点窗口整体前移 512 点对窗口内数据实时提取特征并输入模型。这种设计在 Python 里实现很直接# 实时推理伪代码 while stream.running: window stream.read(win_len) # 读取最新 1024 点 features extract_features(window, fs) # 特征提取 scaled scaler.transform([features]) # 用训练时的 scaler prob model.predict_proba(scaled)[0][1] # 故障概率 if prob threshold: alarm_manager.trigger() stream.step(step) # 窗口前移 512 点这里最容易忽略的是特征提取函数必须和训练阶段完全一致。我自己吃过一次亏训练时用 rfft 算频谱能量部署时图省事改用 psd 估计结果同一个轴承的故障概率从 0.9 掉到 0.3。从那以后我每次部署前都强制走一遍流程加载 scaler 和模型后先拿一段训练集里的原始数据跑一次推理确认输出概率和离线时一致再接入实时数据流。这个回放验证步骤只要五分钟但能拦截掉大部分特征不一致、归一化参数丢失、模型加载错版本的问题。模型更新方面我不建议一开始就做在线学习。更稳妥的做法是设定一个触发条件当滚动窗口内的故障概率长期处于 0.4-0.6 的灰色地带、或误报率在两周内显著升高时把这段时间收集的数据导出人工标注后再离线重训练。这样既保证模型能适应设备缓慢退化又避免在线更新引入的数据污染。资源包里配套的 dataset 和 train.py 能让你在本地完整跑通这条路替换成自己的设备数据后评估指标、混淆矩阵、阈值曲线这些关键产物都会自然产出。希望帮到你。本文还有配套的精品资源点击获取