在光储优化调度中的建模与实战)
在工厂屋顶光伏项目做并网联调时我遇到过一件挺打脸的事。那套储能系统装好之后运营负责人坚持用“最简单可靠的策略”光伏出力大的午间时段固定给电池充满晚上高峰时段固定放电。结果一个多云天气的下午光伏出力从350kW在三分钟内掉到120kW储能电池还停在满充状态厂区负载同时上涨功率缺口全部压到变压器上最终触发过载保护逆变器脱网。问题出在哪调度策略没有把“光伏预测波动”“电池电量状态”“实时电价信号”放到一个模型里统筹考虑。从那以后我开始认真对待光储优化调度这件事并确定了一个基本思路把调度问题做成一个最大可解释、可验证、可部署的数学规划模型核心工具就是整数规划更准确地说是混合整数线性规划MILP。这篇内容我会完整地讲一遍从问题定义、模型搭建、代码实现到现场验证的过程也会把运行中踩过的坑一并列出来。内容面向做光储项目开发、运维和搞能量管理系统EMS的工程师也适合刚入门想系统理解优化调度原理的同学。这里头没有太多虚的“算法包装”全是能直接落到代码和现场的东西。1. 光储调度的问题本质离散决策让“最优”变成了组合爆炸1.1 调度员每天要做的三个核心决策光伏储能联合系统的调度表面上看是“什么时候充电、什么时候放电”实际上每一时刻调度员都在做一组离散选择储能系统处于充电状态、放电状态还是待机状态同时还要决定从电网买电还是向电网卖电。这些状态不是连续可调的旋钮而是互斥的动作开关。以常见的工商业光储系统为例功率调度周期通常取15分钟或1小时一天24个或96个时段每个时段都要确定上述状态组合。如果只是按人工经验做“午充晚放”的时间表遇到电价波动、负荷波动和光伏预测偏差叠加时很容易出现我前面说的那种事故。1.2 为什么连续优化模型搞不定这个场景很多人第一反应是光储调度不就是线性规划LP吗把充放电功率、购售电功率全部设为连续变量目标函数是费用最小化约束是功率平衡和SOC递推然后调用求解器一把梭。问题在于光储系统的物理约束中有不少“非此即彼”的逻辑。比如储能电池在同一个调度时段内不允许同时充电和放电这个约束用两个连续变量P_ch和P_dis没法天然表达。若不强制互斥优化器为了“套利”可能会让电池一边充满一边放空得出一个物理上无意义的答案。系统在某个时段只能选择购电或售电之一同样存在状态互斥。如果系统包含多台PCS储能变流器或柴油发电机还有机组启停问题那是更典型的0-1决策。这些逻辑必须靠整数变量通常取0或1来建模。引入整数变量之后问题从线性规划升级为混合整数线性规划。千万别小看这个“升级”数学性质有了本质变化可行域从连续凸集变成离散点集的组合求解难度不再是多项式时间可保证而是典型的NP难问题。上面提到的“午充晚放”经验法则恰恰是对这个离散问题的一种极其粗糙的启发式近似。1.3 一个0-1变量如何解决“互斥”难题关于充放电互斥标准做法是引入两个二进制变量u_ch和u_dis分别代表充电状态和放电状态然后把充电/放电功率的上限约束写成P_ch[t] ≤ P_max × u_ch[t]P_dis[t] ≤ P_max × u_dis[t]u_ch[t] u_dis[t] ≤ 1这样一来当u_ch1时P_ch可以在0到P_max之间取任意值但u_dis必须为0P_dis只能为0。反之亦然。两个变量同时为0时储能处于待机状态。这组约束就是整数规划在光储调度中最经典的落地场景。这种建模方式看似简单但它是整个优化调度的基石。没有这个0-1互斥机制任何求解器给出的“最优解”都可能是物理上无法执行的一纸空文。后面我会详细展开当实际部署时如果约束写得不够严谨求解结果看起来费用很低但送到PCS执行时会直接报警拒动。2. 为什么是整数规划与其他优化方法的正面较量2.1 MILP远比想象的“线性”问题灵活混合整数线性规划这个名词容易让人误解以为只能处理线性关系限制很大。实际上工程中大量非线性关系可以通过分段线性化、大M法、逻辑约束等方式转换成MILP可处理的形式。光储调度里的核心关系——SOC递推、充放电效率、功率上下限——本身就是线性或近似线性的所以MILP几乎是量身定做的工具。用一个通俗类比MILP就像在棋盘格子上下棋有些变量是连续的“棋子位置”有些变量是离散的“棋子数量”而约束就是“棋规”。线性目标函数让你清楚知道每走一步的代价整数变量则保证每一步都符合实际规则。求解器在背后用分支定界、割平面等方法在规则范围内找到成本最低的那套走法。MILP还有一个其他算法很难替代的优势它给出的是全局最优解在模型和预测数据准确的前提下。对于光储这种投资动辄几十万到上百万的设备一个“接近最优”的局部解可能意味着每年几万块钱的收益损失这个账要算清楚。2.2 动态规划可行但会撞上“维度诅咒”动态规划DP也常被用来做储能调度。核心思路是把调度问题视为多阶段决策每个阶段的状态是电池SOC决策是充放电功率用递推方程逐步求最优。DP的原理很漂亮但它面临严重的“维度诅咒”如果SOC离散成100个档位调度周期96个时段状态转移是100×96次计算但如果系统不只一个储能还有多个可控负荷、多台机组状态变量变成高维向量时计算量呈指数级爆炸。我曾在项目里尝试用DP做“光伏储能柴油发电机”的联合调度SOC和油箱油位两个状态变量就导致计算时间到了不可接受的程度。相比之下MILP求解器对中等规模的光储问题几十个整数变量、几百个约束基本上几秒到几分钟内就能求出全局最优解。2.3 启发式算法为什么只能当备胎遗传算法、粒子群这类启发式算法在很多优化场景里很流行。但用在光储调度上我持保留态度。启发式算法的问题在于不保证最优性甚至不保证每次运行结果一致调参困难种群大小、交叉概率、变异概率都得人工试收敛判据模糊你不知道什么时候该停约束处理麻烦充放电互斥、SOC边界这类约束处理不好容易产生大量不可行解。不是说启发式算法一无是处而是在光储调度这类“模型清晰、规模适中、需要高可信度”的场景里MILP是更稳妥的基线方案。启发式更适合那些模型难以精确建模、维度极高、对最优性要求不高的探索性问题。下表归纳了几种方法在我实际使用中的对比方法最优性保证计算速度典型光储场景建模复杂度适用场景线性规划LP有但无法表达离散逻辑极快低不含互斥/启停的简化问题混合整数线性规划MILP有全局最优秒级到分钟级中主流光储调度、微网调度动态规划DP有离散化精度内状态维数高时极慢中高单储能/小状态空间启发式算法GA/PSO无严格保证中低探索性、大规模近似问题3. 模型搭建的完整推演从物理过程到数学约束3.1 决策变量把“一天24小时”变成求解器的世界开始建模前先把系统边界和决策变量定义清楚。以一个典型的工商业光储系统为例光伏装机500kW储能电池额定容量1000kWhPCS额定功率500kW系统与电网通过一台800kVA变压器连接。时间尺度上我习惯用1小时为一时段一天24个时段如果现场对功率波动敏感可以细化到15分钟96时段模型结构不变只是变量数量变为4倍。决策变量分为两类连续变量P_ch[t]充电功率kW、P_dis[t]放电功率kW、P_buy[t]电网购电功率kW、P_sell[t]向电网售电功率kW、SOC[t]储能荷电状态%。整数/二进制变量u_ch[t]、u_dis[t]充放电互斥标志、u_grid[t]购售电状态标志1表示购电0表示售电。这里u_grid[t]的引入是为了避免系统在同一时段既从电网买电又向电网卖电。虽然后面加上网电价小于购电价时优化器可能不会故意做“低卖高买”的事但现场存在光伏反送和负荷波动叠加的情况不加互斥约束的话优化结果中可能出现极短时段内购售电同时发生的伪解。所以这个0-1变量建议从一开始就加上。3.2 目标函数成本最小化不是唯一答案光储调度的目标函数不同项目差异很大。如果是用户侧储能通常是全天运行费用最小即购电费用减去售电收益。如果是电网侧项目可能还要考虑峰谷套利收益、需量电费管理、辅助服务收益等。建议第一版模型只做最核心的全天购电成本 - 售电收益同时把储能充放电循环造成的寿命损耗折算成成本纳入目标函数这个细节容易被忽略但很关键。目标函数可以写成min Σ_{t1}^{24} [ price_buy[t] × P_buy[t] - price_sell[t] × P_sell[t] c_degrad × (P_ch[t] P_dis[t]) ]其中price_buy[t]是t时段购电价price_sell[t]是上网电价或者售电价c_degrad是储能的单位充放电折旧成本。把折旧成本放进目标函数后优化器就不会为了让几块钱电价差去做无意义的深度充放从而提高电池循环寿命。3.3 约束条件每一行数学公式对应一个现场物理限制约束条件是最容易出问题的地方。少一个约束求解器就能“钻空子”给出一个无法执行的方案多一个冗余约束求解时间可能成倍增加。以下是光储调度最关键的几组约束也是我每次建模的“标准体检项”。第一组是功率平衡。任意时刻系统内所有功率必须平衡光伏出力 储能放电 电网购电 负荷 储能充电 电网售电。写成P_pv[t] P_dis[t] P_buy[t] P_load[t] P_ch[t] P_sell[t]这个约束是能源管理的物理基础不存在任何讨价还价的余地。第二组是SOC递推关系。储能电池的荷电状态是随时间累积的上一时刻SOC加上本时段充入或放出的能量得到当前SOCSOC[t1] SOC[t] (η_ch × P_ch[t] - P_dis[t] / η_dis) × Δt / E_ratedη_ch和η_dis分别是充电和放电效率工程上锂电池通常取0.92~0.97。这里有个新手容易踩坑的细节充电效率是“存进去多少”放电效率是“放出来多少”两个方向不能混用否则SOC递推会产生系统偏差运行十几个小时后SOC就漂移到边界外了。第三组是充放电互斥和功率限幅。前面已经写过用两个二进制变量加P_max限幅即可保证储能不会“边充边放”。同时还有电池本身的功率上限约束P_ch[t] ≤ PCS额定功率、P_dis[t] ≤ PCS额定功率。第四组是SOC上下限约束。出于安全和寿命考虑电池SOC通常限制在10%~90%之间不允许过充过放SOC_min ≤ SOC[t] ≤ SOC_max第五组是末端SOC恢复约束。调度周期结束时为了避免第二天无电可用通常要求SOC回到初始值附近SOC[T] SOC_init这个约束还可以写成SOC[T] ≥ SOC_end_min的松弛形式。需要注意的是如果强制要求精确等于初始值在光伏预测偏差较大的情况下可能导致整个调度计划不可行实际部署时建议给一个上下浮动区间。第六组是并网功率约束。变压器容量有限从电网购电和向电网售电的功率都要小于变压器允许的最大值P_buy[t] ≤ P_trans_max × u_grid[t]P_sell[t] ≤ P_trans_max × (1 - u_grid[t])这组约束同时实现了购售电互斥。3.4 为什么这些约束一个都不能少我在交付项目时总会被问一个问题“这些约束是不是写得有点多能不能简化”我的回答是每砍掉一组约束求解器一定会找到利用这个“漏洞”的方案只是时间早晚问题。举个例子如果不加SOC上下限约束优化器会在低谷电价时段把电池充到100%哪怕最后几个时段只有几分钱电价差它也会为了最小化目标函数把电池榨干到0%。电池管理系统BMS层面虽然会强制保护但调度指令和BMS保护冲突时最终结果往往是PCS反复启停系统稳定性严重受损。再比如不加入末端SOC恢复约束求解器会在最后一个时段把SOC“清零”导致第二天早晨无电可用。这个错误我在早期版本里真实犯过当时调试时看到调度计划最后几小时放电功率特别大直觉不对一查果然是末端约束漏了。4. 代码实现用Pyomo把一个调度问题变成可执行程序4.1 工程选型为什么我选了PythonPyomoCBC建模工具方面工业界常驻选项是GAMS、MATLABYALMIP、PythonPyomo。我目前的项目基本都基于PythonPyomo原因有三条一是开源免费客户现场不需要额外买许可证二是生态好数据预处理、结果可视化跟pandas、matplotlib无缝衔接三是模型可读性强非算法背景的同事看代码也能大致理解。求解器方面开源首选CBCCOIN-OR Branch and Cut它处理中小规模的MILP问题够用。如果模型规模较大比如96时段、多储能、多机组建议换Gurobi或CPLEX商用求解器的分支定界效率确实高一个数量级但许可证费用不低。前期开发验证用CBC完全足够交付阶段再根据实际规模评估是否升级。4.2 模型代码逐段拆解下面给出一段可运行的核心框架代码省略具体数据加载部分展示如何用Pyomo实现前面推导的模型。这里采用1小时时段、24个调度周期。import pandas as pd from pyomo.environ import * # ---------- 参数输入 ---------- T 24 dt 1.0 # 时段长度小时 E_rated 1000.0 # 电池额定容量kWh P_pcs 500.0 # PCS额定功率kW eta_ch 0.95 # 充电效率 eta_dis 0.95 # 放电效率 SOC_min, SOC_max 0.1, 0.9 # SOC上下限 SOC_init 0.2 # 初始SOC SOC_end_min 0.2 # 末端SOC下限 P_trans_max 800.0 # 变压器最大交换功率 # 从外部读入的序列数据 price_buy [...] # 分时购电价长度24 price_sell [...] # 上网电价长度24 pv_forecast [...] # 光伏出力预测长度24 load_forecast [...] # 负荷预测长度24 # ---------- 模型定义 ---------- m ConcreteModel() m.t RangeSet(0, T - 1) # 连续决策变量 m.P_ch Var(m.t, domainNonNegativeReals, bounds(0, P_pcs)) m.P_dis Var(m.t, domainNonNegativeReals, bounds(0, P_pcs)) m.P_buy Var(m.t, domainNonNegativeReals, bounds(0, P_trans_max)) m.P_sell Var(m.t, domainNonNegativeReals, bounds(0, P_trans_max)) m.SOC Var(RangeSet(0, T), domainNonNegativeReals, bounds(SOC_min, SOC_max)) # 二进制变量 m.u_ch Var(m.t, domainBinary) m.u_dis Var(m.t, domainBinary) m.u_grid Var(m.t, domainBinary) # 目标函数购电成本 - 售电收益 电池折旧 def obj_rule(m): deg_cost 0.02 # 单位充放电折旧成本元/kWh return sum( price_buy[t] * m.P_buy[t] - price_sell[t] * m.P_sell[t] deg_cost * (m.P_ch[t] m.P_dis[t]) for t in m.t ) m.obj Objective(ruleobj_rule, senseminimize) # 约束1功率平衡 def power_balance_rule(m, t): return (pv_forecast[t] m.P_dis[t] m.P_buy[t] load_forecast[t] m.P_ch[t] m.P_sell[t]) m.power_balance Constraint(m.t, rulepower_balance_rule) # 约束2SOC递推 def soc_update_rule(m, t): return m.SOC[t1] m.SOC[t] ( eta_ch * m.P_ch[t] - m.P_dis[t] / eta_dis ) * dt / E_rated m.soc_update Constraint(m.t, rulesoc_update_rule) # 约束3末端SOC不低于下限 def soc_end_rule(m): return m.SOC[T] SOC_end_min m.soc_end Constraint(rulesoc_end_rule) # 约束4充放电互斥大M法 def ch_power_limit_rule(m, t): return m.P_ch[t] P_pcs * m.u_ch[t] def dis_power_limit_rule(m, t): return m.P_dis[t] P_pcs * m.u_dis[t] def mutex_rule(m, t): return m.u_ch[t] m.u_dis[t] 1 m.ch_limit Constraint(m.t, rulech_power_limit_rule) m.dis_limit Constraint(m.t, ruledis_power_limit_rule) m.mutex Constraint(m.t, rulemutex_rule) # 约束5购售电互斥 def grid_buy_limit_rule(m, t): return m.P_buy[t] P_trans_max * m.u_grid[t] def grid_sell_limit_rule(m, t): return m.P_sell[t] P_trans_max * (1 - m.u_grid[t]) m.grid_buy_limit Constraint(m.t, rulegrid_buy_limit_rule) m.grid_sell_limit Constraint(m.t, rulegrid_sell_limit_rule) # 初始SOC赋值 m.SOC[0].fix(SOC_init) # ---------- 求解 ---------- solver SolverFactory(cbc) result solver.solve(m, teeFalse) # ---------- 结果落盘 ---------- plan pd.DataFrame({ hour: list(range(1, T1)), pv: pv_forecast, load: load_forecast, P_ch: [m.P_ch[t].value for t in m.t], P_dis: [m.P_dis[t].value for t in m.t], P_buy: [m.P_buy[t].value for t in m.t], P_sell: [m.P_sell[t].value for t in m.t], SOC: [m.SOC[t].value for t in m.t], }) print(plan)这段代码虽然简洁但已经覆盖了光储调度最核心的建模要素。实际项目中你还需要加上数据清洗、异常值处理、单位校验、结果校验等环节。特别注意单位一致性如果P的单位是kWE的电池容量单位是kWh时间单位是小时那么SOC递推公式中功率乘以时间得到的是kWh不需要额外的换算系数但如果时间步长是分钟就需要除以60。4.3 结果如何解读调度计划与费用对比运行完代码求解器会给出每个时段的充放电功率、购售电功率和SOC曲线。解读结果时要重点看三件事一是SOC曲线是否在允许范围内平滑变化。正常情况下SOC应该呈现“谷充峰放”的锯齿形曲线不会出现频繁的高低跳变。二是充放电互斥是否成立。看看有没有同一个时段P_ch和P_dis同时大于0的情况如果有说明约束写错了或者求解器数值有问题。三是购售电互斥是否成立。检查P_buy和P_sell是否同时出现正值。我曾经一次部署时发现CBC求解后的P_sell和P_buy在某个时段同时为正值排查半天发现是u_grid约束中P_trans_max写成0了导致二进制变量被强制取值1模型退化成购电模式。这类问题往往不是算法问题而是参数初始化错误写代码时一定要做参数合理性检查。5. 三种典型天气下的调度行为实测模型跑通之后我习惯用三组典型场景去验证调度行为的合理性晴朗夏日、多云天气、阴雨时段。下面结合实测数据分析每个场景下整数规划给出的调度策略有什么特征。5.1 晴朗夏日光伏削峰与晚高峰放电的配合晴朗夏日的特点是光伏出力大且稳定中午时段光伏出力远超负荷需求。以500kW光伏、300kW平均负荷的工商业场景为例中午光伏峰值可达430kW而负荷可能只有200kW富余电力开始向储能充电。整数规划给出的策略通常是早上7点前后电价尚在平时段光伏出力爬升储能开始小功率充电上午10点到下午2点光伏大发储能以接近额定功率充电尽量把午间富余电力存下来傍晚17点到21点高峰电价时段储能开始放电替代高价电网购电夜间低谷时段如果末端SOC约束允许储能保持待机或少量充电为次日早晨做准备。这一策略的本质是光伏消纳和峰谷套利的叠加。光伏大发时段充电相当于把本来可能的“低价上网电”变成“高峰自用电”避免了低卖高买的价格倒挂。实测中这个场景下优化调度相比固定“午充晚放”策略日运行费用能降低10%~15%主要收益来自准确预测了晚高峰的负荷大小从而提前预留了足够的SOC而不是机械地“充满再放”。5.2 多云天气预测偏差下的“保守策略”特征多云天气是光储调度最麻烦的场景。光伏出力波动剧烈预测曲线和实际曲线可能相差30%以上。此时整数规划会表现出明显的“保守”特征储能不会在光伏大发的第一个时段就满功率充电而是预留一部分SOC空间以应对后续可能的光伏骤降。举个例子一个多云日的中午预测显示12点到14点光伏平均出力300kW但实际13点云层遮挡后出力掉到120kW。优化模型在12点时段给出的充电功率可能只有150kW而不是满功率的500kW。这种“不那么贪婪”的策略恰恰是在权衡了充电收益和失负荷风险之后的最优选择。这里要特别提醒单次静态优化一次性求解全天24小时对预测误差非常敏感。如果预测严重偏差模型给出的计划可能在执行到一半时不再可行。实际项目里更稳健的做法是采用滚动时域调度RHC每隔15分钟或1小时用最新的预测数据重新求解未来4~6小时的调度计划。MILP在这个场景下的优势是求解速度快重新求解一次只需要几十秒完全满足滚动周期的需求。5.3 阴雨时段纯峰谷套利的经济账阴雨天光伏出力很低基本可以忽略。此时调度问题退化成纯粹的峰谷套利低谷电价时段充电高峰电价时段放电。整数规划给出的策略高度依赖于峰谷价差和电池折旧成本。假设谷电0.4元/kWh峰电1.1元/kWh储能综合效率是0.95×0.95≈0.9。一次完整充放循环充1kWh、放0.9kWh的毛利是0.9×1.1 - 1×0.4 0.59元/kWh。如果我把电池折旧成本设定为0.02元/kWh那么套利利润是正的优化器会尽量在低谷充满、高峰放完。但如果峰谷价差缩小到0.3元/kWh以下加上折旧成本后套利空间为负优化器会把电池SOC维持在较高水平但不进行频繁充放甚至干脆待机。这个结论很有工程意义不是所有光储系统都适合每天做满充满放的循环。峰谷价差不足时储能更合理的定位是“备用容量”而非“套利工具”。目标函数中c_degrad这个系数的设定直接影响调度策略是激进还是保守需要结合电池的实际循环寿命成本来标定。5.4 三场景经济性对比为了更直观地说明优化调度的价值下表列出了三种天气下“固定策略”和“整数规划优化策略”的日运行费用对比单位元场景数据来自一个典型工商业项目天气类型固定策略日费用优化调度日费用降费比例主要收益来源晴朗夏日28502450约14%光伏消纳峰谷套利多云天气31302820约10%预测感知决策留有余量阴雨时段35603320约7%纯峰谷套利减少无效循环需要注意这个对比的前提是电量电价模型足够准确且实际执行时能基本跟踪调度指令。如果现场通信延迟大、PCS响应慢优化收益会被显著侵蚀这就需要在下文的部署经验部分做针对性处理。6. 现场部署踩坑与进阶方向6.1 求解器选型和求解时间控制选型上开源CBC适合中小模型但有几个问题一是数值稳定性偶尔会出问题二是分支定界策略比较“愣”遇到大规模整数变量时求解时间可能失控。我遇到过24时段、只有48个二进制变量的小模型CBC用了十几分钟还没收敛换Gurobi后两秒出解。所以交付商业项目时我倾向于直接用商用求解器前期验证才用CBC。如果预算有限必须留在开源方案可以考虑用CBC求解时设置时间限制和相对MIP Gap限制。比如设定gap1%或时间上限120秒这样即使没有收敛也能拿到一个质量足够好的可行解对于调度场景完全够用。注意不要以默认参数直接跑大模型否则一旦陷入分支定界深谷调度任务根本无法按时执行。6.2 SOC数值漂移与约束松弛前面提到过SOC递推公式中充电和放电效率如果处理不当模型内部计算的SOC和BMS实际反馈的SOC会产生偏差。这个偏差虽然每个时段只有零点几个百分点但累计运行几天后可能达到5%~10%导致调度计划的置信度下降。解决思路有两个。一是定期校准每6小时或每天凌晨用BMS上报的真实SOC覆盖模型中的SOC初始值重新滚动求解。二是把SOC当作目标函数中的软约束加上惩罚项让优化器尽量把SOC保持在目标范围内而不是生硬地规定边界。这样即使BMS反馈和模型计算存在偏差也不会出现调度指令撞上BMS保护边界的尴尬。6.3 从单日优化到滚动调度的进阶路线前面已经多次提到滚动时域调度这里给出一个具体的工程建议将调度周期从24小时缩短为“未来6小时”每15分钟滚动一次每次滚动时读取最新的光伏预测、负荷预测、BMS的SOC和电价信号用MILP求解出未来6小时的调度计划但只执行下一个15分钟窗口的指令下一周期重复上述过程实现模型预测控制MPC式的闭环调度。这种模式对预测误差有天然的抗干扰能力也能及时响应电网调度指令。实测下来滚动调度相比单日静态调度在多云天气下的运行费用还能再降3%~5%同时基本杜绝了“计划不可行”的问题。唯一的代价是计算频率提高但对MILP求解器来说6小时、24个时段的模型规模完全不是负担。6.4 从单储能到多资源的模型扩展如果项目后期加入柴油发电机、可调负荷、多台储能MILP模型可以非常自然地扩展。每加一台机组就增加一组0-1变量和启停约束每加一个可调负荷就增加一个连续变量和功率可调区间。模型规模线性增长求解器依然能够处理。我现在的标准做法是把调度引擎封装成一个微服务输入是预测数据和系统参数输出是功率调度计划接口用标准JSON格式。这样不管是接入现有EMS还是后期扩展为配合电网需求响应都能快速迭代不用推倒重来。最后再分享一个经验整数规划模型不是写完就完事它需要持续根据现场运行数据进行标定和修正。光伏预测模型在换季之后精度会下降电池的实际容量会随着老化衰减电价政策也可能调整。我一般建议每季度回头重新校准一次模型参数特别是电池效率、可用容量和折旧成本这三个系数。踩过几次坑之后你会明白调度算法真正上线的难点不在“求解”而在“让模型始终贴近物理世界”。这需要你把每个约束、每个参数背后对应的现场设备逻辑都吃透而这恰恰是光储优化调度项目里最值钱的功夫。