☰
基于Matlab+Yalmip的氢氨综合能源系统优化调度建模实战
2026/10/20 9:39:26 网站建设 项目流程

做含氢气氨气的综合能源系统优化调度,听起来像是个很“跨界”的方向,但这两年做下来,我发现这其实是综合能源系统里非常有代表性的一个分支。氢气、氨气作为长时储能和跨季节储能的载体,跟电、热、冷耦合在一起,比单纯“光伏+储能+电锅炉”那种模型复杂一个量级,但解决的大问题很实际:可再生能源弃了可惜,存又存不住,氢氨转化正好能把多余的电变成化学能,需要的时候再发电或者供热。

这篇博文我主要想聊聊我用Matlab从零搭建这套调度模型的过程,包括系统的组成假设、各类设备建模、约束怎么写、目标函数怎么定,以及最让人头疼的混合整数非线性问题在Matlab里怎么处理。文章偏实操,给的是可复现的思路和代码片段,不是纯讲概念。适合正在做综合能源优化调度、微电网调度、或者氢能掺氨混烧方向的同学参考,也适合刚入门Matlab+Yalmip的小伙伴抄作业。

1. 内容整体设计与思路拆解

1.1 为什么选择氢氨综合能源系统

先说一个基本认知:传统风光储系统的储能环节,主要靠电化学电池,但电池存储时长有限,通常就是几小时到一天,成本还高。想要把夏天富余的光伏电量留到冬天用,电池根本不现实。氢气作为储能介质,可以通过电解水制氢,然后储氢罐存储,需要时用燃料电池发电或热电联产。但氢气本身有个痛点:储运难、体积能量密度低,直接大规模存储压力大。氨气作为氢的载体,可以通过合成氨把氢固定下来,氨的液化温度低、储运技术成熟,而且可以直接用于燃气轮机或锅炉掺烧,也可以裂解回氢再供给燃料电池。所以氢氨联合的本质是:氢作为“中间介质”,氨作为“长期存储和跨场景使用”的载体。这样一个系统就有了电、氢、氨、热四种能量形态的互相转化。

我做的这个调度项目,背景设定在某工业园区,包含风电、光伏两种可再生能源电源,配套碱性电解槽制氢、氢气储罐、合成氨装置、氨储罐、氨裂解器、氢燃料电池热电联产、电锅炉和常规电网交互。调度周期取24小时,时间尺度1小时。核心目标是:在满足园区电负荷和热负荷的前提下,通过合理分配各设备出力,最大化系统日运行收益,也就是售电收入加上可能的副产品收入,减去购电成本、设备运行维护成本、启停成本和碳排放成本。

1.2 整体调度架构:能量枢纽的视角

这个系统如果不用“能量枢纽”的框架去看,很容易被设备间的箭头绕晕。我建模时先画了一张能量流图,其实不需要复杂软件,手画就行,关键是思考顺序是“从源头到负载”。源侧是风电、光伏和电网购电;中间转换层分两条路径,一条是电直接供负荷或者给电锅炉供热,另一条是电给电解槽制氢,氢一部分储存、一部分送合成氨变成氨;氨又可以裂解回氢,或者直接作为燃料供锅炉/燃气轮机(这里简化成氨裂解后供燃料电池);最终负荷侧是电负荷、热负荷外送。

用Matlab建模型的时候,这套图就转化成一组功率平衡方程和不同设备模块之间的耦合变量。每个设备在任意时段t都有输入能流和输出能流,中间的转化关系用效率系数描述。整个优化调度的本质,是在这些能流关系、容量约束、爬坡约束、启停逻辑约束构成的可行域里,寻找一个让目标函数最大的决策变量组合。

1.3 为什么选择Matlab+Yalmip而不是其他工具

这个问题我被人问过很多次。调度优化无外乎三个思路:一是自己写智能算法,比如粒子群、遗传算法;二是用GAMS这种专业优化建模语言;三是用Matlab+Yalmip调用求解器。我自己最终选了第三种,原因有三。

第一,调速系统的约束条件多且复杂,尤其含0-1变量的设备启停、储罐动态、化学反应耦合关系,智能算法虽然不用求导但很容易陷入局部最优,而且每次运行结果可能不一样,写到论文里或工程报告里很难交代清楚。第二,GAMS建模自由度高但语法偏硬,而且很多人不熟,图方便我直接在Matlab里用Yalmip,语法像自然语言,写约束基本跟写公式一样。第三,Yalmip底层可以调Gurobi或Cplex,决策变量既有连续也有二进制,目标函数是线性的,约束大多是线性或分段线性,直接构建成MILP问题,求解快、全局最优、可复现。

当然这个项目里如果只用线性模型,合成氨和裂解这种过程里的非线性效率、燃料电池电压模型等就很难精细表达。所以我在某些地方做了线性化近似,或者在局部用分段线性函数替代连续非线性函数。后面我会重点讲这个坑。

2. 核心组件建模与参数标定

2.1 可再生能源出力模型

风电和光伏的出力曲线,我在项目里没有用复杂的气象仿真,而是直接给了一个典型日的归一化出力系数,然后乘以装机容量。风电装机假设30 MW,光伏装机20 MW。用的公式很简单:

[ P_{wt}(t) = \eta_{w}(t) \cdot P_{w}^{cap} ] [ P_{pv}(t) = \eta_{pv}(t) \cdot P_{pv}^{cap} ]

其中 (\eta_{w}(t)) 和 (\eta_{pv}(t)) 是归一化出力系数,曲线从实际项目的气象数据里提取,也可以自己设定一个比较合理的24点数组。这里有个容易忽略的点:新能源出力是“不可调度”还是“可弃”的。我建模时保留了弃风弃光变量,也就是允许调度中心在出力过剩时丢弃一部分可再生能源,而不是强制全部消纳。这个取舍很关键,因为系统最优解很可能在某个时段“主动弃掉”0.5 MW光伏,省下储能和氢系统循环损耗,反而提高整体收益。

实际上我在代码里把风光实际出力写成:

[ P_{wt}^{use}(t) + P_{wt}^{curt}(t) = P_{wt}^{pred}(t) ]

这样 (P_{wt}^{curt}(t)) 是个非负连续变量,作为弃风弃光量。通过这种方式,优化算法自己会判断“是消纳好还是弃掉好”。

2.2 电解槽制氢模型

电解槽是电转氢的核心。我采用的是碱性电解槽模型,简化成效率模型:输入电功率,输出氢气流量(kg/h或者标方)。工程上温度、压力、电流密度变化都会影响效率,但在日内调度中,一般把电解槽效率当作常数处理,或者分成最小运行功率段内的固定效率。我在模型里定义:

[ m_{H2}^{prod}(t) = \frac{\eta_{el} \cdot P_{el}(t)}{H_{H2}^{HHV}} ]

这里 (H_{H2}^{HHV}) 是氢气的高位热值,单位取 kWh/kg,大约33.3 kWh/kg。我用的电解槽额定功率为5 MW,效率 (\eta_{el} = 0.6),那么5 MW满发时每小时产氢量是 (5*0.6 / 33.3 = 0.09) 吨,也就是90 kg/h。这个数不算小,在园区场景里差不多够5辆氢能重卡一天的加注量。

实际建模时还要加最小技术出力约束和爬坡约束。电解槽不能无限低负荷运行,一般碱性电解槽最低负荷30%左右,低于这个值容易发生气体交叉,有安全风险。所以约束是:

[ 0.3P_{el}^{cap} \cdot u_{el}(t) \le P_{el}(t) \le P_{el}^{cap} \cdot u_{el}(t) ]

(u_{el}(t)) 是0-1变量,表示启停状态。这个约束我强烈建议你保留,别只写上下限。如果没有0-1变量关联,优化器会让电解槽在负荷0.1 MW这种“物理上不可能运行”的状态下跑,看似结果很漂亮,实际上完全失真。

爬坡约束方面,我设置的爬坡上限为额定功率的20%/分钟,折算到1小时步长其实影响不大,但加上了以后启动和停机过程更符合实际。

2.3 储氢罐与储氨罐的动态模型

储氢罐我按高压气态储氢处理,状态变量是每一时刻罐内氢气总质量 (m_{H2,tank}(t))。它的动态方程是:

[ m_{H2,tank}(t) = m_{H2,tank}(t-1) + m_{H2}^{in}(t) - m_{H2}^{out}(t) - m_{H2}^{react}(t) ]

其中 (m_{H2}^{react}(t)) 是可送往合成氨装置或燃料电池的氢气量。储罐容量我用30吨,初始储量设为15吨,限制最大储量为30吨,同时为了寿命和安全限定了最小储量为5吨。在实际项目里,储氢罐不能完全放空,必须保留垫底气压和死容量,这个细节你不加,调度结果在边界时段就容易出现“刚好用空”的情况,工程上不可行。

储氨罐的建模类似,但它的特殊性在于:合成氨装置输出的是液态氨,质量流量和氢质量流量之间需要一个化学计量比。1 mol氮气和3 mol氢气合成2 mol氨,氢氨质量比 (m_{H2}:m_{NH3} = 6:34 \approx 1:5.67),也就是说生产1吨氨需要约0.176吨氢。反过来,氨裂解产生氢,1吨氨裂解理论上能得到0.176吨氢。这个折算比例就是个常数,在Matlab里直接写成系数相乘。很多人第一次做氢氨系统容易搞混这个比例,结果质量和能量流算出来都对不上。

合成氨反应本身是放热反应,余热可以回收,但在简化调度模型里我没有单独建热回收模块,而是把产热折合成固定的热效率系数,直接计入热功率平衡。这样做是有代价的:氨合成反应的温度窗口、催化剂寿命等和运行状态的关系完全没体现。但对于日前调度来说,这种简化是可接受的,毕竟我们关注的是小时级能量分配,不是设备内部的化学过程。

2.4 燃料电池热电联产与锅炉模型

氢燃料电池热电联产系统在模型中承担的是“用电+供热”的双重角色。它消耗氢气,输出电功率和热功率。电效率和热效率分别取45%和40%,也就是说氢的化学能中45%变成电、40%变成热、其余损失。这个模型用矩阵表达更方便:

[ \begin{bmatrix} P_{fc}^{e}(t) \ Q_{fc}^{h}(t) \end{bmatrix} = \begin{bmatrix} \eta_{fc,e} \cdot L_{H2} \ \eta_{fc,h} \cdot L_{H2} \end{bmatrix} \cdot m_{H2}^{fc}(t) ]

注意这里燃料消耗量用kg/s换算成功率。为避免每小时氢耗量过大,我在模型里对燃料电池最大电功率做了30 MW的额定限制,同时加了最小运行出力约束。

电锅炉则比较简单,电转热效率取0.95,输入电功率上限10 MW,没有启停约束(实际中电锅炉可以快速启停,所以不需要0-1变量)。不过为了平滑调度曲线,我还是给它加了一个爬坡速率约束,爬坡上限5 MW/h。这个约束让电锅炉出力不会在相邻时段出现0和10 MW的死跳,避免电网侧功率冲击。

2.5 与电网交互模型

园区与外部电网的交互是双向的,可以购电也可以售电。购电电价和售电电价采用分时电价,我用了三时段电价:峰时10点到15点、18点到21点,电价为0.95元/kWh;平时7点到10点、15点到18点、21点到23点,电价为0.62元/kWh;谷时23点到次日7点,电价为0.32元/kWh。售电价格为购电价格的80%,用这个差异制造“套利空间”。如果购售电价一样,系统就没有动力去存氢、存氨再卖电了。

交互约束很简单:

[ P_{grid}^{buy}(t), P_{grid}^{sell}(t) \ge 0 ] [ P_{grid}^{buy}(t) \cdot P_{grid}^{sell}(t) = 0 ]

严格来说,同时买和卖在数学上可能被优化器“钻空子”,因为目标函数是收益最大,不会傻到买高卖低,但如果电价在某些时段出现倒挂,可能会同时买和卖来套取某种补贴。为了稳妥,我直接用二进制变量约束二者互斥,虽然增加了一个0-1变量,但避免了很多不必要的调试困扰。这一步属于工程经验,很多初学论文里不写,但实际代码中必须处理。

3. 优化调度模型构建与求解

3.1 目标函数怎么定

我的目标函数是“日运行收益最大化”,写成:

[ \max \sum_{t=1}^{24} \Big[ c_{sell}(t)P_{grid}^{sell}(t) - c_{buy}(t)P_{grid}^{buy}(t)

  • c_{H2}^{sell}(t)m_{H2}^{sell}(t) + c_{NH3}^{sell}(t)m_{NH3}^{sell}(t)
  • c_{op}^{el}P_{el}(t) - c_{op}^{fc}P_{fc}^{e}(t)
  • c_{start}^{el}u_{el}^{start}(t) - c_{start}^{fc}u_{fc}^{start}(t)
  • c_{CO2}E_{co2}(t) \Big] ]

卖氢和卖氨的价格,我是按固定单价处理的,氢按35元/kg,氨按3800元/吨。这在园区场景里自然对应对外出售副产品。碳排放成本这里简化成购电碳排放和天然气(如果有)排放,但项目里没有天然气,所以我只对电网购电量乘一个排放因子,再用碳价折算。

这个目标函数有个特点:收益来源里包含了能量转换过程中的“物资产出”,不只是电。正因为有氢和氨的销售收入,系统才可能在电价低谷大量买电制氢、合成氨,而不是单纯存电。这个思路和时下“电-氢-氨”一体化项目的商业逻辑是一致的。

3.2 约束条件的分类与写法

我在Yalmip里把约束分成了5组,调试时非常清爽。

第一组是功率平衡约束。电功率平衡:

[ P_{wt}^{use}(t) + P_{pv}^{use}(t) + P_{grid}^{buy}(t) + P_{fc}^{e}(t) = P_{load}^{e}(t) + P_{el}(t) + P_{eb}(t) + P_{grid}^{sell}(t) + P_{curt}(t) ]

热功率平衡:

[ Q_{fc}^{h}(t) + Q_{eb}^{h}(t) = Q_{load}^{h}(t) ]

除这两条,还有氢气平衡、氨平衡。氢气平衡公式:

[ m_{H2}^{tank}(t) = m_{H2}^{tank}(t-1) + m_{H2}^{prod}(t) - m_{H2}^{fc}(t) - m_{H2}^{syn}(t) - m_{H2}^{sell}(t) ]

氨平衡:

[ m_{NH3}^{tank}(t) = m_{NH3}^{tank}(t-1) + m_{NH3}^{syn}(t) - m_{NH3}^{crack}(t) - m_{NH3}^{sell}(t) ]

注意裂解后的氢量通过系数 (\alpha_{NH3,H2}) 折算进燃料电池的可用氢中,不单独设变量也行,我是单独设了一个 (m_{H2}^{crack}(t) = \alpha_{NH3,H2} \cdot m_{NH3}^{crack}(t))。

第二组是设备容量约束,所有设备的出力上下限和储罐容量上下限,前面已经说了一部分。第三组是启停逻辑约束,包括电解槽、燃料电池最小启停时间以及启动指示变量生成。由于调度周期只有24小时,最小启停时间我设了1小时,实质上只需要关联启停指示变量。

[ u_{el}^{start}(t) \ge u_{el}(t) - u_{el}(t-1) ] [ u_{el}^{start}(t) \ge 0 ]

加上启动成本进入目标函数,优化器会自动避免频繁启停。第四组是爬坡约束,第五组是购售互斥约束。

3.3 非线性环节的线性化处理

这个项目最大的建模难题,是合成氨装置的运行特性。真实的合成氨装置有最低运行负荷限制,而且产量和输入氢气的质量流量基本是线性的,但单位产品能耗在小负荷下会偏高。如果把这些特性写成非线性函数,整个模型就不再是MILP,求解时间可能从几秒变成几分钟甚至不收敛。我的处理策略是:把合成氨装置按恒效率线性模型简化,但给最小运行负荷设置一个超过50%的较高比例,这样避免优化器在极小负荷下“开一台又低效又启停频繁”的装置。实际参数取额定产氨2吨/小时,最小负荷60%。如果你需要对低负荷效率下降做精细分析,可以用分段线性函数拟合,每一段引入一个0-1变量,但会增加求解负担。

燃料电池的效率曲线也一样。有的文献会给燃料电池建电压-电流非线性模型,但这种微观模型更适合设备级设计,不是调度级该用的。调度级用稳恒效率就够了。我甚至见过有人用matlab的fmincon直接去求解非线性调度模型,不是不行,但每一次搜索都要算一遍潮流和化学平衡,24小时模型两天都跑不完,非常不推荐。

3.4 核心Matlab代码框架

下面我把Yalmip代码的骨干结构贴出来,没有贴完整数据,但核心建模套路都在。我用的是[x, ~] = value()拿结果。代码里我用到binvar声明二进制变量,sdpvar声明连续变量。

%% 参数声明 T = 24; P_wt_pred = [数据1×24]; % 风电归一化出力×装机 P_pv_pred = [数据1×24]; P_load_e = [数据1×24]; Q_load_h = [数据1×24]; Cap_el = 5; % MW Cap_fc = 30; % MW Cap_eb = 10; % MW Eff_el = 0.6; Eff_fc_e = 0.45; Eff_fc_h = 0.40; Eff_eb = 0.95; H2_HHV = 33.3; % kWh/kg R_ratio = 17/3; % N2质量系数等,折算用 %% 变量定义 % 连续变量:实际发电、存储量、流量等 P_wt = sdpvar(1, T); P_pv = sdpvar(1, T); P_el = sdpvar(1, T); P_fc_e = sdpvar(1, T); P_eb = sdpvar(1, T); m_H2_tank = sdpvar(1, T+1); m_H2_prod = sdpvar(1, T); m_H2_fc = sdpvar(1, T); m_H2_syn = sdpvar(1, T); m_NH3_tank = sdpvar(1, T+1); m_NH3_syn = sdpvar(1, T); m_NH3_crack = sdpvar(1, T); P_grid_buy = sdpvar(1, T); P_grid_sell = sdpvar(1, T); P_curt = sdpvar(1, T); % 二进制变量 u_el = binvar(1, T); u_fc = binvar(1, T); u_grid = binvar(1, T); % 购售互斥 % ... start变量略

约束部分用循环写最简单明了:

Constraints = []; for t = 1:T Constraints = [Constraints, P_wt(t) + P_curt(t) == P_wt_pred(t)]; Constraints = [Constraints, P_pv(t) + P_curt_pv(t) == P_pv_pred(t)]; Constraints = [Constraints, P_wt(t)+P_pv(t)+P_grid_buy(t)+P_fc_e(t) == ... P_load_e(t)+P_el(t)+P_eb(t)+P_grid_sell(t)+P_curt(t)]; Constraints = [Constraints, Q_fc_h(t)+Q_eb_h(t) == Q_load_h(t)]; Constraints = [Constraints, 0.3*Cap_el*u_el(t) <= P_el(t) <= Cap_el*u_el(t)]; Constraints = [Constraints, Eff_el*P_el(t) == m_H2_prod(t)*H2_HHV*1000]; % 注意单位 Constraints = [Constraints, m_H2_tank(t+1) == m_H2_tank(t) + m_H2_prod(t) ... - m_H2_fc(t) - m_H2_syn(t)]; Constraints = [Constraints, 5 <= m_H2_tank(t+1) <= 30]; % 其他类似 end Constraints = [Constraints, m_H2_tank(1) == 15, m_H2_tank(T+1) == 15];

目标函数按前面公式写,然后用optimize求解。

Objective = sum( ... ); optimize(Constraints, -Objective, sdpsettings('solver','gurobi','verbose',2));

这里必须出一个重要提醒:单位一致性。我一开始就是因为功率单位是MW,而氢质量流量单位是kg/h,两者直接混进同一个公式导致约束意义混乱,排查了很久。建议全项目统一为“MW + t/h + MWh + t”四个基础单位,然后在氢-功率转换时除以或乘以热值。热值如果用kWh/kg,就先把MW乘1000变成kW再除,得到kg/h。比如电解槽公式实际应该是:

[ m_{H2}^{prod}(t) \text{(kg/h)} = \frac{1000 \cdot \eta_{el} \cdot P_{el}(t) \text{(MW)}}{33.3 \text{(kWh/kg)}} ]

上面代码里的*1000就是为了这个。不然你会算出每小时几千吨氢的荒唐结果。

3.5 求解结果怎么验证

不要跑完optimize就算完事。我拿到结果第一件事,是检查残差和平衡方程。Matlab里可以用check函数看看所有约束的最大误差,一般小于1e-6就合格。然后我会画四条关键曲线:系统总电功率平衡图、氢流量图(产氢-用氢-储氢)、氨流量图、储罐液位变化图。如果某一时刻储氢罐流量曲线出现锯齿状剧烈抖动的“bang-bang”现象,往往说明目标函数里缺少了对设备运行不平稳的惩罚项。可以在目标函数里加一个微小的运行惩罚系数,比如对电解槽启停惩罚加大,或对储罐变化量加入L1范数惩罚,来平滑调度结果。

另外,不要只看收益。我还会做几个对比场景:纯电化学储能场景、不加氨存储场景、全部消纳场景。对比后能清楚看到氢氨系统的优势区间。在结果合理性检查上,注意看边界条件:初始罐存量与期末罐存量是否满足耦合约束,以及购售互斥变量是否真的互斥了。

4. 数据准备、关键参数调试与结果分析

4.1 典型日数据与负荷设定

我用的算例是夏季典型日。风电在凌晨出力高,光伏在中午出力高,形成明显的“双峰”特性。电负荷曲线是典型的工业负荷,上午9点到12点、下午14点到18点双峰,夜间低值。热负荷则是冬季高夏季低,但我这个算例为了突出氢氨系统的运行价值,人为设定为“全年有基本工业用热”,热负荷高峰出现在早上8点和晚上20点,最大15 MW。

这组数据看起来简单,但对调度结果的影响极大。如果你直接随便写一组平滑的负荷曲线,模型可能所有设备都跑不出“启停变化”。我建议任何做复现的人都先用自己身边典型日的数据,把最大值、最小值、峰谷差设计得合理一点,然后再跑模型。

4.2 敏感性分析:氢价和氨价的影响

项目里最有意思的结论,来自对氢价和氨价的敏感性分析。我把氢出售价从20元/kg逐步调到60元/kg,每一次重新求解,观察系统行为:氢价低时,系统倾向于氢全部内部使用(燃料电池发电+供热),不对外卖;氢价高时,系统增加电解槽出力,不仅夜里谷电制氢,连白天的一部分光伏电也用来制氢,并且大量氢转化为氨对外出售。氨价的影响类似。

这让我意识到,这类“氢氨综合能源系统”的优化调度结果,本质上由市场价格信号驱动,而不是由系统自带的物理特性驱动。所以做论文或项目报告时,不能只给一个价格下的结果,至少要做三档价格敏感性分析,否则很难说服别人你的调度策略具备鲁棒性。

4.3 调度结果中的典型运行模式

在一个典型结果里,凌晨0点到7点,风电出力大但负荷低,电网电价低,系统外送电又卖不上价,于是电解槽满负荷运行,产氢一部分进储氢罐,一部分直接进合成氨装置生产液氨。上午9点到12点,光伏出力上升,但电负荷也涨,电解槽降负荷运行,燃料电池停机,系统从电网少量购电。下午14点到18点,电价升高,储氢罐里的氢开始送燃料电池发电,同时氨裂解补一部分氢,以高价卖电。晚上电价回落,电锅炉启动补热缺口,合成氨装置继续运行把白天积累的氢转成氨。

这套“谷时制氢、峰时发电、全天产氨”的运行模式,完美诠释了氢氨系统为什么比单纯“电池储能”厉害:电池只能“电到电”,氢氨系统可以把峰谷价差套利扩展成“电到氢到氨”的跨时段、跨品类套利,副产品还能直接卖给化工用户。这是整个项目理论上最硬核的价值点。

4.4 经济性指标怎么算

求出每个设备出力后,除了目标函数里的总收益,我还会算几个辅助指标:单位制氢成本(元/kg)、单位合成氨成本(元/吨)、系统综合能源利用率和可再生能源消纳率。单位制氢成本等于电解槽总运行成本加启停成本加摊分的购电成本,除以总产氢量。注意:氢的去向不同,分摊方法也不同。我采用的简单做法是,先算整体总成本减掉卖氨收入,剩余成本全部由售氢和售电收益承担,这在经济学上叫副产品收益冲抵法。

综合能源利用率定义为“终端有效用能 / 系统输入总一次能源折算值”,这个指标可以侧面验证模型的能量流是否守恒。算出来一般会在70%~85%之间。如果超过90%,你就要检查是不是效率参数给高了,或者某些耗散项漏掉了。

5. 常见问题与排查技巧实录

5.1 求解器报错“Infeasible problem”怎么办

这是最常见的问题,几乎每个跑优化模型的人都会遇到。刚把模型搭起来的时候,约束一堆,可能因为某个参数笔误导致可行域为空。我的排查顺序是这样:

第一步,缩小时间尺度,只跑1个或2个时段,手动算一下这个时段的功率平衡是否成立。第二步,检查储罐初始值,比如储氢罐初始储量过低、储氨罐初始储量过低,可能导致某些时段无论怎么调节都满足不了“期末储量不低于下限”的约束。第三步,检查是否有强加的矛盾约束,比如同时要求“燃料电池必须满发”和“燃料电池不能大于电负荷”,这种情况我在测试时出现过。

如果还不确定性来源,Yalmip提供了一个很有用的函数iff或者逐步注释约束的技巧。我把约束一条条注释掉,看问题是否变可行,用二分法找到冲突约束。实践中最快发现问题的往往是“功率平衡里某分量符号写反了”,比如把电解槽耗电写成了发电加进平衡方程,就会导致白天系统电量崩溃。

5.2 求解时间过长,如何加速

原始模型如果加入太多0-1变量,比如为每一台设备再加3段线性分段,求解时间会快速增加。我的经验是:

  • 尽可能松弛0-1变量:能不启停的连续设备就不要加启停变量,比如电锅炉完全可以连续运行,不需要binvar。
  • 减少分段线性化的段数:用3段代替10段,换来的误差不到5%,求解速度能快一个数量级。
  • 设定相对MIP gap:sdpsettings('gurobi.MIPGap', 0.01),让求解器找到1%内的次优解就停止。工程调度完全够用,没必要追求数学上绝对最优。

在Matlab里跑24小时模型,变量数量大概在200~400个之间(含约50个0-1变量),用Gurobi求解一般几秒就能出结果,完全不需要分布式计算。如果你模型跑到分钟级别,先怀疑是不是非线性约束或二次目标混进来了。

5.3 结果出现负收益或设备全停

有一个很反直觉的情况:在某个算例里,目标函数最优解居然是“什么都不干”,新能源全弃,购电只满足电负荷,所有设备停机。这说明你的固定成本过高、价格太低,或者你忘了加“必须消纳一定比例新能源”的政策约束。我后来在模型里加了最低消纳率约束,比如风光弃电率不超过5%,防止优化器摆烂。工程中确实也有这种约束,新能源消纳责任权重是政策层面的刚需,加入后结果明显更合理。

5.4 储氢罐储量上下界轻微波动

因为模型里储罐动态方程是线性的,通常不会出现超限。但当你用了分段线性化以后,可能出现局部效率变化导致储罐储量在临界点抖动。解决办法是给储罐上下限留2%左右的冗余,比如容量上限30吨,直接设成29.5吨,下限5吨就设成5.5吨。这种“软抠门”的处理方式能避免求解器在整数割平面上反复试探边界,求解更稳定,而且对优化结果影响很小。

6. 用一套好用的数据管理习惯,少走弯路

做一个完整优化调度项目,模型本身只是三分之二的工作,剩下三分之一是数据和结果管理。我用Matlab做这个项目时,把数据和模型完全分离:参数全部放在一个结构体params里,结果变量全部放在results结构体里。这样改一个负荷数据,不需要动约束代码,直接在结构体上改就行。每次算完立即保存results和params到.mat文件,文件名带上日期场景标签,比如results_summer_wind30_pv20.mat。这个习惯救了我很多次,不然模型改到第5版时,自己都记不清哪个结果对应哪组参数。

在画图方面,我推荐画4张图:功率平衡堆叠图、氢流量与储罐液位双轴图、氨流量与储罐液位双轴图、各设备启停状态热力图。如果目标是做学术论文,再加一张关键变量灵敏度分析表就完整了。画图用plot加stairs组合,储罐液位用阶梯线更符合离散时间步长的特点。

7. 最后再分享一点我的实操体会

这套含氢氨的综合能源系统优化调度,做完一遍之后,我才真正理解“跨介质储能”和“广义储能”这两个概念落到模型里意味着什么。传统储能模型只需要写一个电池SOC动态方程,而氢氨系统需要同时跟踪氢和氨两种化学形态的质量流,还要在两个储罐和四个转换环节之间做质量守恒。模型复杂度的上升,是物理场景的复杂带来的,不是数学本身故意刁难人。

说句实在话,刚开始调这个模型时,我老想着把效率曲线、反应温度、压力损失全部塞进模型,结果陷入了非线性优化的泥潭。后来跟一位做能源系统规划的同行交流,他点醒了我:调度模型不是设备仿真模型,它的价值在于给出“未来24小时各类设备需要怎么协调运行”的相对正确趋势,而不是精确到小数点后三位的设备热力学状态。从那以后我果断做线性化,只保留对调度决策影响最大的非线性(比如最低负荷、启停成本),结果求解效率和结果稳定性都大幅提升。

如果你也要做类似的模型,我的建议是:第一版哪怕把电解槽效率、燃料电池效率全都写成常数,也要先把能量平衡跑通。等你对系统的运行规律有了感觉,再逐步加入更精细的模型。千万不要一上来就上非线性、混合整数、多目标,那样一旦有问题,你连定位都无从下手。先“能跑”,再“跑好”,最后“跑精”。这个顺序,比任何求解器调参技巧都重要。

整个项目里还有个让我印象深刻的细节:氢能做燃料、氢能充电、氢能供热、氨能外销,这四种功能本质上都是同一套“电-氢-氨-热”耦合的能量流在不同时段的不同分配。调度的艺术就在于,在价格信号和物理约束的夹缝里,寻找每一小时最优的分配方案。这个思路不仅适用于这篇论文算例,放到真实的工业园区能源管理平台上,同样成立。希望这篇分享能帮你少踩几个坑,绕过我去年的弯弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询