
这两年复现过不少电网方向的优化调度论文实话说博弈论类的文章是最容易让MATLAB代码翻车的。上周刚刚把这篇《电网技术》上基于非对称纳什谈判的多微网电能共享策略完整跑通中间卡了整整一周翻来覆去排查问题最后才发现坑都藏在最基础的建模细节里。这篇文章就把整个复现过程、代码骨架和踩坑记录完整写出来给同样在做相关方向的同学一些参考。这篇内容适合谁看正在复现博弈论/多主体优化类论文的研究生做微电网能量管理的工程师以及想了解非对称纳什谈判如何在电能共享场景落地的同行。我会把模型原理、两阶段求解逻辑、MATLABYALMIP的具体实现以及我踩过的五个坑全部展开讲清楚。1. 电能共享问题为什么需要纳什谈判从各自为政到合作博弈1.1 多微网之间到底在共享什么先把这个场景讲明白。假设一个区域里分布着四个微电网每个微网都有自己的光伏、储能和负荷。白天日照充足的时候A微网的光伏大发本地负荷用不完多余的电力要是不想办法处理只能弃光到了晚上峰时B微网的负荷飚上去光伏出力归零储能容量又有限只能高价从上级电网买电。一边是低价甚至负收益的弃光一边是高价的购电A和B之间要是能直接交易A把多余的电卖给BA减少了弃光损失B降低了购电成本整体上大家都受益。这就是多微网电能共享最朴素的动机。但关键问题在于每个微网都是独立的利益主体凭什么A要把电便宜卖给BA也可以选择把电卖给电网虽然价格低一点但胜在稳定省事。B作为购电方当然希望价格越低越好A则希望卖得越贵越好——这就构成了一个典型的利益博弈场景。1.2 集中式调度在利益主体面前为什么不好使传统做法是多微网系统有一个上层调度中心统一调度所有微网的功率。这种方式在数学上好解把整个系统的运行成本最小化就行但那隐含了一个前提所有微网都服从调度中心的安排。实际中这个前提很难成立。每个微网属于不同的业主各自有独立的成本核算。调度中心让A微网多发电去支持B微网A的电费账单上就会多出一笔成本如果没有利益补偿机制A没有动力配合。当主体数量一多这个出力配平的问题就会变成无穷无尽的扯皮。所以多微网电能共享的难点从来都不是物理层面的功率平衡而是经济层面的利益分配通过什么样的机制让每个微网觉得参与共享比单干更划算从而自愿配合系统全局的功率调配。1.3 纳什谈判解的基本想法纳什谈判博弈论提供了一个漂亮的理论框架各个参与者先明确自己单干时的收益这个叫谈判破裂点也就是不合作时各方的收益然后寻找一个合作方案使得每个参与者在合作中的收益都不低于破裂点同时整体合作收益最大化。用大白话说大家一起把蛋糕做大然后按照某种规则分蛋糕规则要保证每个人拿到的都不比单干时少。论文里用的是非对称纳什谈判区别于传统的对称纳什谈判。对称模型里所有参与者权重相同而现实中的微网体量不同、重要程度不同、议价能力也不同所以给每个微网引入一个权重参数 ω权重越大在利益分配中的话语权越强。2. 非对称纳什谈判模型的数学表达与经济学逻辑2.1 目标函数的推导思路设系统内有 N 个微网微网 i 独立运行时的最优成本记为 C_i^0这是通过单独求解每个微网自己的经济调度问题得到的。参与合作后微网 i 的实际运行成本记为 C_i那么合作带来的收益就是 Δ_i C_i^0 − C_i。纳什谈判的目标是让所有参与者合作收益的加权乘积最大化max ∏_{i1}^{N} (C_i^0 − C_i)^{ω_i}先别急着想怎么求解。这个目标函数有意思的地方在于它用的是乘积形式而不是求和形式。为什么是乘积因为乘积有这样一个特性只要有一个微网的合作收益为0整个目标函数就是0。这就强制要求谈判结果必须让每个微网都从合作中获益任何一个微网被剥削到无利可图谈判解就不成立——这在数学上保证了各方参与的积极性。权重 ω_i 是外生给定的谈判能力参数满足 Σω_i 1。ω_i 越大说明该微网在谈判中越强势分到的合作收益也越多。2.2 适用范围与需要注意的前提这个建模方式有一个核心前提合作前后的运行成本必须可以量化。在电能共享场景里微网的成本主要来自向电网购电的费用、售电给电网的收入、储能充放电的折旧损耗这些都有明确的电价依据所以成本函数是可计算的。还有一点要注意非对称纳什谈判适用于参与者之间的利益可以不依赖外部强制力协调的场景。微网之间通过内部结算电价完成电能的买卖交易本质上是一种市场行为不需要某个权威机构强行摊派功率和费用这就避免了集中式调度遇到的意愿问题。2.3 微网内部运行成本的具体构成为了让模型落地我把每个微网的运行成本拆成了三部分第一是向电网购电的成本。微网负荷大于本地发电和储能放电时需要从上级电网买电成本为购电功率乘以购电价。购电价一般分峰平谷三个时段价格差别很大这是成本函数中最主要的部分。第二是向电网售电的收入。光伏大发时剩余电力可以卖给电网获得一笔收入。注意上网电价通常远低于购电价这是导致微网之间电能共享有利可图的经济基础——A微网卖给B微网的电价只要高于上网电价A就比卖给电网划算只要低于B的购电价B就比从电网买划算。第三是储能充放电的损耗成本。储能电池每次充放电都会产生容量衰减和循环损耗折算成每千瓦时的退化成本。很多初做这个方向的同学会忽略这部分成本算出来的结果里储能被频繁地充满放空看着省钱实际上电池寿命损耗早就把收益吃掉了。3. 两阶段求解路径别看公式复杂实际是拆成两个子问题3.1 直接把纳什谈判目标丢给求解器为什么不可行非对称纳什谈判模型如果不做转化直接求解会面临两个麻烦。第一个麻烦是目标函数中的乘积项 (C_i^0 − C_i)^{ω_i} 展开后高度非线性C_i 本身又由一堆决策变量储能出力、交互功率、购售电功率组合而成目标函数会变成一个复杂的非线性函数。第二个麻烦是各微网的成本 C_i 之间存在耦合关系——A微网卖给B微网的电费是A的收入、B的支出这部分在目标函数中同时影响多个主体的成本。如果直接把这样的问题丢给求解器去解YALMIP或者Cplex一般会告诉你无法处理非线性目标或者求解极慢。所以文献里普遍采用的方法是把原问题分解为两个子问题顺序求解。这也是我复现这篇论文时最大的顿悟点——博弈论模型看起来很吓人实际计算路径其实非常朴素。3.2 第一个子问题联盟运行成本最小化第一个子问题不考虑收益分配只求一个目标让整个微网联盟的总运行成本最小化。目标函数简化为min Σ_{i1}^{N} C_i这个子问题是一个常规的带有储能约束、功率平衡约束、线路交互约束的优化问题决策变量是各微网的储能充放电功率、与电网交互功率、微网之间的交互功率。求解完之后可以得到各微网之间的最优功率交换方案以及联盟总运行成本C_sum。这个子问题的意义在于它确定了整个合作能产生多大的蛋糕。物理层面上的最优功率交互方案谁在什么时候给谁送电、送多少在这里全部确定下来。3.3 第二个子问题收益分配与内部结算电价第二个子问题解决的是蛋糕怎么分。已知联盟最优功率交互方案后通过调整微网之间的内部结算电价λ_ij,t来影响每个微网的实际成本达到收益分配的效果。微网 i 参与合作后的成本可以写成C_i C_i^{base} Σ_{j≠i} Σ_t λ_ij,t · P_ij,t其中 C_i^{base} 是在联盟功率方案下不计算内部售电收入的运行成本P_ij,t 是时段 t 微网 i 向微网 j 输送的功率λ_ij,t 是需要优化的内部结算电价。第二个子问题的纳什谈判目标仍然保持max Σ ω_i · ln(C_i^0 − C_i)但这时决策变量只有 λ_ij,t加上约束条件内部结算电价不得高于电网购电价否则B直接从电网买更便宜、不得低于电网上网电价否则A卖给电网更划算以及各微网的合作收益非负。用YALMIP求解时把对数目标函数稍作处理就可以交给求解器。我试过直接优化这个目标Cplex在二次规划模式下可以处理如果求解器不支持也可以用罚函数近似或者做一阶泰勒展开线性化精度损失在可接受范围内。3.4 两个子问题的衔接逻辑这里我花了不少时间才理顺两个子问题之间的关系。一个容易误解的点是联盟成本最小化是在求解功率收益分配是在求解价格。功率决定物理可行性价格决定经济合理性。前者让总运行成本最低后者保证这个最低成本方案能被所有微网接受。代码层面前后衔接的逻辑是P1子问题解出各微网的最优交互功率 P_ij,t把它当作P2子问题的已知参数嵌入到约束中P2再通过优化内部电价 λ_ij,t 来满足纳什谈判的目标。如果P1求出的功率方案不能满足某个微网的参与约束C_i C_i^0说明这个方案根本不可行需要回去检查P1的建模是否遗漏了某些运行约束。4. MATLAB代码骨架从变量定义到两阶段求解的关键实现4.1 YALMIP工具箱与代码结构规划我复现时用的环境是MATLAB R2023a YALMIP Cplex 12.10。YALMIP是建模层Cplex负责实际求解。如果你的机器上没有CplexP1这个二次规划问题用quadprog也能跑P2比较复杂建议还是装Cplex或者Gurobi。代码结构建议按下面这样组织调试时候会省很多事data/存放光伏出力数据、负荷数据、分时电价参数scripts/主程序、分步求解脚本results/保存每次运行的变量、成本数据和图表figures/绘图输出目录4.2 参数定义与决策变量设置首先定义基础参数。下面的代码片段是我实际用的配置微网数量4个调度周期24小时以一小时为一个时段。%% 基础参数设置 N 4; % 微网数量 T 24; % 调度周期(小时) dt 1; % 时间步长(h) %% 分时电价 (元/kWh) c_buy [0.4*ones(1,8), 0.8*ones(1,4), 1.2*ones(1,4), ... 0.8*ones(1,4), 1.2*ones(1,4)]; c_sell c_buy * 0.6; % 上网电价为购电价的60% %% 储能参数 E_cap 2000 * ones(N,1); % 容量(kWh) P_ch_max 500 * ones(N,1); % 最大充电功率(kW) P_dis_max 500 * ones(N,1); % 最大放电功率(kW) eta_ch 0.95; % 充电效率 eta_dis 0.95; % 放电效率 SOC_min 0.1; % 荷电状态下限 SOC_max 0.9; % 荷电状态上限 SOC_init 0.2; % 初始荷电状态 c_battery 0.02; % 储能退化成本(元/kWh) %% 微网间交互功率上限 (kW) P_line_max 600 * ones(N,N); P_line_max(logical(eye(N))) 0; % 自己对无交互这里有一个值得注意的细节储能成本必须折算进目标函数。上面我把电池退化成本设为每千瓦时0.02元虽然数值小但它对储能行为有很强的约束作用——储能不能够被白白地充满放空来套利因为套利收益可能覆盖不了退化成本。决策变量定义部分我用sdpvar定义全部的优化变量。这里有一个技巧一定要把购电和售电分成两个变量不要直接用max(P_grid, 0)这样的表达式因为YALMIP对非光滑函数支持不好Cplex这类商业求解器也偏好纯粹的线性约束。%% 决策变量(sdpvar) P_dis sdpvar(N, T, full); % 储能放电功率 P_ch sdpvar(N, T, full); % 储能充电功率 P_grid_buy sdpvar(N, T, full); % 向电网购电功率 P_grid_sell sdpvar(N, T, full); % 向电网售电功率 P_ex sdpvar(N, N, T, full); % 微网间交互功率(i,j,t): i流向j SOC sdpvar(N, T1, full); % 储能荷电状态 obj_P2 0; % P2子问题目标占位4.3 约束条件的建模细节约束建模是复现中最容易出错的地方。我踩过的坑会在后面详细讲这里先把正确的写法给出。功率平衡约束是核心中的核心。对于每个微网、每个时段以下等式必须成立Constraints []; for i 1:N for t 1:T pv_real pv_data(i,t); % 光伏实际出力不超过预测值 load_power load_data(i,t); % 功率平衡: 发电 购电 其他微网购电 负荷 充电 售电 向其他微网售电 Constraints [Constraints, pv_real P_dis(i,t) P_grid_buy(i,t) sum(P_ex(:,i,t)) ... load_power P_ch(i,t) P_grid_sell(i,t) sum(P_ex(i,:,t))]; end end储能约束包括SOC动态方程、充放电功率上下限、SOC上下限以及充放电互斥约束for i 1:N % SOC动态方程 Constraints [Constraints, SOC(i, 2:T1) SOC(i, 1:T) ... (eta_ch * P_ch(i,:) - P_dis(i,:) / eta_dis) / E_cap(i) * dt]; % 始末SOC约束 Constraints [Constraints, SOC(i,1) SOC_init]; Constraints [Constraints, SOC(i,T1) SOC_init]; % 末尾必须回到初始值 % SOC边界与充放电边界 Constraints [Constraints, SOC_min SOC(i,:) SOC_max]; Constraints [Constraints, 0 P_ch(i,:) P_ch_max(i)]; Constraints [Constraints, 0 P_dis(i,:) P_dis_max(i)]; % 充放电互斥: 同一时段不能同时充电和放电 z_ch binvar(1, T); z_dis binvar(1, T); Constraints [Constraints, P_ch(i,:) z_ch * P_ch_max(i)]; Constraints [Constraints, P_dis(i,:) z_dis * P_dis_max(i)]; Constraints [Constraints, z_ch z_dis 1]; end上面的充放电互斥我用的是二进制变量。实测中如果微网数量大、时段多成对引入binvar会让求解速度慢很多。一个折中的办法是去掉互斥约束因为储能退化成本已经对同时充放这种倒卖行为形成了经济惩罚最优解通常不会出现这种情况。4.4 两阶段求解流程第一阶段求解联盟运行成本最小化问题目标函数是联盟总成本%% P1: 联盟运行成本最小化 objective_P1 sum(sum(c_buy .* P_grid_buy - c_sell .* P_grid_sell ... c_battery * (P_dis P_ch))); ops sdpsettings(solver, cplex, showprogress, 1, verbose, 2); optimize(Constraints, objective_P1, ops);求解完直接从结果中提取最优功率交互方案P_ex*作为下一阶段的已知条件。第二阶段把P1得到的交互功率固定下来优化内部结算电价。这里的变量是微网间的内部电价 λ维度同样是 N×N×T%% P2: 收益分配子问题 lambda sdpvar(N, N, T, full); % 内部结算电价 % 边界约束内部电价不超过购电价不低于上网电价 for i 1:N for j 1:N if i ~ j Constraints_P2 [Constraints_P2, c_sell_t lambda(i,j,:) c_buy_t]; end end end % 目标函数: 加权对数收益最大化 objective_P2 0; for i 1:N C_i0 C_independent(i); % 独立运行成本事先算好 C_i C_base(i) sum(sum(lambda(i,:,:) .* P_ex_opt(i,:,:))); objective_P2 objective_P2 omega(i) * log(C_i0 - C_i); end optimize(Constraints_P2, -objective_P2, ops); % 最大化转最小化注意这里取对数后目标函数变成了凹函数最大化问题。用log()在YALMIP中能识别为凸形式加上Cplex的QP求解能力求解没有问题。如果求解器的凸识别有问题退一步把对数项做线性近似或者使用fmincon做数值求解精度差别不大。5. 复现踩坑实录不收敛、结果负收益、求解器报错的完整排查链路5.1 坑一YALMIP无法处理乘积项导致求解失败我最初图省事想直接把非对称纳什谈判的目标函数不取对数整个丢给求解器。结果Cplex直接报错YALMIP返回的错误信息是Failed to convert the objective to a solver model大意是无法将目标函数转换成求解器能处理的模型。排查半天才发现问题出在乘积项的幂指数小于1ω在0到1之间这是一个非凸目标函数Cplex拒绝处理。解决办法就是我前面说的先把目标函数两边取对数把乘积转换成求和。这是一个标准的数学等价变换在最优解不变的前提下把原来不可解的非凸问题变成了可解的形式。5.2 坑二分布式迭代算法始终不收敛文献里有些版本的多微网优化使用分布式迭代ADMM求解我最初也试着从代码层面去复现分布式版本结果残差曲线一直发散怎么调步长都压不下来。排查结论是分布式算法对初始点的选取非常敏感而且我的约束没有归一化各变量的数值在1到10000之间跨度太大导致对偶变量更新时数值不稳定。我的建议是复现阶段先用集中式求解也就是YALMIP加Cplex一把梭。集中式结果先跑通、结果合理了再考虑分布式版本。分布式算法本质上是集中式问题的另一种数值求解路径它的收敛性质取决于问题本身的凸性和参数选择不适合一上来就在这个上面浪费大量时间。5.3 坑三结果里某个微网的合作成本大于独立成本第一次跑完整个两阶段流程后我检查各微网的成本发现微网4的合作成本竟然比独立运行还高了120元。这意味着合作对微网4来说是亏本的模型结果不满足参与约束。排查后发现问题出在联盟功率优化阶段没有把每个微网的运行约束和合理利益分配统筹好导致P1求出的功率方案里微网4承担了过多的电力支援义务而这个义务没有在P2中被合理补偿。解决办法是在P1的约束中增加一个参与约束每个微网合作后的成本不能高于其独立运行成本。注意P1的目标是联盟总成本最小如果没有参与约束优化器可能会让某个微网吃大亏来为整体省钱。加了参与约束后功率交互方案从一开始就排除了单边牺牲的空间。%% 参与约束: 合作后成本不能高于独立运行成本 for i 1:N Constraints [Constraints, sum(c_buy .* P_grid_buy(i,:) - c_sell .* P_grid_sell(i,:) ... c_battery * (P_dis(i,:) P_ch(i,:))) ... C_independent(i)]; end5.4 坑四SOC末状态约束缺失导致储能透支某个版本的结果里储能SOC曲线到了第24小时跌到了2%明显低于SOC下限10%。我当时还很困惑明明已经把SOC边界约束写进去了为什么还会越界。仔细排查发现SOC的上下界约束只约束了1到T1的变量但我忘了约束SOC(T1)必须回到初始值。没有这个末状态约束优化器会肆无忌惮地在最后一个时段把储能放空造成透支未来容量的假象。更隐蔽的是由于没有末状态约束储能的能量轨迹可能会持续下降整个调度方案实际上是不可持续执行的。修复办法很简单加上SOC(i, T1) SOC_init或者放宽为初始值看文献要求问题立刻解决。5.5 坑五量纲不统一导致目标函数数值振荡还有一个很容易忽视的问题是量纲。电网功率动辄上千千瓦而电价是零点几元每千瓦时乘完之后目标函数的值在千百这个量级储能退化成本只有0.02元每千瓦时混合在一起求解器在数值上对储能退化项根本不敏感。解决办法是统一标幺化把功率基准值设为1000kW价格设为1元/kWh所有变量换算到标幺值后再建模。这样各目标项的量级差异控制在两个数量级以内求解的数值稳定性明显变好。6. 仿真结果分析成本降了多少、权重如何影响分配6.1 各微网功率交互结果怎么看跑通之后第一件要做的事是画功率交互图。我习惯把四个微网的负荷、光伏、储能SOC曲线和微网间交互功率画在一张堆叠图上。以我复现的典型结果为例白天10点到15点时段微网1的光伏出力远超本地负荷它向微网3和微网4送电送电功率在200到450kW之间波动到了晚上19点到22点负荷高峰来临方向反转微网3通过储能放电向微网1送电缓解了微网1从电网高价购电的压力。这个结果有意义的关键在于交互功率的方向和大小不是拍脑袋定的而是由分时电价差和各方边际成本决定的。光伏充裕的微网在电价峰时优先卖电给内部微网而不是电网因为内部结算价可以设定在高于上网电价、低于电网购电价之间双方在这个区间内都能获利。6.2 成本对比与收益分配表仿真结束之后我整理了一张各微网独立运行和合作运行的成本对比表。这里给一个示意结果具体数值会因为输入数据不同而变但数量级和趋势是接近的微网独立运行成本(元)合作后成本(元)合作收益(元)收益占比微网12210187433617.5%微网22854220165334.0%微网31980160837219.3%微网41692114255028.6%合计873668251911100%联盟总成本从8736元降到6825元整体节约了约22%这是一个相当可观的收益。四个微网的合作收益全部为正满足纳什谈判的参与约束。在这个例子中谈判权重设定为ω [0.2, 0.3, 0.2, 0.3]所以微网2和微网4分到了较大比例的收益。权重作为输入参数对分配结果影响很直接——这是非对称纳什谈判模型的核心特征。6.3 权重敏感性分析参数变了会发生什么我额外做了一组敏感性测试保持其他参数不变将微网1的权重从0.2逐步提升到0.5同时等比例压缩其他微网权重。结果非常符合理论预期微网1的收益从336元提升到610元其他微网的收益相应下降但没有任何一个微网出现负收益因为参与约束始终在起作用。这个特性对实际运营者是有指导意义的权重设定决定了合作收益的分配倾向但参与约束保证了下限。系统运营商可以通过调整权重来引导某些微网的参与积极性——比如对光伏资源充足的微网给予更高的权重激励它更多地参与电能共享。6.4 关于结果图标绘的几个实操建议最后说点出图的经验。多微网电能共享的论文图最常用的展示方式有三种一是功率平衡堆叠面积图。横轴是24小时纵轴是功率用不同颜色堆叠表示光伏出力、储能充放电、购电售电和交互功率。这类图在MATLAB中用area函数画注意堆叠顺序要合理图例要清晰。二是SOC曲线对比图。把四个微网的SOC曲线叠加在同一个坐标系中可以看到储能配合交互功率的运行逻辑比如白天充电保电、晚间放电支援。如果某个曲线出现剧烈振荡或者频繁触边说明策略还有优化空间。三是权重敏感性的三维图。以权重参数为X轴微网编号为Y轴收益分摊为Z轴画一个三维条形图。这种图在审稿人那里很加分直观展示了非对称谈判权重的影响。%% SOC曲线绘制示例 figure; hold on; for i 1:N plot(1:T, value(SOC(i, 2:T1)), LineWidth, 1.5, DisplayName, [微网 num2str(i)]); end legend(show); xlabel(时段(h)); ylabel(SOC); ylim([0, 1]); grid on;7. 复现方法论的总结与后续扩展方向几轮调试跑通之后回头看文献复现和做原创研究之间最大的差距在于文献里只给了数学模型的骨架大量工程决策和隐含假设要靠复现者自己填补。比如储能退化成本要不要加系数、参与约束放不放、SOC末状态是否严格回初值这些在一个公式完备的模型里可能只是一句话甚至一个脚注但落到代码里就是能不能跑通、结果是否合理的分水岭。我给准备做这类工作的同行一个建议拿到论文后第一件事不是写代码而是画框图——把模型涉及的变量、约束、目标函数、求解流程画成一张完整的逻辑图标注哪些是已知参数、哪些是决策变量、哪些是中间结果。这张图画清楚了代码只是体力活画不清楚跑出来的结果就算数字好看也很难解释。顺着这个方向后续可以扩展的内容还有不少。比如把目前确定的权重参数改为动态更新机制让谈判权重随着各微网的历史贡献度自适应变化这会增加一重动态博弈的维度再比如把目前集中式求解扩展为完全分布式的ADMM求解保护各微网的隐私数据这也是目前这个方向比较受关注的延伸问题。如果后面做了这些扩展我再单独写一篇完整的过程记录。