
做电力系统优化调度这些年我最大的感受是拿到“计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度”这类题目时很多人第一反应是开写代码结果往往卡在“模型没想清楚”上白白浪费大量时间。这个题目听起来长其实拆开就三块——虚拟电厂VPP、碳捕集CCS、电转气P2G再加一个“垃圾焚烧”做协同对象。它们之间不是简单叠加而是通过“电-碳-气”三条能量流耦合成一个混合整数线性规划MILP问题。这篇文章把我从物理过程分析、数学模型搭建到MATLABYALMIP求解、调参与坑点排查的完整流程整理出来适合正在做低碳调度、虚拟电厂聚合、碳捕集与电转气协同方向的硕博生和相关方向工程师直接参考。我会尽量用“能直接抄作业”的方式讲变量怎么设、约束怎么写、求解器怎么配以及哪些地方容易算出来反常识的结果。文章不回避细节因为这类问题真正的门槛恰恰都在细节里。1. 系统架构与物理过程拆解1.1 为什么要把碳捕集、电转气和垃圾焚烧放进同一个虚拟电厂虚拟电厂这个概念本身不神秘它就是把分布式电源、储能、可控负荷聚合成一个整体参与电网调度。但传统虚拟电厂考虑的基本是“电功率平衡”很少把“碳”当成一个需要调度的资源。随着低碳要求越来越高光算电不够了得把碳排放当成一条独立的流来管理。碳捕集装置CCS的价值在于它让火电/燃气机组从“固定碳排放源”变成了“可调碳排放源”。捕集率高低能调捕集能耗能调这就等于给发电机组加了一个“碳调节阀”。但CCS有个天然短板——捕下来的CO2得找去处否则不是储存就是浪费。这时候电转气P2G就接上了P2G利用电力制氢再把氢和CO2合成甲烷等于把“捕下来的碳”重新变成“可用能源”。垃圾焚烧在这里扮演的角色很巧妙。一方面它是稳定的基荷电源处理城市生活垃圾有环保刚性另一方面它的烟气里富含CO2而且比火电烟气更“干净”是P2G非常合适的碳源。所以把垃圾焚烧和CCS/P2G放进同一个VPP本质上做的是“物质流-能量流-碳流”的联合调度垃圾焚烧产电产碳CCS捕碳P2G耗电耗碳产气燃气轮机再烧气形成一个半闭环系统。1.2 系统组成与能量流走向我采用的典型VPP架构包含六个单元燃气轮机含CCS、垃圾焚烧电厂、风电场、光伏电站、电储能系统、P2G装置含储气罐和储碳罐。为了方便建模我做了一个常见的集总假设P2G的电解槽和甲烷化反应器合并为一个等效模块输入是电能和CO2输出是甲烷暂不细算中间氢缓冲过程。这个假设在日度调度尺度下完全够用很多公开文献也都是这么处理的。能量流分三条线看电风机、光伏、燃气轮机、垃圾焚烧一起送电加上电储能充放和主网购电共同满足负荷。P2G和CCS作为“可调用电负荷”出现在系统里这是协同的关键。碳燃气轮机和垃圾焚烧排放CO2CCS按捕集率从烟气中提取CO2送入储碳罐储碳罐再给P2G供碳。剩余未捕集的CO2通过碳交易机制折算成成本。气P2G产出的甲烷进入储气罐可以卖给天然气网也可以回供给燃气轮机替代天然气我用“售价/自用替代成本”统一折算成收益。这三条流通过“CCS捕碳量 P2G耗碳量 储碳罐变化量”这个等式耦合在一起。COP捕集-电转气协同的调度本质就是找到每个时段最优的“捕多少碳、用多少电产气、何时储气”组合。用一句生活化的话说传统调度只做“电源和负荷匹配”这个模型要做“电、气、碳三个仓库的联合库存管理”。2. 数学模型构建目标函数与约束条件2.1 目标函数怎么写才完整我用的目标函数是最小化系统总成本共六项。第一项是与主网交互成本分时购电价格为正售电给主网为负第二项是燃气轮机燃料成本按出力的二次函数分段线性化实际编程时用分段线性逼近避免非线性第三项是垃圾焚烧处理成本这里要加上环保补贴收入否则单独看垃圾处理是亏钱的第四项是CCS运行成本主要是捕集能耗对应的电费和溶剂损耗折算第五项是P2G运行成本包括耗电成本已经从功率平衡中算了一次所以这里只算设备维护和CO2原料成本避免重复计费和设备折旧第六项是碳交易成本采用基准线法[ C_{carbon}P_{CO2}\times(E_{total}-E_{quota}) ]其中 (E_{total}) 是实际总排放量(E_{quota}) 是免费配额二者差值乘以碳价。捕集的CO2不算“排放”因为已经物理封存在产物里了。这个处理方式和当前国内碳市场的“排放核查”逻辑是对应的。另外我加了弃风弃光惩罚项惩罚系数取单位发电收益的1.5倍左右。这样做的好处是让优化结果不会出现“为省钱主动弃掉大量风电”的极端情况。如果你手头算例的风电渗透率不高惩罚系数可以设小一点但一定要有否则解的空间不完整。2.2 核心约束条件逐条拆解功率平衡约束是调度模型的基石[ P_{grid}(t)P_{gt}(t)P_{wte}(t)P_{w}(t)P_{pv}(t)P_{es}^{dis}(t)P_{load}(t)P_{ccs}(t)P_{p2g}(t)P_{es}^{ch}(t) ]这里有一个细节我特别提醒P2G和CCS必须当作“可调负荷”放在等式右侧而不是简单地放在左侧当作负电源。虽然数学上等价但物理意义上区分清楚能防止你后面加约束时漏项。燃气轮机约束包括出力上下限和爬坡约束。出力下限不能设成0因为含CCS的机组通常有最小技术出力。爬坡约束要考虑CCS能耗带来的净出力波动我的处理是把“毛出力”和“净出力”分开描述毛出力决定燃料成本和排放净出力参与平衡。CCS装置建模是我认为最容易翻车的部分。我用的线性化模型是烟气CO2量 (E_{fg}(t)\alpha_{gt}P_{gt}(t)\alpha_{wte}P_{wte}(t))即排放量与机组出力线性相关。捕集量 (E_{cap}(t)\eta_{ccs}(t)E_{fg}(t))其中捕集率 (\eta_{ccs}(t)) 是连续决策变量范围取[0.6, 0.9]这是目前溶剂法碳捕集系统的实际可调范围。捕集能耗 (P_{ccs}(t)\beta_{ccs}E_{cap}(t))相当于每捕集一吨CO2要消耗约0.2~0.3MWh电能。这样建模的好处是捕集效率可变调度系统可以在“多捕碳多耗电”和“少捕碳少耗电多买碳配额”之间做经济性权衡。P2G模型设输入电功率 (P_{p2g}(t))甲烷产量 (Q_{ch4}(t)\eta_{p2g}P_{p2g}(t)/H_{ch4})其中 (H_{ch4}) 是甲烷热值(\eta_{p2g}) 取0.55~0.65含电解槽和甲烷化全流程效率。CO2消耗量为 (E_{co2}^{p2g}(t)\gamma_{co2}Q_{ch4}(t))化学计量系数 (\gamma_{co2}) 约为1.83 t CO2/t CH4。这里要注意如果P2G产甲烷只为卖给气网那这个系数就是硬约束如果还有别的碳源补充需要额外加购碳变量。我的算例默认CCS捕集量是唯一碳源所以加约束 (E_{co2}^{p2g}(t) \leq E_{cap}^{avail}(t))。垃圾焚烧约束这个最容易被忽略但现实意义极强。城市生活垃圾必须“日产日清”不能因为电价低就停烧所以我加了“最低处理量”约束(M_{wte}(t) \geq M_{min})同时出力受额定容量限制。垃圾焚烧烟气碳排强度按吨垃圾0.5~0.7吨CO2折算这部分碳同样进入CCS捕集范围。储能约束电储能采用SOC递推式 (SOC(t1)SOC(t)\eta_{ch}P_{ch}(t)-P_{dis}(t)/\eta_{dis})限制SOC在[0.1,0.9]充放功率上限各设。储气罐和储碳罐类似但注意气体量和时间尺度要统一我习惯用热值单位MWh和吨分别计。2.3 非线性项的线性化处理这个模型里真正的非线性项不多但处理不当照样报错。最常见的是“CCS捕集能耗 单位能耗 × 捕集量”里捕集量取决于连续变量捕集率和排放量的乘积即 (\eta_{ccs}(t)E_{fg}(t)) 是一个双线性项。我用两种办法解决一是直接离散化捕集率档位比如设0.6/0.7/0.8/0.9四档配合二进制变量选档这样双线性项变成线性组合二是用McCormick包络松弛但会有松弛误差。我推荐第一种简单、稳健、物理意义清晰代价只是多几个二进制变量。燃气轮机燃料成本采用分段线性化用3~4段近似二次曲线误差控制在2%以内完全够用。P2G效率和CCS单位能耗我都假设为常数这样全模型就是一个标准的MILP用Gurobi或Cplex几分钟内就能求出24时段最优解。如果你的算例特别大需要做8760小时连续计算建议把捕集率档位进一步减少到2~3档并考虑用滚动时域方法降规模。3. MATLAB实现从算例数据到YALMIP代码3.1 算例数据准备清单和推荐参数这部分直接给一套我当时调试用的“抄作业参数表”你可以按自己的系统改。单位统一为功率MW电量MWh气体量吨或MWh价格元。参数数值说明燃气轮机容量200 MW出力下限50 MW垃圾焚烧容量60 MW最低日处理垃圾量1200 t/d风电容量150 MW预测曲线由外部输入光伏容量100 MW预测曲线由外部输入电储能容量80 MWh最大充放功率20 MWP2G额定功率40 MW效率0.6CO2系数1.83CCS单位捕集能耗0.25 MWh/t捕集率范围0.6~0.9分时电价峰/平/谷 1.2/0.7/0.3 元/kWh时段划分按当地政策天然气价格2.5 元/m³折算热值约3.0元/kWh碳交易价格80 元/t可做灵敏度分析燃气轮机排放系数0.6 tCO2/MWh毛出力口径垃圾焚烧排放系数0.6 tCO2/t垃圾烟气经CCS处理风电、光伏、负荷的24小时预测曲线我一般用历史数据加随机扰动生成或者直接用OpenEI数据集。如果你只是测试模型也可以用正弦曲线模拟负荷峰在早晚高峰风电竞晚大光伏中午大——这组组合本身就容易触发弃风正好能体现P2G的消纳价值。3.2 YALMIP建模核心代码我采用MATLAB YALMIP Gurobi的配置。YALMIP负责建模Gurobi负责求解比手写Cplex API省很多事。关键代码如下% 决策变量 P_gt sdpvar(1, 24, full); % 燃气轮机毛出力 eta_ccs binvar(1, 24, 4); % 捕集率档位选择4档 P_p2g sdpvar(1, 24, full); % P2G耗电功率 E_cap sdpvar(1, 24, full); % 碳捕集量 SOC_es sdpvar(1, 25, full); % 储电SOC V_gas sdpvar(1, 25, full); % 储气罐储量 C_flow sdpvar(1, 24, full); % 购电功率 P_cur sdpvar(1, 24, full); % 弃风弃光功率 % 约束装配 Constraints []; for t 1:24 % 功率平衡注意CCS和P2G在负荷侧 Constraints [Constraints, ... C_flow(t) P_gt(t) P_wte(t) P_w(t) P_pv(t) ... P_es_dis(t) P_load(t) P_ccs(t) P_p2g(t) P_es_ch(t)]; % CCS烟气CO2量与捕集量 E_fg alpha_gt * P_gt(t) alpha_wte * P_wte(t); E_cap(t) E_fg * sum(eta_ccs(t, :) .* eta_levels); % CCS捕集能耗 P_ccs(t) beta_ccs * E_cap(t); % P2G耗碳量与产气量 Q_ch4(t) eta_p2g * P_p2g(t) / H_ch4; E_co2_p2g(t) gamma_co2 * Q_ch4(t); E_co2_p2g(t) E_cap_storage_avail(t); % 储气罐动态 V_gas(t1) V_gas(t) Q_ch4(t) - Q_gas_sell(t); end % 目标函数总成本最小 Objective sum(C_buy .* C_flow) - sum(C_sell .* Q_gas_sell) ... sum(C_fuel .* P_gt) sum(C_carbon .* (E_total - E_quota)) ... sum(C_penalty .* P_cur); % 求解配置 ops sdpsettings(solver, gurobi, verbose, 2, mipgap, 0.01); optimize(Constraints, Objective, ops);这里有几个细节我特意说明一下。捕集率档位我用binvar加四档系数约束里加一行sum(eta_ccs(t,:)) 1保证只选一个档位这样双线性项就变成了“排放量 × 选中的常数”是线性约束。储气罐末端状态我一般约束为不低于初始值避免优化结果把气全卖光这在工程上叫“周期末库存约束”。3.3 结果提取与画图求解之后value()函数提取各变量我习惯画三张图第一张是24小时功率平衡棒状堆叠图直观看到峰谷时段各电源和负荷比例第二张是碳捕集量、P2G耗碳量、储碳罐存量的三条曲线验证“碳流平衡”是否成立第三张是储能SOC和储气罐储量的双轴曲线看能量转移的时段特征。画图时我踩过一个坑时序数据里如果出现功率为负值的小数基本不是真实调度结果而是单位或者符号问题。比如把CCS耗电误放到了电源侧出图后会看到负荷曲线被抬高或压低一块。先检查约束符号再检查画图代码大多数问题一目了然。4. 求解要点与参数调优经验4.1 求解器选择与YALMIP配置MATLAB环境下求解MILP首选Gurobi或Cplex两者在分支定界算法上表现稳定。如果实验室没有商业授权IBM的Cplex有学术版Gurobi也有学术授权实在不行可以用YALMIP自带的CBC开源求解器但中等规模以上算例会明显变慢。我的经验是24时段、捕集率4档96个二进制变量的规模Gurobi求解时间在10秒内CBC可能要几分钟。YALMIP配置Gurobi要注意版本匹配我用的是YALMIP R2023和Gurobi 10.x。装好之后先用一个小测试算例跑通ops sdpsettings(solver, gurobi); x sdpvar(1,1); optimize([x 0, x 1], -x, ops);如果这能正常解出来说明连接成功。如果报No solver found八成是Gurobi的license环境变量没设置或者YALMIP缓存了错误路径执行yalmiptest查看检测结果即可。4.2 场景对比设计无CCS、有CCS、CCSP2G协同为了说明协同价值我固定同一组预测曲线对比三个场景场景总成本万元碳排放t弃风率说明基准无CCS、无P2G186.5105015.8%风电被大量丢弃含CCS但不配P2G191.262012.1%碳排降了但成本上升CCSP2G协同183.85804.6%成本和碳排双降这个结果其实很能说明问题单独加CCS减排效果明显但捕集成本让总成本上升加入P2G后把原本要弃掉的风电用来产气气卖给气网的收益摊薄了CCS的运行成本同时捕下来的CO2成为原料不再需要额外处理或封存费用因此总成本和碳排放同时下降。这正是“协同”两个字的核心价值。实际项目中如果碳价升高到100元以上甚至基准场景也会主动压低火电出力、提高CCS捕集率调度策略对碳价的灵敏度很高。这块建议做完主算例之后做个碳价灵敏度分析画一条“总成本/碳排放随碳价变化”的双轴曲线答辩和论文里非常好用。4.3 数值稳定性与收敛性调优MILP模型最让人头大的不是建模而是求解器“塌了”——表现为Inf或耗时暴涨。我从实践中总结了三条核心经验。大M法的M值不要拍脑袋取10000尽量取该约束物理上限的1.5~2倍。比如购电功率上限是200MWM取300就够。M太大会导致数值病态Gurobi虽然鲁棒性好但也会出现假可行解。所有涉及气体量和电量的单位必须换算到同一时间尺度。我见过把“每小时产气量”和“24小时总产气量”直接相加导致模型不可行的情况排查了一个通宵才发现差了个24倍。建议在数据准备阶段把所有曲线按“小时值”对齐单位统一为MW/MWh/t。捕集率档位离散化后约束矩阵的系数范围会跨越0.001到1000建议在YALMIP里不要做太多scalingGurobi自带的预求解会自动处理。如果遇到数值警告第一反应是查约束里有没有量级相差超过1e6的系数而不是去调求解器参数。5. 常见问题与排查技巧实录5.1 典型报错和对应解法现象可能原因解决办法Infeasible problem功率平衡等式缺项或储碳罐/储气罐约束过紧先删掉储罐约束跑通后再逐步加回No solver foundYALMIP没有正确识别Gurobi运行yalmiptest检查license环境变量求解时间过长二进制变量太多或MIPGap过严mipgap放宽到0.01减少捕集率档位结果出现极小负值非负变量约束缺失或数值误差检查所有sdpvar是否声明为非负或在结果中做容差截断碳排放结果为负碳配额/捕集量的符号写反逐项核对E_total - E_quota - E_cap的结构我最常遇到的是第一种明明单看每个约束都合理组合起来却不可行。排错方法是“从简到繁”先把CCS和P2G从模型中去掉解一个纯VPP调度可行之后再加CCS再加P2G。每加一块如果不可行问题就在刚加的约束里缩小排查范围效率极高。5.2 垃圾焚烧“保底处理”约束的坑这个坑我在第一次建模型时就踩过。因为目标是成本最小化如果不加“最低垃圾处理量”约束优化结果会直接把垃圾焚烧出力压到0因为处理垃圾要花钱。现实中这是不可能的——城市垃圾必须每天处理这不是经济决策而是环保底线。但我第一次跑出来的结果垃圾焚烧出力全程为0我还以为代码写错了查了半天才发现是缺少保底约束。加上 (M_{wte}(t) \geq M_{min}) 之后垃圾焚烧才作为基荷稳定出现在出力曲线上。这个现象也说明了一个建模原则凡是现实世界中“必须做的事情”一定要显式写成约束而不是指望目标函数替你表达。垃圾处理、储能周期末电量、储气罐末期储量、电网备用容量这些“隐性硬约束”缺一个优化结果就会往“取巧”的方向跑表面数值很漂亮实际完全不可用。5.3 捕集率离散化与连续化的精度权衡有些论文用连续捕集率模型变成二次约束规划QCP或非线性规划NLP。我之前也试过连续模型的碳排放可调空间更大结果更“好看”但求解稳定性差稍微改一个参数就小时级跑不完。后来固定用4档离散捕集率结果差异在1%以内求解时间从十几分钟降到几秒。对你写论文来说“离散化捕集率”在物理上也有依据——实际碳捕集系统改变溶剂循环量时本来就接近分档调节所以这不是简化过猛而是贴合工程实际。如果你确实需要连续捕集率建议用分段线性化表达捕集能耗曲线而不是直接乘一个乘积项。比如把捕集率区间分成三段每段内能耗线性递增这样既保留连续可调节性约束形式仍然是线性刚好卡在MILP框架内。5.4 整体调度结果反常识时的自查清单如果结果里P2G从不启动或CCS捕集率总取最小值先别急着调权重按这个顺序查一遍查分时电价峰谷差是否足够大P2G盈利靠的是“低谷买电产气、高峰卖气/卖电”峰谷差小于P2G损耗成本时它当然不启动这不是模型错是经济机制不成立。查弃风量是否够多P2G的另一价值是消纳弃风风电渗透率低时它没有用武之地。查碳价是否超过CCS单位减排成本如果80元/t的碳价低于捕集能耗成本折算值系统当然选择买配额而不是捕碳。最后查储气罐卖出价如果天然气售价低于“谷时电费折算的产气成本”储气罐会空置这同样是经济信号不是bug。这三种“反常识”其实都是模型在按经济学规律做正确决策恰恰说明模型逻辑没问题需要调整的是场景参数让它更贴近你想验证的现实问题。写在最后一点实际调试的小心得这套模型我前后改了三版最深的体会是做能源系统优化物理过程的理解比数学工具更重要。你只有在心里清楚“CO2从哪来、到哪去、中间经过几个罐子”才能写出不自相矛盾的约束。YALMIP和Gurobi只是工具它们不会告诉你“垃圾焚烧不能随便停”、“储气罐末时段最好留一点气”这类隐含条件而这些东西往往决定模型算出来的结果能不能落地。另外建议你拿到代码后第一件事不是改参数而是用一个极简单的2时段系统手工推一遍最优解和程序输出对一下。只要这个对上了扩展到24时段基本不会有大问题。我每次调试新模型都这么做省下来的时间远大于手工计算花的半小时。这个技巧算是这些年做优化调度项目最值钱的经验之一。