手把手教你用Python搭建一个轴承故障预测模型 手把手教你用Python搭建一个轴承故障预测模型说实话轴承故障预测这个话题我大概是从 2019 年开始做的到现在 7 年时间了模型换了三轮——从最初的 SVM到 XGBoost再到现在的 LSTM。期间踩过的坑能写一本书。今天这篇不写教科书就讲讲我一个一线工程师的实战过程。为什么我最后选了 LSTM 而不是 Transformer你可能会问现在 Transformer 这么火为什么不上去年 Q3 我们接了一个化工企业的项目他们那批离心泵上的 SKF 6205 轴承每秒 25.6kHz 采样跑一段加速退化试验原始数据加起来有 80GB。我先上了一版 Transformer4 层 encoderd_model128在 A10 上训了 6 个小时——效果拉胯到我都想把键盘砸了。后来换成了 3 层 LSTM隐藏层 128 维训练 4 小时轴承内圈故障检出率直接拉到 94%。踩坑提醒对于一维振动信号这种局部模式占主导的数据LSTM 反而比 Transformer 好用。Transformer 注意力是全局的会把噪声也当特征学进去。这一点我后来和西门子一个专门做振动分析的德国工程师聊过他们的内部结论也是在小数据 高采样率场景下LSTM 还是稳。数据预处理这一步最容易被低估很多人上来就开始搭模型我劝你冷静一下。振动信号预处理没做好后面模型再花哨都是垃圾。我一般这么干import numpy as np from scipy import signal import pywt # PyWavelets 1.5 def preprocess_vibration(raw_signal, fs25600): raw_signal: 原始振动信号1D numpy array fs: 采样率Hz 返回处理后的信号 提取的统计特征 # 1. 带通滤波保留轴承特征频率范围一般 500-8000Hz # 实际工程中我们用 butterworth 4 阶 b, a signal.butter(4, [500, 8000], btypebandpass, fsfs) filtered signal.filtfilt(b, a, raw_signal) # 2. 小波去噪db4 小波3 层分解 coeffs pywt.wavedec(filtered, db4, level3) # 软阈值去噪 sigma np.median(np.abs(coeffs[-1])) / 0.6745 threshold sigma * np.sqrt(2 * np.log(len(filtered))) coeffs[1:] [pywt.threshold(c, threshold, modesoft) for c in coeffs[1:]] denoised pywt.waverec(coeffs, db4) # 3. 时域特征 features { rms: np.sqrt(np.mean(denoised**2)), kurtosis: np.mean((denoised - np.mean(denoised))**4) / (np.std(denoised)**4), peak: np.max(np.abs(denoised)), crest_factor: np.max(np.abs(denoised)) / np.sqrt(np.mean(denoised**2)), } return denoised[:len(raw_signal)], features注意这里有个细节滤波之后小波去噪的阈值选择强烈不建议用通用公式。我们现场数据噪声特性差异很大硬套sigma * sqrt(2*log(n))经常把信号本身的细节也抹掉了。我们后来改成了自适应阈值——根据每段信号的 SNR 动态调整。这一步在论文里没人会告诉你但你做了就知道差距有多大。滑动窗口切分样本轴承故障预测的本质是时序分类问题给定过去 T 个时间步判断接下来 T1 时刻会不会出故障。我用的窗口长度是 1024 个点约 40ms步长 256重叠率 75%。别小看这个步长选择——步长太大训练样本少步长太小样本之间冗余度高模型会过拟合到一些不该学的模式上。def make_windows(signal_data, window1024, step256): 把长信号切成固定长度的窗口 windows [] for start in range(0, len(signal_data) - window, step): windows.append(signal_data[start:start window]) return np.array(windows) # 假设正常数据 normal_windows make_windows(normal_signal) fault_windows make_windows(fault_signal)模型搭建3 层 LSTM Dropout下面这版模型是我们线上跑了一年多的版本。PyTorch 2.1。import torch import torch.nn as nn class BearingLSTM(nn.Module): def __init__(self, input_dim1, hidden_dim128, num_layers3, num_classes4): super().__init__() # num_classes: 正常/内圈故障/外圈故障/滚动体故障 self.lstm nn.LSTM( input_dim, hidden_dim, num_layers, batch_firstTrue, dropout0.3 ) self.fc nn.Sequential( nn.Linear(hidden_dim, 64), nn.ReLU(), nn.Dropout(0.5), nn.Linear(64, num_classes) ) def forward(self, x): # x shape: (batch, seq_len, input_dim) lstm_out, _ self.lstm(x) # 取最后一个时间步 last lstm_out[:, -1, :] return self.fc(last)踩坑提醒dropout0.3是经验值。我之前试过 0.5准确率更高一点点但模型震荡非常厉害训练后期 loss 曲线会反复横跳。0.3 是个稳的折中点。0.2 偏欠拟合0.4 偏不稳定。训练时一个反常识的发现很多人觉得准确率越高越好——但在预测性维护里高准确率往往意味着你根本没学到东西。因为故障样本天然就少。我那个化工项目里正常的样本有 12 万条4 类故障加起来只有 8000 条比例 15:1。这种情况下你训练一个模型啥也不学全部预测为正常准确率能到 94%。所以我看的不是 accuracy而是per-class recall和混淆矩阵。我们 KPI 卡的指标是内圈故障召回率 ≥ 90%误报率 ≤ 6%。# 训练时一定要用 class_weight 处理不平衡 from sklearn.utils.class_weight import compute_class_weight import numpy as np y_train [...] # 你的训练标签 weights compute_class_weight(balanced, classesnp.unique(y_train), yy_train) class_weights torch.FloatTensor(weights).cuda() criterion nn.CrossEntropyLoss(weightclass_weights)上线后真正的问题模型漂移你训练的时候数据是 2024 年的工厂里设备的工况、负载、油液状态在缓慢变化。到 2025 年 5 月模型召回率从 94% 掉到 81%——这就是概念漂移concept drift。我们的解决方案是在线学习 定期重训每 24 小时做一次增量训练用新采集的标注样本每周做一次全量重训用 KL 散度监控输入分布KL 0.1 触发告警具体怎么落地的下一篇我会拆开来讲。最后说两句预测性维护不是一个模型 一堆数据 出活儿它是一个持续运营的过程。模型上线那天不是结束是开始。如果你刚开始接触这个领域我的建议是别上来就追最新的架构。把数据预处理、特征工程、类别不平衡处理这三件事做扎实比换个 SOTA 模型有用 10 倍。有具体问题可以评论区聊看到会回。下次见。