手把手教你用Python搭建一个轴承故障预测模型
手把手教你用Python搭建一个轴承故障预测模型
说实话,轴承故障预测这个话题我大概是从 2019 年开始做的,到现在 7 年时间了,模型换了三轮——从最初的 SVM,到 XGBoost,再到现在的 LSTM。期间踩过的坑能写一本书。今天这篇不写教科书,就讲讲我一个一线工程师的实战过程。
为什么我最后选了 LSTM 而不是 Transformer
你可能会问:现在 Transformer 这么火,为什么不上?
去年 Q3 我们接了一个化工企业的项目,他们那批离心泵上的 SKF 6205 轴承,每秒 25.6kHz 采样,跑一段加速退化试验,原始数据加起来有 80GB。我先上了一版 Transformer(4 层 encoder,d_model=128),在 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, fs=25600):
"""
raw_signal: 原始振动信号,1D numpy array
fs: 采样率,Hz
返回:处理后的信号 + 提取的统计特征
"""
# 1. 带通滤波:保留轴承特征频率范围(一般 500-8000Hz)
# 实际工程中我们用 butterworth 4 阶
b, a = signal.butter(4, [500, 8000], btype='bandpass', fs=fs)
filtered = signal.filtfilt(b, a, raw_signal)
# 2. 小波去噪:db4 小波,3 层分解
coeffs = pywt.wavedec(filtered, 'db4', level=3)
# 软阈值去噪
sigma = np.median(np.abs(coeffs[-1])) / 0.6745
threshold = sigma * np.sqrt(2 * np.log(len(filtered)))
coeffs[1:] = [pywt.threshold(c, threshold, mode='soft') 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 个时间步,判断接下来 T+1 时刻会不会出故障。
我用的窗口长度是 1024 个点(约 40ms),步长 256,重叠率 75%。别小看这个步长选择——步长太大训练样本少,步长太小样本之间冗余度高,模型会过拟合到一些不该学的模式上。
def make_windows(signal_data, window=1024, step=256):
"""
把长信号切成固定长度的窗口
"""
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_dim=1, hidden_dim=128, num_layers=3, num_classes=4):
super().__init__()
# num_classes: 正常/内圈故障/外圈故障/滚动体故障
self.lstm = nn.LSTM(
input_dim, hidden_dim, num_layers,
batch_first=True, dropout=0.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)
踩坑提醒:dropout=0.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', classes=np.unique(y_train), y=y_train)
class_weights = torch.FloatTensor(weights).cuda()
criterion = nn.CrossEntropyLoss(weight=class_weights)
上线后真正的问题:模型漂移
你训练的时候数据是 2024 年的,工厂里设备的工况、负载、油液状态在缓慢变化。到 2025 年 5 月,模型召回率从 94% 掉到 81%——这就是概念漂移(concept drift)。
我们的解决方案是在线学习 + 定期重训:
- 每 24 小时做一次增量训练(用新采集的标注样本)
- 每周做一次全量重训
- 用 KL 散度监控输入分布,KL > 0.1 触发告警
具体怎么落地的,下一篇我会拆开来讲。
最后说两句
预测性维护不是"一个模型 + 一堆数据 = 出活儿",它是一个持续运营的过程。模型上线那天不是结束,是开始。
如果你刚开始接触这个领域,我的建议是:别上来就追最新的架构。把数据预处理、特征工程、类别不平衡处理这三件事做扎实,比换个 SOTA 模型有用 10 倍。
有具体问题可以评论区聊,看到会回。下次见。
更多推荐


所有评论(0)