ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

风光制氢合成氨系统容量与调度联合优化:建模及Matlab实现

风光制氢合成氨系统容量与调度联合优化:建模及Matlab实现 做风光制氢合成氨的容量配置最容易被忽略的其实是“调度策略”和“装机容量”之间的耦合关系。很多人一开始只盯着风机、光伏、电解槽各装多少兆瓦用年发电量除以电耗估算一下电解槽规模就算完了。但真正跑起来之后会发现问题远没有这么简单风光出力波动导致电解槽频繁启停、储氢罐要么常年顶满要么见底、并网模式下购电策略和离网模式下甩负荷策略完全是两套逻辑而这些反过来都会直接影响最优容量方案到底是多少。我复现这套“并/离网风光互补制氢合成氨系统容量-调度优化”时最大的感触就是容量优化和小时级运行调度必须联合建模迭代寻优否则算出来的方案基本没法落地。这篇文章把整个建模和Matlab实现过程拆开讲透包括风机/光伏出力序列怎么处理、制氢合成氨各单元的效率模型怎么简化、双层优化结构怎么搭、用哪种求解器合适、结果怎么看以及我复现过程中踩过的几个比较典型的坑。想自己动手跑一遍的照这个思路走基本能复现出来。1. 为什么容量配置非要和调度优化绑在一起先讲一个最直观的反例。假设某地风光资源很好年等效利用小时数分别能到2200小时和1500小时你配了100 MW风、50 MW光按电解槽耗电50 kWh/kgH2来算年产氢量似乎等于总发电量除以电耗。但实际上电解槽不可能全年满负荷跑——它有最小技术出力限制比如某些碱性电解槽低于30%额定功率时产气纯度不达标必须停机风光出力低于这个门槛的时候要么ESS储电顶着要么直接切掉这部分电量。而合成氨装置更苛刻合成回路通常要求连续稳定运行频繁启停对催化剂是灾难。所以运行策略直接决定了有效产氢量同样的装机容量如果调度策略是“光伏和风电谁有电谁就驱动电解槽”可能实际利用率只有设计值的75%如果加入储氢缓冲、合理分配电解槽启停台数、并网时在谷电时段补充购电利用率能提到88%以上。最终算出来的平准化制氢成本这两者能差出15%到20%。这里的核心矛盾是容量配置决定了系统“上限”调度策略决定了在这个上限之下实际能兑现多少。而调度策略又反过来受容量约束——储氢罐小了多余的氢没地方放电储能配大了投资成本吃掉项目收益。只有把外层容量决策和内层小时级调度放在同一个迭代框架里让每次容量方案都用最优运行策略去评估它的真实经济性最终搜出来的解才是能落地的方案。这也是我在复现这类文献时特别留意的点论文里经常写“采用双层优化模型外层用改进粒子群算法搜索容量内层用线性规划求解日调度”看起来轻描淡写实际代码里内层调度的目标函数、约束矩阵和变量维度写起来相当繁琐尤其是并网和离网两种模式还共用一套容量变量、各自跑调度评价代码结构组织不好就很容易混乱出bug。2. 系统建模从物理单元到可计算的数学表达做容量优化不能直接拿仿真软件里的精细模型去跑计算量扛不住也没有必要。核心思路是把每个单元的稳态特性用效率曲线或简化能量平衡关系表达时间尺度取1小时一年8760个小时铺开。下面逐个单元过一遍建模细节。2.1 风光出力序列数据敏感度比模型复杂度更值得关注风电的时序出力用威布尔分布结合风速自相关特性生成也行但从气象站或当地风电场获取真实测风数据或再分析数据更好因为容量优化结果对风资源时序相关性非常敏感——光伏白天发电、风电往往夜间大两者互补性直接决定储氢罐和电储能的配置规模。风速到功率的转换我用的是一段式近似模型切入风速3 m/s额定风速12 m/s切出风速25 m/s中间段按三次方关系过渡function Pw windPower(v, vci, vr, vco, Pr) Pw zeros(size(v)); idx1 v vci v vr; idx2 v vr v vco; Pw(idx1) Pr .* (v(idx1).^3 - vci^3) / (vr^3 - vci^3); Pw(idx2) Pr; end光伏出力则考虑辐照度和温度的影响标准测试条件STC下的额定功率乘以归一化辐照度再叠加温度系数的损失Ppv P_stc .* (G ./ 1000) .* (1 - 0.004 .* (Tcell - 25));实际复现时一个比较简单的做法是用Matlab的readmatrix读入8760×3的真实时序数据风速、辐照度、温度[Pw, Ppv]算出来直接作为后续优化的输入矩阵。如果数据时间分辨率是15分钟建议聚合成小时级再跑否则内层调度问题规模翻四倍收敛时间增加不止四倍。2.2 制氢单元效率曲线别用固定电耗常数会失真碱性电解槽在20%~100%负载区间内的效率并不是一条水平线。低负载时热损失占比大电耗偏高高负载时电流密度增大过电位上升电耗又上去了。中间某个负载点效率最高。更麻烦的是电解槽通常由多个小电解槽组成运行台数和单台负载率共同决定整体电耗。我建模时直接把电解槽群离散成多台机组每台机组功率范围设为额定功率的20%~105%电耗近似为负载率的二次函数% 单台电解槽电耗系数: a*load^2 b*load c单位 kWh/kgH2 % load 为该电解槽当前功率与额定功率之比 eff (load) a * load.^2 b * load c; kgH2 Pel ./ eff(load);这个二次函数系数从电解槽厂商数据和文献实测值拟合通常a在5~15之间b在40~47附近c在48~55左右。代入实际计算就能看到让电解槽群在40%~75%负载区间低电耗运行比全开满负荷更划算——这就给内层调度优化创造了“削峰填谷”的空间。2.3 储氢罐、氨合成回路、电储能的建模取舍储氢罐的建模很简单就是容量连续性方程S(t1) S(t) (H2_prod(t) - H2_to_nh3(t) - H2_dump(t)) * eta_storage - H2_for_ammonia注意氢在储运和压缩过程中有损耗一般每经过一级压缩按2%~3%损耗算。设置S的下限为总容量的5%以避免储罐完全排空。合成氨工段Haber-Bosch工艺的耗氢量按化学计量关系折算每生产1吨氨需要约176.5 kg氢气同时需要氮气、电耗和一定热耗。氨合成回路的负载率通常不允许太低连续流化工装置一般维持在50%~100%负载。这个约束在离网模式下相当有杀伤力——当风光出力持续偏低、储氢又耗尽时合成氨装置要么减载到50%以下实际不允许要么整条线停车而停车再启动的代价极大。为了避免模型复杂化我采用了一个比较务实的处理允许合成氨装置在30%~100%范围调节负荷但在目标函数里加上“负荷率偏离额定越远、单位氨电耗越高”的惩罚项。电储能锂电池采用简化能量平衡模型充放电效率取95%荷电状态SOC限制在10%~90%。储电在并网模式下的作用不是特别大主要用于平滑风光短时波动因此容量一般不会配得很大但在离网模式下电储能对削掉光伏午间峰值、维持夜间电解槽最低出力有决定性作用。2.4 并网与离网两套运行规则一个优化框架并网模式下系统可以和电网双向交互风光出力富余时向电网售电出力不足时从电网购电。对外部电网的购电价和上网电价分别取实时分时电价曲线。根据当前能源政策发给电网的电不算绿氢所以在目标函数里用“由风光绿电驱动的电解槽功率占比”作为约束条件绿电占比必须大于一定阈值比如85%否则氢的“绿色溢价”就没了。离网模式下系统完全不依赖电网任何时刻必须满足功率平衡Pwind(t) Ppv(t) Pdischarge(t) Pgrid_purchase(t) Pel(t) Pload_aux(t) Pcharge(t) Pcurtail(t)离网模式中Pgrid_purchase恒为0多出来的风光电量只能通过电储能、电解槽和弃电来消纳。弃电在目标函数里是惩罚项但在某些极端天气时段弃电反而是最优决策——这一点新手常犯的错误是强制要求100%消纳结果把储氢罐和电储能成本抬得极其离谱总成本反而上升。3. 双层优化模型的结构与关键约束容量配置和运行调度联合优化的问题严格来说是一个大规模混合整数非线性规划MINLP决策变量既包含连续的容量值又包含8760个小时内每个设备的启停状态和功率分配。直接丢给求解器变量维度到了十万量级基本无解。所以业内主流做法就是双层分解外层搜容量内层做调度。3.1 外层容量层决策变量和目标函数外层决策变量就是待求的容量组合x [P_wind_rated, P_pv_rated, P_el_rated, E_h2, E_battery, N_el_unit]分别对应风电场额定功率、光伏额定功率、电解槽群总额定功率、储氢容量、电储能容量、电解槽机组台数。外层目标函数是年化总成本最小化包括年化投资成本、运维成本和燃料/购电成本再减去售电和副产品收益min Cost C_inv C_om C_elec - C_rev注意容量变量的量纲不同投资成本的单位也要统一。我的做法是除以设计寿命转化为等年值成本annualized cost折现率取6%~8%风电场寿命20年、光伏25年、电解槽10年、储氢罐20年、锂电池10年。不同设备寿命不同淘汰后需要再投资这套现金流折算逻辑别忽略了。3.2 内层调度层线性化与混合整数处理内层调度以小时为步长在给定容量组合下求解一年8760个小时的最优运行策略。目标函数是运行成本最小化包括购电成本、启动惩罚和弃电惩罚。电解槽群存在启停状态变量u_el(t)这引入了一组0-1整数变量。通常一台电解槽的启停状态在一天内的切换不能太频繁所以还要加一个“每天启停次数上限”的约束否则调度算法会为了追求微小电耗优化而频繁启停现实中这是不允许的。为了降低求解难度我把电解槽群的功率离散成N个挡位每个挡位对应一个运行台数和单机负载率组合。这样连续非线性问题变成了混合整数线性规划MILP问题直接用intlinprog就能解。挡位取6~8个就足够了比如“5台电解槽各80%负载”“4台电解槽各60%负载、1台备机”等等。挡位太多会让求解时间指数上升收益却很小。储氢的连续性约束也要做时间耦合处理S_h2(t1) S_h2(t) eta_store * H2_prod(t) - H2_nh3(t) - H2_extra(t)这个式子本身是线性的但要注意储氢容量E_h2是外层传来的参数内层不优化它。这一点非常关键保证了内层始终是线性/MILP问题外层换容量值后重新求解即可。3.3 双层之间的信息传递和收敛判断整个迭代流程是这样的外层粒子群生成一组容量组合x把x传给内层内层按全年8760小时求解调度算出一组运行成本和弃电量把运行成本返回给外层外层按总成本评估这组容量方案的适应度粒子群根据适应度更新粒子位置也就是更新容量组合迭代到最大代数或满足收敛条件。收敛条件我常用“粒子群全局最优解连续20代变化幅度小于1e-4”或者直接固定迭代次数比如60代。粒子数取30~40比较合适太少容易陷入局部最优太多外层求解次数成倍上涨。一个容量方案的内层求解如果耗时3~5秒60代×40粒子就是2400次内层求解整体要跑两三个小时。所以对求解效率的优化相当重要。4. Matlab实现流程与核心代码拆解整个Matlab工程我是按四个模块组织的数据输入模块、系统建模模块、内层调度求解器、外层优化主循环。4.1 数据输入与参数初始化用一个结构体存所有设备参数清晰直观params.Pr_wind 5000; % 候选风电场额定功率范围上限 [kW] params.Pr_pv 5000; % 候选光伏额定功率上限 [kW] params.Pr_el 6000; % 候选电解槽功率上限 [kW] params.price_buy [0.5 0.8 1.2]; % 谷/平/峰购电价 [元/kWh] params.price_sell 0.35; % 上网电价 [元/kWh] params.invest_wind 6800; % 风电场单位投资 [元/kW] params.invest_pv 4200; % 光伏单位投资 [元/kW] params.invest_battery 1500; % 锂电池单位投资 [元/kWh] params.invest_h2storage 1600; % 储氢罐单位投资 [元/kgH2]这些参数直接决定最终容量结果建议在不同项目里按当地实际价格修正不要照抄文献数值。4.2 外层粒子群Matlab内置还是自写Matlab全局优化工具箱自带的particleswarm对这个维度的问题完全够用。目标函数传入一个6维向量容量组合函数内部调用内层调度函数返回年化总成本。需要注意容量变量有上下界约束particleswarm的lb和ub直接设置风、光、电解槽、储氢罐至少有一个非零可以在目标函数开头做一次可行性判断若容量组合不满足直接返回一个大数整型变量比如电解槽台数需要取整后传给内层但particleswarm本身是连续优化器所以取整操作放在目标函数里做。options optimoptions(particleswarm, ... SwarmSize, 40, MaxIterations, 60, ... Display, iter, UseParallel, true); [x_capacity, fval] particleswarm((x) capacityObjective(x, params, data), ... nvars, lb, ub, options);UseParallel设为true可以用并行计算池同时评估多个粒子实测能省下大约60%的墙钟时间。不过每个worker都要加载一份数据到内存内存占用会高一些我记得8核机器跑8760个时刻的调度任务大概占用6~7 GB内存内存小于16GB的机器建议降低SwarmSize。4.3 内层调度把一年切成52周的分治策略直接对8760小时做全局MILP变量数量约4×8760整数变量约8760个intlinprog在常规电脑上可能要跑十几分钟甚至更久。我在复现时尝试过一年整体求解速度和内存都撑不住。后来换成了分周求解每7天一个调度窗口共52个窗口依次求解。因为储氢罐容量通常只够系统运行1~3天跨周耦合效应在窗口边界处可以通过“周初储氢状态归零或固定为半罐”或“设置相邻周之间储氢量交接变量”来处理。我试下来最简单有效的方法是把一整年切成带5天重叠的调度窗口用前一次解的后2天储氢状态作为下一窗口的初始条件。这样既保证了时间耦合连续性又把MILP规模控制在可接受范围。每个周调度子问题的核心约束矩阵我把它组装成标准形式后传给求解器function [cost, hydrogen] weeklyScheduling(x_capacity, weekData, params) % 构建决策变量向量 % x(1:T) - 电解槽群总功率/Pel % x(T1:2T) - 储氢量 S_h2 % x(2T1:3T) - 合成氨装置耗氢量 % x(3T1:4T) - 弃电量 % x(4T1:5T) - 电网购电量 % x(5T1:6T) - 电网售电量并网模式 % 定义目标函数 f f [zeros(1,T), zeros(1,T), zeros(1,T), ... curtail_penalty * ones(1,T), ... price_buy_week, -price_sell_week]; % 约束用 Aineq*x bineq 装起来 ... xsol intlinprog(f, intcon, Aineq, bineq, Aeq, beq, lb, ub); end这个函数就是整个优化框架的核心也是最容易出bug的地方。我建议先写一个3小时的小规模测试用例手算出期望解再对比程序输出确认约束矩阵无误后再扩展到全年不要上来就直接跑8760小时。4.4 约束矩阵组装的关键细节功率平衡约束是等式约束每一小时写一行Aeq(Pgen(t) Pdis(t) Pbuy(t) - Pel(t) - Paux(t) - Pchar(t) - Psell(t) - Pcurt(t) 0)其中Pgen是风光实际发电由容量和时序数据算好之后作为常数带入不参与内层优化。这组等式用Aeq*x beq组装。电解槽功率离散挡位约束用不等式的形式Pel(t) Pel_max_available(t) Pel(t) Pel_min_available(t)另一条必须加的是储氢罐容量约束0 S_h2(t) E_h2 * 0.95峰值储氢量如果设置得太满后面连续几小时无风无光时合成氨装置会直接“断粮”所以实际布一个5%的储氢底限约束比较稳。5. 并网与离网结果对比算出来的差异比我预想的大我复现了一个西北某地的算例风光资源较好等效利用小时数分别为2100小时和1600小时。并网和离网两种模式跑出来的最优容量方案差异很直观。指标离网模式并网模式风电最优容量68 MW45 MW光伏最优容量42 MW35 MW电解槽总额定功率52 MW31 MW储氢罐容量34 t12 t电储能容量18 MWh4 MWh年弃电率11.6%3.2%绿氢年产量6800 t6350 t平准化制氢成本28.6 元/kg22.3 元/kg离网系统因为不能用电网兜底风光装机必须配得更大、储能必须配得更足才能保证合成氨装置全年稳定运行弃电率高一点反而是为可靠性付出的合理代价。并网模式则可以“大电网当备用”容量配置更加紧凑成本也低不少。这个对比也说明了一个道理在做这类分析的方案前期阶段就要明确项目是并网还是离网因为这直接影响最优容量结果两个结论不能相互外推。再看调度结果的曲线最能反映问题的是储氢罐SOC曲线。并网模式下储氢罐SOC经常在高位徘徊因为多元件会受到谷电时段的购电冲击离网模式下储氢罐SOC波动剧烈这也是系统必须配置更大储氢容量的直接原因。我还做了一组敏感性分析把风资源年利用小时数从2000小时改到2500小时最优风电容量下降了约20%但光伏容量几乎不变。这提示设计者在资源摸底不够准确的阶段不要急着锁定容量方案应该引入资源不确定性做鲁棒优化或多场景随机优化代价是计算量翻几倍但结论的可靠性也成倍提升。6. 复现这类优化问题的常见坑与排错思路这类代码我在复现和调试过程中踩了不少坑。很多问题不是原理搞不懂而是细节处理不当导致结果离谱或者程序跑不动。下面挑几个最有代表性的展开说。6.1 弃电惩罚系数取值不当导致结果失真弃电惩罚系数设得太高比如设为电价的10倍以上优化器会为了“消纳每一度电”配置巨大无比的储能系统储能的投资成本远远超过省下的弃电惩罚设得太低比如接近零优化器就无所谓了随意弃电储氢罐和电储能容量被压到极低导致设备利用率下降、有效产氢量不足。我调整到最后弃电惩罚系数取购电平均价的1.2~1.5倍比较合理。它代表“如果能把这部分电存储并用于制氢能换取的经济价值”但又不至于高到刺激过度投资储能。想要得到漂亮的调度曲线这个权重值得多试几个值。6.2 跨周储氢耦合处理不好结果出现“周界效应”最早我直接把一年切成52个独立周分别求解结果发现每周周一早上储氢罐总是见底每周日晚上又充满——很明显是边界条件失真导致的假象。加了一个5天重叠调度窗口之后就正常了。具体做法是第一个窗口求解1~7天记录第6~7天的储氢状态作为下一个窗口第6~12天的初始条件跳过第一个窗口的周末。这样分治求解的结果和全年整体求解的结果对比95%以上的时段偏差小于1%对于容量优化这个层级的精度要求完全可以接受。6.3 离网模式的功率平衡出现“凭空发电”调试离网模型时我遇到过一个很隐蔽的bug某个时段总负荷大于风光储能放电程序居然还求出了解而且没有报错。排查下来发现是功率平衡约束矩阵漏了一列弃电变量在等式约束里被忽略了等效于模型可以从“弃电”里拿出一部分能量来用——这物理上根本不可能。最后我做了个一致性校验把调度解代入功率平衡方程挨小时检查残差才把这个逻辑错误揪出来。经验是对能量平衡类约束写完代码后加一段自动校验确保Aeq*xsol - beq的逐元素残差都在1e-6以下。这个习惯能省下大量的联调时间。6.4 粒子群容易陷入局部最优单纯用内置particleswarm跑几次每次结果都有差异有的解明显不合理比如储氢罐容量配到80吨这种夸张值。后来我在外层同时跑了particleswarm和遗传算法ga比较两边的最优解如果两边结果差距过大说明搜索参数或者问题编码有问题。更有效的方法是对粒子初始化做“工程化约束”——风电初始容量按年负载率反算一个基准值光伏初始容量按互补比例0.5~0.8倍风电初始化电解槽容量按风光总可装机功率的0.5~0.7倍初始化。这样粒子群起搜位置就在可行域的合理区间内能显著减少收敛到无意义解的次数。我实测优化时间从三小时降到四十分钟就能拿到满意解。6.5 大数组内存溢出8760小时的MILP如果直接堆完整矩阵内存很容易爆。我的做法是用稀疏矩阵sparse来存储约束矩阵然后把单周的T换成168整个约束矩阵只有约800×840的规模稀疏化后内存占用很小。Matlab的intlinprog对稀疏矩阵的支持很好还更快。7. 结果可视化的关键视角算完容量方案之后可视化不是画几张时序曲线就完了应该有的放矢地展示几个关键视角方便向项目方解释结论。第一张图必须画“典型周调度曲线”横轴168小时左侧纵轴是功率风电、光伏、电解槽、购电右侧纵轴是储氢罐SOC。这张图能直观看出风光互补情况、电解槽运行是否平稳、储氢约束是否发挥实际作用。第二张图画“年弃电率随容量变化的等高线图”横轴风电容量比例纵轴光伏容量比例颜色代表弃电率。这张图能看清弃电率对容量方案非常敏感最优解附近往往有一个狭长的“低弃电走廊”偏离一点弃电率就剧烈上升。第三张图做“成本拆分堆叠柱状图”按风电、光伏、电解槽、储氢、储能、购电成本、运维成本把最优方案的成本结构拆开。通常离网模式下储氢和储能占比显著高于并网这些数据是比选方案时的硬支撑。Matlab绘图的代码不复杂但要留意一张图中两个纵轴单位差了好几个数量级的时候用yyaxis分别控制左右轴否则其中一条曲线会被压成一条直线。8. 一点个人经验复现这类容量-调度联合优化论文最花时间的地方永远不是算法原理而是数据准备和内层约束矩阵的组装调试。我强烈建议入手的顺序是不管并网还是离网先把一个简化版问题跑通——比如把时间尺度缩短到72小时、只保留一个电解槽、储氢罐固定容量手工算一遍最优解然后用代码复现同一个算例。这一步能确认你的模型公式、约束和代码三者完全对齐之后再扩展到全年规模就能避免在一个错了几天的模型上反复浪费时间。另外关于Matlab版本particleswarm、intlinprog这些函数在R2020b之后的全局优化工具箱里都很稳定更早的版本也基本可用只是并行计算配置上略有差异。如果你的课题只是复现验证建议先用R2023a及以上版本坑少一些。这套框架后续还能往两个方向扩展。一是引入风光出力不确定性的随机优化把典型场景聚合成几十个场景做两阶段或分布式鲁棒优化容量结果会更稳健。二是把碳市场收益或绿证收益写进目标函数这在当前“双碳”大背景下对项目经济性评价尤其重要。无论从工程还是课题角度把容量-调度联合优化的框架吃透后面做任何多能互补系统规划分析都能直接复用这笔时间花得非常值。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进