做综合能源系统优化的朋友,对“碳交易+电制氢”这个组合应该不陌生。这两年关于IES热电调度的论文,十个里有七八个都绕不开这两个关键词:一边是碳约束越来越严,系统必须为碳排放付出成本;另一边是风光大发时段弃电严重,需要电制氢这样的灵活性负荷来消化。我最近完整跑通了一个MATLAB项目,正是把这两件事耦合在一起:考虑阶梯式碳交易机制,引入电制氢设备,让电、热、氢三种能源在同一个调度框架下协同优化。这篇博文就把整个建模思路、数学表达、代码实现和踩坑经历完整梳理一遍,给准备复现类似工作的读者一个可以直接参考的框架。
如果你正准备做IES优化调度、碳交易机制建模、或者P2H接入系统的研究,这篇文章应该能帮你省下一两周的摸索时间。下面直接进入正题。
1. 项目背景与整体设计思路
1.1 系统里都有什么设备
先把这个综合能源系统的物理拓扑讲清楚。最典型的结构包含这些设备:
- 电源侧:风电机组(WT)、光伏(PV)、上级电网购电、热电联产机组(CHP)
- 热源侧:CHP、燃气锅炉(GB)、电锅炉(EB)、氢燃料电池(FC)产热,部分模型还会计入电解槽废热回收
- 氢环节:电解槽(EL)、储氢罐(HST)、氢燃料电池(FC)
- 负荷侧:电负荷、热负荷、氢负荷
这个拓扑在大量论文里都有出现。实际建模时要注意,如果不加电储能,系统灵活性会弱一些,此时电制氢就承担了主要的跨时段调节作用。既然题目要求“热电优化”,热平衡就是核心约束之一,建议至少要有燃气锅炉作为补充热源,否则在风电低谷、CHP出力受限时,热负荷可能根本满足不了。
1.2 为什么选“阶梯式碳交易”而不是固定碳价
固定碳价本质上是一个线性惩罚项,对系统的影响是均匀的:每多排一吨碳,成本固定增加一个常数。但真实碳市场并不是这样,配额短缺越严重,边际履约成本往往越高。阶梯式碳交易机制就是把碳排放权价格分成几档,超出配额越多,额外购买的碳价越高。
从优化角度看,阶梯碳价是一个分段线性凸函数,相当于给碳排放设置了一个递增的惩罚斜率。这意味着系统在安排调度时,不只是“算总账”,而是会主动控制每个时段的碳排放强度。特别是当阶梯足够陡时,模型会自发倾向于用低碳设备替代高碳设备,比如用氢燃料电池替代燃气锅炉供热。
从工程角度,各省的碳配额分配和履约机制也普遍带有阶梯惩罚性质。所以在项目里引入阶梯式碳交易,并不是为了增加模型复杂度,而是让优化结果更贴近政策实际。
1.3 电制氢在系统里的作用不是“多一个设备”那么简单
很多新手看到P2H,第一反应是“就是一个电负荷嘛”。这个理解太浅了。P2H同时具备“可转移的电负荷”和“可再生的氢源”双重属性。
在风电大发时段,电价低、弃风量多,电解槽启动,把多余电能转化为氢气储存起来;在热负荷高峰或电负荷高峰时段,燃料电池释放氢能,同时提供电和热。这样一来,系统实际上多了一条“电→氢→热/电”的能量搬移通道,原本“以热定电”的热电耦合关系被打破了。
举个具体例子:没有P2H时,冬季夜间热负荷高,CHP为了保证供热必须维持较高电出力,而此时风电也处于高峰,系统电出力严重过剩,唯一的选择是弃风或者向电网低价售电。有了P2H之后,这部分多余电力驱动电解槽制氢,氢气存起来留到白天峰时段通过FC放能,系统灵活性显著提升。所以做这个项目时,不要只把P2H当成一个耗能设备去建模,它真正的作用是给系统增加一个跨时段、跨介质的灵活性资源。
2. 阶梯式碳交易机制建模
2.1 碳配额与实际碳排放核算
碳交易建模的第一步是搞清楚“配额从哪来、排放怎么算”。
免费配额通常有两种给法。第一种是按系统总负荷乘以基准排放强度,比如系统电负荷和热负荷各乘以一个碳排放基准值,加总得到该周期的免费配额。第二种是按各碳排放源分别给配额系数,比如CHP每输出1MWh电力给一个碳配额,燃气锅炉每输出1MWh热力给一个配额。我建议用第二种,因为各设备的配额系数可以单独调整,做敏感性分析时更灵活。
实际碳排放的核算范围要小心,通常包括三部分:
- CHP燃烧天然气的直接排放,按电出力或燃料消耗量折算
- 燃气锅炉燃烧天然气的直接排放
- 外购电力的间接排放,按电网平均排放因子折算
写成公式就是:
E_actual = sum(λ_chp * P_chp + λ_gb * Q_gb + λ_grid * P_buy)其中λ是各环节的排放系数,单位是tCO₂/MWh。需要注意燃料折算逻辑:如果是按燃料消耗量算,要先从热出力反推天然气消耗量,再乘以燃料排放因子;如果按出力乘以排放系数,则是简化处理,适合规划层面的调度模型。
配额和实际排放都算清楚之后,得到配额缺口:
x = E_actual - E_quota这个x就是进入阶梯碳价计算的输入量。
2.2 阶梯碳价的分段线性化表达
阶梯碳价的核心思想是:配额缺口越大,单位碳价越高。常见设置是每段长度d(比如50吨),基准碳价为c₀,第一段价格c₀,第二段价格2c₀,第三段价格3c₀,以此类推。
数学上要处理的是一个分段线性函数。如果直接用if-else逻辑,在优化模型里是行不通的,必须线性化。标准做法是引入0-1变量和Big-M约束。
假设阶梯分3段,引入二元变量z₁、z₂和连续变量v₁、v₂,分别表示第1段和第2段是否启用、每段的购买量:
x = v₁ + v₂ 0 ≤ v₁ ≤ d * z₁ 0 ≤ v₂ ≤ d * z₂ v₂ ≥ d * z₁ # 第2段启用之前,第1段必须填满 z₂ ≤ z₁ # 必须第1段激活后才能激活第2段 z₁, z₂ ∈ {0,1}这套约束的逻辑是:低价段必须优先填满,只有第1段达到上限d之后,第2段才会启用。由于碳价单调递增,目标函数是成本最小化,模型天然倾向先用低价段,所以实际上不需要额外强制约束,但加上写段约束会让模型更严谨,尤其是做敏感性分析时。
对应碳交易成本为:
C_CO2 = c₀ * v₁ + 2c₀ * v₂如果允许系统出售富余配额,即x为负数的情况,可以在模型中增加一个卖出变量v_sell,按基准碳价获得收益:
C_CO2 = c₀ * v₁ + 2c₀ * v₂ - c₀ * v_sell实际项目中,很多论文设定碳交易只考虑购买配额(即x≥0),这样模型简单一些,但会失去“卖出富余配额”这一层经济反馈。我建议把卖出也加上,因为当系统引入大量P2H和FC之后,碳排放可能会低于配额,这时候卖出收益是影响结果的一个真实因素。
2.3 整数变量的Big-M取值实践
阶梯碳价建模中,最容易被忽视的坑是Big-M常数。M如果取得太大,LP松弛会变得很松,混合整数规划求解时分支定界效率极低,本来几秒能解完的问题可能要几分钟;但如果M取太小,又可能把可行域截掉,导致模型约束失真。
我的经验做法是:先注释掉碳交易约束,用纯线性规划跑一遍模型,记录系统的最大碳排放量,然后用这个值乘以1.2到1.5倍作为M,确保M始终大于变量的实际取值范围。
在YALMIP里面,提供了implies函数可以处理逻辑约束,但传统线性不等式写法效率更高。你可以两者都试一下,看自己用的求解器对哪种形式求解更快。
3. 电制氢环节建模与热电协同
3.1 电解槽模型与产热回收
电解槽的建模相对简单:输入电功率P_el,输出氢功率H₂_prod,效率η_el通常在0.6到0.7之间(按氢的高热值计):
H₂_prod = η_el * P_el除了产氢,电解槽运行时会伴随产热,这部分热量在冬季供热场景中是有价值的。可以在模型里加一个热回收系数η_heat,取值大概在0.2到0.4之间:
Q_el_rec = η_heat * P_el但这个项是否加入,取决于你设定的系统结构。如果只是做“热电联供”层面的优化,忽略废热回收影响不大;如果热负荷很紧张,废热回收就会在结果中体现为“电解槽在供热时段有额外收益”,会改变电解槽的运行策略。实操中建议先不加,等基础模型跑通后再把这个细节加进去,对比一下差异。
电解槽的约束还有出力上下限:
P_el_min ≤ P_el ≤ P_el_max以及爬坡约束,但电解槽本身就是快响应设备,在小时级调度中一般不需要限制爬坡速度。这一点和CHP不同,我后面会展开说。
3.2 储氢罐的动态状态约束
储氢罐对跨时段能量搬移至关重要,它把电解槽的产氢和燃料电池的用氢在时间维度上解耦。
储氢罐的动态模型用SOC(荷电状态)表示:
SOCₜ₊₁ = SOCₜ + (H₂_in - H₂_out) * Δt其中H₂_in来自电解槽产氢,H₂_out供给燃料电池或直接氢负荷。SOC有容量上下限:
0 ≤ SOCₜ ≤ SOC_max这里有一个实操中非常容易踩的坑:周期初末约束。很多论文要求SOC_1 = SOC_T,即调度周期开始时和结束时的储氢量一致,称为“循环一致性”。这个约束听起来合理,但实际求解时是强约束,如果系统里氢负荷不稳定,很可能导致模型无解。
我建议改成初始储氢量设为固定值,期末储氢量给一个可行范围,比如:
SOC_1 = 0.2 * SOC_max 0.2 * SOC_max ≤ SOC_T ≤ 0.8 * SOC_max这样既保证了储氢罐在周期内有调节空间,又避免了强等式约束造成的可行域收缩。等模型全部跑通、结果稳定了,再回过头试严格的循环一致性约束,看是否有可解空间。
3.3 氢燃料电池:热电联产的“可调节阀门”
氢燃料电池是P2H链条的最后一环,作用是释放氢能,同时提供电和热。模型如下:
P_FC = η_FC_e * H₂_in_FC Q_FC = η_FC_h * H₂_in_FC其中H₂_in_FC是燃料电池消耗的氢功率(按热值计),η_FC_e是发电效率,η_FC_h是产热效率,两者之和通常为0.8到0.9左右。
这里有个关键选择:FC的热电比是固定还是可调。如果固定,则:
Q_FC = R_FC * P_FC其中R_FC是一个常数,典型值在1.0到1.5之间,取决于FC类型。如果用可调模型,电效率和热效率之间会有耦合关系,这就变成非线性问题了。对于24时段的小规模MILP模型,固定热电比是主流做法,简单且收敛可靠。
FC在系统中的角色,简单说就是“把氢变成电和热的转换器”。它的价值在于:当碳价高、气价高、电价也高的时候,用储存的氢来发电供热,可以同时规避三方面的成本压力。
3.4 P2H如何改变系统的热电运行方式
把以上设备串起来,就能看出P2H对系统运行方式的本质改变。
没有P2H时,系统只有两条能量路径:CHP供给电和热,GB补热。系统必须围绕热负荷调整CHP出力,电力过剩就弃风或卖电。这是典型的“以热定电”。
有P2H之后,系统出现第三条路径:电→氢→电/热。谷电时段(风电大发或电价低),电解槽吸收多余电力产氢储氢;峰电时段,FC放氢产电产热。于是系统运行方式变成:
- 夜间风电大发、电价低时:电解槽启动,吸收多余电力,CHP可以适当降低出力,热负荷由GB补充
- 白天峰电、热负荷高时:FC放氢供能,替代部分购电和GB用气
- 碳价高时:系统会尽量多用氢能、少用天然气,减少碳排放
这个机制的本质是“低谷买电制氢、高峰放能”,把氢能当作跨时段储能来用。调度模型会自发地在碳价、气价、电价三者的博弈中优化出最优策略。所以从这个角度重新审视题目中的“热电优化”,P2H让原本单纯的热电联供变成了“电-热-氢”的联合优化,这个扩展才是这个项目真正值得做的原因。
4. 优化目标与约束体系
4.1 目标函数:四项成本怎么平衡
整个优化模型的目标函数是总运行成本最小化,包含以下几项:
min F = C_grid + C_gas + C_CO2 + C_curtail + C_om- 购电成本C_grid:从上级电网购电的费用,按分时电价计算,公式为sum(P_buy_t * price_t * Δt)
- 购气成本C_gas:购买天然气的费用,按热值计价,公式为sum(Q_gas_buy_t * gas_price * Δt)
- 碳交易成本C_CO2:按第2章的阶梯碳价模型计算,这是整个项目最核心的成本项
- 弃风惩罚C_curtail:为了避免模型为了省钱而随意弃风,给弃风加一个单位惩罚系数。这个系数一般取得比边际电价略高,比如每MWh弃风惩罚100到200元,让模型在“弃风”和“其他调节手段”之间做一个折中
- 运维成本C_om:各设备的单位出力运维成本,包含CHP、GB、EB、电解槽、FC等
这里有一个非常重要的工程细节:单位统一。如果功率用MW,时间间隔Δt=1小时,则能量单位是MWh,价格单位用元/MWh,成本自动就是元。如果功率用kW,价格用元/kWh,则成本是元的千分之一。写代码前务必把单位确定下来,不然后面检查结果时非常痛苦。
4.2 电、热、氢三类平衡约束
能量平衡约束是整个模型的骨架。三类平衡必须分别满足:
电平衡:
P_wind + P_pv + P_chp + P_fc + P_buy + P_dis = P_load + P_el + P_eb + P_char左边是电源提供,右边是负荷和用电设备消耗。如果系统中有电储能,放电P_dis、充电P_char需要区分开;如果不加电储能,就把这两项去掉。注意:电储能充放电不能同时进行,要么用同一个变量正负表示并加约束,要么引入二元变量。
热平衡:
Q_chp + Q_gb + Q_eb + Q_fc + Q_el_rec = Q_load热平衡通常不考虑管网损耗,按严格平衡处理。如果热负荷数据本身波动很大,允许加一个很小的松弛变量也可以,但基础模型建议严格平衡,这样问题更清晰。
氢平衡:
H₂_prod_el - H₂_in_fc - H₂_load - (SOCₜ₊₁ - SOCₜ) = 0这个式子把电解槽产氢、储氢罐充放、FC耗氢和氢负荷串在了一起。
这三类平衡约束看似简单,但实际写代码时特别容易漏项。我遇到过的情况是:电平衡里忘了加电解槽的耗电,结果系统凭空多出一部分电,算出来的购电成本偏低,结果完全失真。建议每加一个设备,先回到三个平衡方程里检查有没有遗漏对应项。
4.3 CHP可行域与机组爬坡约束
CHP的建模方式会直接影响结果质量。简单模型用线性热电比:
Q_chp = c_m * P_chp这种模型相当于强制CHP的热电出力比例固定,操作简单但过度约束了机组的灵活性。实际抽凝式CHP可以在一定范围内调整热电比,用四边形可行域建模更准确。
四边形可行域在P-Q平面上用四个顶点定义,约束写成几组线性不等式。以常见的抽凝式机组为例,可行域大概包含这四个约束方向:
- 最大电出力上限
- 最小电出力(与热出力相关)
- 最大热出力限制
- 最小冷凝工况下的电出力下限
用YALMIP实现时直接写四组不等式即可,形式上和普通上下限约束没有区别,只是约束矩阵复杂一些。
爬坡约束方面,我建议加在CHP和GB上:
|P_t - P_{t-1}| ≤ ramp * Δt注意t=1时需要设定初始出力。电解槽和FC响应速度快,在小时级调度里不需要爬坡约束,加了反而增加求解负担。
4.4 约束取舍的实战经验
这条可能是整个项目里最“实际”的建议:约束不是越多越好。
很多刚做这个课题的同学喜欢把所有设备都加上爬坡、上下限、最小启停时间等约束,模型规模迅速膨胀,求解时间从几秒变成几分钟,甚至直接不可行。我的经验是:
- 24时段小规模模型,爬坡约束加在CHP和GB上即可
- 电储能如果没有明确要求,可以先不加,减少二元变量
- 储氢罐初末约束设成宽松范围,不要用严格等式
- 如果只是复现论文结论,可以先求一遍无整数变量的松弛模型,确认可行域没有问题,再加整数变量
先跑通再丰富,好过一次到位但解不出来。
5. MATLAB实现:从数学到代码
5.1 整体代码结构
我强烈建议把代码模块化,不要所有东西堆在一个脚本里。推荐目录结构:
- main.m:主程序,定义参数、调用构建函数、求解
- params.m:参数定义文件,包括负荷、风电、电价、气价、碳价、设备参数
- build_vars.m:定义所有决策变量(YALMIP语法)
- build_constraints.m:构建全部约束
- build_objective.m:构建目标函数
- solve_and_post.m:求解、提取结果、绘图
这种模块化的好处是显而易见的:改参数只需要动params.m;换设备拓扑只需要改约束构建部分,不用动主程序。我做完这个项目之后,做多场景对比(固定碳价vs阶梯碳价、有P2Hvs无P2H)只需要写一个循环脚本,非常方便。
5.2 YALMIP建模关键代码
核心建模代码如下,关键部分加注释说明:
% 决策变量定义(T=24时段) P_chp = sdpvar(1, T); % CHP电出力 Q_chp = sdpvar(1, T); % CHP热出力 P_gb = sdpvar(1, T); % 燃气锅炉热出力 P_el = sdpvar(1, T); % 电解槽电功率 P_fc = sdpvar(1, T); % 燃料电池电功率 Q_fc = sdpvar(1, T); % 燃料电池热出力 SOC_hst = sdpvar(1, T); % 储氢罐储氢量 P_buy = sdpvar(1, T); % 购电功率 P_wind_use = sdpvar(1, T); % 风电实际出力 % 阶梯碳交易变量 z = binvar(K-1, T); % 0-1变量,K=3时有两个二进制变量 v = sdpvar(K-1, T); % 每段的配额购买量约束定义示例:
Constraints = []; % 电平衡 Constraints = [Constraints, P_wind_use + P_pv + P_chp + P_fc + P_buy == ... P_load + P_el + P_eb]; % 热平衡 Constraints = [Constraints, Q_chp + Q_gb + Q_eb + Q_fc == Q_load]; % 氢平衡与储氢罐SOC更新 Constraints = [Constraints, SOC_hst(2:end) == SOC_hst(1:end-1) + ... (eta_el*P_el(1:end-1) - P_fc(1:end-1)/eta_fc_e - H2_load(1:end-1))];注意这个氢平衡公式中,P_fc是FC的电出力,要换算回氢输入功率,需要用FC的发电效率做除法:H₂_in_FC = P_fc / η_FC_e。
阶梯碳交易的线性化约束:
% 实际碳排放 E_actual = lambda_chp * sum(P_chp) + lambda_gb * sum(Q_gb) + lambda_grid * sum(P_buy); E_quota = quota_chp * sum(P_chp_max) + quota_gb * sum(Q_gb_max); x = E_actual - E_quota; % 阶梯分段线性化 x = sum(v, 1); % 配额缺口由各段购买量组成 for k = 1:K-1 Constraints = [Constraints, v(k,:) >= 0, v(k,:) <= d * z(k,:)]; if k > 1 Constraints = [Constraints, v(k,:) >= d * z(k-1,:)]; end end % 单调激活:后续段必须先激活前续段 for k = 1:K-2 Constraints = [Constraints, z(k+1,:) <= z(k,:)]; end % 碳交易成本(假设3段价格分别为c0, 2c0, 3c0) C_co2 = c0 * (sum(v(1,:)) + 2*sum(v(2,:)) + 3*sum(v(3,:)));这里需要特别提醒:上面是逐时段做阶梯分段,即每个时段单独计算配额缺口和阶梯成本。另一种做法是对整个调度周期汇总碳排放后一次性结算,这取决于你的碳配额结算周期假设。两种方式都有人用,但要保证前后一致,不要混用。
5.3 求解器配置与求解
YALMIP本身不是求解器,它只是一个建模层,真正求解需要调用底层求解器。推荐用Gurobi,求解MILP的效率和稳定性都非常好。求解设置如下:
ops = sdpsettings('solver', 'gurobi', 'verbose', 2, ... 'gurobi.MIPGap', 0.01, 'gurobi.TimeLimit', 300); sol = optimize(Constraints, Objective, ops); if sol.problem == 0 disp('求解成功'); else disp(['求解失败,错误码:' num2str(sol.problem)]); yalmiperror(sol.problem); endMIPGap设置到0.01(1%)通常足够了,工程上不需要证到全局最优。TimeLimit设300秒,万一模型卡住也能拿到一个可行解(如果求解器输出的话)。
如果你不想装Gurobi,用MATLAB自带的intlinprog也可以,但需要在YALMIP里指定求解器为intlinprog,且不能修改Gurobi特有的参数。我实测下来,这种几十个二元变量、几百个连续变量的MILP模型,Gurobi几秒就能解完,intlinprog稍慢一些但也在可接受范围。
5.4 后处理与结果可视化
求解后提取各个变量的结果:
P_chp_opt = value(P_chp); Q_chp_opt = value(Q_chp); P_fc_opt = value(P_fc); SOC_opt = value(SOC_hst);画图方面,推荐两类图:
一是电平衡堆叠图,用area命令把电源(风电、光伏、CHP、FC、购电)按正方向堆叠,把负荷(电负荷、电解槽、电锅炉)按负方向堆叠,形成上下镜像的balance图,这是论文里最常见的形式。
二是成本构成柱状图,把购电成本、购气成本、碳交易成本、弃风惩罚分别画成柱状,不同方案放一起对比,一眼能看出碳交易和P2H对成本结构的影响。
6. 关键结果分析与有效性验证
6.1 怎么证明模型是对的
模型做完之后,不能只看“求解成功”就觉得万事大吉,要花时间做对比验证。我建议至少做三组对比:
第一组:基准系统(无P2H、固定碳价)与含P2H系统的结果对比。这组对比验证P2H的有效性——加入电解槽和FC后,弃风率应该下降,总成本应该有所变化。如果含P2H的系统弃风率反而上升,或者总成本比基准系统还高,那要检查是不是FC效率设置太低、储氢罐容量太小,或者P2H的设备成本参数有问题。
第二组:固定碳价与阶梯碳价的对比。固定碳价下,系统只需要对总碳排放量做一个线性惩罚;阶梯碳价下,系统会主动控制碳排放峰值的时段分布。对比结果应该看到:阶梯碳价下系统的碳排放总量更低,但碳交易成本可能反而更低,因为系统提前优化了排放行为。
第三组:不同碳价基数、不同阶梯长度的敏感性分析。这种分析能证明模型对碳价变化的响应是合理的——碳价越高,P2H利用小时数越高,碳排放下降越明显。
6.2 碳价敏感性分析怎么做
敏感性分析的操作很简单:写一个for循环,把碳价基数c₀从50扫到300元/吨,步长50,每次重新求解模型,记录总成本、碳排放量、弃风率、P2H产氢量,最后画双轴图。
预期结果是:随着碳价上升,碳排放量单调下降,P2H产氢量上升,弃风率下降。如果曲线出现非单调的突变,说明模型里可能存在数值问题或者约束不够平滑。
我实际跑的时候发现一个有意思的现象:碳价从50升到100时,碳排放量下降非常明显,因为系统从容地提高FC出力、减少GB产热;但从150升到300时,碳排放下降变得平缓。这是因为CHP的最小出力约束和储氢罐容量限制了系统进一步减排的能力。这个现象本身就是一个很好的分析点,可以写进结论里。
6.3 从结果里找异常的方法
运行结果中出现一些“难以解释”的现象时,不要怀疑模型的逻辑,先去查参数设置。
如果碳交易成本出现负数,检查是否允许卖出配额。如果不允许,需要在约束里加x≥0,或者设置富余配额不能产生收益。
如果FC出力一直为0,大概率是FC效率设置太低,导致“制氢-储氢-放氢”全链条效率经济性不如直接购电购气。这是最常见的问题:整个P2H链条的综合效率大约0.6×0.5=0.3左右(电解效率0.6、FC发电效率0.5),如果不考虑峰谷电价差和碳成本,P2H往往经济性不如直接购电。只有在电价峰谷差足够大、或者碳价足够高的情况下,P2H才能体现价值。
如果储氢罐SOC一直处于上限或下限,说明储氢罐容量设置偏小或偏大,调整容量参数即可。没必要因为这个怀疑模型错误。
7. 常见问题与排查经验
7.1 不可行解怎么排查
这是这个项目里出现频率最高的问题,没有之一。一旦遇到,按以下顺序排查:
- 查看
sol.problem返回的错误码,确认是Infeasible还是Numerical issue - 把所有负荷和出力画在一张图上,看哪个时段明显不平衡
- 临时注释掉整数约束,只求松弛LP,看是否可行。如果LP可行而MILP不可行,问题出在整数约束上;如果LP也不可行,问题出在平衡约束或设备上下限上
- 检查储氢罐初末约束,这是最容易导致不可行的地方。把
SOC_T的范围放宽,或者把初始SOC设为外生变量 - 检查电平衡约束里有没有漏掉设备的耗电项
7.2 求解时间过长
原因通常有三个:
第一个是二元变量太多。阶梯碳价如果逐时段建模,24时段×3段=72个二元变量,求解时间可能偏长。可以考虑改为全局碳结算——整个调度周期只算一次总碳排放量的阶梯成本,二元变量降为2-3个。两种建模的经济含义不同,但如果你只是快速验证,全局结算能省很多时间。
第二个是Big-M参数太大。M取变量实际范围的1.2到1.5倍即可。M过大会让LP松弛质量变差,分支定界效率骤降。
第三个是MIPGap设置过小。设到0.01就够了,如果完全不设,求解器默认追求全局最优,时间会指数上升。
7.3 YALMIP报错“No suitable solver”
这是环境问题。YALMIP自带的bnb求解器求解MILP非常慢,几十个二元变量可能就要跑很久。建议安装Gurobi或者至少用MATLAB自带的intlinprog。在YALMIP里检查求解器可用性:
yalmiptest这个命令会列出所有可用求解器。确认Gurobi出现在列表中,然后用sdpsettings('solver','gurobi')调用。
7.4 数值问题导致结果不连续或震荡
现象:相邻时段同一个设备出力突变,不符合物理规律。原因通常是:没有爬坡约束、Big-M过大导致数值精度丢失、或者单位不统一导致目标函数中某些项权重失衡。
解决方案就是之前强调的:统一单位为MW和元/MWh,给CHP和GB加爬坡约束,检查各项成本的量级是否在一个数量级内。如果购电成本是几十万元级别,而碳交易成本只有几百元级别,目标函数中碳交易的影响会被忽略,得出的结果看起来就像“碳价没起作用”。
7.5 复用与扩展的小技巧
这个项目框架最大的价值是扩展性好。跑通之后,后续可以在此基础上做很多扩展:
- 多典型日场景:把单一天场景扩展为多个典型日,加入场景概率
- 考虑不确定性:用鲁棒优化或随机优化处理风电出力不确定性
- 加入电储能和储热装置,进一步丰富系统灵活性资源
- 将固定效率的FC改为变效率PWL模型,细化设备模型
- 考虑需求响应,让部分电负荷和热负荷具备可平移性
我个人的体会是,这个模型框架最值钱的地方在于:一旦设备模型和约束体系写清楚,之后换设备、换机制、换场景都是“搭积木”式的修改,而不是推倒重来。
最后说点个人体会。这类IES优化项目,很多时候难的不是数学本身,而是把物理过程抽象成数学表达、再把数学表达落成可调试代码的过程。我做完这个项目的一个明显感受是:阶梯碳交易和P2H的真正价值,不是让模型变得更复杂,而是让系统多了一个经济信号(碳价)和一个物理自由度(氢能储放)。两者叠加之后,很多原本“被迫弃风”或“以热定电”的困境,会在优化结果里自动消失。如果你也在做类似的课题,建议先把基准系统跑通,再一步步加机制。这个项目整体上是个性价比很高的练手对象:模型不算复杂,但结果很能说明问题。希望这篇梳理能帮你少走点弯路。