基于MyEMS与CNN-LSTM的预测性维护预警系统实战
做工厂设备管理的朋友应该都有体会设备“带病运行”最怕的不是坏而是坏在深夜、坏在产线满负荷的时候。我去年负责的一批车间设备就是这种状况最终搭建了一套基于 MyEMS 的预测性维护预警系统用 CNN-LSTM 模型对设备运行数据进行故障预警实测预警准确率做到了 92%。这篇文章就把这套方案从数据采集、特征工程到模型训练和平台落地的完整过程拆开讲一遍适合正在做设备健康管理、工业物联网和能源数据应用的工程师参考。真正动手之前我对预测性维护的理解也比较粗浅以为重点都在算法上。真正跑完这一轮才发现算法只占不到一半的工作量价值和难度更多藏在数据清洗、特征提取和工程化部署里。这也是为什么我会选中 MyEMS 作为数据底座——它本身是一套开源能源管理系统内置了设备台账、测点接入、能耗统计和告警通知模块能稳定采集设备层的实时运行数据省去从零搭一套数据中台的麻烦拿来当预测性维护的数据源非常合适。CNN-LSTM 则是近期在时序预测上被反复验证的组合模型CNN 负责从原始窗口数据里提取局部特征LSTM 负责捕捉长周期的时间依赖两者搭配把设备的异常模式抓得比较准。接下来就按我的实操顺序把整个流程完整展开。1. 方案背景与选型逻辑1.1 设备的“突然失效”到底有多贵刚开始做这件事的时候生产部门给我算过一笔账。车间里一台空压机突然停机哪怕只是轴承损坏这种不大不小的故障从发现异常到停机检修再到重新恢复产线通常要耗掉4到6个小时。按当时产线的单位时间产值折算一次非计划停机造成的损失是维护成本的十几倍更别提还要临时调维修人员、加急采购备件全是在正常计划之外烧钱。定期维护也没能真正解决问题。按固定周期换油、换轴承、做保养确实能降低一些突发故障但“到期就换”意味着很多零件还在健康状态就被提前淘汰维护成本居高不下。更现实的问题是同一批设备所处工况差别很大有的常年低负荷运行有的天天顶着极限负载跑用同一个维护周期去套必然出现“该修的时候没修不该修的时候瞎修”的尴尬局面。这里面最核心的痛点从来不是“要不要维护”而是“到底什么时候需要维护”。预测性维护做的事情就是把这个决策从“按日历”变成“按状态”。它不靠固定周期而是靠设备运行数据判断异常是否正在形成、故障是否快要发生。这样既能把突发停机压下去又能避免过度维护本质上是在成本和风险之间找到最优切入点。可要实现这个判断第一步就得有足够好、足够连续的数据源这也是我最后落在 MyEMS 上的原因。1.2 为什么在我的场景里选 MyEMS 当数据底座MyEMS 的开源属性是最吸引我的一点。团队当时已经用 MyEMS 做了一年多的能源管理水电气的计量数据、重点设备的电参量数据全部接在里面。也就是说我需要的历史数据已经在库里躺着不需要再补一套采集系统。MyEMS 本身支持 Modbus TCP、OPC UA、BACnet 这些工业常见的通信协议可以直接对接 PLC、电表和传感器网关采集层不用动。第二个原因是它把“设备”和“测点”的关系管理得比较清楚。MyEMS 中有空间、设备、能耗表和测点几个层级我可以给每台设备挂上多个测点比如电流、电压、温度、压力这些测点在库里都有规范的编码和单位。模型训练时只要按设备 ID 把对应测点的时序数据拉出来不需要做特别复杂的映射。第三个原因是集成方便。MyEMS 提供 REST API历史数据能通过 API 导出告警规则也能通过配置触发外部 Webhook。这意味着我训练好的模型推理结果可以反写回 MyEMS 的数据库再走它现成的通知机制推给现场人员告警链路能闭环不需要再造一套消息系统。1.3 自建方案与传统工业预测性维护平台怎么权衡当时也不是没考虑过直接买成熟商业平台。市面上工业预测性维护供应商不少功能看起来都很完整界面也漂亮但也存在一些现实阻碍。我把两类方案放在一张表里对比过维度自建MyEMS CNN-LSTM商业化预测性维护平台初始成本低主要投入是人力和服务器较高通常按设备和点位收费数据掌控力强数据全在自己库里弱关键数据要上传到对方平台模型可解释性完全可控能看每一层特征黑盒只能看平台给的结果定制能力高规则和模型都能改低只能按平台流程走需要的人才需要具备软件和算法能力的工程师现场实施为主算法要求低迭代速度自己说了算按业务节奏来依赖供应商排期最后选择自建并不是说商业平台不好而是我们团队已经有 MyEMS 的数据基础也有能写代码、能调模型的同学。如果现场没有任何数据积累团队又只有设备管理员和电工师傅那买现成平台反而是更稳妥的做法。技术选型没有绝对的好坏关键看自己手里有什么牌。2. 从原始数据到训练样本数据工程全流程2.1 传感器点位选择与采样策略模型再厉害输入信号选错了也白搭。我以车间里几台空气压缩机和循环水泵为试点目标是识别轴承退化、转子不平衡、冷却失效这几类常见故障。传感器点位的选择原则很简单故障发生前后哪些物理量一定会有变化就选哪些。对旋转类设备来说轴承早期磨损最先反映在振动信号上但是这个信号对采样频率要求很高低采样率的普通 PLC 抓不到细节。我当时能够拿到的振动数据是独立振动传感器采集的采样间隔在秒级而电流、电压、温度这类慢变量PLC 本身就在采间隔10秒记录一条。还有排气压力、冷却水温度、设备累计运行时长这些参数对判断负载和散热状态很有帮助。确定好的测点清单大概是这样的信号对应故障类型采集间隔备注三相电流过载、转子异常10秒反映负荷波动线电压缺相、电源异常10秒电压跌落会影响设备定子/轴承温度润滑不足、轴承磨损60秒升温趋势是关键振动加速度轴承早期故障、不平衡1秒~5秒高频细节少但趋势可用排气/出口压力管路堵塞、阀件异常10秒压力突变特征明显冷却水温度散热失效60秒与设备温度互为印证累积运行时长寿命周期评估每日汇总辅助特征最终我选了 12 个核心特征作为模型输入。采样频率也不是越高越好太高的频率数据量爆炸训练和存储成本都上来对慢变故障来说10秒级别已经能捕捉到足够的退化趋势。2.2 缺失值、噪声与标准化处理工业现场的数据永远不可能是干净的。设备停机的时候PLC 可能整段没有数据上传网络抖动会让某些采样点丢失传感器偶发的尖峰毛刺更是家常便饭。这些脏数据如果不处理模型很容易学到“假特征”。清洗流程我固定成四步。第一步是排序去重时间戳按顺序排好同一条时间戳重复记录的直接删掉。第二步是缺失值处理连续缺失超过10分钟的数据段我直接切除因为这种大段空洞多半是设备停机或采集断线补出来也会污染模型连续缺失在几个采样点以内的用前向填充补一下保持序列连续。第三步是去极值对每个特征用滑动窗口算一个正常的取值范围超出上下限的点替换成边界值或直接剔除防止一次传感器毛刺把后续窗口数据拉到异常区间。第四步是平滑对振动和电流这类波动大的信号做了窗口为5的移动平均把随机抖动压下去让退化趋势更清晰。标准化这一步也容易被忽略。工业数据量纲差异很大电流几十安培温度几十摄氏度振动可能是零点几如果直接丢进神经网络数值大的特征会主导梯度和距离计算。我用的不是常见的 MinMaxScaler而是 RobustScaler它基于中位数和四分位距做变换对尖峰异常值不那么敏感。比如温度偶尔出现一个显著远离正常区间的点RobustScaler 不会把正常数据压缩到很窄的范围里这对后续模型收敛和泛化更有好处。from sklearn.preprocessing import RobustScaler # 假设已经完成清洗得到 train_df feature_cols [current_a, current_b, current_c, voltage_ab, temp_bearing, vibration, pressure_out, temp_cooling] # 只在训练集上拟合 scaler验证集/测试集只能 transform scaler RobustScaler(quantile_range(5, 95)) scaler.fit(train_df[feature_cols]) import joblib joblib.dump(scaler, myems_scaler.joblib) train_df[feature_cols] scaler.transform(train_df[feature_cols])这里有一条红线必须记住标准化参数的拟合只能使用训练集数据一旦用全量数据拟合 scaler验证集信息就泄漏进训练流程了后面所有评估指标都会虚高。2.3 滑动窗口切分与故障标签生成模型不是拿单条记录去判断故障而是拿一段时间内的连续信号去做判断。设备退化是一个过程单看某一秒的电流可能完全正常但把过去几十分钟的走势串起来就能看出明显的恶化趋势。这就是滑动窗口的意义。我的窗口长度是 300 个采样点配合 10 秒的采样间隔相当于每个样本覆盖了 50 分钟的历史数据。窗口滑动的步长设置为 30 秒也就是每 30 秒生成一个新样本保证相邻样本有大量重叠不会漏掉关键转折点。标签生成是整个数据工程里最容易出错的地方。我的做法是定义一个“预警提前期”如果一台设备在未来2小时内发生了故障那么当前时刻的样本标签就是 1如果未来2小时内没有发生故障标签就是 0。def build_dataset(ts_df, fault_events, horizon120, window_len300): X, y [], [] timestamps ts_df[timestamp].values step 3 # 30秒 3个点 * 10秒 rows ts_df[feature_cols].values for i in range(0, len(rows) - window_len, step): end_time timestamps[i window_len - 1] # 判断从窗口结束时刻起未来 horizon 分钟是否发生故障 is_fault_window False for event in fault_events: if end_time event[fault_start] end_time timedelta(minuteshorizon): is_fault_window True break X.append(rows[i:i window_len]) y.append(1 if is_fault_window else 0) return np.array(X, dtypenp.float32), np.array(y, dtypenp.float32)关键的判断逻辑是“窗口结束之后”的未来时段内是否发生故障而不是窗口内部是否包含故障数据。一开始我踩过这个坑发现模型在验证集上准得离谱上线之后立刻失灵。后来排查才知道标签把窗口内部已经发生的故障也标记为 1模型学到的其实是“看到故障样本就预报故障”但真实使用场景里窗口数据都是故障发生之前的数据两者分布完全不同。这个问题属于典型的事件泄漏做时序模型的时候一定要小心。3. CNN-LSTM 模型的设计与训练过程3.1 把 CNN 和 LSTM 拼在一起的底层逻辑先讲个直观的类比。设备运行数据就像一段连续的视频监控CNN 类似于一个擅长识别画面局部特征的观察员能在很短的时间片段里发现画面里的异常闪动、边缘变化LSTM 则像一个有长期记忆的复盘员能把过去几十分钟甚至几小时的变化轨迹串起来判断“刚才那个小异常是不是在持续扩大”。CNN 的优势在于局部特征提取。一维卷积核在时间序列上滑动能捕捉到尖峰、突降、周期性波动这类局部形态。比如轴承磨损到一定程度时振动信号会出现高频冲击卷积核很容易把这个冲击形态识别出来。多个卷积核叠加之后虽然单个核只看到很短的一段但多层结构可以让感受野逐步扩大最后覆盖到更长的上下文。LSTM 的优势在长期依赖。设备退化往往不是突变的温度持续缓慢上升、电流在负载不变的情况下逐渐漂移这类现象要放在较长的时间尺度里才能看出规律。LSTM 通过门控机制控制信息的记忆和遗忘能保留几十个时间步之前的关键趋势信息和 CNN 提取的局部特征正好互补。如果只用一个模型会怎样单独用 CNN它能抓到局部形态但窗口太短时对缓慢趋势无能为力单独用 LSTM它能记住长期走势但容易忽略局部突变点。两个模型串联起来等于先把原始信号压缩成一组更高层的局部特征再让 LSTM 在这些特征之间建立时间依赖。对设备故障这种“先有局部异常、再逐步演化成大故障”的场景这种组合很贴合实际。3.2 网络结构与关键参数我的模型结构并不复杂复杂模型在这个数据规模下反而容易过拟合。整体结构是两层 Conv1D 提取局部特征经过池化降维后接入一层 LSTM再接两层全连接输出二分类概率。import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import (Conv1D, MaxPooling1D, LSTM, Dropout, Dense) model Sequential([ Conv1D(filters64, kernel_size3, activationrelu, paddingsame, input_shape(300, 12)), MaxPooling1D(pool_size2), Conv1D(filters128, kernel_size3, activationrelu, paddingsame), MaxPooling1D(pool_size2), LSTM(units64, return_sequencesFalse), Dropout(0.3), Dense(units32, activationrelu), Dropout(0.2), Dense(units1, activationsigmoid) ]) model.compile(optimizertf.keras.optimizers.Adam(learning_rate1e-3), lossbinary_crossentropy, metrics[accuracy]) model.summary()几个参数选择背后的考虑卷积核大小我选 3。时间序列的局部形态通常在几个采样点内就能表现出来kernel_size3 已经足够更大的核反而会让参数变多、训练变慢。两层卷积叠加的作用是扩大感受野。第一层卷积看到的是 3 个点经过一次池化后第二层卷积能覆盖更长的范围模型就能从“局部尖峰”进一步看到“持续多次尖峰组成的退化片段”。MaxPooling 的 pool_size 是 2每经过一次池化序列长度减半既能减少计算量也能把卷积提取到的关键特征保留下来丢掉一些冗余信息。LSTM 的 units 选了 64。实验时试过 128效果提升很小但参数量翻了很多训练时长明显增加。从工程角度讲数据量有限的时候模型容量不是越大越好。两个 Dropout 放在 LSTM 和全连接层之间比例分别取 0.3 和 0.2。这一层对抑制过拟合作用很大不加深的模型结构全靠它压住验证集和训练集的差距。训练环节我也做了几个关键设置优化器用 Adam初始学习率 1e-3批次大小 64最多迭代 70 个 epoch但配合早停机制如果验证集的 F1 在 10 个 epoch 内没有提升就提前终止训练避免白白浪费时间学习率在训练中段用 ReduceLROnPlateau 降到 1e-4让模型在后期收敛得更稳。3.3 不平衡样本的处理与训练策略设备故障样本天然是稀缺的。我统计了试点设备的两年历史数据真正带故障标签的样本只占全部样本的不到 6%。直接拿原始比例训练模型会“躺赢”因为什么都不预测只输出 0 就能拿到 94% 的准确率但这样的模型在实际场景里毫无价值。处理不平衡我用了组合策略不是只靠某一种方法。第一是负样本下采样把无故障负样本抽出正样本四倍左右的数量让模型不至于被负样本淹没。第二是类别权重在损失函数里给正样本更高的权重我用的是 class_weight{0: 1.0, 1: 3.0}。这样模型分错一个故障样本产生的损失远大于分错一个正常样本模型会更愿意去“冒险”把模糊样本判定为正类。第三是评估指标改用 F1而不是只看准确率后面评估部分会细讲。训练数据划分也很有讲究。我没有用随机切分而是按时间顺序切分前 70% 的数据训练中间 20% 做验证最后 10% 做最终测试。随机切分在时序问题上容易造成数据泄漏因为相邻窗口之间高度相关类似的样本会同时出现在训练集和测试集里模型等于见过“答案”再考试指标自然好看但部署上线立刻现原形。4. 模型接入 MyEMS 与告警联动部署4.1 把模型封装成标准推理服务训练完成的 Keras 模型要变成真正能用的服务而不是躺在 Jupyter Notebook 里。我在生产环境用 FastAPI 包了一个轻量推理服务模型加载一次常驻内存每次请求传入一个长度为 300 的时间窗口特征服务返回故障概率分数。import joblib import numpy as np import tensorflow as tf from fastapi import FastAPI from pydantic import BaseModel app FastAPI() model tf.keras.models.load_model(/data/cnn_lstm_model.h5) scaler joblib.load(/data/myems_scaler.joblib) class WindowInput(BaseModel): windows: list # shape: (300, 12) app.post(/predict) def predict(data: WindowInput): x np.array(data.windows, dtypenp.float32) x scaler.transform(x.reshape(-1, 12)).reshape(x.shape) score float(model.predict(x[np.newaxis, :, :], verbose0)[0][0]) return {score: score, level: alarm if score 0.6 else normal}这里有一个非常容易犯的错推理阶段的输入数据必须使用训练时保存的同一个 scaler而且特征的顺序要严格保持一致。如果训练时特征顺序是 [电流, 温度, 振动]推理时给 [温度, 电流, 振动]模型不会报错但结果完全是乱的。4.2 在 MyEMS 上配置预警规则和通知通道推理服务部署好后还需要一个调度任务定时从 MyEMS 拉取最新窗口数据调推理服务拿分数再把分数写回 MyEMS。我写了一个 Python 脚本用系统的计划任务每分钟跑一次对核心设备轮询推理。1. 读取 MyEMS 中最近 300 个采样点的特征数据 2. 调用本地 /predict 服务得到故障概率分数 3. 将分数写入 MyEMS 数据库中的自定义测点表 4. MyEMS 告警规则检测到分数 0.6 且持续 3 分钟 5. 触发 Webhook向企业微信/邮件/短信通道推送告警MyEMS 本身就有告警模块支持阈值告警和规则的组合判断。我把模型输出的分数当成一个新测点接入然后配置一条“设备故障预警”规则当分数超过 0.6 且持续 3 分钟触发告警。这个持续时间的条件很关键直接用单次打分触发会非常敏感偶尔一次噪声就能引起误报。告警消息里我不仅附上了故障概率还把窗口内的原始关键特征趋势图一起带过去。现场工程师收到告警后可以一眼看到温度和电流的走势而不是只看一个数字怀疑系统是不是在乱报。4.3 部署后要特别注意的运维细节上线只是开始后续运维才是长期工作。我在部署过程中积累了几条必须注意的细节。第一点是模型文件和数据清洗逻辑要一起版本化。光有模型文件但不知道当时的特征顺序、清洗规则、平滑窗口参数三个月后连作者自己都说不清楚。我的做法是把模型文件、scaler、特征清单、版本号写进同一个配置文件夹每次更新模型就整体替换。第二点是推理服务的性能和稳定性。模型本身不大CPU 上推理一次的时间只有几十毫秒但 FastAPI 在高并发场景下依然要注意加载模型的开销。模型加载不要放在每个请求里应该在服务启动时完成加载并常驻内存。第三点是监控模型漂移。设备维修更换主力零件后传感器数据分布会发生明显变化模型可能突然失效。我每周用历史数据回测一次模型在最近一周数据上的 F1如果指标掉得厉害就要人工介入检查必要时补充新数据重新训练。5. 实测效果与踩坑记录5.1 实际数据表现与指标解读模型在时间顺序划分的测试集上跑出的核心指标如下指标数值说明准确率92.1%全部样本中判断正确的比例精确率88.7%所有预警中确实是故障前兆的比例召回率90.4%实际故障中被成功提前识别的比例F189.5%精确率和召回率的综合值AUC0.96区分故障与非故障样本的能力很多人看到准确率 92% 就认为模型很牛但在类别不平衡场景下准确率是最容易被“虚假繁荣”误导的指标。如果负样本占 94%那么一个什么都不预测、永远输出 0 的模型准确率也有 94%。所以真正要看的是精确率和召回率的组合。精确率关心的是一旦发出预警是不是真有事召回率关心的是所有故障里有多少被提前抓住。这两个指标天然此消彼长阈值设高一点精确率上去了但召回率下降会漏报阈值设低一点召回率上去了但误报会增多现场人员会对预警脱敏。我最后选了 0.6 这个阈值是在实验里同时画出精确率和召回率随阈值变化的曲线取两条线交叉附近的位置兼顾两边的需要。5.2 落地过程中踩过的坑这一行做久了会发现能在测试集上“跑通”只是第一步真正决定项目生死的是那些细节陷阱。复盘这次实战我觉得最有分享价值的其实是这几个踩坑经历。第一个坑是标签泄漏这个前文已经讲过。把故障发生之后的窗口也标成正样本模型在验证集上表现极好换到真实场景立刻失灵。排查方法很简单抽样看一批被误判成故障的窗口画出来发现里面有一半已经包含故障波形。从那以后我每次生成标签都会多检查一遍窗口结束时间和故障开始时间的先后关系。第二个坑是时区错乱。最初从 MyEMS 拉数据和模型推理脚本用的不是同一套时间基准导致窗口数据的时间偏移了两个小时。模型把一个多小时前的正常数据当成当前状态判断成功率再高也没意义。排查过程很痛苦最后把所有时间戳统一成 UTC 存储显示层才做本地时间转换类似的问题才算根治。第三个坑是类别权重设得过大。为了提升召回率我一度把 class_weight 调到 {0: 1.0, 1: 8.0}结果模型变得极度“敏感”误报率飙升到每小时三次现场人员一天后就开始无视告警。后来降低权重并把误报抑制放到阈值和持续判定上效果反而更好。权重只是训练时的引导不是最终的决策标准。第四个坑是预警提前量太久。初版模型做到了“故障前4小时预警”听起来很强大但实际使用中发现提前4小时发出告警后操作员去现场检查了一圈设备还在正常运转就会认为告警是误报。预警不是越早越好而是要在故障风险足够明显、留给现场反应时间又足够的窗口期内发出。我最终把模型的重点预置在 1 到 2 小时之间这个时间对绝大多数备件替换和现场确认是合理的。第五个坑是告警没有配套处置动作。系统开始稳定预警后现场人员收到了预警消息但不知道该做什么。后来我重新梳理了预警消息内容在告警里附带设备自查清单、可能涉及的备件列表、历史维护记录链接让操作员收到消息后能按流程行动而不是干瞪眼。一个预测系统要产生实际价值必须走到“预测到之后怎么办”这一步。6. 这套方案的边界和扩展思考6.1 哪些设备真正适合这套方案不是所有设备都适合套用这套方案。从我的实践看适合预测性维护的设备通常具备几个特点第一是旋转或运动部件多轴承、齿轮、转子这类部件退化过程可以被振动和温度信号捕捉第二是设备价值高或停机损失大值得投入传感器和建模成本第三是设备运行规律相对稳定工况不会频繁剧烈变化。反过来说工况变化剧烈的设备比如频繁启停、负载忽高忽低的产线传感器数据里混杂了大量正常波动模型区分故障和正常工况的难度会明显上升。还有一类设备故障前几乎没有可测量的物理征兆比如某些电子元件突然失效就完全不适合做数据驱动预测硬上模型只会得到一堆没有实际意义的预警。6.2 后续可以扩展的方向这次做的是设备级二分类预警也就是判断“未来是否会故障”。再往前走一步可以基于同样的数据和特征尝试做剩余使用寿命预测输出一个“预计还能运行多少小时”的连续数值这对备件计划和生产排程会更有价值。但 RUL 预测对标签质量要求更高需要设备一直运行到真正的故障时刻数据的收集周期更长。模型层面也有很多可做的优化。比如把振动信号的频域特征、小波包分解后的能量特征加进来可能比单纯使用时域波形更敏感也可以尝试用 Transformer 结构替代 LSTM对长序列的依赖建模能力更强。不过这些优化都要建立在数据质量扎实的基础上模型升级是锦上添花数据工程才是地基。最后说一个我做下来最深的体会预测性维护项目的成败不取决于模型选得多新潮而是取决于设备机理是否被理解、数据是否被认真清洗、标签是否定义准确、告警是否真正驱动了现场行动。CNN-LSTM 只是这整条链路里最“显眼”的一环它的确好用能取得 92% 的预警准确率也验证了这套技术路线的可行性。但如果把项目比作一辆车模型只是发动机底盘和传动系统——也就是数据采集、数据治理、告警运营——才是让整辆车真正跑起来的关键。做这类项目耐住性子把数据基础打牢比急着上大模型重要得多。