ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

主从博弈与共享储能微网:从KKT到Gurobi的双层优化实战指南

主从博弈与共享储能微网:从KKT到Gurobi的双层优化实战指南 简介基于主从博弈理论的共享储能与综合能源微网优化运行研究MATLAB代码包面向能源系统领域的研究人员与研究生用于解决微网运营商、共享储能服务商与用户聚合商之间的协同优化问题。代码完整复现参考论文建立三者各自的优化运行模型提出微网运营商与用户聚合商间的Stackelberg博弈框架并验证均衡解存在唯一性。仿真平台为MATLAB YalmipCPLEX采用启发式算法与求解器结合的方式迭代求取均衡策略可有效权衡各方利益并实现收益双赢。压缩包共十二个文件包括十个.m源程序、一个系统关系图PNG和一个数据xlsx整体约二百一十三KB代码结构清晰便于二次开发。目前已有三百零三人学习适合需要参考主从博弈建模与求解细节的研究者直接使用。1. 主从博弈与共享储能园区微网为什么绕不开这个组合同一个园区里三家厂房都想装储能。一家怕一次性投入太大一家算下来一年用不满两百个循环还有一家更直接——管这个的人换了三茬没人有空盯充放电策略。储能本身不难难的是分散到每个用户手里就变成了高成本累赘。共享储能把电池集中到运营商手上用户按次买充放电服务问题随之变成运营商该怎么定价用户该怎么响应。主从博弈理论正是为这个问题准备的建模框架——运营商先出价微网用户看到价格再调整用能计划最后收敛到双方都接受的均衡。这篇笔记按这条路走一遍理论怎么映射到工程、目标函数怎么建、KKT怎么转、Gurobi怎么调以及复现这类项目时最常踩的五个坑。2. 主从博弈理论在微网里的角色谁先出价谁跟着走2.1 决策时序决定了博弈类型为什么是Stackelberg而不是Nash两个参与者的决策时序相同各猜各的那是Nash博弈时序不同一方先亮牌、另一方再行动这是Stackelberg博弈。微网里的共享储能恰好是后者运营商手里攥着储能资产和定价权用户手里只有用能选择权。运营商先公布充电价和放电价用户再决定自己的负荷曲线和储能充放策略这个先后关系天然成立不需要任何人为假设。Stackelberg均衡的基本逻辑也很直白领导者先出价但出价必须建立在「跟随者会最优响应」这件事上。你运营商的充电价定到 2 块钱一度用户干脆不用你的储能自己扛峰谷差你的收益反而是零。所以在求解时不能只优化运营商的收益还必须把用户的理性响应作为约束条件嵌进去。这一步是整个研究的核心也是后面所有数学工作的起源。和集中式联合优化相比主从博弈的工程优势很明显。集中式要求所有用户把负荷数据、设备参数、生产计划全部上报给一个中心再做全局最优且不说商业上用户愿不愿意交出隐私光是数据清洗和通信可靠性就能拖垮一个项目。主从博弈只需要价格信号这一个媒介——运营商发价格用户回应用电量双方之间不需要交换任何私有信息。2.2 三个要素的工程映射参与者、策略集与收益函数任何一个博弈模型都可以拆成三个要素参与者、策略集、收益函数。落到这个标题里参与者就是两层上层是共享储能运营商它可能同时是微网的配电网运营商或虚拟电厂聚合商下层是若干个综合能源微网用户比如一个园区里的不同厂房、商业综合体或居民楼宇。策略集的划分要和实际业务对得上。上层运营商的策略是充放电价——充电价是用户往储能里存电时付给运营商的费用放电价是运营商从储能电池放出的电量里给用户的回购价这两个价格可以分别定也可以按固定价差联动常见做法是各自作为独立的24时段决策变量让优化器自己去找最优曲线。下层用户的策略更丰富可转移负荷的启停时刻、电锅炉的出力、燃气轮机的开停机、从储能的充放电量这些共同构成用户对价格的响应函数。收益函数是所有建模工作的落脚点。运营商挣的是价差向用户售电的收入加上放电回收的电量价值减去从主网购电的成本和储能的运维、损耗、寿命折损。用户省的是用能成本在同样的舒适度约束下通过储能和综合能源设备的协同把自己的电费和气费压下来。注意这里的收益函数必须都写成可计算的货币量博弈论里叫支付函数工程上就是两本账。2.3 理性人假设的边界从完全信息到滚动优化的落地折中学术模型默认参与者都是理性人完全信息总能找到最优响应。但实际项目里用户不会严格按照一个数学最优解去运行尤其是涉及舒适度、生产计划这类非经济因素时偏差往往会超过30%。如果直接拿均衡价格去做实时调度大概率会翻车。我一般会做两个折中。第一把主从博弈当作一个离线定价工具算出一个分时电价曲线作为运营商的报价基准第二在日内运行阶段换成模型预测控制MPC以均衡价格为参考点用滚动优化吸收负荷预测误差。这样既保留了主从博弈的定价公平性又兼顾了工程上的鲁棒性。换句话说博弈模型负责回答「价格定多少合理」MPC负责回答「这个时刻实际怎么调度」——前者是战略层后者是战术层。提示如果你发现论文里的博弈均衡在工程现场跑出来的效果和仿真差很多先检查你的用户是不是真的按价格响应了而不是怀疑求解器有问题。3. 共享储能与综合能源微网建模先把目标函数和约束写对3.1 用能源集线器把电、热、冷、气拼成一张网综合能源微网建模有个通用骨架叫能源集线器它把不同能源载体从输入到输出映射成一张网络。典型配置是光伏和风电发绿电燃气轮机CHP同时发电和产热燃气锅炉补热缺口电制冷机提供冷量蓄电池做电功率的缓冲再加上共享储能作为外部服务接入点。氢能、P2G这类设备很多园区第一版不会上建完基础模型再预留接口即可。电功率平衡是主骨架光伏出力加CHP发电加储能放电功率加主网购电等于用户电负荷加储能充电功率加电制冷机耗电。热功率平衡类似CHP余热加燃气锅炉产热等于热负荷。冷功率平衡由电制冷机的COP乘耗电量得到。这三个平衡方程把设备全串起来了后续的目标函数和约束都以它们为底盘。建立这些平衡方程时变量时标统一是第一步。我习惯把整个优化周期离散成24个时段每时段1小时变量全部带时间下标。这样可以自然对接分时电价、光伏出力预测曲线和用户负荷曲线也为后面写KKT做铺垫。离散时间后所有设备出力都可以表示成连续变量储能充放互斥则用整数变量处理整个问题变成混合整数规划。三个平衡方程的具体形式如下电平衡为 Ppv(t)Pchp(t)Pd(t)Pbuy(t) Pload(t)Pc(t)Pec(t)热平衡为 Hchp(t)Hgb(t) Hload(t)冷平衡为 Cec(t) COPec × Pec(t)。其中 Pd(t)、Pc(t) 是储能的放、充电功率这部分和运营商的定价直接相关。3.2 共享储能的运行约束SOC递推、充放互斥与寿命惩罚共享储能和普通储能在物理约束上没有本质区别区别在商业模式上——电池是运营商的资产用户只有使用权。因此运营商的约束要覆盖整个电池的调度周期而用户侧只需要在电量购买上限内做决策。物理上必须严格的约束有三个SOC递推、充放互斥、功率上下限。SOC递推方程是SOC(t1) SOC(t) (ηc×Pc(t) − Pd(t)/ηd) / E_max其中ηc和ηd是充放电效率E_max是额定容量。这个方程把不同时段的储能状态串成一条链也让储能在低谷充电、高峰放电的套利行为在模型里自然涌现。充放互斥用两个0-1变量实现充电状态和放电状态不能同时为1同时功率乘以状态变量限定上下限。这组约束在上层运营商的优化问题中出现因为它管理的是物理资产。寿命惩罚值得单独提一下。电池每做一次深度充放循环容量都会衰减这是储能运营里最容易被忽略的成本项。常见做法是把放电量折算成退化成本加进运营商的日运行费用里。折算系数可以从电池循环寿命曲线取比如额定循环次数是6000次电池更换成本是每度电对应多少元两者相除得到一个单次循环退化成本。系数宁大勿小因为共享储能的使用频率远高于单个用户的私有储能。3.3 上下层目标函数的工程取舍谁在最大化什么上层的目标函数是运营商的净收益最大化充电服务收入加放电回购收益减去从主网购电的成本、储能运维成本和上面说的退化成本。注意这里有个隐含逻辑充电功率和放电功率不是运营商自己决定的是用户响应价格之后的结果。这就是主从博弈和普通调度优化最大的区别——运营商的决策变量是价格而价格影响的是用户的决策用户的决策反过来决定运营商的收益。下层的目标函数是用户侧的总用能成本最小化用户从主网购电的费用、向运营商买充电服务的费用加上燃气费用减去向运营商放电得到的回购收入。约束包括楼宇自身的功率平衡、可转移负荷的占比上限、用电舒适度温度区间等。这个下层问题必须是凸的或者至少是能通过线性化变成凸的否则后面的KKT转换会失效。两层目标函数联合起来的意义在于运营商不能单方面抬高充电价因为用户会用「少充电、多买电网电、调整负荷曲线」来惩罚你用户也不能无限压低回购价因为运营商可以选择不放电让储能闲置。均衡价格就是这两个约束条件互相拉扯后的交点。3.4 模型参数表一组能跑通算例的默认值参数设置在主从博弈微网模型里非常影响结果给一组我常用的默认值供参考实际算例中按项目数据替换即可参数符号示例值说明储能额定容量E_max1000 kWh园区级共享储能常见量级储能最大功率P_max250 kW充放电共用功率上限SOC上下限SOC_min/max0.1 / 0.9保护电池寿命充放电效率ηc / ηd0.95 / 0.95磷酸铁锂电池典型值主网分时电价峰/平/谷1.2 / 0.8 / 0.4 元/kWh以当地电网代理购电价格为准CHP电热比σ1.2热电联产机组热功率/电功率可转移负荷比例α0.15用户最大可调整负荷占比燃气价格c_gas3.2 元/m³按热值折算进模型这套参数跑出来的典型结果是运营商在低谷时段充电价压到接近谷电价引导用户多充高峰时段放电回购价抬高到接近峰电价引导用户多放用户的用能成本比不用共享储能时低10%到18%。低于8%说明价格信号传导不畅可能需要调整可转移负荷占比或充放电效率。4. 求解实现用KKT把双层问题变单层再交给求解器4.1 双层问题在求解器里的位置为什么不能直接solveGurobi、Cplex这些求解器再强也救不了双层优化——因为它们接受的输入是一个带约束的单层优化问题。主从博弈天然是两层的上层在选价格下层在给定价格下选响应两层各自有各自的目标函数和约束。商业求解器没有「把另一个优化问题塞进约束」的能力所以必须做转换。最常见且可靠的转换路线是KKT条件法。思路是把下层的优化问题用它的KKT条件替代如果下层问题是凸的KKT条件是原问题最优解的充分必要条件那么把KKT条件作为上层问题的约束双层问题就变成单层问题。这个单层问题里包含互补松弛条件也就是「乘子乘约束等于0」这种非光滑等式需要再做线性化才能交求解器。这个转换过程有三个前提条件要自查下层目标函数凸、下层约束是线性的、下层决策变量是连续的。综合能源微网里绝大多数设备约束可以写成线性目标和是线性项加二次项只要没有非凸的双线性项KKT转换基本能保成功。如果用户侧出现了整数变量——比如设备的开停机状态——下层就变成了混合整数规划KKT条件不再成立这时只能另走启发式迭代或者把整数变量直接提到上层。4.2 核心代码框架变量、互补约束与目标函数我用YalmipGurobi这套组合来做单层化求解代码骨架如下去掉算例数据只留结构方便按自己的设备参数往里填%% 主从博弈双层转单层求解框架Yalmip Gurobi % 上层运营商定价下层用户响应的KKT条件嵌入 T 24; % 优化周期24小时 % 上层变量充电服务价、放电回购价 lam_c sdpvar(T,1); % 充电价元/kWh lam_d sdpvar(T,1); % 放电价元/kWh % 储能物理变量 soc sdpvar(T,1); % 荷电状态 Pc sdpvar(T,1); % 充电功率 Pd sdpvar(T,1); % 放电功率 zc binvar(T,1); % 充电状态0-1变量 zd binvar(T,1); % 放电状态0-1变量 % 储能约束组 Constraints_es [soc(1) 0.5 (Pc(1)*0.95 - Pd(1)/0.95)/1000]; for t 1:T-1 Constraints_es [Constraints_es, ... soc(t1) soc(t) (Pc(t)*0.95 - Pd(t)/0.95)/1000]; end Constraints_es [Constraints_es, ... 0.1 soc 0.9, ... zc zd 1, ... 0 Pc 250*zc, ... 0 Pd 250*zd]; % 下层用户问题的KKT互补约束示意用户的购电变量x_user x_user sdpvar(T,1); % 用户从主网购电量 nu_low sdpvar(T,1); % 不等式约束乘子 z_comp binvar(T,1); % 互补条件辅助0-1变量 BigM_user 500; % 根据功率上限推算 % 互补约束 0 nu_low ⊥ (x_max - x_user) 0线性化为 Constraints_kkt [0 nu_low BigM_user*(1-z_comp), ... 0 (500 - x_user) BigM_user*z_comp]; % 上层目标运营商净收益最大化 Objective sum(lam_c .* Pc lam_d .* Pd - ... % 服务收入 0.8 * (Pc Pd) - ... % 运维成本 0.05 * Pd); % 退化成本 % 拼接所有约束并求解 Constraints [Constraints_es, Constraints_kkt, ... lam_c 0, lam_d 0, ... x_user 0]; options sdpsettings(solver,gurobi,verbose,2); optimize(Constraints, -Objective, options); % 提取结果 lam_c_opt value(lam_c); lam_d_opt value(lam_d);这段代码里三个部分要重点看。第一是储能的SOC递推用循环拼约束这是Yalmip里处理时段耦合约束的标准写法注意SOC初值0.5要放在循环外面否则会重复约束。第二是互补约束的线性化原条件是两个非负表达式相乘等于0这里引入0-1变量z_comp把乘积条件拆成两条线性不等式这是求解是否稳定的关键。第三是目标函数加了负号并调用optimizeYalmip默认最小化所以最大化要取负。BigM_user的取值需要在运行前手动估算它必须大于x_user可能出现的最大值。这里的500对应的是用户购电功率上限500kW实际项目里从约束定义直接取上界就行。M值取太小会切掉可行域里的最优解取太大则让线性松弛变得非常松求解时间翻几倍。这个度的把握是后面第5章要展开讲的坑。4.3 代码背后的求解链从KKT到MIQP再到Gurobi上面那段代码的底层求解链是这样的Yalmip把变量、约束和目标函数组装成内存里的模型对象因为引入了0-1变量模型变成混合整数二次规划MIQP或混合整数线性规划MILP。Gurobi拿到这个模型后用分支定界加内点法求解对偶间隙降到默认阈值以下就返回最优解。整个过程里KKT条件的正确性决定了模型对不对Big-M线性化决定了模型好不好解0-1变量数量决定了难不难解。变量越少越好这也是为什么很多实现里把下层用户的设备变量压缩成几个聚合变量而不是把每台设备都拆开。压缩的代价是响应函数的精度下降但换来的是求解时间从小时级降到分钟级工程上值得。需要特别说明的是这类模型的求解时间高度依赖互补约束的数量。每个互补约束要引入一个0-1变量和一组Big-M不等式24时段乘以下层用户数量变量规模很容易冲到几千个。如果遇到求解时间无法接受优先做两件事把下层用户按负荷特性聚类成3到5类而不是逐楼宇建模把互补约束里的Big-M改成分段常数不同时段用不同的M值。4.4 三个必调参数分时电价范围、SOC初值与充放电效率分时电价范围直接影响均衡价格的边界。运营商的充电价不可能高于用户直接从主网购电的谷段电价否则用户没有动机充电放电回购价也不可能低于用户向主网购电的峰段电价否则用户没有动机放电。在设置lam_c和lam_d的上下界时直接拿主网购电价格的峰谷值作为硬边界能在建模阶段就排除掉一大片无意义的解。SOC初值对24小时优化结果的影响常被低估。默认0.5看似合理但如果储能前一天收盘时SOC是0.8第二天初值还用0.5会低估可放电量导致高峰时段放电不足。工程做法是先不管初值跑一遍用求解结果的最后一个时段SOC作为第二天的初值迭代两三轮后初值就接近真实运营状态了。充放电效率的参数标定不要迷信铭牌值。电池在实际运行中还有线路损耗、温控功耗、PCS储能变流器损耗综合效率通常比铭牌低3到5个百分点。用铭牌效率算出来的套利收益会偏乐观落地时运营商按这个收益给用户承诺优惠最后兑现不了很容易引发商务纠纷。5. 避坑主从博弈微网优化里最常翻车的五个地方5.1 拉格朗日乘子符号写反求解器直接报Infeasible现象按KKT条件转换后Gurobi跑出来的结果是Infeasible或者解出来的价格全跑到边界上充电价等于谷电价、放电价等于峰电价模型完全退化成无博弈的调度问题。原因KKT条件里stationarity条件有严格的正负号约定。下层问题是「最小化成本」时不等式约束的乘子取非负等式约束乘子自由stationarity表达式里各项的正负号一旦写反最优解就跑到了原始问题的鞍点上甚至整个可行域被翻转。解决把stationarity条件按「目标函数梯度 不等式约束梯度转置×乘子 等式约束梯度转置×乘子 0」这个标准形式套进去写完后先做一个小算例验证用一个已知解析解的两阶段问题对照检查。我每次搭新模型都会先跑一个只含一台设备、2个时段的极简版能通过再放大到24时段。5.2 Big-M取值玄学填大了拖速度填小了切解现象同样的模型Big-M取100能解出合理结果取1000后求解时间从30秒涨到15分钟取10则解出来的价格曲线在高峰时段莫名其妙地偏低怎么调约束都没用。原因Big-M填太大线性松弛的范围过宽分支定界每层节点都要处理很多冗余搜索求解效率急剧下降填太小把互补约束的可行域切掉了一块最优解落在被切掉的那部分求解器只能在次优区域里打转。解决给每个互补约束单独算Big-M上界从物理约束里反推。例如放电功率的互补约束Big-M取P_max×ηd就行不需要全局统一取一个大常数。分时不同、时段不同可以分别定义M_t牺牲一点建模工作量换来求解提速一到两个数量级非常划算。5.3 SOC在低谷时段「假死」储能一动不动现象优化结果里低谷时段SOC曲线是一条水平直线储能既没充电也没放电看起来像储能坏了。但约束条件里明明有充电空间。原因目标函数里没有激励储能在低谷充电的项。充电价如果被约束成高于谷电价用户充电反而亏钱于是下层最优响应就是不充不放。但这里SOC又必须满足递推约束求解器就把SOC定在一个不违反上下限的自由值上形成了一个数学上合理、物理上荒诞的「假死」状态。解决给目标函数加一个最小的充电电量约束或者设定低谷时段充电价必须低于谷电价一个固定差价强制储能参与套利。更优雅的做法是在SOC递推方程里引入充电目标项——让SOC在谷时段结束时尽量靠拢到一个预设值比如0.8用一个小权重系数实现。注意出现SOC假死时先查目标函数里储能相关项的系数再查价格边界最后才考虑约束错误。前两个原因占九成概率。5.4 KKT条件只是必要条件下层非凸时单层化会骗你现象KKT单层化后的最优目标值和原双层模型通过迭代法求出的最优值差距超过20%而且差距在不同算例中不固定。原因KKT条件是凸优化问题的充要条件但如果下层问题里有非凸项——比如用户的燃气轮机效率随出力非线性变化、储能充放同时存在导致的双线性项——KKT条件就只是必要条件转换后的单层模型解出来可能是局部最优点、鞍点甚至和原问题的最优解不搭边。解决在做KKT转换前先做一次凸性自检把所有变量的二次项列出来检查它们的Hessian矩阵是否半正定。发现非凸项有三个处理手段把非凸项线性化分段线性逼近把非凸决策变量提升到上层干脆放弃KKT改用交替迭代法——上层固定价格求下层下层固定响应求上层循环到价格不再变化为止。交替迭代虽然不保证收敛到全局最优但至少每一步的物理意义都是清晰的。5.5 初值随便给KKT的互补约束可能把最优解排除在外现象同一个模型换一组SOC初值求解结果完全不同而且两组结果的上层收益差距明显。第一次跑出来的结果看起来更好但换初值后就再也复现不出来了。原因KKT单层化后的模型是非凸的——互补约束线性化引入0-1变量后混合整数模型本身仍然存在非凸性Gurobi找到的是全局最优但这两组初值跑出的结果差异说明求解器在数值稳定的边界上挣扎初值决定了对偶间隙的收敛路径。解决给SOC初值、价格初值和乘子初值都设置合理默认值具体做法是先跑一个去掉互补约束的宽松版本用它的解作为完整模型的初值。这个技巧在工程里叫「启动模型」效果非常明显能把求解时间压缩一半以上也能显著减少随机抖动。6. 算例验证怎么证明你求出来的是均衡而不是巧合6.1 三组对照实验下的收敛判据验证主从博弈模型是不是真的收敛到了均衡我习惯做三组对照第一组是单独的共享储能调度不做博弈运营商按固定价差运营第二组是集中式联合优化假设所有用户信息完全公开第三组是主从博弈均衡解。对比这三个方案的总成本、储能利用率、峰谷差削减率这三个指标能直接看出博弈模型的价值落在哪里。收敛判据要定量不能只看图。我会记录上层目标函数值在每次迭代或每次求解后的变化量当连续三轮变化小于1e-3时判定收敛。同时记录价格信号的波动幅度如果充电价或放电价在相邻迭代之间反复跳变说明模型里存在多个相近的局部均衡需要引入价格平滑约束限制相邻时段价格的变化速率。6.2 验证均衡的「扰动法」偏离均衡价是否让上层利润下降验证是否处于Stackelberg均衡有一个实用的扰动法取出求解的均衡电价把充电价和放电价分别向上向下偏移5%到10%重新求解下层用户的响应看运营商的利润是否都下降。如果两边扰动都让利润下降说明当前价格确实是运营商的最优定价如果某个方向扰动后利润反而上涨说明求解器没有找到真正的均衡需要重新检查模型。灵敏度分析也不能跳过。把分时电价整体平移±20%、把储能容量扩大或缩小30%、调整可转移负荷占比观察均衡价格和用户响应是否按合理方向变化。储能容量增大后充电价应该下降可转移负荷占比提高后峰谷差削减率应该上升。这些方向的合理性判断比数值本身更说明问题。做这一类研究这几年我最大的教训是拿到任何一个双层优化问题第一件事永远不是写代码而是先画一张决策时序图——谁先动、谁后动、每个角色的信息边界在哪里、价格信号在哪个环节传递。这个习惯救了我很多次希望帮到你。本文还有配套的精品资源点击获取
返回列表