预测性维护这个概念,圈子里聊了很多年,真正能在车间里跑起来并且拿到实打实效果的项目,其实不多。今年上半年我带着一支小团队做了一个压缩机故障预警项目,数据底座用了开源的 MyEMS 平台,算法选的是 CNN-LSTM 组合模型,最终在独立测试集上把故障预警准确率做到了 92%,平均能比故障真正发生提前 3 到 4 个小时发出预警。这篇就来讲讲我们当时为什么这么选型、数据是怎么处理的、模型是怎么训练和部署的,以及中间踩过的几个比较深的坑。如果你正准备在工厂里搭一套设备预警系统,或者已经在用 MyEMS 想往上做智能分析,这篇文章应该对你有点用。
1. 项目概述与方案选型
1.1 现场痛点:为什么需要预测性维护
我们接到这个需求的时候,客户那边的核心痛点非常直接:一台螺杆式空气压缩机,是整个压缩空气系统的主力设备,一旦停机,后端的几条产线都要跟着降速甚至停线。传统的维护模式是什么?被动维护加计划维护。被动维护就是坏了再修,非计划停机一次的损失,用他们的话说,半年的维护预算都搭进去了。计划维护做得也不算差,每三个月做一次全面保养,按厂家手册定期换油、换滤芯、检查皮带,但问题也很明显:一部分零部件还没到寿命就被强制换掉,造成浪费;另一部分故障恰恰发生在保养间隔的中间,计划维护对此无能为力。
预测性维护要解决的就是这个“中间地带”的问题:在设备还没有明显故障、但已经出现劣化趋势的时候,提前把风险提示出来。它的核心逻辑并不神秘——设备从正常到故障,通常会经历一个性能退化过程,在振动、温度、电流、压力这些信号里会留下蛛丝马迹。问题是怎么从这些高噪声的信号里把退化趋势“读”出来。传统阈值报警的方式太粗了,往往等阈值被突破时设备已经出了大问题;真正能提前几小时或者几天发现问题的,必须靠模型从多维时序数据里学规律。
1.2 数据底座:为什么选 MyEMS
选 MyEMS 做数据底座,主要看重它开源和完整的协议接入能力。MyEMS 本身是一套能源管理系统,支持 Modbus、OPC UA、BACnet、DL/T 645 等常见的工业通信协议,能对接大量的 PLC、电表、传感器和设备控制器。它自带数据采集器、MySQL 存储、REST API 和 Web 可视化界面,也就是说,从数据接入、入库到开放接口,这一整套基础设施它都给你搭好了,我们不需要从零写采集程序,也不用自己搭报表和监控面板。
当时我们对比过两种路线:一种是自己用脚本读 Modbus 数据然后存 InfluxDB,另一种就是直接用 MyEMS。前者的灵活度高,但采集点位管理、断线补传、历史数据查询这些都要自己造轮子,工程量不小。MyEMS 这些能力是现成的,而且作为开源软件,我们还能改源码,这点在项目里有实际意义——后面做告警联动时,我们就是直接调用它的 API 往设备监测报表里写告警信息,省了不少对接时间。对于中小规模的产线智能化改造,MyEMS 这个层级的数据底座已经够用到“冗余”了。
1.3 算法选型:CNN-LSTM 为什么合适
模型选型的过程,其实是在几个候选方案里反复比较后才定下来的。最开始我们尝试过 XGBoost,树模型在表格数据上表现不错,但用在多通道时序信号上有两个问题:一是它天然不考虑时间顺序,需要人工构造大量滞后特征;二是当设备运行工况变化时,树的泛化能力明显下降。后来换成纯 LSTM,长短期依赖能学了,但对局部异常形态的捕捉不够敏感,故障前那种短时间内的波形畸变,经常被淹没在正常的工况波动里。
CNN-LSTM 的组合思路是一个很自然的折中方案。CNN 是一维卷积,专门用来在时间窗口内提取局部特征——某个测点在十几分钟内出现的尖峰、毛刺、阶跃变化,卷积层都能高效地捕捉到;LSTM 再接在 CNN 后面,负责学习这些局部特征在更长时间尺度上的演化规律,从“局部异常”过渡到“持续劣化”再到“即将故障”。这正好契合设备退化过程的本质:早期是细微的局部异常,中期形成持续趋势,后期快速恶化。除了效果上的考虑,CNN-LSTM 还有一个工程上的优势:它可以直接吃原始的多通道时间序列,特征工程的压力小,到了部署阶段调试起来也方便。
2. CNN-LSTM 模型结构与数据工程
2.1 模型网络结构拆解
我先把最终跑通的模型结构放出来,后面再解释每个部分的考量。
model = Sequential([ Conv1D(filters=64, kernel_size=3, activation='relu', padding='same', input_shape=(256, 9)), BatchNormalization(), MaxPooling1D(pool_size=2), Conv1D(filters=32, kernel_size=3, activation='relu', padding='same'), BatchNormalization(), MaxPooling1D(pool_size=2), LSTM(units=64, return_sequences=False), Dropout(0.3), Dense(units=32, activation='relu'), Dropout(0.2), Dense(units=1, activation='sigmoid') ])输入形状是(256, 9),表示一个样本包含 256 个时间步,每个时间步有 9 个通道(5 个原始测点加 4 个派生统计特征,后面会展开说)。第一层 Conv1D 用 64 个大小为 3 的卷积核,在时间维度上滑动,相当于每次看 3 个连续时间步的局部模式,然后池化把时间维度减半;第二层卷积继续在更高层抽象上提取特征,再做一次池化;然后进入 LSTM 层,把 CNN 提炼出来的特征序列做时序建模,最后通过带 sigmoid 的全连接层输出一个 0 到 1 之间的故障概率值。
这里有两个设计细节值得注意。第一,卷积层的激活函数用了 ReLU,一是计算简单、不容易梯度消失,二是对设备信号里的正异常(比如温度升高、振动增大)能有更清晰的响应。第二,BN 层放在卷积层后面,作用是把每一层输出的分布拉回标准范围,训练稳定性提高不少。我们在实际训练中发现,不加 BN 时 loss 曲线经常出现“锯齿状”的震荡,加了以后明显平滑了。Dropout 的作用是防过拟合,尤其是故障样本数量不多的情况下,不加 Dropout 很容易把训练集背下来,测试集上一塌糊涂。
2.2 数据采集与预处理:MyEMS 侧的关键配置
数据是整套模型的地基。这个项目里我们通过 MyEMS 采集了压缩机的 5 个核心测点:A 相电流、B 相电流、排气温度、排气压力、油温。采样频率配置成每 10 秒采集一条记录,这样每天每个测点大约产生 8640 个数据点,5 个测点就是 4 万多条,连续采集 3 个月,累计超过 300 万条,数据量对 MySQL 来说完全没压力,对于模型训练也够用了。
数据不是采上来就能直接用的,这里有一个坑:MyEMS 采集器偶尔会因为网络抖动或者设备通信超时产生缺失值。我们的处理策略分两层。第一层是实时链路,如果某次采集失败,MyEMS 会在下个周期自动补采,这对趋势分析影响不大;第二层是离线训练前,我们会做一次完整的质量检查:缺失比例超过 5% 的时段直接剔除;缺失比例较低的时段用线性插值补全;对明显的离群点(比如电流瞬间跳到额定值 5 倍以上,明显是传感器误报)用前后相邻值的均值替换。另外,所有数值在进模型之前都做了一次标准化,方法就是(x - mean) / std,均值和标准差用训练集统计,测试集和线上推理时沿用训练集的统计量,避免信息泄露。
2.3 特征工程与滑动窗口设计
很多人以为用深度学习就不需要做特征工程了,这是一个常见的误解。CNN-LSTM 能自动学特征,但不代表喂进去的原始数据不需要整理。这个项目里我们做了两层处理:一层是窗口化,另一层是统计特征补充。
窗口大小为什么取 256?这里有个计算逻辑。采样周期是 10 秒,256 个时间步对应约 42 分钟的历史数据。我们把项目里的压缩机运行规律拉出来看了一下,一个完整的加减载循环大约在 30 到 50 分钟之间,窗口至少要覆盖一个完整循环,模型才能区分“正常工况切换”和“异常趋势发展”。同时,LSTM 的计算量跟序列长度成正比,窗口再拉长到 512 甚至 1024,训练时间和显存占用都会显著增加,而测试发现准确率并没有明显提升,所以最终定在 256。窗口滑动步长设为 128,也就是两次相邻采样窗口有 50% 的重叠,这样数据量能够翻倍,对于样本量紧张的项目来说是很划算的做法。
至于统计特征,我们给每个窗口额外计算了 4 个派生特征:当前窗口内的均值、标准差、峰峰值和变化斜率(首尾差值除以窗口时长),然后拼在原始信号后面,相当于把 5 通道输入扩展为 9 通道输入到模型里。这三组特征分别刻画了信号的基线水平、波动幅度和整体趋势,等于帮模型提前框定了重点,实验显示加上之后准确率提升了大约 2 个百分点。
2.4 标签构造:故障预测为什么不是简单的二分类
训练标签怎么打,是整个项目里最需要认真对待的问题之一。最朴素的做法是把“设备是否发生了故障”作为标签,故障时刻之前的样本都标为 0,故障发生时刻以及之后标为 1。这样做有两个毛病:一是模型只能学到“坏了之后”的特征,起不到预警作用;二是故障时刻附近的样本数量极少,类别严重不平衡。
我们采用的是“预警窗口”标签法。拿那几次有记录的故障事件举例,从故障停机往前推 4 小时定义为预警窗口,窗口内的样本全部标记为 1,窗口之外的正常运行样本标记为 0。也就是说,模型学习的目标不是“当前是否故障”,而是“未来 4 小时内是否会发生故障”。这是预测性维护里非常关键的一个思路转变:与其让模型判断“现在发生了什么”,不如让它判断“接下来会发生什么”。4 小时这个窗口长度也不是拍脑袋定的,而是和工艺团队讨论后定的:4 小时足够维护人员响应和处理,又不会因为窗口太长导致样本定义过松、正常波动也被误标为正样本。
3. 模型训练与 92% 准确率验证
3.1 数据划分与训练环境
样本准备好之后,第一步先划分数据集。我们按时间顺序来划分,而不是随机划分——这一点很重要。如果用随机划分,模型会“偷看”到未来时段的数据分布,造成评估结果虚高,到线上部署时立刻现出原形。划分比例大概是训练集 70%、验证集 15%、测试集 15%,三个集合按时间先后依次排列。为了进一步压低过拟合风险,训练集里尽量多涵盖不同工况,验证集和测试集则包含最近两个月的数据,这样最能反映“用过去的数据预测未来”的真实场景。
训练环境没什么特别的,一台带 RTX 3090 显卡的工作站,Python 3.9 + TensorFlow 2.10。故障样本大约有 3500 个窗口,正常样本大约有 21000 个窗口,正负样本比例约 1:6,这个比例我们后面通过加权损失函数来处理。训练轮数 60 个 epoch,batch size 64,初始学习率 0.001,并且用 ReduceLROnPlateau 在验证集 loss 连续 3 个 epoch 不下降时自动把学习率减半。整个训练大概跑了 40 分钟,时间上完全能接受。
3.2 超参数是怎么调出来的
超参数方面,有几个可以分享的经验。卷积核大小的选择上,我们试过 3、5、7,最终 kernel_size=3 效果最好。原因也不难理解:在 10 秒一个采样点的尺度上,真正的故障特征通常集中在较短的时间局部,太大的卷积核反而会把相邻的正常波动也“卷”进来,模糊了特征边界。LSTM 单元数我们对比了 32、64、128,64 在这个数据集上性价比最高,128 时训练时间翻了接近一倍,准确率只提升了不到 1 个百分点。Dropout 比例 0.3 和 0.2 是我们试了几个值以后相对稳的组合,太低防不住过拟合,太高会导致模型欠拟合、准确率反而下降。
| 参数 | 尝试值 | 最终选择 | 选择原因 |
|---|---|---|---|
| 卷积核大小 | 3 / 5 / 7 | 3 | 局部特征集中,大的卷积核会引入噪声 |
| LSTM 单元数 | 32 / 64 / 128 | 64 | 64 性价比最高,128 训练时间翻倍但收益甚微 |
| Dropout 比例 | 0.2 / 0.3 / 0.4 | 0.3(LSTM后)0.2(Dense后) | 太低过拟合,太高欠拟合 |
| 滑窗大小 | 128 / 256 / 512 | 256 | 覆盖完整工况周期,同时控制计算量 |
还有一个直接提升效果的点,就是类别不平衡。1:6 的比例虽然不算极端,但如果不处理,模型为了最小化整体 loss,会倾向于把所有样本都预测成“正常”,因为这样已经把准确率拉到 85% 了。我们用了两个手段:一是在损失函数里给正样本加权,权重设为 6,让模型把“漏报故障”当成比“误报正常”更严重的错误;二是用数据增强中的时间扭曲,对正样本做小幅度的随机缩放和平移,扩充故障样本的多样性。这两个手段叠加下来,模型对故障样本的召回率从处理前的 52% 提升到了 86% 左右,提升幅度非常明显。
3.3 92% 准确率从哪来
这里必须说清楚,所谓的 92% 是指独立测试集上,模型在最优阈值下的综合准确率,而不只是训练集上的拟合结果。测试集包含了最近两个月的完整数据,其中故障窗口 480 个、正常窗口 3200 个左右。模型对每个样本输出一个 0 到 1 的概率值,默认阈值 0.5 的情况下准确率大概是 88%,但通过分析验证集上的 PR 曲线,我们发现把阈值调高到 0.65 时整体表现最好,这时候测试集准确率是 92.3%,故障召回率 86.7%,F1 值大约 0.90,平均预警提前量 3 小时 47 分钟。
有两点值得强调。第一,准确率 92% 不是越高越好,还要看误报率和漏报率的实际代价。在一个实际产线上,漏掉一次故障可能意味着一整条线停机,而多报一次故障最多是让维护人员多跑一趟检查。所以我们在选阈值的时候,不是单纯追求准确率最大,而是让漏报率控制在一个可以接受的水平(低于 15%),同时尽量压低误报率。第二,92% 这个数字是在测试集上算出来的,上线之后仍然需要持续跟踪实际表现,因为设备工况会变,数据分布会发生漂移,模型需要定期重训。
4. 部署上线与 MyEMS 告警联动
4.1 模型服务化与容器部署
模型训练完后进入部署阶段。我们的部署方案比较轻量:把 Keras 模型导出为 SavedModel 格式,用 TensorFlow Serving 提供推理服务,Docker 容器封装后跑在一台普通的服务器上。TensorFlow Serving 的好处是原生支持 SavedModel,版本管理和热加载都方便,而且对请求的响应时间控制得很好。我们用 Python 脚本模拟线上请求做了压测,单次推理平均耗时在 10 毫秒左右,对于 10 分钟一次定时预测的场景来说,性能绰绰有余。
如果你不想引入 TensorFlow Serving,另一个可行的选择是用 ONNX Runtime。先通过 tf2onnx 把 SavedModel 转成 ONNX 格式,再用 ONNX Runtime 加载推理,整体依赖更轻,CPU 上也能跑得很快。但 ONNX 转换过程中偶尔会有算子兼容性问题,特别是自定义层和某些高级 API 生成的算子,需要额外处理。我们的建议是:团队里有 TensorFlow 经验就首选 TensorFlow Serving,省心;要部署到边缘盒子或资源受限的设备上,再考虑 ONNX Runtime。
4.2 定时推理与告警触发流程
在线推理的逻辑并不复杂,每 10 分钟执行一次,循环里做四件事。第一步,从 MyEMS 的 REST API 拉取过去 42 分钟的 5 个测点数据,把单位换算和缺失值处理做完;第二步,用训练时保存的标准化参数对数据做缩放,然后构造成(1, 256, 9)的输入张量;第三步,调用推理服务得到故障概率;第四步,把概率值和当前时间写入 MyEMS 的设备监测数据库,同时判断是否超过阈值,超过则触发告警。
告警触发我们还做了一个“连续确认”机制,避免个别孤立的高概率值造成误报。具体逻辑是:连续 3 次推理(也就是 30 分钟内)概率都超过 0.65,才正式推送告警;如果只有一次超过阈值,后面又回落到正常范围,就只记录日志,不打扰维护人员。这个机制上线后,误报数量明显减少,一线工人的信任度也提高了。
4.3 告警消息推送与值班响应
正式告警确认后,怎么通知到人也是一个重要环节。我们通过 MyEMS 的告警 API 写入系统告警列表,同时用了一个简单的 Webhook 脚本推送到企业微信群里,消息内容包括设备名称、故障概率、当前关键测点数值、最近 42 分钟的图片化趋势图。维护人员在群里直接看到信息,就能判断是否需要立即到现场处理。生成趋势图这个细节看似不起眼,但实际价值很大——光给一个“概率 0.82”的数字,现场人员很难快速建立信任;配上一张趋势图,他们能直观看到哪个测点在爬升、是不是真的异常,响应效率完全不同。
5. 常见问题与排查技巧实录
5.1 误报率高,模型老是无缘无故报警
项目上线头两周,我们收到了最多的反馈就是“误报”。排查下来发现主要有三个原因:一是工况切换带来的正常波动,比如设备在加减载瞬间,电流和压力会发生短时大幅变化,模型误判为故障前兆;二是个别测点的传感器偶尔会出现瞬时尖峰,被模型当成了故障特征;三是某些外部环境变化(比如气温骤降)导致信号基线偏移。
针对这三个原因我们分别做了处理。对于工况切换,我们在输入特征里增加了一个“当前机组负载率”的通道,让模型学会区分“负载变化”和“故障趋势”;对于传感器尖峰,在预处理阶段加入一个中值滤波,把单点跳变抹平;对于环境变化,用滑动平均法把历史基线做动态校准。这一轮优化之后,误报率从最初的每天 3 到 4 次降到了平均每两天 1 次左右,效果非常明显。
| 误报根因 | 信号表现 | 处理方式 |
|---|---|---|
| 工况切换 | 加减载瞬间电流压力短时大幅波动 | 增加负载率通道,让模型区分工况切换与故障 |
| 传感器尖峰 | 单个测点瞬时限值跳变 | 中值滤波,抹平单点异常 |
| 环境基线偏移 | 气温骤降导致整体信号平移 | 滑动平均法动态校准历史基线 |
5.2 模型换到另一台同型号设备上效果变差
测试集上 92% 的准确率,并不意味着换一台设备就能直接复制。我们尝试把训练好的模型迁移到另一台规格相同、但运行年限不同的压缩机上,准确率掉到了 78% 左右。原因很好理解:每台设备的磨损程度、传感器安装位置、运行工况都不一样,新设备的信号分布和训练数据存在差异。
解决思路是迁移学习。我们用原来设备的数据训练出一个预训练模型,然后把新设备的少量数据(大约 2000 个样本窗口)拿来做微调,只训练最后两层全连接层,前面的卷积和 LSTM 层冻结住。微调了 10 个 epoch 之后,准确率回升到了 89%。这个经验很重要:做预测性维护的项目,不要指望一个模型吃遍整个车间,务必要给每台关键设备留出数据适配和微调的时间。
5.3 训练数据里故障样本太少怎么办
故障场景本身就是小概率事件,很多设备一年也发生不了 1 次故障,样本量不足是预测性维护项目里最常见的难题。我们这次能有 3500 个故障窗口,已经算运气不错,因为客户提供了过去两年的历史维护记录和部分故障阶段的数据。如果遇到更极端的情况,比如一台新设备没有任何故障记录,有两条可以尝试的路线。
第一条是借助同类型设备的公开故障数据集做预训练,比如轴承故障诊断领域常用的 CWRU 数据集,虽然设备类型不完全一样,但故障信号的时频特征有一定的通用性,预训练后再用自己设备的正常数据做微调。第二条是用自监督或者无监督的思路:先用大量正常数据训练一个自编码器或者基于重构误差的异常检测模型,把明显偏离正常分布的时段自动标出来作为“候选故障样本”,人工确认后再交给 CNN-LSTM 去训练。这种方法不能完全替代真实的故障数据,但至少能把冷启动问题的缓解一大半。
5.4 一线工人不信模型,怎么办
这其实是最容易被忽略、也最关键的问题。一个再好的预警模型,如果现场维护人员不相信它、收到告警也不去看,那一切都是零。我们在项目启动时就和客户强调,预警系统要设计成“辅助工具”,而不是“裁判”,因此特别做了三个细节:第一,每条告警都附带可追溯的实时数据和趋势图,维护人员可以自己验证模型判断是否合理;第二,系统里设置了一个“处置反馈”按钮,工人处理完告警后要填写处理结论(正常巡检、发现隐患、已维修等),这些反馈会成为下一轮模型优化的标注数据;第三,项目初期每周和一线班组开一次复盘会,把误报和漏报的案例拿出来一起讨论,及时调整阈值和告警规则。
事实证明,这些“非技术”的投入才是项目最终被接受的关键。到项目验收时,一线工人对待模型告警的态度已经从不信任变成了依赖,甚至主动要求系统在更多设备上部署。
写到这里差不多到了我特别想和大家分享的一段个人体会。92% 的准确率听起来很漂亮,但真正落地之后你会发现,预测性维护的难点从来都不只在模型本身,而在于数据质量、工程链路、业务协作和一线信任这四件事。模型选 CNN-LSTM 还是别的架构,其实只是决定项目效果上限的一个环节;把数据采集、特征处理、告警闭环和反馈机制做扎实,才是让上限真正兑现的保障。如果你正在规划类似的设备预警项目,我的建议很简单:先把数据链路跑通,再谈模型效果;先解决“误报烦人”的问题,再追求“准确率更高”;先让现场工人愿意看告警,再谈算法优化。这条路没有捷径,但每一步走稳了,结果是实打实的。