简介这份资源面向机械工程、工业自动化与人工智能方向的学习者及工程技术人员聚焦如何用机器学习方法完成设备健康管理与故障预警帮助读者建立从信号预处理到模型部署的完整认知。压缩包共38个文件以20个py源码与18个pyc编译文件为主源码覆盖数据集加载、模型定义与训练脚本等模块整体约75KB结构紧凑便于直接运行与二次修改。目前已有618人学习下载具备一定参考热度。内容围绕振动与声音信号的清洗、归一化和特征提取展开涉及频谱特性、时域统计量等特征工程方法并对比决策树、随机森林、支持向量机及CNN、RNN等模型在小样本与复杂非线性场景下的适用差异。读者可据此掌握训练集、验证集、测试集划分与交叉验证流程理解准确率、召回率、F1分数及AUC-ROC等评估指标并思考误报与漏报代价不同时的标准调整策略为模型部署与实时监控打下实践基础。1. 从振动信号到故障标签机械故障诊断为什么总在“最后一公里”翻车设备在实验室台架上跑得好好的一上产线就误报这是机械故障诊断最常见的翻车现场。很多人以为把振动数据丢进一个机器学习模型跑出 99% 的准确率就万事大吉结果换一台设备、换一个转速模型直接失灵。问题不在算法本身而在于从信号采集到特征构造再到模型部署这条链路上每一步都有隐性的假设在悄悄失效。“基于机器学习的机械故障诊断”这个方向核心要解决的是用加速度计、声发射或电流信号自动判断轴承、齿轮箱、转子等部件处于正常、内圈故障、外圈故障还是滚动体故障状态。它适合设备运维工程师、状态监测方向的研究生以及想把机器学习落地到工业场景的算法工程师。读完这篇你能拿到一套从原始振动信号到可复现诊断结果的最小闭环包括数据怎么切、特征怎么选、模型怎么训、上线前怎么验。2. 信号预处理与数据集构建把振动波形变成模型能吃的样本2.1 为什么不能直接把原始振动序列丢给分类器原始振动信号是一维时序采样率动辄 12.8 kHz 或 25.6 kHz一段 10 秒的数据就有十几万个点。直接拿去做分类维度灾难先不说故障冲击成分会被大量正常运转的基频和噪声淹没。常见做法是先做分帧再对每一帧提取统计特征或时频特征把不定长序列变成定长特征向量。分帧的核心参数是窗长和重叠率。窗长要覆盖至少一个完整的故障冲击周期。以轴承为例如果外圈故障特征频率 BPFO 是 120 Hz那么一个冲击周期约 8.3 ms窗长取 1024 点在 12.8 kHz 采样率下约 80 ms能覆盖多个冲击周期同时保留足够的时间分辨率。重叠率一般取 50%防止故障冲击落在窗边界被截断。注意窗长不是越大越好。窗太长会混入转速波动窗太短则频率分辨率不足低转速设备的故障特征频率可能只有几十赫兹需要相应加长窗长。2.2 用 Python 做分帧和时域特征提取的最小代码下面这段代码把一段振动信号切成定长帧并提取常用的时域统计特征。这些特征计算量小适合作为基线方案。import numpy as np from scipy.stats import kurtosis, skew def frame_signal(signal, frame_len1024, overlap0.5): 将一维振动信号分帧返回二维数组 (n_frames, frame_len) step int(frame_len * (1 - overlap)) frames [] for start in range(0, len(signal) - frame_len 1, step): frames.append(signal[start:start frame_len]) return np.array(frames) def time_domain_features(frame): 提取单帧的时域特征 rms np.sqrt(np.mean(frame ** 2)) # 有效值反映能量 peak np.max(np.abs(frame)) # 峰值反映冲击强度 crest peak / (rms 1e-12) # 峰值因子对早期冲击敏感 kurt kurtosis(frame, fisherTrue) # 峭度正常轴承约 3故障时增大 sk skew(frame) # 偏度反映波形不对称性 p2p np.max(frame) - np.min(frame) # 峰峰值 return [rms, peak, crest, kurt, sk, p2p] # 假设 signal 是加载好的一维 numpy 数组采样率 12.8 kHz frames frame_signal(signal, frame_len1024, overlap0.5) features np.array([time_domain_features(f) for f in frames]) print(features.shape) # (n_frames, 6)这段代码的逻辑是先分帧把长序列切成短片段再对每帧算 6 个时域指标。frame_len控制频率分辨率overlap控制帧间平滑度。crest和kurt是轴承故障诊断里最常用的两个指标早期微弱冲击在 RMS 上几乎看不出来但峭度会明显偏离 3。实际使用时建议把frame_len和overlap作为超参数用验证集上的诊断准确率来选。2.3 公开数据集怎么选、怎么切分才不泄漏机械故障诊断常用的公开数据集有凯斯西储大学轴承数据集、帕德博恩轴承数据集、MFPT 数据集等。以凯斯西储大学数据集为例它包含 0 到 3 马力负载下、不同故障直径的内圈、外圈和滚动体故障数据采样率有 12 kHz 和 48 kHz 两种。切分数据集时最容易犯的错误是按帧随机划分。同一段连续信号切出来的帧高度相关随机划分会让训练集和测试集共享大量相似样本准确率虚高。正确做法是按“记录”划分同一段采集记录要么全进训练集要么全进测试集。更严格的按“负载”或“转速”划分用 0 马力数据训练用 3 马力数据测试这样得到的准确率才接近真实部署场景。划分方式训练集测试集适用场景按帧随机80% 帧20% 帧不推荐严重高估按记录80% 记录20% 记录同工况快速验证按负载0/1 马力2/3 马力跨工况泛化评估按设备设备 A设备 B跨设备迁移评估3. 特征工程与模型选型从手工特征到端到端诊断3.1 时域、频域、时频域特征到底该选哪一类时域特征计算快、物理意义明确但对早期微弱故障不够敏感。频域特征通过 FFT 把信号变到频域能直接看到故障特征频率及其谐波是轴承诊断的经典手段。时频域特征如短时傅里叶变换、小波包能量兼顾时间和频率分辨率适合变转速工况但计算量大、特征维度高。我一般会先用时域特征加频域特征做基线。频域特征不要只取幅值谱的峰值更稳的做法是取故障特征频率处的幅值及其前几阶谐波的幅值再取这些幅值的比值。比如外圈故障时BPFO 处的幅值会显著上升同时 BPFO 与转频的比值也会变化。把这些比值作为特征比单纯看绝对幅值更抗工况变化。3.2 用随机森林和 1D-CNN 做两个基线模型随机森林适合手工特征向量训练快、可解释性好。1D-CNN 适合原始信号或时频图能自动学习特征但需要更多数据和调参。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # features 是上一步提取的特征矩阵labels 是对应故障标签 pipe Pipeline([ (scaler, StandardScaler()), # 标准化随机森林对尺度不敏感但便于对比 (rf, RandomForestClassifier( n_estimators200, # 树的数量200 通常够用 max_depth12, # 限制深度防过拟合 min_samples_leaf3, # 叶节点最小样本数控制平滑度 random_state42 )) ]) # 按记录分组交叉验证groups 是每条记录对应的编号 scores cross_val_score(pipe, features, labels, cv5, groupsgroups) print(fCV accuracy: {scores.mean():.4f} /- {scores.std():.4f})随机森林的n_estimators从 100 到 500 之间调max_depth和min_samples_leaf是防过拟合的关键。如果交叉验证准确率很高但测试集掉得厉害优先检查数据划分有没有泄漏再考虑降模型复杂度。1D-CNN 的输入是分帧后的原始信号不需要手工特征。一个轻量结构是三层卷积加全局平均池化参数量控制在 10 万以内避免在小数据集上过拟合。import torch import torch.nn as nn class FaultCNN(nn.Module): def __init__(self, n_classes4): super().__init__() self.net nn.Sequential( nn.Conv1d(1, 16, kernel_size15, stride2, padding7), # 大卷积核抓冲击 nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(16, 32, kernel_size7, stride1, padding3), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(), nn.AdaptiveAvgPool1d(1), # 全局平均池化输出 64 维 nn.Flatten(), nn.Linear(64, n_classes) ) def forward(self, x): return self.net(x) # x: (batch, 1, frame_len)第一层用kernel_size15的大卷积核是为了在浅层就捕捉到故障冲击的宽包络。stride2降低序列长度减少计算量。全局平均池化替代全连接层能显著减少参数量在小样本上更稳。训练时用 Adam 优化器学习率 1e-3配合余弦退火batch size 取 64。3.3 类别不平衡时用什么损失函数实际设备大部分时间正常运行故障样本远少于正常样本。如果直接用交叉熵模型会倾向于预测正常类。常见做法是给交叉熵加类别权重权重取类别频率的倒数。更稳的做法是用 Focal Loss让模型聚焦难分类样本。class FocalLoss(nn.Module): def __init__(self, alphaNone, gamma2.0): super().__init__() self.alpha alpha # 各类别权重张量形状 (n_classes,) self.gamma gamma # 聚焦参数2.0 是常用值 def forward(self, logits, targets): ce nn.functional.cross_entropy(logits, targets, weightself.alpha, reductionnone) pt torch.exp(-ce) return ((1 - pt) ** self.gamma * ce).mean()gamma越大模型越关注难样本。alpha按类别频率倒数设置比如正常类占 90%故障类各占 3% 左右正常类权重设 0.1故障类设 1.0 左右。实际调参时先用加权交叉熵跑通再换 Focal Loss 对比验证集上的召回率。4. 避坑与排查机械故障诊断落地时最容易踩的五个坑4.1 准确率 99% 但现场误报不断现象测试集准确率很高部署到现场后正常设备频繁报故障。原因通常是数据泄漏或工况不匹配。同一段信号切出的帧被随机分到训练集和测试集模型记住了这段信号的噪声模式换一段信号就失效。解决按记录或按负载划分数据集用跨工况测试集评估。如果跨工况准确率掉到 60% 以下说明特征或模型对工况过拟合需要加入转速归一化或工况自适应层。4.2 峭度指标对早期故障不敏感现象轴承已经有轻微剥落但峭度值仍在 3 附近模型判正常。原因峭度对冲击敏感但早期故障冲击能量弱被背景噪声掩盖。解决不要只依赖峭度结合包络谱分析。对信号做带通滤波后取包络再对包络做 FFT看故障特征频率处是否有明显峰值。包络谱对早期故障的敏感度远高于原始峭度。4.3 变转速工况下模型集体失灵现象定转速训练的模型在升速或降速过程中准确率骤降。原因转速变化导致故障特征频率漂移定频带内的能量分布完全改变。解决用阶次分析代替频率分析把时间域信号按转速重采样到角度域故障特征在阶次域中位置固定。或者用短时傅里叶变换加转速通道作为模型输入让模型自己学习转速补偿。4.4 模型文件太大塞不进边缘设备现象1D-CNN 参数量几十万部署到嵌入式采集器上内存不够。原因全连接层和过多的卷积通道。解决用全局平均池化替代全连接通道数从 64 起步而不是 256卷积核数量按 16、32、64 递增。量化到 INT8 后模型大小能压到几百 KB。如果还不够用知识蒸馏把大模型的能力迁移到小模型。4.5 标签噪声让模型学偏现象训练集里有些样本标签标错模型在验证集上表现不稳定。原因人工标注故障类型时内圈和外圈故障在时域波形上有时难以区分。解决先用无监督聚类看特征空间分布把明显离群的样本挑出来复核。训练时用标签平滑把硬标签变成软标签降低个别错误标签的影响。如果标注成本高考虑用半监督学习只用少量标注样本加大量无标注样本。5. 从离线模型到在线诊断验证方法与一个实用技巧模型训完只是开始真正难的是验证它能不能在线上稳住。我一般会做三件事第一用历史数据做回放测试把模型按时间顺序跑一遍看报警是否集中在故障发生前后而不是随机散布。第二做对抗测试在正常信号里人为注入不同信噪比的故障冲击看模型在什么信噪比下开始漏报这个阈值就是现场可用的灵敏度边界。第三留一段完全没参与训练的设备数据做盲测盲测准确率低于 80% 就不上线。一个实用技巧是用“报警持续时长”代替单帧报警。单帧误报很容易被后续正常帧淹没但如果连续 5 帧以上都报故障真实故障的概率大幅上升。实现上用一个长度为 5 的滑动窗口窗口内故障帧占比超过 60% 才触发报警。这个简单策略能把现场误报率降一个数量级代价是报警延迟增加几帧对大多数旋转设备来说完全可以接受。from collections import deque class AlarmFilter: def __init__(self, window_size5, threshold0.6): self.window deque(maxlenwindow_size) self.threshold threshold def update(self, pred_label): pred_label: 0 表示正常非 0 表示故障 self.window.append(1 if pred_label ! 0 else 0) if len(self.window) self.window.maxlen: return False return sum(self.window) / len(self.window) self.thresholdwindow_size和threshold需要根据设备允许的报警延迟来调。旋转设备通常允许几秒到几十秒的延迟窗口可以取大一点。往复设备对延迟敏感窗口要短。这个后处理逻辑不挑模型随机森林和 CNN 都能接。我自己踩过最深的坑是太相信离线指标。有一次用凯斯西储大学数据训了个模型交叉验证 98%兴冲冲拿到现场数据上跑结果正常设备报警率超过 30%。后来把现场数据按转速分层一看训练集里根本没有现场那种转速区间的数据。从那以后我养成了一个习惯任何模型上线前必须用目标设备上采到的、完全没参与训练的数据做一次盲测盲测不过关离线指标再漂亮也不放行。希望帮到你。本文还有配套的精品资源点击获取
