最近在做一个虚拟电厂优化调度项目,模型里同时聚合了碳捕集火电机组、垃圾焚烧电厂和电转气(P2G)装置,用Matlab搭模型、Yalmip建模、Gurobi求解。这类“计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度”的题目现在很常见,但打开论文看公式是一回事,真正把代码跑起来又是另一回事:变量维度、碳平衡边界、分段线性化、求解器不可行……每一步都可能卡住。这篇文章不打算复述论文摘要,而是按我自己从建模到调参的完整顺序,把这个题目的模型怎么搭、目标函数怎么线性化、Matlab代码怎么组织写清楚,以及哪些地方最容易把调度结果搞偏。如果你正在做类似方向的毕设或工程项目,可以直接参考这套思路。
1. 为什么要把碳捕集、P2G和垃圾焚烧放进同一个调度框架
1.1 三个主体能形成“碳闭环+新能源消纳”的双循环
要理解这个模型,先要搞清楚这三个主体放在一起的真实诉求是什么。虚拟电厂(VPP)本身是一个聚合层概念:把分散在配电网里的分布式电源、储能、可控负荷整合成一个整体,对外像一个电厂一样接受调度。但聚合进来的机组类型差异非常大,尤其是碳捕集电厂和垃圾焚烧电厂。
碳捕集电厂本质上还是同步机组,但它的净出力受捕集能耗影响,对外输送的功率是“发电功率减去捕集能耗”的结果。垃圾焚烧电厂则受环保约束,城市垃圾每天必须处理,不能像常规火电那样“停机备用”。P2G则是一种“电负荷+气源”的混合体,用电力制氢再甲烷化,既能消纳弃风弃光,又能产生天然气,还能源源不断地消耗二氧化碳。
这三者放在一起,最大的价值在于形成一个双循环。传统火电加装碳捕集后,烟气里的CO2被溶剂捕获下来,但捕获下来的CO2如果只封存,经济性并不好。P2G的甲烷化反应正好需要CO2:电解水制氢后,氢气和CO2在催化剂作用下反应生成甲烷和少量水,捕集下来的CO2就从“负担”变成了“原料”。另一边,当电网出现弃风时,比如凌晨风电大发而负荷处于低谷,多余电能可以驱动P2G转化为天然气储存;如果P2G单独运行,它还需要额外寻找CO2来源或向大气购买碳,碳效益打了折扣。所以把碳捕集电厂、垃圾焚烧电厂与P2G联合调度,能同时实现高碳机组低碳化改造、风电消纳加强、燃气增产,从系统级而不是设备级优化成本与碳排放。
1.2 为什么这类题通常要写成MILP而不是用启发式算法
这类调度问题包含机组启停0-1变量、碳交易阶梯区间选择、分段线性成本,本质是一个混合整数线性规划问题。在Matlab生态里,最靠谱的路线是Yalmip+Gurobi/Cplex,而不是用一个遗传算法或粒子群去硬搜。
原因是:模型规模通常是24时段,变量数量在几百个量级,0-1变量几十个,对成熟MILP求解器来说是很小的问题,几秒钟就能稳定求解到全局最优。而启发式算法不仅每次结果有随机性,而且面对分段线性、逻辑约束时很难给出可靠的可行性。更重要的一点是,MILP能生成对偶信息和约束影子价格,后续做碳价敏感性分析、市场交互研究时非常有用。
2. 三类核心设备的建模:变量多,耦合关系是关键
建模阶段我建议不要急着敲代码,先用表格把“输入-输出-耦合关系”列清楚。机组、P2G、碳捕集模块之间有共享变量:CO2流、电功率流、气流量。如果变量维度没对齐,Yalmip会直接报维度错误,或者更坑的是约束尺寸对但物理意义错,结果解出来一团乱。
| 模块 | 主要输入 | 主要输出 | 关键耦合变量 |
|---|---|---|---|
| 碳捕集机组 | 燃料、捕集能耗 | 净发电出力、逃逸CO2、捕获CO2 | 净出力、C_capt、C_emit |
| P2G设备 | 电能、CO2、水 | 天然气、氧气(不计) | P_p2g、G_p2g、C_consume |
| 垃圾焚烧电厂 | 垃圾量、辅助助燃燃料 | 发电出力、烟气CO2 | P_msw、C_emit_msw |
下面逐个拆开讲。
2.1 碳捕集机组的“净出力-捕集能耗-烟气碳量”三角关系
碳捕集火电机组通常按两输入三输出来建模。输入是燃料热量和捕集能耗,输出是上网电力、烟气CO2排放量、被捕集的CO2量。核心公式如下:
P_net(i,t) = P_raw(i,t) - P_ccs(i,t) % 净出力 = 毛发电出力 - 捕集能耗 P_ccs(i,t) = lambda_ccs * C_capt(i,t) % 捕集能耗与捕获CO2量成正比 C_flue(i,t) = e_intensity * P_raw(i,t) % 烟气CO2总产生量 C_flue(i,t) = C_capt(i,t) + C_emit(i,t) % 总烟气碳 = 捕获碳 + 逃逸碳这里的lambda_ccs通常取0.25~0.35 MWh/tCO2,取决于再生塔蒸汽消耗量。e_intensity是机组单位毛发电量的CO2排放强度,燃煤机组一般在0.8~0.9 t/MWh。把捕集能耗写成与捕集量成正比的线性式,是调度模型里的常见假设,因为胺法吸收/再生的主要能耗与处理量近似线性。热力学上更精细的模型当然可以做,但调度模型覆盖全天24小时、多个机组时,线性化是精度和求解效率之间的平衡点。
有几个易错点特别提醒:
- 捕集能耗的物理含义是“在厂用电中增加了一部分”,优化模型里要体现在净出力上,不要单纯把它当作外部电负荷再叠加一次,否则功率平衡会重复计算。
- 捕集量上限不是简单取决于烟气总量,还和吸收塔处理能力相关,需要定义
C_capt <= C_capt_max。 - 捕集装置有爬坡限制,不能期望一个小时内捕集量从0跳到满值,溶剂循环和控制系统都有滞后,要加爬坡约束。
2.2 P2G的输入侧和输出侧要分开建模
P2G分两步:电解水制氢,然后和CO2在甲烷化反应器中合成甲烷。调度模型通常只关心三件事:耗电量、产气量(折算成热值功率)、耗碳量。
G_p2g(k,t) = eta_p2g * P_p2g(k,t) % 产气功率 = 综合效率 * 耗电功率 C_consume(k,t) = beta_c * G_p2g(k,t) % 消耗CO2量与产气功率成正比综合效率eta_p2g在0.55~0.65之间,取决于电解效率和甲烷化效率。beta_c表示产出单位热值天然气所需CO2量,工程上大约0.7~0.85 t/MWh。如果自己推导,可以用化学计量比和热值换算,但调度模型直接用经验系数更方便。
P2G的运行边界常被忽略,导致结果过于理想:
- 电解槽有最小负载,一般不允许低于额定功率的10%~20%,否则效率骤降;但有些论文出于简化会设0,这会让P2G在系统最需要时以极低功率运行,实际中做不到。
- 电解槽电功率爬坡很快,但甲烷化反应器是化工设备,负载变化率会限制整体爬坡。所以模型里要给P2G加爬坡约束。
- P2G消耗的CO2并不强制要求来自碳捕集。但在协同调度模型里,我们会加一个“碳源选择”机制:优先使用内部捕集CO2,外购CO2设定较高价格,这样模型会自动选择内部碳闭环,体现协同价值。
2.3 垃圾焚烧电厂的“必须处理量”是最容易被低估的约束
垃圾焚烧电厂在虚拟电厂里通常当作可控电源处理,但不能完全照搬火电模型。因为它承担城市固废无害化处理任务,在调度周期内必须处理掉当日入厂的垃圾量,这是硬约束。所以模型里一般要写:
- 出力上下限:焚烧炉稳定燃烧需要出力下限,通常是额定的50%~60%,不是0;
- 全天垃圾处理量下限:
sum(G_msw(t)) >= G_msw_total。没有这个约束,优化器一定会把所有时段出力压在下限,省燃料成本,但实际垃圾根本没烧完; - 垃圾焚烧发电效率通常在18%~22%,单位燃料成本较低,有时还可以计入政府补贴,体现在目标函数里可能是负成本项,促使它多发;
- 如果给垃圾焚烧电厂也加碳捕集,它的烟气碳量同样简化成与发电量线性,适用2.1中的捕集关系。但要注意,垃圾焚烧的烟气成分比煤电复杂,这里只是工程简化。
3. 目标函数与碳交易成本:阶梯碳价必须线性化
3.1 目标函数怎么搭
目标函数的基本形式是:
min 总成本 = 常规机组燃料成本 + 启停成本 + 运行维护成本 + 碳捕集运行成本 + P2G运行成本 + 垃圾焚烧处理成本/补贴 + 碳交易成本 + 弃风惩罚常规机组燃料成本可以用二次函数,但求解时要做分段线性化。启停成本用0-1变量,一次启停固定成本。碳捕集运行成本可以折进捕集能耗中惩罚,也可以单独写成一个与捕集量正比的线性项。P2G运行成本主要是设备维护和辅助能耗,按产气量线性。
垃圾焚烧部分的成本要看考虑的是“单纯发电”还是“城市固废处理”。如果是后者,入厂垃圾的处理费收入会抵消很大一部分发电成本,因此目标函数里的单位成本可能是负值。这个设计会明显影响调度结果,要结合实际补贴政策设定,别乱填。
弃风惩罚也必须有。没有弃风惩罚,很多情况下最优方案就是直接弃掉额外的风电,反正风电边际成本也是0。要给它一个单位惩罚系数,比如200元/MWh,才能体现可再生能源消纳的优先级。
3.2 碳交易成本的线性化方案
碳排放交易是这套模型的低碳导向核心。目前常见的是阶梯型碳交易机制:根据净上网电量发放免费配额,实际排放低于配额可出售,超出需要购买,且连续超出时碳价递增。
先定义配额:
E_quota(t) = quota_rate * P_vpp_net(t) % 配额与虚拟电厂净输出电量挂钩 E_trade(t) = E_emit_total(t) - E_quota(t) % 需要交易的碳量,负值代表出售然后构建分段线性成本。比如三段碳价分别为100、150、200元/tCO2,分段上限为Delta1、Delta2。直接用min/max函数会让问题变成非光滑优化,求解器不稳定。我建议用binvar加Big-M做区间选择,虽然增加了整数变量,但MILP求解器非常擅长这类约束。
核心思路是定义分段量E_seg(k,t)和分段选择变量z(k,t),保证每个时段的超额碳量先填满低区间再流入高区间。在Yalmip里可以手工写:
z = binvar(3, T, 'full'); E_seg = sdpvar(3, T, 'full'); % 每个时段的总超额碳量 = 各分段之和 Constraints = [Constraints, E_trade == sum(E_seg, 1)]; % 每个分段量非负 for k = 1:3 Constraints = [Constraints, E_seg(k,:) >= 0]; end % 后一段只有在前面段满额时才能占用,用大M控制 M_bound = 5000; % 大于可能的碳排放量即可 Constraints = [Constraints, E_seg(2,:) <= z(1,:) * Delta1 + M_bound * (1 - z(1,:))];具体写法可以根据区间数调整,但思想是一致的:用0-1变量锁定额外区间,用连续变量表达区间内碳量。实现时最怕的是M取值过大,导致分支定界效率下降,所以M要尽量贴近实际量级,比如最大碳量是3000 tCO2,M设5000就够。
4. Matlab+Yalmip建模实操:从0到可跑的代码框架
4.1 变量定义与维度约定
如果你用Matlab做这套题,我建议用Yalmip+Gurobi,而不是自己写求解器。Yalmip可以让你像写数学公式一样组织符号变量,极大减少矩阵堆叠工作。
我习惯按“参数区-变量区-约束区-目标区-求解区-后处理区”组织代码。变量命名要统一:第一维是设备编号,第二维是时间。后处理时直接P_g(i,:)就是第i台机组一整天的时序。
T = 24; n_g = 2; % 常规机组数 n_ccs = 1; % 带碳捕集机组数 n_msw = 1; % 垃圾焚烧电厂数 n_p2g = 1; % P2G设备数 P_g = sdpvar(n_g, T, 'full'); % 常规机组出力 u_g = binvar(n_g, T, 'full'); % 启停状态 P_raw = sdpvar(n_ccs, T, 'full'); % 碳捕集机组毛出力 P_ccs = sdpvar(n_ccs, T, 'full'); % 捕集能耗 C_capt = sdpvar(n_ccs, T, 'full'); % 捕获碳量 C_emit = sdpvar(n_ccs, T, 'full'); % 逃逸碳量 P_msw = sdpvar(n_msw, T, 'full'); % 垃圾焚烧出力 P_p2g = sdpvar(n_p2g, T, 'full'); % P2G耗电 G_p2g = sdpvar(n_p2g, T, 'full'); % P2G产气sdpvar默认创建满矩阵,最好显式加维数和'full',避免列向量矩阵乘法时维度混乱。垃圾焚烧的碳捕集变量可以单独定义,也可以在同一个矩阵里加一行,看你的规模。
4.2 功率平衡和碳平衡是两类最核心约束
功率平衡要写在“净出力”层面:
P_wind_used = sdpvar(1, T, 'full'); % 实际消纳风电 Constraints = [Constraints, ... sum(P_g,1) + sum(P_raw - P_ccs,1) + sum(P_msw,1) + P_wind_used ... == P_load + sum(P_p2g,1)];这里P2G耗电是负荷,放在等式右边;弃风量就是P_wind_pre - P_wind_used,在目标函数里加惩罚。
碳平衡约束是这套模型的灵魂,也是很多人建模时最容易算重复的地方。我建议用“净排放”作为系统的唯一口径:
系统净排放 = 所有机组未捕集逃逸排放量 - 外部封存抵扣 + P2G外部购碳?更稳妥的写法是把碳捕集和P2G分开算,再统一汇总:
C_capt_total = sum(C_capt,1) + C_capt_msw; % 总捕获碳 C_emit_total = sum(C_emit,1) + C_emit_msw; % 总逃逸碳 C_consume_p2g = beta_c * sum(G_p2g,1); % P2G耗碳量 % 系统总排放参与碳交易的口径,C_consume_p2g作为碳利用抵扣项 E_emit_total = C_emit_total - C_consume_p2g;注意这里有个逻辑陷阱:如果碳捕集捕获的CO2已经被记入“逃逸排放”之外,那么P2G再去扣一遍就会重复计算。所以要么把C_consume_p2g理解成“碳捕集后未封存而进入P2G的量”,不重复在净排放里扣;要么就按照上面代码这样把E_emit_total定义为“逃逸排放-被P2G利用的外部碳”,但要给自己固定一套边界,不要一会儿扣一会扣。我在调试中就因为口径不一致,出过碳排放量为负数的不合理结果。
4.3 求解器设置与不可行排查
ops = sdpsettings('solver', 'gurobi', 'verbose', 2, 'showprogress', 1); diagnosis = optimize(Constraints, Objective, ops); if diagnosis.problem ~= 0 yalmiperror(diagnosis.problem) end遇到不可行时,我一般这样排查:
- 先去掉碳交易分段约束,只跑基础功率平衡和机组约束,看是否可行;
- 再逐项加P2G、碳捕集、垃圾处理量约束,每次加完都用
check(Constraints)看哪些约束残差过大; - 检查爬坡约束是否跨时段错位,第2时段的爬坡应当用
P(:,2)-P(:,1),不要写反; - 检查Big-M是否过大,过大M会拖垮求解性能。
5. 算例设计:怎么看出“协同”的价值
5.1 四套对照算例怎么设计
搭好代码之后,最怕打印一堆结果却说不清优劣。我一般设计四个对照场景:
- 场景A:普通虚拟电厂,含常规燃煤机组、垃圾焚烧、风电,没有碳捕集和P2G;
- 场景B:在场景A基础上加碳捕集,捕获的CO2直接封存;
- 场景C:在场景A基础上加P2G,但P2G所需CO2来自外部采购;
- 场景D:在场景A基础上同时加碳捕集和P2G,捕集的CO2优先供给P2G。
通过四个场景的对比,能拆出碳捕集和P2G各自的作用,以及协同带来的增量。对比指标建议用四个:总运行成本、净碳排放量、弃风率、P2G的时段利用率。
下面是我以一个典型24小时系统跑出来的趋势方向(具体数据取决于参数,仅说明变化关系):
| 场景 | 总成本 | 净碳排放 | 弃风率 | 说明 |
|---|---|---|---|---|
| A(基准) | 最高 | 最高 | 明显弃风 | 无CCS、无P2G,碳交易成本高 |
| B(+CCS) | 中等 | 明显下降 | 弃风可能反而增加 | 捕集能耗挤占风电出力 |
| C(+P2G) | 中等 | 略下降 | 弃风下降 | P2G消纳风,但CO2靠外购 |
| D(CCS+P2G) | 最低或接近最低 | 最低 | 很少弃风 | P2G利用捕集的CO2,形成闭环 |
这里特别值得注意场景B:加碳捕集后,捕集能耗很大,在负荷低谷时段反而需要机组提升毛出力来维持净出力,可能导致弃风加剧。如果没有P2G,这些“为了捕集而额外发出的电”无处可去。加了P2G之后,低谷时段P2G启动,既消纳了风电和多余电量,又消耗掉捕集下来的CO2,相当于把“为减碳而付出的成本”部分转化为天然气资产。
5.2 从结果曲线看协同价值
画图时,重点看三条曲线:
- 风电场出力曲线:对比预测可用功率、实际消纳功率、P2G耗电功率,能直观看出P2G在哪些时段“吃掉”了弃风;
- CO2流向曲线:画碳捕集捕获量、P2G耗碳量、封存量三条线,看协同时段是否对齐。如果P2G耗碳量和捕集量在低谷时段重合,说明碳闭环起了作用;
- 系统净排放柱状图:叠加碳交易成本和P2G收益,看看协同场景的经济性来源在哪里。
我在调试时发现,很多算例的场景D并不是所有时段P2G都满负荷,而是集中在凌晨风电大发、负荷低谷时段启动。白天P2G会主动降出力,因为风电消纳压力小,直接用市电更划算。这说明时序耦合是对的。如果结果里P2G全天满负荷,那大概率是参数设置或爬坡约束缺失,要回去查。
6. 代码扩展方向与个人实操体会
6.1 可扩展的几个方向
这套模型做完以后,可以往很多方向继续扩展:
- 把风电预测误差显式建模,改成多场景随机优化。Yalmip里可以按场景循环叠约束,目标函数改成期望值,求解时间会明显增加,但结果更稳健;
- 加入电储能。电储能和P2G既竞争又互补:储能效率高,P2G能产气并利用CO2,系统会依据成本自动决定用哪种缓冲资源;
- 把碳捕集的详细热力过程加进来,比如再生塔能耗随负荷率非线性变化,甚至让P2G的余热供应捕集再生能耗,形成热-电-气耦合;
- 做碳价敏感性分析,从50元/t拉到200元/t,观察CCS和P2G何时真正进入经济运行。这个结论对政策解读和项目论证都很有用;
- 考虑配电网潮流约束,用DistFlow或直流潮流替代单节点平衡,但要注意求解规模上升。
6.2 个人实操过程中的几条经验
最后说几条实实在在的经验。第一,建模之前先画能量流和碳流图,把每个变量的耦合关系标出来,后面写约束基本是“抄图”。第二,不要一上来就上复杂的捕集热力学模型,先用线性模型跑通,再逐步扩展。第三,变量命名一定要统一,我习惯决策变量全大写,参数小写或带前缀,否则后处理时找bug会非常痛苦。第四,做算例对比时,固定同一份负荷曲线和风电预测数据,只改设备参数,结果才有可比性。
这套“碳捕集+P2G+垃圾焚烧虚拟电厂优化调度”虽然看起来像是关键词拼接的题目,但实际是一个多能互补、低碳环保和经济效益三方博弈的典型优化问题。把模型逻辑理清、Matlab代码跑通之后,后续无论是继续深入做随机优化、多能流还是市场机制研究,都有了扎实的基础。尤其是碳平衡和分段线性化这两道关,突破一次,后面再遇到类似题目基本就能顺手拈来。