光伏-储能-负荷联合预测:给 EMS 装上"预知能力"
本文结合我正在维护的一套工业储能管理系统的真实工程实践,聊聊如何把预测能力嵌入 EMS 决策链路。代码片段均来自项目实际文件,不是伪代码。
一、为什么 EMS 需要预测?
我在调试这套系统的 MQTT 指令链路时,发现一个有意思的现象:EMS 的充放电决策完全依赖当前时刻的 SOC 和预设的时段计划表。翻开 ems_management.c,核心逻辑大概是这样的:
c
BOOL IsBatt_Plan2_StartChrg(...) {
if (pEMS->nTMSector < 0) return FALSE;
if (pPlanSectorNow->emBatt_WorkMode != BATT_WORKMODE_FIRST_CHRGING) return FALSE;
if (nBattSOC >= pPlanSectorNow->nStopSOC) return FALSE;
return TRUE;
}
逻辑很清晰:查当前时段(nTMSector),对比工作模式(emBatt_WorkMode)和停止 SOC 阈值(nStopSOC),决定充还是不充。
这套逻辑在电价稳定、负荷规律的场景下跑得很好。但现实是:光伏出力受云层影响可以在几分钟内从满功率跌到零;工厂负荷在节假日和工作日差异巨大;电网侧的峰谷价差窗口也在动态变化。
纯规则驱动的 EMS 本质上是在用昨天的地图走今天的路。
预测的价值在于:把"当前状态 → 决策"的单步反应,升级为"未来状态预估 → 提前布局"的前瞻控制。这就是 MPC(模型预测控制)的核心思想,后面会详细展开。
二、数据基础:我们手里有什么原料?
预测模型不是凭空建的,得先搞清楚系统里有哪些可用的数据。
2.1 光伏逆变器采样数据
项目里的 PV 采样模块(_inverter_sampler/)支持 Growatt、华为、固德威、阳光电源等主流逆变器品牌,通过 RS485 轮询采集。核心数据结构 PV_INVERTER_DATA(定义在 blob_model.h:4539)包含:
c
typedef struct {
float fOutputActivePower; // AC 侧输出有功功率 (W)
float fPVInputTotalPower; // DC 侧总输入功率 (W)
float fPVGroupVolt[MAX_PV_NUM]; // 各组串电压 (V)
float fPVGroupCurr[MAX_PV_NUM * 2]; // 各组串电流 (A)
float fDailyPowerGeneration; // 当日发电量 (kWh)
float fTotalPowerGeneration; // 累计发电量 (kWh)
int iActivePowerRate; // 有功功率限制百分比
int iWorkMode; // 0=未安装 1=等待 2=运行 3=故障
} PV_INVERTER_DATA;
汇总层面还有 A_BLOB_PV_INVERTER_DATA_TOTAL,字段 fTotalPVOutputActPower 给出全站光伏总出力(kW),这是预测模型的直接训练标签。
对预测有用的特征 :fPVGroupVolt(组串电压反映辐照度变化)、iWorkMode(故障状态需要剔除异常样本)、fDailyPowerGeneration(日累计量用于校准预测偏差)。
2.2 DLT645 负荷计量数据
负荷侧通过 DLT645 协议采集电表数据(dlt645_kWmeter.c)。协议帧结构是标准的 0x68 + 地址域 + 0x68 + 控制码 + 数据域 + CS + 0x16,采集的量包括:
- 三相电压、电流、有功功率、无功功率
- 正向/反向累计电量(
WH_FRAME_TYPE_GET_TOTALWH/WH_FRAME_TYPE_GET_RVSTATALWH) - 最大需量(
WH_FRAME_TYPE_GET_PMAXDEMAND)------这个字段对需量控制预测特别有价值
数值解码用 BCD 转换:
c
float ConvertBCD_2WhVal(BYTE *pData, int nLen, int nDecimalPos) {
// BCD 编码 → 浮点,nDecimalPos 控制小数点位置
}
实际运行中,fGridMeter_Power[1](实时电网功率,kW)和 fCabMeter_Power[1](柜内功率)是负荷预测的核心输入,采样周期通常是 1 分钟。
2.3 储能电池状态
电池 SOC 存在 nSOC(千分位,0-1000 对应 0-100%),充放电能量分别记录在 fCalc_ChargedEnergy 和 fCalc_DisChargedEnergy。这些数据在预测框架里扮演"状态变量"角色------预测模型的输出要结合当前 SOC 才能转化为可执行的充放电指令。
三、光伏发电功率预测
3.1 NWP + 历史数据融合
光伏功率预测的难点在于它强依赖气象条件,而气象本质上是混沌系统。工程上通行的做法是两路信号融合:
NWP(数值天气预报)路 :从气象局或商业 API 获取未来 24-72 小时的辐照度(GHI/DNI/DHI)、云量、温度预报。辐照度是光伏出力的物理驱动量,理论上 Ppv=η⋅A⋅G( η 为效率, A 为面积, G 为辐照度),但实际逆变器效率随温度、老化程度变化,所以 NWP 只能给出粗粒度的趋势。
历史数据路 :用过去 30-90 天的 fTotalPVOutputActPower 时序数据,学习"同类天气下的实际出力曲线"。关键是天气类型分类------晴天、多云、阴天、雨天的出力曲线形态差异极大,需要先做天气聚类再分类建模。
融合方式:NWP 预报作为外生变量(exogenous variable)输入模型,历史序列提供自回归特征。
3.2 模型选型:为什么不用 LSTM?
很多人第一反应是 LSTM,但在工业场景里我更倾向于 N-BEATS 或 PatchTST,原因如下:
N-BEATS(Neural Basis Expansion Analysis for Time Series)的核心思想是把时序分解为趋势项和季节项的叠加,每个 block 学习残差。对光伏这种有强日周期性的序列,这种归纳偏置(inductive bias)非常合适。而且 N-BEATS 是纯 MLP 结构,没有循环依赖,训练速度快,部署到嵌入式 Linux 上的推理开销也小。
PatchTST 把时序切成 patch(类似 ViT 对图像的处理),用 Transformer 的自注意力捕捉长程依赖。对于需要融合 NWP 多变量输入的场景,Transformer 的多头注意力天然适合处理异构特征。
Informer 用稀疏注意力(ProbSparse Attention)把复杂度从 O(L2) 降到 O(LlogL),适合预测窗口很长(比如 72 小时)的场景。
对于本项目这种 15 分钟粒度、预测 24 小时的需求,我会优先试 PatchTST,patch size 设为 16(对应 4 小时历史窗口),预测长度 96 个点。
3.3 不确定性量化:分位数回归
点预测给出的是期望值,但 EMS 决策需要知道"最坏情况下光伏出力是多少"。这就是概率预测的价值。
分位数回归(Quantile Regression)的损失函数:
Lq(y^,y)=q⋅max(y−y^,0)+(1−q)⋅max(y^−y,0)
同时预测 q=0.1,0.5,0.9 三个分位数,就得到了预测区间。EMS 在做保守决策时用 P10(下界),做激进决策时参考 P90(上界)。
实现上,只需要把模型最后一层的输出维度从 1 改为 3,损失函数换成三个分位数损失的加权和,其余架构不变。
四、负荷预测:挖掘工业负荷的周期性规律
4.1 工业负荷的三层周期结构
工业负荷不像居民负荷那样平滑,它有很强的生产节律:
- 日内周期 :班次切换(早班/中班/夜班)导致功率阶跃,
fGridMeter_Power的时序上会看到明显的台阶状跳变 - 周周期:工作日 vs 周末,某些工厂周末停产,负荷降到基础维持水平(空调、照明、待机)
- 节假日模式:春节、国庆等长假期间负荷曲线完全异于平日,需要单独建模
从 DLT645 采集的历史数据里,可以用 STL 分解(Seasonal-Trend decomposition using LOESS)把这三层周期显式分离出来,再分别建模。
4.2 特征工程
除了历史负荷序列本身,以下特征对工业负荷预测有显著提升:
| 特征 | 来源 | 说明 |
|---|---|---|
| 星期几(one-hot) | 系统时钟 | 捕捉周周期 |
| 是否节假日 | 日历 API | 节假日模式切换 |
| 时段索引(0-47) | nTMSector |
对应 EMS 的 30 分钟时段划分 |
| 最大需量 | WH_FRAME_TYPE_GET_PMAXDEMAND |
反映生产强度 |
| 滞后特征(lag-24h, lag-168h) | 历史数据 | 昨天同时刻、上周同时刻 |
| 滚动均值(rolling mean 1h/4h) | 历史数据 | 平滑短期波动 |
nTMSector 这个字段在 EMS 代码里本来是用来查时段计划表的,但它同时也是一个天然的时间特征------把它直接 embedding 进模型,比用原始时间戳效果好。
4.3 多变量时序预测
负荷预测不是孤立的,光伏出力、电池 SOC、历史电价都会影响实际负荷(比如高电价时段工厂会主动降负荷)。把这些变量一起输入 Informer 或 PatchTST,做多变量 → 单变量(或多变量 → 多变量)预测,通常比纯单变量模型提升 10-20% 的 MAE。
五、预测结果注入 EMS:MPC 框架
这是整个方案最关键的一环,也是最容易被忽视的一环。很多项目做完预测就停了,预测结果没有真正改变决策逻辑。
5.1 现有 EMS 决策的局限
回到 ems_management.c 里的逻辑:IsBatt_Plan2_StartChrg() 和 IsBatt_Plan2_StopDischarge() 都是单步决策,只看当前时刻的 SOC 和时段配置。这意味着:
- 如果预测到下午 2 点光伏会大发,现在(上午 10 点)就应该少充一点,留出空间吸纳光伏;但现有逻辑不知道这件事,会按计划表继续充到
nStopSOC。 - 如果预测到晚上 8 点有一个负荷高峰,现在就应该开始储能,但现有逻辑要等到那个时段才触发。
5.2 MPC 的基本框架
MPC(Model Predictive Control,模型预测控制)的思路是:在每个控制周期,用预测模型生成未来 N 步的状态预估,然后求解一个优化问题,得到未来 N 步的最优控制序列,只执行第一步,下一个周期滚动重复。
优化目标(以经济性为例):
minut,...,ut+N∑k=0N−1ckbuy⋅Pgrid,k+−cksell⋅Pgrid,k−+λ⋅Δuk2
其中 ckbuy/sell 是预测的电价, Pgrid+ 是购电功率, Pgrid− 是售电功率, λ⋅Δuk2 是控制平滑项(避免频繁切换充放电)。
约束条件:
SOCmin≤SOCk≤SOCmax Pbatt,min≤Pbatt,k≤Pbatt,max SOCk+1=SOCk+Ecapηchg⋅Pbatt,k+−Pbatt,k−/ηdis⋅Δt
功率平衡约束:
Ppv,k+Pbatt,k+Pgrid,k=Pload,k
这里 Ppv,k 和 Pload,k 就是预测模型的输出。
5.3 与现有代码的接口设计
要把 MPC 嵌入现有系统,最小侵入的方式是:不改变底层执行逻辑,只替换决策层的参数生成。
现有系统通过 MQTT 下发 cmd/set/emu/powerCmd 来控制充放电模式,通过 cmd/set/emu/dispatchPower 下发调度计划。MPC 优化器可以作为一个独立进程运行,每 15 分钟执行一次:
- 从数据库读取最新的
fTotalPVOutputActPower、fGridMeter_Power、nSOC - 调用预测模型,生成未来 24 小时的 P^pv 和 P^load
- 求解 MPC 优化问题(用
scipy.optimize或cvxpy) - 把优化结果转换为
dispatchPower的 JSON 格式,通过 MQTT 下发
注意 EMU_dispatchPower 函数(GH_DigitalEnergy_Pub.c:2582)里有时间戳校验:
c
// GH_DigitalEnergy_Pub.c:2631
if(ABS(Now_time - Ts_val->valueint) > 15) {
pMqtt->bDisPower = TRUE;
return -1;
}
MPC 优化器下发指令时,时间戳字段必须用设备当前时间(误差 ≤15 秒),这是一个容易踩的坑。
5.4 不确定性在 MPC 中的处理
前面提到分位数预测给出了 P10/P50/P90 三条曲线。在 MPC 里,可以用鲁棒 MPC 的思路:
- 光伏预测用 P10(保守估计),避免因高估光伏出力导致过度放电
- 负荷预测用 P90(保守估计),确保高负荷场景下有足够储能余量
- 在约束里加入 SOC 缓冲带(比如 SOC 下限从 10% 提高到 15%),为预测误差留出安全裕度
这种处理方式不需要复杂的随机优化,计算开销小,适合嵌入式 Linux 环境。
六、工程落地的几个实际问题
6.1 数据质量
DLT645 采集偶尔会有通信超时,ConvertBCD_2WhVal 返回异常值。训练数据里需要做异常检测(3σ 准则或 IQR 过滤),否则一个坏点会严重影响模型。
光伏逆变器的 iWorkMode == 3(故障)期间的数据要标记并剔除,不能用来训练"正常发电"的模式。
6.2 模型更新频率
光伏组件会随时间老化(年衰减约 0.5-0.8%),负荷模式会随生产计划变化。模型不能训练一次就永久使用,建议每月用最新 90 天数据重新微调(fine-tune),而不是从头训练。
6.3 预测失效时的降级策略
预测模型是软件,会出 bug,NWP 数据源也可能断线。系统必须有降级逻辑:当预测模块不可用时,自动回退到现有的规则驱动模式(IsBatt_Plan2_StartChrg 那套逻辑)。这个降级切换可以通过 emControlMode 字段来实现------预测驱动模式和规则模式对应不同的控制模式枚举值。
6.4 计算资源约束
项目运行在 ARM 嵌入式 Linux 上(从 src/mkenv_arm_st 可以看出是 ARM 交叉编译环境)。深度学习模型推理需要考虑资源限制:
- N-BEATS 的 MLP 结构可以用 ONNX Runtime 部署,推理一次 24 小时预测在 ARM Cortex-A 上通常在 100ms 以内
- MPC 优化问题如果是线性规划(LP),用
glpk或HiGHS求解,几十个变量的问题毫秒级可解 - 如果资源实在紧张,可以把预测和优化放到云端,设备只负责执行,通过 MQTT 接收优化后的调度计划
七、小结
从"当前 SOC + 时段表 → 充放电决策"到"未来 24 小时预测 + MPC 优化 → 前瞻调度",这不是一个简单的模型替换,而是整个决策架构的升级。
核心链路是:原始采样数据(PV 逆变器 + DLT645 电表)→ 特征工程 → 时序预测模型(PatchTST/N-BEATS)→ 概率预测区间 → MPC 优化求解 → MQTT 指令下发 → 设备执行。
每个环节都有工程细节需要处理,但最重要的是把预测结果真正接入决策闭环,而不是让它停留在离线分析报告里。现有代码里的 dispatchPower 接口已经提供了足够的控制自由度,缺的只是上游的预测驱动层。
这套方案在理论上可以把储能系统的经济收益提升 15-30%(相比纯规则调度),具体数字取决于当地电价结构和光伏/负荷的波动程度。值得投入工程资源去做。
代码引用均来自项目实际文件,文中行号以当前代码版本为准。