资讯动态

基于主从博弈的售电套餐定价与购电策略MATLAB复现

发布时间:2026/10/18 20:47:56 来源:尧图企业网站定制
简介这份资源是《基于主从博弈的售电商多元零售套餐设计与多级市场购电策略》论文的MATLAB复现代码包面向电力市场、综合能源与博弈论方向的研究生、科研人员及算法工程师帮助读者理解售电商与多元用户之间的主从博弈建模思路并复现论文中的算例仿真结果。压缩包共10个文件约1.44MB包含6个m脚本、2个mat数据文件、1份pdf说明文档和1个p文件分别对应主从博弈求解、粒子群优化、用户套餐选择、价格决策等核心模块数据文件用于存放典型电价与仿真结果。资源围绕峰谷分时电价、昼夜用电捆绑、峰谷惩罚补偿、阶梯递增电价、固定单一电价五类零售套餐展开结合日前、月度、年度多级市场购电场景采用粒子群算法与CPLEX求解纳什均衡。目前已有373人学习下载适合希望快速掌握主从博弈在电力零售中建模流程与代码实现细节的读者参考。1. 售电商套餐定价为什么总在“拍脑袋”与“被套利”之间反复横跳做售电侧优化的人大多有过这种体验套餐折扣率定低了用户不买账定高了用户全跑来套利中长期市场一签就亏。更麻烦的是零售套餐不是孤立决策——它直接决定你在中长期、日前、实时三级市场里怎么报量报价。很多模拟项目里把套餐设计和购电策略分开做结果就是零售侧算出来的“最优折扣”到了市场侧根本执行不了。这篇要拆的就是基于主从博弈把这两层耦合起来的一套 MATLAB 复现路径上层售电商给多元零售套餐分时电价、固定折扣、阶梯返利等下层用户按自身负荷和风险偏好选套餐并调整用电博弈均衡点同时给出购电组合。适合做电力市场、综合能源、需求响应方向的同学和工程师尤其是需要一套能跑通、能改参数、能出图的代码骨架的人。下面按“模型怎么立 → 代码怎么搭 → 参数怎么调 → 坑在哪”的顺序讲透。2. 主从博弈建模谁先动、谁跟、均衡怎么定义2.1 为什么用主从博弈而不是单层优化单层优化把售电商和用户当成一个整体目标函数直接加总得到的“最优”在现实中不可执行——用户不会配合你牺牲自己的电费。主从博弈Stackelberg把售电商设为领导者先公布套餐集合用户是跟随者在给定套餐下选择使自己用电成本最小、满意度最高的方案。均衡条件是售电商在预判用户最优反应的前提下没有动机单方面调整套餐用户在给定套餐下也没有动机换套餐或改负荷。这个结构天然对应零售侧的实际决策顺序也是这套代码能同时输出套餐参数和购电曲线的根本原因。常见做法是用逆向归纳求解先解下层用户问题得到每个套餐下的最优负荷响应和选择概率再把这个反应函数代回上层把双层问题转成带均衡约束的单层优化。MATLAB 里可以用 fmincon 做外层下层用线性规划或二次规划快速求解迭代到套餐参数不再变化为止。2.2 三层市场的购电决策怎么嵌进上层售电商的购电渠道一般拆成三层中长期合约、日前市场、实时平衡市场。中长期锁定大部分电量价格低但偏差考核严日前市场补足预测偏差价格波动大实时市场兜底价格最高但灵活。上层目标函数是售电利润期望约束包括各市场购电量非负、中长期签约比例上下限、实时市场偏差惩罚、以及零售侧电量平衡。一个容易忽略的点是用户对套餐的响应会改变负荷曲线进而改变各市场的购电量分配。所以上层每调整一次套餐下层就要重新求解一次用户响应再更新购电组合。这个嵌套关系是整套代码的计算量主要来源也是后面调参时最需要关注的地方。2.3 均衡存在性与求解终止条件主从博弈的均衡不一定存在尤其在用户选择模型是非凸的时候。工程上一般做两件事一是把用户选择概率用 Logit 模型平滑化避免离散选择带来的跳变二是设置最大迭代次数和套餐参数变化阈值比如相邻两次迭代的折扣率变化小于 1e-4 就认为收敛。如果跑满迭代还没收敛优先检查下层用户问题的可行域是不是随套餐变化出现了空集而不是盲目加迭代次数。3. 用 MATLAB 搭出可复现的双层求解框架3.1 数据准备与参数结构体设计先把所有输入整理成一个结构体后面调参只改这个结构体不动主逻辑。典型字段包括用户数量、各用户的基础负荷曲线、电价弹性系数、套餐候选集合、三层市场的价格预测序列、中长期签约比例上下限、实时偏差惩罚系数。% params.m —— 所有可调参数集中在这里 params.Nuser 50; % 用户数量 params.T 24; % 调度时段小时 params.baseLoad rand(params.Nuser, params.T) * 2 0.5; % 基础负荷 MW params.elasticity 0.15 * ones(params.Nuser,1); % 电价弹性 params.nPlan 3; % 套餐数量 params.discountRange [0.85, 1.0]; % 折扣率搜索范围 params.midRatio [0.6, 0.9]; % 中长期签约比例约束 params.priceMid 320 * ones(1, params.T); % 中长期电价 元/MWh params.priceDA 380 40*sin((1:params.T)/24*2*pi); % 日前价格 params.priceRT 450 80*rand(1, params.T); % 实时价格 params.penaltyRT 1.5; % 实时偏差惩罚系数逻辑说明baseLoad 用随机数只是占位实际复现时替换成某地区典型日负荷或某模拟项目的负荷数据。elasticity 控制用户对电价的敏感程度值越大下层响应越剧烈上层越容易震荡。discountRange 和 midRatio 是上层优化的边界设得太宽会导致求解时间暴涨建议先用窄区间跑通再放宽。3.2 下层用户最优响应Logit 选择加负荷调整下层对每个套餐求解用户的最优用电量和选择概率。用电量调整用一个简单的二次规划目标是最小化电费加不舒适度惩罚约束是总用电量不低于基础负荷的某个比例。function [prob, loadOpt] lowerLevel(plans, params) % plans: nPlan x T 的套餐电价矩阵 % 返回每个用户选择各套餐的概率和对应最优负荷 prob zeros(params.Nuser, params.nPlan); loadOpt zeros(params.Nuser, params.nPlan, params.T); for i 1:params.Nuser utility zeros(1, params.nPlan); for k 1:params.nPlan % 二次规划min 电费 不舒适度 H diag(2*params.elasticity(i)*ones(params.T,1)); f plans(k,:); A -eye(params.T); b -0.7*params.baseLoad(i,:); lb zeros(params.T,1); ub 1.5*params.baseLoad(i,:); loadOpt(i,k,:) quadprog(H, f, A, b, [], [], lb, ub, [], ... optimoptions(quadprog,Display,off)); cost plans(k,:) * squeeze(loadOpt(i,k,:)); utility(k) -cost 0.1*randn(); % 加随机项模拟偏好差异 end expU exp(utility - max(utility)); prob(i,:) expU / sum(expU); end end逻辑说明H 和 f 构成二次目标第一项是不舒适度第二项是电费。A 和 b 保证用电量不低于基础负荷的 70%避免用户为了省钱把负荷压到不现实的水平。ub 允许上浮 50%给需求响应留空间。Logit 里的随机项模拟用户偏好异质性去掉它会导致所有用户选同一个套餐上层优化失去意义。3.3 上层售电利润最大化把用户反应代回上层用 fmincon 搜索套餐折扣率和各市场购电量。目标函数里零售收入按用户选择概率加权购电成本按三层市场价格和购电量计算再加实时偏差惩罚。function profit upperLevel(x, params) % x 前 nPlan 个是折扣率后面是各市场购电量比例 discount x(1:params.nPlan); midRatio x(params.nPlan1); plans discount * ones(1, params.T) .* (params.priceDA * ones(1,params.nPlan)); [prob, loadOpt] lowerLevel(plans, params); retailLoad zeros(1, params.T); for i 1:params.Nuser for k 1:params.nPlan retailLoad retailLoad prob(i,k) * squeeze(loadOpt(i,k,:)); end end totalLoad sum(retailLoad); midQty midRatio * totalLoad; daQty (1 - midRatio) * 0.7 * totalLoad; rtQty totalLoad - midQty - daQty; revenue sum(retailLoad .* mean(plans,1)); cost midQty*mean(params.priceMid) daQty*mean(params.priceDA) ... rtQty*mean(params.priceRT) params.penaltyRT*abs(rtQty)*mean(params.priceRT); profit -(revenue - cost); % 取负供 fmincon 最小化 end逻辑说明plans 的构造把折扣率映射成各套餐的分时电价实际复现时可以改成更复杂的套餐结构比如阶梯返利或固定折扣加峰谷价。midRatio 是中长期签约比例daQty 和 rtQty 按剩余电量拆分这里用 0.7 是经验值表示日前市场覆盖剩余电量的七成剩下三成进实时。penaltyRT 乘实时电量绝对值模拟偏差考核。目标取负是因为 fmincon 默认求最小。3.4 主循环与收敛判断把上下层串起来外层用 fmincon内层每次调用 lowerLevel迭代到套餐折扣变化小于阈值。x0 [0.95*ones(1,params.nPlan), 0.75]; lb [params.discountRange(1)*ones(1,params.nPlan), params.midRatio(1)]; ub [params.discountRange(2)*ones(1,params.nPlan), params.midRatio(2)]; options optimoptions(fmincon,Display,iter,MaxIterations,50,... StepTolerance,1e-4,ConstraintTolerance,1e-4); [xOpt, fval] fmincon((x) upperLevel(x, params), x0, [], [], [], [], lb, ub, [], options); fprintf(最优折扣率: %s\n, mat2str(xOpt(1:params.nPlan),3)); fprintf(中长期比例: %.3f\n, xOpt(params.nPlan1));逻辑说明x0 给一个接近 1 的折扣和 0.75 的中长期比例避免从边界出发导致下层不可行。MaxIterations 设 50 是保守值实际跑通后可以加到 200。StepTolerance 和 ConstraintTolerance 控制收敛精度太小会卡住太大会得到粗糙解。跑完后 xOpt 就是均衡套餐参数和购电比例。4. 参数怎么调折扣率、弹性系数与市场比例的联动4.1 折扣率搜索范围决定求解成败折扣率下限设到 0.85 以下下层用户会大量涌向低价套餐上层零售收入被压垮fmincon 容易在边界震荡。建议先用 [0.9, 1.0] 跑通观察最优折扣落在哪再决定是否放宽。如果最优折扣贴着下限说明用户弹性设大了或者市场竞争太激烈这时候要回头检查 elasticity 的取值是否符合实际。4.2 弹性系数与用户数量的匹配elasticity 是下层响应的核心参数。值太小用户对电价不敏感套餐设计失去意义值太大下层负荷曲线剧烈变形上层购电组合跟着跳。经验做法是先用 0.1 跑一遍看用户负荷调整幅度是否在基础负荷的 20% 以内超过就调小。用户数量 Nuser 增加时下层计算时间线性增长但上层目标函数的随机性会下降收敛反而更稳。如果跑一次超过十分钟先把 Nuser 降到 20 做调试。4.3 中长期比例与实时惩罚的平衡midRatio 下限 0.6 是很多市场的常见约束上限 0.9 防止售电商把全部电量锁在中长期失去灵活性。penaltyRT 设 1.5 表示实时偏差按 1.5 倍实时电价惩罚这个值直接决定售电商愿不愿意在日前市场多买。如果发现最优解里实时购电量接近零说明惩罚设高了如果实时购电量占比超过 30%说明惩罚设低了或者日前价格预测太保守。5. 避坑与排查跑不通、不收敛、结果反直觉时看哪里5.1 下层 quadprog 报不可行现象主循环跑几步就报 “No feasible solution found”。原因套餐电价过高时用户被约束在最低用电量 70% 以上电费可能超过其承受能力但模型里没有收入约束实际是 ub 或 lb 设置与基础负荷不匹配。解决检查 baseLoad 是否有零值或负值把 lb 从 0.7 降到 0.5或者在目标里加一个松弛变量惩罚不可行度。5.2 上层 fmincon 在边界反复震荡现象迭代输出里折扣率一直在上下限之间跳不收敛。原因下层 Logit 的随机项每次调用都重新生成导致上层目标函数不光滑。解决把随机项固定成一个种子或者在主循环外预生成一组用户偏好参数传入 lowerLevel 复用。5.3 最优套餐所有用户都选同一个现象prob 矩阵某一列全是 1其他接近 0。原因套餐之间差异太小或者 Logit 的效用尺度没调好。解决拉大套餐折扣率差异或者在 utility 里加一个与套餐特征相关的固定偏好项比如分时电价套餐给夜间用电多的用户额外权重。5.4 购电组合里实时市场占比异常高现象xOpt 里实时购电量超过总负荷的 40%。原因日前价格预测偏低或者实时惩罚系数设得太小。解决检查 priceDA 和 priceRT 的均值关系实时价格均值应该明显高于日前把 penaltyRT 从 1.5 提到 2.0 再跑。5.5 收敛后利润为负现象fval 是正数因为取了负说明售电利润为负。原因折扣率太低加上中长期签约比例过高零售收入覆盖不了购电成本。解决先检查 priceMid 是否设得比零售电价还高如果是调低中长期价格否则收紧折扣率下限或者允许售电商在实时市场少买。6. 进阶技巧用场景法验证均衡稳健性跑通一次均衡不代表模型可靠售电商最怕的是套餐设计只在某一条价格曲线上成立。我一般会做场景扫描把日前和实时价格按历史波动范围生成 50 组场景每组跑一次主从博弈看最优折扣率和购电比例的分布。如果折扣率标准差超过 0.03说明套餐设计对价格预测太敏感实际落地要留安全边际。nScene 50; discountRec zeros(nScene, params.nPlan); midRec zeros(nScene, 1); for s 1:nScene params.priceDA params.priceDA .* (1 0.1*randn(1,params.T)); params.priceRT params.priceRT .* (1 0.15*randn(1,params.T)); [xOpt, ~] fmincon((x) upperLevel(x, params), x0, [], [], [], [], lb, ub, [], options); discountRec(s,:) xOpt(1:params.nPlan); midRec(s) xOpt(params.nPlan1); end fprintf(折扣率均值: %s标准差: %s\n, mat2str(mean(discountRec),3), mat2str(std(discountRec),3));这段代码把价格场景扰动加进去每次重新求解。折扣率标准差大说明套餐参数需要加鲁棒约束比如在目标函数里加一个方差惩罚项或者把折扣率限制在一个更窄的区间。midRec 的分布还能告诉你中长期签约比例在价格波动下是否稳定如果经常贴着上限说明售电商有强烈的锁量动机实际谈判时可以争取更有利的中长期价格。另一个技巧是把用户按行业拆成几类每类给不同的 elasticity 和基础负荷曲线再跑主从博弈。这样得到的套餐集合更接近真实零售产品也能看出哪类用户对折扣最敏感、哪类用户更看重分时电价。我自己的习惯是先用单类用户跑通全流程确认收敛和利润合理再逐步加用户类型每加一类就检查一次下层可行性和上层收敛速度。这套代码骨架不复杂难的是参数之间的联动关系调通一次之后换数据、换市场规则都能复用。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑