电-热综合能源系统两阶段日前日内多时间尺度优化调度Matlab实现
2026/9/8 8:48:30 网站建设 项目流程

这个课题我前前后后做过好几轮,从最初照着一篇参考论文的思路复现,到后来自己改模型、换求解器、调算例,中间绕了不少弯。现在回头看,"考虑需求响应的电-热综合能源系统两阶段日前日内多时间尺度优化调度策略研究"这类题目,在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, T+1, '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的初值必须等于实际测量值;第二,设置求解器的MIPGap,Gurobi的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单阶段日前优化无DR
S2两阶段优化无DR
S3两阶段优化仅价格型DR
S4两阶段优化价格型+激励型DR

5.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里能玩的变体很多——引入碳交易、加入电转气、做多目标优化——但核心的建模规范和代码组织方式是可以一直复用的。希望这篇梳理对你复现和改进自己的算例有帮助。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询