电力需求响应——SEMS/REMS层级架构下的多站协调控制
作者:储能系统固件开发工程师
日期:2026-04-14
项目背景:工商业储能管理系统(BESS),支持川崎项目多站联合需求响应,SEMS为站级本地EMS,REMS为上级远程EMS
一、为什么需要两级EMS?
最初,项目只有一个站级EMS(站级能量管理系统,SEMS)。SEMS负责控制本站的PCS充放电,执行时段计划、防逆流、防过载,运行得还算平稳。
然而,当客户在川崎项目提出"多个储能站联合响应电网调峰指令"的需求时,问题来了:
电网调度中心下发一条指令:
整个工业园区在未来30分钟内需放电500kW园区内有4个储能站,每站容量不同,当前SOC不同,部分站在检修
谁来决定每个站放多少?
如果某站BMS故障,其他站如何自动补偿?
这就是 REMS(Remote Energy Management System,远程EMS) 存在的根本原因。REMS是站上的站——它管理多个SEMS,协调整体功率分配,对接电网调度。
头文件里这行注释写得很清楚:
// REMS 上级EMS
// SEMS:本地EMS REMS:上级EMS
二、系统架构:三层控制金字塔
┌───────────────────────────────────────
│ 电网调度中心 / 虚拟电厂平台 │
│ Demand Response 指令下发 │
└─────────────────┬─────────────────────
│ TCP/IP (Modbus/MQTT)
┌─────────────────▼─────────────────────
│ REMS(远程EMS) │
│ 多站功率分配 防逆流/防过载汇总 │
│ clientInfoSum 聚合所有站状态 │
└────┬──────────┬────────────┬──────────
│TCP │TCP │TCP
┌────▼───┐ ┌──▼────┐ ┌────▼───┐
│ SEMS-1 │ │SEMS-2 │ │ SEMS-N │
│ 站级EMS│ │站级EMS│ │ 站级EMS│
└────┬───┘ └──┬────┘ └────┬───┘
│ │ │
PCS-1 PCS-2 PCS-N
BMS-1 BMS-2 BMS-N
关键设计决策:REMS和SEMS通过TCP长连接通信,SEMS定期向REMS上报 B6_LSYS_DATA(系统实时数据),REMS解析后计算分配方案,再下发 UCTL(Unit Control)命令给各SEMS。
这套架构的核心数据结构是 A_BLOB_SEMS_CLIENTINFO_SUM,它是REMS视角下所有站的聚合快照:
A_BLOB_SEMS_CLIENTINFO_SUM* pClientSum = &pEMS->clientInfoSum;
// REMS下发的控制意图
EMSCtrlMode emEMSCtrlMode_REMS = pClientSum->emEMSCtrlMode_REMS;
ACDC_WORKSTATU emACDC_WorkStatu_REMS = pClientSum->emACDC_WorkStatu_REMS;
float fActvPwrReq_REMS = pClientSum->fActvPwrReq_REMS;
// 各站聚合数据
pClientSum->fMax_Chrg_PwrAbilitySum // 所有站可用充电能力之和
pClientSum->fMax_DisChrg_PwrAbilitySum // 所有站可用放电能力之和
pClientSum->nSOCMinSum // 所有站最低SOC
pClientSum->nSOCMaxSum // 所有站最高SOC
三、站级EMS(SEMS)的职责边界
3.1 SEMS能做什么
SEMS是"执行者",它:
管理本站PCS状态机(Idle → WaitCharge → Charging → Discharging → Faulted → OffGrid)
执行本地时段计划(
planSectorNow,按时段设定充放电模式和截止SOC)实时防逆流/防过载(基于本站电表数据)
向REMS上报本站实时状态(功率、SOC、故障状态)
执行REMS下发的控制指令(但要过优先级滤网)
3.2 SEMS不能做什么
SEMS 不知道其他站的状态,也不能直接协调跨站功率。这是REMS的职责。
当 enableREMSCtrl == FALSE 时,SEMS完全独立运行,只执行本地计划;当 enableREMSCtrl == TRUE 时,SEMS将REMS的指令叠加到本地决策上。
3.3 PCS状态机:SEMS的核心执行引擎
sems_statemachine.c 是SEMS的"运动控制器"。状态机在 Initialize_SEMS_StateM() 中初始化:
PcsSMItem *p = &pCCU->semsSMItems[0];
INIT_PCS_STATE_MACHINE(p, "Idle", PCS_STATE_IDLE, OnIdle);
INIT_PCS_STATE_MACHINE(p, "WaitCharge", PCS_STATE_WAIT_CHARGE, OnWaitCharge);
INIT_PCS_STATE_MACHINE(p, "Charging", PCS_STATE_IN_CHARGING, OnCharging);
INIT_PCS_STATE_MACHINE(p, "WaitDischarge", PCS_STATE_WAIT_DISCHARGE, OnWaitDischarge);
INIT_PCS_STATE_MACHINE(p, "Discharging", PCS_STATE_IN_DISCHARGING, OnDischarging);
INIT_PCS_STATE_MACHINE(p, "Faulted", PCS_STATE_FAULTED, OnFaulted);
INIT_PCS_STATE_MACHINE(p, "OnOffGrid", PCS_STATE_OFFGRID, OnOffGrid);
状态驱动循环在 Sample_SEMS_StateM() 中执行,关键判断:
if (pCCU->emPMCtrlMode == AUTO_CTRL_PM_MODE)
{
for (n = 0; n < pCCU->emsMgmtData.nNodeNum; n++)
{
// 跳过未注册的客户端(SEMS节点)
if (s_MMClients[n].bNeedRegister) continue;
PcsSMInfo* pSMInfo = &pCCU->semsSMInfos[n];
emState = pSMInfo->pCurrSMItem->pfnActionPcs(pCCU, n); // 执行当前状态动作
pSMInfo->pLastSMItem = pSMInfo->pCurrSMItem;
pSMInfo->pCurrSMItem = &pCCU->semsSMItems[emState]; // 切换状态
if(pSMInfo->pCurrSMItem != pSMInfo->pLastSMItem)
{
pSMInfo->pCurrSMItem->tm_EnterPcs = GetCurrentTime(); // 记录进入时间
}
}
}
WaitCharge → Charging 的超时保护(PCS启动慢):
static PcsSMState OnWaitCharge(ROUGH_CCU_DATA* pCCU, int n)
{
// 请求不是充电了?立即返回Idle
if (emACDC_WorkStatuReq != STATU_ACDC_RECTIFY)
return PCS_STATE_IDLE;
// PCS已经开始充电,进入Charging状态
if (emACDC_WorkStatuNow == STATU_ACDC_RECTIFY)
return PCS_STATE_IN_CHARGING;
// 等待超过90秒还没启动 → 判定启动失败
if (ElapseMS(pSMInfo->tm_LastStartWork) >= MAX_INTV_MS_WAIT_PCS_WORK) // 90000ms
{
LogOut_WorkingStatus(pCCU, n, "Start Chrg Fail");
return PCS_STATE_IDLE;
}
// 还在等,持续下发充电命令
Send_6330UctlWorkPwrAuto(pCCU, STATU_ACDC_RECTIFY, n, MAX_INTV_MS_CONTINUE_WORK);
return PCS_STATE_WAIT_CHARGE;
}
这里体现了一个工程经验:PCS从收到指令到实际出力,往往需要20~90秒(变压器励磁、预充等),系统必须耐心等待而不是反复重试。
四、指令优先级:本地保护 > REMS指令 > 本地策略
这是整个架构中最关键的安全设计。优先级从高到低:
4.1 第一优先级:本地保护(硬拦截)
在 RequestACDC_WorkStatu() 中,无论REMS发来什么指令,如果系统状态不满足条件,直接返回 STATU_ACDC_IDLE:
static ACDC_WORKSTATU RequestACDC_WorkStatu(ROUGH_CCU_DATA* pCCU, int n)
{
// 0. 离网判断:优先级最高,一旦检测到离网,立即停止所有充放电
if (JudgeIfNeedEnterOffGrid(pCCU, n))
return STATU_ACDC_IDLE;
ACDC_WORKSTATU emACDC_WorkStatuReq = MapBattWorkMode2ACDC_WorkStatu(pCalc_Node->emBatt_WorkMode);
// 1. 充电请求:满足以下任意条件 → 拦截,返回Idle
if (emACDC_WorkStatuReq == STATU_ACDC_RECTIFY)
{
if (pB6LSYSLast_SEMS->emCCUStateSum != CCU_STATE_IDLE || // 任意从站故障
pB6LSYSLast_SEMS->emPMCtrlModeSum != AUTO_CTRL_PM_MODE || // 手动模式
pB6LSYSLast_SEMS->fMax_Chrg_PwrAbilitySum0 < MIN_PWR_PCS_OUT_ABL || // 充电能力不足2kW
fMaxCurrAbilitySum < MIN_CURR_PCS_OUT_ABL) // 电流能力不足2A
{
TRACEW("RequestACDC_WorkStatu STATU_ACDC_RECTIFY return STATU_ACDC_IDLE ...");
return STATU_ACDC_IDLE;
}
return STATU_ACDC_RECTIFY;
}
// 放电请求同理...
}
三个本地保护关卡:
emCCUStateSum != CCU_STATE_IDLE:只要有一个从站处于故障/锁定状态,整组停止emPMCtrlModeSum != AUTO_CTRL_PM_MODE:只要有一个从站在手动模式,自动控制拒绝执行功率能力门槛:
MIN_PWR_PCS_OUT_ABL = 2.0kW,MIN_CURR_PCS_OUT_ABL = 2.0A——能力太低说明BMS快耗尽或PCS异常
4.2 第二优先级:REMS指令(远程覆盖本地计划)
当REMS下发控制指令时(emEMSCtrlMode_REMS == EMSCtrlMode_Remote),SEMS切换到 bCtrlByREMS = TRUE 模式:
// 保留REMS下发的控制意图,不被ZERO_POBJS清零
EMSCtrlMode emEMSCtrlMode_REMS = pClientSum->emEMSCtrlMode_REMS;
ACDC_WORKSTATU emACDC_WorkStatu_REMS = pClientSum->emACDC_WorkStatu_REMS;
float fActvPwrReq_REMS = pClientSum->fActvPwrReq_REMS;
ZERO_POBJS(pClientSum, 1); // 清零聚合数据,但下面立刻恢复REMS参数
pClientSum->emEMSCtrlMode_REMS = emEMSCtrlMode_REMS;
pClientSum->emACDC_WorkStatu_REMS = emACDC_WorkStatu_REMS;
pClientSum->fActvPwrReq_REMS = fActvPwrReq_REMS;
注意这个写法:先把REMS参数保存到局部变量,ZERO_POBJS 清零整个结构体后再写回——防止聚合计算把REMS指令覆盖掉。这是一个精心设计的"保留域"。
REMS控制激活时,还会启用 WorkASAP(尽快充放电)模式:
// 加快自动充放响应,WorkASAP
pEMS->bChrgAsap = (pEMS->bEnter_AutoChrg_SEMS || pEMS->bCtrlByREMS);
pEMS->bDiscAsap = (pEMS->bEnter_AutoDisc_SEMS || pEMS->bCtrlByREMS);
WorkASAP 激活时,使用 fMax_Chrg_PwrAbilityA(快速响应通道的能力值)而不是经过滤波平滑的 fMax_Chrg_PwrAbility,响应速度更快但波动更大,适合需求响应的秒级响应场景。
4.3 第三优先级:本地时段计划(正常运营)
没有REMS干预时,SEMS按 planSectorNow(当前时段计划)执行:
if (!pEMS->bChrgAsap && !pEMS->bDiscAsap)
{
// 按计划执行,经过SOC判断和滤波平滑
pCalc_Node->fMax_Chrg_PwrAbility = pB6LSYSLast->fMax_Chrg_PwrAbilitySum;
// SOC到达截止值时,自动停止
bStopChrgBySoc_Plan = (pB6LSYSLast->nSOCMinSum >= (BYTE)pCalc_Node->nStopSOCReq);
if (bStopChrgBySoc_Plan)
{
pCalc_Node->fMax_Chrg_PwrAbility = 0; // 充电能力清零 → 不再充电
pCalc_Node->emBatt_WorkMode = BATT_WORKMODE_IDLE;
}
}
五、多站防逆流/防过载:功率分配的工程实现
5.1 防逆流(Anti-Reflux)
防逆流的目标:禁止储能向电网倒送电。当 enableAntiRflx_REMS == TRUE 时,放电总功率上限被网侧电表实时数据约束:
if (pEMS->enableAntiRflx_REMS)
{
// 放电能力 ≤ 当前允许的最大放电限制(来自上级EMS)
pClientSum->fMax_DisChrg_PwrAbilitySum =
MIN(pEMS->fTotal_PessDischgLmt_Org, pClientSum->fMax_DisChrg_PwrAbilitySum);
pClientSum->fMax_DisChrg_PwrAbilitySumA =
MIN(pEMS->fTotal_PessDischgLmt_Org, pClientSum->fMax_DisChrg_PwrAbilitySumA);
}
fTotal_PessDischgLmt_Org 的计算路径:电网侧实际负载 - 电网输入允许最大功率,物理含义是"还能从储能放多少电而不产生逆流"。
5.2 防过载(Anti-Overload)
防过载的目标:禁止充电功率超出电网允许的输入上限。实现对称:
if (pEMS->enableAntiOvld_REMS)
{
pClientSum->fMax_Chrg_PwrAbilitySum =
MIN(pEMS->fTotal_PessChgAvaliable_Org, pClientSum->fMax_Chrg_PwrAbilitySum);
pClientSum->fMax_Chrg_PwrAbilitySumA =
MIN(pEMS->fTotal_PessChgAvaliable_Org, pClientSum->fMax_Chrg_PwrAbilitySumA);
}
5.3 多站功率分配:SOC排序 + 比例分配
REMS拿到总放电需求 fActvPwrReq_REMS 后,需要把它分配给N个站。核心算法在 Ems_Mgmt_Main_SEMS() 中的分配链:
// Step 1: 刷新各节点参数(从各站LSYS数据计算能力)
Refresh_NodeParam_SEMS(pCCU);
// Step 2: 节点排序(按SOC或按已充电量)
if (pEMS->emSortMethod == SORT_BY_CHGENG)
{
Sort_NodeOrderByChrgedEnergy(pCCU); // 按历史充电量排序(均衡能量吞吐)
}
else // SORT_BY_SOC
{
if (pEMS->emBatt_WorkModeReq == BATT_WORKMODE_FIRST_CHRGING)
Sort_NodeOrderBySoc(pCCU, CHARGE); // 充电:SOC低的优先充
else if (pEMS->emBatt_WorkModeReq == BATT_WORKMODE_FIRST_DISCHRG)
Sort_NodeOrderBySoc(pCCU, DISCHARGE); // 放电:SOC高的优先放
}
// Step 3: 功率分配
if (pEMS->emBatt_WorkModeReq == BATT_WORKMODE_FIRST_CHRGING)
{
MPET_Calc4DispatchChrgMode(pCCU); // 计算总充电需求
Dispatch_ESSLmtBattChrg_Power(pCCU); // 按能力比例分配给各站
}
else if (pEMS->emBatt_WorkModeReq == BATT_WORKMODE_FIRST_DISCHRG)
{
MPET_Calc4DispatchDisChrgMode(pCCU); // 计算总放电需求
DisPatch_ACLoadReq_DisChrg_Power(pCCU); // 按能力比例分配给各站
}
SOC排序 + 优先级分配的逻辑:
放电时:SOC最高的站先放(保证总体可放电时长最长)
充电时:SOC最低的站先充(快速补充最需要的站)
当某站
fMax_DisChrg_PwrAbility == 0(BMS禁止放电或SOC耗尽),跳过该站,剩余功率分配给其他站
5.4 总功率上限保护(多级串联)
// 第一层:REMS限制(来自上级调度)
if (pEMS->enableAntiOvld_REMS)
pClientSum->fMax_Chrg_PwrAbilitySum = MIN(pEMS->fTotal_PessChgAvaliable_Org, ...);
// 第二层:站级拓扑限制(二级主控才有)
if (pCCU->lcCfg.lcType > LC_TYPE_1st)
{
pClientSum->fMax_Chrg_PwrAbilitySum = MIN(pCCU->fTotalPowerLimit, ...);
pClientSum->fMax_DisChrg_PwrAbilitySum = MIN(pCCU->fTotalPowerLimit, ...);
}
三层 MIN() 串联:REMS限制 → 拓扑限制 → BMS物理能力,任何一层的上限都可以限制最终分配功率,取最严格的那个。
六、需求响应(Demand Response)的SEMS实现路径
6.1 需求响应信号的接收
电网调度下发DR信号,REMS解析后写入 pClientSum->emACDC_WorkStatu_REMS 和 fActvPwrReq_REMS。SEMS在 Refresh_NodeParam_SEMS() 开始时保留这些值:
// DR信号解析后的执行链:
// 电网调度 → REMS TCP → pClientSum.emACDC_WorkStatu_REMS = STATU_ACDC_INVERTER
// → pClientSum.fActvPwrReq_REMS = 500.0 (kW)
// → 各SEMS解析 → emBatt_WorkMode = BATT_WORKMODE_FIRST_DISCHRG
// → 状态机触发 OnIdle → Send_6330UctlWorkPwrAuto(STATU_ACDC_INVERTER)
// → PCS开始放电
6.2 待机缓冲机制(Standby)
需求响应有一个特殊场景:DR信号发出后,PCS从待机到满功率出力需要10~30秒。SEMS用 bStandby_BLL 管理这个缓冲:
// 待机命令同步给各从站(节拍800ms)
if (pB6LSYSLast->bStandby_BLLSum != pEMS->bStandby_BLL &&
ElapseMS(pCalc_Node->fTmLastSend6360Standby) > 800)
{
pCalc_Node->fTmLastSend6360Standby = GetCurrentTime();
Send_6360UCTL_Standby(n, pEMS->bStandby_BLL); // 发送待机命令
}
待机状态的组合逻辑:
pEMS->bStandby_BLL = (
pEMS->planSectorOrg.emBatt_WorkMode == BATT_WORKMODE_STANDBY || // 计划待机
pEMS->bStandby_Auto || // 自动触发待机(等待DR信号到来)
pEMS->bStandby_Set || // 用户设置待机
pEMS->bStandby_AEMS // 上级AEMS要求待机
);
当系统处于 STATU_ACDC_STANDBY 时,PCS保持热备用状态——变压器励磁已完成,逆变器待机,可以在秒级内响应出力指令,这是DR快速响应的关键。
6.3 SOC截止的双层保护
DR执行中,当SOC降至截止值时,SEMS同时检查两个来源的停止条件:
// 来源1:时段计划设定的截止SOC
bStopDiscBySoc_Plan = (pB6LSYSLast->nSOCMaxSum <= (BYTE)pCalc_Node->nStopSOCReq);
// 来源2:SEMS/REMS设定的截止SOC(更快响应)
bStopDiscBySoc_SetSEMS = (pB6LSYSLast->nSOCMaxSum <= pEMS->nStopSOC_DiscSet_SEMS);
if (bStopDiscBySoc_Plan || bStopDiscBySoc_SetSEMS)
{
pCalc_Node->fMax_DisChrg_PwrAbility = 0; // 停止放电
pCalc_Node->emBatt_WorkMode = BATT_WORKMODE_IDLE;
}
两个截止SOC并列检查,任一触发都停止——这是双重保险,防止SOC过放损坏电池。
6.4 通信故障的降级处理
SEMS检测到某从站通信故障时,该站的聚合贡献被清零:
if (ElapseS(pClient->tmLastRcvHeatbeat) >= MAX_S_COMMFAIL_SEMS)
{
pClient->bCommFailSemsClient = TRUE;
}
if (pClient->bCommFailSemsClient)
{
ZERO_POBJS(pB6LSYSLast, 1); // 清零该站数据
pB6LSYSLast->emCCUStateSum = CCU_STATE_FAULTED; // 标记为故障
}
整个系统的状态聚合采用"最差优先"原则:
// 只要有一个站通信全断 → 整体Faulted
if (bCommFailAll || bAllFAULTED || pCCU->emCCUState == CCU_STATE_FAULTED)
pClientSum->emCCUStateSum = CCU_STATE_FAULTED;
// 只要有一个站锁定 → 整体Locked
else if (bAllLOCKED || pCCU->emCCUState == CCU_STATE_LOCKED)
pClientSum->emCCUStateSum = CCU_STATE_LOCKED;
七、AI结合:多智能体强化学习(MARL)实现多站最优协调
7.1 现有规则算法的局限
当前的功率分配算法(SOC排序 + 比例分配)是确定性规则,存在明显局限:
| 限制 | 说明 |
|---|---|
| 无预测性 | 只看当前SOC,不预测未来负荷和光伏出力 |
| 无全局最优 | 按容量比例分配不一定是经济最优 |
| 无学习能力 | 每次DR都用同样逻辑,无法从历史中改进 |
| 无价格敏感 | 不区分峰谷电价时段的分配优化 |
7.2 MARL(多智能体强化学习)的适用性
将每个SEMS站视为一个智能体(Agent),REMS作为中央协调者(Coordinator),构建MARL框架:
┌───────────────────────────────────────
│ REMS(全局协调者) │
│ 观测:所有站SOC、功率、电价、负荷预测、DR信号 │
│ 奖励:总经济收益 - 电池损耗 - DR违约罚款 │
└──────┬──────────────┬────────────┬────
│ │ │
┌────▼───┐ ┌────▼───┐ ┌────▼───┐
│Agent-1 │ │Agent-2 │ │Agent-N │ ← 每个站一个智能体
│本地策略│ │本地策略│ │本地策略│
│(Actor) │ │(Actor) │ │(Actor) │
└────────┘ └────────┘ └────────┘
7.3 状态空间、动作空间与奖励函数设计
状态空间 \(s_t^i\)(第 $i$ 站在时刻 $t$ 的观测):
s_t^i = [
SOC_i, // 当前SOC(0~1)
P_max_chrg_i, // 最大可充电功率
P_max_dischrg_i, // 最大可放电功率
P_grid_i, // 本站电网侧功率(防逆流参考)
P_load_i, // 本站负荷功率
DR_target, // 当前DR目标功率
price_t, // 当前电价
soc_min_sum, // 所有站最低SOC(共享状态)
P_total_dischrg_now // 当前所有站总放电功率(共享状态)
]
动作空间 \(a_t^i\)(第 $i$ 站在时刻 $t$ 的决策):
a_t^i ∈ {充电功率设定, 放电功率设定, 待机}
// 离散化:[-P_max, -0.75*P_max, -0.5*P_max, 0, 0.5*P_max, 0.75*P_max, P_max]
全局奖励函数 \(R_t\):
$$R_t = \underbrace{P_{total,t} \cdot \Delta t \cdot price_{DR}}{\text{DR响应收益}} - \underbrace{\sum_i \alpha_i \cdot |P_i - P{i,target}|}{\text{分配误差惩罚}} - \underbrace{\sum_i \beta \cdot C{degradation}(SOC_i, I_i)}{\text{电池损耗代价}} - \underbrace{\mathbb{1}[P{grid} > 0] \cdot \gamma}_{\text{逆流惩罚}}$$
7.4 与现有SEMS代码的集成点
MARL的输出可以映射到现有的控制接口,无需修改底层逻辑:
// 当前代码:规则算法计算的分配结果
pCalc_Node->fACDC_Distri_4_BattChrg_Pwr // 充电功率分配
pCalc_Node->fBatt_Distri_4_ACDC_Pwr // 放电功率分配
// MARL接入点:在 Dispatch_ESSLmtBattChrg_Power() 之前注入AI决策
// 伪代码:
void MARL_Override_Dispatch(ROUGH_CCU_DATA *pCCU)
{
if (!pEMS->enableMARLCtrl) return;
float marl_actions[MAX_NODES];
MARL_GetActions(pCCU, marl_actions); // 调用ONNX推理接口
for (int n = 0; n < pEMS->nNodeNum; n++)
{
// 注入AI决策,但仍受硬件能力上限约束
pEMS->calc_Node[n].fACDC_Distri_4_BattChrg_Pwr =
MIN(marl_actions[n], pEMS->calc_Node[n].fMax_Chrg_PwrAbility);
}
}
关键设计原则:MARL的输出必须通过 MIN() 约束在物理能力范围内,AI决策不能绕过硬件保护。
7.5 训练数据来源
项目代码中已有完整的日志输出(LOGFD):
LOGFD("Ems_Mgmt_Main_SEMS WorkModeReq:%s ... "
"f_PgridMeter:%.1f f_PcabMeter:%.1f ... "
"fMax_Chrg_PwrAbilitySum:%.1f ... "
"nSOCMinSum:%d nSOCMaxSum:%d fSOCSum:%.1f ...",
...);
这些日志可以直接作为离线训练数据集:提取每秒/每分钟的状态快照,计算实际的电费收益作为奖励标签,用于离线强化学习(Offline RL)或模仿学习训练初始策略。
7.6 MARL vs 规则算法:实测效果预期
根据类似项目的研究数据:
| 指标 | SOC排序规则 | MARL |
|---|---|---|
| DR响应偏差 | ±15% | ±5% |
| 电池循环寿命 | 基准 | +8%~12%(减少不必要深放) |
| 峰谷套利收益 | 基准 | +12%~18% |
| 计算延迟 | <1ms | <10ms(ONNX推理) |
八、工程坑与经验总结
在这套SEMS/REMS架构的开发过程中,我踩过几个深坑:
1. ZERO_POBJS 清零时机的问题。clientInfoSum 在每个采样周期开始时被清零然后重新累加,但REMS下发的控制意图(emEMSCtrlMode_REMS 等)是跨周期持久的。早期版本没有把这些字段单独保护,导致REMS指令在下一个采样周期被清零,PCS在充放电中途突然停止。现在的做法是先保存到局部变量,清零后写回。
2. 多站状态聚合的"最差优先"原则引发的误报。
最初用"任意一站故障就停全局",在某次SEMS客户端TCP断线3秒(网络抖动)时,整个系统停止充电。后来加入了心跳超时容忍(MAX_S_COMMFAIL_SEMS)和单站/全部通信失败分级处理。
3. PCS启动时序与状态机不同步。
PCS从收到充电命令到DC侧电流超过2A(MIN_CURR_WORKING),现场测量最慢需要87秒。MAX_INTV_MS_WAIT_PCS_WORK = 90000ms 就是根据这个数据设定的。如果超时阈值设得太小,会导致频繁的"启动失败"误判,进而触发不必要的保护动作。
4. WorkASAP模式的振荡问题。
REMS下发DR指令时启用 bDiscAsap,使用未经滤波的功率能力 fMax_DisChrg_PwrAbilityA。在早期版本中,这导致功率指令高频抖动,PCS频繁在满功率和最小功率之间切换。解决方案是在REMS指令切换时加入200ms防抖延迟,在 Filter_Disttribute_Power() 中保留必要的低通滤波。
5. 多级拓扑(LC_TYPE_1st/2nd)的兼容性。
一级主控和二级主控的功率分配逻辑不同——一级主控直接控制下属SEMS,二级主控需要将限制值传递给一级主控再传给SEMS。代码中用 pCCU->lcCfg.lcType > LC_TYPE_1st 区分,但早期测试时搞混了层级,导致总功率限制被应用了两次(平方级削减)。
九、结语
SEMS/REMS的层级架构,本质上是分布式控制系统(DCS)在储能场景的特化实现。它解决了三个核心矛盾:
速度 vs 协调:本地状态机(毫秒级响应)+ 远程协调(秒级响应)的分层,兼顾快速保护和全局最优。
自主 vs 服从:本地保护硬拦截(永远最优先)+ REMS指令软覆盖(可被本地保护推翻),安全性有保障。
规则 vs 智能:确定性规则保证基本可靠性,MARL在此基础上追求经济最优,两者不对立而是互补。
从代码层面看,sems_statemachine.c 是"执行者",sems_ems_management.c 是"决策者",两个文件的分工之所以清晰,正是因为架构设计时已经明确了职责边界。这种关注点分离(Separation of Concerns)的设计思想,是整个系统可维护性的基础。
电网需求响应不只是一个算法问题,更是一个系统工程问题。在正确的架构基础上,AI算法才能发挥它应有的价值——而不是让AI去处理那些本应由确定性逻辑处理的安全关键场景。
本文基于实际工程项目代码 sems_statemachine.c + sems_ems_management.c 分析撰写,注释引用来自原始代码。