这篇文字想说清楚一件事:当你面对一个包含多个微网、各自主体不同、源荷曲线完全错位的系统时,为什么要用非对称纳什谈判做电能共享的优化与利益分配,以及这套模型在 MATLAB 里到底怎么落地。去年我接手一个五微网的电能共享调度项目,最初用简单的统一售电价把多余光伏卖给相邻微网,结果一个拥有大量储能的微网直接退出,理由很简单:它觉得自己在帮别人调峰,却没有获得额外收益。换成非对称纳什谈判之后,把每个微网的合作收益增加量和谈判权重挂钩,系统才真正转起来。这篇文章我会从数学模型、等价分解、MATLAB 代码框架到调试技巧全部过一遍,适合正在做微网优化、综合能源系统仿真的同学参考。
1. 这模型到底在解决什么问题
1.1 多微网电能共享的本质是一笔“互相救急”的生意
多个微网挂在一片配网下,源荷曲线往往差异很大。有的微网白天光伏出力远大于负荷,甚至要弃光;有的微网则天天依赖柴油发电机去填补缺口,发电成本高得吓人。电能共享做的事,就是让这些微网之间通过联络线互济功率,把富余的清洁电能直接卖给缺电的邻居,减少弃光,也减少高成本机组出力。
这个设想本身不复杂,难在“怎么结算”。邻居之间拉线卖电,不能照搬电网的统购统销电价,也不能让所有人都按同一个固定价成交。因为不同微网对交易的需求强度不一样,有的属于“没你这笔电我就得开柴油机”,有的属于“我的电卖不卖得掉都无所谓,大不了弃光”。这种地位差异如果不反映到结算机制里,合作就是脆弱的。
非对称纳什谈判就是在这样一个背景下被引入的。它把多微网电能共享建模成一个带权重的谈判问题,让每个微网的合作收益增量跟它的议价能力挂钩。权重高、贡献大、替代成本高的微网,理应分到更多合作红利;权重低、可有可无的微网,收益就少一些。这样处理的结果,比我最初用的那种“平均分蛋糕”稳定得多。
1.2 为什么夏普利值和固定比例分成都不够香
做利益分配,理论上有不少现成工具。夏普利值强调公平,把每个参与者对联盟的边际贡献算清楚,但它的计算量随微网数量指数爆炸,N 稍大一点就基本没法算;核仁法更追求唯一解,却常常得到一些在工程上难以解释的极端分配。固定比例分成虽然简单,但又忽视了不同时段、不同交易功率下各方的真实贡献差异。
纳什谈判解的好处在于它有一组漂亮的公理化支撑:帕累托最优、对称性(权重相等时)、无关选择独立性等。它把“合作剩余该给谁”转化成一个可求解的优化问题,而且引入了权重之后,天然能描述参与主体的不对等地位。工程上这意味着什么?意味着我们不用拍脑袋定内部结算价,而是通过求解谈判问题,反推出一个让所有微网都接受的交易价格或转移支付。
另一个关键点:纳什谈判问题本身是非线性、强耦合的,但通过一个非常经典的等价分解技巧,它可以拆成两个好算的子问题。先是系统总成本最小化,再做收益分配。这个分解我后面会详细讲,它直接决定了 MATLAB 代码的复杂度。
1.3 对称与非对称的核心区别
对称纳什谈判的模型是所有参与者权重相同,解出来的结果是合作剩余的均分(在可转移支付假设下)。非对称纳什谈判则在目标函数里给每个微网配上权重 ω,权重越大,话语权越强。
从数学上看,两种形式只在目标函数的幂次上有区别:
max Π (U_i - U_i^0)^(ω_i),i = 1…N
ω_i 全部相等时,退化为对称模型;ω_i 不同时,就变成非对称谈判。这个变化看起来小,实际效果差别很大。我后面会给一个两微网算例,算完你会发现,权重 0.7 和 0.3 的分配结果,简单说就是“省下来的钱按权重比例分”。所以在多微网电能共享的工程场景里,设置权重的过程,本质上就是在定义每个微网的“谈判地位”。
2. 数学建模拆解:从纳什乘积到可求解的两步优化
2.1 谈判破裂点:先算清楚“不合作”的底线
任何谈判都有一个底线,也就是谈崩了怎么办。在多微网电能共享模型里,这个底线就是各微网独立运行时的收益或成本。假设微网 i 在不参与共享、只跟外部电网交易的情况下,最优运行成本是 C_i^ind,那么它的谈判底线的收益可以写成 U_i^0 = -C_i^ind。
这里有个我踩过的坑:很多论文直接把独立运行成本当作常数,随便给一组数,但其实这一项必须用跟联盟模型完全一致的负荷曲线、设备参数去单独建模求解。否则你算出来的合作收益增量是虚高的,分配到各微网后也根本经不起推敲。我当时是先在 MATLAB 里写了独立调度模型,得到每个微网的分时成本和总成本,再拿去做谈判破裂点。
第二步是让所有微网参与电能共享,构成大联盟,求解总运行成本最小化问题,得到 C_total^coop。联盟总收益增量就是 Δ_total = Σ C_i^ind - C_total^coop,也就是大家合作一共省了多少钱。这个 Δ_total 是整个纳什谈判问题的“分蛋糕大小”。
2.2 纳什乘积与对数变换:一个让问题变友好的技巧
纳什谈判的目标函数是最大化 Π(U_i - U_i^0)^(ω_i)。直接看这不是个能轻松丢进求解器的东西,乘积形式对很多优化算法不友好。但因为对数函数是单调递增的,最大化这个乘积等价于最大化它的对数形式:
max Σ ω_i ln(U_i - U_i^0)
对数变换把乘积变成了求和,形式上温和不少,但还没完。这个目标里 U_i 又跟每个微网的运行决策、交互功率耦合在一起,要直接一起求解还是困难。
关键来了。在电能共享这种可转移支付的场景里,合作收益增量可以在联盟内部通过转移支付进行重新分配,于是可以把问题拆成两个阶段:
- 第一阶段:最小化联盟总运行成本,得到总合作收益 Δ_total。
- 第二阶段:在总收益固定的前提下,让每个微网分到的增量收益 Δ_i 尽量满足谈判目标,即求解:
max Σ ω_i ln(Δ_i),约束 Σ Δ_i = Δ_total,Δ_i ≥ 0
第二阶段只需求解一个关于 Δ_i 的小规模分配问题。对这个小问题列 KKT 条件,可以直接得到结论:在没有任何附加约束时,最优分配满足 Δ_i / Δ_j = ω_i / ω_j,也就是说合作节省按权重比例分配。这个结论非常有用,我第一次在 MATLAB 里跑完两阶段模型后,拿分配结果一对比,发现比例误差在 0.1% 以内,当时心里就踏实了。
2.3 权重系数的物理意义与设置方法
非对称纳什谈判里,权重是最主观的一个参数,也是最容易被论文一笔带过的地方。工程上我见过几种常用取值思路:
- 按微网可调度资源容量占比,比如储能容量大、柴油机容量大的微网,调节能力强,理应在谈判中更有话语权;
- 按微网对共享交易的实际贡献,比如一段时间内净输出电量多的微网,权重更高;
- 按负荷重要性和缺电成本,缺电后损失大的微网愿意在谈判中让步,换取供电可靠性;
- 按政策引导方向,比如希望提高新能源消纳,就给新能源资源富集的微网更高权重。
我实际做项目时会尽量避免某一权重拍脑袋乱定。一个经验做法:先跑一版对称权重算例,得到每个微网的交互功率和收益增量,再按交互贡献的正相关性来设权重,最后做敏感性分析,看权重在合理范围内波动时,各微网收益的变化有多大。这样做出来的结果可解释性强,跟业主讨论时也拿得出依据。
2.4 两微网算例:权重怎么直接改变成交价
光说公式可能还是绕,我拿一个极简的两微网算例说明。微网 1 有 800 kW 光伏,负荷只有 400 kW,富余 400 kW;微网 2 没有光伏,负荷 600 kW,只能靠柴油机补电,柴油发电成本约 0.9 元/kWh。微网 1 如果单独卖电给外部电网,上网电价只有 0.35 元/kWh;微网 2 若向外部电网买电,电价是 0.8 元/kWh。
独立运行情况下,微网 1 卖 400 kW 给电网,一小时收益 140 元;微网 2 用柴油机发 400 kW,一小时成本 360 元。两家合计“净收益”为 -220 元(这里用成本为负收益表示)。
联盟运行后,微网 1 把 400 kW 直接卖给微网 2,替代柴油发电。总成本降低到多少取决于内部成交价,但不管成交价怎么定,总合作收益增量是固定的:柴油机 0.9 元/kWh 与上网电价 0.35 元/kWh 之差乘以 400 kW,也就是每小时节省 220 元。
按对称权重 ω1 = ω2 = 0.5,节省的 220 元两家各拿 110 元。微网 1 收益从 140 变成 250,反推内部成交价为 0.625 元/kWh;微网 2 成本从 360 变成 250,买价同样是 0.625 元/kWh。两家都划算。
如果改成非对称权重 ω1 = 0.7、ω2 = 0.3,则微网 1 分到 154 元,微网 2 分到 66 元。微网 1 的总收益变成 294 元,内部成交价抬高到 0.735 元/kWh,微网 2 的总成本降到 294 元。结果就是:有余电、能救急的微网 1 在谈判中拿走了更大比例的合作红利。这个算例把所有公式都变成了一笔一笔看得见的账,非常有助于理解整个模型。
| 权重组合 (ω1, ω2) | MG1 分得节省(元/h) | MG2 分得节省(元/h) | 内部成交价(元/kWh) |
|---|---|---|---|
| (0.5, 0.5) | 110 | 110 | 0.625 |
| (0.7, 0.3) | 154 | 66 | 0.735 |
| (0.3, 0.7) | 66 | 154 | 0.515 |
3. MATLAB 实现路径:工具链、变量配置与 ADMM 求解
3.1 为什么选 Yalmip + Gurobi,而不是纯内置函数
MATLAB 里做优化调度,有人习惯直接用 linprog、quadprog 或者 fmincon。坦白说,微网规模小、约束简单的时候没有毛病,但一旦涉及储能 SOC 递推、购售电同时性约束、多微网交互功率耦合,这种“手写矩阵”的方式能让人崩溃——因为你每加一个约束,就要回头改一大串稀疏矩阵的组装逻辑。
我的做法是 Yalmip 建模 + Gurobi 求解。Yalmip 是 MATLAB 下的一个建模语言,它的核心价值是让优化问题的表达贴近数学公式,你写约束和目标函数的方式几乎跟写论文公式一样,改模型也只需要增删一行约束。Gurobi 的线性规划、二次规划、混合整数规划求解能力都很强,同样一个调度问题,求解速度比 MATLAB 内置 intlinprog 快很多。
有一点要提醒:无论你用 R2022b 还是更新的版本,Yalmip 的接口都挺稳定,但记得安装 Gurobi 后要在 MATLAB 里跑一遍gurobi_setup。学校正版授权或者试用版都够用,基本不需要额外的花销。
3.2 微网内部模型的变量与约束长什么样
以有光伏、储能、柴油发电机、本地负荷且可与外部电网交易的微网为例,优化变量包括:柴油机出力 P_g、储能充电 P_ch、储能放电 P_dis、储能荷电状态 SOC、向外部电网购电 P_buy、向外部电网售电 P_sell、与其他微网的交互功率 P_ij。
功率平衡约束是所有微网模型的基石:P_g + P_dis + P_buy + P_pv + Σ P_ij_in = P_load + P_ch + P_sell + Σ P_ij_out。储能 SOC 满足时间递推关系 SOC(t+1) = SOC(t) + (η_ch·P_ch - P_dis/η_dis)·Δt / E_cap。柴油机要考虑出力上下限和爬坡约束,购售电要设置不能同时发生的约束,联络线交互功率也要受线路容量限制。
有个细节我特别想说:储能充放电不能同时进行,严格建模需要引入二进制变量。但含二进制变量后,ADMM 子问题的求解会明显变慢。我第一版模型为了先验证纳什谈判逻辑,直接用连续变量并用一个小惩罚项避免同时充放电;跑通之后再加入二进制变量做混合整数版本。按这个顺序推进,能省一半调试时间。
3.3 电能共享的耦合约束与 ADMM 分布式求解
多微网的电能共享在数学上体现为一组耦合约束:微网 i 输送给微网 j 的功率,必须等于微网 j 从微网 i 接收的功率,方向相反,即 P_ij + P_ji = 0。把这组约束直接放进一个大模型集中式求解,结果当然没问题。但在真实场景里,不同微网可能分属不同运营主体,谁都不愿意把内部设备参数、负荷数据全部交给一个中央调度中心,这时候就需要分布式求解。
ADMM(交替方向乘子法)是处理这类可分解耦合问题的标准工具。它的思路是:先给每个微网构造一个增广拉格朗日目标函数,把耦合约束以拉格朗日乘子和二次惩罚项的形式加入各微网自己的子问题;然后各微网并行或顺序求解自己的子问题,再交换交互功率变量,更新乘子,反复迭代直到收敛。通信上只需要交换相邻微网的交互功率承诺值和乘子,内部数据完全不用公开。
不过我也要说句实话:如果所有微网都属于同一运营主体,集中式求解更快、更稳,ADMM 更多是用来验证分布式算法的机制和隐私保护价值。论文里常写 ADMM,实际工程项目里要权衡清楚。
3.4 核心 MATLAB 代码片段与逻辑解读
下面给出一个的简化版框架,方向索引之类的细节需要你根据图结构调整。
主循环代码:
% 参数初始化 num_mg = 5; % 微网数量 num_edges = 7; % 联络线数量 T = 24; % 调度时段数 rho = 0.1; % 惩罚系数 max_iter = 300; tol = 1e-4; P_ij_old = zeros(num_edges, T); lambda = zeros(num_edges, T); P_ij_result = cell(1, num_mg); for iter = 1:max_iter % 各微网独立求解子问题 for i = 1:num_mg [P_mg_i, P_ij_local] = solve_mg_subproblem(i, lambda, rho, P_ij_neighbor_set{i}); P_ij_result{i} = P_ij_local; end % 更新拉格朗日乘子 for e = 1:num_edges i = edges(e, 1); j = edges(e, 2); P_send = P_ij_result{i}(edge_map{i}(e)); P_recv = P_ij_result{j}(edge_map{j}(e)); lambda(e, :) = lambda(e, :) + rho * (P_send + P_recv); end % 原始残差和对偶残差 primal_res = 0; for e = 1:num_edges primal_res = primal_res + norm(P_ij_result{edges(e,1)}(edge_map{edges(e,1)}(e)) + ... P_ij_result{edges(e,2)}(edge_map{edges(e,2)}(e)), 2); end if primal_res < tol break; end end子问题函数片段:
function [P_mg, P_ij_out] = solve_mg_subproblem(i, lambda, rho, P_ij_neighbor) P_buy = sdpvar(1, T); P_sell = sdpvar(1, T); P_ch = sdpvar(1, T); P_dis = sdpvar(1, T); SOC = sdpvar(1, T + 1); P_g = sdpvar(1, T); P_ij_local = sdpvar(1, T); Constraints = []; % 功率平衡 Constraints = [Constraints, P_g + P_dis + P_buy + P_pv(i, :) + P_ij_in ... == P_load(i, :) + P_ch + P_sell + P_ij_out]; % 储能SOC约束 Constraints = [Constraints, SOC(2:T+1) == SOC(1:T) + (0.95 * P_ch - P_dis / 0.95) / E_cap(i)]; Constraints = [Constraints, SOC(1) == 0.3, SOC >= 0.2, SOC <= 0.9]; % 设备上下限 Constraints = [Constraints, 0 <= P_g <= P_g_max(i, :)]; Constraints = [Constraints, 0 <= P_ch <= P_ch_max(i), 0 <= P_dis <= P_dis_max(i)]; Constraints = [Constraints, 0 <= P_buy <= P_grid_max, 0 <= P_sell <= P_grid_max]; % 目标:本地成本 + 拉格朗日项 Objective = sum(c_g * P_g + c_buy * P_buy - c_sell * P_sell) ... + sum(lambda_edge .* P_ij_local) ... + sum(0.5 * rho * (P_ij_local + P_ij_neighbor).^2); optimize(Constraints, Objective, sdpsettings('solver', 'gurobi', 'verbose', 0)); P_mg = value([P_g; P_buy; P_sell; P_ch; P_dis]); P_ij_out = value(P_ij_local); end这段代码最需要注意的地方是乘子更新的符号和方向。不同论文对 P_ij 的正方向定义不同,你在复现时最好先在两微网算例上验证“送端输出等于受端接收”,再扩展到多微网。符号一旦弄反,ADMM 大概率发散的。
4. 调试阶段常见的坑与排查经验
4.1 ADMM 不收敛或残差震荡
这是我被问得最多的问题。ADMM 迭代几百步后残差还在几个 MW 级别来回横跳,多半不是模型错,而是参数没调好。首要检查点是交互功率的量纲:如果模型里功率基准是 kW,而惩罚系数 ρ 还在 1 附近,二次惩罚项会大到离谱,把目标函数淹没了。我习惯先把所有功率归一到 MW 或 p.u.,再让 ρ 从 0.1 起步,观察残差曲线做调整。
如果残差出现周期性震荡,可以用自适应 ρ 的思路:当原始残差远大于对偶残差时,把 ρ 乘 2;反过来就除以 2。这个技巧在工程实现里很实用,能显著加快收敛。另外,初始乘子不要轻易给非零值,零乘子配合一个合理的 P_ij 初始化,通常更稳定。
4.2 权重设置导致分配结果不合常理
有时候你按资源容量比例设置了权重,但某个微网分到的收益增量居然超过它在独立运行下的最大潜在收益,这说明第二阶段分配问题缺少上限约束。比如一个本身完全不需要合作的微网,被强行塞进联盟还分配了大额收益,这显然不符合谈判直觉。
解决办法是在第二阶段加约束 Δ_i ≤ Δ_i^max,其中 Δ_i^max 是微网 i 从合作中能获得的潜在最大收益增量,通常用该微网在合作前后极端场景下的成本差值来估计。另外,目标函数里 log(Δ_i) 要求 Δ_i 严格大于 0,所有分配变量都要加一个很小的下界,比如 1e-6,否则 Yalmip 会报出 NaN。
4.3 Yalmip 与 Gurobi 的版本兼容问题
Gurobi 9 之后与 Yalmip 配合得总体不错,但有个常见报错是关于非凸二次约束的。在 ADMM 子问题中,增广拉格朗日项的 (P_ij + P_ij^ref)^2 是目标函数里的二次项,Gurobi 能处理凸二次目标;但如果这个二次项被误写进约束,就容易报出 “Unable to convert quadratic constraint to linear”。我的建议是:二次项只放目标函数,不放约束;等值约束先用线性表达,需要时再加辅助变量。
遇到版本兼容问题,先更新 Yalmip 到最新版,再看 Gurobi 的 license 是否正确加载。老版本的 MATLAB 配合新 Gurobi 时,偶尔会遇到接口文件路径不对,重新执行gurobi_setup通常能解决。
4.4 功率平衡无解与不可行
光伏出力高峰期,如果微网内部缺乏足够储能和可调负荷,功率平衡等式约束很容易造成模型不可行。初学者容易上来就硬解 infeasible,然后开始怀疑人生。正确做法是在功率平衡约束里加入非负松弛变量,并对松弛量施加较高的惩罚价格,比如 1000 元/MWh。这样模型永远是可行的,松弛量也能告诉你哪些时段、哪个微网确实出现了供需缺口,方便继续分析。
| 问题现象 | 排查顺序 | 常用手段 |
|---|---|---|
| ADMM 残差震荡 | 看量纲 → 调 ρ → 自适应 ρ | 归一化功率基准,ρ 初始 0.1 |
| 分配结果超上限 | 检查 Δ_i^max 约束 | 加收益上限约束 |
| 二次约束报错 | 检查目标 vs 约束 | 二次项移入目标函数 |
| 模型不可行 | 加松弛变量 | 高惩罚价格松弛供需约束 |
5. 从复现到扩展:我的一些真实体会
5.1 能跑通代码,不代表能直接落地
模型复现成功只是第一步。真实项目里,各微网独立运行成本的申报很容易被操纵,虚报成本可以抬高自己在谈判中的收益分成。如果多个微网同属一个运营主体,内部调度用非对称纳什谈判做算法基准没有问题,但跨主体的真实交易结算就要考虑机制设计的问题。
我的建议是把非对称纳什谈判当成一个“价格发现工具”,而不是直接替代现货市场的规则。用它反推出来的内部电价、转移支付,可以作为各微网间双边协商的参考基准。
5.2 这个模型还能往哪些方向扩展
非对称纳什谈判非常容易跟其他机制嵌套。比如目标函数里加入碳交易成本、绿证收益,谈判权重就可以反映碳减排贡献;把固定权重扩展到分时权重,能刻画不同时段议价能力的动态变化;接入需求响应柔性负荷后,谈判结果会影响各微网的负荷调整策略,形成更丰富的互动。
另一个我比较看好的方向是把权重参数化,用历史运行数据去回归或学习最优权重,让谈判结果在长期运行中逼近某种公平性和效率的平衡。很多论文喜欢把权重固定,实际运行里权重应该是动态的。
5.3 给刚开始复现这个模型的同学一句经验
务实地建议一下推进顺序:先做两个微网、24 时段、无储能的算例,手算都能算清楚的那种,把纳什谈判分配逻辑跑明白;再加储能、加柴发,跑 ADMM 看收敛行为;最后扩展到 N 微网的随机拓扑。千万别一上来就搞五微网加混合整数加碳交易,那样只会让调试难度翻倍,你根本分不清是算法问题还是建模问题。
我用这个序贯方式做过三个类似项目,每次都能快速定位模型缺陷。非对称纳什谈判是个值得细啃的模型,把基础算例吃透,后面无论换场景、换约束、换目标函数,框架都能稳稳撑住。