引言
工厂里最贵的不是设备本身,而是非计划停机。一条产线停一小时,损失可能超过一套传感器系统全年的采购费。传统维护有两种姿势:坏了再修(事后维修),或者到点就换(预防性维护)。前者赌运气,后者浪费寿命——轴承可能还有三千小时寿命就被换掉。预测性维护(Predictive Maintenance, PdM)的吸引力在于第三种姿势:用传感器数据实时评估健康状态,在故障发生前的最佳窗口安排维修。国际上有专门的PHM学科,NASA公开的C-MAPSS涡扇发动机数据集和FEMTO轴承数据集就是这个领域的"ImageNet"。本文梳理从振动信号处理到剩余寿命(RUL)预测的完整技术栈。
问题建模:先想清楚预测什么
PdM项目最常见的失败原因不是模型不行,而是问题定义错了。实际有三类问题,难度递进。
异常检测:设备当前状态是否偏离正常。只需要正常运行数据,用自编码器或孤立森林就能做,是最容易落地的起点。
故障诊断:已经发现异常,判断是哪类故障(不平衡、不对中、轴承外圈损伤)。这是监督分类问题,难点在于故障样本稀少且标注依赖维修记录。
RUL预测:设备还能安全运行多久。这是回归问题,需要"从健康到失效"的全生命周期数据,最难但价值最大——直接回答"什么时候修"。
工程上建议按这个顺序递进:先用异常检测把监控体系跑起来,积累故障事件数据,半年到一年后再做诊断,数据足够厚了再上RUL。
振动信号特征工程:老将不死
深度学习大行其道的今天,特征工程在PdM领域依然重要,因为振动信号的物理意义太明确了。
时域特征:有效值(RMS)反映整体能量,峰值和峰值因子(Crest Factor)对冲击敏感,峭度(Kurtosis)对早期轴承故障的脉冲成分特别灵。轴承早期点蚀在RMS上几乎看不出变化,但峭度会从3(高斯分布的水平)蹿到5以上。
频域特征:FFT频谱上的特征频率是诊断的金标准——轴承内圈、外圈、滚动体、保持架各有特征频率,可以用几何参数和转速精确计算。频谱上特征频率及其边带的出现,几乎可以"定罪"具体部件。
时频域特征:振动信号非平稳,小波包分解和经验模态分解(EMD)能捕捉瞬时特征。包络解调(Envelope Analysis)是轴承诊断的杀手锏:对共振频带滤波后取包络,能把淹没在噪声里的周期性冲击提出来。
深度学习的角色不是替代这些知识,而是降低对它们的依赖——端到端模型直接从原始波形学特征,适合故障模式复杂、人工特征设计困难的场景。
实战:1D-CNN加BiLSTM预测剩余寿命
以C-MAPSS数据集为例,21维传感器时序数据,目标是预测发动机的剩余循环数。一个经典的轻量方案是1D-CNN提取局部时序模式,BiLSTM捕捉长程退化趋势:
import torch import torch.nn as nn class RULPredictor(nn.Module): def __init__(self, n_sensors=21, hidden=64): super().__init__() self.cnn = nn.Sequential( nn.Conv1d(n_sensors, 64, kernel_size=5, padding=2), nn.BatchNorm1d(64), nn.ReLU(), nn.Conv1d(64, 64, kernel_size=5, padding=2), nn.BatchNorm1d(64), nn.ReLU(), nn.AdaptiveAvgPool1d(16), # 压缩时间维,减轻LSTM负担 ) self.lstm = nn.LSTM(64, hidden, num_layers=2, batch_first=True, bidirectional=True) self.head = nn.Sequential( nn.Linear(hidden * 2, 64), nn.ReLU(), nn.Dropout(0.2), nn.Linear(64, 1), # 输出RUL ) def forward(self, x): # x: [B, seq_len, n_sensors] -> CNN需要 [B, n_sensors, seq_len] feat = self.cnn(x.transpose(1, 2)) # [B, 64, 16] out, _ = self.lstm(feat.transpose(1, 2)) # [B, 16, 2*hidden] return self.head(out[:, -1]).squeeze(-1) # 取最后时刻 # 标签处理技巧:RUL分段线性——早期寿命封顶(如125循环),后期线性衰减 def piecewise_rul(rul, cap=125): return torch.clamp(rul, max=cap) model = RULPredictor() opt = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4) for x, rul in train_loader: pred = model(x) loss = nn.functional.mse_loss(pred, piecewise_rul(rul)) opt.zero_grad(); loss.backward(); opt.step()几个关键细节:滑动窗口长度取30个循环左右,太短捕捉不到退化趋势;传感器要做归一化,但部分传感器在退化全程几乎不变,可以直接剔除;RUL封顶是标准做法,因为发动机在健康阶段的"剩余寿命"没有物理意义;评估除了RMSE,还要看PHM评分函数——它对"预测偏乐观"(实际寿命比预测短)的惩罚更重,因为漏报比误报危险得多。
从数据集到真实现场:四个坑
C-MAPSS上跑个高分容易,落地到车间是另一回事。几个高频坑:
一是工