
接手这个课题之前我正被一个多微网协同调度的仿真项目卡得难受。课题组最初的方案是集中式优化几个微网的时候还算顺利一扩到10个节点求解器内存占用肉眼可见地往上涨更头疼的是各个微网的内部运行数据没人愿意全部交出来——数据隐私在联合调度里从来都是躲不开的坎。后来我仔细过了一遍基于非对称纳什谈判的多微网电能共享运行优化策略发现它恰好同时解决这三件事分布式求解、隐私保护、利益合理分配。我花了两个多星期把整套策略用Matlab完整实现并反复调参这篇就记录我从模型原理到代码落地、再到调试验证的全过程。如果你正在做微电网群、分布式能源交易或博弈论相关的优化仿真这篇文章应该能帮你少走不少弯路。1. 从对称到非对称纳什谈判模型在微网共享中到底改了什么1.1 为什么要用博弈论解决电能共享多微网电能共享的第一步不是写代码而是想清楚为什么不能用普通的单层优化解决。微网与微网之间存在天然的博弈关系每个微网有自己的负荷、分布式电源、储能设备它们既不希望自己的运行成本变高又不愿意在共享过程中吃亏。用集中式优化的时候目标函数通常是把所有微网的成本加在一起求最小。这个数学上没问题但实际落地有两个硬伤。硬伤之一是计算复杂度。微网数目一旦变多优化变量里除了连续功率变量还可能包含储能充放电状态、机组启停状态等整数变量整个模型会迅速膨胀。我实测过一个中等规模场景集中式建一个混合整数二次规划单次求解就要几十秒如果还要跑多场景蒙特卡洛仿真时间成本完全不可接受。硬伤之二是利益分配的透明度。集中式优化能算出一个全局最优运行策略但它不告诉你每个微网应该承担多少成本、享受多少收益。如果直接按谁出力多谁多摊成本这种粗暴规则分账必然有人不满意。博弈论里的纳什谈判不同它把一个合作优化问题转化为如何分蛋糕的问题每个参与者都有明确的谈判破裂点最后得到的解同时满足个体理性、帕累托最优和对称性或加权后的一致性这才符合工程实际。1.2 对称纳什谈判的短板所有人的谈判力都一样纳什谈判的基本形式是最大化参与者效用的乘积对称纳什谈判中每个参与者的权重完全相等。听起来公平但放在多微网场景里就有点理想化了。用大白话讲几个微网合伙做一单生意其中一个微网提供了最多的可调容量和最多的共享电量另一个微网只是偶尔买点余电最后大家平分利润前者心里能平衡吗在对称纳什谈判框架下谈判解会尽量让每个微网获得相同的效用增量或者更准确地说让每个参与者的效用乘积最大化而不考虑各自的贡献差异。可实际场景中光伏资源充裕的微网往往有大量富余电量可共享而负荷型微网主要是买方角色储能容量大的微网还能提供备用和调峰服务。这些参与者在共享合作里的付出完全不同如果权重全都一样结果是高贡献微网分享到的利益并未显著高于低贡献微网长期来看合作根本维持不下去。所以就有了非对称纳什谈判。它的核心改动是给每个参与者引入一个谈判权重 (\omega_i)用来度量该微网在合作系统中的话语权或议价能力。权重越大谈判结果越向该微网倾斜。这个简单的改动让模型从一个理想公平的解变成了一个按贡献分配的解工程适应性一下子强了很多。1.3 非对称谈判的数学形式与权重的实际含义先把数学模型摆出来。假设系统里有 (N) 个微网每个微网独立运行时的最小总成本记为 (C_i^0)这代表谈判破裂点——如果我不跟你合作我自己单独玩最低成本是多少。合作后经过电能共享和统一协调整个系统有一个总最优成本 (C_{\text{total}}^{\text{shared}})然后需要给每个微网分摊一个成本 (C_i)使得所有微网的分摊成本加起来等于系统总成本[ \sum_{i1}^{N} C_i C_{\text{total}}^{\text{shared}} ]并且分摊后的成本不能高于独立运行成本[ C_i \le C_i^0 ]否则这个微网没有合作动机。定义成本节约额为 (\Delta C_i C_i^0 - C_i)这是每个微网从合作中获得的收益蛋糕。非对称纳什谈判问题的目标函数是最大化加权乘积[ \max \prod_{i1}^{N} \left( C_i^0 - C_i \right)^{\omega_i} ]其中 (\omega_i 0) 是第 (i) 个微网的谈判权重。当所有 (\omega_i) 相等时它就退化成了对称纳什谈判所以对称只是非对称的一个特例。这个目标函数有一个很关键的代数变形由于对数函数严格单调递增最大化乘积等价于最大化对数加权和[ \max \sum_{i1}^{N} \omega_i \ln \left( C_i^0 - C_i \right) ]这个变形在求解时特别有用因为对数项是凹函数整个目标函数变成凹最大化问题在凸约束下能直接交给优化求解器处理也方便后续用ADMM分布式求解。至于权重怎么定数学上没有任何强制要求纯属工程决策。我自己的做法是用各微网的最大可共享电量占比来构造权重即 (\omega_i E_i^{\text{surplus}} / \sum_j E_j^{\text{surplus}})其中 (E_i^{\text{surplus}}) 是微网 (i) 在典型日内的富余电量上限。也有文献喜欢用可调容量占比、新能源装机占比或综合贡献度来设定哪种更合理取决于你研究的场景。总之这一步是结合常见实践补充设定的需要在论文里自圆其说代码实现上只需要保证权重为正数且归一化即可。2. 多微网系统怎么建模先算独立运行成本再谈共享收益2.1 微网内部的设备模型与约束多微网电能共享的模型通常分两层第一层是每个微网内部的设备与运行约束第二层是微网之间的功率交互约束。代码实现的时候我建议先把单微网模型写成一个独立的函数模块这样无论是计算独立运行成本、还是参与系统联合优化都能复用同一套模型。单微网内部的核心设备包括分布式光伏、风力发电、储能系统、常规燃气机组和本地负荷。光伏和风机在大多数算例中按预测出力处理也就是给定的功率曲线不作为优化变量。真正参与优化的主要是储能和燃气机组。燃气机组的发电成本常用二次函数近似[ C_i^{\text{gen}}(P_{i,t}) a_i P_{i,t}^2 b_i P_{i,t} c_i ]其中 (P_{i,t}) 是微网 (i) 在时段 (t) 的机组出力。一个微网里如果有多个机组将所有机组的成本加起来。储能系统用SOC递推方程建模[ \text{SOC}{i,t1} \text{SOC}{i,t} \eta_c P_{i,t}^c \Delta t - \frac{P_{i,t}^d}{\eta_d} \Delta t ]其中 (\eta_c)、(\eta_d) 为充放电效率(P_{i,t}^c)、(P_{i,t}^d) 为充放电功率。需要满足储能容量约束、SOC上下限约束、充放电功率上下限约束以及一个日终SOC回归约束通常是回归到初始值否则优化结果会让储能一次性放空看着很省成本实际是薅未来时段的羊毛。每时段的电功率平衡是微网内部最重要的约束[ P_{i,t}^{\text{gen}} P_{i,t}^{\text{pv}} P_{i,t}^{\text{wind}} P_{i,t}^{\text{dis}} \sum_{j \in \Omega_i} P_{ji,t} P_{i,t}^{\text{load}} P_{i,t}^{\text{chg}} ]其中 (\Omega_i) 是与微网 (i) 存在连接关系的邻居微网集合(P_{ji,t}) 表示从邻居微网 (j) 流入微网 (i) 的功率正值代表从外部受电负值代表向外送电。这里要注意微网之间的交互功率同时出现在两个微网的平衡方程里一正一负这就是系统层面的耦合约束。2.2 共享电量与联络线耦合约束微网之间通过联络线交换功率需要满足两个基本设定方向相反但数值相等的约束 (P_{ij,t} -P_{ji,t})以及联络线传输容量约束 (|P_{ij,t}| \le P_{ij}^{\max})。前者是所有分布式协同优化里最核心的耦合约束也是后面ADMM要重点处理的难点。构造联络线容量约束时我建议直接把两个方向的限值都写上而不是只写上限因为代码里如果不小心让变量符号反向负的交互功率可能不受约束。另外如果考虑网络损耗很简单的做法是在平衡方程里加一个损耗项比如 (P_{ji,t} -(1 - \theta_{ij})P_{ij,t})其中 (\theta_{ij}) 是线损率。但第一次实现时建议先忽略线损把基本逻辑跑通后再加损耗否则出了问题很难分辨是优化逻辑的锅还是损耗建模的锅。在Matlab中用矩阵形式统一写约束会比逐条循环高效很多。比如把所有时段的功率平衡约束整理成 (A_{\text{eq}} x b_{\text{eq}})把联络线容量、机组爬坡、储能SOC上下限整理成 (A_{\text{in}} x \le b_{\text{in}})。变量的排列顺序在写目标函数、约束矩阵和提取结果时都要保持一致我踩过无数次顺序错位的坑教训就是从一开始就要定义一个全局变量索引函数比如getIndex(i, type, t)这种避免在多个函数里手写魔数。2.3 独立运行成本与谈判破裂点第二阶段问题的基础多微网电能共享的完整求解一般分两阶段。第一阶段求解系统层面的电能共享运行优化目标是让整个系统总运行成本最小得到各个微网的调度计划以及微网间的交互功率第二阶段才是真正的纳什谈判分配它固定第一阶段的调度结果只优化每个微网的成本分摊 (C_i)使加权收益乘积最大化。第一阶段的优化问题可以用下面这个简洁形式表示[ \min \sum_{i1}^{N} C_i^{\text{op}}(x_i) ][ \text{s.t. } x_i \in \mathcal{X}i, \quad P{ij,t} P_{ji,t} 0 ]其中 (\mathcal{X}i) 是微网 (i) 内部所有设备约束构成的可行域。求解这个模型并不需要谈判权重参与它只负责找出把蛋糕做大的方案。第二阶段问题的输入是这个总成本 (C{\text{total}}^{\text{shared}})加上每个微网的独立运行最优成本 (C_i^{\text{ind}})也就是谈判破裂点然后解前面说的加权纳什谈判问题得到最终分摊成本。第二阶段的关键约束是总成本封闭性所有微网分摊成本之和要严格等于第一阶段算出的系统总成本。因为第一阶段的目标是让总成本最小而第二阶段只是分配不能再把调度变量放开。我见过不少初学的人在这块犯迷糊他们让第二阶段重新优化了储能出力结果算出来的分摊成本其实对应了一套完全不同的调度方案第一阶段的那套全局最优共享计划被架空了。正确的做法是第二阶段只更新成本分摊变量和支付转移变量调度计划保持第一阶段的结果不变。3. 求解流程为什么会选ADMM以及子问题的拆分思路3.1 从集中式到分布式求解的动机第一阶段的多微网共享优化是一个带耦合约束的优化问题理论上可以直接用集中式求解器一口气算完。前面提过问题规模大了之后求解压力会上升而且所有微网都需要把内部设备参数交给一个中心节点这在现实中很难被接受。尤其当微网分属不同利益主体时谁都不愿意把自己的负荷曲线和储能容量全盘托出。ADMM交替方向乘子法是解决这类问题的经典工具。它的核心思想是把原来的全局问题分解成多个可以并行求解的局部子问题微网之间只需要交换少量边界变量——比如联络线交互功率——就能通过迭代逼近全局最优解。这样每个微网只向邻居暴露交互功率内部的数据保留在本地隐私安全性和计算效率都得到了保障。基于非对称纳什谈判的多微网电能共享策略最自然的分布式实现路径是第一阶段用ADMM解系统协调优化第二阶段也用ADMM解收益分配。两个阶段共用同一套迭代框架代码复用率很高调试起来也方便。3.2 ADMM迭代的核心变量与更新逻辑ADMM要处理的核心耦合约束是微网间的交互功率一致性[ P_{ij,t} S_{ij,t}, \quad P_{ji,t} -S_{ij,t} ]其中 (S_{ij,t}) 是引入的辅助变量代表联络线参考功率。为了把它嵌入优化目标我们构造增广拉格朗日函数把一致性约束松弛进目标函数[ L \sum_i C_i(x_i) \sum_{(i,j),t} \left[ \lambda_{ij,t}(P_{ij,t} - S_{ij,t}) \frac{\rho}{2} (P_{ij,t} - S_{ij,t})^2 \right] ]其中 (\lambda_{ij,t}) 是拉格朗日乘子(\rho) 是惩罚系数。ADMM每一步迭代的结构非常清晰P步更新本地变量每个微网给定当前的参考功率 (S^{(k)}) 和乘子 (\lambda^{(k)})独立求解自己的子问题得到本地交互功率 (P_{ij,t}^{(k1)})。S步更新参考变量把相邻微网求出的交互功率取平均或者更严谨地说最小化关于 (S) 的增广拉格朗日项得到新的参考功率 (S_{ij,t}^{(k1)} (P_{ij,t}^{(k1)} - P_{ji,t}^{(k1)}) / 2)。D步更新乘子按 (\lambda_{ij,t}^{(k1)} \lambda_{ij,t}^{(k)} \rho (P_{ij,t}^{(k1)} - S_{ij,t}^{(k1)})) 更新乘子。收敛判据使用原始残差和对偶残差的双阈值判断。原始残差定义为 (|P_{ij} - S_{ij}|)反映的是本地决策和参考变量之间的偏差对偶残差定义为 (\rho |S^{(k1)} - S^{(k)}|)反映的是参考变量的更新幅度。两个残差同时小于设定阈值迭代才终止。第二阶段纳什谈判问题的ADMM结构也类似只是本地变量变成了分摊成本相关的支付变量约束变成了总成本必须等于系统全局最优成本。代码实现时我把两个阶段的ADMM循环封装成了同一个迭代框架只是子问题求解器不同。3.3 谈判权重、惩罚系数与收敛性的关系非对称纳什谈判对ADMM收敛性的影响主要藏在权重 (\omega_i) 上。前面提到目标函数会转成加权对数形式不同微网的 (\omega_i) 相差越大对数项梯度的差异就越明显可能会让子问题的迭代轨迹出现较大偏差。实际代码里如果发现收敛很慢首先检查权重是否归一化其次检查最大和最小权重之间是否相差超过一个数量级如果是建议考虑把权重做平滑处理。惩罚系数 (\rho) 的调节也很关键。我在代码里把 (\rho) 设成可配置参数然后写了一段循环测试脚本从0.01到100按对数间隔取值观察迭代次数和最终目标函数值的差异。实践发现(\rho) 太小原残差容易呈锯齿状振荡表现为相邻两次迭代的交互功率一直在两个值之间跳变(\rho) 太大收敛速度确实变快但迭代后期精度受限因为惩罚项的权重压过了目标函数本身解会出现系统性偏移。比较好的做法是分阶段用不同 (\rho)第一阶段用较大的 (\rho) 快速进入可行域第二阶段把 (\rho) 调小精细收敛到目标函数的最优值。一个非常实用的小技巧把第一阶段的最终交互功率和乘子直接作为第二阶段的初始值可以显著减少第二阶段的迭代次数。我一开始没做这个衔接第二阶段要从零开始迭代白白多了几十轮循环。4. Matlab代码框架从数据表到结果图的全流程实现4.1 代码目录结构与数据准备写Matlab代码的第一步不是直接开一个脚本往里堆代码而是先规划目录结构。我习惯把代码分成core、data、utils三个目录这样后续扩展场景时不用到处找文件。一个典型的目录结构如下NashBargaining/ ├── main.m ├── data/ │ ├── load_profile.xlsx % 各微网负荷曲线 │ ├── pv_wind_data.xlsx % 光伏和风电出力曲线 │ ├── microgrid_config.m % 微网设备参数配置 │ └── system_topology.m % 网络拓扑与联络线参数 ├── core/ │ ├── build_microgrid_model.m % 构建单微网模型 │ ├── solve_stage1.m % 第一阶段共享优化 │ ├── solve_stage2_admm.m % 第二阶段纳什谈判分配 │ ├── update_dual_vars.m % 更新对偶变量 │ └── check_convergence.m % 收敛判据 └── utils/ ├── init_parameters.m % 初始化参数 ├── plot_results.m % 结果可视化 └── output_report.m % 输出结果表格数据准备阶段负荷曲线和光伏风电曲线建议直接用Excel或CSV统一存成时间序列。我用的数据是某省某地典型负荷数据但具体数值不需要太较真你可以用自己手头的数据或者随机生成有代表性的典型日曲线。关键是数据的时间分辨率统一我一般取24个时段每时段1小时。microgrid_config.m里定义了每个微网的设备参数包括机组成本系数、出力上下限、爬坡率、储能容量、充放电效率、SOC上下限、联络线容量等。建议以结构体形式组织mg(i).gen.a、mg(i).gen.b、mg(i).gen.c这种写法读取时清楚不容易混淆。4.2 核心模块的建模实现要点build_microgrid_model.m的核心任务是把每个微网的调度问题转化为标准优化模型。如果用Matlab内置求解器目标函数和约束都需要写成矩阵形式。我建议构建一个结构体model其中包括model.Aeq、model.beq等式约束矩阵功率平衡、SOC递推model.Aineq、model.bineq不等式约束矩阵出力上下限、SOC范围、联络线容量model.c或model.H、model.f目标函数系数线性或二次项model.lb、model.ub变量上下界这里有一个很关键的性能优化点尽量用向量化方式一次性生成约束矩阵不要用for循环一行一行拼 (A_{\text{eq}})。Matlab的矩阵拼接开销很大微网数量多、时段多时效率差距可达数十倍。我习惯的做法是先用稀疏矩阵占位然后用索引批量赋值。一段典型的约束构建代码如下function model build_microgrid_model(mg, T, dt) % 变量顺序: 1..T 机组出力, T1..2T 储能充电, 2T1..3T 储能放电, % 3T1..4T 与邻居交互功率(按联络线顺序排列), ... nVar 4 * T; % 示意实际根据微网设备数量调整 % 初始化稀疏约束矩阵 Aeq sparse([], [], [], T 1, nVar); % T个功率平衡 1个SOC终值约束 % 功率平衡约束机组光伏风电放电受入 负荷充电 for t 1:T Aeq(t, t) 1; % 机组出力 Aeq(t, 2*T t) 1; % 储能放电 % ... 光伏风电在常数项体现交互功率通过邻接矩阵映射 beq(t) mg.load(t) - mg.pv(t) - mg.wind(t); end % ... 其他约束类似 endupdate_dual_vars.m对应ADMM的D步写法很直接lambda lambda rho * (p_ij - s_ij);但要注意乘子数组要和联络线拓扑一一对应建议维护一个link_list矩阵每行记录一条联络线的两个端点微网编号然后所有数组都按这个顺序排列。这样迭代起来不容易乱。关于求解器的选择如果单微网子问题没有整数变量目标函数是二次的变量连续可以直接用quadprog如果模型里含整数变量需要用intlinprog或者ga但计算量会明显上升。我的首次实现刻意把储能充放电状态变量省去用连续变量配合互补约束近似降低求解难度跑通之后再逐步增加模型复杂度。4.3 结果可视化与报告输出代码跑通只是第一步结果能否讲清楚故事更重要。我通常输出三类核心图第一类是微网间共享功率曲线图横轴是时段纵轴是交互功率曲线按联络线分组能直观看到电能共享的方向和大小。第二类是成本对比柱状图把每个微网的独立运行成本、共享后的分摊成本并排画出来差距就是谈判红利。第三类是收益分配堆积图把每个微网的成本节约额按权重比例可视化配合文字说明谈判权重的实际影响。绘图代码放在plot_results.m里统一调用一个plot_shared_power(bus_result, link_list)函数负责把共享功率画成瀑布图或堆叠图plot_cost_comparison(ind_cost, share_cost)画柱状图。此外我建议把所有微网的分摊成本、共享电量、成本节约率输出为一个文本表或Excel表写论文时直接引用数据不用临时回看命令行。5. 调试经验复盘最容易踩的坑和我的验证方法5.1 谈判权重和破裂点设置的三个基准坑先讲我踩得最深的一个坑破裂点 (C_i^0) 不是随便给的常数而是每个微网在完全不参与共享时的独立运行最优成本。有些人为了省事直接把独立运行成本设成一个拍脑袋的数值结果第二阶段求解时目标函数里的对数值很可能出现负值或异常导致整个迭代发散。第二个坑是权重没有归一化。如果第二阶段的权重加起来不等于1目标函数的量级会漂移ADMM的收敛判据就不稳定。我的做法是在init_parameters.m里读入原始权重后立刻做归一化并打印出来核对确保每个权重都是正数且总和为1。第三个坑前面已经提过——第二阶段又把调度变量放开了。记住这个原则第二阶段只分配成本不重新调度。如果发现第二阶段求解结果里的储能出力曲线和第一阶段不一样多半是约束没写对。我建议在第二阶段模型里把交互功率直接设成固定常数而不是优化变量这样从结构上杜绝了这个bug。5.2 收敛判据与惩罚参数调整的心得ADMM最磨人的就是调 (\rho)。我的调试步骤是固定的先用两个微网的极简算例跑通然后观察原残差和对偶残差曲线。如果原残差呈锯齿状说明 (\rho) 太小惩罚力度不足以把本地决策拉向参考变量如果对偶残差下降特别缓慢说明 (\rho) 偏大需要减小。当然也有两者都正常但迭代次数很多的情况这时可以尝试给参考变量更新加一个松弛因子让 (S^{(k1)}) 的更新不那么激进。我还会设置一个最大迭代次数上限比如500次并在每次迭代时记录目标函数值、残差值、分摊成本变化量。如果迭代终止时残差不够小或者收益分配不满足个体理性约束程序会打印告警而不是静默通过。这点对写论文很重要审稿人会质疑你的收敛性问题而你有完整的迭代日志就能直接回应。5.3 用集中式全局最优做金标准验证最后也是最关键的一步验证分布式ADMM的结果是否真的逼近全局最优。做法很简单对小规模算例3到5个微网同时跑集中式优化和分布式ADMM对比两个结果。第一阶段的总成本偏差应该控制在一个很小的范围我自己的经验是1%以内实际通常在0.1%量级。第一阶段一致性约束的残差也需要检查它反映分布式求解是否真正满足了微网之间的功率耦合约束。第二阶段的验证则集中在几个不等式上每个微网的分摊成本是否都低于独立运行成本、所有微网分摊成本之和是否严格等于系统总成本、以及不同权重设置下高权重微网的收益是否不低于低权重微网。这三条满足才说明纳什谈判解是可实施的稳定解。顺便说一句如果你手头的算例一直调不到理想的收敛精度先别急着怀疑算法。把数据缩小到3个微网、6个时段手算一遍独立运行成本和功率平衡很多问题立刻就能定位到是模型构建错误还是迭代逻辑错误。我一直保留着这个小算例作为回归测试用例后面每次改模型都先跑它比任何单元测试都管用。代码全部跑通之后我个人最深的一个体会是非对称纳什谈判框架真正的价值不在于那个加权乘积公式而在于它逼着你把每个微网凭什么参与合作、参与后能得到什么这个问题想清楚。谈判权重怎么设、破裂点怎么算、成本怎么分摊每一步背后都是对经济关系的理解而不是单纯的数学技巧。调试阶段我反复看那张共享功率曲线和成本节约额汇总表某个微网权重从0.3改成0.5后它的分摊成本立刻下降、邻居微网分摊成本小幅上升这种可解释性比任何花哨的算法都更能说服电力公司里的合作方。希望这篇文章能帮你在自己的多微网电能共享场景里把这条路走通。