做综合能源优化调度这个方向,绕不开的话题就是冷热电联产(CCHP)和微网优化调度。这两年问这类问题的朋友明显多了,手里要么是导师丢一个Matlab程序让跑通,要么自己想从零搭一套完整的冷热电联产优化模型却不知道从哪里下手。这篇内容我打算结合自己做过的综合能源系统优化调度程序,把冷热电联产建模、优化目标设计、约束条件写法、Matlab求解实现这些关键节点逐一拆开讲清楚,顺便把微网优化调度的学习路径也梳理一遍,希望能让刚接触这个领域的朋友少走点弯路。
1. 冷热电联产系统的优化调度到底在做什么
1.1 先搞清楚系统里有哪些设备
综合能源系统的核心特征是多能互补。一个典型的冷热电联产系统,通常包含燃气轮机或内燃机这样的原动机、余热锅炉、吸收式制冷机、燃气锅炉、电制冷机,以及电储能和蓄热/蓄冷装置。燃气轮机燃烧天然气发电,发电后的高温烟气进入余热锅炉产生蒸汽或热水,这部分热能可以用来供热,也可以驱动吸收式制冷机产生冷水。电制冷机则消耗电能来制冷,燃气锅炉作为补热设备在余热不足时启动。
这套流程单看每个设备都不复杂,真正复杂的是设备之间的耦合关系。发电、供热、制冷这三条能量流在系统里交错在一起,一个设备的出力变化会影响好几个下游环节。比如燃气轮机多发电,烟气余热就多,可供的热量和冷量也随之增加,但与此同时天然气的消耗量也会上升。调度程序要做的,就是在满足电负荷、热负荷、冷负荷的前提下,决定每个时段各设备的出力是多少,让总运行成本最低,同时兼顾碳排放和可再生能源消纳,这就构成了一个典型的优化调度问题。
1.2 优化调度问题的数学本质
用数学语言描述,调度问题是一个带约束的混合整数线性规划(MILP)问题。决策变量包括各机组的连续出力值、储能设备的充放功率,以及设备启停状态这样的0-1整数变量。目标函数一般是运行成本最小化,包括购电费用、天然气费用、设备启停费用、运维费用,加上弃风弃光的惩罚项。约束条件包含功率平衡约束、设备出力上下限、爬坡速率约束、储能SOC约束等。
很多同学第一次看到这个模型会觉得复杂,其实拆开来看就是"目标函数加约束条件"的经典框架。做综合能源优化调度,本质上是在做一件事:把物理系统的运行规则转化为数学表达式,然后交给求解器去算。Matlab在这个领域之所以流行,是因为它有YALMIP这样的建模工具箱,可以很直观地把优化问题描述出来,再调用cplex、gurobi等商业求解器进行求解,整个流程比直接写求解算法要省力太多。
2. 系统建模环节:从物理设备到数学表达式
2.1 供给侧设备的建模方法
可以把系统建模分成供给侧、转换侧、负荷侧和储能侧四个部分来看。
供给侧首先是燃气轮机,它的核心参数是发电效率η_ge和热电比。燃气轮机的燃料消耗量可以用发电功率除以发电效率来估算,而余热回收量则由热电比乘以发电功率得到。比如某一型号的燃气轮机额定发电效率为30%,热电比为1.3,那么发1kW电大约要消耗3.33kW的天然气化学能,同时回收约1.3kW的热量。
余热锅炉的建模比较简单,它的输出热功率是余热回收量乘以换热效率,通常取值在0.8到0.9之间。吸收式制冷机的性能用COP来描述,一般单效机组COP在0.7左右,双效机组可以到1.3。燃气锅炉本质上就是一个热源,效率通常在0.85到0.92。电制冷机的COP一般在3到5之间,比吸收式制冷效率高得多,这也是夏季时段用电制冷优先级往往高于吸收式制冷的根本原因。
写代码的时候,这些参数都会汇总到一个参数结构体里。我习惯把每个设备的效率、容量、爬坡速率、启停成本都定义成可配置的参数,方便后期做参数敏感性分析。第一次搭模型的人容易犯的错误是把效率定义成一个固定标量,实际上部分设备的效率会随负载率变化,更精细的模型可以查设备厂商提供的效率曲线,用分段线性函数来拟合,精度提高不少,代价是模型规模会增大。
2.2 负荷侧与储能侧的关键细节
负荷侧的数据是优化问题的输入边界。典型日的电负荷、热负荷、冷负荷曲线通常按小时划分,一天24个时段,工程上也有用15分钟一个时段、一天96个点的做法。时段划分越细,结果越接近真实运行情况,但求解规模成倍增加。做研究或者做课程设计,24时段已经足够说明问题。
储能设备是让调度问题产生时间耦合的关键。蓄电池的建模核心是SOC递推方程:SOC(t+1) = SOC(t) + η_ch * P_ch(t) * Δt / E_cap - P_dis(t) * Δt / (η_dis * E_cap)。这个公式看着简单,但要注意充放电效率不对称的情况,铅酸电池和锂电池在充放电效率上有明显差异,建模时务必分开设置。蓄热水箱的建模思路类似,多了热损耗项,通常用损耗系数乘以温差来近似。储能在优化中发挥的作用是削峰填谷:电价低的时候充能,电价高的时候放能;光伏大发的时候充电,晚上负荷高峰再放电。这些现象都会在调度结果中自然呈现,也是判断模型是否合理的直观依据。
2.3 能源集线器模型的作用
在综合能源系统分析中,能源集线器(Energy Hub)是一个重要的建模视角。它把系统看作一个多输入多输出的黑箱,输入是天然气、电网购入电力、太阳能等,输出是电负荷、热负荷、冷负荷,中间通过耦合矩阵描述能量转换关系。形式化的表达是L = C * P,其中P是输入功率向量,L是输出负荷向量,C是耦合矩阵。这个框架的优点是物理意义清晰,特别适合做多能源系统的静态分析和规划层面的优化。实际做调度优化时,我会用能源集线器的思想来梳理功率平衡关系,但具体实现还是拆成每个设备的详细模型来写,这样灵活性和可扩展性更好。
3. 目标函数与约束条件的完整设计
3.1 运行成本最小的目标函数怎么写
以最常见的单目标优化为例,目标函数包括购电费用、燃料费用、运维费用和启停费用四个部分。
购电费用是电网交互功率乘以分时电价。分时电价数据一般分峰平谷三个时段,各地区的电价政策不同,实际项目里要按真实数据来设定。天然气费用是燃料消耗总量乘以天然气单价。运维费用用单位出力的运维成本系数乘以出力来近似,这个系数从设备资料里查,不同设备差异很大,燃气轮机大约在0.01元/kWh量级,储能设备的运维成本会高一些。
启停费用在日前调度中通常简化处理,因为启停费用影响的主要是机组组合决策。有些模型还会加入碳排放目标,把CO2排放量乘一个碳价系数并入目标函数,形成"经济性+环保性"的加权目标。需要留意的是多目标问题的量纲问题:成本是万元量级,碳排放是吨量级,直接加权会导致数值上完全被某一项主导,通常要归一化处理,或者用多目标优化的约束法来处理。
3.2 约束条件里最容易踩坑的几个点
约束条件方面,除了最基本的电热冷功率平衡约束,还有几类约束特别容易出错。
机组出力约束不能只写上下限,还要注意爬坡约束。燃气轮机的爬坡速率通常写成每15分钟或每小时的最大出力变化量,如果漏掉这个约束,求解器给出的调度曲线会出现相邻时段出力跳变的"锯齿形"结果,物理上根本无法实现。
储能约束最容易被忽略的是同一时段不能同时充放电。如果没有耦合约束,优化器为了"套利"可能在同一时段既充电又放电,这在物理上是无效的。标准处理办法是引入0-1变量:充电状态和放电状态互斥,P_ch(t) ≤ Critical_M * u_ch(t),P_dis(t) ≤ Critical_M * (1-u_ch(t)),其中Critical_M是一个足够大的常数,术语上叫大M法。这个技巧几乎所有储能优化模型都会用到。
电网交互约束同样需要区分购电和售电。现阶段微网项目大多是"自发自用、余电上网"模式,购电和售电价格不对称,通常售电价低于购电价。这种不对称价格结构下,必须用两个非负变量分别表示购电功率和售电功率,否则优化会利用价格差做"低买高卖"的无风险套利,得出荒谬的结果。这个点在设计目标函数时就要考虑进去,约束里也要加上购售电互斥约束。
变量边界条件的量纲统一也是个容易被忽视的问题。功率变量用kW还是MW,能量变量用kWh还是MWh,价格用元/kWh还是元/MWh,必须全程一致。我遇到过一位朋友的程序跑出的结果数值大得离谱,排查半天发现是天然气热值单位和价格单位差了1000倍。这类问题求解器不会报错,结果也长得像模像样,但物理量完全不对。
4. Matlab程序架构与求解实现
4.1 程序整体结构与数据流
整个Matlab程序可以按照功能划分为几个模块:参数初始化、数据读取、模型构建、求解、结果后处理。
参数初始化模块里定义所有设备参数和系统参数,包括设备容量、效率、爬坡速率、分时电价、天然气价格、碳排放系数等。数据读取模块负责从Excel或者mat文件里读取负荷曲线和可再生能源出力曲线。模型构建模块是核心,用YALMIP工具箱定义决策变量、目标函数和约束条件。求解模块调用求解器并处理异常情况。结果后处理模块负责画图、输出表格、计算各项经济性和能耗指标。
我在实际项目中会把参数分成两类:一类是固定不变的配置参数,直接写在脚本里;另一类是场景相关的数据,比如不同季节的负荷曲线,放到Excel文件中读取。这样做的好处是换一个典型日数据,不需要改代码,只改输入文件就行。分享给同学或者写论文的时候,这个习惯会省很多沟通成本。
4.2 用YALMIP建模求解的实践细节
用YALMIP定义变量是建模的第一步。连续变量用sdpvar定义,例如P_gt = sdpvar(1, 24)定义了一个1行24列的变量向量,表示燃气轮机24个时段的出力。整数变量和二进制变量用binvar和intvar定义。储能充放电状态变量就是典型的binvar。
约束条件用循环语句逐时段生成,然后用 [] 拼接起来。比如电功率平衡约束可以写成下面这个形式:
constraints = []; for t = 1:24 constraints = [constraints, P_gt(t) + P_pv(t) + P_buy(t) - P_sell(t) + P_dis(t) - P_ch(t) == P_load(t)]; end目标函数的定义比较直接,就是把各项成本累加。天然气成本要注意燃气轮机和燃气锅炉是两套独立的消耗,加总的时候别漏项。定义好目标和约束后,调用optimize函数求解:
ops = sdpsettings('solver', 'cplex', 'verbose', 2, 'showprogress', 1); result = optimize(constraints, objective, ops);求解完成后用value函数提取变量结果,比如P_gt_opt = value(P_gt),再进行后续分析。这里的技巧在于求解器选项的设置:diagnostics选项会返回求解状态信息,如果问题不可行,可以通过diagnostics信息初步定位问题来源。
求解器的选型需要单独说一句。cplex和gurobi是目前工业界用得最多的两个求解器,功能强大,但对学生来说最现实的问题是license。好消息是这两家都有面向学术用户的免费授权,用自己的学校邮箱申请就行。如果暂时申请不到商业求解器,YALMIP内置支持的开源求解器glpk也能求解MILP问题,小规模算例完全够用,只是大规模问题时求解速度差距很大。我个人的建议是:学习阶段先装glpk跑通流程,等模型复杂度上来了再换gurobi或cplex。
模块化脚本设计之外,还要强调一下注释习惯。调度程序涉及大量变量和约束,变量命名尽量用有意义的缩写,比如P_gt表示燃气轮机出力,SOC表示储能状态。我见过很多同学的代码用x1、x2这种命名方式,跑通一时爽,回头改模型的时候根本分不清哪个是哪个,代价非常大。
4.3 结果可视化与经济性分析
结果可视化是检验模型合理性的基本手段。常用的图包括各设备出力堆叠图、电/热/冷平衡图、储能SOC曲线、电网交互功率曲线。把各设备的出力和对应负荷画在一起,一眼就能看出功率平衡是否存在缺口、储能充放电是否符合预期规律。
经济性分析方面,常用的指标有系统日运行成本、单位供电成本、一次能源节约率(PESR)和CO2减排率。PESR是比较联供系统和传统分供系统的指标,计算公式为PESR = (F_sp - F_cchp) / F_sp,其中F_sp是分供系统的燃料消耗,F_cchp是联供系统的燃料消耗。做方案对比的时候,通常会把冷热电联产系统与"电网购电+电锅炉+电制冷"的分供方案放在一起比较,计算PESR和减排率,这两个指标是综合能源系统论文里出现频率最高的结果。
我在写程序的时候会在结果输出部分自动生成一份汇总表,列出各设备的总出力、总能耗、总成本和各类指标。这样做既方便自己对比方案,也方便直接复制数据到论文里使用。汇总表用Matlab的writetable输出到Excel,格式对齐,后期整理报告会很省心。
5. 微网优化调度基础学习路线建议
5.1 从经典经济调度入门
如果之前没有接触过优化调度,建议先不要直接上冷热电联产这种多能耦合模型。微网优化调度有一个经典的学习阶梯:先做纯电微网的经济调度(ED),再过渡到机组组合(UC),然后加储能,最后扩展到多能互补。
第一步的纯电微网模型非常简单,系统里只有燃气发电机、光伏、储能和电网交互。目标函数就是运行成本最小,约束只有功率平衡、机组出力和储能SOC。这个模型用YALMIP写下来不超过100行代码,却包含了优化调度的所有核心要素。把这100行跑通、看懂、会改,后面的多能系统只是在设备数量和耦合关系上做加法。
5.2 逐步扩展储能与多能互补
纯电微网跑通后,第二步加储能环节。储能引入了时间耦合,你会发现SOC递推关系和充放电互斥约束带来的麻烦,这是理解动态优化的一次很好的训练。第三步可以考虑把电锅炉或热泵加进去,形成简单的热电联供。此时热负荷和电负荷之间出现耦合,你需要处理热功率平衡约束和设备的电热转换关系。最后再加吸收式制冷机,形成完整的冷热电三联供系统。
每走一步,建议都把前一版的代码备份一份。用git做版本管理,这是很多做仿真的人容易忽略的习惯。改坏模型是常态,能回退到上一个能跑通的版本,心情会好很多。
5.3 进阶方向:不确定性处理与多目标
学会了确定性优化调度,进阶方向大致有三条。第一条是处理不确定性:光伏出力和负荷预测都有误差,需要用鲁棒优化或随机规划来处理,引入场景法或模糊集。第二条是多目标扩展:在成本目标之外加入碳排放目标或能源利用率目标,用加权法或者epsilon约束法处理。第三条是考虑更复杂的运行约束:包括最小启停时间、备用容量约束、需求响应等。
这三条路线都不简单,但都是研究和工作中的常见需求。不管走哪条路线,基础的确定性MILP模型都是"地基",务必先把地基打牢。
6. 常见问题与调试实录
6.1 求解器提示不可行怎么办
这是出现频率最高的问题。第一天跑模型的新手遇到"problem is infeasible"基本都会慌,其实排查思路是固定的。
先看约束是否矛盾。最常见的矛盾来源是发电机最大出力加储能最大放电功率仍然小于负荷峰值,也就是说任何时刻都无法满足功率平衡。这种情况需要放宽机组容量或者调整负荷数据,在数学上就是加大约束的可行域。
再看储能SOC递推是否合理。SOC初值、每个时段的充放电量和SOC上下限必须满足累计约束。如果一天内储能充入的总能量远大于容量,SOC边界必然被突破,模型报不可行是必然结果。调试方法很简单:把SOC约束单独拆出来检查,看一天累计的充电量和放电量是否在容量允许范围内。
第三种情况是二进制变量的互斥约束设置错误。比如充电状态和放电状态的互斥约束如果写反了,会导致所有时段要么只能充电要么只能放电,与优化目标冲突时无解。检查这类问题可以用一个技巧:把目标函数临时改为常数0,只求解可行性问题,如果这样都不行,说明约束本身有矛盾;如果可行,说明是目标函数与约束之间存在数值或逻辑上的冲突。
6.2 结果不合理:异常曲线的几个原因
模型能求解成功,但结果曲线"看起来不对劲",这种情况更隐蔽。常见的异常包括:燃气轮机出力恒定在某一平台值且不随负荷变化;储能从不在峰时放电;购电量明显高于实际需求。
这类问题十有八九出在价格数据上。如果购电价格设置成固定值而不是分时电价,优化器没有削峰填谷的动力,储能就会整个调度周期内纹丝不动地保持在初始SOC。如果天然气的单位热值成本算错了,燃气轮机可能完全不启动,整个系统退化成纯购电模式,联供系统的优势完全体现不出来。
调试时建议先把目标函数展开成逐项核对。写一个调试脚本,计算出目标函数里每一项的量级,和负荷数据、电价数据做一次数量级对比,很多问题都是一眼就能看出来的。我遇到过最离谱的一次是天然气价格单位填错,导致燃料成本比购电成本还低一个数量级,结果优化器让燃气轮机全功率运行,富余电力大量卖给电网,从物理上看简直是"永动机",实际上就是单位写错造成的。
6.3 代码实现层面的易错点排查
代码层面的易错点集中在几个地方。第一是向量维度不匹配,负荷数据是24行1列,变量定义成1行24列,矩阵加减时Matlab会隐式扩展或者直接报错,务必在定义变量和数据读取后统一维度。第二是逗号和分号混淆,这个低级错误但非常常见,尤其在循环体里写矩阵拼接的时候,一个符号错了结果完全不对。
第三是参数初始化遗漏。有的参数在约束条件里用到了但没在参数定义段赋值,变量残留上一次运行的值。对于反复调整参数的程序,建议脚本每次运行都清空工作区,或者用结构体统一存储参数,避免散落的全局变量造成干扰。
6.4 调试利器:用测试算例代替完整算例
最后分享一个独家调试习惯:准备一个极简测试算例,比如把24时段缩减到4个时段,只有一台发电机和一组储能,负荷曲线设成阶梯形状,电价设成两种谷峰交替。如果这种简化算例都无法得到合理结果,说明模型逻辑存在问题;如果简化算例表现正常,再逐批放回复杂设备。
这个小算例的调试速度快到毫秒级,而且因为你完全清楚最优解应该长什么样,一旦结果不符合预期,可以快速定位到具体的约束或目标函数项。完整算例一跑几十秒甚至几分钟,一条条试错效率低得多,测试算例的方式能节省大量时间。这是我做综合能源优化调度以来最实用的调试技巧,没有之一。
7. 程序扩展方向与实践心得
跑通一个基础版的冷热电联产优化调度程序只是第一步。实际项目里需求往往是动态变化的,程序架构的扩展能力决定了后续的改造成本。
一个常见的扩展是加入碳捕集设备或者Power-to-X环节,此时能量流从电热冷三条扩展到电热气三条甚至更多。另一个高频需求是接入实际运行数据做滚动优化,也就是把日前调度程序改造成模型预测控制框架,每隔15分钟到1小时滚动求解一次。这个方向结合了优化和控制的思路,是工程落地的关键技术路径,做微网能源管理系统的朋友可以重点关注。
还有一个扩展方向是多微网互联。多个微网之间可以互相交换功率,形成微网群,调度问题从单主体优化变成多主体博弈。常用的求解框架包括交替方向乘子法(ADMM)和纳什均衡逼近等,数学要求明显更高,但研究成果也更丰富。
程序本身的技术栈方面,如果算例规模持续增大,可以考虑把Matlab和Python结合使用。Matlab负责YALMIP建模求解,Python负责数据预处理和后端服务,很多工业级能源管理平台就是这么做的。这个迁移成本不高,但收益很可观。若想进一步推广,基于Python的pyomo或者JuMP也是不错的替代品。
我自己做这个方向几年下来,最大的体会是:综合能源优化调度的门槛不在代码,而在模型。代码只是工具,真正需要下功夫的是理解每个设备的物理特性、理解能量耦合的逻辑、理解约束条件的工程含义。很多同学拿到代码第一件事就是改参数看结果,遇到不合理的结果却无从下手,原因就在于对模型的理解不够深。建议拿到任何一份程序,先逐行读代码,把每个变量、每个约束对应的物理含义标注出来,读明白再动手跑,这个过程虽然慢,但收益远超预期。
最后分享一点:优化调度是一个实践性很强的方向,光看不练是不行的。拿到示例程序先复现,再改模型,然后试着从头实现一个简单算例,最后再挑战复杂系统。这个过程走一遍,不敢说精通,但至少遇到问题时心里有底,知道该往哪里排查。希望这篇内容能帮你走通这段路。