ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于LSTM的工厂电力负荷预测实战:从MyEMS数据到削峰填谷应用

基于LSTM的工厂电力负荷预测实战:从MyEMS数据到削峰填谷应用 先说明一下项目背景我们工厂的能源管理系统EMS选型时评估过好几套商业方案最后决定基于开源的 MyEMS 做二次开发。这次要解决的痛点很具体——生产车间的电力负荷波动大峰谷电价差异明显如果能提前一天准确预判负荷曲线就能在保证生产的前提下把尖峰时段的可中断负荷调度开一年下来电费能省不少。而负荷预测这个环节我们最终用的是 LSTM 神经网络实测下来预测准确率能做到 95% 左右MAPE 约 5%这篇文章就把整个落地的过程、踩过的坑、以及怎么和 MyEMS 对接的关键细节都写出来给同样在做能源管理、想引入 AI 预测能力的朋友一个可以直接抄作业的参考。1. 项目整体设计与思路拆解1.1 为什么选 MyEMS 而不是直接买商业方案先说选型。商业能源管理平台确实省事报价也的确是“省电费的几分之一”这种算法但问题是负荷预测这个核心功能往往是黑盒模型怎么训练的、特征怎么选的、换到我们这种三班倒的离散制造车间还准不准全都说不清。MyEMS 的好处是开源、数据库结构清晰MySQL / MongoDB 都可选、自带 REST API 和数据采集器而且它本身就预留了“自定义数据报表”和“数据的二次加工”空间我们完全可以在不破坏原有框架的前提下把 AI 预测能力作为独立的微服务挂进去。我的建议是如果你的目标是真正掌握预测能力、后续还要做需求响应或碳排管理用 MyEMS 做底座 自建预测模型ROI 会高很多。梳理一下我当时的整体架构数据源车间电表 能源采集器Modbus RTU/TCP→ 存入 MyEMS 的 MySQL 数据库特征层从 MyEMS 历史表中抽取功率、电量、温度、生产排班等模型层独立的 Python 服务TensorFlow / Keras 实现 LSTM定时训练、输出预测结果应用层预测结果写回 MyEMS 数据库在 Grafana 和 MyEMS 看板上展示并触发削峰填谷策略。这里有个关键选择预测结果为什么写回 MyEMS 而不是单独存在模型服务的库里因为 MyEMS 的前端和报表模块只认数据库里的实测数据表我们通过它的“虚拟表”能力把预测负荷作为一列“扩展指标”暴露出来这样就能用 MyEMS 自带的图表组件直接展示不需要额外开发前端。1.2 为什么是 LSTM而不是 XGBoost 或普通 RNN很多朋友会问做时序预测XGBoost 也能做而且调参快、可解释性强为什么偏偏选 LSTM我的回答是负荷预测本质上是一个“长距离依赖 多周期性叠加”的问题。工厂的电力负荷不仅受当前时段影响还受“昨天同一时刻”“上周同一工作日”“节假日前后”这些长期模式影响。XGBoost 这类树模型通常需要人为构造大量滞后特征和滑窗统计特征等于是“人肉特征工程”而 LSTM 的循环结构天然具备门控记忆机制能在训练过程中自动学习“该记多久之前的信息、该忘掉哪些冗余信息”。具体到网络结构我用的是单层 LSTM 加全连接输出层128 个隐藏单元输入序列长度取 96 个点一天 96 个 15 分钟采样点输出就是未来 96 个点的预测。也有人用双层 LSTM 或者加入 Attention 机制但对工厂日负荷曲线这种“强周期中波动”的数据单层 LSTM 在参数量和拟合能力之间已经取得了很好的平衡加深网络反而容易在小数据集上过拟合。我的经验是——先用简单模型跑通全链路再根据残差决定要不要加复杂度不要一上来就堆模型。2. 核心细节解析与实操要点2.1 特征工程决定准确率上限的往往是特征而不是模型很多 AI 入门的朋友容易盯着模型结构折腾但做过实际项目的人都知道特征工程的上限决定模型准确率的上限。LSTM 虽然能自动提取时序特征但并不会帮你造出“节假日标记”这种它根本看不见的信息。我最终使用的特征集合是这样的历史负荷序列过去 96 个采样点的功率值核心特征时间编码小时(0-23)、星期几(0-6)、是否工作日、是否节假日这些都用 one-hot 或周期性编码比如小时用 sin/cos 编码避免把“23 点和 0 点”当成两个距离很远的数值外部温度我们工厂的中央空调负荷占比接近 30%所以环境温度对负荷有显著影响这部分用气象 API 拉取的历史数据生产排班标记三班倒模式下早班、中班、夜班的设备启停规律完全不同我会把排班表作为一列数值特征输入。这里一个特别容易踩的坑是预测未来一天时如果特征里包含了“未来的温度”那就只能用它前一天的气象预报值不能用实测值否则就是“特征泄漏”。我一开始没注意这个问题模型在历史回测上表现极好MAPE 在 2% 以下但换到真正预测未来 24 小时就崩到 10%原因就在这——测试集里混入了只能事后才知道的实测温度。后来我统一改成“用今天的实际温度预测明天”并明确把特征分成“已知序列”和“预报序列”两类问题就迎刃而解了。2.2 数据清洗与异常处理脏数据是时序预测的头号杀手你在网上看到的入门教程一般都给的是处理好的公开数据集标点缺失、异常点都很少。但真实工厂的数据简直是“野生环境”电表重启会导致一段时间的数据为 0通信闪断会留下 NULL还有偶尔的开关大设备造成的冲击尖峰。我的清洗策略是按 15 分钟粒度重采样后做以下处理连续性校验时间戳必须连续缺失的采样点先用前后值的线性插值填充如果缺失超过 2 小时则标记为异常日整段剔除该日数据异常值剔除采用 Hampel 滤波器滑动窗口内的值如果偏离中位数超过 3 倍 MAD中位数绝对偏差判定为异常点并剔除。这种方法比“均值 ± 3σ”更稳健因为功率尖峰本身会拉高标准差用均值法容易把真实的峰值误删数据归一化LSTM 对feature缩放敏感我用的是 MinMaxScaler把所有特征统一到 [0,1] 区间。注意缩放器必须只在训练集上 fit然后应用到验证集和测试集防止未来信息泄露。我建议把这部分做成一个独立的“数据质量看板”每次训练前自动统计缺失率、异常点个数并可视化输出。千万不要跳过这一步——我实测过数据清洗前后的模型 MAPE 能差 2~3 个百分点。2.3 MyEMS 的数据存储结构和 SQL 取数示例MyEMS 的默认数据库是 MySQL核心数据表集中在t_statistic_hourly、t_statistic_daily、t_realtime_input这几张表里。为了拿到 15 分钟粒度的原始功率数据我实际主要用的是原始数据表配置对应的点位后自动记录。取数逻辑大概是SELECT start_datetime, value FROM t_realtime_input WHERE point_id 车间总进线_有功功率 AND start_datetime BETWEEN 2024-01-01 00:00:00 AND 2024-06-30 23:45:00 ORDER BY start_datetime ASC;如果你们用的是 MyEMS 的 aggregation 表要注意粒度可能被聚合成了小时级或天级做负荷预测最好直接读取原始明细表。另外MyEMS 支持接入 InfluxDB 这类时序数据库如果你的采集点很多、数据量很大建议把原始时序数据放到 InfluxDB 里MySQL 只保留业务统计结果这样查询性能会好很多。3. 实操过程与核心环节实现3.1 LSTM 模型的完整代码实现我把核心代码贴出来加了一些关键注释。这套代码是用 TensorFlow 2.x 跑的如果你用的是 PyTorch思路完全可以平移。import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau from tensorflow.keras.optimizers import Adam # 时序数据构造函数 def build_sequences(features, target, seq_len96, pred_len96): 把长序列切成 (样本数, 时间步, 特征数) 的输入格式 seq_len96 代表用过去96个点1天预测未来96个点1天 X, y [], [] for i in range(len(features) - seq_len - pred_len 1): X.append(features[i : i seq_len]) y.append(target[i seq_len : i seq_len pred_len]) return np.array(X), np.array(y) # 假设 data 是已经清洗并按时间排序的 DataFrame # 特征列power, hour_sin, hour_cos, weekday, is_workday, temp features data[[power, hour_sin, hour_cos, weekday, is_workday, temp]].values target data[power].values.reshape(-1, 1) # 归一化 scaler_x MinMaxScaler(feature_range(0, 1)) scaler_y MinMaxScaler(feature_range(0, 1)) features_scaled scaler_x.fit_transform(features) target_scaled scaler_y.fit_transform(target) # 构造序列 seq_len 96 pred_len 96 X, y build_sequences(features_scaled, target_scaled, seq_len, pred_len) # 划分训练集/验证集/测试集按时间顺序不打乱 split1 int(len(X) * 0.7) split2 int(len(X) * 0.85) X_train, y_train X[:split1], y[:split1] X_val, y_val X[split1:split2], y[split1:split2] X_test, y_test X[split2:], y[split2:] # 构建LSTM模型 model Sequential([ LSTM(128, activationtanh, return_sequencesFalse, input_shape(seq_len, X.shape[2])), Dropout(0.2), Dense(64, activationrelu), Dense(pred_len) # 输出 96 个预测值 ]) model.compile(optimizerAdam(learning_rate0.001), lossmse, metrics[mae]) # 早停和学习率衰减 callbacks [ EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), ReduceLROnPlateau(monitorval_loss, factor0.5, patience5, min_lr1e-5) ] history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs100, batch_size32, callbackscallbacks, verbose1 ) # 预测并反归一化 y_pred model.predict(X_test) y_pred_inv scaler_y.inverse_transform(y_pred) y_test_inv scaler_y.inverse_transform(y_test)这个代码框架非常通用换数据集基本也能直接用。三个地方需要注意输入序列和输出序列长度96/96是根据“15 分钟一个点、预测未来一天”的需求定的如果你只需要预测未来 1 小时那 pred_len 就改成 4损失函数用的是 MSE衡量指标我习惯最后算 MAPE这样业务方更容易理解“95% 准确率”是什么概念Dropout 加在了 LSTM 层之后防止过拟合但不要加太多0.2~0.3 比较稳。3.2 训练过程与超参数选择的经验记录训练过程我走了好几轮弯路这里把最终稳定下来的超参数和调整逻辑说清楚隐藏层神经元128。我试过 32、64、256结论是 64 欠拟合验证集 MAPE 稳定在 7% 以上256 过拟合风险高且训练时间翻倍128 最均衡学习率0.001。使用 Adam 优化器并配合 ReduceLROnPlateau 在验证 loss 停滞时自动降一半学习率。新手最常见的错误是学习率设成 0.01 甚至 0.1loss 直接发散Batch size32。小批量能让梯度更稳定我用 64 也能收敛但在数据量不大的情况下 32 表现更好Epochs100 EarlyStopping。100 是上限实际通常在 40~60 轮就触发了早停别傻傻跑满 100 轮输入序列长度961天。我对比过 48半天和 168一周48 会丢失“昨天同时刻”的模式168 则容易引入太多噪声96 是经验最优。训练时的另一个关键动作是 dtype 和显存控制。模型本身不大几万个参数CPU 也能训练但用 GPU 能快 5~10 倍。如果服务器没有 GPU 也没关系这个体量的数据 CPU 训练也就十来分钟完全可接受。每轮训练完后我会把验证集上的预测曲线和实际曲线画出来叠加对比重点关注三个时间段早高峰7:00-9:00、晚高峰18:00-20:00、夜班低谷23:00-5:00。模型在高峰时段容易低估在低谷时段容易高估这是负荷预测的正常现象需要通过误差分析进一步优化。3.3 预测结果如何回写 MyEMS 并展示模型训练好之后工程化部署才是真正让业务跑起来的关键。我的方案是写一个独立的 Python 预测服务每天凌晨 2 点自动执行一轮预测因为凌晨的生产计划已经确定、气象预报数据也更新了生成未来 24 小时的负荷曲线然后通过 MyEMS 的 REST API 写入自定义数据表。MyEMS 提供了api/report相关接口但我们没有直接改它的核心表而是新建了一张ai_power_forecast表表结构很简单CREATE TABLE ai_power_forecast ( id INT AUTO_INCREMENT PRIMARY KEY, forecast_time DATETIME NOT NULL, power_kw DECIMAL(10,2) NOT NULL, is_actual TINYINT DEFAULT 0, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );这张表同时存预测值和实际值实际值由定时任务从 MyEMS 的实时表同步过来这样在报表里就能直接对比“预测 vs 实际”方便持续监控模型漂移。前端展示上我推荐两个方案如果你们已经在用 Grafana直接配置 MySQL 数据源写一条 SQL 查询ai_power_forecast表画出双曲线对比图非常快如果想在 MyEMS 自带界面里看可以利用 MyEMS 的“自定义菜单 Dashboard”功能虽然配置复杂一点但业务人员用起来更统一。我们实际用的是方案 1因为 Grafana 的告警功能还能顺手把“预测偏差过大”这个场景管起来——当预测值和实际值偏差超过 15% 时自动发企业微信通知提醒我检查模型状态。4. 准确率是怎么炼成的评估、调优与迭代4.1 评估指标95% 这个数字是怎么算出来的很多第一次接触负荷预测的人听到“95% 准确率”第一反应是“那你预测值和实际值几乎一模一样嘛”。严格来说不是这样的这里的准确率通常指的是 MAPE平均绝对百分比误差小于等于 5%MAPE (1/N) * Σ(|实际值 - 预测值| / 实际值) * 100%当 MAPE 5% 时大家习惯说“准确率 95%”。注意这里有个隐藏问题当实际负荷接近 0 的时候MAPE 的分母趋近于 0误差百分比会被放大到离谱所以我实际评估时加了保护逻辑——只统计功率大于某个阈值比如 50kW的采样点低于阈值的点直接跳过避免夜班停机时段把整体指标拉低。另外我不建议只盯一个 MAPE。至少还要同时看MAE平均绝对误差便于理解业务上平均每 15 分钟差多少 kW直接对应到电费损失峰值时段 MAPE单独统计每天 8:00-20:00 的误差如果整体 MAPE 低但峰值时段高说明模型把“平段”拟合得很好、却搞不定“峰段”这对削峰填谷策略来说是致命的P95 误差取误差分布的 95 分位数代表“最差的那 5% 时刻偏差有多大”用于评估极端情况下的可靠程度。我们最终的测试集结果大概是整体 MAPE 5.2%峰值时段 MAPE 6.8%夜班低谷 MAPE 4.1%。考虑到我们已经把所有可解释的特征都用上了这个成绩在生产场景里算很不错了。4.2 误差分析从残差里找模型升级方向我的习惯是每次训练完不急着调参而是把残差预测值 - 实际值按小时、按星期几分组画箱线图这样错误模式就藏不住了。做的过程中发现几个规律第一周一上午的预测误差明显偏大。原因很简单我们工厂周日部分产线休息周一一早设备集中启动启动电流尖峰非常大而且每周启动的设备和顺序不完全一样随机性强。这个靠模型本身很难提升我最终的折中方案是对周一早高峰的预测值做 3% 的经验修正降低系统性低估。第二夏季和冬季误差比春秋季大。这是空调负荷导致的而我用的温度特征只是“当前温度”没有积累“历史温度对建筑热惯性的影响”。如果想进一步提升可以加一个“过去 24 小时平均温度”的特征相当于给模型一个“墙体蓄热”的信号模型就能学会“前几天热即使今天温度下降空调负荷也不会立刻下来”。第三节假日切换日的误差很大。比如五一放假前一天、国庆后开工第一天负荷规律完全变化。LSTM 学的是“工作日”和“休息日”两套模式但对于“工作日的前一天是休息日”这种组合并没有很明确的感知。后来我在特征里加了一列“昨天是否休息”误差才明显回落。这些误差分析做完后模型的 MAPE 大约从 6.8% 降到了 5.2%充分说明调参不如“理解业务 修正特征”来得有效。4.3 上线后的模型漂移监控机制模型上线不是终点而是运营的起点。工业负荷会随着生产订单变化、设备增减、生产工艺调整而缓慢漂移。我见过不少项目模型上线时准确率亮眼三个月后越跑越偏最后业务部门彻底不看了。我的做法是建两个防线每日自动回测任务每天凌晨用最近 7 天的实际负荷数据和“上一版本模型”做一次回测如果 MAPE 连续 3 天超过 8%就触发重新训练流程每月定时重训不管有没有漂移每月 1 号用截止到上月末的全部数据重新训练一次防止季节性变化带来的慢漂移。这里还有个小细节重新训练时要控制“训练数据窗口”。我的数据库里存了最近两年的历史数据但 LSTM 如果拿两年数据全量训练一方面训练慢另一方面太老的数据可能包含了已经停产的生产线模式反而不利于对新模式的拟合。我最终用了“滚动一年窗口”只取最近 365 天的数据训练事实证明漂移跟踪效果更好。5. 部署架构与 MyEMS 集成的最佳实践5.1 模型服务部署的两种方式预测服务在工程上可以做成两种形态我根据自己的经验简单对比一下第一种是“定时离线预测”。适合每日预测一次的负荷预测场景写一个 Python 脚本通过 cron或 Windows 任务计划每天凌晨执行预测结果写入 MySQL。优点是实现简单、故障影响面小即使模型崩溃也不会影响 MyEMS 主服务缺点是做不到“小时级滚动更新”。第二种是“常驻 API 服务”。用 FastAPI 或 Flask 把模型封装成 HTTP 接口需要预测时随时调用。优点是灵活可以对接需求响应、储能充放电策略等实时场景缺点是需要额外维护一个常驻进程还要考虑并发和监控。我们最终是两种都上了每日负荷预测用离线脚本而“未来 4 小时超短期预测”用于储能系统充放电控制用了 API 服务。两个模型共享一套特征工程代码只是输入序列长度和输出长度不同。5.2 MyEMS 二次开发时的几个关键注意点如果你是用 MyEMS 做二次开发有几个工程上的细节值得提前注意权限体系MyEMS 有完整的用户和角色权限AI 预测服务如果要写数据库不要用 root 账号连单独建一个只读 只写ai_power_forecast表的账号安全又可控时区问题MyEMS 默认时区要统一否则预测结果和时间戳对不上我们踩过“服务器 UTC、数据库 CST”导致预测曲线整体偏移 8 小时的坑数据回写频率预测服务每天只写 96 条记录后面实际值同步也是 96 条数据量极其小MySQL 完全没压力不需要额外考虑性能优化与 MyEMS 时钟对齐如果你的采集系统有延迟比如实时表的数据可能延迟 5 分钟写入那实际值的同步任务最好延迟 15 分钟再执行避免取到不完整的最后几个点。5.3 削峰填谷策略的联动实践模型预测出来不是放着好看的最终要落到控制策略上。我们的初步联动是这样的每天凌晨拿到预测曲线后程序自动计算“峰段比如 9:00-11:00的预测总负荷”。如果预测峰值超过设定的阈值比如变压器容量的 85%就触发两个动作向生产调度发送预警消息建议把部分非关键设备如仓库照明、非连续性辅助设备调整到平段运行如果厂里配了储能系统就把预测峰段的放电功率建议值发给储能控制器提前做好充放电规划。这套逻辑跑通之后我们当月的峰段用电量比上月下降了约 7%虽然不全是负荷预测的功劳还包括生产计划调整但至少证明用 AI 做负荷预测最大的价值不是“算得准”而是“算得早”——提前知道曲线才有了调度的提前量。这才是能源管理从“事后统计”升级到“事前预判”的关键。6. 常见问题与排查技巧实录6.1 模型预测结果普遍偏低的排查思路遇到过最典型的问题是某次重新训练后模型预测值整体偏低 10% 左右但 MAPE 看起来还行因为偏差是系统性的平均下来不明显但峰谷形态还在。排查思路按顺序来先看数据清洗是不是最近一版的异常值处理把一些真实高峰值误判为异常点直接剔除了我用 Hampel 滤波器时窗口设得太大、阈值设得太严格就容易误删真峰再看特征一致性训练时用的温度特征来自历史实测预测时用的是气象预报如果预报温度系统性偏低模型预测的空调负荷也会系统性偏低最后看模型结构确认一下 Dense 输出层是不是加了 bias网络是否能输出负值功率不可能为负有可能会出现“截断效应”导致预测值被压向 0。排查这类问题我强烈建议直接把“预测值 vs 实际值”的散点图画出来看是沿着 yx 线上下均匀分散正常还是整体偏到对角线下方系统性偏低一眼就能分清是“随机误差”还是“系统偏差”。6.2 训练 loss 不下降怎么办loss 不下降或者下降极慢时先检查三件事数据是否经过了正确的归一化如果特征里有一列是“星期几”直接用了 0-6 的整数而另一列是千瓦级别的功率数值两者量级差太多梯度更新会被大数值特征主导训练会非常慢。解决方法是把所有特征都归一化到 [0,1] 区间学习率是否合适先用 0.001 试如果 loss 在震荡完全不降降到 0.0001 再试是否存在 NaN 或无穷值数据清洗阶段如果没处理干净比如除零导致 infloss 很容易变成 NaN。用np.isnan(data).sum()扫一遍是最快的排查方式。6.3 预测曲线“滞后”现象的处理很多用 LSTM 做时序预测的朋友都会遇到一个经典的“滞后”问题预测曲线比实际曲线晚了一两个采样点拐点处尤其明显。产生的原因是模型在训练时学到了一种“偷懒策略”——直接把上一时刻的值复制到下一时刻因为对大多数平稳片段来说这样做的 loss 已经很小了。我的处理办法有两个把损失函数从 MSE 换成“MSE 一阶差分惩罚”也就是在原始 loss 上加上对预测曲线一阶差分相邻点差值的绝对值的惩罚项强制模型学会“变化”而不是“复制”缩短预测步长如果把未来 96 个点直接一次性输出模型很容易回归到均值的“安全区”但如果我们改成“滚动预测”——每次只预测未来 4 个点然后用预测值续接输入再预测后面 4 个点——滞后现象会明显改善。代价是推理时间变长但对我们 15 分钟粒度的场景来说完全可接受。6.4 常见问题速查表问题现象可能原因排查与解决方案整体 MAPE 高10%特征泄漏 / 数据未清洗 / 时序划分错误检查测试集是否包含未来信息手动可视化 10 条预测曲线找规律预测曲线明显滞后模型学到恒等复制策略改用滚动预测loss 中加差分惩罚项高峰低估、低谷高估特征缺少生产排班信息加入班次标记、昨日同时段负荷特征训练 loss 为 NaN数据含 inf / NaN / 学习率过大检查数据质量降低学习率节假日预测误差特别大训练集中节假日样本太少特征中加入“节前/节后/节日中”标记必要时单独训练节日模型模型上限后性能逐渐下降概念漂移建立每日回测告警每月定期重训6.5 几件做了会大幅提升项目体验的小事最后分享几个非技术但很实用的经验一是要把模型输出做成“可解释”的界面。业务领导不会关心 LSTM 是什么他们只需要看到“明天下午两点会出现负荷高峰预计 1200kW比今天高 15%”这样的结论。我专门做了一个自动生成的预测报告每天发到工作群把专业语言翻译成业务语言项目关注度和资源支持明显不一样。二是做任何模型改动前先备份当前版本的模型文件和训练数据快照。一个不大的模型文件也就几十 MB但如果你改完代码后发现效果还不如上个版本没有备份就后悔莫及了。三是尽量让预测结果暴露给一线操作人员。我们做了个简单的微信推送每天早晨把“今日峰段提醒 可中断负荷建议”推给车间主任他们真的会去看——因为预测的准所以信因为信所以在关键时刻愿意配合调度。这个信任的建立比任何技术指标都重要。根据我个人做完这个项目的体会负荷预测这件事模型选型和调参顶多占三分力气剩下七分都在数据治理、特征理解和工程集成上。LSTM 本身就是个很成熟的算法难的不是跑通代码而是把工厂的实际业务规律翻译成模型能看懂的信号。如果你正准备做类似的能源管理 AI 改造我建议先别急着追求算法复杂度从 MyEMS 里的历史数据入手把数据洗干净、把业务特征理清楚再用 LSTM 建一个简单可用的基线然后一步步迭代——这条路看着慢实际却是最快能见到业务价值的路。
返回列表