
1. 为什么综合能源调度要拆成“日前日内”两步走先说说项目最核心的一个判断综合能源系统的调度不能靠单次优化一锤子定音。原因很实在。综合能源系统里既有电、热、气等多种异质能源耦合又有风电、光伏这类强间歇性电源还有负荷侧的随机波动。如果把所有决策都放在一个时间尺度里做要么因为预测误差太大导致方案根本不具备可执行性要么为了追逐“全局最优”把模型规模撑到求解器直接崩溃。而“日前日内两阶段调度”本质上是一种工程妥协用日前阶段处理那些需要提前安排、响应慢、调整成本高的决策用日内阶段处理那些临近执行、预测精度高、需要快速修正的决策。两阶段各干各擅长的事既保证经济性又保证可执行性。用生活里的事类比一下——旅行规划。日前调度相当于你出发前一晚定好大致行程几点起床、走哪条高速、在哪站加油。这些决策需要提前安排但不用太精细因为第二天路况未必完全按预期走。日内调度则是你真正开车上路之后每半小时看一眼实时路况该绕行就绕行、该改服务区就改服务区。如果出发前就把每条车道的速度都定死这趟车压根开不了。所以我拿到“综合能源系统日前日内两阶段调度分析”这个题目以后第一反应就是这个标题本身就是这个领域最标准的解题框架。用Matlab和Yalmip实现又是一个成熟工程组合。Yalmip负责把优化问题“翻译”成求解器能懂的格式Matlab负责数据预处理、模型搭建、结果可视化。整个项目做完我最大的感受是难点根本不在怎么调用Yalmip而在于怎样把一个物理上合理的两阶段调度问题转化成数学上可解、代码上不崩的优化模型。2. 整体建模思路目标函数、约束和不确定性处理2.1 两阶段决策变量怎么划分日前阶段的决策变量主要特征是“慢变量”和“离散变量”。比如燃气轮机的启停状态、储能设备是否处于充电/放电模式、与上级电网的购售电协议电量、设备检修安排这些都要提前一天定下来。它们要么响应速度慢要么涉及机组组合这种0/1逻辑临时改代价极高所以必须放在日前。日内阶段的决策变量是“快变量”在进入实际运行时段后根据最新预测数据滚动修正。比如各机组在分钟级或小时级的实际出力调整、储能实际充放电功率、切负荷或弃风的应急调整量。因为日内阶段的信息更准所以这些变量可以做更精细的调整用来弥补日前预测偏差。这两类决策变量之间通过“耦合约束”联系在一起。日内阶段的出力调整量是在日前基础方案上的增量或减量不能脱离日前基线凭空产生。这也是两阶段模型和单阶段模型最本质的区别单阶段模型只有一个时间断面或者虽然有时间维度但所有决策是同时优化出来的两阶段模型则天然带有一个“决策顺序”先定基线再定调整量这种结构用数学语言写出来就是标准的随机优化或鲁棒优化形式。2.2 目标函数经济性为主惩罚项是灵魂目标函数一般写成最小化综合运行成本核心包括三块第一是日前阶段的设备运行成本。燃气轮机的燃料成本、购电成本、设备启停成本这些在日前就要算清楚。燃气轮机燃料成本通常用二次函数描述但二次函数在混合整数线性规划里没法直接解所以要做分段线性化处理。这是我在项目里第一个踩坑点后面会细说。第二是日内阶段的调整成本。日内阶段对日前计划做出的每项调整都是有代价的比如机组爬坡越限的惩罚、储能在非计划时段动作的折旧损耗、紧急购电的溢价成本。这个惩罚项的设置很有讲究——如果设得太低日内阶段会过度依赖调整失去“日前定基线”的意义如果设得太高日内阶段就完全不敢动预测偏差来了只能硬扛。我实测下来比较合理的做法是把调整惩罚系数设为正常运行成本的1.5到3倍具体倍数要根据历史预测误差的分布特性来标定。第三是惩罚性成本主要针对负荷失电和弃风弃光。这类变量一般是松弛变量加上它们的目的不是为了真的去切负荷或弃风而是为了保证任何情况下模型都有可行解。松弛变量的惩罚系数要设置成远高于其他所有成本项建议至少高出两个数量级确保模型只有在极端情况下才会动它们。2.3 约束条件电、热、气三类都要覆盖综合能源系统的“综合”二字主要体现在约束条件的多能互补特性上不只是电功率平衡那么简单。电力平衡约束是最基础的上级电网购电加各机组出力加储能放电等于电负荷加储能充电加电转气设备消耗。这个约束在每个调度时段都要满足。这里要注意电转气P2G设备消耗的是电功率产出的是天然气或氢气它是电-气耦合的桥梁。热力平衡约束要考虑热网管道的传输延迟。热网跟电网不太一样热功率在管道里以每秒几米的速度传输不像电那样接近光速。所以热负荷跟热源之间不是即时平衡而是要做一个“储热罐”或者“一阶惯性环节”的延迟处理。如果忽略这个延迟日内调度很容易出现热源出力已经调了、但热负荷端温度还没变化的错位现象。天然气平衡约束在综合能源系统里也越来越多见了。燃气轮机和燃气锅炉消耗天然气P2G设备制取天然气上级气网也可以供气。我在项目里做了一个简化处理把天然气网络视为节点平衡模型暂不考虑管道气流方程的非线性动态。这不是偷懒而是因为日内调度的滚动周期通常只有4到6小时气管网动态的时间常数远大于调度周期做准稳态处理完全够用。2.4 风光出力不确定性怎么建模两阶段调度里最核心、也最影响结果质量的部分就是对风电、光伏预测误差的处理。目前主流做法有三种。第一种是场景法用蒙特卡洛抽样生成大量风光出力场景然后把原问题改写成带场景期望值的目标函数做成随机规划。优点是比较贴近真实概率分布缺点是场景一多模型规模迅速膨胀求解时间难以接受一般要做场景削减比如用快速前向选择法Fast Forward Selection把1000个场景砍到10到20个。第二种是区间法只考虑风光出力在某区间内波动目标是在最坏情况下保证系统不越限属于鲁棒优化。优点是模型规模小、求解快缺点是过于保守经济性明显变差。实际项目的做法通常是将鲁棒边界设置为预测值的80%到120%区间而不是遍历全部可能范围。第三种是我个人更推荐的混合法项目里也最终采用了日前阶段用区间鲁棒做“安全兜底”日内阶段用确定性场景做“经济修正”。说白了就是日前把安全边界卡死日内再根据实际滚动预测结果做经济性微调。这样做的好处是既不会因为模型过于保守导致成本飙升也不会因为缺乏安全余量导致操作系统频繁告警。3. 工具选型为什么是Matlab加Yalmip有些朋友可能会问综合能源调度用Python加Pyomo不也行吗当然行。但用Matlab加Yalmip有它独特的工程优势。Yalmip是瑞典学者Johan Löfberg开发的一个Matlab建模工具箱核心价值在于让优化问题的建模和求解完全解耦。你在Matlab里用接近数学公式的方式定义变量、目标函数和约束条件最后调用solvesdp或optimize命令Yalmip会自动判断问题类型——是线性规划、混合整数线性规划、二次规划还是二阶锥规划——然后匹配你安装的求解器去解。这意味着你不需要为每一种求解器单独学一套建模语法换求解器的时候业务代码根本不用动只改一个参数就行。对做研究的同学来说这省掉的远不止是学习成本更多是来回调试的时间成本。Matlab本身的价值也很明确它是矩陈实验室Matrix Laboratory定位的环境对向量化运算和矩阵操作的表达天然适合电力系统这种基于节点和支路的网络化建模。Yalmip用Matlab表达约束的方式几乎就是照着公式抄的可读性极强。比如要定义一个变量P_gt表示燃气轮机出力写sdpvar P_gt就完事了要定义一个0/1决策变量写binvar x。这种语法的爽感用过的人都懂。求解器方面我实测下来的配置是连续问题用fminconMatlab自带或quadprog混合整数问题用gurobi或cplex。Gurobi在学生授权下免费性能在工业界和学术界都是第一梯队特别适合日内滚动这种需要反复求解的场景。如果你的模型里二阶锥约束比较多——比如交流潮流约束或储能变流器容量约束——Gurobi 9.0之后版本对二阶锥的支持也很完善。Cplex在MILP问题上跟Gurobi互有胜负但相比之下Gurobi的许可证申请更简单所以我个人更推荐Gurobi作为默认求解器。有一个细节需要特别提一下Yalmip和Matlab的版本兼容性。新版本Matlab在2020年之后改了图形渲染底层早期某些Yalmip版本在画图时会出现坐标轴异常的问题。如果遇到优先升级Yalmip到2021年之后的版本基本上都能解决。Matlab 2026b之前我测试过Yalmip最新版没有遇到不兼容如果是老项目迁移我建议先在原版本环境测通再考虑升级。4. 项目实操从数据准备到结果分析4.1 数据准备预测数据是调度的“地基”我常说一句话优化模型的力度再大也填不了预测数据不准的坑。数据准备这个环节占整个项目工作量的比重通常不会低于40%。首先准备负荷数据。电负荷、热负荷、气负荷每个时段一个值最好有至少连续一周的历史数据用于做负荷预测。如果项目中有真实历史数据还好没有的话就用典型日负荷曲线加随机扰动生成。负荷曲线的形状直接影响机组组合结果尤其是早晚高峰时段做数据的时候格外注意峰谷差的合理性。然后是新能源出力数据。风电和光伏的预测出力曲线是日内阶段的核心输入。除了给预测值还需要给预测误差的标准差或置信区间。这个误差数据非常关键因为它直接决定了日前鲁棒区间宽度的设置。我当时的处理方式是统计历史预测误差的分布发现风电预测误差大致服从零均值、标准差约为装机容量10%到20%的正态分布光伏因为夜间出力为零误差分布有明显的有界性用Beta分布拟合更合适。设备参数方面至少需要燃机的最小技术出力、最大出力、爬坡速率、燃料成本系数储能的容量、充放电功率上限、充放电效率、自放电率P2G设备的转换效率和容量上限燃气锅炉的效率储热罐的容量和热损失率。设备参数有问题优化结果再好看也是空中楼阁后面一旦做实时仿真必然露馅。最后是外部市场价格参数。电网购电价要区分峰谷平时段天然气的单位热值价格也要明确。这些外部价格参数是经济性优化的直接驱动者价格结构设置不合理会导致储能或P2G设备出现“为了赚差价而无效运行”的伪经济行为。4.2 核心代码框架Yalmip建模的标准三段式代码结构我习惯分成初始化、建模、求解分析三个模块每块都有明确的职责边界。初始化模块负责数据导入、参数设置、定义决策变量。决策变量定义这步要特别小心Yalmip里变量定义本身不占内存多少真正占内存的是约束矩阵展开之后的稀疏结构。所以能用向量化定义的就不要用循环逐个定义能用sdpvar声明矩阵的地方就别用cell数组堆几百个变量。建模模块是重点。先写目标函数再写约束条件。在Yalmip里约束用[]拼接例如Constraints []; Constraints [Constraints, P_balance P_gt P_buy - P_sto_ch - P_load]; Constraints [Constraints, 0 P_gt P_gt_max];把所有约束拼好之后调用optimize(Constraints, Objective, sdpsettings(...))求解。solve参数设置里最核心的是solver选项直接指定用哪个求解器然后是verbose选项建议设为1方便看到求解迭代过程savesolveroutput选项设为1方便诊断问题时提取求解器的内部信息。求解完之后把最优值取出来分析。Yalmip取变量值的方式是value(P_gt)相当直观。结果分析阶段最常见的问题是直接看数值不做合理性校验。我的习惯是至少做三类校验功率平衡校验把每时段的各类功率代回平衡方程误差超1e-6就要查、爬坡校验相邻时段出力差是否在爬坡速率范围内、储能SOC连续性校验SOC变化量是否与充放电功率乘以效率一致。这三类校验过了结果基本靠谱。4.3 求解过程实录一个典型日循环以24小时日前调度加4小时日内滚动为例我描述一下典型执行流程。日前调度在每天00:00执行。加载次日预测数据后先做场景削减——把1000个蒙特卡洛场景通过快速前向选择法削减到15个代表性场景——然后建立随机规划模型。注意这里的目标函数是“期望成本最小”所以每个场景都有一个对应的约束副本而日前决策变量机组启停等是所有场景共享的。这就是“非预期性约束”的本质日前决策在做的时候你还不知道明天到底会是哪个场景发生所以这些决策不能依赖特定场景的信息。求解这个带场景的MILP问题15个场景、96个时段15分钟分辨率、约50个决策变量、2000多条约束的规模Gurobi在不到5分钟之内能解到最优间隙小于1%。如果解到1小时还没收敛优先检查是不是有约束写错了而不是怀疑求解器性能。我在项目里就遇到过因为燃气轮机启停变量定义重复导致模型不可行求解器卡在预处理阶段不出来的问题。日内阶段是滚动执行的。举个例子系统运行到上午10点就基于更新过的10点到14点的负荷预测和风光预测重新求解一次4小时的调度问题步长为15分钟。日内问题规模比日前小很多不涉及机组启停整数变量属于连续线性规划或二次规划Gurobi或自带求解器几秒就能解完。日内求解完拿到调整量后会有一个很重要的确认步骤检查日前基线加日内调整量之后的总出力是否超出设备硬限。如果超了在约束里加一个松弛量重新求解如果每次都不超说明日前的鲁棒区间设置得过于宽裕经济性有提升空间后面可以考虑收窄区间。4.4 结果分析和方案评价结果分析不能只看总成本一个数要拆开看购电成本、燃料成本、启停成本、储能折旧成本、惩罚成本各占多少。理想结果应该满足几个特征储能设备在峰时段放电、谷时段充电P2G设备在电价低或风电过剩时段运行燃气轮机在高峰时段启停次数少但出力平滑。如果这些特征没出现说明模型或数据里大概率有问题而不是系统“聪明地”找到了反常识的最优解。我做方案评价常用的指标有四个第一个是两阶段总成本相对理想基线的增量。理想基线是假设预测完全准确、可以在一个模型里直接求解得到的成本两阶段成本比这个基线高多少反映了“信息不确定性”带来的代价有多大。一般高出5%到15%都算正常。第二个是弃风弃光率。这是约束设置合理性的直接体现。如果弃风弃光率为零且没有任何惩罚成本多半说明模型对新能源消纳太宽松如果弃风率超过20%则可能说明储能容量配置不足或P2G转化能力太小。第三个是日前计划在日内执行时的偏差率。统计每个时段日前出力和日内调整后实际出力的差值的均值这个值越小说明日前预测准确度越高、日前计划“含金量”越高。第四个是计算时间。两阶段调度方案如果计算时间超过调度周期的三分之一在实际工程部署中就没有实用性了。对96时段日前加4小时日内的规模总求解时间控制在15分钟以内是合理目标。5. 常见问题速查与排查实录5.1 模型求解报“Infeasible problem”怎么办这类问题我一共遇到过三种原因。第一种是约束之间逻辑冲突。最典型的例子某个时段设定了储能必须同时充电和放电。单独看充电和放电约束都没问题但它们不可能同时满足导致模型无解。排查方法是用Yalmip的check(Constraints)命令逐一检查每条约束的残差残差为负的约束就是问题所在。第二种是设备启停变量和出力范围限制耦合过强。燃气轮机启停变量为0时出力必须为0但出力上限又设成了一个大于0的下限导致逻辑矛盾。解决办法是引入“启动状态的出力范围”约束标准写法是Constraints [Constraints, P_gt_min*z P_gt P_gt_max*z];其中z是0/1启停变量。这样z0时出力被强制为0z1时出力在上下限之间自由变。第三种是数据质量问题。负荷太大、新能源预测太小、购电上限太低三者一叠加系统无论如何都满足不了平衡约束。这种情况要检查数据或者适当放开购电上限或加入切负荷松弛变量。5.2 求解器“卡住”或者很慢这是Matlab求解综合能源模型的经典痛点。先说结论90%的“慢”不是因为问题太大而是因为建模方式导致求解器做了大量无效探索。第一个常见原因是没有给0/1变量提供好的初值。MILP求解器靠分支定界法搜索一个好的初始可行解能大幅加速过程。Yalmip里可以用assign给变量赋初值例如assign(z, ones(size(z)));这样求解器一开始就有一个“所有机组都开”的可行解作为起点分支定界树能少探索很多。第二个原因是约束中用了大量非线性函数。比如把二次成本函数直接写进约束没有做分段线性化。Yalmip虽然能自动把二次目标转换但约束里二次导致问题变成非凸求解器直接劝退。正确的做法是用implies或binvar辅助变量把二次曲线切成几段线性近似。第三个原因是Big-M法的M值设置不当。M值太大求解器的数值稳定性崩盘M值太小又把可行域错误地压缩了。我的经验是M值取该变量物理上限的10到100倍比较合适。比如燃气轮机出力上限是100MWM取1000但如果出力上限本来就只有1MWM取1000就太大了。好的习惯是分不同约束单独定义M值不要全模型用一个全局M。5.3 结果数值怪异的排查有一次我调度完储能在同一个调度时段内既充了大量电又放了大量电——租金浪费一点意义都没有。原因是目标函数里储能充放电的单位成本参数是0模型觉得充了再放不花钱就形成了“假循环”。解决方法是给充放电加互斥约束或者给充放电行为加一个很小的单位成本惩罚。还有一次出现“燃气轮机在低谷时段频繁启停”的现象。单看成本启停成本不高、低谷时段电价低确实不如关掉燃机划算。但频繁启停对设备寿命的损耗没体现在模型里。解决办法是在目标函数中增加启停次数的分段惩罚——允许启停但超过一定次数后惩罚成本陡增。这是模型和工程现实之间需要做平衡的经典示例。5.4 关于Yalmip调试的几个私密技巧调试数学优化模型跟调试普通程序是两种思路。普通程序可以加断点一行行看优化模型只能靠“外部诊断”。我这里分享三个比较管用的手法。第一个是做一个“极小测试用例”。把模型缩到只有3个时段、2台机组、1个储能手动演算一遍最优解再用代码求解对比结果。如果极小规模都对不上说明建模有逻辑错误如果对得上再逐步扩大规模找哪个环节引入的问题。第二个是“热启动检验”。先解一个确定性场景只保留一个场景、不看不确定性再解随机场景模型对比两者的目标函数值和决策变量。随机模型的目标函数值一定不会优于确定性模型因为约束更紧如果出现随机模型成本反而更低基本就是编程逻辑写反了。第三个是“约束激活检查”。最优解解出来之后用dual命令求出所有约束的对偶变量对偶变量显著非零的约束就是“紧约束”active constraints。看紧约束是否符合工程直觉优化结果如果把储能的SOC上限卡在100%说明储能容量是瓶颈后续可以考虑扩容量或调节价格结构这个信息对项目决策很有价值。5.5 常见问题速查表现象可能原因排查思路模型报不可行约束逻辑冲突用check定位残差为负的约束模型不可行但约束都合理数据太紧无可行域放开购电上限或引入切负荷松弛求解时间过长MILP没有好初值手动给0/1变量赋初值解的结果反常识缺失设备行为惩罚项增加启停次数惩罚、充放电互斥约束随机模型成本低于确定性模型逻辑写反或场景处理错误对比两类模型的目标函数值储能SOC不守恒效率参数设置不一致检查充放电效率和初始SOC设置热功率平衡对不上忽略热网延迟加入储热罐或热惯性环节6. 两阶段调度的工程落地与进一步扩展思路做这个项目的最终目的不能停留在“解出一个优化结果”。真正的价值在于通过调度方案把综合能源系统的运行潜力释放出来。我做完这个项目之后有几个感触比较深的扩展方向。第一个方向是日内滚动的闭环化。目前常见的做法还是“滚动优化、开环执行”也就是算出来一组指令操作员按指令去执行但执行结果的反馈信息没有自动回到模型里。闭环化的做法是在每次滚动优化前用最新的实测数据储能的真实SOC、热网的真实温度去刷新模型状态而不是继续沿用上一次预测的状态初值。这样做最大的好处是误差不像滚雪球一样积累调度方案越跑越贴合系统真实状态。第二个方向是需求侧响应资源的精细化建模。两阶段调度的弹性其实在很大程度上压在了需求侧如果能让一部分柔性负荷主动调整用能时段日前基线的不确定性会大幅降低日内阶段需要付出的调整代价也会随之变小。但需求侧响应不是简单的“负的发电机”它涉及用户用能舒适度、参与意愿、激励价格等多个维度建模复杂度明显更高。这部分做得好会把整个调度方案的经济性带上一个台阶。第三个方向是求解效率的进一步优化。两阶段模型最怕的是规模膨胀。我前面提到场景削减是压缩随机规划规模的有效手段但如果场景维度高、削减少还是容易爆。另一种思路是利用“线性调度的政策结构”——不是真的把每个场景的约束都列进去而是假设日内决策是日前决策的仿射函数比如“实际出力日前出力K×预测误差”把随机问题转成带仿射决策规则的确定性问题。这种做法牺牲了一点最优性换来的是计算规模至少降一个数量级在实时性要求高的场合很值得考虑。第四个方向是跟数字孪生或实时仿真结合。调度结果不能只停留在Excel表格或曲线图层面最好是能接一个仿真环境让调度指令在仿真系统里跑一遍看看系统物理过程是否真的按预期走。Matlab本身的Simulink平台完全可以承担这件事搭建一个小规模的电热联合仿真模型把优化结果作为外生输入考察温度、频率、SOC等物理量的变化轨迹这是验证调度方案可用性的高效路径。7. 实践体会这个项目做下来有三句话我想分享给后面做类似工作的人。第一句两阶段调度模型的难点不在“两阶段”而在两阶段之间的耦合关系处理。很多人的模型日内阶段只是把日前模型换了个时段范围重新解一遍并没有真正的“调整量”概念这样搞出来严格说只能叫“两次单阶段调度”谈不上两阶段优化。两阶段的灵魂在于日前决策对日内决策有“锁定作用”日内决策只能围绕日前基线做扰动这个耦合关系没建好整个模型的含义就变了。第二句复杂系统建模的工程标准不是“越精确越好”而是“够用、能解、可解释”。我见过不少同行一上来就要建交流潮流、天然气动态管流、热网暂态方程结果是模型写了一千行约束求解器运行三天三夜还收敛不了。合理的降维和简化例如用直流潮流近似交流潮流、用热惯性近似热网动态在工程调度这样的业务场景是完全站得住的。第三句调试优化模型最耗时间的不是写代码而是“跟数据较劲”。数据异常导致的报错信息往往不具备逻辑性Yalmip只回你一句“infeasible”你根本不知道是哪个数据出了问题。所以我现在做项目第一步永远是花半天的时间做数据可视化把所有输入数据的曲线都画出来。数据曲线一眼扫过去量级错、单位错、毛刺过大的问题全都现原形。省下的调试时间远超这半天投入。如果让我给刚接触这个方向的人一条最具体的建议那就是第一版模型只做两件事——单目标函数、线性约束和确定性场景。先把最基础的电功率平衡加储能模型跑通再加热网再加气网再加随机场景一步一验证。这样虽然慢但每一步出错都好排查。等到整套流程都稳定了再回头用场景法或区间法引入不确定性你会发现之前积累的调试技巧全部派得上用场。