光伏-储能-负荷联合预测:给 EMS 装上"预知能力"

本文结合我正在维护的一套工业储能管理系统的真实工程实践,聊聊如何把预测能力嵌入 EMS 决策链路。代码片段均来自项目实际文件,不是伪代码。


一、为什么 EMS 需要预测?

我在调试这套系统的 MQTT 指令链路时,发现一个有意思的现象:EMS 的充放电决策完全依赖当前时刻的 SOC 和预设的时段计划表。翻开 ems_management.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)包含:

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 转换:

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_ChargedEnergyfCalc_DisChargedEnergy。这些数据在预测框架里扮演"状态变量"角色——预测模型的输出要结合当前 SOC 才能转化为可执行的充放电指令。


三、光伏发电功率预测

3.1 NWP + 历史数据融合

光伏功率预测的难点在于它强依赖气象条件,而气象本质上是混沌系统。工程上通行的做法是两路信号融合:

NWP(数值天气预报)路:从气象局或商业 API 获取未来 24-72 小时的辐照度(GHI/DNI/DHI)、云量、温度预报。辐照度是光伏出力的物理驱动量,理论上 P p v = η ⋅ A ⋅ G P_{pv} = \eta \cdot A \cdot G Ppv=ηAG η \eta η 为效率, A A A 为面积, G G G 为辐照度),但实际逆变器效率随温度、老化程度变化,所以 NWP 只能给出粗粒度的趋势。

历史数据路:用过去 30-90 天的 fTotalPVOutputActPower 时序数据,学习"同类天气下的实际出力曲线"。关键是天气类型分类——晴天、多云、阴天、雨天的出力曲线形态差异极大,需要先做天气聚类再分类建模。

融合方式:NWP 预报作为外生变量(exogenous variable)输入模型,历史序列提供自回归特征。

3.2 模型选型:为什么不用 LSTM?

很多人第一反应是 LSTM,但在工业场景里我更倾向于 N-BEATSPatchTST,原因如下:

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 ( L 2 ) O(L^2) O(L2) 降到 O ( L log ⁡ L ) O(L \log L) O(LlogL),适合预测窗口很长(比如 72 小时)的场景。

对于本项目这种 15 分钟粒度、预测 24 小时的需求,我会优先试 PatchTST,patch size 设为 16(对应 4 小时历史窗口),预测长度 96 个点。

3.3 不确定性量化:分位数回归

点预测给出的是期望值,但 EMS 决策需要知道"最坏情况下光伏出力是多少"。这就是概率预测的价值。

分位数回归(Quantile Regression)的损失函数:

L q ( y ^ , y ) = q ⋅ max ⁡ ( y − y ^ , 0 ) + ( 1 − q ) ⋅ max ⁡ ( y ^ − y , 0 ) \mathcal{L}_q(\hat{y}, y) = q \cdot \max(y - \hat{y}, 0) + (1-q) \cdot \max(\hat{y} - y, 0) Lq(y^,y)=qmax(yy^,0)+(1q)max(y^y,0)

同时预测 q = [ 0.1 , 0.5 , 0.9 ] q = [0.1, 0.5, 0.9] 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 N 步的状态预估,然后求解一个优化问题,得到未来 N N N 步的最优控制序列,只执行第一步,下一个周期滚动重复。

优化目标(以经济性为例):

min ⁡ u t , . . . , u t + N ∑ k = 0 N − 1 [ c k b u y ⋅ P g r i d , k + − c k s e l l ⋅ P g r i d , k − + λ ⋅ Δ u k 2 ] \min_{u_t, ..., u_{t+N}} \sum_{k=0}^{N-1} \left[ c_k^{buy} \cdot P_{grid,k}^+ - c_k^{sell} \cdot P_{grid,k}^- + \lambda \cdot \Delta u_k^2 \right] ut,...,ut+Nmink=0N1[ckbuyPgrid,k+cksellPgrid,k+λΔuk2]

其中 c k b u y / s e l l c_k^{buy/sell} ckbuy/sell 是预测的电价, P g r i d + P_{grid}^+ Pgrid+ 是购电功率, P g r i d − P_{grid}^- Pgrid 是售电功率, λ ⋅ Δ u k 2 \lambda \cdot \Delta u_k^2 λΔuk2 是控制平滑项(避免频繁切换充放电)。

约束条件

S O C m i n ≤ S O C k ≤ S O C m a x SOC_{min} \leq SOC_k \leq SOC_{max} SOCminSOCkSOCmax
P b a t t , m i n ≤ P b a t t , k ≤ P b a t t , m a x P_{batt,min} \leq P_{batt,k} \leq P_{batt,max} Pbatt,minPbatt,kPbatt,max
S O C k + 1 = S O C k + η c h g ⋅ P b a t t , k + − P b a t t , k − / η d i s E c a p ⋅ Δ t SOC_{k+1} = SOC_k + \frac{\eta_{chg} \cdot P_{batt,k}^+ - P_{batt,k}^- / \eta_{dis}}{E_{cap}} \cdot \Delta t SOCk+1=SOCk+EcapηchgPbatt,k+Pbatt,k/ηdisΔt

功率平衡约束:

P p v , k + P b a t t , k + P g r i d , k = P l o a d , k P_{pv,k} + P_{batt,k} + P_{grid,k} = P_{load,k} Ppv,k+Pbatt,k+Pgrid,k=Pload,k

这里 P p v , k P_{pv,k} Ppv,k P l o a d , k P_{load,k} Pload,k 就是预测模型的输出。

5.3 与现有代码的接口设计

要把 MPC 嵌入现有系统,最小侵入的方式是:不改变底层执行逻辑,只替换决策层的参数生成

现有系统通过 MQTT 下发 cmd/set/emu/powerCmd 来控制充放电模式,通过 cmd/set/emu/dispatchPower 下发调度计划。MPC 优化器可以作为一个独立进程运行,每 15 分钟执行一次:

  1. 从数据库读取最新的 fTotalPVOutputActPowerfGridMeter_PowernSOC
  2. 调用预测模型,生成未来 24 小时的 P ^ p v \hat{P}_{pv} P^pv P ^ l o a d \hat{P}_{load} P^load
  3. 求解 MPC 优化问题(用 scipy.optimizecvxpy
  4. 把优化结果转换为 dispatchPower 的 JSON 格式,通过 MQTT 下发

注意 EMU_dispatchPower 函数(GH_DigitalEnergy_Pub.c:2582)里有时间戳校验:

// 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),用 glpkHiGHS 求解,几十个变量的问题毫秒级可解
  • 如果资源实在紧张,可以把预测和优化放到云端,设备只负责执行,通过 MQTT 接收优化后的调度计划

七、小结

从"当前 SOC + 时段表 → 充放电决策"到"未来 24 小时预测 + MPC 优化 → 前瞻调度",这不是一个简单的模型替换,而是整个决策架构的升级。

核心链路是:原始采样数据(PV 逆变器 + DLT645 电表)→ 特征工程 → 时序预测模型(PatchTST/N-BEATS)→ 概率预测区间 → MPC 优化求解 → MQTT 指令下发 → 设备执行

每个环节都有工程细节需要处理,但最重要的是把预测结果真正接入决策闭环,而不是让它停留在离线分析报告里。现有代码里的 dispatchPower 接口已经提供了足够的控制自由度,缺的只是上游的预测驱动层。

这套方案在理论上可以把储能系统的经济收益提升 15-30%(相比纯规则调度),具体数字取决于当地电价结构和光伏/负荷的波动程度。值得投入工程资源去做。


代码引用均来自项目实际文件,文中行号以当前代码版本为准。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐