尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

主从博弈下共享储能与综合能源微网优化运行:建模、求解与MATLAB实现

主从博弈下共享储能与综合能源微网优化运行:建模、求解与MATLAB实现 做综合能源微网优化的朋友多少都遇到过这样一个问题系统里不只有一家说了算储能运营商、微网运营商、电网公司各有各的利益这时候集中式优化就会显得“不讲武德”。我在做共享储能与多微网协同运行的研究时主从博弈Stackelberg game给了我一个很顺手的建模框架——上层共享储能运营商定价下层微网根据价格安排自己的购售电和机组出力两类主体按主从关系分层决策。这篇文章就基于MATLAB代码实现把主从博弈的共享储能与综合能源微网优化运行研究的建模思路、求解方法、代码结构和踩坑经验完整拆一遍。适合正在做综合能源、微网优化、共享储能方向的研究生和工程师参考尤其适合那些已经掌握基本优化建模、但刚开始接触博弈模型的同学。1. 研究问题与整体思路拆解1.1 共享储能与综合能源微网到底在解决什么问题综合能源微网可以理解为一个小型的多能互补系统包含电、气、热等多种能源形式内部有光伏、风电、燃气轮机、锅炉、储能等设备自己发电不够就从电网和气网购能。这类系统研究热度高但落地时有一个很实际的痛点——单个微网自己装储能成本高、利用率低尤其是负荷波动大的小型微网储能设备大部分时间都在闲置投资回收周期拉得特别长。共享储能把储能设施从单个微网中独立出来由一个运营商统一建设、统一调度多个微网按需求购买充放电服务或租赁容量。这样做的好处有两个层面一是摊薄了储能的固定投资成本运营商可以把10MWh的储能容量同时服务五个微网容量利用率远高于各建各的二是实现了负荷特性的互补调度。我做算例时发现工业微网白天负荷高、傍晚下降而居民微网恰好相反这种异质负荷搭配共享储能峰谷互补的收益空间非常明显比各建各的储能总成本能省下15%到20%。但商业模式成立有一个核心问题储能运营商不可能白干活它要在充电价和放电压之间赚取价差同时希望微网尽量多使用自己的服务而微网侧则希望购电价尽量低、卖电价尽量高。双方目标冲突单纯用集中优化没法体现这种交易关系——集中式优化默认所有主体听一个总调度中心指挥但现实里储能运营商和微网是独立利益方微网不愿意把自己的真实成本和负荷数据完全上报运营商设计价格时也不会考虑每个微网内部设备细节。所以这类问题天然适合用博弈论来建模特别是主从结构运营商先报价微网再响应双方在分层决策框架下达到一个稳定的均衡状态。1.2 为什么选主从博弈而不是集中优化我刚开始做这个课题时第一版模型用的就是传统的集中式优化把储能运营商和各微网当成一个统一主体目标函数是整个系统的总成本最小约束是全网功率平衡、机组出力上下限、储能SOC。这种模型的好处是数学简单一个线性规划或混合整数线性规划就解决了在“统一决策中心掌握所有信息”的假设下全局最优解确实漂亮。但放到实际市场环境下这个解没法落地。核心原因是激励不一致集中式优化给微网A分配了一个“多用储能购电”的计划可微网A算了一笔账发现自己燃气轮机发电比购电更便宜它就不会执行这个计划。博弈论框架的逻辑则是承认层级与利益差异领导者储能运营商先做决策公布充放电价和容量租赁费跟随者各微网在给定价格下优化自己的运行计划再把购电量反馈给领导者。领导者根据微网响应调整价格循环往复直到达到Stackelberg均衡。我把两种建模方式的本质区别捋一下。集中式优化的目标函数只有一个约束也只有一个集合求解出来的结果是一个“社会最优”方案主从博弈的目标函数分层约束分层求解出来的结果是一个“个体最优且相互一致”的均衡方案。这个方案不一定是系统总成本最小的但它满足每个主体最大化自身利益的行为逻辑所以更容易被执行。对于共享储能这种市场化运营场景主从博弈几乎是必然选择。1.3 模型框架上层与下层的交互逻辑整个模型的交互流程分三步。第一步上层共享储能运营商作为领导者在价格允许范围内设定每个时段的充电服务费价差和容量租赁价格。第二步下层各综合能源微网收到价格信号后以自身运行成本最小为目标决策分布式电源出力、内部储能的充放电、与共享储能的交互功率、向上级电网的购电量。第三步运营商汇总所有微网的响应需求检验储能容量约束和收益目标更新价格后进入下一轮迭代直到价格和用电计划都收敛。我在MATLAB里是用两个子问题交替求解的上层问题每次求解一次下层N个微网并行求解N个独立优化问题。微网之间互不影响天然就适合并行化改造。关于均衡再多说一句。在最终解处任何一方单方面改变策略都无法让自己获得更多收益上层价格是在下层最优响应下的最优价格下层用能计划是对该价格的最优响应。代码里一定要做这个验证否则算完只给一个结果数字审稿人如果问“怎么证明这是均衡”就很难回答。这部分我在第4节详细写验证方法。2. 数学模型构建与关键约束2.1 上层共享储能运营商的定价模型上层模型的决策变量是各时段的充电价、放电压以及如果有容量租赁服务的话容量租赁价格。目标函数是收益最大化主要包含三部分向微网售电的收入、从微网购电的成本、容量租赁收入。写成数学形式就是max 收入 Σ(λ_dis(t) × P_dis(t)) - Σ(λ_ch(t) × P_ch(t)) λ_rent × E_rent其中 λ_dis 是放电售电价格λ_ch 是充电购电价格P_dis 和 P_ch 分别是各微网与共享储能的总交互功率。约束方面包括共享储能充放电功率上限、SOC动态约束、一天始末SOC平衡、价格上下限。价格区间设置是上层模型的一个关键点。区间太窄博弈空间被压死运营商没有优化余地模型退化为固定电价问题区间太宽运营商会把价格推到极端结果微网发现还是直接向电网购电更划算共享储能反而被闲置。我的经验是取电网峰谷电价的上下浮动20%作为可行区间这样既能保证储能服务有竞争力又给定价留了空间。这里有一个建模上的硬骨头上层目标函数里出现了 λ 和 P 的乘积也就是双线性项。如果直接把双层问题丢给非线性求解器对初始值的依赖非常严重十有八九不收敛。处理办法有两种一种是用强对偶定理把下层目标函数等价替换掉另一种是迭代求解时把价格固定再求功率这样每一轮内层问题都是线性的。具体实现在第3节展开。2.2 下层综合能源微网的运行优化模型下层每个微网在收到价格信号后优化自己内部所有设备的出力。我这里以一个典型微网为例包含光伏、燃气轮机、燃气锅炉、电锅炉可选、内部储能可选同时承担电负荷和热负荷。目标函数是运行成本最小min 购电成本 购气成本 - 售电收益 设备运维成本约束条件可以分为四组。第一组是功率平衡约束电功率平衡要求光伏出力、燃气轮机发电、内部储能放电、向共享储能购电、向电网购电的总和等于电负荷、电锅炉耗电、内部储能充电、向共享储能售电的总和热功率平衡要求燃气锅炉供热、余热回收供热、电锅炉供热的总和等于热负荷。第二组是设备出力约束燃气轮机和锅炉有上下限光伏出力受预测值约束。第三组是爬坡约束燃气轮机相邻时段出力变化不能超过设定值。第四组是储能约束内部储能SOC要满足0.1到0.9的范围以及充放电功率上限。写代码的时候有个容易混淆的点每个微网同时面对两类“外部电源”一个是共享储能一个是上级电网。我在模型里把购电成本区分成两个变量P_grid 和 P_store这样方便考察不同价格水平下微网的购电选择偏好。实测下来当共享储能的充电价低于电网谷价时微网会优先在谷时段通过共享储能充电当放电压接近电网峰值时峰时段也会优先用共享储能放电。这种替代关系撑起了共享储能运营商的基本盘。2.3 多能耦合与约束处理综合能源微网研究里最看重也最容易写错的是电和热之间的耦合关系。燃气轮机发电会产生余热通过余热回收装置可以转化为热能供给热负荷电锅炉则是反过来消耗电能产生热能。这导致电功率平衡和热功率平衡不能独立写必须加耦合约束P_gt(t) × η_gt P_e_gt(t) H_gt(t) P_gt(t) × η_rec H_eb(t) P_eb(t) × COP_eb简单解释一下燃气轮机的电出力等于燃气输入乘以发电效率余热回收量等于燃气输入乘以回收效率电锅炉制热量等于电功率乘以制热性能系数。这三条公式在Yalmip里直接用等号约束拼进去就行。需要提醒的是多能耦合虽然看起来高级但不要为了耦合而耦合。我在审阅一些同行的代码时发现有人把热负荷模型写成非线性函数比如把锅炉效率写成出力的二次函数结果下层问题不再是LPkkt()无法处理整个求解流程崩掉。如果论文的侧重点是博弈机制而不是设备级热力学建议热系统部分尽量用线性模型比如固定效率或者分段的线性模型这样既能保留“综合能源”的特色又不影响博弈模型的求解稳定性。另外一个实用约定内部储能和共享储能不要直接交换。微网内的自有储能只服务于本微网共享储能由运营商统一调度两者在物理上分离。我刚开始建模时图省事让内部储能也能向共享储能卖电结果模型多了一层电量分配逻辑主从博弈变成了更复杂的三层嵌套结构难度陡增且收益提升很小。除非论文研究的就是分布式储能参与共享市场否则不建议给自己找这个麻烦。3. MATLAB代码实现与求解细节3.1 两种求解路线KKT转化与迭代逼近主从博弈求解有两条主流路线我把它们的特点整理成了一张对照表。对比项KKT转化法迭代逼近法数学复杂度高需要线性化互补松弛条件低保持双层独立求解求解质量较好单层求解可重复可能收敛到局部均衡实现难度中高依赖Yalmip kkt函数低主循环加求解器即可计算时间单次求解较快迭代多轮整体较慢适用场景学术论文审稿认可度高工程验证、代码调试第一种是把下层问题用KKT条件替换将双层问题转化为单层MPEC带均衡约束的数学规划然后用Big-M处理互补松弛条件最终变成MILP一次求解。这种方法的优势是数学严谨能直接交给Gurobi求解缺点在转换过程繁琐Big-M取不好会有数值问题。第二种是迭代逼近上层价格由外部更新规则生成下层用求解器求准确响应上下层交替直到收敛。这种方法实现简单、每一步的物理含义清楚、出了问题容易定位缺点是需要迭代几十到几百轮速度慢而且价格更新策略不当会震荡。我实际做下来的建议是论文投稿优先KKT转化路线审稿人对“双层问题一次性求解”的接受度高工程验证和前期调试用迭代路线因为你可以把每一轮价格和功率变化都打印出来看视觉化理解博弈过程。3.2 KKT转化与线性化的代码实现先说一个省事的工具Yalmip内置了kkt()函数可以自动生成下层优化问题的KKT系统。我前几版代码就是靠它做双层转单层的核心代码框架长这样% 定义下层问题 F_lower [constraints_lower]; O_lower objective_lower; % 生成KKT系统 [K, details] kkt(F_lower, O_lower, sdpsettings(solver, gurobi)); % 合并到上层约束中 F_upper [F_upper, K]; % 求解单层问题 ops sdpsettings(solver, gurobi, verbose, 0); optimize(F_upper, O_upper, ops);kkt()用起来快但要求下层问题必须是凸的具体来说目标函数和约束对决策变量都要求是仿射函数或凸二次函数。如果下层有0-1变量就完全不行。我遇到过不少次“无法处理非凸约束”的报错最后查下来都是因为下层某个约束写得不小心引入了非线性项。检查方法很简单把每个约束单独打印出来看有没有变量乘积、绝对值、取整操作之类的。如果不想依赖kkt()可以手动展开KKT。对下层约束 g(x) ≤ 0引入对偶变量 μ ≥ 0互补松弛条件 μ × g(x) 0 是非线性的需要用Big-M线性化。过程是引入0-1变量z写两组线性约束% 互补松弛线性化mu * g 0 constraints [constraints, mu M_big * z]; constraints [constraints, g M_big * (1 - z)]; constraints [constraints, mu 0, z 0, z 1];当 z1 时 μ 被压到0当 z0 时 g 被压到0互相排斥正好替代乘积条件。这套写法是MPEC建模的标准操作代码量不大但对Big-M的取值非常敏感取值技巧我在5.3节详细展开。3.3 迭代求解与价格更新策略迭代路线对新手友好得多。核心结构是一个外层循环套下层求解价格更新用阻尼因子平滑。我贴一个可以直接改来用的骨架price_dis 0.8 * ones(T, 1); % 放电价初值 price_ch 0.4 * ones(T, 1); % 充电价初值 alpha 0.2; % 阻尼因子 tol 1e-4; for iter 1:max_iter price_ch_old price_ch; price_dis_old price_dis; % 下层响应各微网独立求解 for k 1:N_mg [P_buy{k}, P_sell{k}, ~] solve_microgrid(mg_data{k}, ... price_ch, price_dis); end % 上层汇总更新价格 [new_ch, new_dis] update_price(P_buy, P_sell, es_data); % 阻尼平滑防止震荡 price_ch alpha * new_ch (1 - alpha) * price_ch; price_dis alpha * new_dis (1 - alpha) * price_dis; % 收敛判断 if norm(price_ch - price_ch_old) norm(price_dis - price_dis_old) tol break; end end阻尼因子alpha是迭代收敛的灵魂。实测下来取0.1到0.3比较稳alpha太大价格容易在相邻迭代间来回震荡alpha太小收敛速度又太慢对于24时段的常规算例一般要50到200轮才能收敛。还有一个容易忽视的细节如果某个时段微网对共享储能的需求恰好为0价格更新公式里会出现除以0的情况。我的处理是给分母加一个很小的正常数比如1e-6或者在该时段直接保持原价格不动。这些防御性写法在调试时能省很多事。3.4 初始参数设置与代码调试顺序初始价格建议取电网分时电价的中值或储能边际成本附近不要拍脑袋随便给。我在算例里先粗略算了一下储能运营的度电成本大约充电价0.35元左右、放电价0.95元左右然后以这个为初值向上向下浮动出可行区间。如果价格初值设在极端位置第一轮微网响应往往也会选择极端策略双层迭代很容易一开始就发散。代码调试顺序我强烈建议分层联调。先单独调下层固定一组任意价格让每个微网正确求解出成本最小的用能计划检查功率平衡约束是否严格满足SOC是否越界。再单独调上层固定微网响应看运营商的收益计算函数输出是否和手算一致。最后才拼装主从循环。直接一口气写完整套代码再从头打补丁定位问题的成本高得多。4. 算例分析与结果验证4.1 系统配置与基础数据我自己搭的测试系统配置如下共享储能额定容量10MWh最大充放电功率2MW充放电效率均为0.95SOC运行范围0.1到0.9初始SOC取值0.2。三个综合能源微网分别模拟工业、商业、居民场景每个微网都含光伏、燃气轮机和电负荷其中工业微网额外配了电锅炉和热负荷。电网分时电价采用典型三段式峰段9:00-12:00和17:00-22:00电价1.2元/kWh平段8:00-9:00、12:00-17:00、22:00-23:00电价0.8元/kWh谷段23:00至次日8:00电价0.4元/kWh。天然气价格取2.6元/m³。所有参数都集中写在程序最前面的参数区方便统一修改。这是老生常谈但值得强调价格、效率、上下限这些参数不要直接嵌进约束里否则后面调参数的时候改一处漏一处最后查错查到崩溃。4.2 优化运行结果解读收敛后的均衡结果有几个典型特征。第一共享储能价格表现出“谷时低价充电、峰时高价放电”的形态谷时段充电价约0.32元/kWh低于电网谷价峰时段放电压约1.15元/kWh低于电网峰值但保留了合理的服务利润。第二各微网响应行为分化明显工业微网白天生产负荷高主要从储能购电同时自己燃气轮机满发居民微网晚高峰从储能购电白天光伏大发时反过来向储能出售多余电量商业微网午间和晚高峰各有一个购电小高峰。第三共享储能运营商日收益约5200元来源大头就是峰谷价差。把三个微网与共享储能的交互电量汇总有一个更直观的印象微网购电量(kWh)售电量(kWh)相对无共享储能的成本变化工业微网3200500下降约12%商业微网2800800下降约9%居民微网36001200下降约15%还有一个有意思的现象共享储能的最优价格并不等于边际成本而是带有策略性加价。这是Stackelberg均衡的典型标志表示运营商在价格区间约束下拥有一定市场力。如果价格区间设得过宽运营商会把价格推到极端结果微网发现直接向电网购电更划算共享储能被闲置运营商的收益反而下滑。所以价格区间实际上是整个模型的“调谐旋钮”。4.3 收敛性与均衡验证迭代求解只回答“收敛到哪”不回答“收敛到的是不是均衡”。我每次跑完主从博弈都会额外做两步验证。第一步把收敛后的价格固定住重新求解每个微网的最优响应检查原迭代得到的微网计划是否真的让目标函数最小。如果某个微网单方面调整计划就能降低成本说明之前的结果不是均衡。第二步反过来把微网响应固定住在一个精细的价格网格上遍历所有可能的充放电价组合求解上层收益检查当前价格是否真的让收益最大。这两步验证在MATLAB里就是多跑几次optimize()计算成本很低但价值很大。我投稿时习惯在结果分析里放一个“均衡验证”小节用一张表格列出“单方面偏离后的收益变化”数据显示微网偏离后成本上升2%到5%运营商偏离后收益下降1%到4%这基本就能证明解是一个合理的Stackelberg均衡。4.4 与集中式优化对比只看运行成本数字集中式优化的系统总成本通常会低于主从博弈。我的算例中集中式系统总成本约19800元主从博弈均衡解约21500元多出的约8%可以理解为分布式决策的信息损失和激励协调成本。这个差距跟审稿人或者项目汇报对象解释时要有策略。集中式优化的前提是所有主体共享全部信息并服从统一调度这在实际市场里不成立主从博弈抛掉了这个理想化假设换来的是一个各方都能接受的均衡方案。你甚至可以这样包装主从博弈解给“系统总成本只增加8%但避免了强制调度带来的执行摩擦”这在机制设计视角下反而是更可靠的结果。5. 常见问题与排查技巧实录5.1 Yalmip求解失败原因排查最常遇到的报错是“Yalmip无法处理非凸约束”或者求解器返回NaN。这种问题十有八九出在下层模型的约束里混入了非线性项。用kkt()之前把下层模型的每个约束逐个打印出来检查确认都是线性或凸二次项再继续。还有一点要注意Yalmip版本不同kkt()函数对约束的支持有差异。旧版本对kkt()的支持不完整经常出现“结果正确但警告刷屏”的情况。建议至少用2021年之后的Yalmip版本搭配Gurobi或CPLEX求解器。如果项目必须锁定旧版本那就别偷懒手动展开KKT条件虽然代码长一些但至少行为可预期。5.2 迭代不收敛的处理价格曲线在相邻迭代间来回震荡这是迭代法最常见的毛病。处理手段有三个加阻尼因子、检查量纲一致性、处理零需求时段。量纲问题值得多说一嘴。目标函数的量纲必须是统一的“元”。如果功率用了kW价格用了元/kWh时间步长是1小时目标函数就是元但如果某个变量用了MW另一个用了kW价格更新公式里数值会差出1000倍收敛性当然一塌糊涂。我吃过这个亏后来统一规定所有功率变量都用kW所有能量变量都用kWh所有价格都用元/kWh一目了然。另一个常见场景某个时段微网对共享储能没有需求价格更新时对应的数据项为0更新公式会不稳定甚至NaN。我的习惯是不管×需求量为0该时段价格就保持上一轮不变等下一轮有需求了再更新。5.3 Big-M参数设置的避坑指南KKT线性化离不开Big-M但Big-M是MPEC建模里最出名的坑。M取太小互补松弛约束过强会人为排除真正的均衡解M取太大数值精度丢失求解器会报numerical issues甚至给出错解。我的经验是根据物理量级反推M。先估算对偶变量的合理范围储能容量约束对应的对偶变量量级大约是价格除以容量在0.1到1之间交互功率上限约束的对偶变量量级与电价接近在0.5到1.5之间功率平衡约束的对偶变量就是节点的边际电价在0.3到1.2之间。基于这个估计M取50到100倍即可通常落在100到10000这个区间。如果担心不同约束差异大可以对每条互补松弛条件分别设不同的M值而不是全局共用一个。5.4 其他容易忽略的坑双层模型的可行域跳变问题也很容易被忽略。上层价格连续变化时下层解的取值可能出现跳变导致上层目标函数不连续。这在经济学术语里叫“不可微的Value Function”直接后果是迭代法收敛困难KKT法求解器的线性化松弛失效。缓解办法是细化价格步长、缩小价格区间或者在价格更新里加更强的平滑。最后提一下求解器版本。Gurobi新版本和Yalmip配合一般没什么问题但老版本会有一些奇怪的兼容性毛病比如Gurobi 9.0配合Yalmip在MILP求解中出现过“二次约束未识别”的问题。我现在用的组合是MATLAB R2022b Yalmip 2023 Gurobi 10.0跑这个模型很稳定。如果读者用的是老版本MATLAB建议至少升级到2020a以上否则工具箱接口对kkt()和SOS1约束的支持都会打折扣。最后分享一个我自己的调参习惯无论论文最终采用哪种求解方法我都会先实现一版迭代求解作为基准。迭代法虽然慢但每一步的逻辑都是透明的——价格怎么变、微网怎么响应都能在MATLAB工作区里一步步看到。把这一版跑通、把均衡验证做完再上KKT转化为单层模型心里就有底了。主从博弈在共享储能和综合能源微网领域里能往下挖的方向还很多比如多领导者对多跟随者的双层博弈、碳交易机制与充放电价的联合优化、需求响应下更复杂的价格函数设计这些都是值得继续做的方向。希望这篇能帮正在啃双层问题的你少走一段弯路。
返回列表