
去年让我印象最深的不是哪套系统宕机了而是一个制造厂里的真实场景主管设备的副厂长手机里有三个工作群设备报警群、维修调度群、备件采购群。报警群平均每天响几十条真正需要处理的就两三起剩下全是误报。他跟我说的那句话特别实在我不是想要更多报警我是想让它告诉我哪个报警值得我半夜爬起来。这句话基本就是预测性维护的核心问题。预警不是推一堆消息而是在设备真正出问题之前给出靠谱的信号。这篇文章我会讲一个完整的实战方案基于 MyEMS 平台采集设备运行数据用 CNN-LSTM 模型做故障预警最终在某条产线测试环境中把预警准确率做到了 92%。整个过程覆盖数据链路搭建、特征工程、模型选型、训练调参和上线部署适合正在做设备管理、能效管理或者工业 AI 落地的工程师参考。1. 为什么坏了再修越来越扛不住设备停机成本已经变了1.1 一次非计划停机的真实代价做过工厂运维的人都有体会设备真正不可用的时候损失不是设备维修费那一项。业界测算过非计划停机每小时的成本从几千到上百万不等关键在于这条设备在产线里的位置。我接触过一家做汽车零部件的企业一台关键加工中心半夜 2 点报伺服报警维修工程师第二天早上 7 点赶到现场光是等待和排查就花了 4 个小时。那 4 个小时里前序工位还在继续生产中间缓冲区堆满后段直接停线。最后算下来废品、延迟交付、加班费、紧急调拨备件运费总损失远大于那根出问题的伺服电缆。这就是坏了再修的被动模式最尴尬的地方。故障停机本身不可怕可怕的是它发生的时间不可控而现代产线的连续性要求又特别高。人没法 24 小时盯住每台设备的每个参数所以必须让系统替人盯着。1.2 定期点检为什么也救不了有人会说那我们有巡检制度每班点检一次每周保养一次还不够吗这里有个逻辑漏洞点检制度是按日历时间排的但设备故障是按劣化状态发生的。举个例子一台减速机的振动值在 45 天里缓慢爬升第 46 天轴承滚道出现剥落第 50 天才会达到损坏阈值。如果巡检周期是 30 天一次那么在两次巡检之间设备完全有条件带病运行很久。就算巡检频率提高到 7 天一次也只能保证发现故障的时间误差在 7 天以内并不能告诉你这台设备未来 72 小时会不会停机。真正需要的是状态检修也就是按照设备实际健康状态来决定维修时机。而状态判断不能靠老师傅的经验拍脑袋必须用数据说话。1.3 预测性维护的本质把维修窗口从事后挪到事前预测性维护Predictive Maintenance就是把设备什么时候会坏这个玄学问题转化成一个有量化输出的时序问题。它不追求预测到精确的某一秒而是给出一个时间窗口比如说这台泵在未来 7 天内故障概率为 87%建议安排检修。有了这个窗口维护团队就可以做三件事把维修动作安排在计划停机窗口内而不是被动等故障提前准备备件、工具和人员不用紧急调拨减少过度保养既不让设备带病运行也不在健康状态下白拆白换所以预测性维护不是要取代维修工而是给维修工一张更靠谱的待办清单。而这张清单能不能让人信服关键取决于模型的准确率够不够高。这就是我们做 CNN-LSTM 模型的出发点。2. MyEMS 平台在整套方案里的角色不是替代 SCADA而是把设备数据变成训练样本2.1 MyEMS 能拿到哪些数据做模型之前最先要解决的是数据从哪来。很多工业企业已经有了 SCADA 或者 DCS但这些系统往往封闭数据接口要额外收费或者只提供只读的实时监控界面拿不到历史明细。MyEMS 在这个链条里的定位是开源的能源管理和设备监控平台它对上通过 Modbus、OPC UA、BACnet、M-bus 等协议对接现场设备对下把点位数据统一采集、存储、清洗再通过标准 API 对外输出。我选 MyEMS 而不是自己写一套采集程序原因很实际开源没有授权费用二次开发可控工业协议覆盖比较全Modbus TCP/RTU、OPC UA 这些主流协议都原生支持自带时序数据存储不用自己拼数据库方案有 Web 界面可以快速查看设备点位和运行状态调试期省事以我们那条测试产线为例现场有一台空压机、两台循环水泵和一条输送线一共接了 42 个点位涵盖三相电流、电压、功率、振动、温度、压力、流量、运行状态等信号。MyEMS 每分钟采集一轮存储到内部时序数据库保留周期设定为 180 天。这个规模和存储策略对中小型产线来说够用了。2.2 数据链路的搭建从采集协议到时空特征矩阵整个数据流可以拆成四个环节采集层现场传感器和控制器通过 Modbus TCP 把数据送到 MyEMS 采集网关。存储层MyEMS 按点位写入时序数据库每个点位是一列时间戳是索引。原始表结构类似于time, point_id, value但对外 API 会转成宽表格式也就是一行一个时间点各点位是列。清洗层这一步看着不起眼实际上最花时间。传感器断线会产出空值通讯毛刺会产生跳变值设备停机时段的数据全是零。如果不过滤模型会把停机和故障混为一谈。特征层经过清洗后的数据还不能直接进模型需要做滑窗特征、归一化和样本标注。这部分我在第 4 节详细展开。有个容易忽略的细节MyEMS 的原始采集频率是 1 分钟但对于振动信号来说1 分钟均值已经丢失了大量高频信息。所以我建议在数据链路里额外加一个边缘网关对振动做 10kHz 左右的原始采样只把统计特征RMS、峰值、峭度传回 MyEMS。这样既不爆存储又保住了诊断有效信息。2.3 为什么用 MyEMS 而不是直接连数据库有人问既然我有 Modbus 数据那用 Python 直接读寄存器不就行了确实可以但长期看会有几个问题寄存器地址映射经常变、断点续采没人管、历史数据没有统一备份。MyEMS 相当于把这些脏活累活做掉了还提供了一套点位管理和权限控制。模型组的人只需要从 API 拉数不需要关心现场通讯细节。我用一个简单示意说明MyEMS 侧配置点位后的数据查询接口返回格式大致是{ start_time: 2025-11-01 00:00:00, end_time: 2025-11-01 00:10:00, points: [ {id: 1, name: compressor_current_a, values: [32.1, 32.3, 31.9, ...]}, {id: 2, name: compressor_temp_oil, values: [58.2, 58.5, 58.1, ...]} ] }拿到这个接口之后我写了一个 Python 数据拉取脚本把 180 天历史数据分批导出为 Parquet 文件再在本地做特征工程和模型训练。MyEMS 的价值是让我只用两天就把这条链路跑通把精力集中在模型上而不是花两周去和现场 PLC 厂商要数据格式文档。3. CNN-LSTM 模型为什么会比拍脑袋阈值更可靠3.1 设备故障信号的两个特点局部突变 长周期退化先想一个问题设备故障预警为什么不能用简单的阈值判断比如电流超过 40A 就报警温度超过 65℃ 就报警。因为故障从来不是单一参数越线而是多个参数在时间维度上的协同变化。拿轴承磨损举例早期轴承磨损会产生微小的冲击脉冲体现在振动信号上是高频成分增加电流和温度可能几乎不变。继续发展摩擦增大导致温度缓慢上升电流小幅波动。等温度明显超过阈值的时候轴承已经离损坏很近了。所以真正有效的预警要能同时捕捉两类信息短期的局部特征振动信号里的瞬态冲击、电流波形里的细微毛刺长期的趋势变化温度、压力的缓慢漂移性能指标的持续退化这就是为什么单靠一个阈值或者简单线性回归不好使。我们需要一个模型能同时看最近几分钟发生了什么也要能记住过去几周这台设备的状态在怎么变化。3.2 CNN 层负责提取局部特征CNN卷积神经网络在图像识别里用得最多但它同样适合处理一维时序信号。核心思想是用一个滑动窗口的卷积核去扫描输入序列提取局部模式。比如卷积核大小为 8 的滤波器扫描连续 8 个时间点的数据可以捕捉到短时间内的跳变、尖峰和形态特征。通俗一点讲CNN 像是一个拿着放大镜的检查员专门盯着信号的一小段看去找那些能代表异常前兆的局部形态。多个卷积核可以并行学习不同的特征有的关注振动冲击有的关注电流波动有的关注温度变化速度。在一维时序上输入是一个形状为(window_size, num_features)的矩阵CNN 的卷积核沿着时间维度滑动输出一组特征图再做池化降维保留主要特征。3.3 LSTM 层负责建模长期依赖LSTM长短期记忆网络是循环神经网络的一种专门解决记住很久之前的信息这个问题。设备退化往往是缓慢的可能需要回顾过去几百个时间步的状态变化。普通神经网络做不到这种长距离记忆而 LSTM 通过门控机制有选择地记住或遗忘历史信息。用生活类比LSTM 像一个记账的人有一本长期账本和一本短期账本。每次进来一条新数据他会对比当前情况决定哪些旧信息要忘掉、哪些新信息要记进长期账本、哪些只作为临时记忆。这样就能保持一个对设备健康状态的整体记忆而不是只看眼前几分钟。3.4 组合逻辑先把片段看懂再把故事串起来CNN-LSTM 模型的组合逻辑很自然输入层接收滑动窗口内的多通道传感器数据形状为(window_size, num_features)CNN 层先用多个一维卷积核提取局部特征比如卷积核尺寸 8、16、32 分别捕捉不同时长的局部模式池化层压缩特征图尺寸减少计算量展平后的特征序列输入 LSTM 层建模时间依赖记住长期趋势全连接层把 LSTM 输出映射到目标类别比如正常/异常预警这里有个选型细节为什么不用纯 LSTM因为纯 LSTM 直接处理原始传感器信号时对局部冲击特征不敏感需要很深的堆叠才能学到短期形态训练难度大。而 CNN 天然适合提取局部特征相当于先帮 LSTM 做了一层特征工程。为什么不用纯 CNN因为 CNN 的感受野是有限的即使膨胀卷积也很难优雅地建模几百步的长期依赖。两者结合正好互相补齐短板。模型结构简化为 PyTorch 代码大致如下import torch.nn as nn class CnnLstmModel(nn.Module): def __init__(self, num_features, window_size, num_classes2): super().__init__() self.conv1 nn.Conv1d(in_channelsnum_features, out_channels64, kernel_size8, padding4) self.conv2 nn.Conv1d(in_channels64, out_channels128, kernel_size5, padding2) self.pool nn.MaxPool1d(kernel_size2) self.lstm nn.LSTM(input_size128, hidden_size64, num_layers2, batch_firstTrue) self.fc nn.Linear(64, num_classes) def forward(self, x): # x: (batch_size, window_size, num_features) x x.permute(0, 2, 1) # (batch_size, num_features, window_size) x torch.relu(self.conv1(x)) x self.pool(torch.relu(self.conv2(x))) x x.permute(0, 2, 1) # 换回序列维度 out, _ self.lstm(x) out self.fc(out[:, -1, :]) # 取最后一个时间步的输出 return out我最初跑出来的效果并不好后来发现问题是输入特征的归一化方式和窗口长度没调对。这些细节在第 4、5 节展开讲。4. 特征工程与样本构建决定上限的往往是数据而不是模型4.1 传感器通道选择与缺失值处理模型能学到什么取决于你喂给它什么。最开始我贪多把 MyEMS 里能拉的 42 个点位全部做成特征想着让模型自己找规律。结果训练特别慢验证集上精度还不到 75%明显是噪声特征干扰了学习。后来重新梳理了一遍把特征分成四个类别电参数三相电流、电压、功率、功率因数热参数润滑油温、绕组温度、冷却水温度振动参数振动速度 RMS、加速度峰值过程参数出口压力、流量、运行状态结合设备机理和现场故障记录拍板删除了一部分跟故障关联度很弱的特征比如环境温湿度、部分辅助设备开关状态。最终保留 24 个有效特征通道。这一步没有统一公式核心思路是让每个特征要么对故障敏感要么能帮助区分不同工况否则就删。缺失值处理上我试用过三种策略前向填充用上一个有效值填充简单但对长时间断线的场景会引入错误线性插值适合短时间缺值比如 5 分钟以内的断点直接删除缺失段适合训练集样本充足的情况最保守最后采用的是组合策略小于 5 分钟的空值用线性插值超过 5 分钟的缺失段直接丢弃不加任何人工生成值。因为模型要学的是设备真实行为而不是假数据。4.2 滑动窗口构造样本标签怎么打监督学习需要输入和标签成对出现。对预测性维护来说输入是过去一段时间窗口内的传感器数据标签是未来一段时间内是否会发生故障。这里有三个关键参数窗口长度 W输入数据向前看多久。我试过 60、120、240 分钟最终用 120 分钟覆盖率较高且参数量可控预测提前量 H提前多久发出预警。这个参数直接决定了预警是否有可操作性设定为 24 小时滑动步长 S相邻样本的时间间隔。步长越小样本越多但样本间相关性也越强具体标注逻辑是如果设备的故障发生时刻为 T那么在 [T - H - W, T - H] 窗口内的数据标注为正样本即将故障因为系统需要在故障前 H 小时发出预警。T 时刻之后的数据也标为正样本但实际部署中我们希望在故障前预警不需要预测故障后的状态所以只保留故障前 24 小时到故障时刻这部分样本。4.3 训练集/验证集划分的典型错误不能随机打乱这是所有时序模型最容易踩的坑。普通机器学习任务随机划分训练集和验证集没问题但设备数据是时间序列同一台设备连续时间段的数据高度相关。如果随机打乱模型相当于见过验证集时间段的上下文验证准确率会虚高上线后立刻打回原形。我采用的正确做法是按时间顺序划分前 80% 时间范围做训练集后 20% 时间范围做验证集并且确保同一故障事件的所有样本都落在同一侧不能把一个故障的前半段划进训练集、后半段划进验证集。这样才能真实反映模型在未见过时间上的泛化能力。4.4 数据不平衡问题故障样本太少怎么解决故障预警场景天然面临类别不平衡设备大部分时间正常故障样本可能只占 2%~5%。不平衡比例超过 20 倍时模型倾向于把所有样本都预测为正常因为这样准确率也能到 95% 以上。处理办法有两个方向数据层面欠采样正常样本、过采样故障样本、或者用滑窗重叠生成更多故障样本损失函数层面给正样本更高的权重比如class_weight或者用 Focal Loss 让模型关注难分类样本我在实践中用的是故障样本过采样 正常样本子采样的组合。把故障类样本滑动步长减小到 5 分钟正常类样本步长保持 30 分钟这样故障样本数量上来了不同故障阶段的数据也更多样训练更稳定。5. 训练与调参复盘从 67% 到 92% 的关键操作5.1 第一版模型为什么只有 67%第一次跑通模型时验证集准确率只有 67%比瞎猜没好多少。我复盘了三个主要原因特征归一化方式错误原始信号里温度在 50~70℃电流在 20~40A功率在几千瓦量纲差异巨大直接喂给神经网络数值大的特征完全压过了数值小的特征。改成 Z-score 标准化并按特征维度分别计算均值和方差后收敛速度明显加快窗口长度太短一开始为了图快窗口长度设成 30 分钟。结果模型根本来不及捕捉缓慢退化趋势预测能力相当弱。改成 120 分钟后效果提升明显类别不平衡这里不再赘述就是上一节提到的问题5.2 超参数调整记录我记录了一组比较典型的超参数调整过程按验证集 F1 分数排序模型变体窗口长度卷积核LSTM 层数/隐层优化器/学习率验证集 F1V13031/32Adam 1e-30.68V212031/64Adam 1e-30.79V312052/64Adam 5e-40.85V412082/64Adam 3e-40.89V524082/64Adam 3e-40.88最终选的不是 F1 最高的 V5而是 V4。原因是窗口 240 分钟意味着预警延迟更大实际运维中厂家希望在故障前 24 小时收到预警就已经够用了再长的窗口对实时推理资源消耗也更高。V4 在 120 分钟窗口下 F1 达到 0.89部署成本更低现场反馈的误报率也可接受。训练超参数最终确定为CNN 两层卷积核尺寸 8 和 5输出通道 64 和 128池化核 2LSTM 两层隐层 64批量大小 64Adam 优化器初始学习率 3e-4每 10 轮衰减为原来的 0.7早停 patience 设为 8。训练约 30 轮收敛单次训练时长在单张消费级显卡上约为 20 分钟。5.3 92% 准确率背后的真实含义这里必须较真一件事92% 是准确率但设备预警场景里更该关注的指标是召回率和误报率。准确率Accuracy所有预测里正确的比例精确率Precision预测为故障的样本里真正故障的比例召回率Recall真实故障样本里被正确预测的比例F1 分数精确率和召回率的调和平均我在验证集上的最终混淆矩阵大致是总样本 12000 条其中真实故障 480 条。模型预测结果真正类 442 条假正类 48 条假负类 38 条真负类 11472 条。算下来准确率 (44211472)/12000 ≈ 95.6%精确率 442/(44248) ≈ 90.2%召回率 442/(44238) ≈ 92.1%。之所以对外说预警准确率 92%其实说的是召回率意思是真实故障里有 92% 被提前 24 小时识别出来了。为什么更看重召回率因为对设备维护来说漏报的代价远大于误报。漏一次可能直接停机误报最多让维修工多跑一趟。所以模型调参时我刻意把分类阈值从 0.5 降到 0.4换来召回率从 85% 提升到 92%代价是误报率略有上升这个权衡在实际运维中是可以接受的。6. 部署上线与误报治理模型跑起来之后才是麻烦的开始6.1 模型服务化定时推理还是实时推理模型训练完只是第一步上线部署的架构决定它能不能真正服务产线。我选择的方式是定时推理 事件触发即时推理。定时推理每隔 15 分钟对全部目标设备做一次预测把结果写入 MySQL由前端大屏展示。这样平均延迟不超过 15 分钟对预测性维护来说完全够用。事件触发推理当 MyEMS 检测到某些参数瞬时突变时立即触发一次额外预测以便捕捉快速发展的异常。服务端实现用 FastAPI 包了一个推理接口流程是查询 MyEMS API 拿最近 120 分钟数据做同样的特征加工调用模型返回故障概率和预警等级。6.2 92% 召回率下还有哪些坑误报分析与阈值调整上线第一个月模型表现没有验证集那么理想主要问题集中在三块第一工况切换引起的误报。产线有时候会换产品型号负载明显下降模型误以为设备异常连续出预警。解决办法是加入工况识别逻辑把当前负载率和历史平均负载率作为额外上下文负载波动超过一定范围时自动挂起预警。第二传感器漂移和复位引起的假信号。某个温度探头在校准之后的数值整体偏移 2℃模型以为是温度爬升趋势。这类问题靠模型内部不容易解决建议在特征层面添加同点位历史分位数来辅助判断。第三相邻设备耦合干扰。一台设备启动时的冲击电流会影响另一台设备的电流读数造成误报。处理方式是扩展窗口特征把关联设备的启停状态也作为特征输入相当于给模型引入车间上下文。我把最终上线的分级预警策略总结如下故障概率预警等级响应动作0.4~0.6黄色关注列入当日巡检重点0.6~0.8橙色预警安排 48 小时内专业点检0.8红色预警准备备件计划 24 小时内检修这个分级的核心思想是别把所有预警都当紧急事件用不同响应成本去中和误报损失。6.3 模型漂移与定期重训设备在老化工况在变化模型不可能一劳永逸。上线之后我保持了一个每周评估任务拉取最近 7 天新增数据跑一遍 validation pipeline对比模型预测结果和实际维修记录。如果连续两周 F1 分数下降超过 5%就触发重训流程。重训不是从零开始而是在原模型权重基础上用最近数据继续训练几个 epoch保留已学到的退化模式同时适应新工况。这种做法既控制训练成本又能缓解模型漂移。7. 最后分享几条实操经验整个项目做下来我的体会有几条比较重要。第一先把数据链路跑通再碰模型。很多人拿到项目就开始搭神经网络结果数据质量问题没解决模型反复调也上不去。我这次是先花了两周把 MyEMS 到特征矩阵的链路彻底跑通中间解决了断点、重复、离群值等一系列数据治理问题后面调模型反而快了。第二验证集要按时间段切分不要随机切。这是时序模型最容易犯错的地方出错之后你看到的准确率全是幻觉上线见光死。第三不要只盯着准确率。预测性维护的核心是辅助决策要把准确率、召回率、误报率和运维成本放在一起看。我把阈值从 0.5 降到 0.4召回率上去了误报变多是可以用分级机制消化的最终运维团队更满意。第四模型结构不用太复杂。我试过把 CNN-LSTM 扩到更深、更宽增加注意力机制效果提升不到 2%但训练和推理耗时明显增加。工业现场最重要的是稳定可解释而不是炫技。如果你也在考虑将预测性维护落地我建议从小范围试点开始选一台高频次运行、故障成本清晰的设备先跑通 MyEMS 数据采集和 CNN-LSTM 模型的完整链路用真实数据说话。等准确率做到八九成再逐步推广到更多设备这条路走得会更稳。