ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

考虑用户舒适度的冷热电多能互补综合能源系统优化调度MATLAB实现

考虑用户舒适度的冷热电多能互补综合能源系统优化调度MATLAB实现

综合能源系统的调度问题,这几年不管是做园区项目还是写论文,热度都一直很高。不过大部分代码和教程都默认“用户是机器”,只盯着购电成本、燃气成本这些经济指标,最后算出来的调度方案往往让室内温度飘得离谱——夏天省钱省到28度,冬天冻到16度,方案拿去汇报的时候自己都心虚。所以我当时拿到“考虑用户舒适度的冷热电多能互补综合能源系统优化调度”这个题目时,第一反应是:这终于不是纯设备模型堆叠了,而是把“人”的感受写进约束和目标函数里。本文就是我基于一套MATLAB实现代码写下的完整拆解,从建模、目标函数设计、YALMIP+求解器实现,到调试中踩过的坑,一次说清楚。

这套内容适合下面几类人看:准备做综合能源优化调度的研究生,刚入职做园区能源站运维的技术人员,还有想从“纯经济学调度”转向“多目标协调调度”的开发者。代码本身覆盖了冷、热、电三种能源的耦合,设备侧包含燃气轮机、余热回收、吸收式制冷、电制冷、储能电池、蓄热罐等常见单元,调度层面则把用户热舒适指标引入优化。看完你不仅能理解为什么舒适度要进模型,还能直接照着搭出自己的调度框架。

1. 项目解析:为什么冷热电多能互补要和用户舒适度绑定

1.1 先搞清楚“多能互补”到底在补什么

很多第一次接触这个概念的朋友,会把“多能互补”理解成“多种设备并联,哪个便宜用哪个”。这话对了一半,但真正的关键在于“能源梯级利用”和“不同能源之间的时空耦合”。我这里举一个最常见的园区配置:燃气轮机烧天然气发电,发电的同时会产生高温烟气,这部分余热不去利用就白白浪费了;把余热送到溴化锂吸收式机组,夏季可以制冷,冬季可以制热;如果余热还有富余,可以存到蓄热罐里,等热负荷高峰再放出来。与此同时,电网购电、电制冷机、电锅炉等设备提供第二条能源路径。

两条路径之间的“互补”体现在哪?一个是设备层面,燃气轮机发电多了,烟气余热就多,但园区电负荷不一定大,热/冷负荷也不一定匹配,于是需要通过电制冷机、储电、蓄热罐来做解耦和缓冲。另一个是时间层面,电价峰谷波动很大,谷电时段可以多购电、多存冷/热/电,峰电时段少购电甚至卖电,实现时间和空间上的能量搬移。调度模型要做的,就是在一个优化周期内(通常是24小时),决定每一台设备出力多少、储能设备充放多少、和电网交换多少功率,使得总运行成本最低。

1.2 用户舒适度:之前被调度模型丢掉的一块拼图

传统优化调度模型里,冷负荷和热负荷通常被视作“刚性的、必须满足的常数”,也就是说负荷曲线是预测好的,系统只需要按照这个曲线去分配各设备出力就行。但实际用户体感不是刚性的,室内温度哪怕偏离目标两三度,人体往往都能接受,只是舒适度下降。反过来,如果为了省成本把温度控制得过高或过低,用户投诉就会立刻找上门。

所以“考虑用户舒适度”本质上是在说:制冷/供热负荷不该是固定值,而是一个可以和室内温度耦合的柔性区间。你需要把建筑热过程(比如一个简化的一阶热容热阻模型)加入进来,让优化算法自己去权衡:这半小时在制冷侧少花点电费、把目标温度放宽到27度,到底划算不划算;如果因为放宽温度导致舒适度指标超限,对应的惩罚是多少。这样调度结果就不会再出现“省了几百块电费、办公楼里所有人热到中暑”的离谱方案了。

1.3 项目能给出什么结果

这套MATLAB代码最终输出的内容包括:24小时各设备最优出力曲线、储能设备充放电曲线、与电网的购售电计划,以及与用户舒适度相关的室内温度变化曲线、舒适度评价指标(本文以预测平均投票PMV为主)。另外还会做两个对比场景:不考虑舒适度的纯经济调度、考虑舒适度的综合调度,通过成本变化和舒适度变化的量化对比,你就能清楚看到“舒适度约束到底花了多少钱买回来”。

2. 核心模型拆解:设备、负荷、舒适度三块基石

2.1 设备建模:从效率曲线到数学校验

这部分是后续优化模型的地基,设备模型建得不对,后面求解出来的调度方案等于空中楼阁。建模时习惯上把设备分为三类:产能设备、转换设备和储能设备。

先说产能设备,最核心的是燃气轮机。它的关键参数有三个:发电效率、热电比、最小稳定出力。很多简化模型直接让发电量乘以固定热电比得到余热量,这在小规模算例里够用,但稍微严谨一点的做法是考虑部分负荷率的影响——负荷率低于某个阈值时发电效率会明显下降,代码里可以用分段线性函数来处理。发电功率、燃料消耗量、余热回收量三者之间的关系一旦确定,燃气轮机的行为就完全被刻画出来了。

转换设备比如电制冷机和吸收式制冷机。电制冷机的COP(能效比)一般取3到5,吸收式制冷机的COP则低一些,通常在1.2到1.5之间。换句话说,同样是产生100kW冷量,电制冷机只要消耗20到30kW电,吸收式机组要消耗70到80kW热。所以直观上电制冷效率高,但它的运行费用取决于电价;吸收式靠余热驱动,燃料成本已经由燃气轮机承担了,边际成本很低。调度优化的一部分魅力就在于平衡这两种机组的出力搭配。

储能设备包括蓄电池和蓄热罐(有些园区还有冰蓄冷,原理一致)。蓄电池建模需要关注充电功率、放电功率、荷电状态SOC、充放电效率,以及当日始末SOC一致性要求。蓄热罐类似,只是能量形式变成了热量。储能设备的动态方程是优化模型里最容易写错的地方,尤其是充放电效率导致的自损耗问题,后文会专门展开。

2.2 负荷模型:刚性电负荷、柔性冷热负荷

园区负荷可以分成两类。电负荷相对刚性,照明、水泵、办公设备这些很难在调度周期内调节,直接作为已知参数输入。冷负荷和热负荷则要区分“建筑需求”和“能源站供给”——建筑侧的冷/热需求会随着室内温度目标改变而改变,这才是舒适度进入模型的关键通道。

我用的建筑热过程模型是一阶等效热容热阻模型,公式大致是:

C_b * (T_in(t+1) - T_in(t)) / Δt = Q_hvac(t) - (T_in(t) - T_out(t)) / R_b

这个式子物理含义很直白:室内温度变化量等于空调系统注入的热量(夏季则是制冷量取负)减去建筑围护结构向室外散失的热量。C_b是建筑热容,R_b是围护结构热阻,这两个参数可以通过建筑的面积、层高、外墙材质等信息粗略估算。有了这个方程,室温和供冷/供热量之间就不再是固定的一对一关系,而是动态的、可权衡的。

2.3 舒适度指标量化:PMV怎么进入优化模型

PMV是美国供暖制冷空调工程师学会标准里的预测平均投票指标,取值通常在-3到+3之间,0表示不冷不热最舒适,正值为偏热,负值为偏冷。完整计算PMV需要六个参数:空气温度、湿度、风速、人体代谢率、服装热阻、平均辐射温度,相当繁琐。

真正放在优化模型里直接计算完整PMV公式是不现实的,因为它包含非线性指数项,会让求解器崩溃。我采用的简化办法是:在典型室内湿度、风速条件下,将PMV近似表示为室内温度的线性函数,即PMV ≈ α * T_in + β。这个近似在18到30度范围内误差可接受,误差通常不超过0.1个PMV单位。然后把PMV约束在[-0.5, 0.5]之间,就转化成了对室温上下限的约束;或者更柔和一点,在目标函数里加一项舒适度惩罚(PMV - PMV_target)^2。

3. 优化调度模型:目标函数与约束条件的完整设计

3.1 目标函数:经济成本和舒适度惩罚怎么配比

这一层是整个程序最核心的逻辑。我先给出目标函数的标准写法,然后解释每个部分的作用:

min Σ[ C_gas(t) + C_grid_buy(t) - R_grid_sell(t) + C_om(t) + C_comfort(t) ] * Δt

其中C_gas是燃气轮机燃料成本,按天然气耗量乘以气价计算;C_grid_buy是向电网购电成本,按购电功率乘以分时电价;R_grid_sell是售电收益,如果系统允许余电上网的话;C_om是设备运行维护成本,一般按发电量和制冷量线性折算;C_comfort是舒适度惩罚项,这是本文区别于普通经济调度的关键。

C_comfort(t) = λ * (PMV(t) - PMV_set)^2

这里PMV_set设为0,λ是权重系数。为什么用平方项而不用绝对值?因为平方项对“偏离量”的惩罚是非线性的,室内温度偏离越大,边际惩罚越重,这样优化求解器会自动尽量避免极端温度;而绝对值函数在零点不可导,对梯度类算法不够友好。λ的取值直接影响调度倾向:λ设0,模型退化为纯经济调度;λ非常大,模型几乎把温度锁死在目标值附近,运行成本随之上升。实操中我会跑一组不同λ的对比实验,画出一条“成本-舒适度”之间的权衡曲线,然后由决策者根据实际偏好选点。

3.2 约束条件:等式约束和不等式约束逐条过

约束这部分是代码量最大、最容易出bug的地方。我按类别梳理:

第一类是功率平衡约束。电功率平衡要求燃气轮机发电、蓄电池放电、电网购电三者之和等于电负荷、电制冷机耗电、电锅炉耗电、蓄电池充电、电网售电之和。冷功率平衡要求电制冷和吸收式制冷的供冷量满足建筑冷负荷。热功率平衡则要处理燃气轮机余热回收、蓄热罐放热、电锅炉制热和建筑热负荷之间的关系。

第二类是设备出力约束。每台设备有出力上下限,部分设备还有爬坡约束,即相邻时段出力变化不能超过设定值。爬坡约束在燃气轮机上尤其重要,可以避免调度方案给出“这一小时满发、下一小时停机”的剧烈波动指令。

第三类是储能约束。蓄电池SOC在0.2到0.9之间,每日始末SOC相等;蓄热罐蓄热量在上下限之间,同样要求周期始末一致。充放电不能同时进行,这里需要引入0-1变量(否则模型会同时充电和放电“套利”)。蓄热罐的充放热状态类似。

第四类是舒适度相关约束。包括室内温度上下限约束、简化PMV取值区间约束。如果目标函数里已经有舒适度惩罚项,那么这部分可以放松为仅保证PMV在[-1, 1]之间,保证不会出现完全离谱的温度;如果采用硬约束方式,则可以严格设到[-0.5, 0.5]。

3.3 求解思路:为什么选YALMIP加商业求解器

模型建完,里面既有连续变量(各设备出力、温度),又有0-1变量(设备启停和储能充放状态),整体属于混合整数规划问题。目标函数含二次项,约束大部分是线性或分段线性的,所以是混合整数二次规划(MIQP)。建模我选YALMIP,它是MATLAB下的优化建模工具箱,接口做得非常干净,你不需要手写标准形式的系数矩阵,直接写表达式、加约束、然后调用求解器就行。

求解器我用的有两类:一类是CPLEX或Gurobi这类商业求解器,性能和数值稳定性都很好,MIQP问题求解规模可以到几十万个变量;另一类是开源的CBC或SCIP,适合中小规模算例,虽然慢一些但胜在免费。如果你的项目规模不大(24时段、10台设备以内),开源求解器完全够用;如果规模大了、变量上万,还是建议用商业求解器。需要注意,YALMIP只是建模层,求解器要自己另外安装并添加路径。

4. MATLAB代码实现:从参数设置到核心调度循环

4.1 工程目录与代码文件划分

按我当时搭这套代码的习惯,工程目录分成四块:./data放负荷曲线、电价参数、设备参数;./model放设备模型函数;./optimization放优化建模主程序;./result放输出图表。模块化拆开的好处是后期调参数不用在几百行的大脚本里翻来翻去。

主程序运行流程如下:先读取并初始化所有参数,然后构建决策变量(连续变量用sdpvar,整数变量用binvar),接着逐条添加约束,再写入目标函数,最后调用optimize函数求解。求解完成后对结果做后处理——把优化变量还原成设备出力序列、室内温度序列,并计算成本项拆分、画图。

4.2 关键参数怎么取

直接抄作业的话,可以按下面这张表设置一个典型的小型园区算例:

参数取值备注
调度周期24小时 / 1小时间隔可根据需要改15分钟
燃气轮机容量1000 kW发电功率上限
发电效率0.35部分负荷率越高效率越高
热电比1.5余热回收功率/发电功率
吸收式制冷COP1.3余热驱动
电制冷COP4.0分体式/离心式典型值
蓄电池容量500 kWh充放电效率各0.95
蓄热罐容量800 kWh周期始末一致
室内温度范围20~26度对应PMV硬约束
舒适度权重λ1.0通过多场景对比确定

电价采用分时电价:峰段1.2元/kWh、平段0.8元/kWh、谷段0.4元/kWh,天然气价格按2.5元/m3折算,1立方米天然气热值约9.7kWh。负荷曲线用模拟数据,夏季典型日电负荷峰值约1800kW、冷负荷峰值约1200kW。

4.3 核心代码:YALMIP建模的关键片段

下面给出优化模型构建阶段最核心的一段参考代码(节选,完整代码很长这里放不下):

%% 定义优化变量 P_gt = sdpvar(1, 24); % 燃气轮机发电功率 P_gt_om = binvar(1, 24); % 燃气轮机启停状态 P_ec = sdpvar(1, 24); % 电制冷机输入电功率 Q_ac = sdpvar(1, 24); % 吸收式制冷机供冷功率 P_grid_buy = sdpvar(1, 24); % 购电功率 P_bat_d = sdpvar(1, 24); % 蓄电池放电功率 P_bat_c = sdpvar(1, 24); % 蓄电池充电功率 SOC = sdpvar(1, 25); % 蓄电池荷电状态 T_in = sdpvar(1, 24); % 各时段室内温度 %% 约束条件 Constraints = []; % 电功率平衡 Constraints = [Constraints, P_gt + P_bat_d + P_grid_buy - P_bat_c ... == P_load_e + P_ec + P_eb]; % 冷功率平衡(忽略网络损耗) Constraints = [Constraints, Q_ec * COP_ec + Q_ac == P_load_c]; % 蓄电池SOC递推(充放电不能同时,用0-1变量v_bat) Constraints = [Constraints, SOC(1) == SOC_init]; Constraints = [Constraints, SOC(t+1) == SOC(t) ... - P_bat_d(t) / eta_d / Cap_bat ... + P_bat_c(t) * eta_c / Cap_bat]; Constraints = [Constraints, SOC <= 0.9, SOC >= 0.2]; % 舒适度约束:PMV近似线性函数 Constraints = [Constraints, a_pmv * T_in + b_pmv >= -0.5]; Constraints = [Constraints, a_pmv * T_in + b_pmv <= 0.5]; % 室内温度动态 Constraints = [Constraints, C_b * (T_in(t+1) - T_in(t)) / dt ... == Q_hvac(t) - (T_in(t) - T_out(t)) / R_b]; %% 目标函数 Objective = sum(fuel_cost * fuel_cons * dt) ... + sum(price_buy .* P_grid_buy * dt) ... - sum(price_sell .* P_grid_sell * dt) ... + sum(lambda * (a_pmv * T_in + b_pmv).^2 * dt); %% 求解 ops = sdpsettings('solver', 'gurobi', 'verbose', 2); optimize(Constraints, Objective, ops);

上面这段是高度浓缩的骨架,实际运行时还需要给每条约束加索引循环,把0-1变量的互斥关系(充电和放电不同时)写成两个不等式约束。值得强调的是,温度动态方程里的Q_hvac(t)在夏季制冷模式下是个负值,因为空调从室内取走热量,这个方向搞反了会导致整个模型无解或者出现“加热和制冷同时开启”的离谱结果。

4.4 调度结果长什么样

以夏季典型日为例,跑完代码后你能看到这样几个现象:谷电时段(23点到次日7点),蓄电池会大量充电,电制冷机也会满负荷运行,把冷量提前存到蓄冷罐/通过建筑蓄冷把室温压到目标下限附近;峰电时段,燃气轮机接近满发,余热驱动吸收式制冷机组供冷,电制冷机降载甚至停机,蓄电池放电支撑电负荷。室内温度变化曲线在24度附近小幅波动,PMV始终处在-0.5到0.5区间。

对比纯经济调度方案,你会发现不考虑舒适度的方案会把室内温度顶到接近27.5度,对应的总运行成本确实降了一些,但舒适度指标已经到了+0.8,属于“明显偏热、部分人感到不舒适”的区间。舒适度约束加进去后,总成本可能上升5%到8%,换来的是全天室内温度保持在舒适区内。这个成本增幅就是“舒适度的价格”,项目汇报时这张对比图非常有说服力。

5. 常见问题与排查实录:这套代码的坑我替你踩了一遍

5.1 求解器报“Infeasible”问题

这是新手碰上概率最高的报错。原因九成以上出在约束条件互相矛盾,比如我一开始把室内温度上限设为24度,但同时把冷负荷平衡里的供冷量上限设置得过低,导致方程无解。排查方法是逐条注释约束,二分定位到具体是哪一组约束导致不可行。还有一个简便操作:把硬约束改成带松弛变量的软约束,比如温度上限写成 T_in <= T_max + slack,并在目标函数里给slack加一个很大的惩罚系数,这样即使约束有冲突也能解出来,同时你能从松弛变量的取值判断哪个约束最紧张。

5.2 储能SOC初始值和周期一致性设错

蓄电池约束如果写成SOC(1)=0.5,但没有加最后的SOC(24)=0.5,循环起来你会发现电池为了追逐峰谷价差,从初始0.5一路放到0.2,因为没有“第二天还要用”的约束。如果加了周期一致性但条件写反了,也可能出现求解器为了让SOC周期相等而强行让设备出力扭曲的情况。我的建议是:先不加周期约束跑一遍,观察SOC自然变化轨迹,看看这个轨迹是否合理,再决定周期末SOC该定多少。如果希望保留每日循环,就把SOC(25)=SOC(1)写进约束(因为状态变量有25个点,SOC(1)对应第0时段的初始状态)。

5.3 舒适度权重λ怎么调都别扭

λ调太小时,模型还是紧抱经济指标不放,温度照样失控;λ调太大时,目标函数里舒适度惩罚项占绝对主导,整个调度变成“不惜一切代价维持恒温”,运行成本飙升。这个问题没有万能解,但有一个比较好的调参路径:先跑λ=0,得到纯经济调度的总成本和平均PMV偏离量;再跑一个λ很大比如100的极限,得到最低PMV偏离量和对应成本;然后在这两个极端点之间用二分法找几个中间点,画出一张“成本-舒适度Pareto曲线”。一般取曲线拐点附近的λ值,性价比最高。

5.4 充放电同时发生

如果没有用0-1变量约束充放电互斥,优化求解器经常会给你一个“同时充电和放电”的结果,比如蓄电池一边以50kW放电、一边以50kW充电,从SOC动态方程看完全没有净变化,但目标函数里却因为两种功率都为正而产生不必要的损耗项(或者在某些公式变形中反而能套到补贴),结果既不合理又可能报错。解决办法是引入二进制变量v_bat,满足:P_bat_c <= v_bat * P_bat_c_max, P_bat_d <= (1-v_bat) * P_bat_d_max。蓄热罐同理。

5.5 室内温度动态方程方向出错

这个坑我记忆特别深。夏季制冷模式下,空调向室内注入的是“负热功率”。如果代码里把Q_hvac(t)当作正值加到温度动态方程右侧,计算结果会变成室内温度越来越高,然后模型为了让温度不超过上限,只能让制冷功率为负值——也就是反向加热,整体乱套。所以务必在代码里分清制冷和制热两个模式,或者用统一的正负号约定:温度动态方程右侧为“注入室内的净热功率”,制冷时取负值,制热时取正值。省略这一步会导致失败。

6. 多场景对比与扩展方向:这套框架还能干什么

6.1 场景一:不同舒适度权重下的帕累托前沿

做这个对比的价值在于,你可以给决策者一个直观的选择依据,而不是扔出一句“我把舒适度约束设为±0.5”。具体实现起来非常简单:外层循环设定λ序列(比如0、0.2、0.5、1、2、5、10),每次循环调用一次优化求解,把总运行成本和平均绝对PMV偏差记录成两个数组,最后用plot画散点图。你会得到一条从左下到右上的曲线,左端是纯经济(低成本高不适),右端是纯舒适(高成本低不适)。项目评审时把这张图放出来,比说一百句“我们考虑了舒适度”都有用。

6.2 场景二:改变电价结构观察设备调度策略变化

把分时电价的峰谷差从0.8元扩大或缩小到0.4元、1.2元,观察蓄电池充放电策略和燃气轮机出力策略的变化。峰谷差越大,储能就越值得多充多放,燃气轮机在峰段发电的经济性也会更突出。这个实验本质上是在验证模型对价格信号的响应能力,如果你做的系统后续要参与需求响应或现货市场,这样的灵敏度分析非常有参考价值。

6.3 扩展方向:从日前调度走向滚动优化

目前这套代码做的是日前开环调度,假设未来24小时负荷、电价完全已知。实际上电价和负荷都有不确定性,更贴近工程需求的做法是改成模型预测控制:每15分钟或1小时滚动一次,用未来4小时或24小时的预测数据滚动求解,只执行当前时段的调度指令,下一周期再重新优化。代码改造并不难,核心是把约束里时段数从24改成预测时域H,同时保留当天的储能初始状态。这样就有了闭环反馈,抗扰动能力会好很多。

6.4 扩展方向:把碳排放入模型

国家层面的双碳背景下,很多园区项目在成本目标之外还要求降低碳排放,这相当于额外增加一个“碳排放成本”项。代码上需要做的事情只有两件:第一,给电网购电、燃气消耗分别加碳排放因子;第二,在目标函数中加入“碳价×碳排放量”的惩罚项。调度结果会很明显地倾向于在燃气轮机和电网购电之间重新分配——燃气轮机碳排放显著低于燃煤火电,模型会自发提高燃气轮机利用率、降低购电比例。

7. 最后说几点实在的体会

做完这套项目后,我对综合能源调度有了几个很深的感受。第一,模型的复杂度和可解释性之间需要平衡,YALMIP让你什么约束都能往上加,但每多加一类约束,排查问题的时间可能增加一倍。如果精力有限,优先把设备模型和储能约束做扎实,舒适度可以先做硬约束版本,跑通了再加惩罚项,千万不要一上来就上满配模型。第二,参数标定的重要性不亚于优化算法本身,热电比、COP、效率这些参数如果是从样本手册上瞎抄的,算出来的调度策略拉闸就废。如果有条件,哪怕用园区一个月的实际运行数据做简单的均值校准,都会比拍脑袋好得多。第三,把结果画成图真的很有帮助。我不止一次通过画设备出力堆叠图发现“燃气轮机凌晨三点莫名满发”这种逻辑上根本解释不通的调度行为,原因追查下来都是约束方向或者参数单位的问题。

最后再分享一个小技巧:给每一个时段的设备出力、储能SOC、室内温度单独画子图,并且加上当日电价曲线作为双纵轴对照。调度方案是否合理,一眼就能看出来——如果电制冷机在电价峰段满负荷运行,蓄电池在谷段大量放电,那多半就是模型约束或者目标函数里漏掉什么了。调参调试时,这套可视化脚本比任何调试器都顺手。希望你在自己的项目里也能少踩几个坑,早点跑出既省钱、又让人体感舒服的调度方案。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进