
做综合能源优化调度这个方向绕不开的话题就是冷热电联产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(t1) 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也是不错的替代品。我自己做这个方向几年下来最大的体会是综合能源优化调度的门槛不在代码而在模型。代码只是工具真正需要下功夫的是理解每个设备的物理特性、理解能量耦合的逻辑、理解约束条件的工程含义。很多同学拿到代码第一件事就是改参数看结果遇到不合理的结果却无从下手原因就在于对模型的理解不够深。建议拿到任何一份程序先逐行读代码把每个变量、每个约束对应的物理含义标注出来读明白再动手跑这个过程虽然慢但收益远超预期。最后分享一点优化调度是一个实践性很强的方向光看不练是不行的。拿到示例程序先复现再改模型然后试着从头实现一个简单算例最后再挑战复杂系统。这个过程走一遍不敢说精通但至少遇到问题时心里有底知道该往哪里排查。希望这篇内容能帮你走通这段路。