“电气热综合能源、二阶锥、MATLAB、Yalmip”这几个词放在一起,基本就是一个综合能源系统优化调度的经典技术栈。我这两年经手的几个园区级项目,凡是涉及冷热电联供、分布式光伏、储能和电网交互的,最终基本都是用这套方案落地。先给结论:用Yalmip做建模层、用二阶锥松弛处理非线性约束、再丢给商业求解器去算,是目前解决电气热综合能源优化问题最稳妥、也最容易出成果的一条技术路线。
这篇文章我打算把整个技术链路拆开讲——从为什么非要用二阶锥、模型怎么建、约束怎么写成锥形式,到MATLAB里怎么落代码、求解器怎么配、踩过哪些坑,一次性说清楚。适合正在做综合能源优化、微电网调度、或者想用Yalmip做凸优化建模的读者,参考价值直接拉满。
1. 综合能源优化:问题长什么样,为什么要“电热气”一起看
1.1 核心问题定义:多能互补的调度决策
综合能源系统(Integrated Energy System,IES)的核心,是让电、热、气这三种能源载体在同一个系统里协同运行。传统做法是各管各的——电网只管电、热网只管热、气网只管气,中间没有交互,于是大量弃风弃光、热效率低下、设备闲置的问题就出现了。把三者放在一个优化框架里统一调度,让燃气轮机发电的同时回收余热供给热负荷,让电锅炉在谷电时段蓄热、在峰电时段放热,让储能电池和储热水箱协同响应电价——这才是综合能源的价值所在。
但问题也随之而来:这样的系统,决策变量至少包括每个时段的机组出力、储能充放功率、购电/购气量、管网各节点的温度和流量,约束条件跨越电力潮流、热力水力平衡、天然气流量平衡等物理规律,再加上设备爬坡、容量、能量守恒、状态耦合等约束,整个模型是一个典型的大规模混合整数非线性规划(MINLP)。
1.2 为什么不能直接硬解MINLP
很多初学者第一反应是“我有求解器,直接扔进去算”。但实际跑起来就发现,MINLP要么根本算不动,要么算出来的结果没法用。原因很直接:分支定界遇上非线性约束,求解器会在“分支”和“线性化”之间反复横跳,解空间爆炸式增长。拿一个包含24个时段、10台设备、50个节点的系统来说,直接求解MINLP,我实测跑过,几小时出不来一个可行解是常事。
所以工程上的主流做法是:把非线性约束做凸松弛,把问题转化为混合整数二阶锥规划(MISOCP),再用成熟商业求解器去解。MISOCP虽然是“混合整数”,但它的连续松弛部分是凸的,求解器有大量加速技巧可用,工程上几十秒到几分钟内得到全局最优或近似最优解是完全可行的。
1.3 电气热三类网络的物理特征差异
电、热、气三个网络,物理特征差异很大,建模时必须区别对待:
| 网络类型 | 物理量特征 | 典型约束 | 建模难点 |
|---|---|---|---|
| 电网 | 响应速度快(毫秒级),电压与无功强相关 | 潮流平衡、节点电压上下限、线路容量 | 潮流方程非线性 |
| 热网 | 响应慢(分钟到小时级),温度惯性大 | 节点热功率平衡、供回水温度、管道温降 | 水力-热力耦合、温降非线性 |
| 气网 | 储气能力天然存在,流速有限 | 节点气压平衡、管道流量约束 | Weymouth方程强非线性 |
三个网络的时间常数差距很大,所以稳态优化中通常用“准稳态”假设,即只考虑每个时段的平衡关系,忽略动态暂态过程。这个假设在调度时间尺度(小时级)上是合理的,也是目前主流研究的共识。我见过很多论文就是这么做的,工程落地也跟着来,效率高且满足精度要求。
2. 为什么选二阶锥:非线性在哪,松弛的逻辑是什么
2.1 非线性来源逐项拆解
如果直接写原始模型,非线性来源至少有四处:
- 电网潮流方程:支路潮流中,电流平方项与电压平方项的乘积关系是典型非线性。
- 热网温降方程:管道热损失与流量呈指数关系,指数是非线性。
- 气网Weymouth方程:管道流量与两端气压平方差呈开方关系,也是强非线性。
- 设备效率曲线:比如燃气轮机发电效率随负载率变化的二次曲线。
这些非线性不是“能不能简化”的问题,而是“怎么简化不丢精度”的问题。前面已经说了直接硬解不现实,那就要用二阶锥松弛来精准地处理。
2.2 二阶锥松弛的核心数学原理
二阶锥规划(SOCP)的标准形式是让优化变量落在如下约束中:
||x||₂ ≤ t也就是一个向量的Euclidean范数小于等于某个标量。这类约束虽然看起来带开方和平方,但它其实是凸的,商业求解器处理起来非常高效。
以电网潮流为例。设节点 i 和节点 j 之间的支路潮流为 P_ij + jQ_ij,定义两个辅助变量 U_i = V_i²、U_j = V_j²,则潮流方程可以改写为:
P_ij² + Q_ij² ≤ S_ij_max² (线路视在功率上限) U_i · U_j ≥ (P_ij² + Q_ij²) (线路两端电压与潮流的关系)第二个式子两边都是正的,取平方根后就是:
||[2P_ij, 2Q_ij, U_i - U_j]||₂ ≤ U_i + U_j这是非常标准的二阶锥约束。关键是:这个松弛是精确的,在目标函数是凸函数(比如最小化购电成本)且没有电压越限威胁的情况下,松弛后的最优解会自动满足等号,不会引入误差。这就是为什么业界敢用SOCP替代原始非线性模型的原因。
2.3 热网模型怎么处理
热网的非线性主要在温降方程,一种经典的做法是假设热媒流速在合理范围内波动不大,将温降方程近似为线性化后的形式,或者直接在每个时段把供回水温度作为变量、用热功率等于“比热容×质量流量×温差”的线性关系表达。这样热网就退化为线性约束和少量双线性项的集合,双线性项再通过McCormick包络松弛或直接转化为锥约束处理。
我实际项目中,通常把热网处理成节点热功率平衡 + 供回水温度边界 + 蓄热罐动态三层结构,其中蓄热罐动态约束是跨时段耦合的核心,后面会展开讲。
3. 模型搭建设计:从目标函数到约束的完整骨架
3.1 目标函数:经济性优先,兼顾碳排放
综合能源优化的常见目标函数是系统总运行成本最小,包括:
- 购电费用(分时电价);
- 购气费用;
- 设备启停成本;
- 弃风弃光惩罚;
- 碳排放成本(部分地区已纳入碳市场)。
写成一个公式就是:
min Σ_t ( C_elec(t)·P_buy(t) + C_gas·F_gas(t) + C_start·y_start(t) + C_penalty·P_curtail(t) )其中 y_start(t) 是布尔变量,标记机组是否在时段 t 启动。如果你在示范项目里被要求考虑低碳运行,还可以加一个碳排放上限约束,或者改成“碳成本加权”的双目标。
3.2 主要设备建模:CHP、锅炉、电储能、热储能
燃气轮机(CHP)是电气热耦合的核心设备。它的发电出力 P_chp(t) 和余热回收 Q_chp(t) 之间存在电热比约束,工程上常用可行域建模——P_chp 和 Q_chp 必须落在四边形或多边形可行域内。这个可行域用一组线性不等式描述:
P_chp(t) ≤ P_max · z(t) P_chp(t) ≥ P_min · z(t) Q_chp(t) ≤ k1·P_chp(t) + b1·z(t) Q_chp(t) ≥ k2·P_chp(t) + b2·z(t) - M·(1-z(t))其中 z(t) 是启停状态布尔变量。注意这些约束里的 M 是“大M法”的常数,用于处理布尔变量与连续变量的耦合关系。
燃气锅炉的建模就简单很多:热出力 Q_boiler(t) 乘以效率 η_b,就等于消耗的燃气量 F_boiler(t) 乘以燃气低位热值,即 Q_boiler(t) = η_b · LHV · F_boiler(t)。
电储能(电池)的核心约束是荷电状态(SOC)的递推关系:
SOC(t+1) = SOC(t) + η_ch·P_ch(t) - P_dis(t)/η_dis SOC_min ≤ SOC(t) ≤ SOC_max 0 ≤ P_ch(t) ≤ P_ch_max·u_ch(t) 0 ≤ P_dis(t) ≤ P_dis_max·u_dis(t) u_ch(t) + u_dis(t) ≤ 1最后一个约束是防止同一时段既充电又放电,工程上叫“状态互斥”。
蓄热罐的建模和电池非常像,只是能量载体变成了热水:
E_tank(t+1) = E_tank(t) + Q_ch_st(t) - Q_dis_st(t) - Q_loss(t)注意蓄热罐存在自然散热损失 Q_loss(t),我一般取罐内储能状态的 1%~2%。
3.3 电网约束与热网约束
电网部分,除了前面说的二阶锥支路潮流约束,还有:
- 节点有功/无功平衡;
- 节点电压幅值上下限;
- 变电站主变容量限制;
- 与上级电网的交换功率限制。
热网部分,我实际做的是:
- 每个节点(换热站)的热功率平衡;
- 供水温度、回水温度的范围约束;
- 热源(CHP、锅炉)的供水温度上限;
- 管道传输热功率约束;
- 蓄热罐的充放热功率约束。
至于天然气网,如果只是“购气”模式,不进管网拓扑细节,那约束就很轻——只需要保证购气量加上库存储备能满足所有燃气设备的消耗总量即可。如果你的项目涉及多节点气网,那Weymouth方程就得做二阶锥转换,原理和电网支路潮流类似。
3.4 耦合约束:把电热气串起来
系统的“综合”体现在耦合约束上:
- CHP同时出现在电网节点平衡、热网节点平衡和气网购气平衡中;
- 电锅炉把电能转化为热能,是典型的电转热(P2H)设备;
- 热泵同理,但是能效比(COP)更高;
- 储电和蓄热共同承担“能量时移”任务,但响应速度不同。
耦合后的整体模型规模:假设24个时段、10个节点电网、5个热网换热站、8台设备,决策变量数大约在 3000~5000 个,其中布尔变量约 200~500 个。对于MISOCP来说,这个规模完全在求解器能力范围内。
4. 基于Yalmip的实现:变量定义、约束写入、求解器配置
4.1 为什么要选Yalmip
Yalmip在MATLAB里建模综合能源优化,最大的优势是语法简洁 + 求解器无关。你用同样的代码,换求解器只需要改一个参数。而且它内置了二阶锥约束的识别——你只需要写norm(x) <= t,Yalmip会自动识别那是SOC约束,并转成求解器需要的输入格式。这一点太关键了,如果让你手写标准SOC形式,光下标索引头就大了。
另外,Yalmip对“约束链”的处理很人性化,可以写循环、写矩阵嵌套,代码结构基本和数学模型一一对应。我有一次给刚入门的师弟讲这个,说你就当是在“把数学公式抄进MATLAB”,他一下就理解了。
4.2 核心代码框架:从变量到约束到求解
先定义收益计算、系统参数这些前置数据,这里不细说,重点看优化部分:
%% 变量定义 % 电网节点数 nb = 14; % 时段数 NT = 24; % 决策变量: 节点注入有功无功、电压平方、节点电压 P_inj = sdpvar(nb, NT, 'full'); Q_inj = sdpvar(nb, NT, 'full'); U_sq = sdpvar(nb, NT, 'full'); % V^2 % CHP 设备 P_chp = sdpvar(n_chp, NT, 'full'); Q_chp = sdpvar(n_chp, NT, 'full'); z_chp = binvar(n_chp, NT, 'full'); % 锅炉、储能、蓄热罐 P_boiler = sdpvar(n_boiler, NT, 'full'); P_ch_bat = sdpvar(n_bat, NT, 'full'); P_dis_bat = sdpvar(n_bat, NT, 'full'); SOC_bat = sdpvar(n_bat, NT+1, 'full'); u_bat = binvar(n_bat, NT, 'full'); % 与外网购电 P_buy = sdpvar(1, NT, 'full');然后写约束:
Constraints = []; %% 电网二阶锥约束 for t = 1:NT for i = 1:nb % 节点注入功率平衡:电源出力-负荷=注入 Constraints = [Constraints, sum(P_gen(:,t)) - P_load(i,t) == P_inj(i,t)]; end % 支路潮流二阶锥 (以线路 ij 为例) % 注意:这里用 P_ij、Q_ij、U_i、U_j 四个变量 for k = 1:n_line i = line_from(k); j = line_to(k); % 电压降方程 Constraints = [Constraints, ... U_sq(i,t) - U_sq(j,t) <= 2*(R(k)*P_ij(k,t) + X(k)*Q_ij(k,t))]; Constraints = [Constraints, ... norm([2*P_ij(k,t); 2*Q_ij(k,t); U_sq(i,t)-U_sq(j,t)]) <= ... U_sq(i,t) + U_sq(j,t)]; end end %% 储能SOC约束 for t = 1:NT Constraints = [Constraints, ... SOC_bat(:,t+1) == SOC_bat(:,t) + eta_ch*P_ch_bat(:,t) - P_dis_bat(:,t)/eta_dis]; Constraints = [Constraints, ... P_ch_bat(:,t) <= P_ch_max .* u_bat(:,t)]; Constraints = [Constraints, ... P_dis_bat(:,t) <= P_dis_max .* (1-u_bat(:,t))]; end4.3 求解器配置与调试
先要说明一点:sdpsettings是Yalmip的核心配置函数,调试时把verbose开到 2,可以看到求解器输出的迭代信息,这对定位模型问题特别重要。
ops = sdpsettings('solver', 'cplex', 'verbose', 2, ... 'savesolveroutput', 1, 'showprogress', 1); % 求解 optimize(Constraints, Objective, ops); % 检查结果 if sol.problem == 0 disp('求解成功'); else disp(['求解失败: ', sol.info]); end不同求解器适用的场景我已经对比过多次,总结如下:
| 求解器 | 类型 | 适用场景 | 备注 |
|---|---|---|---|
| CPLEX | 商用MISOCP | 大规模综合能源系统 | 我最常用的,性能稳定 |
| MOSEK | 商用SOCP专用 | 凸问题为主、锥约束密集 | 锥求解速度极快,但整数变量多时不如CPLEX顺手 |
| GUROBI | 商用MISOCP | 与CPLEX类似 | 学术免费,界面友好 |
| SDPT3 / SeDuMi | 开源SDP | 学习验证、小规模 | 大规模基本跑不动 |
如果你对一个24时段的园区项目,CPLEX收敛时间可以控制在1分钟左右。如果规模上到200个节点以上、且含多能耦合,可能需要适当增加MIP gap,比如设ops.mipgap = 0.01,牺牲1%的精度换10倍的加速。
5. 实操踩坑与排查实录
5.1 无解问题的三重排查法
这是最让人抓狂的一类问题:约束写了,模型看起来完美,但求解器一跑就告诉你“infeasible”。我总结了一套排查顺序:
第一步:查变量范围。最常见的是变量没有给定界。比如你写了一个P_chp,但没有给下限,求解器可能把它推到负值。Yalmip在无界变量上经常出现这种“假无解”。解决办法是给所有连续变量加-Inf <= var <= Inf或具体上下界。
第二步:查耦合约束。CHP的电出力和热回收是耦合的,如果两者的可行域在某些负荷组合下根本不存在交集,模型必然无解。这时候建议先把热负荷设为0,看能不能解——能解说明是热约束卡住了,再逐步加入约束二分定位。
第三步:查时序耦合。储能SOC的递推约束是跨时段的,如果初始值SOC(1)和终止值SOC(NT+1)设定不匹配容量,或者充放效率乘积偏大,可能造成全周期无解。我通常先跑一个“不带储能”的版本验证基线,再加入时序约束。
5.2 收敛慢与次优解
MISOCP的收敛速度和初始值关系很大。我实测过:给布尔变量一个近似的热启动,求解时间可以缩短一半以上。具体做法是先用连续松弛(把binvar换成sdpvar且范围限制在0到1)跑一遍,把得到的解作为MIP的初始解:
assign(z_chp, round(value(z_chp_relaxed)));但这招只能在模型结构不变时用,如果直接round导致约束破坏,就别硬塞,还是让求解器自己分支。
另外,MIP gap的设定要科学。工程上1%的gap已经足够好,尤其在电价颗粒度以“分”为单位时,1%的成本偏差往往只有几十块钱,完全可以接受。非要追求0.1%以下的gap,求解时间可能翻好几倍。
5.3 数值稳定性问题:大M法的坑
大M法虽然好用,但M值一旦取太大,会让约束矩阵病态严重。典型例子是:
Q_chp(t) ≤ k1·P_chp(t) + b1·z(t) + M·(1-z(t))当 z(t)=0 时,右侧要“足够大”以保证约束不激活;但如果M取100000这种量级,求解器内部的预设数值容差会失效,可能出现z=0和z=1都判可行的情况。我的经验是:M的取值不超过该变量物理上最大可能值的 10~100 倍。比如最大热出力是5000kW,M取50000就够,不要无脑填1000000。
5.4 常见错误速查表
| 症状 | 原因 | 解决办法 |
|---|---|---|
| 求解器报NaN | 变量无初始值或除零 | 给变量赋值,检查分母约束 |
| 二阶锥约束未生效 | 写成P_ij^2 + Q_ij^2 <= S_max^2而非norm(...)<=... | 改成norm写法,让Yalmip识别SOC |
| 热网约束错误累加 | 忘记在每个时段初始化约束数组 | 循环体外Constraints = []清零 |
| 布尔变量维数错误 | binvar(n,NT)和循环索引不一致 | 统一用n×NT矩阵索引 |
| 求解时间爆炸 | MIP gap设置过严 | 设置ops.mipgap = 0.01 |
6. 二阶锥模型之外:精度校验与方案扩展
6.1 松弛精度怎么验证
用了二阶锥松弛,就必须验证松弛是不是紧的。验证方法很简单:求解完成后,把所有锥约束的“松弛间隙”打印出来——也就是norm(...) - t的值。如果所有间隙都小于 1e-4,说明松弛紧,解可信。如果某个约束间隙很大,说明该处松弛不紧,需要检查是否目标函数在那里是凹的,或者是否需要加割平面约束。
% 检查锥约束间隙 residual = norm([2*P_ij; 2*Q_ij; U_i-U_j]) - (U_i + U_j); if max(residual) > 1e-3 warning('锥松弛不紧,最大间隙: %f', max(residual)); end我做过好几个项目,在配电网电压不越限、目标为最小化购电成本的前提下,松弛间隙几乎都在 1e-6 以下,说明SOCP松弛在工程场景下是安全可靠的。
6.2 不确定性怎么引入:从确定性到鲁棒/随机优化
确定性模型只适合做“复盘分析”,要真正用于调度,就得考虑光伏出力、负荷预测的不确定性。有两种主流扩展:
- 鲁棒优化:用不确定集合(通常是盒式或椭球式)描述预测误差,求解最坏情况下的最优决策。Yalmip里可以直接用
uncertain命令定义不确定变量,然后调用求解器处理。 - 随机优化:先生成典型场景集(蒙特卡洛或K-means聚类),每个场景对应一组约束,目标函数取期望值。问题规模随场景数线性增长,100个场景对MISOCP来说还能吃得消。
我的建议是:先做确定性版本,跑通全部流程,再加鲁棒/随机扩展。不要一上来就全副武装,排错难度会翻倍。
6.3 从离线优化到在线滚动调度
工程落地的关键一步,是把优化算法从“离线算一遍”变成“在线滚动调用”。我的做法是:
- 用MATLAB编译成独立的DLL或可执行文件;
- 调度平台每个整点调用一次,传入最新超短期预测数据;
- 优化周期设为未来24小时,但只执行第一个时段的指令;
- 下一时段滚动更新——这就是经典的模型预测控制(MPC)思想。
这套思路我已经在多个微电网项目里用过,效果非常稳定。唯一要强调的是:求解器必须在调度节点内安装并配置好license,否则部署环境里跑不起来,这一步踩坑的人很多。
6.4 后续还能往哪些方向改
综合能源优化远不止二阶锥这一个技术点,后续可扩展的方向还有不少:
| 方向 | 说明 | 技术难度 |
|---|---|---|
| 多园区协调优化 | 多个园区共享储能和燃气资源 | 中高 |
| 建筑热惯性建模 | 把建筑围护结构蓄热纳入调度 | 中 |
| 交通-能源耦合 | 电动汽车充电负荷与配网协同 | 中高 |
| 碳流追踪 | 统计每个用户的隐含碳排放 | 中等 |
| 分布式求解算法 | 用交替方向乘子法(ADMM)分解大问题 | 高 |
我的个人建议是,先把“电热气耦合 + 二阶锥 + Yalmip”这套基本功做扎实,再往这些方向延展。基底不牢,所有上层建筑都白搭。
最后再分享一点实际心得
第二次跑MISOCP模型遇到求解失败时,不要急着调求解器参数,最有效的做法是“降低复杂度逐层排查”——先跑纯电、再加热、再加气,最后加储能时序。每加一层都用optimize的返回信息确认那层约束本身没问题。这个“分层验证”的习惯,帮我省下的调试时间比我写模型的时间还多。
还有个小技巧:给每个区块的约束取个注释名,比如%% === 电网潮流 ===、%% === 储能SOC ===,一旦求解器报错,Yalmip能通过sol.info定位到出问题的约束组,配合注释能节省大量时间。别小看这个习惯,模型大到几千行约束时,这个操作价值立竿见影。
希望这篇经验能帮到正在啃综合能源优化的朋友。先把二阶锥和Yalmip的配合搞清楚,你在综合能源这条路上就走稳了。