资讯动态

主从博弈下售电商多元零售套餐与多级市场购电策略Matlab实现

发布时间:2026/10/21 15:12:35 来源:尧图企业网站定制
做电力市场研究的朋友应该都有体会售电公司这行最头疼的两件事一是零售侧怎么设计套餐才能既吸引用户又不亏本二是批发侧怎么在多个市场里买电才能把成本压下来。而这两个问题偏偏又耦合在一起套餐定得不好用户的用电行为就会变得很难看购电成本跟着飙升购电策略不合理套餐定价又没有成本支撑。这就像左手和右手互相牵制单边优化怎么都绕不过去。今天想聊的这个项目——“基于主从博弈的售电商多元零售套餐设计与多级市场购电策略”恰好就是一套把这两个耦合问题同时解决的方案。它来自EI收录论文的模型复现用Matlab完整实现了主从博弈框架下的双层决策模型适合电力市场方向的研究生、售电公司从事策略分析的从业者以及正在学双层优化和博弈论的算法爱好者参考。我前前后后把这套代码改过几个版本踩过不少坑也总结了一些工程化落地的心得。下面直接按建模思路、核心模型、求解实现、Matlab实操排查这几个维度拆开讲尽量让你拿到代码时少走弯路。1. 项目整体设计与核心建模思路1.1 售电公司的双重身份决定了必须用博弈先把业务逻辑想清楚。售电公司在电力市场链条里的位置很特殊向下游用户卖电电价可以自己定向上游发电企业或批发市场买电电价只能被动接受。这种“上半场是价格接受者、下半场是价格制定者”的双重身份是理解这个项目所有建模逻辑的起点。如果只用传统优化方法通常的做法是把套餐设计和购电策略拆成两个独立问题先假定用户用电量是固定的优化购电成本再根据购电成本反推套餐价格。但问题在于用户从来不是固定负荷。你定了峰谷电价用户就会把负荷从高峰挪到低谷你设了阶梯电价用户就会卡着阶梯阈值控制用电量。用户的这些行为反馈又会反过来改变售电公司的购电曲线和成本。如果一开始没把这种反馈关系建进去算出来的套餐再好也是纸上谈兵。主从博弈Stackelberg博弈正是为这种“先决策、后响应”的层级关系设计的。售电公司先公布零售套餐是博弈中的领导者用户看到套餐后再调整自己的用电行为是跟随者。用户的最优反应又会作为约束进入售电公司的决策模型。这样一来两个层面的目标函数和约束条件互相嵌套形成一个完整的双层优化问题套餐和购电策略在同一个框架下联合求解。1.2 整体架构上层管套餐、下层管负荷、中间用市场衔接说到这套方案的建模结构可以用一句话概括上层售电公司决策零售套餐和购电计划下层用户决策用电量两者通过零售电价和用电负荷耦合购电策略再通过多级市场价格传导进来。具体来说上层模型的目标是最大化售电公司净利润包含零售收入、购电成本两大部分。决策变量包括零售套餐参数比如分时电价、阶梯价格、固定服务费和购电组合中长期合约电量、日前市场购电、实时市场调整。下层模型的目标是用户效用最大化决策变量是每个时段的用电量约束包括用电上下限、可转移负荷比例、总用电量范围等。这里需要特别注意上下层之间有双向的信息传递机制。上层把零售电价作为参数传给下层下层通过求解用电效用最优化问题把最优用电曲线返回给上层上层再在新负荷曲线下重新评估利润。整个模型算到最后要找到一个“用户对所有套餐方案的最优反应都是这个方案”的均衡点也就是Stackelberg均衡。从实现上讲这个双层结构不能直接交给求解器。常用的做法是把下层优化问题用KKT条件替换变成上层的约束从而把双层问题转化为单层混合整数规划。这个转化过程是这个项目的核心关键点后面我会详细拆。2. 零售套餐设计的建模细节与用户响应逻辑2.1 多元零售套餐怎么落到数学表达式里先说套餐设计。现实中售电公司的零售套餐花样很多但归纳起来核心的定价维度就那几个。这个项目里我重点讲三种常见类型在Matlab里的建模方式第一种是分时电价套餐。把一天24小时分成峰、平、谷三类时段每个时段对应不同电价。数学上就是一组分段常数函数c_t c_peak高峰时段、c_flat平段、c_valley低谷时段。决策变量是各时段电价约束条件包括电价上下限以及峰谷价差的合理区间——价差太小削峰填谷的激励不够价差太大用户可能会投诉或者选择别家。第二种是阶梯电价套餐。设定一个或者多个电量阈值用电量落在哪个区间就执行对应价格。这个模型的难点在于用户的总用电量一开始是未知的它取决于电价本身所以阶梯阈值、阶梯价、用户响应之间形成联动。在多层阶梯下建模需要用0-1变量判断用户落入哪个阶梯区间这也是模型变成MILP的主要原因之一。第三种是“固定服务费电量电价”的两部制套餐。每月固定收一笔服务费覆盖基础成本和固定投资然后每度电再收一个电量电价。这种套餐的好处是收入稳定坏处是用户会觉得“不管用不用电都要交钱”参与度可能打折。实际建模中这三种套餐往往是组合出现——比如用户可以选择分时电价套餐然后在这个基础上再叠加阶梯折扣。组合套餐的好处是用户面更广不同类型的用户工业用户、商业用户、居民用户都能找到适合自己的方案。2.2 用户用电响应模型效用函数和弹性矩阵用户侧模型的常见做法是用效用函数描述用户的用电满意度。最常用的二次效用函数形式是U(q) α·q - (β/2)·q²其中q是用电量α和β是效用参数α越大说明用户对用电的需求越迫切β越大说明用户的饱和效应越强。用户的最优用电量就是在每个时段使边际效用等于电价的位置即∂U/∂q 电价。从用户角度看目标函数是净效用最大化也就是用电效用减去电费支出。用户面临的问题可以写成max Σ[α_t·q_t - (β_t/2)·q_t² - p_t·q_t]s.t. 每个时段用电量上下限约束相邻时段负荷变化率约束总用电量范围约束。这套用户响应模型很经典它在经济学上对应消费者理论中的“需求函数”推导给定价格用户通过优化得到需求曲线。把用户的KKT条件推导出来得到的最优用电量和电价之间的关系本质上就是弹性系数矩阵的体现。实操中α、β这些参数的获取是难点。最可靠的方法是拿到真实的负荷数据用最小二乘法做参数辨识。如果项目阶段没有实测数据也可以参考同行业的负荷弹性研究报告来设定合理区间再用灵敏度分析验证结果对参数变化的稳定性。2.3 释放一点为什么主从博弈比“两阶段随机优化”更适合这里这里有必要展开一个容易混淆的点。很多人可能第一反应是这不是个随机优化问题吗市场价格不确定需求不确定用两阶段随机规划不就行了吗两阶段随机优化能做但它和主从博弈的视角不同。随机优化里的“不确定性”来自外部环境市场价格、新能源出力等决策者只是被动应对不确定性。而主从博弈处理的是“策略性互动”——用户不是天然的不确定变量他们会根据你的套餐主动调整行为。这属于内生反馈不是外生随机性。换句话说随机优化假设用户负荷是一个随机变量而主从博弈把用户看成有独立决策能力的理性主体。这个区别很关键因为它直接决定了模型的解读方式在主从博弈下套餐设计的每一分钱定价都要考虑到用户会“反击”而随机优化里没有这种策略对抗关系。3. 多级市场购电策略与双层模型转化3.1 中长期、日前、实时三级购电框架购电侧的多级市场结构可以从风险管理的角度去理解。电价波动风险大所以不能把宝全押在某一个市场上要分层配置。中长期合约市场对应的是“基本盘”。提前数月甚至一年锁定一部分电量价格锁定消除大部分价格风险。在模型里中长期购电量的特点是在日前决策之前就必须确定相当于第一阶段决策变量购电价格是已知的常数。日前市场对应的是“调整层”。在运行前一天根据负荷预测和新能源出力预测在中长期的基础上补足缺口或者卖出富余。日前市场电价是分时波动的通常用一组场景来表示不确定性。实时市场对应的是“纠偏层”。运行当天实际负荷和预测值不可能完全一致偏差部分通过实时市场平衡。实时电价往往比日前高用来惩罚预测不准造成的偏差。这三层市场放在同一个模型里的逻辑是中长期锁定的比例越高价格风险越小但电量结构可能越偏离实际需要导致日前和实时调整量变大反之中长期买太少日前价格大幅波动时风险又控制不住。这就是一个典型的风险-成本均衡问题。3.2 购电成本函数建模与耦合约束在模型里购电成本分三块C_total C_longterm C_dayahead C_realC_longterm 中长期合约电量 × 合约价格常数一次性决策C_dayahead Σ(日前购电量_t × 日前价格_t)每个时段都不一定但日前价格已知或场景已知C_real Σ(实时调节量_t × 实时价格_t)这里有个关键耦合约束每个时段的实际负荷q_t必须等于中长期电量、日前购电量和实时调整量之和考虑正负调节多买的部分算卖回市场。这个约束把用户侧的负荷决策和购电策略紧紧绑在一起。如果进一步考虑风险偏好可以在目标函数里加一个风险惩罚项比如用条件风险价值CVaR来控制最坏场景下的购电成本。项目的基础版本可以不加但对有CVaR要求的场景这套框架稍微改造一下就能扩展。3.3 KKT条件转化把双层问题掰成单层这是整个项目里最有技术含量的一步。前面提到上下层模型嵌套不能直接求解。业界最成熟的处理方法是KKT条件替换法把下层优化问题转化为它的KKT最优性条件作为上层模型的约束条件。具体来说下层用户问题是一个凸二次规划目标函数二次可微约束是线性的所以KKT条件是充要的。写下用户问题的KKT条件包含三部分拉格朗日函数对q_t的偏导等于0平稳性条件原可行条件和对偶可行条件互补松弛条件对偶变量乘以对应的约束松弛量等于0前两类是线性的直接加入上层模型即可。麻烦的是互补松弛条件——两个非负数的乘积等于0这是一个非线性等式。在Matlab里处理它的标准做法是用大M法引入0-1变量。如果原问题的某个约束是g(x) ≤ 0其对应的对偶变量是λ那么互补松弛条件λ·g(x) 0就等价于两个约束g(x) ≤ M·z λ ≤ M·(1-z)其中z是0-1变量M是一个足够大的正数。这样就把非线性项变成了线性不等式整个模型升级为MILP可以交给求解器处理。为什么这么麻烦还没有替代方案因为双层问题在理论上即使上下层都是凸问题整体仍然可能非凸KKT转化搭配MILP是目前商业求解器能稳定求解的可靠路径。学术界研究过其他方式比如迭代启发式算法但收敛性没有保证容易被局部最优卡死。这里有一个很重要的注意事项大M参数的选择直接影响求解质量。M太大会导致数值病态求解器容易产生不可行的伪解M太小又会错误地截断可行域。实操中我一贯的做法是先求解一个稍宽松的松弛问题根据对偶变量的量级来估计一个合理的M通常取对偶变量理论上限的10到100倍。4. Matlab实操过程与代码实现要点4.1 准备工作工具箱选择和脚本框架先说环境。这套代码我用的是YALMIP搭配求解器主用求解器是GUROBI或者CPLEX这类商业求解器都行没有的话用MATLAB自带的intlinprog也可以只是规模大时速度会差一些。YALMIP是一个建模工具箱它的优势是用数学表达式的角度写约束代码可读性非常高非常适合双层优化模型这类结构复杂的项目。写约束时几乎和数学表达式一样后面维护也方便。代码从结构上划分通常包含五个模块参数初始化脚本——所有原始数据的录入负荷参数、电价场景、用户效用系数下层用户决策问题的约束构建函数KKT条件转化函数上层主问题含KKT条件转化后的约束组装求解与结果输出模块4.2 数据准备与场景设计模型里需要准备的数据大致包括数据类别具体内容来源建议典型负荷曲线24小时基准负荷区分工作日/周末历史实测或模拟参考用户效用参数α、β序列负荷弹性回归或行业报告中长期合约价格单一常数市场合同报价日前市场价格24小时价格序列可能多场景历史日前市场出清价实时市场价格24小时价格序列历史实时市场出清价电价上下限零售电价可行区间政策监管要求实操里有几点建议。负荷曲线建议取夏/冬两个典型日分别建模再聚合比单日模型更稳定。日前电价场景如果数据充足用K-means聚类把历史电价聚成3~5个典型场景配上概率权重比简单取平均更接近真实分布。用户效用参数中的β系数控制在让用户需求的弹性在合理范围内一般价格弹性绝对值在0.1到0.5之间太激进的弹性会让模型振荡。4.3 核心代码实现分步拆解下面按YALMIP的写法给一个核心骨架方便你对照理解每个子模块的作用。首先是用户下层问题的KKT条件生成。如果用YALMIP的KKT命令可以直接生成但为了深入理解模型结构我建议先手写一遍% 定义用户决策变量负荷 q sdpvar(24, 1); % 24小时用电量 % 用户问题最大化效用 - 电费 % 目标函数 U alpha*q - 0.5*q*diag(beta)*q - price*q % 约束条件 Constraints_user []; Constraints_user [Constraints_user, q_min q q_max]; % 总用电量约束 Constraints_user [Constraints_user, sum(q) Q_total_min, sum(q) Q_total_max]; % 用KKT命令直接获取KKT条件 % [KKT_sys, details] kkt(Constraints_user, Objective_user, q);如果不用YALMIP的KKT自动生成工具手写方式如下% 拉格朗日乘子 lambda_low sdpvar(24, 1); % 下限约束的对偶变量 lambda_high sdpvar(24, 1); % 上限约束的对偶变量 mu_sum_low sdpvar(1); mu_sum_high sdpvar(1); % 平稳性条件alpha - beta.*q - price - lambda_low lambda_high - mu_sum_low mu_sum_high 0 Stationarity alpha - beta .* q - price - lambda_low lambda_high - mu_sum_low mu_sum_high 0; % 互补松弛条件用大M法 M_big 1000; z_low_low binvar(24, 1); z_high_high binvar(24, 1); % 对每个时段t % lambda_low(t) M * z_low(t) % (q_min - q(t)) M * (1 - z_low(t)) % lambda_high(t) M * z_high(t) % (q(t) - q_max) M * (1 - z_high(t))上层售电公司的问题就比较直接了。决策变量有零售电价、中长期电量、日前购电量、实时调节量等目标函数是收入减去三项购电成本。% 上层决策变量 price_retail sdpvar(24, 1); % 零售分时电价作为套餐的核心参数 E_long sdpvar(1); % 中长期合约电量常数 E_day sdpvar(24, 1); % 日前市场购电量 E_real sdpvar(24, 1); % 实时市场调节量正买负卖 % 目标函数零售收入 - 购电成本 Revenue sum(price_retail .* q); % 售电收入 Cost_long E_long * price_long; Cost_day sum(E_day .* price_day); Cost_real sum(E_real .* price_real); Profit Revenue - Cost_long - Cost_day - Cost_real; % 耦合约束 Constraints_top []; Constraints_top [Constraints_top, q E_long E_day E_real]; Constraints_top [Constraints_top, E_long 0, E_day 0]; % 实时调节量允许正负把上下层组装起来加上KKT条件和二进制变量生成MILP问题最后调用求解器% 组装完整问题 Constraints_total [Constraints_user_kkt, Constraints_top, ...]; Objective_total -Profit; % 最小化负利润 % 求解 ops sdpsettings(solver, gurobi, verbose, 2, mipgap, 0.01); optimize(Constraints_total, Objective_total, ops);这里main脚本的完整流程是加载参数→构建约束→求解→后处理出图。后处理部分建议至少输出三类结果最优套餐价格曲线随时间变化、用户负荷响应曲线对比优化前后的负荷曲线、成本和利润组成柱状图。这三张图是论文展示的核心图也是你判断结果是否合理的第一手依据。5. 常见问题与排查技巧实录5.1 求解不收敛或者找不到可行解这是双层优化项目里出现频率最高的问题。我的排查顺序很固定先检查大M设置。大M太大MILP的LP松弛界面会出现数值问题求解器会报“Numerical issues大M太小可行域被错误截断报“Infeasible problem”。我试过的经验值是让M尽量小但确保它比所有对偶变量的理论上限大10倍以上。判断方法先在有解但不含互补松弛条件的松弛问题里查一下对偶变量的量级再据此定M。其次检查二进制变量总数。24时段的双层问题加阶梯套餐的多级判断二进制变量很容易过百MILP一旦上了几百个二进制变量求解时间会指数级增加。如果求解器半小时还出不来建议先固定一部分设计变量比如先固定阶梯阈值只优化分时价格验证可行性后再放开。最后还要留意互补松弛条件的配对方向。KKT条件转化时一个常见的笔误是把原问题约束方向搞反导致互补松弛条件永远无法满足。这种问题没有语法错误但结果永远是infeasible。排查时需要回到原始约束逐一检查“原始松弛×对偶变量0”的配对关系。5.2 结果很奇怪套餐价格算出来几乎全是边界值如果你得到的分时电价要么贴着上限、要么贴着下限中间值很少大概率是模型里缺少足够强的平滑约束或者惩罚项。这种情况我遇到过核心原因是套餐价格对下层用户决策的杠杆作用太强模型倾向于采用“极限定价”策略。解决方法是加入约束限制峰谷价差或者对用户效用的β参数做调整。增加用户对电价的敏感性提高β会让需求曲线更富有弹性套餐价格变化对负荷的影响更明显模型才有动力去寻找中间价格。另一个更工程化的方法是给零售电价的变化范围设定一个窄一点的区间让模型被迫在区间内部做文章。5.3 灵敏度分析该怎么做重点看哪几个参数这个项目里值得做灵敏度分析的参数我建议重点看三个用户效用参数ββ增大意味着用户对电价更敏感套餐定价空间变小购电策略中日前市场的占比应该上升。日前市场价格波动幅度波动越大中长期合约配比应该越高。实时市场价格惩罚系数惩罚越高模型会约束日前购电尽量贴近实际负荷减少实时调整量。做法上不需要重新跑整套流程可以写一个循环脚本对每个目标参数从0.8倍基准值到1.2倍基准值扫一便画出关键输出指标净利润、套餐均价、负荷峰谷差的变化曲线。这组结果往往比主算例本身更有说服力审稿人和管理层都很吃这一套。5.4 参数合理性别让模型“算得漂亮但跑不通”最后说一个容易被忽略的问题模型参数和实际业务脱节。代码算出来的结果可能很漂亮——利润很高、负荷曲线很平稳——但仔细一看零售电价定为每千瓦时三块钱这在任何现实市场里都不可能被监管通过。模型漂亮不等于业务可行。我的习惯是在写完模型后做一次“业务合理性审查”每个决策变量的最优值都对照现实约束检查一遍看电价是否落在政策允许区间看中长期电量占比是否在合理范围内看用户负荷变化是否符合电价弹性常识。这一步不花太多时间但能避免在汇报时被问住。结语与实操心得整套项目做下来我最大的体会是主从博弈建模本身不难难的是工程化落地时那些“隐形的坑”——大M怎么取、互补松弛怎么配对、MILP规模怎么控制、参数怎么标定。这些经验在论文里往往一笔带过可恰恰是代码能不能跑通、结果能不能用的关键。如果你是第一次上手这类项目我建议不要一上来就追求模型的复杂性。先跑通一个最简版本分时电价套餐配单日前市场24时段10个用户聚合体不加阶梯不加实时市场。在这个骨架上验证KKT转化和MILP求解没问题了再逐步加功能模块。我自己的经验是一个能跑的简单模型远比一个跑不动的复杂模型有价值。最后分享一个平时不太会写在文档里的小技巧调试双层优化模型时优先验证“固定上层变量时下层能否复现出理论最优解”。拿一个已知结果的简单场景手动固定套餐价格用单独的优化器解下层用户问题再将结果与双层联合求解的用户部分对比。对得上说明KKT转化没问题对不上问题大概率出在互补松弛条件或大M配合上。这招在调试效率上的提升真的是谁用谁知道。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑