ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

产消者能量共享的非合作博弈与ADMM分布式优化实战

产消者能量共享的非合作博弈与ADMM分布式优化实战 1. 场景解析为什么多产消者能量共享值得做做这个项目之前我第一反应是现在的电力系统研究早就不是单纯“发电厂—电网—用户”这种单向流动的模型了。分布式光伏、小型风机、储能、电动汽车这些资源在用户侧铺开之后每家每户不再只是“用电的”他们家里可能装着屋顶光伏白天发电多得用不完晚上又得从电网买电。这种既是生产者又是消费者的角色学界给了一个专门的名字——产消者prosumer。产消者出现以后一个很自然的问题就来了你家白天光伏大发用不完我家晚上电动车要充电如果你们两家能直接互相交易是不是比各自跟电网买卖更划算答案是肯定的但这背后藏着一堆麻烦事。最核心的麻烦是每个产消者都是独立的主体都有自己的利益诉求谁也不想吃亏该怎么让他们自愿参与这种本地能量共享本项目做的就是这件事用分布式优化的方法求解一个多产消者之间通过非合作博弈达成的能量共享方案。每个产消者独立决定自己的充放电策略、购售电策略大家共同决定一个共享电价最终让各自成本都下降而不是靠一个中心化的调度机构强制分配。这个思路在当下的配电网研究中非常主流尤其适合高比例分布式光伏接入的社区、园区、微电网场景。如果你正在做电力系统方向的毕业设计、论文仿真或者想搞清博弈论怎么跟优化问题结合这个项目非常值得拿来练手。它不只是一个数学模型还配套了全套matlab代码从博弈建模、分布式求解、迭代收敛判断到结果绘图一套流程走的完整。适合有三方面基础的人懂一点电力系统潮流或经济调度懂一点最优化理论哪怕只是会用fmincon想学分布式算法的具体落地。2. 模型设计从问题描述到优化目标2.1 产消者内部运行模型怎么搭建模的第一步是把每个产消者看成一个包含本地负荷、分布式电源、储能系统的节点。为方便统一表达把每个产消者记为编号i总共有N个产消者参与共享。每个产消者在每个调度时段t内需要满足自己的功率平衡约束P_load(i,t) P_pv(i,t) P_bat_discharge(i,t) − P_bat_charge(i,t) P_grid_buy(i,t) − P_grid_sell(i,t) P_shared(i,t)这个式子看着长意思不复杂负荷需求由分布式光伏、储能放电、向电网购电、从其他产消者接收共享功率来共同满足反过来如果光伏出力多了可以给储能充电、卖给电网或者共享给别的产消者。P_shared(i,t)就是产消者i在时段t与其他产消者的净交换功率正值表示接收共享负值表示输出共享。这个变量是整个博弈模型的耦合核心后面分布式求解的时候处理的就是它。储能系统需要额外考虑荷电状态SOC的动态约束SOC(i,t1) SOC(i,t) (η_charge · P_bat_charge(i,t) − P_bat_discharge(i,t) / η_discharge) · Δt / E_bat(i)实际代码里还加了SOC上下限、充放电功率限制这些不等式约束。这里要特别提醒一点储能充放电功率变量如果同时出现会导致模型病态因为同一个储能既充电又放电数学上不会“亏”但物理上毫无意义。常规做法是引入二进制变量进行互斥约束但这样模型就成了混合整数规划求解复杂度上一个台阶。我给产消者做的是连续优化这时候最简单的处理是给充放电功率各设一个小的正收益系数让优化目标自动避免同时充放电这种无意义结果或者直接按分段方式建模。具体用哪种取决于你手头求解器的能力。2.2 每个产消者的成本函数和目标函数每个产消者运行的目标很简单一天下来总费用最小。总费用由三部分组成C_i Σ_t [ (λ_buy(t) · P_grid_buy(i,t) − λ_sell(t) · P_grid_sell(i,t)) · Δt c_bat_degradation(i,t) ]其中λ_buy是分时购电价λ_sell是上网电价通常低于购电价c_bat_degradation是储能循环损耗折算成本。这个损耗项非常关键如果没有它优化结果会倾向于频繁浅充浅放实际储能寿命损失很大但在模型里却看不到代价。不少做能量管理的论文都不加这一项仿真曲线倒是漂亮但工程上一算经济账就是亏的。我用的是线性化的循环老化成本模型按吞吐电量折算虽然简单但够用于经济性比较。产消者之间共享功率的结算则通过共享电价λ_shared(t)进行。共享电价不是固定的而是由所有产消者的共享需求量共同决定——这正是非合作博弈的耦合机制。2.3 为什么需要非合作博弈框架这里必须把博弈论在问题中的作用说透。多个产消者共享能量时表面看是一个合作共赢的事但每个产消者都是理性的、自私的不会因为“整体利益最大”就牺牲自己的利益。如果简单搞一个集中式优化强行让总成本最小很可能出现的情况是某个产消者被迫多买电另一个被迫多卖电虽然系统总账好看了但某一家觉得自己吃亏了就不愿意参与。那怎么让大家自愿参与呢答案是设计一个激励机制。非合作博弈的思路是给定其他产消者的共享策略每个产消者都选择对自己最有利的策略当所有人都无法通过单方面改变策略来降低自身成本时博弈达到纳什均衡。在这个均衡点每个产消者都是“自愿”参与共享的——因为任何偏离都会让自己的成本上升。这个逻辑跟现实中的讨价还价很像小区里几户人家想共享光伏电量谁都不想吃亏那就先各自报一个价格和电量谈不拢就调整直到大家都没动力再改为止。数学上找到这个均衡点就得到了一个稳定且可执行的共享方案。2.4 共享电价的设计与迭代机制共享电价λ_shared(t)的确定是这个项目最巧妙的环节也是代码调试时最容易卡住的地方。常用的做法是采用“供需比”或“违规惩罚”机制每个产消者提交自己的期望共享功率汇总所有产消者的共享需求如果总需求大于总供应则提高共享电价反之降低共享电价。这个更新规则可以写成λ_shared^(k1)(t) λ_shared^(k)(t) α · (Σ P_shared_demand − Σ P_shared_supply)α是步长因子取值很关键。取得太大迭代振荡发撒取得太小收敛可能要几百上千次仿真时间难以接受。这个参数没有固定最优值跟系统的规模、负荷曲线、光伏出力数据都有关系我实际调试时一般从0.005起步观察前50次迭代的残差曲线再手动微调。代码里我预留了参数接口方便做敏感性分析。3. 分布式求解交替方向乘子法的具体落地3.1 从集中式到分布式的必然性如果把整个系统的所有产消者看成一个整体统一建模求解问题就退化为一个标准的混合整数线性规划MILP或二次规划QP。对几十个产消者、24个时段的小规模案例集中式优化完全够用Yalmip加cplex几秒钟就能搞定。那为什么还要费劲做分布式优化答案在于实际场景的三个痛点一是隐私保护每个产消者不愿意把自己完整的负荷数据、储能配置、光伏出力曲线交给一个中央调度方——这相当于把家里的用电习惯全暴露了二是计算负担产消者数量如果扩展到几百甚至上千个集中式模型规模急剧膨胀求解时间不可控三是决策自主性产消者希望保留对自己的储能、负荷的完全控制权而不是被动接受一个全局控制指令。分布式优化正好解决这三点每个产消者只跟邻居或协调者交换极少的边界信息比如共享功率和共享电价自己的内部模型和参数完全保留在本地。本项目采用的就是交替方向乘子法ADMM它是目前分布式优化里最成熟的方法之一处理带耦合约束的凸优化问题收敛性有理论保证而且代码实现相对简单。3.2 全局耦合约束的引入用ADMM求解多产消者共享问题核心是构造全局耦合约束。每个产消者i都有一个局部变量P_shared(i,t)表示它与其他产消者的净交换功率。物理上所有产消者的净交换功率之和必须为零Σ_i P_shared(i,t) 0对所有t这个约束的含义是某个产消者输出的共享功率必须恰好等于其他产消者接收的共享功率系统内部共享功率守恒不产生也不消失。这正是耦合所在——每个产消者的决策受制于其他人的整体决策。ADMM的处理方式非常优雅引入一个全局变量z(t)将约束拆成两个等价约束P_shared(i,t) − z_i(t) 0 且 Σ z_i(t) 0。这样每个产消者只需要跟全局变量z进行协调而不需要直接跟每个其他产消者通信。在只含有共享功率平衡约束的场景下迭代更新还有更简单的形式——共享电价λ_shared直接充当对偶变量每次迭代根据全局功率失衡量进行梯度上升这正是我之前提到过的供需比定价机制。从数学上看两者本质上是一回事。3.3 ADMM的迭代流程整个分布式优化循环分为三步直到收敛第一步每个产消者并行求解自己的子问题。给定上一轮迭代的共享电价和共享参考值每个产消者求解一个本地优化问题得到新一轮的最优共享功率。这个子问题规模很小本地24时段调度问题matlab用linprog或quadprog解决速度快到毫秒级。第二步协调者汇总所有产消者的共享功率计算全局失衡。如果共享需求不等于共享供应说明当前电价没有达到均衡需要更新共享电价继续迭代。第三步判断收敛条件。用原始残差和对偶残差的双重判定公式如下r_prim ||Σ P_shared(i,t)||₂r_dual ||ρ · (P_shared^(k1) − P_shared^(k))||₂两个残差同时小于容忍度比如1e-4才判定收敛。只看原始残差收敛还不够实际算例中经常出现原始残差很小但对偶残差还很大的情况只用一个判据容易提前终止拿到的根本还没到均衡解。3.4 分布式优化中三个需要处理的问题ADMM核心步骤不算复杂但让它真正稳定运行还需要处理三个问题第一个是步长参数ρ的自适应调整。固定ρ能跑通简单场景但一旦产消者数量增多或负荷波动剧烈固定步长要么收敛慢要么直接振荡。我在代码里加了残差平衡策略如果原始残差大于对偶残差的一定倍数比如10倍就把ρ乘以1.4反过来如果对偶残差更大就把ρ除以1.4。这个策略源于Boyd在2011年发表的ADMM综述论文文献上叫“residual balancing”。工程效果非常显著迭代次数往往能下降一半以上调试时特别值得尝试。第二个是目标函数的凸性。ADMM的理论收敛性依赖子问题的凸性。如果每个产消者的成本函数包含非凸项比如某些离散决策、不可导的阶梯电价ADMM的收敛性不再有严格的理论保证可能陷入振荡。如果做的是阶梯电价场景我建议用交替方向乘子法的变体或者先用一个光滑函数近似阶梯电价等收敛后再做离散决策的整数归整实测效果会更稳定。第三个是数值尺度。电力系统问题中功率动辄几百千瓦电价不过零点几元/千瓦时两者数值差距巨大。如果不做归一化处理ADMM更新方程中的梯度大小会被功率量纲主导导致电价更新形同虚设。我在代码里把功率统一折算到标幺值以100kW为基准所有目标函数系数统一折算到“千元”单位这样各数值量级相近迭代稳定性提升明显。4. 代码实现与调试matlab从零搭起4.1 代码整体结构与数据流这个项目的matlab代码整体上分为四个模块第一是数据源模块包含产消者数量、时段数、分时电价、各产消者的光伏出力曲线、负荷曲线、储能参数。我建议把这些数据单独存放在一个结构体数组中每个产消者对应一个结构体元素字段规规矩矩地写清楚。这样做的好处是后面写子问题函数时传参方便可读性也强。第二是ADMM协调模块负责迭代主循环、共享电价更新、残差计算和收敛判断。这个模块不涉及具体的产消者内部模型只跟共享功率和共享电价打交道。第三是产消者本地优化模块每个产消者调用一次这个函数输入本地数据加共享电价输出本地最优共享功率和成本。第四是结果展示模块把迭代收敛过程、共享功率结果、各产消者成本对比画成图。整个数据流是主循环先给共享电价和共享功率赋初值然后对每个产消者调用本地优化汇总共享功率后更新电价检查残差循环直到收敛。逻辑非常清晰最开始调试时用单步调试跟一遍迭代过程就全明白了。4.2 产消者本地子问题的matlab实现本地子问题是整个程序的核心我用matlab的linprog或quadprog求解。每个时段产消者的决策变量包括储能充放电功率、向电网的购售电功率、共享功率。目标函数是购电成本减售电收益加储能损耗成本。% 决策变量定义以24时段为例变量分段排列 % x [P_bat_ch(24); P_bat_dis(24); P_grid_buy(24); P_grid_sell(24); P_shared(24)] % 目标函数系数购电为正售电为负储能为损耗成本 f [c_bat_ch_coeff * ones(24,1); ... c_bat_dis_coeff * ones(24,1); ... lambda_buy * dt; ... -lambda_sell * dt; ... zeros(24,1)]; % 注意共享功率这一项不直接进目标函数而是通过共享电价在对偶更新中体现这里有个细节需要特别注意产消者本地的共享功率之所以不直接放进目标函数是因为共享功率的结算价格是λ_shared而λ_shared在本地求解时是已知参数。如果直接把它乘进目标函数那么在产消者视角下共享功率相当于一个固定价格的购售电项处理方式是P_shared为正值接收共享时其成本系数为λ_sharedP_shared为负值输出共享时其收益系数为λ_shared。这样写进目标函数实现上更直接也跟ADMM的理论推导保持一致。4.3 约束条件的matlab矩阵组装线性规划的关键工作是构造Aeq、beq、A、b矩阵。功率平衡约束是等式约束每个时段一条所有发电项之和等于负荷。储能SOC约束是动态的可以通过逐时段递推转化为不等式约束或者直接把SOC作为辅助变量加入决策变量。我推荐把SOC作为辅助变量加进去因为这样可以在约束中直接写SOC上下限代码逻辑更清晰。% SOC辅助变量的状态转移约束示意 % SOC(t1) SOC(t) eta_ch * P_bat_ch(t) * dt / E_bat - P_bat_dis(t) * dt / (eta_dis * E_bat) % 写成矩阵形式SOC(t1) - SOC(t) - (eta_ch*dt/E_bat)*P_bat_ch(t) (dt/(eta_dis*E_bat))*P_bat_dis(t) 0功率上下限约束、购售电功率上限约束都是标准的不等式约束组装方式不复杂但要注意矩阵维度对应正确。我踩过的坑是24时段、5类变量决策变量总共120维如果矩阵行数写错matlab会报维度不匹配排查起来非常浪费时间。建议从少时段比如4个时段先调试通过再扩展到24时段能省大量时间。4.4 迭代主循环代码示意% ADMM主循环简化版 for k 1:max_iter % 步骤1每个产消者并行求解本地优化 for i 1:N x_opt{i} solve_prosumer(prosumer_data{i}, lambda_shared, rho); P_shared_old{i} P_shared{i}; P_shared{i} x_opt{i}.P_shared; end % 步骤2更新共享电价对偶变量 imbalance sum([P_shared{:}], 2); % 每个时段的净失衡向量 lambda_shared lambda_shared rho * imbalance; % 步骤3自适应调整rho r_prim norm(imbalance, 2); r_dual rho * norm(cell2mat(P_shared) - cell2mat(P_shared_old), 2); if r_prim 10 * r_dual rho rho * 1.4; elseif r_dual 10 * r_prim rho rho / 1.4; end % 步骤4收敛判断 if r_prim tol r_dual tol break; end end这个循环结构非常标准。实际跑算例时设置max_iter等于500容忍度取1e-4大多数场景下50到200次迭代就能收敛。如果超过这个范围还没收敛优先检查数据归一化和rho初值多数问题出在这两处。4.5 求解器选择与性能优化本地子问题是线性规划问题matlab自带的linprog完全够用。但要注意matlab的linprog在不同版本上的表现差异不小新版本R2021之后默认算法求解速度明显快于旧版本。如果产消者数量多比如50个以上可以把“每个产消者求解本地问题”从for循环改成parfor并行循环matlab并行工具箱启动后计算时间能从线性增长变成近恒定效果非常明显。我不建议在这个项目里使用fmincon来求解本地问题因为问题是线性的用非线性求解器不仅慢还可能引入数值问题。如果后续要扩展成非线性成本函数比如燃气轮机的二次成本函数再切换到quadprog或fmincon不迟。5. 仿真结果分析与案例验证5.1 用什么数据来做仿真为了让复现实验的人有据可依我准备了典型数据3个产消者24个时段步长1小时。产消者1配置较大容量的屋顶光伏30kW和储能50kWh产消者2配置中等光伏20kW和较小储能20kWh产消者3没有光伏只有少量储能10kWh负荷曲线各不相同。分时电价采用常见的峰谷电价峰时10:00-15:0018:00-21:001.2元/kWh平时7:00-10:0015:00-18:000.8元/kWh谷时21:00-次日7:000.4元/kWh。上网电价设为0.4元/kWh全网统一。这个数据设置虽然简单但涵盖了不同类型的产消者有净输出能力的、有平衡型的、有净需求的恰好能体现共享博弈的价值。5.2 三种场景的对比仿真做了三个场景的对比场景A无共享每个产消者各自独立优化只跟电网交易。这是基准场景。场景B集中式优化所有产消者作为一个整体进行统一调度目标是整个系统总成本最小。场景C分布式优化加非合作博弈即本项目的方法每个产消者独立优化通过共享电价迭代达成纳什均衡。结果十分清楚相比场景A场景B的系统总成本最低但存在内部利益分配不均的问题——某个产消者成本下降明显另一个产消者反而成本上升。场景C的总成本介于A和B之间但关键优势是每个产消者的成本都下降了没有输家。这正是非合作博弈相比集中式优化在分布式场景下的价值所在。从共享功率结果看光伏高峰时段12:00-14:00产消者1和2向产消者3共享功率共享电价介于上网电价和购电价之间。产消者1和2通过共享获得比上网卖电更高的收益产消者3通过共享获得比从电网买电更低的成本各方都有改善。5.3 收敛性与参数敏感性结果典型运行结果大约70次迭代收敛原始残差降到1e-4以下共享电价逐步趋于稳定。收敛曲线呈典型的“快速下降、缓慢逼近”形态——前20次迭代残差下降很快后面是精细调节。共享电价的迭代轨迹也很有意思从初值0.6元/kWh出发经过几次振荡后稳定在0.73元/kWh左右刚好落在谷时购电价和峰时购电价之间符合理论预期。rho参数敏感性测试结果显示rho取0.01时收敛最慢约200次取0.1时收敛最快约60次取1.0时前30次迭代振荡明显最终仍需约100次收敛。这说明rho确实需要一个温和的中间值不是越大越好。6. 常见问题与排查思路6.1 本地子问题无解怎么办这是调试中最常见的问题。产消者的负荷曲线在某个时段特别高而光伏、储能、电网购电上限都到达边界仍满足不了负荷导致可行域为空linprog直接报错。排查思路分三步先看功率平衡等式约束是不是写错了比如符号方向反了再看电网购电功率上限是否设置过小跟负荷峰值对比一下峰值负荷肯定不能超过购电上限加储能最大放电加光伏最大出力最后看SOC初值和终值约束是否合理如果约束SOC终值不低于初值而储能容量太小可能无法满足跨时段的电量转移。6.2 迭代振荡不收敛特征共享电价在某个区间来回跳残差曲线呈锯齿状降不下去。我遇到最多的是两种情况一是rho取值过大共享电价更新幅度过大每次都在均衡点两侧越过形成振荡二是本地目标函数中存在数值不光滑的地方比如阶梯电价或分段成本函数导致产消者策略在边界附近来回切换。处理办法是降低rho到原来的十分之一再试或者启用rho自适应调整策略。如果振荡依旧检查目标函数系数是否有量级差太多的问题适当做归一化。还有一种隐蔽原因多个产消者的共享功率初始值设置为零但如果产消者数量对称比如两个产消者参数完全一样迭代可能出现“对称破缺”现象来回跳动。解决办法是给初值加一个很小的随机扰动破坏对称性。6.3 收敛过慢特征残差一直在缓慢下降但迟迟达不到容忍度。常见原因是容忍度设置过严比如设1e-6对几十上百维的问题来说没必要。另外一个原因是共享电价的更新步长太小每一步调整幅度不够。解决建议把容忍度放宽到1e-3到1e-4这在工程上精度足够同时确认rho取值不过小如果rho已经很大但残差下降仍慢考虑是否约束建模过于保守导致可行域收窄。6.4 结果合理性的判据仿真做完怎么判断结果合不合理我通常看三个指标一是共享功率守恒所有产消者共享功率逐时段求和应该接近零误差在容忍度内二是每个产消者的成本相比无共享场景都下降如果出现某个产消者成本上升说明博弈尚未达到均衡检查迭代是否真的收敛三是储能SOC曲线在合理范围内充放电循环次数不过于频繁如果SOC曲线频繁满充满放说明储能损耗成本系数设定过小。7. 几个值得继续深挖的扩展方向这个项目虽然已经形成了一个完整的分布式优化加非合作博弈闭环但实际电力系统场景往往更复杂有几个方向值得继续挖。第一是多时段耦合下的储能策略升级。当前模型里储能只参与日内的能量搬移如果加入跨日调度SOC的初值和终值约束就要重新设计可以做多日滚动优化更加贴近实际运行。第二是网络约束的引入。当前的共享功率平衡只做了总量守恒没有考虑配电网的网架结构和线路容量约束。如果产消者地理位置分散共享功率需要通过配电网传输那么必须加入潮流方程和线路容量限制。此时问题变成带网络约束的分布式优化ADMM的处理方式会有所调整需要把共享功率与网络节点注入功率耦合起来。第三是纳什均衡唯一性分析加随机场景。非合作博弈可能存在多个均衡点不同均衡点对应的系统效率差异可能很大。如果引入光伏出力的随机性均衡点解集进一步复杂化。可以做蒙特卡洛仿真观察不同随机场景下共享电价和总成本的分布考察均衡的稳定性。第四是博弈模型的升级。当前模型是纯非合作博弈产消者之间没有合作机制。实际上可以通过Shapley值或核仁法设计合作博弈的收益分配机制让产消者在“先合作、后分配”的框架下实现全局最优。这个方向也是目前学术界研究的热点。根据我个人的经验这几个扩展方向中加入配电网网络约束的实际工程意义最大因为当前的“完全无约束共享”假设在低压配电网中很难成立。但要提醒的是加入网络约束后问题性质会发生变化ADMM的实现难度和调试成本都会显著增加。建议先在这个简化案例上完全跑通、吃透代码的每个细节再做网络化扩展。最后分享一个我在调试博弈迭代时的独门技巧把每次迭代的共享电价和共享功率都存下来画在一张图上。看到收敛曲线就能立刻判断出当前是步长太大还是太小、是振荡还是发散。这个习惯帮我省了大量纯靠读日志排查的时间。建议你也试试跑通一次完整的从建模到代码实现再到结果分析的流程比看十篇论文都有用。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进