开篇说几个很多做能源优化调度的同行都容易搞混的点:主从博弈不是给两个目标函数分别求最优,也不是简单把多目标加权变成一个目标——它解决的是两个或多个决策主体之间存在先后行动顺序、并且后行动者会对先行动者的策略产生反馈的场景。我在做综合能源系统调度时,碰到的典型问题是多个能源主体各自利益不一致,但又通过电能交互互相牵制,这时用传统单层优化会把利益冲突压成一个问题,结果方案虽然全局可行,放回实际运行却没人愿意执行。后来把模型改成“计及需求响应和电能交互的多主体综合能源系统主从博弈优化调度策略”,再用Matlab把求解流程跑通,才真正把调度方案落到可验证的均衡点上。
这篇文章把我从建模到代码实现再到算例分析的关键细节都梳理出来。适合正在做综合能源系统、微电网、区域多能互补调度的研究生或工程师参考,尤其是遇到“谁定电价、谁响应价格、价格和电量互相耦合”这类问题的人。文中不会贴完整源码,但会把核心框架、KKT转换逻辑、Matlab实现的坑和数据实验的关键结论讲透,方便你照着搭自己的模型。
1. 为什么单主体优化做不了多主体调度:问题的真正起点
做能源系统调度的人一开始接触的模型,大多是单目标优化:给定负荷、新能源预测曲线,在满足供需平衡和设备约束的前提下,让总运行成本最低,或者让碳排放最小。这类模型的隐含假设是——整个系统只有一个决策者,所有设备、负荷都听它的。但实际工程项目里,情况复杂得多。
1.1 多个利益主体的真实场景
以某园区综合能源系统为例,上层可能是一个综合能源服务商,它管着燃气轮机、储能、以及从上级电网购电的联络线,目标是运行收益最大;下层是若干个办公楼宇用户或者负荷聚合商,它们的用电负荷不是刚性的,可以调一部分,每个用户的目标是电费支出最小。服务商想把电价定得高一点,赚更多售电收入;用户又希望电价低一点,并愿意通过调整用电时段来换激励。这种“上层定价、下层响应”的关系直接放进单层优化里根本说不清,因为你不知道在优化目标里该把电价当变量还是当参数。
1.2 电能交互带来的强耦合
除了买卖双方的利益矛盾,电能交互本身还带来物理约束上的耦合。比如服务商和用户之间有联络线功率上限;服务商既可以向上级电网买电,也可以把自己多余的清洁能源卖给用户;用户侧的光伏如果在某个时段大发,还可以反送电给服务商。这些功率流彼此交织,同时要求不能出现“既大量购电又大量售电”这种没有实际意义的调度结果。如果只用人工加权把上下层目标合并,很难正确处理这些双向潮流约束,而且加权系数到底取多少并没有物理依据。
1.3 从工程现象提炼决策顺序
我后来总结出一个判断模型是不是需要主从博弈的简单标准:能不能自然地分出“先决策者”和“后决策者”,并且先决策者必须预测后决策者的反应。综合能源系统里,服务商先公布电价和激励政策是说得通的,用户看到电价后调整用电计划也是实际行为。这个过程有明确的先后顺序和反馈机制,正是Stackelberg博弈的典型结构。想通这一点之后,后面建数学模型就比较顺了。
在这个背景下,“计及需求响应和电能交互”就很好理解:需求响应让下层主体具备调节能力,电能交互让上下层在物理层面产生双向联络。两者合在一起,才需要主从博弈来刻画。
2. 从物理场景到数学模型:架构、变量与约束
模型搭建阶段,不建议一上来就去翻文献里的通用公式,而是先画清楚能源流向和信息流向,再定变量。我当时把整个问题拆成三个模块:上层主体、下层主体、电能交互网络。
2.1 上层综合能源服务商的建模
上层的核心业务是从上级电网购电、利用自有分布式电源和储能进行调节,再把电能销售给下层用户。它的目标函数大致可以写成:
上层收益 = 售电收入 + 向上级电网售电的收入 - 向上级电网购电成本 - 设备运维成本 - 激励型需求响应补偿支出
决策变量包括:各时刻从上级电网购电功率、向上级电网售电功率、储能充放电功率、燃气轮机出力,以及最关键的一类——面向用户的售电价和向上级电网的购电价等价格型变量。
这里需要注意,购电和售电不能同时发生。我会用一对0-1变量和联络线功率上下限把这个逻辑约束住,避免整数变量在外层被求解器“偷懒”忽略。如果省略了这个约束,结果很容易出现同时买电又卖电的荒谬调度。
2.2 下层用户模型的三种需求响应形式
下层主体的需求响应建模方式,直接决定了整个模型是线性还是非线性。我建议分三类来处理:
- 价格型需求响应:用户用电量随售电价变化,常见形式为弹性函数,用户侧电费最小化问题里会出现电量与电价相乘的双线性项。
- 激励型需求响应:服务商给用户一个削减负荷的补偿单价,用户选择申报削减量,这里需要把削减量约束在可调范围内。
- 可转移负荷:针对分时电价场景,用户把部分负荷从高电价时段转到低电价时段,转移量有上限,并且总转移量在一天内守恒。
实际建模时,我不建议把三种响应方式全混在一个下层问题里,否则KKT推导会非常痛苦。可以先做“价格弹性 + 可转移负荷”的组合,等单层化跑通后再加激励型。
2.3 电能交互的数学表达
电能交互部分需要明确联络线功率的参考方向。我习惯约定:下层从上层购电为正方向,下层向上层送电为负方向,这个值通过上层售电价和下层用电量相乘得到,会出现在双方的交易成本中。除了功率平衡之外,还要加上联络线传输容量约束。
如果交互网络不是简单星形结构,而是一个有多节点的配电网,建议用DistFlow潮流模型替代单一功率平衡方程,避免忽略节点电压约束。不过,那会引入非线性项,一般需要配合二阶锥松弛处理。我的做法是先在星形拓扑下把博弈求解逻辑跑通,再扩展潮流细节。
2.4 目标函数与约束的核心结构
上层和下层各自拥有独立目标,这是主从博弈与集中式优化的最大区别。上层把电价视为自己可控制的策略变量,下层把电价视为参数,但下层的最优用电量又反过来影响上层的收益函数。数学上可以写成如下形式:
Leader: max f_L(x, y(x)) s.t. g_L(x, y(x)) ≤ 0 Follower: max f_F(x, y) s.t. g_F(x, y) ≤ 0其中 x 是上层策略,如电价、设备出力;y 是下层决策,如用电量、可转移负荷、削减量;y(x) 表示 y 是 x 的函数。这个“y 嵌套在 f_L 里”的结构,就是博弈模型区别于普通双层规划的核心点,同时也是求解时最大的难点。
约束方面,我列出常踩的几个:储能SOC首末时刻相等、充电和放电不能同时、用户可转移负荷全天总量守恒、联络线功率方向唯一、电价上下限防止出现无穷解。每个约束都要注意量纲统一,比如电价单位是元/kWh,功率单位是kW,能量单位是kWh,如果接口上把SOC能量约束写错,计算结果会非常离谱。
3. 主从博弈求解:KKT单层化与双线性项处理
主从博弈模型写出来后,关键问题是怎么求解。业界常用两类思路:一类是迭代求解,上层先给定一组电价,下层求解得到用电计划,上层再把用电计划代回来更新电价,如此反复直到收敛;另一类是KKT单层化,把下层优化问题转化为它的最优化条件,并入上层问题中,一次性求出均衡。我的推荐是项目前期直接用KKT单层化,理由和踩坑细节下面展开。
3.1 为什么选KKT单层化而不是纯迭代
纯迭代法实现简单,逻辑跟博弈过程一一对应,但有两个实际问题:首先,上层每次更新电价时,如果下层问题不是严格凸的,迭代过程可能来回震荡,无法收敛;其次,即使收敛,也可能收敛到的是从特定初始电价出发的局部均衡点,缺乏全局可信度。KKT单层化一旦把问题写成混合整数线性规划,理论上全局求解器会给出稳定解,更容易复现。
当然,KKT单层化也有代价:下层问题必须是凸问题,而且还需要满足线性约束规范。如果下层是线性规划或凸二次规划,就可以直接用KKT条件等价替换,当前我正在处理的需求响应模型正是这种结构,所以这个方法非常合适。
3.2 下层问题的KKT条件推导要点
下层一般是一个最小化电费支出的线性规划问题,它的KKT条件包括四个部分:
- 原问题可行性条件:下层自己的约束,比如用电量上下限、可转移负荷平衡。
- 对偶可行性条件:每个不等式约束对应的对偶变量要满足符号限制。
- 平稳性条件:对下层决策变量求梯度等于零,这里会出现电价和对偶变量的关系。
- 互补松弛条件:原约束和对偶变量的乘积等于零。
关键难点在下层目标函数里如果包含二次项,比如价格弹性需求响应中用电量乘以电价,这个二次项在下层问题中必须是凸的。所以我建模时把下层的需求响应二次项限定为用电量的二次凸函数,保证KKT等价性成立。
3.3 双线性交易项的线性化处理
刚学主从博弈时,我最容易卡住的地方是上层目标函数里有“售电价 × 用户用电量”这种双线性项。如果直接保留这个非线性,问题就变成了非凸的,全局求解基本没戏。解决这个问题需要用到强对偶定理。
思路是利用下层问题的最优性,把下层目标函数的最优值用其对偶问题表达出来,再由强对偶性将其转换为上层目标函数中双线性项的部分替代。具体来说,上下层之间通过购售电交易产生的费用项,可以被重新表达成仅包含对偶变量和原问题参数的线性形式,从而避开两个决策变量的直接相乘。
这个推导过程需要非常小心,比如强对偶等式的符号方向如果写反,优化结果会表现为上层收益被无限夸大,检查半天也查不出具体问题。我建议在搭建模型时单独写一个脚本,专门验证强对偶关系在原参数取值下是否成立,而不是等到整体求解失败后再找原因。
3.4 互补松弛条件的Big-M线性化
互补松弛条件形如“对偶变量 λ ≥ 0,约束函数 h(y) ≤ 0,且 λ × h(y) = 0”。因为 λ 和 h(y) 都是优化变量,它们相乘是非线性。工程上最常用的处理手段是引入0-1变量 z,拆成如下形式:
h(y) ≥ -M·z h(y) ≤ M·(1-z) λ ≥ 0 λ ≤ M·z其中 M 是一个足够大的正数。这个 M 取值我一般取约束物理最大值再乘上2到5倍,并不是越大越好。M太大会让混合整数规划可行域变松,求解时间明显上升;M太小会把真实可行解排除掉,甚至直接报不可行,所以调试时第一件事就是检查每组互补松弛条件的M是否合理。
另外一个容易被忽视的点是,0-1变量的数量决定了求解复杂度。下层约束越多,需要引入的0-1变量越多。如果用户数量很多,建议不要对每个用户的每个时刻都做KKT单层化,而是先聚合再建模。
3.5 单层化后的最终问题形态
把所有KKT条件并入上层模型后,整体会变成一个混合整数二次规划或混合整数线性规划。如果上下层都是线性,最后得到的是MILP,Matlab自带整数线性规划求解器在中小规模下就能处理。如果下层有二次项,最后会变成MIQP,此时需要检验目标函数二次项是否正定,实际项目中我常用分段线性化把二次项逼近成线性项,以换取求解稳定性。
单层化后的模型规模会比初始问题大不少,因为每一个下层约束都会对应一个对偶变量和若干个Big-M辅助变量。我在模拟项目X中处理24时段、3个用户聚合体、每用户约40个约束时,最终变量数达到几千个,求解时间通常在几十秒内,效率可以接受。如果规模进一步增长,就要考虑利用问题分解或并行计算。
4. Matlab代码实现的核心框架与调试心得
Matlab实现这件事,很多第一次接触的人会误以为“博弈模型”需要专门的软件,实际上核心还是优化模型的标准实现流程。下面我把自己的代码结构和关键函数划分讲一下,不贴全代码,但说清楚每个模块的作用。
4.1 整体模块划分
我的代码通常分成四个文件:
- 参数文件:定义系统参数、设备参数、分时电价区间、需求响应系数、储能容量效率。
- 模型构建函数:用Matlab优化工具箱变量定义决策变量,写约束和目标函数。
- 求解与后处理脚本:调用求解器,整理结果变量,输出各时段电量、电价、费用。
- 辅助验证函数:包含无需求响应对照组、聚合求解验证、强对偶验证等。
这种分离方式让我可以方便地对比不同场景。调模型时最忌讳把所有参数、变量、约束都写在同一个脚本里,因为一旦结果异常,你很难定位是参数输入错、约束漏写还是求解器数值问题。
4.2 核心变量的定义方式
我会把上层决策变量定义成Matlab优化变量,电价变量定义为行向量,长度为24;设备出力、储能SOC、从电网购售电功率也都是24维向量。下层用户决策变量则根据用户数量定义成二维矩阵,行是时段,列是用户编号。
关键一点:上下层交易功率的符号必须全局统一。我在交易矩阵中规定正值表示用户从服务商购电,负值表示用户向服务商反送电,上层收益函数中用正负号自动区分收入和支出。如果不统一,同一个电能交互可能被重复计入两次成本,结果就错了。
4.3 约束写入的先后顺序
约束写入的顺序会直接影响调试效率。我的经验是固定顺序:先写功率平衡、再写设备出力范围、然后写储能SOC、最后写KKT互补松弛约束。互补松弛约束是出错率最高的地方,应放在最后单独校验。
为了快速排查不可行,我会把互补松弛约束单独做成一组,并用一个布尔开关控制是否加入模型。先关掉互补松弛约束,只加入层问题和下层原问题约束,求解原模型;如果此时可行,说明问题出在互补松弛部分,可以逐个条件检查。
4.4 求解器选型与参数设置
Matlab自带intlinprog对中小规模MILP够用,但如果模型变量规模大、分支定界深度深,自带求解器速度会成为瓶颈。工程上可以接入通用商业MIP求解器或者开源求解器,通过在Matlab中建立模型后统一调用它们求解。
求解器参数里,最需要调的是MIP相对gap上限。我一般把它设在1%附近,因为主从博弈均衡解对边界精度要求不必太高,过小的gap会让求解时间成倍增长,而且很多时间其实浪费在证明最优性上。
4.5 运行流程与结果输出
运行流程大概如下:先加载参数,构建模型,调用求解器,提取结果后跑一个后处理脚本把电价曲线、用电量曲线、储能充放电曲线、各主体成本收益导出并画图。画图阶段我习惯把几次对比场景放在同一张图里,方便看需求响应前后的峰谷变化,这一点在算例分析时特别有用。
如果求解结果出现“部分时段弃风弃光,但系统又同时购买高价电”这种矛盾现象,通常不是设备约束出错,而是上层却没有把可再生能源的边际成本设为零,或者电价限价区间设置不合理。检查顺序一般是从目标函数系数开始,而不是一上来改约束。
5. 算例结果分析:从曲线到结论
没有结果的调度模型是空谈。这一节我用一组典型算例来分析主从博弈模型能带来什么变化。因为实际参数不同,我这里给出的数值只是作为趋势参考,关键看规律。
5.1 算例设置
我设置的模拟案例是一个综合能源服务商加三个办公楼宇用户聚合体的日调度问题,调度时间粒度为1小时,共24个时段。燃气轮机和储能作为服务商侧调节资源;用户侧有三种需求响应能力,包括价格弹性响应、可转移负荷响应和小部分激励削减响应。仿真中设置两组对照组,第一组完全无需求响应,第二组仅价格型需求响应,第三组计及价格型和激励型两类需求响应。
5.2 需求响应对负荷曲线和运行费用的影响
结果显示,无需求响应时,用户负荷峰值集中在上午10点到下午4点,服务商为了满足峰值负荷,需要启动高成本的燃气轮机并高价购电;加入价格型需求响应后,用户会把大约8%的峰值负荷转移到晚间低谷时段,服务商的购电成本明显下降。
如果把激励型响应也加进来,用户在午高峰参与削减的空间会进一步压低峰值负荷,服务商调度灵活性提升。三组情形的对比典型数值如下表:
| 场景 | 峰值负荷相对大小 | 服务商购电成本相对值 | 用户电费相对值 |
|---|---|---|---|
| 无需求响应 | 1.00 | 1.00 | 1.00 |
| 仅价格型需求响应 | 0.92 | 0.87 | 0.93 |
| 价格型+激励型响应 | 0.84 | 0.80 | 0.89 |
这里的相对值说明,考虑需求响应后,用户电费并不是绝对意义上的最低,因为用户在响应过程中可能牺牲了部分用电舒适度,但从综合成本看仍有明显优势。这个结论和只看目标函数数值的直观理解有很大区别,也是博弈模型比集中式模型更能反映现实的地方。
5.3 电价均衡与设备出力的联动
从结果看,主从博弈得到的均衡电价并不是恒定不变的,而是在净负荷曲线基础上平缓波动。高负荷时段电价升高,低负荷时段电价降低,用户基于电价转移负荷之后又反过来抑制电价尖峰,形成一种相互制约的稳态关系。
储能设备在这种价格机制下的表现很有意思:它不再单纯追求“低充高放”的套利,而是同时考虑向上级电网买电、向下层售电、以及自身调节容量的综合价值。某些时段即使电价偏低,储能也可能充电,目的是为下一次高峰时段的高价售电蓄能。这一类联动关系如果不用博弈模型很难呈现出来。
5.4 需求响应系数变化的敏感性分析
我还做了需求弹性系数从0.1到0.3变化时的敏感性分析。弹性越大,用户对电价越敏感,均衡价格尖峰下降越明显,同时用户可转移负荷的增长会削弱服务商的定价能力。
这个结果给工程决策的启示是:如果服务商过于依赖高电价盈利,而用户需求响应能力很强,最终的均衡收益很可能低于预期。因此在实际运营中,服务商不能只盯价格设计,还要考虑用户侧可调节资源的潜力和用户参与响应的成本。
6. 常见坑与工程化建议
最后这部分集中写我在这个项目中踩过的坑,以及对想复现这个模型的同行的一些实在建议。
6.1 KKT条件的适用前提一定要提前确认
KKT单层化并不万能。如果下层问题存在整数决策变量,比如用户侧储能带有0-1充放电状态,那么下层的整数规划就不能直接写KKT。早期我看文献时没注意这一点,把带0-1变量的用户模型直接做KKT,结果得到的所谓“均衡解”在代入原下层模型后并不满足下层最优性。
解决办法是测试下层问题凸性之前,先移除0-1变量,比如把储能充放电约束改写成线性互补形式,或者把离散决策放在上层处理,下层只保留连续变量。如果项目确实需要下层整数决策,就只能用迭代算法并容忍可能的收敛困难,KKT这条路走不通。
6.2 Big-M参数的调试技巧
互补松弛的Big-M是我花时间最多的地方。经验是:不要把M设成同一个常数,而要针对每个互补松弛条件单独设置。比如用电量上下限约束的M取最大负荷的2倍,储能SOC约束的M取储能容量的2倍,联络线功率约束的M取线路容量的3倍。单独设置并不会增加太多编码成本,却能让求解器分支效率明显提升。
判断M是否合适的方法也简单:把求解出来的对偶变量和约束函数各自代入互补条件,检查乘积的绝对值是否在极小范围内。若存在很大的不为零的乘积,说明对应组的M太小或变量取值有误。
6.3 如何验证得到的解是真正的均衡
这一步很多教程不会提,但实际项目必须做。解完KKT单层化模型后,我会固定上层的电价和激励策略,单独重新求解下层问题,对比此时的下层目标值和KKT单层模型中的下层目标值。如果两者一致,说明上层的价格策略确实诱导了下层按预期响应,均衡性质基本成立。
另一种方法是把下层问题得到的最优用电量传回上层,固定用电量重新优化上层价格,看看上层目标能否进一步提升。如果不该再提升,说明当前解是稳定的均衡点。两个验证方法我建议都跑,至少能避免模型看起来收敛实际却是求出一个非可行解的问题。
6.4 从单层优化迁移到博弈模型的思维变化
做惯了单层优化的人,一开始建模容易把下层用户的某个约束直接写进上层的全局约束集合里。这种做法会让下层失去“独立决策”的意义。一个准则是:凡是下层用户自己能做决定的变量,例如用电量、转移负荷、削减量,都不能由上层直接固定,而只能通过价格信号影响。把它们变成上层变量后,KKT条件的平稳性和互补性都会失真。
我推荐的做法是建模前先用文字明确“谁是决策者、各自拥有哪些变量、哪些变量是对方参数”。这种“角色表”看起来琐碎,却能避免后期重大返工。
6.5 可扩展方向
跟纯粹低成本买设备或调储能套利的项目不同,博弈调度在真正工程落地时还涉及不确定性问题,例如新能源出力和负荷预测误差,建议在原模型中增加场景法或鲁棒优化,把不确定参数从确定值扩展成多个场景。另一个自然扩展是引入热、气等多能源耦合,上层主体同时制定电、热价格,下层用户对不同能价做联合响应。这时候模型会变大很多,但主从博弈的建模框架完全一样,只是KKT条件数量成倍增加。
如果后续需要跟实时控制系统对接,我建议先做模型预测控制的滚动窗口版本,把日调度的24时段决策变成一个滚动优化问题,根据新的预测数据不断更新电价和出力计划,这样更贴合运行实际,而不是停留在离线的开环优化结果上。
最后分享一个我经常用的检查技巧:在完成Matlab实现后,先设置一个极简案例,比如只有1个用户、2个时段、无储能、无需求响应,手动推导这个案例的均衡解,再和代码输出对比。如果连极简案例都对不上,那么大规模算例的结果再好都不可信;反之,调通极简案例后再逐步提升复杂度,整个调试过程会顺畅很多。希望这些经验能让你在实现主从博弈调度策略时少走弯路。