电热联合调度这个方向,最近几年在学术圈和工程圈都特别火。你要是搞过综合能源系统优化,肯定对“多能互补”“源荷互动”这些词不陌生,但落到代码层面,能把电-热耦合、需求响应、日前日内多时间尺度串起来跑通的实现,其实并不多。前阵子我正好把一个两阶段日前日内多时间尺度优化调度策略的项目完整用Matlab实现了一遍,今天就把这套东西从建模到代码拆开讲讲。
这套策略的核心价值在于,它不再把电和热当成两个独立的系统去分别优化,而是利用热力系统的蓄热惯性,配合用户侧的需求响应资源,在日前阶段做出全局经济性最优的调度计划,再在日内阶段根据超短期预测误差做滚动修正。项目整体用Matlab代码实现,适合正在做综合能源优化调度方向研究的学生,也适合刚入门想找一个完整可复现框架的工程师参考。你跟着这篇文章走一遍,不仅能看懂模型,还能直接拿去改成自己的算例。
1. 项目整体思路与调度框架设计
1.1 为什么需要两阶段多时间尺度调度
先聊一个基本问题:为什么不能只做一个单阶段的优化?很多人刚开始接触这个领域,习惯性地把调度问题建成一个确定性的单目标优化模型,一次性求出全天96个时段的机组出力。这种做法的确简单,但实际跑下来,你会发现在真实运行中根本没法直接用。
原因在于预测误差。可再生能源出力和负荷需求都是提前预测的,到了当天实际运行的时候,光伏可能因为一片云突然掉一半出力,电负荷也可能因为天气变化出现明显偏差。如果调度计划是提前一天定死的,第二天只能靠人工手动调整,遇到波动大的天气,系统就很容易出现功率失衡。所以必须要有一个日内阶段,用超短期的预测信息去修正日前计划。
但问题也随之而来:日内阶段通常时间尺度短,15分钟甚至5分钟一个时段,如果每个时段都重新优化所有变量,计算负担会非常大,而且容易让机组频繁调节,影响设备寿命。于是我们把调度分成两个层次——日前阶段解决“明天大致怎么运行最经济”的问题,日内阶段解决“接下来15分钟怎么微调最合理”的问题。这就是两阶段多时间尺度调度的基本逻辑。
1.2 需求响应在框架中的定位
需求响应在这个框架里不是锦上添花,而是主动的调节资源。传统的调度中,负荷被当成固定参数,系统只能通过调节供给侧来平衡功率。但实际用户侧的用电用热行为是有弹性的,比如工厂的可转移负荷,某个工序可以从电价高峰时段挪到低谷时段;楼宇的空调负荷,在保证热舒适度在一定范围内的情况下,完全可以短时调整设定温度。
把需求响应纳入调度模型,相当于把一部分调节能力从供给侧转移到需求侧。这在电-热耦合系统里意义格外大,因为热负荷的弹性比电负荷大得多,建筑本身有热惯性,短时间内供热功率稍微降一点,室内温度不会立刻掉下来。利用这部分热弹性,可以在电价峰时段降低电锅炉或热泵的电功率消耗,把省下来的电用于更重要或更经济的用途,同时利用热网管网的蓄热特性维持用户端温度。
我在项目中把需求响应分成了三类:可转移电负荷、可削减电负荷、可调节热负荷。前两类比较容易理解,第三类则是通过室内温度舒适度区间来建模的,这也是热负荷弹性和电力负荷弹性最大的区别。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统建模:电-热耦合的数学表达
2.1 电力侧设备建模
电力侧的模型相对标准,主要包括热电联产机组(CHP)、燃气锅炉、电锅炉、储能装置,以及外网购电。关键的数学模型用输出功率和爬坡约束描述。
CHP机组的电出力与热出力之间存在耦合关系。常用的建模方式是热电联产可行域,简化处理时用线性关系表达:
matlab复制% CHP机组电热耦合约束
P_chp(t) + k_heat * H_chp(t) <= P_chp_max;
P_chp(t) >= P_chp_min;
H_chp(t) <= H_chp_max;
其中 k_heat 是热电比系数,表示每单位热出力对应的电出力上限。不同的CHP类型,热电比不同,抽汽式机组的调节范围比背压式宽。代码里我把这个系数作为可配置参数,方便切换到不同机组类型。
储能电池的模型则是典型的时序递推,核心是SOC状态转移:
matlab复制SOC(t+1) = SOC(t) + (P_chg(t)*eta_chg - P_dis(t)/eta_dis) * dt / capacity;
注意充放电效率的方向,充电时能量进入电池有损耗,放电时能量出来也有损耗,数学表达式上效果不一样。很多新手容易在这里搞混,导致SOC越算越离谱,这点后面讲调试时还会再提。
电锅炉和燃气锅炉的模型相对简单,就是输入燃料(或电能)转化为热能,用效率系数折算。电锅炉虽然在能量转换效率上很高,但本体建设成本和运行电费是重点约束,所以调度中会结合分时电价自动规避峰时段。
2.2 热力侧模型与热惯性
热力系统和电力系统最大的不同在于动能很小、惯性很大。电网中的功率几乎是瞬时平衡的,但热网里的热水从热源流到用户端需要时间,管道本身也有热容量,这导致热力系统有天然的“缓冲”能力。
在代码实现中,热力侧采用了简化的节点法模型。主要描述供水温度和回水温度的分布与热功率的关系。一般用节点热平衡方程:
matlab复制% 热网节点温度混合方程,简化为矩阵形式
T_supply = T_return + H_exchange ./ (c_p * m_dot);
这里的难点在于热网模型如果建得太细(比如每个管道都做动态热传导模型),求解时间会急剧增长,不适合和电力调度耦合优化。所以实用中往往把热网简化为一个带有蓄热系数的聚合模型,用一个虚拟的“热储能”来表征管网的蓄热能力。
另外,建筑热惯性也用一阶等效热参数模型描述:
matlab复制T_in(t+1) = T_in(t) * exp(-dt/tau) + (R*Q_hvac(t) + T_out(t)) * (1 - exp(-dt/tau));
这个模型基于热阻热容的等效电路思想,房间温度变化跟供热功率、室外温度、围护结构热阻和时间常数有关。tau 是时间常数,通常几十分钟到几小时不等,取决于建筑的保温性能。这个模型是热需求响应建模的基础,也是整个热负荷弹性能否发挥出来的关键。
2.3 需求响应建模:弹性从哪里来
需求侧建模我细化成了三类。可转移负荷用启动状态变量表示,比如洗衣机、工业流程,其运行时段可以在满足总工作量的前提下平移。数学上就是每个时段负荷的上下限约束和总用电量守恒约束。可削减负荷则通过削减比例上限约束,配合补偿成本进入目标函数。
可调节热负荷我重点说一下。传统热负荷建模给的是固定热功率需求曲线,但这个项目利用热舒适度区间建立了一个弹性热负荷模型:
matlab复制T_min <= T_in(t) <= T_max;
Q_hvac(t) >= k * (T_set_lower - T_in(t));
Q_hvac(t) <= Q_hvac_max;
室内温度允许在18到24摄氏度之间波动(具体范围可设),只要不越界,供热功率就是灵活的。这样在电价高峰时段,允许室内温度稍微下降一点,热泵或者电锅炉就可以降功率运行。实际上人体对1-2摄氏度的温度变化感知并不明显,但系统节省的电费却相当可观。
3. 两阶段调度模型构建与求解
3.1 日前调度模型:目标是全天经济最优
日前调度的周期是24小时,时间分辨率为1小时(也可以细化到15分钟,即96个时段)。模型的目标函数是全天总运行成本最小化,包括燃料成本、购电成本、需求响应补偿成本、设备启停成本等。
目标函数数学表达如下:
matlab复制% 日前目标函数
Objective = sum(sum(C_fuel * P_chp) + C_grid * P_buy ...
+ C_dr * P_shed + C_start * start_flag ...
- C_grid_sell * P_sell);
权重系数里,燃料成本按天然气价格折算,购电成本按分时电价曲线输入,需求响应补偿成本是激励用户参与调节的费用系数。注意,这里不是目标函数越长越好,每一项都需要能测到实际含义,不然结果不好解释。
日前阶段的约束包括功率平衡约束、设备出力上下限约束、爬坡约束、储能SOC约束、热网节点温度约束、室温舒适度约束等,整个优化问题是一个混合整数线性规划(MILP)。为什么会有整数变量?因为机组的启停状态是0-1变量,启停动作本身不能连续变化。这部分我用YALMIP工具箱统一建模,求解器用Gurobi或Cplex都可以,实测下来Gurobi在大型MILP问题上的速度要更快一些。
3.2 日内滚动修正模型:跟踪日前计划的偏差
日内阶段采用模型预测控制(MPC)思想,每15分钟滚动一次,预测未来4小时的运行情况。核心思路是,在满足安全约束前提下,尽可能让日内出力逼近日前计划,同时修正预测误差引入的偏差。
日内目标函数包含两部分:一部分是运行调整成本,另一部分是日前计划偏差惩罚项:
matlab复制Objective_rt = sum(C_adj * abs(P_rt - P_da)) + C_penalty * violation;
这个“偏差惩罚”是关键设计。如果不加惩罚,日内优化就会完全无视日前计划,自己重新算一个最优解,这样日前阶段就白做了。而且机组频繁大范围调节,运行成本反而更高。加了偏差惩罚后,日内调整会被限制在一个合理范围内,只在必要时做修正,整体调度的稳定性大幅提升。
我试过把偏差惩罚系数调得很小,结果日内阶段频繁把机组出力和日前计划拉开很大差距,虽然单看日内成本更低了,但把两阶段加起来看,总成本反而上升了,而且机组调节频率明显增加。这个经验参数需要根据实际系统反复试。
3.3 求解方法选择:直接求MILP还是分解算法
很多文章喜欢用交替方向乘子法或者拉格朗日松弛把问题分解成电-热子问题分别求解,这样听起来很高端。但从工程实现角度考虑,对于中小规模的测试系统(比如IEEE 33节点配电网加上一个6节点热网),直接用商业求解器解整体MILP就够了。
我倾向于先把问题完整建模、用求解器直接求解,把调度的“最优解”先拿到手。等理解了问题的耦合特性和求解瓶颈,再考虑要不要为了分布式计算或者隐私保护去设计分解算法。很多同学一上来就仿照论文做基于ADMM的分布式求解,到自己复现时发现热网收敛性特别差,调半天参数也调不好,反而把主问题丢了。
如果你的算例节点数太多,求解时间无法接受,可以考虑两种简化办法:一是把热网进一步聚合,等效成几个集中式节点;二是把MILP松弛成线性规划(去掉启停变量),先算一个近似解作为参考,再通过启发式方法修正。实测下来,前者对精度影响相对可控,并且能显著降低求解时间。
4. Matlab代码实现与核心模块解析
4.1 代码整体结构与数据流
一个干净清晰的项目目录结构能让调试效率翻倍。我建议按功能划分模块,把数据、模型、求解、结果分开:
text复制项目根目录/
├── data/ % 输入数据:负荷曲线、电价、设备参数等
├── model/ % 约束构建、目标函数构建
├── solver/ % 调用YALMIP和Gurobi/Cplex求解
├── results/ % 结果保存,生成图表
├── main_DA.m % 日前调度主文件
├── main_RT.m % 日内滚动调度主文件
├── plot_results.m % 结果可视化
我个人建议数据用Excel或CSV统一管理,不要直接写在脚本里。原因很简单:调度问题的参数特别多,电价曲线、负荷曲线、设备参数零零总总几十项,如果全写在代码里,改参数要翻半天;用表格管理后,改参数只需要改数据文件,代码一行不用动,这也方便做参数敏感性分析。
4.2 关键代码段:约束构建与求解调用
这里展示一个日前调度约束构建的核心片段,核心是用YALMIP定义决策变量,然后循环添加约束:
matlab复制%% 定义决策变量
P_chp = sdpvar(1, T, 'full'); % CHP电出力
H_chp = sdpvar(1, T, 'full'); % CHP热出力
P_eb = sdpvar(1, T, 'full'); % 电锅炉功率
P_buy = sdpvar(1, T, 'full'); % 购电功率
u_chp = binvar(1, T); % 启停变量
SOC = sdpvar(1, T+1, 'full'); % 储能SOC
%% 约束集合
Constraints = [];
for t = 1:T
% 功率平衡
Constraints = [Constraints, P_chp(t) + P_dch(t) - P_chg(t) + P_buy(t) ...
+ P_pv(t) - P_eb(t) == P_eload(t) - P_dr_resource(t)];
% CHP出力上下限与启停耦合
Constraints = [Constraints, P_chp_min * u_chp(t) <= P_chp(t) <= P_chp_max * u_chp(t)];
% 储能SOC递推
Constraints = [Constraints, SOC(t+1) == SOC(t) + (P_chg(t)*eta_chg - P_dch(t)/eta_dis) * dt / cap_ess];
end
%% 求解
ops = sdpsettings('solver', 'gurobi', 'verbose', 2);
optimize(Constraints, Objective, ops);
你要特别注意YALMIP里 binvar 和 sdpvar 混用时的求解时间。启停变量多了之后,MILP的整数变量规模迅速膨胀,如果T=96且机组数量多,求解时间可能从几秒钟涨到几分钟。这时候可以考虑把部分快速启停设备从整数变量改成连续变量,或者用滚动时段简化。
4.3 参数设置与场景设计
我建议设计三个对比场景来验证需求响应和两阶段策略的效果:
- 场景1:无需求响应、单阶段日前调度(基准场景)
- 场景2:有需求响应、单阶段日前调度
- 场景3:有需求响应、两阶段日前日内调度
这样做的好处是能分开量化两部分的贡献。一开始我做对比时,只比较了“有需求响应”和“无需求响应”,发现总成本下降确实明显,但分不清是因为需求响应本身还是因为两阶段框架。后来加了场景1作为基准,结论一下子清晰很多。
参数敏感性方面,重点考察需求响应补偿价格和热舒适度区间宽度对调度结果的影响。一个典型的发现是,当补偿价格低时,用户参与意愿低,需求响应容量小;但补偿价格过高时,系统倾向于过度依赖需求响应,导致某些时段切负荷行为频繁,总成本反而上升。所以补偿价格存在一个最优区间,这个可以用后续的重复仿真搜索出来。
4.4 结果可视化与性能分析
结果可视化是项目收尾的关键环节。我一般画四类图:一是电功率平衡堆叠图,能看出各时段电源出力和负荷的匹配情况;二是热功率平衡堆叠图;三是SOC和室温变化曲线,验证热惯性效果;四是日前计划与日内实际出力的对比曲线,展示两阶段调度的修正效果。
画图用Matlab自带函数就够,不必引入额外库。一个实用技巧是统一设置set(groot, 'defaultAxesFontSize', 12)之类的全局样式,保证所有图风格一致。生成的图保存为PDF矢量格式,图片清晰度更高。
5. 实例验证与结果分析
5.1 测试系统与参数
为了验证模型有效性,我用的测试系统包含一个CHP机组(额定电功率2MW,热功率2.5MW)、一个电锅炉(1MW)、一台燃气锅炉(2MW)、一个储能电池(容量1MWh,最大功率0.5MW),配了一个简化的4节点热网。电负荷、热负荷和光伏出力曲线使用典型的冬季日曲线,分时电价采用峰谷平三段式结构:峰段1.2元/kWh,平段0.8元/kWh,谷段0.4元/kWh。
具体到需求响应参数,可转移负荷比例设为15%,可削减负荷上限设为10%,可调节热负荷的室内温度允许范围设为20±2摄氏度,需求响应补偿单价取0.15元/kWh。
5.2 结果对比:三个场景的经济性分析
跑完三个场景后,结果如下表所示:
| 场景 | 总运行成本(元) | 购电成本(元) | 燃料成本(元) | 需求响应补偿(元) | 成本下降比例 |
|---|---|---|---|---|---|
| 场景1 | 28560 | 9320 | 19240 | 0 | 基准 |
| 场景2 | 26340 | 8270 | 17380 | 690 | 7.78% |
| 场景3 | 25180 | 7630 | 16890 | 660 | 11.83% |
从数据能看得很清楚:单加入需求响应(场景2)能降7.78%成本,主要贡献来自电价峰时段的负荷转移;再加上两阶段日前日内调度框架(场景3),成本进一步下降,总降幅达到11.83%。为什么两阶段还能继续降成本?因为日内滚动修正让光伏预测误差导致的备用成本大幅减少了,系统不需要为了预测误差预留过多的高成本备用容量。
5.3 多时间尺度协同的实际效果分析
再细看场景3的调度曲线,日前阶段安排电锅炉在凌晨谷电时段满功率产热,同时利用热网蓄热特性把热量储存在管网和建筑围护结构中;在电价峰时段(上午9点到11点、晚上6点到9点),电锅炉降功率运行甚至停机,热量主要由蓄存的热能释放,CHP机组也尽量在峰时段多发有功电力送到电网,同时利用余热供热。
日内阶段的效果更直观。某天下午3点光伏出力由于云层遮挡在15分钟内下跌了30%,日内模型检测到功率不平衡后,优先通过储能放电和轻微削减可削减负荷来平衡功率,同时允许部分区域室温在舒适度范围内缓慢下降,没有让CHP机组大幅爬坡。这些响应动作叠加在一起,就让系统在保证安全的前提下,避免了高成本机组的紧急调峰。
6. 常见问题与调试心法
6.1 求解器报错与收敛性问题
跨入这一步,你会遇到一个经典报错:“Infeasible problem”。模型不可行是YALMIP最常见的败点,原因往往不是瓶颈约束,而是边界条件存在矛盾。
一条实用调试路径是:先只加功率平衡约束,不加设备上下限和爬坡约束,如果此时可解,说明平衡约束本身没问题;然后逐步加入设备出力约束、储能SOC约束、热网温度约束,每一步都验证是否仍能求解。这样二分定位,找到了导致冲突的约束组。
另一个常见问题是求解时间过长。如果Gurobi跑几分钟还不出结果,优先检查整数变量数量和Big-M取值是否合理。很多MILP求解慢的问题,都是因为Big-M设得过大,导致线性松弛效果很差。实际工程中把Big-M设成设备最大出力的1.5倍到2倍就够,不要随手写1e6。
6.2 热网建模的常见坑
热力系统建模踩坑概率远高于电力侧。最常见的是热网时间常数设置不当,导致室内温度变化曲线极其缓慢,调度结果显得“热惯性无限大”,这是不对的。
我吃过一次亏:建筑时间常数设了10个小时,结果日内模型发现即便关掉所有热源,室内温度24小时内也就降1度,于是调度模型“聪明地”在峰时段把供热功率降为零,结果用户端温度虽然没越界,但供热公司考核的是供水温度舒适度,这种极端的“蓄热供给”在工程上根本不可接受。后来我把时间常数调成2小时,模型给出的调度策略就合理多了。
热网蓄热系数也是类似问题。取值过大会夸大管网蓄热能力,调度结果偏乐观;取值过小则浪费了热网的调节潜力。建议根据实际管网的管道长度、直径和保温情况,参照传热学公式先粗算一个范围,再结合运行数据修正。
6.3 需求响应参数的敏感性调试
需求响应补偿价格和舒适度范围这两个参数,直接决定了可用的需求响应容量。我在调参时发现一个规律:补偿价格从0.1元/kWh升到0.2元/kWh时,总成本先降后升,拐点出现在约0.15元/kWh。原因是补偿价格太低,用户不愿参与;太高则系统过度依赖需求响应,支付的补偿费用超过了削峰带来的购电成本节省。
舒适度范围方面,允许范围从2摄氏度扩到4摄氏度,需求响应潜力大约增加40%,但用户投诉风险也明显上升。所以工程上我一般不会只用舒适度范围来定参数,还会叠加一个“连续低温持续时间”约束,比如不允许室内温度低于22度超过连续2小时,避免用户在长时间内感觉到不适。
调试需求响应参数时,建议用控制变量法:固定其他参数不动,逐一跑参数扫描,把结果画成曲线,直观寻找敏感区间。不要同时调多个参数,否则结果出了变化你根本分辨不了是哪个参数引起的。
最后再分享一个小的实操技巧。Matlab里跑这种大规模优化,我习惯用profile on开性能分析,找到代码里计算瓶颈。很多时候瓶颈不是求解器,而是你自己写的约束循环太慢。比如给96个时段逐一添加约束,如果用for循环反复拼接Constraints矩阵,效率极低。实际项目里我改成用repmat、kron等向量化方式批量生成约束矩阵,再一次性传给优化模型,求解器和Matlab本身的耗时能下降70%。这种工程层面的细节,论文里从来不会写,但对于真正要跑代码、调算例的人来说,影响非常大。
