尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

风光制氢合成氨容量调度优化:Matlab+Cplex联合建模实践

风光制氢合成氨容量调度优化:Matlab+Cplex联合建模实践 1. 项目概述与整体设计思路1.1 项目背景与系统构成前阵子复现了一个典型的可再生能源制氢-合成氨容量配置项目主题是“风光互补制氢合成氨系统的容量-调度联合优化用Matlab调Cplex求解”。这类系统这几年在新能源圈子里讨论很多本质上解决的是风光出力波动与化工连续生产之间的匹配问题。风电、光伏出力天然不稳定电解水制氢相当于把电力转化为便于存储的氢能氢再与空气中分离出来的氮气通过合成氨工艺生成氨氨既是化工原料又是相对易储运的绿色燃料。项目代码同时考虑了并网和离网两种运行模式并网模式下系统可以与电网买卖电作为缓冲离网模式下则完全自平衡。系统的抽象结构并不复杂左侧是风电场和光伏电站中间是电解槽、储氢罐、合成氨装置配套空分制氮右侧在并网模式下多一个电网交互节点。所有设备都有容量上限所有能量流都必须满足时序平衡。所谓“容量-调度优化”就是把两个层面的问题放进同一个优化框架容量问题决定风电装机、光伏装机、电解槽规模、储氢罐规模、合成氨装置规模各建多大调度问题决定在每一个小时或典型日风电怎么分配、电解槽开多少功率、储氢罐什么时刻充放、合成氨装置保持多高负荷、并网模式买电还是卖电。这两个问题单拆开看都不难难在互相耦合。装几台电解槽取决于全年氢需求的预期而氢需求又由合成氨装置的调度决定合成氨装置的负荷又反过来受风光出力和储氢状态影响。最稳妥的做法就是把容量变量和调度变量丢进一个统一的优化模型里一次性交给求解器处理。这也是标题里“容量-调度优化”要表达的核心意思。1.2 为什么必须做“容量-调度”联合优化很多人拿到这类题目第一反应是先算容量再算调度两步走。但两步走的坑在于容量配置的结果依赖于对未来运行方式的假设这些假设一旦和真实最优调度不一致容量投资就是错的。举个具体例子如果容量优化阶段假设电解槽全年满发风光配比会倾向多装风电保证氢量但实际调度里夜间风光出力低电解槽根本达不到满发储氢罐又不够大最后氢供应不足合成氨装置被迫降负荷项目收益被高估。反过来只做调度优化不做容量优化给定一个固定装机容量调度就算做得再漂亮也无法回答“这个站一开始该建多大”的问题而建多大恰恰是投资决策里最敏感的变量。所以项目里常见的做法是单层大模型容量决策变量是标量调度决策变量是时间序列它们一起作为决策变量目标函数统一考虑年化投资成本加年运行成本约束条件同时覆盖投资约束和时序运行约束交给Cplex求解。这种方式建模直观求解在MILP框架下能保证全局最优比启发式算法稳定得多。1.3 求解器选型为什么是Cplex做这类优化求解器选型基本等于半条命。项目用的是Cplex这在能源系统优化领域算是长期验证过的选择。Cplex对大规模线性规划LP和混合整数线性规划MILP的数值稳定性好分支定界效率高内置的预处理和切割平面技术很成熟。相比之下遗传算法、粒子群这类启发式算法虽然不需要建模太严格但无法保证收敛到全局最优而且每次运行结果可能不一样。对于容量配置这种动辄上亿元的决策一个“差不多”的解和一个全局最优解之间的差距可能就是几千万元的偏差。Cplex在Matlab里的接入方式有两种一种是通过YALMIP建模工具箱另一种是直接用Cplex提供的Matlab接口函数。我更推荐YALMIP因为它的语法接近数学表达式写法约束可以通过向量化表达式一次性传入调试起来比手拼稀疏矩阵舒服得多。后续第3节我会把环境配置和核心代码结构都列出来。2. 数学模型如何把系统“翻译”成优化问题2.1 目标函数的构成与量纲先看目标函数。模型里的成本主要分三块投资成本年化、运行维护成本、购电成本并网模式。收入主要两块卖电收入并网模式和氨销售收入。写成数学表达式就是目标 年化投资成本 年运行成本 年购电成本 − 年卖电收入 − 年氨销售收入投资成本年化要把设备初投资乘上一个年化系数CRF。CRF的公式是 r(1r)^n / [(1r)^n − 1]r是折现率n是设备寿命。这个系数经常被初学者漏掉——如果不做年化把30年的设备投资直接和1年的运行成本加在一起量纲就错了优化结果会拼命缩小设备容量。比如说折现率8%、寿命20年CRF大约是0.10左右也就是1000万元的设备投资折算到每年约100万元。代码里应统一按“万元/年”或“元/年”口径处理及时把各设备初投资折算成年值。氨销售收入的结构要写清楚氨产量设为变量Q_am单位吨价格price_nh3按元/吨。合成氨的氢耗换算要用化学计量关系生产1吨氨约消耗0.18吨氢。这个换算系数在代码里写成一个常数后面会接进储氢罐平衡方程。如果这个系数搞错模型的物料平衡就会失真优化结果看起来正常实际上氢耗和产氨根本不匹配。2.2 容量层决策变量与约束容量层的决策变量主要是各设备的额定容量风电装机、光伏装机、电解槽功率上限、储氢罐容量、合成氨装置产能上限离网模式再加一个蓄电池容量如果有。每个设备通常有上下限约束比如当地可建设面积限制了风电最大台数、光照资源限制了光伏容量资金预算也可以作为约束加进去。这些边界条件的取值看似不起眼实际上对结果影响很大建议做敏感性分析时优先扫这些参数。容量层还有一个容易被忽略的环节设备额定容量和运行功率的换算关系。比如电解槽额定功率10MW功率调节范围一般是20%—100%也就是最低运行功率2MW。这意味着如果风电总出力低于2MW电解槽要么停机要么靠电网或储能把功率补足离网模式下无法工作。这类“最小负载率”约束是模型可行性的关键很多复现失败都是卡在这一条。2.3 运行层约束四类关键约束逐一拆解运行层约束是整个模型里最耗费精力的部分按约束类别逐一说明。第一类是功率平衡约束。系统在任何时刻都必须满足风电出力 光伏出力 购电并网 电解槽用电 合成氨装置用电 售电并网 蓄电池充放电修正离网。这里所有功率必须先归一化到“系统母线”口径风电出力并不是全额给电解槽有时候还要考虑主动弃风弃光所以在变量里会保留限电空间。第二类是电解槽运行约束。除了功率上下限还要考虑爬坡约束和开关状态。如果引入二进制变量表示电解槽开停机最小负载率的表达就用得上P_el P_min * b_elP_el P_max * b_el。引入整数变量的代价是模型变成MILP求解难度上升但换来的是更真实的运行逻辑。如果调度粒度是小时级开关变量数量等于T乘设备数T是全年8760小时的话变量规模会很大通常要用典型日压缩。第三类是储氢罐时序约束。储氢量S_h2(t)的状态转移可以写成S_h2(t1) S_h2(t) eta_el * P_el(t) * dt − h2_am(t)其中eta_el是电解制氢转换效率h2_am(t)是合成氨装置消耗氢气量dt是调度时段长度单位小时。注意储氢罐不能是空的合成氨装置要连续运行所以一般还要设置最低储量约束。另一个关键约束是终值约束S_h2(end) S_h2(1)这是典型日优化里的周期性条件代表罐子不能凭空多出氢气也不能把初始储存的白拿光不然下一轮循环就失衡了。第四类是合成氨装置运行约束。合成氨装置有最低负荷率限制也有最大产能限制单位时间产氨量决定了氢耗速率。在简化模型里合成氨装置可以看成一个“功率-氢耗-产氨”耦合的模块通过线性关系把电耗、氢耗和氨产量联动起来。氮气基本默认充足不太需要关注空分系统如果想更贴近工程可以再加一个空分电耗常数进功率平衡方程。2.4 并网与离网模式模型差异在哪儿并网和离网不是简单删一个变量的问题。并网模式下系统有电网这个“无限池子”缺电就买、富电就卖优化结果往往倾向于小储氢罐、大电解槽因为电价低时可以直接从电网取电制氢不需要为了应付天生波动而多投资储能设备。但并网模式有一个重要约束同一时刻不能同时购电和售电否则模型会在买卖价差之间做无意义的套利计算出虚假收益。代码里可以用一个二进制变量b_grid实现购电量 M * b_grid售电量 M * (1 − b_grid)。离网模式则要严格满足自平衡功率平衡方程里没有电网项风光出力必须实时等于负荷唯一能跨时段搬移能量的是储氢罐如果有蓄电池则也包括它。离网模式下模型更容易不可行原因很简单连续无风的几个晚上光伏不出力风电又不够电解槽被迫停机合成氨装置还得稳定供氢储氢罐里的氢可能撑不到天亮。如果模型算出来不可行先别急着怀疑求解器大概率是某天的风光出力低谷和电解槽最小负载率约束撞车了。2.5 时间序列简化典型日的选取与权重全年8760小时建模变量规模直接爆炸尤其是二进制变量。常见工程化做法是先做典型日聚类把风速、光照、温度、电价序列聚成若干个典型日每个典型日按出现频次赋予权重优化目标也按权重折算到全年。聚类方法用k-means就够但要注意把风速和光照放在同一个样本里做标准化不要单独聚类。聚多少个典型日视精度要求而定一般4到12个就够用。我见过有人把8760小时全部塞进去结果Cplex跑了两天还在0.5%的gap里打转最后只能靠加求解时间上限强行截断得到的解根本没有说服力。用典型日是为了快速得到稳定解前提是论文或报告里要交代清楚聚类方法、聚类个数以及误差分析。这一段内容在复现项目里非常重要直接决定最终模型的可解性。聚类完之后每个典型日的权重乘以该日运行成本最后累加就是全年的运行成本口径。3. MatlabCplex实战环境配置、代码结构与调参3.1 环境配置YALMIP和Cplex的版本匹配先说环境配置。Cplex要能被YALMIP正常调用核心是把Cplex的Matlab路径加进来并且确保Cplex版本和Matlab版本兼容。比如Matlab R2023a配Cplex 12.10/12.12一般没问题Matlab太新而Cplex太旧时会因为缺少对应的动态库报“Unable to load mex file”。个人建议优先用yalmiptest命令诊断如果Cplex的状态是found就说明路径加载成功。YALMIP本身只是个建模层不参与求解但它要求Cplex的求解器文件在搜索路径里。配置完成后在Matlab命令窗口跑一句yalmiptest输出里看到CPLEX-IBM并且status是found才算配置完成。如果没找到多半是路径没加对或者addpath之后没保存path换个工作区就丢了。建议把路径写进startup.m文件一次性解决每次打开都要手动加路径的麻烦。3.2 决策变量定义与目标函数的代码写法项目里建议先定义容量变量标量再定义运行变量向量全部用sdpvar二进制变量用binvar。一个典型的定义片段如下T 24; % 单日调度时段 % 容量决策变量 C_wind sdpvar(1,1); C_pv sdpvar(1,1); C_el sdpvar(1,1); C_h2 sdpvar(1,1); % 储氢罐容量 C_am sdpvar(1,1); % 合成氨产能上限 % 运行决策变量 P_wind sdpvar(T,1); % 风电消纳功率 P_pv sdpvar(T,1); P_el sdpvar(T,1); % 电解槽输入功率 P_am sdpvar(T,1); % 合成氨装置电耗功率 P_buy sdpvar(T,1); % 购电功率, 并网模式 P_sell sdpvar(T,1); % 售电功率, 并网模式 S_h2 sdpvar(T,1); % 储氢罐储氢量 b_el binvar(T,1); % 电解槽开关状态 b_grid binvar(T,1); % 购售电互斥标志, 并网模式目标函数写成CRF 0.08; % 按折现率和设备寿命计算 IC CRF * (inv_wind*C_wind inv_pv*C_pv inv_el*C_el inv_h2*C_h2 inv_am*C_am); OC om_ratio * (inv_wind*C_wind inv_pv*C_pv inv_el*C_el inv_h2*C_h2 inv_am*C_am); EC sum(price_buy .* P_buy * dt) - sum(price_sell .* P_sell * dt); Rev price_nh3 * Q_am; % Q_am由合成氨产量累加得到 obj IC OC EC - Rev;这里Q_am不能凭空定义需要从合成氨装置的产氨约束推出来。一般做法是把单位时间产氨量设计成变量Q_am_t(T,1)乘时段数累加得到年产量再由化学计量关系把氢耗量H2_need_t送进储氢罐平衡方程。这样收入项和耗氢项就挂上了钩。做这一步时建议一开始就算好单位换算避免后面再对“元/MWh”、“元/吨”、“MW”三套单位体系重新对齐。3.3 约束条件写入矩阵化比for循环快得多约束的写入位置在目标函数之后、optimize之前。Matlab新手常见做法是用for循环把每条约束逐条加进F [F, expr];这在约束数量少的时候没问题但到了几百上千条约束反复append会显著拖慢建模速度。更好的做法是尽量用向量表达式一次写完整段约束。功率平衡约束F [F, P_wind P_pv P_buy P_el P_am P_sell];风电、光伏出力不超过可用资源F [F, 0 P_wind avail_wind .* C_wind]; F [F, 0 P_pv avail_pv .* C_pv];电解槽最小负载率与开关联立F [F, P_el r_min * C_el .* b_el]; F [F, P_el r_max * C_el .* b_el];储氢罐状态转移与容量限制F [F, S_h2(2:end) S_h2(1:end-1) eta_el * P_el(1:end-1) * dt - H2_need(1:end-1)]; F [F, S_h2 S_h2_min]; F [F, S_h2 C_h2]; F [F, S_h2(1) S_h2(end)]; % 周期性约束并网模式的购售电互斥M 1000; % 足够大, 但不要离谱 F [F, P_buy M .* b_grid]; F [F, P_sell M .* (1 - b_grid)];这里的M是经典坑位。M取1e6模型数值条件会非常差求解器容易报numerical troubleM取小了又会误伤正常解。实际项目中先用一个粗略的上限比如最大负荷的1.5倍够用即可求解结果出来后再检查有没有触碰到M边界。经验数据是M不要超过问题正常量级的3倍。如果嫌求M烦可以把P_buy和P_sell改为共享一个总交换功率上限的写法省掉互斥二进制变量代价是可能允许同买同卖的小额操作但对大多数规划性研究这个近似误差可以接受。3.4 求解参数设置mipgap、时间限制与诊断输出最后调用optimize时求解参数直接传进去ops sdpsettings(solver,cplex, ... verbose,2, ... cplex.mip.tolerances.mipgap,0.01, ... cplex.timelimit,3600); result optimize(F, obj, ops);mipgap设成1%意思是求解器只要找到的整数解和最优下界之间相对差距小于1%就停止。这是大模型里非常实用的设置——死磕到0%的gap瓶颈和收益往往不成比例。timelimit控制最大求解时间避免模型卡在难以收敛的分支定界过程。verbose等级决定输出多细调试阶段设2跑大模型设0减少控制台刷屏。求解完之后第一时间检查result.problem。0表示求解成功1表示不可行2表示数值问题4表示求解中断。YALMIP的check(F)可以逐条检查约束的最大违反量定位是哪一条约束出了问题。这些诊断方法是复现路上最值得掌握的工具。4. 复现避坑指南常见问题与排查方法4.1 Cplex找不到或mex文件加载失败这个问题排在首位因为环境都跑不通后面全白费。常见原因有三个一是Cplex的Matlab接口路径不在搜索路径里二是Matlab版本和Cplex版本不兼容三是Windows/Linux下Cplex安装路径含中文或空格导致动态库加载失败。排查思路很简单先执行yalmiptest看状态再检查addpath是否正确。如果还不行打开Cplex安装目录下的cplex/matlab/文件夹看有没有对应平台的子目录确认这个子目录在Matlab路径里。个人建议装Cplex时直接用默认安装路径别放中文目录。4.2 离网模式一求解就不可行问题出在哪里离网模式不可行是这类项目里最高频的bug。多数情况下并不是模型写错了而是物理条件本身就不允许。排查建议按顺序来第一看功率平衡。把风力光伏的可用出力乘容量序列调出来逐时段算最大可发电量再看电解槽最小负载率和合成氨装置基础电耗的总和如果某个时段可发电量小于最小必须功率那这个场景一定无解。处理办法有两种配置储能平滑功率或者允许一定比例切负荷量在方案里通常表述为失负荷率约束。第二看储氢罐周期性约束。终值等于初值的要求很严格如果初始储量设太低、合成氨装置又必须维持连续生产模型可能无论如何都找不到周期内的可行状态转移。变通做法是把终值约束放宽成S_h2(end) S_h2_min_period或者把初值设成和终值一致的未知变量让求解器自己寻找平衡点。第三看电解槽最小负载率。前面讲过P_el r_min * C_el * b_el这条约束在某些低出力时段会逼迫模型要么把电解槽开到最低功率、要么停机。如果忽略启停费用模型可能频繁启停工程上不现实但数学上可行。如果发现“可行但结果诡异”先检查是不是缺了启停成本惩罚或最小连续运行时间约束。4.3 MIP求解太慢卡在1%的gap不动弹Cplex对MILP的求解速度总体不错但模型一旦上了几千个二进制变量还是会慢。缓解手段按优先级排第一选择是压缩时间序列用典型日聚类代替8760小时第二选择是减少二进制变量——电解槽开关变量如果对结果不敏感可以换成功率下限约束的连续化近似第三选择是设置mipgap和timelimit接受次优但工程上够用的解第四选择是warm start用启发式解或上一轮迭代的解作为初始解喂给Cplex大幅缩减分支定界搜索空间。还有一点很少有人提Cplex预处理阶段会翻来覆去检查约束如果模型里冗余约束过多预处理时间也会很感人。把明显重复的约束删掉比如两个等式方程可以互相推出的那种能让求解速度明显提升。实际调试时可以先用一个小规模样例验证模型正确性再放大到全规模否则一边查正确性一边等求解效率极低。4.4 优化结果反常识问题多半在目标函数或单位复现完第一版模型我遇到过“最优结果是啥也不建”氨产量为零设备全不投资。第一反应是模型错了仔细排查后没发现问题最后发现是氨销售收入价格给低了导致卖氨收入覆盖不了投资和运行成本模型理性选择就是不做。所以看到反常识结果先别急着怀疑求解器去检查经济参数氨价、电价、设备造价、折现率任何一个偏差都可能彻底改变最优配置。这类系统的经济性测算结果存在真实边界条件参数差10%结论可能完全翻转。另一个容易踩的坑是单位。风电出力在模型里通常用“占额定容量的比例系数”表示avail_wind取值0到1之间再乘容量才能得到MW。如果avail_wind误填成kW/ MW混合结果直接废掉。建议建模最开始就把单位约定写清楚所有变量统一用MW、MWh、吨、元三套基础单位并且互算关系单独写在代码注释里。4.5 结果分析怎么从优化解里提炼有用结论代码跑通拿到最优解之后事情并没有结束。容量层面要给出各设备容量配置运行层面要给出典型日调度曲线。我最常用的分析手段一是画出典型日内风电、光伏、电解槽功率、储氢量、氨产量的时序曲线观察风光资源互补性在什么时段体现二是做敏感性分析把氨价、碳价、设备造价在±30%范围扫一遍看最优容量配置如何移动这种图往往是论文或报告里最有力的证据三是并网和离网两种模式综合对比量化电网为系统带来的灵活性价值。通常结论是并网模式允许更小储氢罐和更大电解槽项目总投资更低。4.6 扩展方向模型还能往哪些方向改复现完这套代码你可能还想加现实因素进去常见扩展至少三个方向。第一个方向是加入碳交易机制或绿氨溢价在目标函数中增加碳排放成本项让系统贴近双碳政策下的真实经济环境。第二个方向是加设备退化模型电解槽频繁启停影响使用寿命在目标函数中加入启停成本或寿命折算调度结果会明显更“温和”。第三个方向是引入需求响应或现货电价曲线并网模式下购电策略就有了博弈空间储氢罐可以和电网交互联动起来变成一个真正意义上的能源枢纽调度模型。无论往哪个方向加核心逻辑还是这套“容量-调度”联合优化的骨架公式、代码、求解器基本都能复用。我个人在实际复现过程中的体会是这类项目最难的不是把代码跑通而是把每个参数背后的物理意义和工程假设搞清楚。Cplex只在数学上保证最优但模型本身是否真实反映系统运行规律完全取决于建模的人。建议拿到代码后先跑通一个典型日的小规模案例确认各类约束行为符合预期再逐步放大时间尺度和设备种类。这样既能快速定位问题也不会在错误模型上浪费大量求解时间。
返回列表