ARTICLE DETAIL

资讯详情

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

主从博弈与KKT条件在综合能源系统优化调度中的工程实践

主从博弈与KKT条件在综合能源系统优化调度中的工程实践 做综合能源系统优化调度的朋友大概率都遇到过这个尴尬局面模型里的设备参数、负荷曲线、分时电价一应俱全单目标优化一把梭求解器跑出来的曲线也漂亮可真要拿到实际场景里却发现没有一个主体愿意按这个调度指令执行。原因不复杂——系统中每个参与者都有自己的利益诉求你替所有人做好“全局最优”决策但别人并不认账。“计及需求响应和电能交互的多主体综合能源系统主从博弈优化调度策略”这套东西我前前后后用了三周从模型推导到Matlab代码全部跑通。这篇博客就把主从博弈建模、KKT条件转换、Yalmip搭配Gurobi求解、以及我在里面踩过的各种坑从头到尾盘一遍。内容偏工程落地适合正在做综合能源系统优化、多主体协同调度、想要复现一篇博弈类论文代码的研究生和工程师。1. 多主体博弈的物理基础谁在跟谁博弈利益边界在哪1.1 典型的多主体综合能源系统架构先定义清楚物理场景不然后面的博弈模型全是空中楼阁。我这次项目里设定了这样一个系统一个区域综合能源系统运营商IESO作为上层决策者它手里握着燃气轮机、储能、以及向上级配电网购电的权力下层是若干个用能主体比如居民社区、商业楼宇和工业用户每个用户本身也可以有光伏和储能不完全是纯负荷。运营商和用户之间不是“命令-执行”关系而是“价格引导-自主响应”关系。运营商制定向用户售电、售热的内部价格用户基于这个价格再决定自己用多少能、怎么安排弹性负荷。同时运营商还要决定自己从上级电网买多少电、燃气轮机发多少电、储能充还是放最终目标是自己的净收益最大。电能交互在这个场景里有三个层次运营商与上级配电网的购售电交互、运营商与用户之间的电能买卖、以及用户侧分布式光伏的余电上网。第三类交互在绝大多数文献里会被简化为固定上网电价但实际做模型时你会发现这部分完全可以和需求响应放到一起建模让用户自己判断“多用还是少用、自用还是上网”。1.2 集中式优化的盲区为什么全局最优解没有人执行很多初学者拿到这个题目第一反应是把所有主体当成一个整体所有设备一起优化不就行了这一步错得比较典型。集中式优化隐含一个前提——存在一个能获得全部信息的调度中心而且所有主体都无条件服从中心指令。但现实情况是用户不是电厂的下属单位商业楼宇不会因为你“全局最优”就牺牲自己的用能舒适度工业用户更不可能为了削峰填谷放弃生产计划。这就牵扯出主从博弈存在的根本理由上层有先动优势先制定价格下层在给定价格下做自己的最优决策上层预测到下层的反应后再优化自己的策略。两方的目标函数各自独立谁也不需要向谁交底全部私有参数。最终达到的均衡虽然不一定等于集中式的系统总成本最优但它是一组“各方都能接受、且单方面改变策略不占便宜”的解在工程上反而更可执行。这也是我认为这个题目值得复现的核心原因它反映的是真实市场环境下能源系统的决策逻辑而不是理想化的大一统调度。2. 上层模型运营商的定价与电能交互决策2.1 上层决策变量与目标函数上层运营商的决策变量分两类。第一类是内部价格信号24个时段的售电价 $P_{el,t}$ 和售热价 $P_{h,t}$价格一般要设上下限约束比如电网购电价的一定倍数区间防止价格跑飞。第二类是实体运行变量燃气轮机出力 $P_{gt,t}$、储能充放电功率 $P_{ess,t}^{ch/dis}$、以及和上级电网的交互功率 $P_{grid,t}$。上层目标函数是自身收益最大化大致长这样收益 售电收入 售热收入 - 向上级买电成本 - 燃气轮机燃料成本 - 储能运维成本 - 需求响应激励补偿其中售电收入是 $\sum p_{el,t} \cdot L_{el,t}$$L_{el,t}$ 是用户在t时段的实际用电负荷也就是下层模型解出来的变量。注意这里出现了上下层变量的乘积后面求解时这是个需要处理的麻烦点先埋个伏笔。燃气轮机的燃料成本按分段线性处理或者用二次函数加分段近似取决于你手头设备的效率曲线精度。储能部分要计SOC连续性约束和充放状态互斥约束互斥条件用二元变量处理这块在Yalmip里很好写。2.2 与上级电网的电能交互约束运营商和上级电网之间不是无限功率买卖的。实际项目里要通过合同或者配网容量约束限制交互功率上限我用的是双向功率约束$P_{grid,min} \le P_{grid,t} \le P_{grid,max}$负数代表向上级电网售电。还有一个容易漏的点上级电网的购电价格是分时的但售电回馈价格往往低得多而且一般不允许运营商套利。我一开始没有加“同一时段不能同时买和卖”的约束结果模型出现了诡异的“低价买高价卖”无限套利曲线后来通过设置售电回馈价格低于任何时段购电价同时在模型里加了一个二元变量做买卖互斥问题才消除。如果你的参数设置里出现储能或电网交互功率同时在正负两个方向有数值九成就是少了这个互斥约束。电能交互这块在Matlab建模过程中其实还挺好实现的就是用sdpvar定义变量然后往约束集合里扔。但注意交互功率的初始值对后期迭代式求解影响非常大我在代码里用上一轮均衡的交互功率作为下一轮迭代初值收敛速度有明显改善。3. 下层模型需求响应主体的自利优化行为3.1 用户的目标函数效用最大化等价于成本最小化下层的每个用户不是被动接受负荷指令的。他们有弹性负荷、可削减负荷甚至拥有分布式光伏和储能。在给定内部售电价的前提下每个用户独立优化自己的用能计划。用户目标函数我采用的是成本最小化形式购电成本 购热成本 用能舒适度损失 - 需求响应激励补偿。这里的用能舒适度损失是刻画价格型需求响应的关键因为用户削减或转移负荷会带来不方便这个不方便需要量化成成本。实际建模里舒适度损失用二次函数比较多比如 $\alpha (L_{base}-L)^2$$\alpha$ 是用户对用能变化的敏感系数。参数取值不同用户的响应弹性就不同。设置方法上居民用户的敏感系数高工业用户的低否则工业用户随便削减负荷就不符合实际了。3.2 可转移负荷与可削减负荷的数学描述价格型需求响应主要通过可转移负荷来实现。我设置了每个用户各时段的转移容量上限同时要求一天内转移前后的总用电量不变——这个约束是最容易写漏的漏掉之后模型就会凭空造电或者消电结果曲线看起来合理实际不可执行。可削减负荷则对应激励型需求响应。用户在日前和运营商签订削减合同约定最大可削减容量和单位削减补偿价格。优化时用户根据价格信号决定切多少运营商在收益目标里减去这笔补偿支出。这里有一个关键陷阱如果削减补偿设置过高用户会疯狂削减负荷去拿补偿整体收益反而下降设置过低用户没有响应意愿需求响应的削峰填谷效果就出不来。我后面第7章专门讲这个参数标定的问题。3.3 上下层博弈的耦合点上下层模型不是两个孤立最优化问题它们通过两个渠道耦合。第一运营商制定的价格变量直接进入下层用户的目标函数和约束用户在求解时把它当作给定常数第二用户优化出来的负荷曲线 $L_{el,t}$ 回到上层目标函数里决定运营商的售电收入和能量平衡是否成立。这个耦合结构就是典型的三层-双层优化结构上层“优化”下层“约束”。从博弈论角度上层是领导者Leader下层是跟随者Follower属于Stackelberg主从博弈。求解目标不是上层单方面的最优而是上下层同时满足最优性条件的均衡点。4. 求解路径从KKT条件到可计算的MILP4.1 双层问题为什么不能直接迭代硬刚熟悉智能优化算法的同学第一反应可能是外层用粒子群或遗传算法搜电价内层用线性规划解用户响应反复迭代直到收敛。这条路我在初版代码里试过结论是“能跑但很折磨”。原因是上下层问题在峰谷电价交替时特别容易出现振荡电价升高导致用户降负荷降负荷又导致运营商收益下降于是运营商降价用户又升负荷如此反复收敛判据很难设。更麻烦的是每次迭代都要重新调用一次求解器24时段、多个用户场景下一轮主从迭代就要好几分钟。所以项目里最终采用的是更扎实的做法利用下层问题的凸性把下层的KKT条件写成约束塞进上层问题整个双层问题变成一个带互补约束的数学规划MPEC然后线性化转成混合整数线性规划MILP一次性求解。这条路数值稳定性比迭代法好得多而且能直接拿到全局最优解。4.2 KKT条件的推导与互补松弛线性化下层用户问题是线性或二次凸规划这个前提下KKT条件是原问题最优解的充分必要条件。对每个用户写出拉格朗日函数然后拆成三组条件主问题梯度条件、对偶可行性条件、原始约束条件。最关键的是互补松弛条件$\lambda \cdot (g(x)-b) 0$即对偶乘子与对应的不等式松弛量至少有一个为零。这个非线性乘积没法直接扔给求解器需要线性化业内标准做法是大M法$\lambda \le M z$$g(x)-b \le M(1-z)$$z \in {0,1}$用二元变量 $z$ 强制至少一边为零。这里的M取值是个技术活我第7章会讲怎么定。4.3 强对偶定理消除双线性项现在回过头处理上层目标函数里“价格×负荷”那双线性乘积项。下层问题的强对偶成立时原问题目标函数和对偶问题目标函数在最优解处相等利用这个等式可以把“价格×负荷”替换成由下层对偶变量和目标函数其他部分组成的形式。这一步几乎所有复现博弈论文的人都会卡住但只要你有下层线性凸结构这个替换是严格成立的放心用。替换完之后整个问题只剩价格变量、上层运行变量、用户负荷变量、对偶乘子、以及二元变量之间的线性关系原来那个双线性的魔鬼消失问题变成标准的MILP。我用的求解器组合是Matlab Yalmip Gurobi规模在几十个变量级别时求解只要几十秒。4.4 两种求解路线的适用边界这里必须把话说清楚KKT单层化不是万能的它要求下层问题必须是凸的。如果你的下层模型含有凹的效用函数、整数决策变量或者非凸的电网潮流约束KKT条件就不再充分这时候要么改模型结构要么退回到迭代求解法。我在项目里也给代码留了一个双模式开关一个走KKTMILP一个走分布式迭代方便对照验证结果。如果你是为了复现期刊论文建议优先走KKT路线审稿人对智能优化算法跑博弈的解通常有很多质疑如果你是为了做实时调度软硬件联调分布式迭代才是工程常态两边各有用武之地。5. Matlab代码实现结构、关键函数与运行流程5.1 工程目录与代码结构整个Matlab工程的目录结构我按功能拆成了四个模块实际写代码时这样组织会清爽很多main.m // 主程序定义场景、调用求解、输出结果 plot_result.m // 绘图脚本 data/ // 参数文件设备参数、负荷、电价、DR参数 model/ // 模型构建函数上层约束、下层KKT、目标函数 utils/ // 工具函数数据预处理、大M取值、结果校验变量定义上我统一用结构体封装参数比如para.gt.pmax表示燃气轮机出力上限para.user.alpha表示用户敏感系数。项目做到中后期变量多了之后这种结构体方式比零散命名变量靠谱得多出bug时排查效率高很多。5.2 Yalmip建模核心代码建模这块Yalmip确实是Matlab里做优化问题最顺手的工具。一个简化版的核心流程长这样%% 定义变量 T 24; P_el sdpvar(1, T); % 运营商售电价 L_user sdpvar(n_user, T); % 各用户负荷 P_ess sdpvar(1, T); % 储能出力正放负充 P_grid sdpvar(1, T); % 上级交互功率 u_ess binvar(1, T); % 储能状态互斥 %% 约束集合 Constraints []; % 运营商功率平衡 Constraints [Constraints, P_gt P_ess P_grid sum(L_user, 1)]; % 储能SOC与互斥 Constraints [Constraints, SOC(2:T) SOC(1:T-1) - P_ess(1:T-1)*eta/dis_cap]; Constraints [Constraints, -P_ess_max*u_ess P_ess P_ess_max*(1-u_ess)]; % 下层KKT转来的等价约束 Constraints [Constraints, KKT_constraints]; %% 目标函数 Objective sum(P_el.*L_user) - sum(c_grid.*P_grid) - cost_gt - cost_dr; %% 求解 ops sdpsettings(solver, gurobi, verbose, 2); optimize(Constraints, Objective, ops);实际项目里KKT约束是一大坨Constraints拼进集合的建议在model/函数里封装成一个独立函数返回约束组不然主程序会臃肿到不可维护。还有一个小细节Gurobi求解MILP时可以把MIPGap参数设置为0.01%左右算例规模不大时几乎不影响时间但能避免默认Gap过早截断导致的原对偶偏差。5.3 迭代式求解与KKT单层化的双模式实现我在代码里留了分布式迭代模式作为交叉验证。具体流程是先给一个初始电价曲线下层求解每个用户的优化问题返回负荷曲线上层根据负荷曲线更新定价策略求解运营商的单层优化问题然后检查电价和负荷的相邻两次迭代差是否小于阈值。这个流程在Matlab里的实现就三个函数循环调用逻辑上比KKT模式直观。但第4章说的振荡问题一直存在我加了阻尼系数后基本收敛$p^{(k1)} p^{(k)} \gamma (\hat{p}^{(k)} - p^{(k)})$$\gamma$ 取0.4左右比较稳。如果读者发现自己迭代算法怎么调都不收敛先检查这个阻尼系数和初值设定大概率就是这两处问题。5.4 从调试到出图的完整流程跑通一个案例我喜欢按三个步骤来。第一步先关掉下层KKT耦合固定一组电价单独求下层用户的最优响应这一步是验证下层模型正确性的关键负荷曲线要符合基本物理直觉。第二步固定用户负荷单独求上层运营商的优化问题看收益是否为正、功率平衡是否满足、有无套利现象。第三步再把上下层耦合起来跑完整的MPEC对比前两步结果判断耦合约束有没有写错。这种分步调试验证的方法虽然慢但对定位模型逻辑错误非常有效。很多时候一上来直接跑双层模型出现“结果很奇怪”的情况根本没法定位是上层错还是下层错还是转换错。绘图部分我用plot函数画电价、负荷、交互功率三张子图加上一个负荷转移前后对比柱状图论文里放图基本够用。6. 算例设计三种场景下博弈均衡结果的对比分析6.1 参数设置与场景说明算例参数我参考的是一个典型工业园区的数据燃气轮机容量1.2MW储能容量1.5MWh上级电网购电分为峰、平、谷三段价格用户负荷分成居民、商业、工业三类各类用户的弹性系数差异明显。大M值取10000收敛阈值1e-4时段数T24用户数量3个。三种对比场景我这么设置的场景需求响应电能交互说明场景1无仅上级购电用户负荷固定运营商只做经济调度场景2价格型DR仅上级购电用户可转移负荷响应内部电价场景3价格型激励型DR上级购电余电上网包含可削减负荷用户光伏余电可上网三个场景跑下来各调各的参对比起来说服力强。实际写论文的同学也可以照这个思路做一个“逐步增加功能”的对比表比只给一个最终结果要专业得多。6.2 关键结果电价、负荷与系统收益的变化趋势先看典型参数下跑出来的结果给一个方向性的结论具体数值大家用自己参数跑出来会不太一样但变化趋势是稳定的场景1作为基准运营商收益最低用户成本最高系统的峰谷差最大。因为固定负荷没有任何调节弹性运营商只能被动跟随用户负荷曲线发电购电高峰时段被迫高价买电低谷时段又用不完自己的燃气轮机。场景2引入价格型DR后运营商通过提高峰时电价、降低谷时电价把一部分峰段负荷转移到了谷段。这个策略带来的连锁反应是燃气轮机在谷段多发电、峰段少发电系统整体设备利用率提高用户总用电量不变但因为把电用在了电价低的时段总购能成本降了约8%到12%运营商从售电量增加和购电成本降低里找补回来利润反而比场景1高。这就是主从博弈最有意思的地方——上层让出一部分峰时利益谷时销售扩张把利润补回来形成一个双方都受益的均衡。场景3增加激励型DR和余电上网后用户可以在高峰时段削减一部分负荷换取补偿光伏余电还能卖回给运营商。这个场景下峰谷差率进一步下降用户成本继续下降但如果削减补偿单价设太高运营商利润会下降这也是我在第7章要单独讲的参数问题。6.3 博弈均衡结果合理性判断拿到一组均衡结果怎么判断它确实是主从博弈均衡而不是模型bug跑出来的伪解我总结了三个验证角度。第一降低上层某个时段的电价用户响应负荷应该上升。如果出现电价和负荷同方向变化的时段说明上下层耦合写反了。第二固定均衡电价不动让用户单方面偏离均衡策略用户的成本应该上升否则说明用户侧没到最优。第三固定用户均衡负荷不动运营商调整价格策略收益应该下降否则说明上层也没到最优。这三个条件的工程含义就是“任何人单方面改变策略都无法获益”也就是纳什均衡的标准定义。我在代码里单独写了一个verification.m脚本验证这些条件每次跑完新参数都会过一遍很有用。7. 实战踩坑记录大M值、迭代振荡、DR参数失当与结果验证7.1 大M值到底取多少才合适互补松弛条件里的M取值是MPEC模型最常见的坑。取值太小会错误地排除掉真正的最优解取值太大会导致求解器数值精度下降出现“明明有解却报不可行”或者“解出来了但对偶乘子不满足互补条件”的诡异情况。我的经验是先忽略互补约束跑一遍纯LP看看对偶乘子的量级大概在多少再乘上50到100倍作为M的初值。以这个项目的参数为例价格单位是元/kWh对偶乘子量级在1以内M取100左右就够用而不是拍脑袋给个1e6。另外Gurobi对MIP的数值鲁棒性虽然好但M的上下限最好差距不要超过7个数量级否则求解稳定性会明显变差。7.2 分布式迭代的振荡陷阱与阻尼调参KKT单层化虽然好但我一直在代码里保留分布式迭代模式原因是它在工程上有独立价值。最头疼的问题是振荡尤其价格变量在峰谷时段会反复横跳。我做过一个调试实验不加阻尼时电价迭代路径呈锯齿状峰时段价格在1.2到1.8元/kWh之间来回弹就是不稳定。解决方案是在价格更新里加阻尼因子 $\gamma$同时限制单次更新的最大变化量。实际调下来$\gamma0.4$、单次价格变化不超过0.05元/kWh时收敛效果最好大概迭代15到20轮左右达到稳定。如果你是做在线调度而不是离线仿真建议在此基础上再加一个低通滤波把价格变化频率进一步压下来降低对实际用户在舒适度上的冲击。7.3 需求响应越调越亏参数失当的典型症状我开始跑场景3时遇到过非常反直觉的结果加需求响应后运营商利润反而比场景2低了。当时第一反应是模型有bug排查了两天才发现是激励型DR的削减补偿单价设得偏高用户的削减响应过度运营商的补偿支出超过了它节省的购电成本窟窿。这个问题的本质是需求响应不是免费午餐运营商让渡利润空间换取用户侧弹性但所有转移和削减行为都有代价。现实项目做需求响应方案时补偿单价设多少一般不是模型一拍脑袋出来的而是要参考用户问卷调查、历史用能数据和同类项目的结算案例。建模时的做法有三种一是按用户中断负荷的停电损失价值估算二是按用户生产损失数据拟合成阶梯补偿曲线三是把补偿单价也设为上层决策变量让博弈模型自己找出最优补偿策略这是进阶玩法模型规模会大一圈但对实际运营很有参考价值。7.4 结果正确性验证的实用手段最后分享一个很多人忽略但最能兜底的方法拿偶数大M和更严格MIPGap重新求解同一个算例结果对比。如果两次求解的均衡电价和负荷偏差很小说明数值参数设置对结果影响不大结果可信如果偏差很大说明问题对M值或Gap设置敏感需要回头重新审视模型条件。另外建议把“固定电价-求用户最优”和“固定负荷-求运营商最优”两个子问题单独跑一遍把子问题最优值加起来和MPEC整体目标值对比差值应该在数值容差范围内。如果对不上问题一定出在KKT转换或线性化环节而不是求解器。这个方法救了我至少三次。写到最后说一点个人体会。主从博弈这类题目最容易让人卡壳的往往不是KKT推导也不是求解器调用而是建模之前没把“谁是leader谁是follower、上层凭什么有先动优势、下层的信息到底公开多少”这三件事想清。我在这个项目里吃过的最大教训就是一开始贪心把所有主体都塞进一个“对等博弈”框架里结果模型复杂度失控均衡解也解释不清。后来老老实实退回到一主多从结构才把问题理顺畅。如果你也在复现类似题目建议先画清楚层级关系和信息流再动手写第一行Matlab代码。
返回列表