
这个课题我前前后后做过好几轮从最初照着一篇参考论文的思路复现到后来自己改模型、换求解器、调算例中间绕了不少弯。现在回头看考虑需求响应的电-热综合能源系统两阶段日前日内多时间尺度优化调度策略研究这类题目在Matlab里实现时真正决定成败的不是某个公式背得多熟而是电热耦合怎么建模、需求响应怎么进约束、两阶段怎么衔接这三个问题有没有想透。这篇博文就围绕这条主线把我自己的建模思路、代码组织方式、以及踩坑之后的修正经验完整梳理一遍给正在做相关方向的朋友一个可直接参考的骨架。1. 从单一电调度到电热耦合这个课题解决的实际问题1.1 为什么必须把热网拉进优化模型很多做电力调度出身的人一开始对热网的态度是能简化就简化。我最早也这么干过把热负荷当成一个可以随时满足的刚性需求CHP机组只按电出力去优化结果算出来在冬季高负荷时段全线越限原因很直白抽凝式CHP的供电功率和供热功率强耦合电出力调上去了热出力跟着变而热网那边的节点温度约束根本不允许你这么随便动。电-热综合能源系统里电和热不是两条平行线而是通过设备耦合在一起的。典型设备包括热电联产机组CHP、电锅炉、热泵、储热罐它们让电能和热能可以互相转化、跨时段平移。比如夜间风电大发、电价低电锅炉可以多耗电产热把多余电能转成热能存在储热罐里白天热负荷上升时再放出来。这个概念人人会说但落到优化调度模型里要考虑的其实是三个层次的问题第一层是设备层CHP的可行域长什么样、电锅炉的效率如何变化、储热罐的蓄放热损耗系数是多少这些直接决定决策变量的可行空间。第二层是网络层电力潮流要满足节点电压/线路容量约束热网要满足节点功率平衡和供回水温度的关系。第三层是时间层热网有热惯性管道里的热水流动有延时这给调度提供了额外的灵活性但也让模型变得难解。1.2 需求响应在这类调度模型里扮演什么角色需求响应Demand Response, DR说白了就是把原本刚性的负荷变成有弹性的资源。在电热综合能源系统里它比纯电力系统里更复杂一点因为负荷不仅有电负荷还有热负荷而热负荷对温度变化存在一定容忍度。这就意味着需求响应既可以通过电价信号让用户调整用电时段也可以通过温度设定值的微调让采暖负荷参与削峰。我在模型里通常把DR分成两类来建模。一类是价格型需求响应用户根据分时电价或实时电价自发调整用电行为用弹性矩阵来描述负荷对电价的敏感程度。另一类是激励型需求响应调度机构事先与用户签订可中断、可削减或可转移负荷协议需要时直接调用但需要支付补偿费用。研究这个课题时如果把DR做得太简单比如只用一个总负荷削减量参数带过那结论基本没有参考价值。真正有说服力的做法是把DR的成本项、响应潜力约束、甚至用户舒适度约束都放进优化问题让模型自己决定什么时候该调用多少DR资源。1.3 多时间尺度调度的本质原因单阶段调度的思路是拿到预测数据一次性算出全天设备出力计划。问题在于预测不可能准。风电出力预测误差、负荷预测误差尤其还有热负荷预测误差叠加在一起可能导致日前计划在日内根本无法执行。多时间尺度的思路则是分层递进日前阶段以小时级分辨率面向第二天的整体运行做经济性最优安排决策机组启停、储热罐蓄放热策略、DR合同量等中长期量日内阶段以分钟级分辨率滚动修正根据最新预测和实时运行状态去调整设备出力让计划更贴合实际。这样做最直观的好处是把不确定性分解到不同尺度去消化。机组启停这类时间常数为小时级的决策放在日前出力微调这类分钟级的决策放在日内每一层只处理自己时间尺度内的问题模型复杂度可控调度结果也更能落地。2. 系统建模CHP、热网与需求响应这三块最难处理的部分2.1 热电联产机组的可行域与线性化处理CHP机组是电热耦合的心脏。建模时最常见的是抽凝式机组它的电出力和热出力可以在一个凸多边形可行域内自由组合。这个可行域的顶点由机组的设计参数决定在Matlab里我会把顶点坐标直接写成矩阵然后用一组线性不等式去逼近可行域。为了说明白假设CHP的电出力为P_cht热出力为H_cht可行域可以用顶点坐标组合表示可行域内任意一点是顶点的凸组合同时满足边界约束。这样处理后原问题就变成了一个混合整数线性规划MILP问题可以直接丢给求解器。实际建模里容易漏掉的是机组的爬坡约束和最小开停机时间约束。爬坡约束在日前阶段用小时级分辨率时要考虑在日内15分钟分辨率时更得考虑否则算出来的出力曲线在分钟尺度上根本执行不了。最小开停机时间约束会让问题引入额外的整数变量也是导致求解时间变长的元凶之一但这块不能省。2.2 热网模型从准稳态到动态延时热网的建模方式直接决定模型是线性还是非线性。最简单的做法是准稳态模型即认为热功率瞬时平衡只考虑节点流入热量等于流出热量不考虑管道传输延时和热损失。这种处理在规划类研究里够用但做调度时会丢掉热惯性带来的灵活性。我在项目里用的是较常被接受的节点法简化模型热网分为供水网络和回水网络每个负荷节点有温度约束比如供水温度95度、回水温度50度通过调节流量或温度来满足热负荷需求。这里的关键点是热功率与流量和温差相乘而流量又是耦合变量所以原模型是带乘积项的非线性问题。为了在保证求解效率的同时保留热网基本约束我采用了两步走第一步固定供水温度采用质调节方式只把流量和回水温度作为变量这样热功率可以线性表达第二步管道延时按平均流速折算成滞后时间对日前调度的影响通过修正系数近似只在日内滚动阶段才考虑更精细的温度动态。这个取舍牺牲了一部分精度但换来了求解的稳定性和可复现性对工程研究来说非常值。2.3 需求响应建模弹性矩阵与可平移负荷的整数表达价格型需求响应我通常用自弹性和交叉弹性矩阵来建模。自弹性表示某时段电价变化对本时段用电量的影响交叉弹性则表示对其他时段用电量的转移效应。表达式写出来大概是ΔL(i)/L0(i) E(i,i) * Δρ(i)/ρ0(i) Σ(E(i,j) * Δρ(j)/ρ0(j))其中E矩阵是弹性系数矩阵。这个模型的好处是能直接嵌入MILP但要注意负荷转移必须满足总量守恒约束即把削减的量在其他时段补回来否则就是单纯的负荷削减不是真正的需求响应。激励型DR里的可平移负荷例如工业流水线连续生产需要引入整数变量来建模如果负荷启动就被视为一个整体在连续若干个时段内保持运行。这类约束用大M法线性化在YALMIP里可以直接写成implied约束。可中断负荷则相对简单只需要一个0-1变量控制中断状态外加一个最大中断次数约束。热负荷需求响应需要额外小心因为热用户的舒适度是以温度带形式体现的。举个例子室内温度设定22度允许上下浮动2度这2度范围其实就对应着热功率的可调范围。我在建模时会把热负荷弹性量化为一个与室外温度相关的可调比例通常取5%-15%这样既给调度留了余地又不至于让用户投诉。2.4 风电与光伏的出力处理新能源方面我会用场景法处理不确定性。典型做法是先用历史数据生成预测误差分布再通过拉丁超立方采样生成场景集最后用同步回代消除法把场景削减到10个左右。每个场景对应一组可能的风电出力曲线目标函数里对场景求期望。这种做法比单纯用期望值预测更稳健也更接近顶刊论文惯用的方法。但如果不想让模型复杂度起飞先期验证代码逻辑时完全可以退回到确定性模型只用预测曲线。等基本框架跑通再把场景扩展进去。我自己的经验是确定性版本调试BUG效率最高场景版本适合做最终对比分析。3. 两阶段日前日内调度分工逻辑与滚动衔接机制3.1 日前阶段决定昨日到今日最关键的大动作日前调度在每一天的零点前运行时间窗口是24小时分辨率一小时。它的任务是确定那些不能临时变的决策机组的启停状态、储热罐的蓄放热策略、与DR用户约定的调用量、向外部电网购电的日合同额度。日前目标函数我设置为系统总运行成本最小包括CHP燃料成本、购电成本、电网售电收益、需求响应补偿费用、弃风弃光惩罚。按我常用的写法min Σ_t [ Σ_i (a_i * P_chp(i,t)^2 b_i * P_chp(i,t) c_i) ρ_buy(t)*P_buy(t) - ρ_sell(t)*P_sell(t) Σ_dr c_dr(t)ΔP_dr(t) λ_curtailP_curtail(t) ]CHP成本函数里的二次项可以用分段线性化转为MILP这一块在YALMIP里做起来不麻烦或者直接用二次规划求解器也可以。只要整数变量不多Gurobi处理得都很快。日前阶段的约束集合包括节点电功率平衡、节点热功率平衡、CHP可行域约束、电锅炉/储热罐出力范围、爬坡约束、储热容量SOC递推方程、需求响应调用量上下限、热网节点温度范围、线路潮流约束。其中热网的潮流约束我会用简化的管道流量平衡代替完整的节点能量方程以避免非线性。3.2 日内阶段滚动优化在修正什么日内阶段采用滚动时域控制Rolling Horizon的思路。每15分钟触发一次优化预测窗口长度为未来4小时也就是16个时段的滚动优化。它接受的输入包括最新的超短期风电/负荷预测、设备当前实际运行状态储热罐SOC、机组当前出力、热网蓄热状态、日前计划给出的参考值。日内优化变量不再包含机组启停启停在日前已经定好了。日内只调整连续变量各设备出力、储热罐蓄放热功率、DR实时调用量、外部购售电功率。这样做有两个直接影响一是模型不含或少含整数变量求解速度大幅提升二是日内决策与日前计划的偏差被限制在给定范围内避免日内结果推翻日前的整体安排从而兼顾了经济性和可执行性。日内目标函数在日前经济目标的基础上增加了一项对日前计划的偏离惩罚。这个惩罚不是可有可无的。如果没有它日内每个滚动窗口都只盯着自己4小时的最优可能会把储热罐的SOC跑偏导致后续时段无热可用加上偏离惩罚之后日内优化会在纠正预测误差和保持计划稳定之间找一个平衡点。3.3 两阶段数据流的衔接设计在不同的代码版本中我的衔接逻辑经历了三次迭代这里分享一下最终版的思路。日前求解结束后保存以下关键变量作为日内阶段的参考各机组的启停状态、各时段各设备的计划出力值、储热罐的计划SOC轨迹、计划购电量和DR调用量。进入日内阶段后每个滚动优化周期读取当前的实际状态量例如当前SOC不是计划值而是实测值再由预测模块生成未来4小时的负荷和风电预测曲线代入优化模型求解得到未来16个时段15分钟一个的控制序列但只执行第一个时段。下一个周期到来后再重复预测-优化-执行的循环。这个设计里最容易出错的地方是时间坐标系。日前是1小时分辨率24个点日内是15分钟分辨率96个点。日期衔接时日内第0时刻对应日前哪个时刻储热罐的SOC从日前计划的哪个值开始这些细节如果不对齐跑出来的曲线会出现明显的跳跃断点。我处理的办法是所有阶段内部统一使用时刻索引在数据输入层单独写一个时间轴转换函数负责把日前计划转换成日内96点分辨率下的基准值再传给日内约束。3.4 为什么两阶段在数学上比单阶段更优从数学上讲做一个24小时、15分钟分辨率、包含完整热网模型和DR整数变量的全时空大规模MILP也不是不能求解但速度会让人崩溃而且在实际调度场景中一天之内的预测误差会随时间的临近不断更新一次性全时段求出的全局最优反而会因为使用了中期预测数据而偏离实际。两阶段方法用日内滚动反馈修正来对冲预测误差本质上是把一个含不确定性的随机优化问题转化为一个确定性日前部分和一个反馈型日内部分的组合兼顾了理论最优性和工程实用性。4. Matlab代码架构设计从变量定义到求解器调用4.1 为什么选YALMIP搭配Gurobi/CPLEX做这个课题我不太推荐纯手写求解器接口也不推荐只用MATLAB内置的linprog/intlinprog去扛。内置求解器用于教学验证没问题但一旦模型规模上到几百个变量和约束内置求解器在数值稳定性和求解速度上明显吃力。我个人习惯是YALMIP做建模层求解器用Gurobi或CPLEX。YALMIP的好处是集合了优化建模语法代码与论文公式之间的对应关系比较直观Gurobi/CPLEX负责真正求解MILP性能差距在复杂算例上非常明显。如果实验室没有商业求解器授权用SCIP或CBC作为替代也可以跑通中小规模算例。我在附录代码里会把求解器名称做成一个可修改的参数方便不同环境下切换。4.2 代码目录结构与关键函数划分一个可维护的项目代码在我看来必须分层我习惯的组织方式如下/root ├── main.m # 主入口定义算例配置 ├── /data # 负荷、风电、电价、热负荷参数 ├── /models # 设备模型矩阵、可行域顶点 ├── /optimize │ ├── day_ahead.m # 日前优化构建与求解 │ ├── intra_day.m # 日内滚动优化 │ └── build_constraints.m # 公共约束构建 ├── /utils │ ├── time_axis.m # 时间分辨率转换 │ ├── dr_model.m # 需求响应参数处理 │ └── plot_results.m # 结果可视化这个结构的核心思想是把模型参数、约束构建、阶段调度分开。若要改算例只需要改data目录下的Excel或mat文件若要改模型只需要动models和build_constraints主程序基本不用动。我此前第一版把全部代码堆在一个脚本里到后来改一个灵敏度参数要翻三处代码效率极低后来才重构到现在这个结构。4.3 核心约束的YALMIP代码风格我贴一下用YALMIP构建日前约束时的核心写法方便大家对照自己的代码找差异% 决策变量定义 P_chp sdpvar(n_chp, T, full); % CHP电出力 H_chp sdpvar(n_chp, T, full); % CHP热出力 P_eb sdpvar(n_eb, T, full); % 电锅炉耗电功率 H_eb sdpvar(n_eb, T, full); % 电锅炉产热功率 SOC sdpvar(n_tes, T1, full); % 储热罐蓄热状态 u_chp binvar(n_chp, T, full); % CHP启停状态 % 约束集合 Constraints []; % CHP可行域约束A_chp/b_chp由顶点坐标计算 for t 1:T for i 1:n_chp Constraints [Constraints, A_chp{i} * [P_chp(i,t); H_chp(i,t)] b_chp{i} (1-u_chp(i,t))*M]; end end这里的技巧在于用大M量连接连续变量与启停变量。当机组停机时u_chp(i,t)0可行域约束必须被松弛掉当机组开机时约束恢复。如果不加大M停机状态下CHP出力被强制为0模型会变成强耦合、且难以求解。类似的处理也用在电锅炉最小出力、储热罐充放热互斥等场景。DR相关的约束里可平移负荷我常用的建模方式是% 可平移负荷x_shift为连续功率z_shift为0-1启动标志 Constraints [Constraints, x_shift 0]; Constraints [Constraints, sum(z_shift) 1]; % 一天最多启动一次 Constraints [Constraints, x_shift P_shift_max * conv_z]; % conv_z是z_shift经过持续运行时间窗卷积得到的运行状态序列这样建模的好处是能表达一旦启动就连续运行指定时长的物理约束且整体仍然保持MILP可解。4.4 求解参数设置与结果导出求解前我习惯做三件事第一给变量赋合理的初值尤其是SOC的初值必须等于实际测量值第二设置求解器的MIPGapGurobi的MIPGap默认值在1e-4对日尺度调度来说可以放宽到1e-3甚至5e-3求解速度显著提升第三记录求解时间和各目标的明细便于调试。ops sdpsettings(solver,gurobi,verbose,2); ops.gurobi.MIPGap 0.005; ops.gurobi.TimeLimit 300; optimize(Constraints, Objective, ops);求解完成后把决策变量统一整理到结构体内保存为result.mat方便后续画图和对比模块读取不需要重新跑一遍优化。5. 典型算例的结果对比与关键参数敏感性5.1 测试系统配置与场景设置我用的算例是在一个6节点电力系统与6节点热力系统耦合的电热综合能源系统上做测试。系统里包含2台抽凝式CHP机组、1台电锅炉、1个储热罐、1个风电场以及常规电负荷和热负荷。日前分辨率1小时日内分辨率15分钟日内滚动窗口4小时也就是16个时段。DR侧假设30%的电力负荷可以参与价格型响应10%的热负荷可以参与温度舒适度调节另外有5%的可平移工业负荷。我设计了四个对比场景场景调度模式需求响应类型S1单阶段日前优化无DRS2两阶段优化无DRS3两阶段优化仅价格型DRS4两阶段优化价格型激励型DR5.2 运行成本与弃风率对比在我的测试算例中几个场景的结果如下数值经过归一化处理场景总运行成本相对S1弃风率CHP燃料成本占比S11.00011.2%62.3%S20.9748.9%60.8%S30.9516.4%58.1%S40.9274.7%55.6%这里有两个值得展开的点。其一S2比S1成本低了约2.6%这个降幅不能简单归因于模型变复杂更多是日内滚动修正让CHP出力曲线更贴合实际负荷减少了无谓的启停和爬坡损耗。其二从S3到S4成本再降约2.4%主要来自激励型DR在晚高峰时段削减了高价电的购入量同时热负荷温度调节让电锅炉可以更灵活地在低谷时段蓄热。5.3 灵活性资源的时间尺度响应特征我特别关注储热罐和DR资源在不同时间尺度的出力模式。从结果来看储热罐的蓄放热决策在日前阶段基本定好了大的充放框架日内阶段只做小范围修正而DR资源则在日内阶段承担了更多的削峰填谷任务尤其是当超短期预测显示某一时段风电突然出力偏低时激励型DR的响应速度明显快于储热罐。这验证了一个设计思路慢速、经济性强的资源储热放在日前规划快速、成本相对高的资源DR调用放在日内平衡。两个时间尺度各司其职整体系统才能既经济又灵活。5.4 热负荷弹性系数的敏感性分析进一步改变热负荷弹性系数从0到0.2观察总成本变化。结果表明当热负荷弹性从0提高到0.1总成本下降约3.8%从0.1提高到0.2成本只再降1.2%边际收益明显递减。这说明热弹性带来的灵活性不是越多越好超过一定程度后用户舒适度损失成本会快速增加调度模型自己也就不愿意进一步利用了。这个结论可以作为实际工程中制定热负荷DR补偿价格的参考依据。6. 复现这套策略时我踩过的坑和总结的经验6.1 热网约束处理不当导致求解器报错第一次把完整热网温度约束加进去时模型直接变成了非线性问题Gurobi不支持YALMIP提示改用BMIBNB或外部非线性求解器结果算一个算例跑了十几分钟还没结束。后来我意识到问题出在管道热功率P c * m * (Ts - Tr)这个式子里的流量m和回水温度Tr同时可变乘积项打破了线性。解决办法前面提过固定供水温度并采用质调节把流量和回水温度的关系通过线性化处理再加上合理的热功率上下限模型就能回到MILP框架。6.2 日前与日内的SOC轨迹对不上还有一个高频问题是储热罐SOC对不上。日前计划中SOC在某个时刻是0.8但日内滚动优化从实测0.75开始如果没有给SOC基准轨迹加一个松弛惩罚日内优化大概率会把SOC越带越偏。我加了一组SOC跟踪约束允许在一定偏差范围内波动超出范围则需要支付惩罚项这样既保证了日内灵活性又不至于偏离日前计划太远。6.3 求解时间爆炸从压缩模型到场景削减当模型包含场景集10个风电场景和完整热网建模时整数变量和约束成倍增长求解时间经常突破10分钟。我在调试阶段先关掉场景用确定性预测跑通全流程确认逻辑无误后再逐步增加场景数量并用快速前向选择法削减场景到5-8个。实测下来从80个场景削减到6个成本偏差不超过1%但求解时间从几小时降到了5分钟以内性价比非常高。6.4 结果可视化排查问题的最有效手段我在这个项目上最深刻的体会是优化问题出Bug时看数值表远不如看曲线图直观。我会把电出力、热出力、储热SOC、DR调用量、弃风量全部画成堆叠面积图和折线图再叠加负荷曲线和电价曲线。有一次SOC曲线出现反向锯齿状变化一看图就知道是蓄放热状态互斥约束写反了修改起来非常快。后来我把plot_results.m写成了标准工具每次跑完必出三类图设备出力图、热网温度/储热状态图、成本构成对比图效率提升特别明显。6.5 给刚开始复现的朋友一个建议如果让我建议一条最快的入门路径我会说先在确定性模型里把所有约束写全哪怕求解慢一点也要保证逻辑正确再用一个简化算例比如单台CHP、单台电锅炉、一个储热罐跑通两阶段衔接然后逐步加入网络约束、DR细节、场景不确定性最后再做算例对比和敏感性分析。每一步都留好断点保存结果这样出了任何问题都能迅速定位到是建模问题、求解问题还是数据问题而不是在一个几千行的脚本里大海捞针。我自己做这类课题的经验是建模思路的可解释性大于一切。某个约束这样加进去要能清楚说出为什么某个结果数值异常要能一步步反推到对应的物理过程。这套两阶段日前日内调度框架在Matlab里能玩的变体很多——引入碳交易、加入电转气、做多目标优化——但核心的建模规范和代码组织方式是可以一直复用的。希望这篇梳理对你复现和改进自己的算例有帮助。