简介:面向能源、电气等相关专业毕业设计与综合能源系统优化研究的完整 MATLAB 源程序,对应论文《考虑P2G和碳捕集设备的热电联供综合能源系统优化调度模型》。模型同时耦合热电联产(CHP)、电转气(P2G)和碳捕集(CCS)单元,并在优化目标中纳入碳交易机制,可用于分析不同低碳技术组合下的系统运行成本与碳排放变化。代码完整复现了论文提出的四种算例场景:不含P2G/CCS/碳交易、仅含P2G、含P2G+CCS、含P2G+CCS+碳交易,每套场景均有独立脚本,运行主程序即可得到对应出图结果,适合作为毕业设计仿真支撑或论文复现基础。压缩包共10个文件,包含6个m脚本(主程序、四个算例模型及绘图函数)和4张PNG结果图,整体约894KB,轻量便于学习与二次修改。目前已有210人浏览学习,代码层次清晰、可读性好,对理解P2G与CCS耦合建模、碳交易机制下的综合能源系统优化调度有直接参考价值。
1. 拿到124号资源后先别急着跑:这套IES调度模型到底在算什么
刚拿到124号资源包里这份《考虑P2G和碳捕集设备的热电联供综合能源系统优化调度模型》的配套源程序,大多数人第一反应是直接跑main,看能不能蹦出论文里那几条平滑曲线。我建议你先缓一下,回答三个问题:P2G把电变成了什么?碳捕集设备在优化里是环保约束还是收益来源?目标函数里的碳交易成本是正是负?想清楚这三件事,代码里的变量和约束才看得懂,改参和排错才找得到方向。
这套模型的本质是把碳交易机制变成调度信号:碳捕集把烟气里的CO2变成P2G的原料,P2G把弃风变成天然气和余热,两个设备形成一条“电-氢-气-热-碳”闭环,优化目标是最小化总运行成本。它适合正在做综合能源系统优化调度的研究生、做园区综合能源方案的工程师,以及想从确定性调度往碳市场方向延伸的从业者。论文可以在知网下载,配合作者博客里的解读看,复现效率会高很多。
2. 模型主干:P2G与碳捕集设备如何进入优化调度框架
2.1 电转气的能流链条:电解槽、甲烷化和余热回收怎么建模
P2G(电转气)在热电联供系统里不是一根“电进去、气出来”的黑匣子。完整的工艺分成两段:电解槽先把电变成氢气,甲烷化反应器再把氢气和二氧化碳合成甲烷。电解槽的电到氢效率,按氢气低位热值算,通常在55%到72%之间;甲烷化这一步还要损失一部分能量,所以整条“电到甲烷”的链路效率,我一般按35%到55%估算。工程上做调度模型时不需要细化电解槽内部的电流密度和温度场,用线性效率等式就够,这也是论文类源程序最常见的选择。
甲烷化是强放热反应,每合成一立方米甲烷会释放大量高温余热,这部分热量可以回收进热网参与供热平衡。也就是说,P2G在优化调度里既是电负荷、又是气源、还是热源。很多人第一次建模只写“P_p2g消耗电、V_ch4产出气”,把热量漏了,复现出来的热平衡曲线怎么都对不上论文。拿到源程序后,先找有没有Q_p2g_heat这个变量,没有的话大概率热回收被简化掉了。
建模时我习惯定义这几个变量:P_p2g是P2G输入电功率,V_h2是产氢量,V_ch4是甲烷产量,Q_p2g_heat是回收余热量。它们之间用效率等式串起来:V_h2等于电解效率乘以P_p2g再除以氢气热值;V_ch4等于甲烷化效率乘以V_h2;Q_p2g_heat等于一个固定系数乘以P_p2g。设备本身还要写上下限约束和爬坡约束,如果程序允许P2G启停,再加一个0-1变量,把启停成本和最小连续运行时间写进去更贴近实际。
2.2 碳捕集设备是电热双耗的虚拟负荷,不是环保配件
碳捕集设备在这个模型里的角色容易被低估。常见做法是用化学吸收法捕集燃气轮机和锅炉烟气里的CO2,吸收塔里让烟气与胺溶液接触,再生塔里用蒸汽加热富液把CO2解吸出来。再沸器要消耗蒸汽,风机和泵要消耗电。折算下来,捕集一吨CO2大约要消耗0.15到0.4千瓦时电,以及2.0到3.5吉焦热。这几项能耗在调度里会变成真实的电负荷和热负荷,直接改变机组出力。
碳捕集在优化里的本质是一个“虚拟负荷”:它消耗电和热,产出CO2,而CO2正是P2G甲烷化的原料。建模时通常引入捕集率变量r_cap,范围在0到上限之间,捕集量C_cap等于烟气排放量乘捕集率。捕集电耗P_ccs等于单位电耗乘C_cap,捕集热耗H_ccs等于单位热耗乘C_cap,这两项分别进电平衡和热平衡。
还有一点容易被忽略:烟气量是随燃气轮机和锅炉出力变化的,不是固定值。如果程序把烟气排放量写成固定常数,碳捕集量和机组工况就脱钩了,优化器会钻空子,比如让捕集设备在机组停运时继续产CO2。拿到源程序后,重点找烟气排放量的计算公式,看它是不是燃气轮机发电功率和锅炉供热功率的函数。另外,如果模型里有碳储罐,还要检查储罐容量约束和始末储量约束,这两条决定了碳捕集能不能跨时段缓冲。
2.3 目标函数与核心约束:碳交易成本如何驱动捕集率
目标函数决定了优化器往哪个方向走。这套模型的目标函数一般是总运行成本最小化,由购电费、购气费、碳交易费和弃风惩罚四块组成。购电费是分时电价乘购电量,购气费是气价乘外购气量,弃风惩罚是惩罚系数乘弃风功率的累计值。碳交易费是要害:先算系统总排放量,再乘(1减捕集率加权平均)得到实际排放,实际排放和配额之差乘碳价就是碳交易成本。当实际排放低于配额时差值为负,等于系统在卖碳配额,这就是碳捕集设备能进优化目标的原因。
约束方面,除了常规的电平衡、热平衡、气平衡,最关键的是一条CO2物料守恒:送入甲烷化的CO2量等于碳捕集量加外购CO2量。这条约束把P2G和碳捕集锁在一起。如果CO2不够用,要么允许外购CO2,要么限制P2G出力,要么靠碳储罐做跨时段缓冲。碳储罐的引入会让模型从24小时静态联动变成跨时段动态调度,储罐容量参数和初始储量都直接影响最优解。
捕集率不是越高越好。捕集率提高会带来额外电耗和热耗,在热负荷高峰时段可能倒逼燃气轮机增加出力,燃料成本增幅超过碳收益。所以最优捕集率大概率落在某个中间值上,而不是贴着上限。如果你跑出的结果里r_cap始终取边界值,先检查捕集能耗参数是否设得偏低,再检查碳价是否高得不合理。
3. 用MATLAB+YALMIP复现主程序:文件结构、建模代码与求解器配置
3.1 先盘点程序包:文件清单、数据字段和单位陷阱
这一类论文配套源程序,结构通常不复杂,但拿到手的第一件事不是跑main,而是列文件清单。我一般会看到这几类文件:一个数据文件,放着负荷、风电、光伏、分时电价和设备参数;一个主入口脚本,从加载数据到出图一气呵成;一个建模脚本或函数,专门定义YALMIP变量、约束和目标函数;一个求解段,配置求解器选项;最后是画图脚本。有些程序把建模和求解合在一个main里,这种反而好查,不用跨文件传变量。
打开数据文件先核对三件事:单位是不是统一,功率用兆瓦还是千瓦,价格用元每兆瓦时还是元每千瓦时;时段数T是24还是96;负荷曲线的时间顺序是不是从0点排到24点。单位不一致是最隐蔽的坑,我见过有人把电价从元每兆瓦时直接当成元每千瓦时用,结果购电费小了1000倍,优化器拼命买电,燃气轮机全停。T值错误会让整个时间序列错位,风电出力和负荷对不上。
如果没有现成的文件清单,我建议按下面的结构自己整理一份说明文档,标出每个文件的作用和哪些参数需要改。排错时能省一半时间。常见文件结构如下表,拿来对照你手头的程序包:
| 文件/脚本 | 职责 | 拿到手先做什么 |
|---|---|---|
| load_data 或 data.xlsx | 负荷、风电、价格、设备参数 | 核对单位与T值 |
| main 或 run_me | 总入口流程 | 直接运行看报错位置 |
| build_model | 定义变量、约束、目标函数 | 对照2.3节检查关键约束 |
| solve_step | 求解器设置与求解 | 确认Gurobi或CPLEX可用 |
| plot_result | 绘图输出 | 与论文曲线做对比 |
3.2 核心建模代码:YALMIP变量、约束和目标函数的写法
不管程序包里的代码写成什么样,放眼望去基本都是这套YALMIP思路。拿到源程序后,把它建模段和下面的代码对照着看:变量定义是否覆盖了P2G、碳捕集、储罐;约束里有没有写电平衡和热平衡;目标函数里碳交易成本是正还是负。下面的中文注释是我排查时最常盯的地方。
%% 决策变量定义(YALMIP接口,T为调度时段数) T = 24; P_gt = sdpvar(1, T); % 燃气轮机发电功率,MW H_gt = sdpvar(1, T); % 燃气轮机供热功率,MWth P_grid = sdpvar(1, T); % 外购电功率,MW P_p2g = sdpvar(1, T); % P2G输入电功率,MW H_eb = sdpvar(1, T); % 电锅炉供热功率,MWth V_ch4 = sdpvar(1, T); % P2G甲烷产量,m3/h V_co2_use = sdpvar(1, T); % 送入甲烷化的CO2量,t/h C_cap = sdpvar(1, T); % 碳捕集量,t/h C_buy = sdpvar(1, T); % 外购CO2量,t/h r_cap = sdpvar(1, T); % 碳捕集率,0~1 u_gt = binvar(1, T); % 燃气轮机启停标志逻辑说明:变量覆盖了系统里所有需要优化器决策的量。sdpvar定义连续变量,binvar定义0-1变量。注意P_gt和H_gt是两个独立变量,靠热电比约束关联,而不是事先算死,这样调度才有自由度。P_ccs和H_ccs、Q_p2g_heat没有列在变量里,是因为它们可以由C_cap和P_p2g按效率系数换算,程序里装在参数计算段就行。
约束块写法:
%% 典型约束集合 Constraints = []; % 电功率平衡:购电+燃气轮机发电+风电 = 负荷+P2G电耗+碳捕集电耗 Constraints = [Constraints, P_grid + P_gt + P_wt == P_load + P_p2g + P_ccs]; % 热功率平衡:燃气轮机供热+电锅炉+P2G余热 = 热负荷+碳捕集热耗 Constraints = [Constraints, H_gt + H_eb + Q_p2g_heat == H_load + H_ccs]; % 燃气轮机热电联产关系:供热功率与发电功率按热电比关联 Constraints = [Constraints, H_gt == (eta_gt_h / eta_gt_e) * P_gt]; % P2G能量转换:甲烷产量 = 电耗 * 电解效率 * 甲烷化效率 / 热值 Constraints = [Constraints, V_ch4 == eta_el * eta_met * P_p2g / HHV_gas]; % CO2守恒:送往甲烷化的CO2 = 捕集量 + 外购量 Constraints = [Constraints, V_co2_use == C_cap + C_buy]; % 设备区间约束 Constraints = [Constraints, P_gt_min <= P_gt <= P_gt_max];逻辑说明:电平衡中碳捕集电耗P_ccs在等式右侧,代表它是不可忽略的负荷;热平衡中碳捕集热耗H_ccs在右侧,P2G余热Q_p2g_heat在左侧作为热源。CO2守恒是这套模型的灵魂,如果没有这条约束,P2G的原料来源和碳捕集设备就各跑各的,模型退化成两个独立装置。
参数说明:eta_gt_e和eta_gt_h是燃气轮机发电效率和供热效率,热电比用效率之比表达;eta_el是电解效率,eta_met是甲烷化效率,HHV_gas是天然气高位热值。这四个参数在数据文件里都能找到,改任何一个都会影响设备运行区间和能源流向。区间约束里的P_gt_min和P_gt_max要留出合理范围,太小会让求解器频繁启停机组。
目标函数:
%% 目标函数:总运行成本最小化 Objective = sum(E_price .* P_grid) ... % 购电费 + gas_price * sum(V_gas_buy) ... % 购气费 + co2_price * (E_real - E_quota) ... % 碳交易费 + pen_waste * sum(P_wt_waste); % 弃风惩罚逻辑说明:E_price是分时电价行向量,用的是逐时点乘,体现峰谷电价对调度的影响。碳交易费括号里是实际排放减配额,实际排放由总排放乘(1减捕集率)得到。当捕集量够大时这项为负,相当于卖碳收益。跑程序时如果发现碳交易费恒等于零,多半是这里写成了正数或漏了捕集率折算。
3.3 求解器配置与结果状态:为什么Gurobi比默认求解器稳
这套模型含0-1变量后是混合整数线性规划问题,YALMIP默认求解器通常不适用。我一般用Gurobi或CPLEX,下面是常用的求解配置:
%% 求解器配置 ops = sdpsettings('solver', 'gurobi', ... 'gurobi.mipgap', 0.001, ... 'gurobi.timelimit', 600, ... 'verbose', 2); result = optimize(Constraints, Objective, ops); if result.info == 'Successfully solved' obj_value = value(Objective); fprintf('最优总成本: %.2f\n', obj_value); else disp(result.info); end参数说明:mipgap设为0.001表示允许的最优性间隙是千分之一,对调度模型精度足够;timelimit设600秒防止某个工况算不完;verbose=2会把求解迭代过程打出来,方便判断是卡在收敛还是卡在不可行。如果result.info不是Successfully solved,先看报错类型,Infeasible问题去第5章对号入座。
结果输出上,我习惯用value()把每个变量断言成数值,再算一遍平衡残差。很多程序只画曲线不计残差,导致约束写漏但图看起来又正常。下一章我会具体讲怎么从数据和曲线反推模型是否写对。
4. 参数标定与结果复现:从论文曲线反推模型写没写对
4.1 负荷曲线与设备容量的取数方法
复现论文曲线最大的坎往往不是代码,而是数据。论文里的负荷曲线、风电出力、分时电价大概率没有随程序包一起给,需要你按论文图手动取值。我一般先把论文里的典型日负荷图放大,读出峰荷、谷荷和大概形状,再用一个简单函数生成平滑曲线;风电光伏同理,注重峰谷位置而不是精确数值。峰谷比如果差太多,燃气轮机和储罐的调度行为会明显偏离论文。
设备容量的取值也有章法。燃气轮机容量按负荷峰值往上留20%到40%裕度;P2G容量不按负荷峰值定,而是按弃风最严重的时段能消纳多少电来定,通常取风电装机容量的15%到30%。碳捕集设备容量由烟气排放量决定,估算方法是满发燃气轮机的CO2排放速率乘期望捕集率。这几个值设得太离谱,优化结果就是废的,曲线形状和论文完全对不上。
数据准备好后,统一放进数据文件。注意T值要和负荷曲线长度一致,24点模型就用24长度向量。程序如果是从Excel读取,检查第一列是不是时间;如果是直接写在m文件里,找数组名对应关系。我踩过最冤的一次是把风电出力数组写反了方向,导致夜间风电高负荷时段全用来给电锅炉加热,燃气轮机停运,热平衡倒是满足了,碳排放却高得离谱。
4.2 三个最敏感的参数:P2G效率、碳捕集率上限和碳价
参数敏感性分析是验证模型行为和找复现偏差最快的手段。我最常动三个参数:P2G电效率、碳捕集率上限、碳价。下面这张表是它们的常见范围和调整时该重点观察的指标。调参规律要心里有数:P2G效率提高,弃风率下降,但P2G用电量可能先升后降,因为效率高到一定程度后,消纳同样弃风所需的电就少了;碳价提高,优化器会主动提高捕集率,直到捕集能耗成本追平碳收益为止。
| 参数 | 常见范围 | 调整时重点看什么 |
|---|---|---|
| P2G电效率 | 0.55~0.72 | 弃风率、甲烷产量、热回收量 |
| 甲烷化效率 | 0.50~0.65 | 购气量、P2G余热供热 |
| 碳捕集率上限 | 0.85~0.95 | 捕集电耗、热耗、碳交易收益 |
| 碳价 | 60~200 元/吨 | 最优捕集率拐点、总成本变化 |
| 捕集热耗 | 2.0~3.5 GJ/t | 供热高峰时段的机组出力和热平衡 |
实际操作上,我会把这三个参数各取三档做9次求解,记录总成本、碳排放、弃风率三个指标,画一张小表。这张表既是模型行为验证,也是和论文结果对照的依据。如果碳价从60调到200,捕集率曲线没有任何变化,说明碳交易成本项在目标函数里可能被写成了常数,优先查这一项。9次求解在24点MILP下通常几分钟内完成,成本很低,值得一做。
4.3 结果自检:平衡残差、成本拆解和曲线目检
跑完程序先别急着截图,先算平衡残差。我给复现程序加过很多次这道检查,每次都有效:
%% 平衡残差自检 res_elec = P_grid + P_gt + P_wt - P_load - P_p2g - P_ccs; res_heat = H_gt + H_eb + Q_p2g_heat - H_load - H_ccs; if max(abs(res_elec)) > 1e-6 || max(abs(res_heat)) > 1e-6 warning('平衡残差不满足,检查电平衡或热平衡约束'); end disp(max(abs(res_elec)));逻辑说明:value()求出的结果代入平衡等式后,理论上残差应该为零。如果残差达到0.1兆瓦以上,说明某条约束没有参与求解,常见原因是约束里写的是P_ccs,而求解变量里根本没定义它。残差为0不代表模型全对,但残差不平衡一定有问题。残差脚本挂在main的最后一行,每次跑完自动过一遍。
成本拆解是第二道检查。把购电费、购气费、碳交易费分别算出来,看三者比例是否合理。燃气轮机为主的热电联供系统,购气费和购电费占大头;碳交易费在碳价80元每吨左右时通常占5%到15%。如果碳交易费占比超过30%,要么碳价参数设太高,要么捕集率变量没有受能耗约束,导致优化器白拿碳收益。
最后是曲线目检:把P2G出力和弃风功率画在同一张图里,P2G出力时段应该和弃风时段高度重合;把碳捕集量和CO2消耗量画在一起,两条曲线形状应该接近。如果P2G在无弃风时段大功率运行,大概率是购气价格设得比甲烷产气成本还高,优化器用P2G产气替代购气,这本身合理,但要和论文前提核对。
5. 避坑与排查:跑这套调度模型最容易翻车的五个地方
5.1 坑一:P2G的氢气没进甲烷化,被当副产品直接卖掉
现象:结果表里V_ch4为零,但V_h2有值,优化器把所有氢气按副产品卖掉,甲烷产量一直上不去。原因:模型里漏写了“氢进甲烷化”的流向约束,氢气变量和甲烷化反应器之间没有关联。解决:补上氢平衡等式,把进甲烷化的氢和直接外售的氢拆成两个变量。外售单价如果设得比甲烷化收益还高,优化器自然把氢全卖掉,甲烷化设备成了摆设。
排查时先看V_h2变量是不是单独存在,再查有没有对应的去向约束。我自己遇到过一次是把氢气外售价格填得比天然气还高,结果P2G变成了纯制氢装置,碳捕集设备捕出来的CO2没地方去,全部外购量清零。调参前先确认这个价格参数的合理性,如果程序包里没给外售氢价,干脆设成0,强制优化器把氢送进甲烷化。
5.2 坑二:碳捕集能耗漏出电热平衡,总用电量对不上
现象:碳捕集设备投运时段,总电耗曲线没有明显抬升,热平衡里碳捕集热耗消失,整体用电量比论文低一截。原因:捕集能耗只写进了目标函数的成本项,没写进电平衡和热平衡约束,变成了一笔“不用承担物理后果”的账。解决:在电平衡右侧加P_ccs,在热平衡右侧加H_ccs,再配合单位捕集能耗系数。修复后最直观的变化是燃气轮机出力曲线整体上移,因为捕集能耗把电负荷和热负荷同时拉高了。
用4.3节的残差脚本能快速定位这类问题:漏写能耗的模型,残差会稳定偏正,且偏差量和碳捕集量成正比。对比碳捕集设备启停前后残差的跳变,基本能锁定是哪一处约束漏了项。
5.3 坑三:求解器报Infeasible,多出在CO2守恒无解
现象:换了某组参数后,YALMIP直接报Infeasible,连初始解都没有。原因:最常见是P2G需要的CO2量超过了碳捕集上限和外购上限,而且模型没有碳储罐缓冲,导致某一时段CO2守恒等式无解。解决:先加一个外购CO2变量,外购价设为高于甲烷售价,让优化器尽量捕集但不至于无解;再检查碳捕集量上限是否随烟气量变化,如果写成了常数,夜间低负荷时必然无解。
定位方法是用YALMIP的assign命令给每个变量赋初值,看约束残差最大的是哪一条。我习惯先放开CO2守恒约束跑一次,看优化器在没有该约束时选了什么解,两边一对比就能看出是原料不足还是储罐容量不够。
5.4 坑四:碳捕集率拉满总成本反升,热耗参数背锅
现象:碳价很高时,r_cap贴近上限,但总成本比碳价低的时候还高。原因:捕集率提高带来额外电耗和热耗,在热负荷高峰时段倒逼燃气轮机增加出力,燃料成本增幅超过碳收益。解决:这通常不是写错,而是系统本身的物理拐点。优先检查碳捕集热耗参数,如果按2.0吉焦每吨设成了4.0吉焦每吨,拐点会出现得很早。把捕集热耗调回合理区间,拐点位置会明显后移。
这类问题最容易和“目标函数符号写反”混淆。区分方法很简单:把碳价设成0,看捕集率是否变成0。如果碳价为0捕集率还很高,说明碳交易项符号写反,优化器把买碳当成了收益;如果碳价为0时捕集率归零,那只是物理拐点,参数调回来就行。
5.5 坑五:MILP收敛太慢,二元变量和分段段数双爆炸
现象:求解器一直停在MIP节点搜索上,gap降不下去,几百秒跑不完一个工况。原因:设备启停变量太多,或者碳捕集能耗被做成了多段分段线性函数,段数一多整数变量规模爆炸。解决:第一步把mipgap放到0.01,先求一个可用解验证流程;第二步去掉非必要启停变量,比如电锅炉在24小时尺度上可以不参与启停优化;第三步把分段线性化的段数从5段减到3段,变量规模立刻下降。调整顺序很重要,先松gap、再减变量,分步做才能看出谁拖慢了求解。
如果程序里用了big-M约束表达设备启停,还要顺便检查M值是否取得过大。M值太大会让线性松弛质量变差,节点搜索数量成倍增加。我见过一个程序把M值写成机组容量的100倍,求解时间从40秒涨到800秒,改成1.2倍容量后立刻恢复。
6. 把模型改成自己的版本:退化模式验证与扩展路径
6.1 先跑退化模式:P2G和碳捕集全关,验证耦合约束
拿到任何IES调度程序,我第一步都是跑退化模式:把P2G容量和碳捕集容量同时设成0,重新求解。这个模式下的最优解应该退化成普通热电联供加购电购气,如果成本反而比完整模型高,说明耦合约束里存在不可行的硬绑定;如果成本比完整模型低,说明P2G和碳捕集在模型里是白白消耗能源的负资产,要检查能量转换效率。退化模式是分辨“耦合写错”和“参数不合理”的试金石。
%% 退化模式:关闭P2G与碳捕集 P_p2g_max = 0; C_cap_max = 0; % 重新建模求解,与完整模式的总成本、碳排放做对比我自己的习惯是每次跑完全部工况,把总成本和碳排放记在一个文本文件里,标上参数版本号。改参数改到第三轮时,翻记录就知道哪个版本是那个“看起来合理但其实是错的”结果。这套方法帮我避了好几次翻车,也推荐给你。
6.2 扩展方向:96时段与两阶段鲁棒
跑通确定性24点模型后,下一步常见扩展是两阶段鲁棒优化,把风电预测误差放进不确定集合,主问题求运行成本,子问题找最恶劣场景。改动集中在数据生成和约束表达,核心模型不需要推倒重来。另一个更简单的扩展是把24点改成96点,15分钟分辨率下,二元变量数量乘4,求解时间可能涨10倍以上。建议先用退化模式测一套数据,确认规模可行再全量跑。
做96点扩展时还要注意爬坡约束的步长换算,原来1小时一步的爬坡率要缩到15分钟一步,不然设备爬坡能力被明显高估。碳储罐的始末储量约束也建议放宽到日尺度,否则跨日调度会把储罐容量需求放大好几倍。这些边界条件想清楚,扩展出来的模型才不是花架子。希望帮到你。
本文还有配套的精品资源,点击获取