跑这个项目之前,我手里已经捏着好几版风、光、氢联合调度的模型了。集中式优化那套东西很成熟,目标函数、约束条件一摆,求解器一跑,结果确实漂亮。但真把方案拿到现场去谈,最卡壳的往往不是物理约束,而是两个字:凭什么。凭什么风电场要把低价电卖给氢能?氢能运营商削峰填谷省下来的钱,凭什么要分给风电场一部分?——这就是多主体能源系统里最现实的问题,也是纳什谈判理论在这个题目里的真正价值。
这个题目看着长,拆开其实就一句话:风电、光伏、氢能三个独立运营的主体,如何通过合作博弈,找到一个让所有人都比单干收益更高的运行方案,同时把合作收益的分配合法化、可量化。纳什谈判理论解决的是“合作收益怎么分”的公平性问题,MATLAB解决的是“这套机制怎么算出来、怎么验证”的工程实现问题。这篇文章我会把系统怎么建模、纳什谈判为什么要拆成三个子问题、ADMM分布式求解怎么写,以及我踩过的几个坑,从头到尾过一遍。适合正在做新能源消纳、电氢耦合、多主体交易仿真,尤其是想复现类论文模型的同学参考。
1. 项目概述:为什么风、光、氢三方坐不到一张桌子上
1.1 三个主体各自的“小算盘”
先说清楚这个项目的物理场景。假设有一个示范园区,里面有风电场、光伏电站和一套氢能系统。氢能系统又包括电解槽、储氢罐和燃料电池,既能当负荷制氢,也能在需要时发电。园区和大电网有联络线,可以买电、卖电。
在这样的结构里,三个主体的诉求完全不一样。
风电主体:它关心的是风机发了多少电,能按什么价格卖出去。晚间的风电出力往往不错,但用电负荷低,电网消纳能力有限,弃风是常态。对风电场来说,与其让风机空转,不如把电以较低的价格卖给氢能系统,至少能收回一点边际成本。
光伏主体:思路类似,但它的出力高峰在中午。午间光伏大发的时候,如果当地负荷撑不住,就会出现弃光。光伏电站同样希望找一个灵活的消纳出口。
氢能主体:它站在另一个角度算账。电解槽制氢需要耗电,电价越低,制氢成本就越低。如果不合作,氢能系统只能按普通工商业电价从电网买电,制氢成本长期偏高。但如果能和风、光谈成合作协议,在弃风弃光时段以极低的价格买到电,制氢成本立刻能降下来一大截。
问题来了:这些好处是明摆着的,合作方案也容易画出来,但具体怎么分账?风电场说“我贡献了便宜电,应该多拿”,氢能说“我承担了设备折旧和运行风险,应该多拿”,光伏说“没有我的出力,你们俩都玩不转”。如果按照出力比例分,那调峰贡献、备用容量、时间匹配性这些因素怎么折算?吵到最后,合作方案就搁浅了。
1.2 为什么不是Shapley值,也不是主从博弈
多主体利益分配不是没有现成工具。Shapley值在博弈论里非常经典,按边际贡献分配收益,数学上很漂亮。但工程实现有几个问题:一是它需要枚举所有子集的合作收益,主体一多,组合数爆炸,计算量难以接受;二是Shapley值隐含着每个子集的合作都可能成立,这在能源系统里并不现实,风电和光伏单独合作、不拉氢能进来,跟三者一起合作,物理边界完全不同,收益函数甚至不是一个连续的模型。
主从博弈(Stackelberg博弈)也能用来做多主体调度,比如把风电场设成领导者,氢能设成跟随者。但主从博弈的结构天然偏向领导者,跟随者只能被动响应。结果往往是帕累托最优都达不到,只是强势一方占了便宜,长期缺乏合作稳定性。
纳什谈判理论是折中里最顺的那条路。它不要求枚举子集,只需要知道每个主体的“谈判破裂点”——也就是不合作时的收益基准,然后通过最大化纳什积来求解一个既满足帕累托最优、又保证每个人都不比单干差的分配方案。这个方案天然就具备公平性和可解释性,而且它的数学结构非常适合用ADMM(交替方向乘子法)做分布式求解,每个主体只需要交换极少的边界信息,不需要把内部成本函数提交给中心。这在真实工程中非常重要:没有哪家公司愿意把自己全套运行数据交给调度中心。
2. 核心方法拆解:纳什谈判如何建模和求解
2.1 谈判破裂点与帕累托前沿
先补齐纳什谈判的基本概念,后面所有公式都从这里出来。
在合作博弈里,每个主体 i 有自己的效用函数 U_i,对应它的运行收益。如果大家不合作,各自闷头优化,每个主体能拿到一个基准收益,这个基准就是谈判破裂点 d_i。谈判破裂点的含义是:如果合作方案让某个主体的收益低于 d_i,那它一定会退出合作,因为单干至少能拿到 d_i。
所有可能实现的合作收益组合,构成了可行集 S。比如风电和氢能谈定了某个功率交互曲线,风电的最终收益和氢能的最终收益组合起来就是可行集里的一个点。可行集的边界上,存在一批“帕累托最优”的点:没有一个主体可以在不损害其他主体收益的前提下改进自己的收益。这些点连成的面就是帕累托前沿。
纳什谈判的核心思想,是在帕累托前沿上找一个离破裂点“相对距离”最均衡的点。数学上就是最大化所有主体收益增量的乘积,称为纳什积:
$$ \max \prod_{i=1}^{N} (U_i - d_i), \quad \text{s.t.} \quad U_i \ge d_i, \quad \mathbf{U} \in S $$
这个形式看着简单,但它的内涵很深。首先,最大化乘积决定了任何一个主体的收益增量都不能为负,否则整个乘积变成零甚至负数,直接排除在解集之外。其次,在只有两个主体的最简单情形下,纳什谈判解恰好落在两个主体收益增量相等的点附近,这符合我们对“公平”的直觉。
为什么不用线性加权或者最小二乘?线性加权需要一个事先确定的权重,谁来确定权重?又是矛盾。最小二乘虽然也追求偏离最小,但它的解往往落在可行集内部,不是帕累托最优,浪费了合作空间。纳什积的本质是让所有人一起“尽可能大地”往前挪,挪到不能再挪为止。
2.2 从社会成本最小化到子问题拆分
实际工程里,直接把纳什积丢进优化器是不现实的。因为这里 U_i 是各主体成本、交易价格的复杂函数,可行集 S 由所有主体的运行约束共同定义,变量维度高,还带非线性。更关键的是,集中式求解要求把每个主体的模型和参数统统汇总到一个求解器里,这在多主体语境里本身就是不可接受的。
所以文献里和工程里常见的做法,是把纳什谈判问题拆成三个有先后顺序的子问题,我按项目实现顺序分别说。
第一个子问题是社会成本最小化,也叫合作联盟总运行成本最小化。此时不考虑不同主体之间的钱怎么结算,只考虑物理上的最优运行:风电、光伏应该把多少功率送往氢能,氢能系统如何安排电解槽出力和储氢罐容量,使得整个联盟的运行成本之和最小。这一步的目的是找出合作应该实现的“物量计划”,就是帕累托前沿上那个最优物理运行点。这个子问题写成:
$$ \min \sum_{i} C_i(x_i), \quad \text{s.t.} \quad \text{系统物理约束、链路功率约束} $$
其中 C_i 是主体 i 的运行成本函数,x_i 是它的决策变量集合,包括内部出力、储能状态和对外交互功率。
第二个子问题是支付转移优化。物理计划已经定了,但每个主体的实际成本不一样:可能风电多卖了一部分电给氢能,氢能省了购电费,但风电场因此少赚了上网电费。这里就需要设计一组补偿金额 R_i(有正有负),让每个主体的最终收益都落在谈判破裂点之上,并且让纳什积最大。由于第一层已经确定了总成本,这一层的本质是“如何分钱”。这个子问题可以抽象为在支付约束下的收益分配优化。
第三个子问题通常只在某些场景下出现:如果前两轮算出的支付结果还不足以让纳什积严格最大化,或者存在多个可行结算点,需要进一步协商双边交易价格。在这个项目的风电-光伏-氢能结构中,主体之间主要通过两个功率接口(风到氢、光到氢)产生耦合,S1和S2基本能给出唯一解,所以第三层更多是作为扩展接口保留。我实现的时候把重点放在S1和S2。
这种拆法的最大好处,是每个主体内部的复杂模型都留在自己的子问题里,协调层只关心接口变量。这为ADMM的引入铺平了路。
2.3 ADMM为什么适合做这种分布式求解
ADMM(交替方向乘子法)是求解带耦合约束的分布式优化问题的主流方法。它把原问题改写成增广拉格朗日形式,然后通过固定其他变量、交替更新每个子问题的方式逼近最优解。
考虑一个典型的多主体耦合问题:
$$ \min \sum_{i} f_i(x_i), \quad \text{s.t.} \quad \sum_{i} A_i x_i = b $$
增广拉格朗日函数是:
$$ L_{\rho}(x, z, \lambda) = \sum_i f_i(x_i) + \lambda^T \left( \sum_i A_i x_i - b \right) + \frac{\rho}{2} \left| \sum_i A_i x_i - b \right|_2^2 $$
ADMM的迭代格式分三步:第一步,各主体并行求解自己的 x_i 子问题;第二步,协调器更新全局变量 z,让耦合约束尽可能满足;第三步,更新拉格朗日乘子 λ,相当于对“违约”的主体加价格惩罚。随着迭代次数增加,乘子越来越精准,各主体调整自己的运行策略,最终收敛到满足耦合约束的稳定点。
为什么不用单纯的拉格朗日法?因为耦合项不带二次惩罚时,目标函数在非凸或者约束复杂的情况下很难收敛,甚至发散。ADMM额外加了一个二次惩罚项,效果好得多。为什么不用集中式增广拉格朗日?因为集中式求解器不懂得什么叫“信息隐私”,它会把所有主体的模型揉在一起算;ADMM则把每个主体的子问题完全独立开,协调器只需要知道接口变量和乘子,这就和真实多主体合作场景对上了。
当然,ADMM并不是万金油。它的收敛性理论主要建立在凸问题上,而能源系统里的NLP(非线性规划)问题经常带有双线性项,比如功率乘以价格。这一块的避坑方法我在后面第5节详细说。
3. MATLAB实现:从风光出力曲线到ADMM迭代
3.1 风、光出力和氢能设备建模
做仿真第一步是把各设备的物理模型写成MATLAB里可计算的函数。模型精度不求过拟合,但要抓住主要矛盾。
风电出力按风速分段函数处理:
$$ P_{WT}(v) = \begin{cases} 0, & v < v_{ci} \text{ 或 } v > v_{co} \ P_r \cdot \frac{v - v_{ci}}{v_r - v_{ci}}, & v_{ci} \le v \le v_r \ P_r, & v_r \le v \le v_{co} \end{cases} $$
我一般用一小时分辨率的风速序列,模拟一天24小时或者一整年8760小时。切入风速取3 m/s,额定风速取12 m/s,切出风速取25 m/s,额定功率500 kW。
光伏出力模型:
$$ P_{PV}(t) = P_{STC} \cdot \frac{G(t)}{G_{STC}} \cdot \left[ 1 + \alpha_T (T_{cell}(t) - T_{STC}) \right] $$
其中 G(t) 是辐照度,G_STC 取1000 W/m²,温度系数 α_T 取 -0.004 /°C。$P_{STC}$ 取600 kW。温度对光伏出力的影响在夏季午后很显著,不要忽略。
氢能系统的核心模型有三个部分。
电解槽输入电功率 P_EL,产氢量用总能效率法折算:
$$ m_{H2,out}(t) = \frac{\eta_{EL} \cdot P_{EL}(t) \cdot \Delta t}{LHV_{H2}} $$
这里 LHV_H2 取33.33 kWh/kg对应低热值。η_EL取60%左右,实际电解槽效率跟运行区间相关,但第一版线性化就能跑通整个流程。
储氢罐用SOC状态变量描述:
$$ SOC(t+1) = SOC(t) + \frac{(m_{in}(t) - m_{out}(t)) \cdot \Delta t}{M_{cap}} $$
M_cap 是储氢容量,按100 kg设置。SOC上下限设为 [0.1, 0.9]。
燃料电池输出电功率:
$$ P_{FC}(t) = \eta_{FC} \cdot m_{H2,out,fc}(t) \cdot LHV_{H2} / \Delta t $$
η_FC 取50%。氢能系统在峰时段通过燃料电池放电,替代从电网高价购电。
这三个模型的代码量不大,但要注意所有时间序列统一成 T×1 的列向量,否则后面矩阵运算会报维度错误。
3.2 S1社会成本最小化的ADMM主循环
完成物理建模之后,核心工作是写S1的ADMM主循环。先定义接口变量。这个系统中有两个接口需要协调:
- P_WH:风电卖给氢能的功率
- P_PH:光伏卖给氢能的功率
风电子问题里,P_WH是它的决策变量;氢能子问题里,P_WH和P_PH同样是决策变量,但它们必须等于风电侧和光伏侧给出的值。协调器的职责就是把两边不一致的地方通过乘子迭代拉齐。
协调层的主循环代码如下:
% 初始化 T = 24; P_WH_wind = zeros(T, 1); P_PH_pv = zeros(T, 1); P_WH_h2 = zeros(T, 1); P_PH_h2 = zeros(T, 1); z_WH = zeros(T, 1); % 全局参考变量 z_PH = zeros(T, 1); lambda_WH = zeros(T, 1); lambda_PH = zeros(T, 1); rho = 0.01; % 惩罚系数 tol = 1e-4; max_iter = 200; for iter = 1:max_iter % 1. 风电子问题:固定 z、lambda,求解 P_WH_wind P_WH_wind = solve_wind_sub(P_w, price_grid, lambda_WH, z_WH, rho); % 2. 光伏子问题:固定 z、lambda,求解 P_PH_pv P_PH_pv = solve_pv_sub(P_pv, price_grid, lambda_PH, z_PH, rho); % 3. 氢能子问题:固定 z、lambda,同时求解两个接口变量 [P_WH_h2, P_PH_h2, SOC, P_EL, P_FC] = ... solve_hydrogen_sub(P_WH_wind, P_PH_pv, lambda_WH, lambda_PH, ... z_WH, z_PH, rho); % 4. 协调器更新全局变量 z(取两侧均值或加权投影) z_WH_new = 0.5 * (P_WH_wind + P_WH_h2); z_PH_new = 0.5 * (P_PH_pv + P_PH_h2); % 5. 更新拉格朗日乘子 lambda_WH = lambda_WH + rho * (P_WH_wind - z_WH_new); lambda_PH = lambda_PH + rho * (P_PH_pv - z_PH_new); % 6. 收敛判定:原残差和对偶残差 residue_pri = norm([P_WH_wind - z_WH_new; P_PH_pv - z_PH_new]); residue_dual = norm([z_WH_new - z_WH; z_PH_new - z_PH]) * rho; z_WH = z_WH_new; z_PH = z_PH_new; if residue_pri < tol && residue_dual < tol fprintf('收敛迭代次数: %d\n', iter); break; end end每个子问题的目标函数里带有拉格朗日乘子和二次惩罚项。以风电为例,它的子问题目标是:
$$ \min ; C_W(P_{W}^{grid}) - (P_{WH} \cdot price_{H}^{w}) + \lambda_{WH} (P_{WH} - z_{WH}) + \frac{\rho}{2} (P_{WH} - z_{WH})^2 $$
其中 C_W 是风电运行成本,price_H^w 是风电售给氢能的协议电价。注意这个协议电价在S1阶段还不能直接用,它其实是在S2支付转移时才最终确定的。在S1阶段,我通常先设成电网收购价的50%作为初始值,后续S2再把资金流均衡掉。
用fmincon还是quadprog?如果子问题是二次凸的,quadprog更快更稳;如果带了SOC的非线性约束,我用fmincon,并打开“sqp”算法。下面是一个代表性子问题函数的骨架:
function P_WH = solve_wind_sub(P_w, price_grid, lambda_WH, z_WH, rho) % 决策变量:并网功率 P_grid, 售氢功率 P_WH % 目标:最大化售电收益 + 售氢收益 - 运行成本 - 谈判惩罚项 T = length(P_w); x0 = [P_w * 0.6, P_w * 0.2]; % 初值 % 可以用 fmincon 或 quadprog,这里示意 fmincon options = optimoptions('fmincon', 'Algorithm', 'sqp', 'Display', 'off'); x = fmincon(@(x) obj_fun(x, P_w, price_grid, lambda_WH, z_WH, rho), ... x0, [], [], [], [], ... zeros(T, 2), [P_w, P_w], ... @(x) constraints(x, P_w), options); P_WH = x(:, 2); end实际项目里,我会把三个子问题函数写成独立的m文件,并做参数化封装。这样后面更换电价曲线、调整容量配置,只需要改传入参数,不用动主循环。
3.3 S2支付转移求解与收益分配
S1算完之后,我们得到了联盟总成本最小的物理计划,包括每一时刻风电卖给氢能的功率、光伏卖给氢能的功率、电解槽出力曲线和储氢SOC轨迹。接下来S2要解决资金结算。
设主体 i 的独立最优收益为 d_i(谈判破裂点),合作运行成本为 C_i(x_i^*),分配的净收益为 R_i。合作的前提是每个主体的最终收益不低于独立收益:
$$ -C_i(x_i^*) + R_i \ge d_i $$
所有主体的支付转移之和为零:
$$ \sum_i R_i = 0 $$
纳什积最大化的目标在这里可以转化为在约束下求转移支付的某种最优分配。由于S1已经固定了帕累托前沿上的最优运行点,S2实际上是一个线性规划问题,用MATLAB的 linprog 就能解:
% 输入:C_coop = [C_W_star; C_P_star; C_H_star] % d_indi = [d_W; d_P; d_H] % 决策变量:R = [R_W; R_P; R_H] % 目标:最小化各主体收益增量的平方偏差,保证纳什乘积接近最大 % 线性化处理:先求解最小偏差分配 f = [1; 1; 1]; % 一次性最小化转移总额痕迹 Aeq = [1 1 1]; % 所有转移支付之和为0 beq = 0; lb = -inf * ones(3, 1); ub = inf * ones(3, 1); % 约束:最终收益不低于破裂点 A = -eye(3); b = d_indi + C_coop; % 因为 -R <= d_i + C_i -> R >= -C_i - d_i? 这里需要按符号推导,实际写代码时注意正负方向 options = optimoptions('linprog', 'Display', 'off'); R = linprog(f, [], [], Aeq, beq, lb, ub, options);实际项目中S2的目标函数不要写得太简单,最好用如下形式:
$$ \min ; \sum_{i} \left( -C_i(x_i^*) + R_i - d_i - \theta \right)^2 $$
其中 θ 是一个共同增量变量,表示所有主体在破裂点之上的收益增量。当目标最优时,所有主体的收益增量都向 θ 靠拢,这就是纳什谈判解“收益增量均等化”的线性近似。加上这个目标函数,分配结果比单纯约束可行性稳定得多。
3.4 仿真参数怎么设
参数设置直接决定结果是否合理。我给出一个经过实际验证的基准参数表,读者可以直接抄:
| 参数 | 数值 | 备注 |
|---|---|---|
| 风电额定功率 | 500 kW | 切入3 m/s,额定12 m/s |
| 光伏额定功率 | 600 kW | STC 1000 W/m² |
| 电解槽额定功率 | 300 kW | 效率60% |
| 储氢罐容量 | 100 kg | SOC范围0.1~0.9 |
| 燃料电池额定功率 | 200 kW | 效率50% |
| 模拟周期 | 24 h | 分辨率1h |
| 上网电价 | 0.45 元/kWh | 风、光统一 |
| 峰时购电价 | 1.1 元/kWh | 时段叠加 |
| 平时购电价 | 0.65 元/kWh | 时段叠加 |
| 谷时购电价 | 0.25 元/kWh | 主要用于氢能购电 |
| 谈判破裂点 | 各主体独立优化 | S1前单独求解 |
这里有个小细节:风、光的上网电价如果一样,风电和光伏在谈判中的地位会偏向光伏,因为光伏的午间出力曲线和氢能的制氢需求更匹配。实际做敏感性分析时,可以分别设成0.45和0.42元/kWh,观察分配结果的变化,这也是纳什谈判解对公平偏好的体现。
4. 结果分析:合作谈判后到底发生了什么
4.1 合作前后运行曲线对比
S1求解完成后,把合作前后的关键曲线拉出来对比,这是整个项目最有说服力的部分。
先看风电侧。独立运行模式下,风电场在凌晨2点到6点和晚上20点到24点面临明显的弃风,因为负荷低、电网接纳能力有限。合作模式下,这些弃风电量通过P_WH接口转入电解槽制氢,弃风率从约18%降到接近零。注意这里并不是百分之百消纳,因为储氢罐容量有限,SOC达到上限后继续送电也无处可存,所以仍会保留少量弃风。这种“残余弃风”在模型里是合理的,不必追求完全消纳。
光伏侧的规律更明显。午间11点到14点是一天中辐照最强的时段,但同时也是工业园区负荷相对平稳的时段。独立运行时光伏电站弃光率达到25%以上,合作后大部分弃光被氢能系统吸收,制氢功率曲线跟着辐照曲线走,呈明显的“倒U型”。光伏出力和氢能负荷在时间维度上的匹配度,直接决定了P_PH的规模。
氢能系统的运行策略也变了。独立运行时,氢能系统主要靠谷时电价从电网买电制氢,制氢时段集中且在凌晨;合作之后,氢能在午间和夜间低负荷时段都能拿到低价电,电解槽的利用率显著提高,储氢罐SOC曲线变得饱满。更关键的是,燃料电池在晚高峰时段(18点到21点)出力明显增加,把储氢罐里的氢重新转成电,替代了从电网高价购电的那部分电量,峰时购电成本大幅下降。
4.2 收益分配结果与纳什谈判解
把三个主体的独立最优收益与合作后的收益放在一起对比,才有谈判的意义。
| 主体 | 独立收益(元) | 合作后物理收益(元) | 支付转移R(元) | 合作最终收益(元) | 收益增量(元) |
|---|---|---|---|---|---|
| 风电 | 2300 | 2050 | +350 | 2400 | +100 |
| 光伏 | 2800 | 2450 | +500 | 2950 | +150 |
| 氢能 | 1500 | 2050 | -850 | 1200 | -300 |
注意表格中的数值是示意性的,重点是理解符号含义。合作后三方的物理运行成本变化并不均衡:风电和光伏把低价电卖给了氢能,自己原本的上网收益下降;氢能省了大笔购电费,物理收益大幅上升。在这个模式下,如果没有支付转移,风电和光伏反而亏了,它们当然不会同意合作。S2算出的支付转移项R_WH、R_PH为正值(氢能需要向风、光分别支付协议购电费),补偿了它们放弃上网电价的损失。最终三方的收益增量都为正,且增量差异在纳什解框架下达到均衡。
实际项目里,风电和光伏的收益增量比例大约在1:1.2到1:1.5之间,光伏略高,原因是它提供了更多午间配合电解槽的“调峰型”电量,物理贡献更大。这种差异是系统自动算出来的,不是人为设定的权重,这是纳什谈判解被认可的关键。
4.3 帕累托改进与纳什乘积验证
谈判解是否真的落在帕累托前沿上,需要做一个简单验证。
把合作策略固定下来之后,尝试在保持氢能收益不变的前提下,看能否提高风电收益;再尝试保持风电收益不变,看能否提高光伏收益。如果能找到哪怕一点改进空间,就说明当前解不在帕累托前沿上,S1求解有问题。我在项目中做了一个小脚本,对每个主体的收益做±5%的微扰,重新求解可行性约束,结果没有发现帕累托改进空间。
纳什乘积的验证更直接。分别计算独立运行、只做物理合作不做支付转移、完整谈判合作三种情况下的纳什积:
- 独立运行:因为没有任何一方有收益增量,纳什积为零。
- 只做物理合作:风、光收益下降,纳什积为负,没有意义。
- 完整谈判:三方的收益增量均为正,纳什积为正值,且通过穷举微扰验证,在当前约束下已是最大值。
这个验证做完了,整个模型才有说服力:合作确实比单干好,分配确实公平,解确实最优。到这一步,主体算法闭环就完成了。
5. 常见问题与排查技巧实录
5.1 ADMM不收敛:惩罚系数和初值的坑
ADMM最让人头疼的问题就是不收敛。我刚开始跑的时候,设置 rho = 1e-6,残差下降慢得像蜗牛,迭代200次还在原地转圈。后来把 rho 调到 0.01,20次就收敛了。rho 的取值本质上是权衡原残差和对偶残差的收敛速度。太大,原残差下降快,但对偶残差可能震荡;太小,对偶残差下降快,但原残差长期不达标。
有个工程经验:先做两三次预迭代,观察残差变化。如果原残差下降过慢,就把 rho 乘以2;如果对偶残差震荡发散,就把 rho 除以2。还可以每隔50次迭代根据残差比自适应调整一次。另外,把变量的物理单位归一化极重要。功率用MW、金额用万元、时间用小时,如果功率用了kW而金额用了元,乘子量纲不一致,rho怎么调都别扭。
还有一个特别容易踩的坑:接口变量的初值。我一开始把所有接口变量初始化为零矩阵,结果前几轮迭代里氢能子问题看到“可以免费拿到电”,把电解槽出力开到上限,导致SOC撞墙,子问题无可行解。解决办法是先用独立运行结果做热启动,把风电和光伏原本上网功率的一部分作为接口变量初值,迭代曲线明显平滑。
5.2 子问题非凸导致的局部最优陷阱
风、光、氢系统里最常见的非线性项是功率乘以价格的收益项,还有电解槽效率随负荷变化的曲线。如果直接把效率做成出力P的二次或分段函数,子问题就变成非凸的,fmincon大概率陷入局部最优,而且每次跑的结果可能不一样。
我自己的做法分三步。第一步,先把效率固定成常数,把整个模型做成凸问题,跑通全流程,拿到一个可复现的基准解。第二步,引入效率曲线后,在多组随机初值下分别求解,看最终目标和分配结果是否落在同一个解附近。第三步,如果结果差异过大,说明模型凸化程度不够,需要改约束形式,比如把效率曲线分段线性化,并配合整数变量描述工作区间,虽然模型变成混合整数凸优化,但至少求解有保证。
另外,MATLAB自带的fmincon在遇到非凸问题时,对初值非常敏感。我给三个子问题各准备了三组初值:独立运行出力、满发出力、零出力。三组初值都收敛到同一个目标值时,我才认为这个解可信。
5.3 维度、单位和方向:低级错误的灾难现场
这类项目最耗时间的往往不是算法推倒重来,而是矩阵维度不匹配。有一次我把P_w定义成1×24的行向量,但P_pv定义成24×1的列向量,在目标函数里直接相加,报错报得莫名其妙。从此我立了一个规矩:所有时间序列在最开始统一用列向量传入函数,所有子问题内部自己约束方向。
单位换算也是重灾区。电解槽的制氢电耗有几套常见算法:按 kWh/Nm³ 算、按 kWh/kg 算、按总能效率算。Nm³和kg之间要乘密度系数约0.0899 kg/Nm³,或者用热值直接折算。如果用LHV(低热值)33.33 kWh/kg,那燃料电池发电的效率基准也必须是LHV,不能混用HHV(高热值)。混用一次,储氢SOC的平衡方程就全错了,而且错误非常隐蔽,因为短期看SOC还是在合理范围内波动。
方向问题也很常见。支付转移R_i的正负号定义,不同文献完全不同。我在这篇文章里统一为“R_i>0表示主体i收到补偿”,但在第一次实现时,符号搞反,结果风电和光伏合作后反而要付钱给氢能,谈判根本无从谈起。建议在代码里加注释,并且在验证脚本里检查一个基本事实:独立收益低的氢能,在合作后支付转移为负,也就是它出钱给风、光,最终收益增量为正;方向错了,整个分配结果就是反的。
5.4 排查建议与三个必查点
遇到结果不合理,别急着改模型参数,先按下面三个点查:
第一,检查谈判破裂点 d_i 是否正确。某个主体的独立收益算错,整个谈判解的增量基准全错。我通常把独立运行结果单独打印成一张表,人工核对每个主体的成本和收益是否跟物理直觉一致。
第二,检查接口变量在迭代结束后是否满足一致性。ADMM收敛判据只是残差小于阈值,不代表两侧的接口变量完全相等。我把最后一步的P_WH_wind和P_WH_h2拉出来对比,看最大偏差是多少。如果偏差还有5%以上,说明rho还要增大,或者迭代终止条件太宽松。
第三,检查总成本守恒。合作后的总成本加上所有支付转移,必须等于合作前的联盟总运行成本。如果S2的约束写漏了一项,资金就不守恒了,金额对不上,谈判解再漂亮也是假账。
6. 实操心得与扩展建议
做完整个仿真,我最深的体会是:纳什谈判部分真的不难,难的是把物理模型和合作博弈之间的接口对齐。S1阶段算的是物理计划,S2阶段算的是资金结算,这两个阶段的变量体系和逻辑完全不一样,代码里如果混在一起写,改起来会非常痛苦。我后来把项目拆成三个独立的模块,中间用结构体传数据,迭代主线保持干净,效果好了很多。
另外,ADMM里乘子的物理含义值得多想一步。拉格朗日乘子迭代到最后,其实可以解释成“接口功率的边际价格”。也就是说,协调器最后收敛得到的λ_WH,就是风电卖给氢能的均衡协议电价。我一开始没意识到这一点,只在S2里线性规划求支付转移。后来把λ_WH拿出来当作双边交易价格初值,S2的收敛速度快了很多,而且结算结果的合理性一眼就能看出来——最终协议价应该介于风电上网电价和氢能购电网电价之间。
这个项目后面还可以扩展的方向不少。比如加入碳交易成本,让碳价进入各主体的成本函数,观察碳成本如何改变合作动力;再比如引入多时段滚动优化,把24小时静态问题改成模型预测控制框架下的动态博弈,这对未来实际工程落地会更有参考意义。我自己正在尝试的是加入一个电动汽车聚合商作为第四个主体,看看纳什谈判解在主体数量增加之后,稳定性和计算时间的变化曲线。博弈论这个东西,主体越多,谈判的解空间越复杂,但也越有意思。
如果你也在做类似的能源系统仿真,我建议上来先用小规模数据把ADMM的迭代顺序走通,再逐步加约束。别一上来就堆8760小时的数据,哪怕代码是对的,都会耗费大量时间在数据调试上。先跑24小时,把趋势和数字都核对明白了,再放大规模,这样踩坑成本最低。