资讯动态

主从博弈驱动的电动汽车充电管理:动态定价与CPLEX求解实战

发布时间:2026/10/16 4:19:03 来源:尧图企业网站定制
搞电动汽车充电调度的同学一定遇到过这种尴尬运营商想靠高电价多赚钱用户看到电价高了就磨蹭到半夜再充两边对着干最后要么充电桩利用率惨淡要么负荷尖峰把小区变压器压得喘不过气。我之前用MATLAB做了一套基于主从博弈的智能小区充电管理代码核心思路就是让代理商当领导者先出动态电价电动汽车用户当跟随者再决定怎么充把这种对抗关系用Stackelberg博弈变成可求解的数学模型。这个项目从模型推导、Yalmip建模到CPLEX求解全部落地代码量不大但里面的推导细节和调参经验挺值得展开说说就想写出来分享给同样做电力系统仿真的朋友。内容方向偏智能电网、电动汽车充电管理方向的仿真科研也适合正在学双层优化和博弈论应用的人参考。如果你手头要复现类似的定价策略或者刚接触Yalmip和CPLEX这篇应该能帮你少走不少弯路。1. 这个项目在解决什么问题1.1 小区充电博弈的痛点先说你实际会遇到的场景。一个普通居民小区晚上六七点开始住户陆续回家把车插上充电桩。如果不加任何管理所有人都在这个时间段抢功率小区变压器容量很快就到顶了甚至可能出现跳闸风险。而充电代理商可以理解成运营充电桩的物业公司或运营商不希望变压器扩容那样成本太高更希望用户错峰充电把负荷平摊到半夜。问题在于用户没有动力配合。用户在意的是充电费用和第二天早上电量够不够哪个时段便宜就哪个时段充并不关心变压器是否过载。代理商如果只用一个固定电价那所有用户都会涌向同一个低价时段形成新的负荷尖峰如果电价定太高用户又可能直接不在小区充了代理商赚不到钱。这本质上是一个利益冲突问题。代理商有价格制定权用户有充电量选择权双方各自优化自己的目标互相影响。常规的单层优化没法处理这种互动关系因为它默认所有主体听统一调度。现实中用户不会完全听调度你让他十点再充他偏要七点就插上。所以需要一个能刻画你定规则我响应的建模框架主从博弈就是这个场景下的首选模型。1.2 为什么是主从博弈主从博弈也叫Stackelberg博弈核心特征是一方先行动另一方观察到行动后再响应。先行动的一方叫领导者后响应的一方叫跟随者。在这个项目里代理商是领导者它先公布未来24小时每个时段的充电电价电动汽车用户是跟随者看到电价后决定自己每个时段充多少功率。容易混淆的是这不是一个动态视频博弈而是一个同时决策但带有先后层级的静态优化问题。代理商在制定电价时就要预判用户的反应它心里有数电价提高一毛钱夜间时段的充电需求会下降多少电价压低五分钱高峰时段的负荷能转移多少。借助下层用户的优化模型可以把这种预判数学化为下层问题的最优解函数然后代回上层目标函数。这样一来代理商的定价决策就不再是拍脑袋而是建立在对用户理性响应的准确建模之上。为什么不用传统的双层规划其实主从博弈在数学形式上就是双层优化只是博弈论给了它更清晰的经济学解释上层目标最大化代理商利润下层目标最小化用户充电费用。两者通过价格和电量耦合。用双层优化的角度理解更容易上手用博弈论的角度理解更容易设计均衡分析二者不冲突。1.3 项目边界与适用场景这套代码针对的是单代理商、多用户、24小时静态定价的典型场景。代理商管理一个小区内的若干充电桩所有用户都有夜间充电需求代理商统一向电网购电再转售给用户。时间粒度设为1小时视作24个决策时段。用户车辆参数电池容量、初始SOC、到达离开时间已知且各异但代理商不做实时滚动优化而是提前一天制定全天电价。它适合用来做策略验证和学术研究比如分析不同用户规模下均衡价格的变化、对比博弈定价与分时电价的削峰填谷效果、观察价格上限对利润和社会福利的影响。如果要做在线实时控制还需要把模型改成模型预测控制的滚动框架交易间隔从1小时压缩到15分钟甚至5分钟这是后续扩展的方向。2. 主从博弈模型设计与数学推导2.1 系统架构与前提假设建模之前必须先定清楚系统里有哪些主体、哪些变量、哪些参数。这个项目里有两个核心主体充电代理商和电动汽车用户。代理商从电网以购电价买电同时向用户收取充电服务费最终用户支付的总价格就是代理商制定的动态电价购电价则参考当地分时电价结构在代码里作为已知参数输入。时间轴设置为1天24个时段每个时段长度为1小时。这个粒度对小区慢充场景完全够用因为慢充本身持续几个小时1小时的决策精度已经能反映错峰转移的效果。如果后续想做得更细改成96个15分钟时段模型结构基本不用动只是变量数量翻四倍。基本的简化和假设有四条。第一忽略充电桩的功率损耗和电池老化成本充电效率统一设为1。第二每辆车的充电功率连续可调范围从0到额定功率模拟慢充桩的调功特性。第三用户必须满足最低充电需求也就是离开时SOC要达到自己设定的目标值这是硬约束。第四传输线路损耗和电压约束暂时不考虑只把变压器容量作为总负荷上限。这些假设看似简化得很厉害但对策略层研究是合理的。充电管理研究的核心是电价引导下的负荷转移规律不是配电网络的潮流细节。如果谁一上来就把三相不平衡、谐波、线损全塞进去模型复杂到根本没法分析博弈关系得不偿失。2.2 上层代理商动态定价模型上层模型里代理商需要决策的就是24个时段的充电价格记为向量每个元素是对应时段的单位电价单位是元/kWh。代理商的目标是最大化全天利润利润等于售电收入减去购电成本。售电收入是所有用户在各个时段充电量的加权和权重就是对应时段价格。购电成本是总充电负荷乘以对应时段的购电价。这里的总负荷不是自由变量而是下层用户优化后的结果由所有用户最优充电策略叠加得到。也就是说代理商的利润函数里隐含了价格影响用户行为这一层关系。约束条件方面代理商不能随意定价。一方面要设置价格上下限避免极端高价损害用户利益这在现实中也对应政府对充电服务费的监管另一方面可以加一个利润率约束比如要求平均售价不低于购电成本的某个比例确保代理商可持续运营。实际操作中我倾向于只设价格上下限其余约束不加因为约束越多越容易出现下层可行性被破坏的问题。这里还要解释一个关键点为什么上层目标不是简单地在低谷时段降价、高峰时段涨价因为用户对高价的响应不是无限度的如果某个时段价格高到用户宁可少充电也不充代理商的总收入反而下降。所以定价问题本质上是寻找一个价格-需求平衡点这正是建模的价值所在。2.3 下层用户充电决策模型下层模型中每个用户是一个独立的小型优化问题。用户的决策变量是自己在24个时段内的充电功率曲线。用户的目标很朴素在自己接入充电桩的时间窗口内最小化总充电费用。每个用户的约束条件包括充电功率不能超过充电桩额定功率不在接入时间窗口内的充电功率强制为0离开时必须达到目标SOC对应的充电量需求。用数学语言描述就是把充电功率限制在0和额定功率之间并要求累计充电量不小于目标充电量。需要注意一个看似矛盾的点目标函数是最小化费用而目标充电量约束是不小于某个值所以用户理论上可能超额充电但超额充电只会增加费用不会带来任何收益因此最优解一定会精确停留在目标充电量上不会多充。这样不小于约束在实际解中等式自动成立。每个用户的到达时间、离开时间、电池容量、初始SOC、目标SOC、最大充电功率都是独立设定的。比如有的车电池容量40kWh初始SOC 30%目标SOC 85%那么最少需要补充22kWh电量另一辆车可能只需要10kWh它的充电灵活性就大得多可以在更便宜的时段轻松完成充电。这种用户之间的异质性是后面分析价格弹性差异的基础。2.4 双层问题如何转化为单层可解模型这个双层问题不能直接丢给求解器因为上下层各自有独立的目标函数和决策变量嵌套在同一个问题里。最经典的求解思路是把下层优化问题用KKT条件替换变成一个数学上等价的单层约束系统。下层用户优化问题是一个线性规划目标函数和约束都是线性的因此满足强对偶条件KKT条件是原问题最优解的充分必要条件。KKT条件包含四类内容拉格朗日函数对决策变量的导数为零即平稳性条件原始可行性条件对偶变量非负条件互补松弛条件。平稳性条件把上层电价变量和下层拉格朗日乘子关联起来这是上下层耦合的核心。互补松弛条件处理的是如果某个不等式约束不起作用则对应乘子必须为0这类逻辑关系。由于互补松弛条件是非线性的乘子乘以约束边界等于0需要引入二进制辅助变量并用大M法线性化。最终得到的单层问题是一个混合整数线性规划MILP包含连续变量价格、充电功率、拉格朗日乘子和二进制变量互补松弛条件指示。求解这个MILP就能得到Stackelberg均衡解。代价是模型规模变大但对几十辆车的规模CPLEX完全能胜任。3. MATLAB代码实现与调试细节3.1 环境准备与工具箱选择MATLAB代码的核心依赖是Yalmip优化建模工具箱和商用求解器。Yalmip的作用是把优化模型用接近数学公式的方式写出来避免手写大规模的稀疏矩阵大幅降低建模出错的概率。求解器方面推荐CPLEX或Gurobi两者都能高效求解混合整数线性规划。我用的是R2020a版本的MATLAB配CPLEX 12.10。Yalmip安装很简单把解压后的文件夹加入MATLAB路径即可。启动时可以用yalmiptest命令检查求解器是否被正确识别。常见的坑是装了Yalmip但MATLAB找不到CPLEX接口大概率是因为求解器安装时间在Yalmip之后重新启动MATLAB或者重跑savepath就能解决。如果没有商用求解器也可以先用MATLAB自带的intlinprog凑合跑小规模算例。Yalmip对intlinprog是支持的但求解速度比CPLEX慢很多而且数值稳定性要差一些几十辆车的算例可能会跑得很痛苦。做严肃的科研复现还是建议直接上CPLEX或Gurobi。3.2 代码结构与核心变量定义这个项目我把代码拆成几个文件主脚本负责整体流程参数文件集中定义所有车辆和电网数据模型构建函数负责在Yalmip中创建优化模型绘图函数负责结果可视化。这样拆的好处是改参数时不用翻主脚本跑敏感性分析也方便。核心变量的定义部分价格用p sdpvar(1, T)表示其中T是时段数24。每辆车的充电功率因为接入时段不同建议用元胞数组P{i} sdpvar(1, T)这样每辆车都有完整的24维变量向量。虽然各车有大量未接入时段变量最终被约束为0但用元胞数组写代码最直观后续优化性能时再改成只对有效时段建变量。拉格朗日乘子也对应定义下层约束中充电功率下限对应的乘子、充电功率上限对应的乘子、目标充电量约束对应的乘子分别用sdpvar创建。特别注意这些乘子必须加非负约束否则KKT条件就错了。二进制变量用于互补松弛条件的线性化数量大约等于车辆数乘以时段数再乘以2再加上车辆数。举例50辆车、24个时段光功率上界和下界的互补松弛就要引入2400个二进制变量这还能扛如果加到200辆车MILP规模会显著上升需要走聚合或分解的路线。3.3 关键代码段的实现思路先展示KKT平稳性条件的核心代码。对每个时段、每辆车价格乘以时间段长度与充电功率、对应乘子之间的关系要满足平稳性条件。用Yalmip写的话就是% 平稳性条件c - lam_low lam_up - mu 0 % c p(t) * dt for i 1:N for t 1:T Model [Model, p(t)*dt - lam_low{i}(t) lam_up{i}(t) - mu(i) 0]; end end这段代码的量级很小但它是上下层耦合的关键。lam_low和lam_up分别对应充电功率下界和上界约束的乘子mu对应目标电量约束的乘子三者都和该时段的充电价格直接挂钩。再来看互补松弛条件怎么用大M法线性化。以充电功率上界约束为例原始条件是如果某个时段的充电功率没有达到额定功率上限则上界对应的乘子必须为0。用二进制变量和两个不等式来模拟这个逻辑% 互补松弛lam_up * (P - Pmax) 0 % 用 z_up 变量指示是否达到上限 M_power 100; % 功率类大M for i 1:N for t 1:T Model [Model, lam_up{i}(t) 0]; Model [Model, lam_up{i}(t) M_power * z_up{i}(t)]; Model [Model, P{i}(t) - Pmax(i) M_power * (1 - z_up{i}(t))]; Model [Model, P{i}(t) - Pmax(i) 0]; % 这里其实是 P 的上界作为原始约束一部分 end end这里有个容易写错的地方P{i}(t) - Pmax(i) 0其实是上界约束本身它不能只说一半。在单层化之后所有原始约束都必须保留。所以写代码时要把原始下界约束、原始上界约束、原始电量约束都加进去再加上KKT条件。少任何一条原始约束解都可能偏离真实可行域。目标函数是整个模型最需要花心思的地方。如果直接把售电收入写成价格乘充电功率目标函数里会出现双线性项问题变成非凸的CPLEX没法保证全局最优。这里用强对偶定理做替换因为下层是线性规划原问题最优值等于对偶问题最优值于是可以把下层目标函数中的双线性项替换成对偶变量表达式。具体代码如下% 目标函数上层利润最大化 % 收入项用强对偶替换后的形式表达 revenue_expr 0; for i 1:N revenue_expr revenue_expr Ereq(i) * mu(i) - Pmax(i) * sum(lam_up{i}); end cost_expr sum(c_buy .* sum_power) * dt; Objective revenue_expr - cost_expr;这里的sum_power是所有车辆各时段功率之和c_buy是分时购电价。通过这个替换目标函数完全线性化模型变成标准MILP求解器可以稳定求出全局最优解。很多初学双层优化的人卡在这一步模型建好却跑不动就是因为没有意识到双线性项需要先处理。3.4 参数初始化怎么设最合理参数初始化直接决定结果合不合理也决定模型是否有可行解。我常用的参数设置如下车辆数量默认50辆电池容量在40到60kWh之间均匀分布最大充电功率统一取7kW对应单相慢充桩。每辆车的初始SOC取20%到50%均匀分布目标SOC统一设90%。到达时间集中在18:00到21:00随机生成离开时间集中在7:00到9:00随机生成。购电价参考典型分时电价结构白天高峰时段比如10:00-15:00和18:00-21:00电价较高夜间低谷时段比如23:00-次日6:00电价较低。代理商售电价格的上下限我通常设为购电价的0.9倍到1.3倍这样既能保证代理商有利润空间又不至于让用户费用高得离谱。有一个特别需要注意的参数用户电量需求不能设得太极端。比如初始SOC只有10%却要求离开时达到95%这辆车需要充50多度电在7kW额定功率下至少要充8个小时如果它的接入时间窗口只有6个小时模型直接无解。所以初始化时最好写一段校验逻辑自动检查每辆车的最大可充电量是否满足目标需求不满足就调整目标SOC而不是让求解器返回infeasible。4. 仿真结果分析与对比4.1 均衡结果的判读模型求解完成后第一个要看的是价格曲线和总负荷曲线的对应关系。正常的结果应该是夜间低谷时段电价相对较低对应充电负荷的集中区域高峰时段电价较高对应充电负荷的谷底。代理商通过这种价差引导用户把充电需求从晚间高峰转移到凌晨低谷。再看代理商的利润构成。收入端里目标电量对应的乘子乘以需求的累加项是大头而功率上界对应的乘子通常只在充电达到上限的时段才起作用。如果某个时段的乘子处处为0说明该时段的功率约束没有收紧充电功率没有触顶这个时段还有继续提高价格的余地。还要检查用户的充电策略是否合理。理论上每个用户都会在自己接入窗口内选择电价最低的时段充满需求电量。如果某个用户最终计费时段分布完全不符合这个规律大概率是KKT条件或者互补松弛条件写错了。我调试时经常把某辆车的功率曲线单独画出来和电价曲线叠在同一张图上一眼就能看出用户是否理性选择低价时段充电。4.2 用户规模变化时的表现模拟用户规模从30辆逐步增加到120辆观察均衡价格和负荷曲线的变化会发现很有意思的规律。用户数量少时单辆车的充电需求对总负荷贡献大代理商定价空间相对受限不敢定太高因为少一个用户就会显著影响收入。用户规模增大后个体用户的议价能力变弱代理商可以小幅抬高整体价格水平利润增长曲线比车辆数量增长得更快。另一个明显变化是负荷集中度。用户数量多时每个用户的充电窗口叠加在一起几乎覆盖全天代理商可以通过价格信号把负荷平滑地分散到多个时段变压器峰值负载率反而可能下降。但如果用户到达时间高度集中比如全部挤在19:00到20:00之间接入即使数量再多可转移的时段也有限削峰效果会打折扣。这个结果对实际运营有直接意义小区充电运营商的定价策略不能只看自己这一侧的成本还要看用户的到达规律和充电需求刚性。用户越集中、需求越刚性价格的调节空间越小用户越分散、需求越弹性价格工具的威力越大。4.3 与固定电价和分时电价的对比我做了三组对比实验固定电价全天不变、常规分时电价、本文的主从博弈动态定价。固定电价下用户全都在自己接入后的最低价时段充电但因为全天价格一样大家没有错峰动机结果就是晚间接入高峰时段的负荷冲得很高变压器很危险。分时电价好一些夜间低谷电价能引导部分错峰但问题是分时电价是静态的它不会考虑当前小区实际负荷水平。某天如果大量车辆都在低谷时段同时充电甚至低谷时段本身也可能出现过载。分时电价只管时段的价差不管小区变压器当前的剩余容量。主从博弈动态定价的优势在于价格直接响应所有用户的聚合需求。代理商在知道所有车辆参数和充电需求的情况下提前算出一个价格曲线这个曲线天然考虑了变压器容量约束和用户响应行为。三组实验对比下来博弈定价方案的负荷峰谷差最小同时代理商利润也没有比理想的分时电价场景低太多。也就是说用户视角看充电费用增幅有限运营商视角看削峰效果好、运营压力小电网视角看负荷曲线更友好。三方利益达到一个相对平衡的状态。5. 踩过的坑与排查经验5.1 不可行解与求解器报错跑MILP最怕的就是求解器直接返回Problem is infeasible不可行。排查不可行问题第一步不是改约束而是把模型拆开先固定上层价格单独求解下层用户模型看下层模型本身是否有可行解。如果单车跑就有问题那就是参数初始化出错比如前面说的充电需求超过可充电时长如果单车没问题那就是上下层耦合时加了过多的价格约束把下层可行域挤没了。另一个常见原因是互补松弛条件里的大M取值太小导致二进制变量的逻辑约束把原本可行的方案剪掉了。遇到这种问题可以先把互补松弛条件整体注释掉只保留原始约束和KKT平稳性条件再求解一次。如果能得到合理的连续松弛解说明瓶颈就在互补松弛条件的建模细节上。5.2 大M参数的选择技巧大M法的核心是选一个足够大的常数让二进制变量取0时对应的不等式自然满足取1时强制对应等式成立。M太小会误伤可行域M太大则会导致数值病态求解器在判定整数变量和连续变量关系时可能出现精度问题。功率相关约束的大M不需要很大因为充电功率本身有上界取一个比最大充电功率大10倍左右的值就行。我在50辆车、7kW额定功率的场景下用100完全够用。电量相关约束的大M与电池容量、目标SOC相关我习惯取1000因为充电电量也就是几十千瓦时量级1000给了足够松弛度没有出现过数值问题。一个实用经验是M能用精确边界算出来就不要偷懒取固定大数。比如功率上界互补松弛条件里的M可以写成Pmax(i)本身因为它是该变量理论上限。精确边界能提升求解速度还能减少数值警告。5.3 目标函数里的双线性项怎么消这点前面提过实际操作时是让很多初学者崩溃的地方。初始建模时很自然地写出价格乘以功率作为售电收入丢给Yalmip后发现报错说目标函数非凸或者CPLEX直接警告无法保证最优解。这个双线性项必须处理。处理方式是强对偶替换前提是下层优化是线性规划。因为线性规划强对偶成立原问题目标最优值等于对偶问题目标最优值于是把下层目标函数中用连续变量和价格构成的乘积项替换成对偶变量表达式。替换之后整个上层目标函数变成线性函数模型从非凸问题变成MILP。这里要特别提醒如果下层模型里添加了二次惩罚项比如为了模拟用户舒适度而加入充电功率平方项下层就不是线性规划而是二次规划强对偶替换依然可以部分使用但对偶问题形式会更复杂。为了保证代码简单和求解稳定我的项目把下层目标保持为纯线性。5.4 大规模算例提速方案当车辆数量超过100辆时MILP的求解时间可能从几十秒飙升到几分钟甚至更久。提速方案有很多我最常用的是用户聚合。先把用户按电池容量、初始SOC、到达离开时间做聚类每一类用户用一个聚合充电变量表示最后再把单辆车的功率约束改写成聚合功率约束相当于在数学上把多个相同特征的用户合并成一个代表用户。聚合后模型变量数量大幅下降求解速度可以提升一个数量级但代价是用户异质性刻画变差。一个折中方案是保持每个用户的原始模型但只把每辆车接入窗口外的变量不创建而不是先创建再约束为0。这样相当于把很多一直为0的变量从模型里删掉规模也能显著减小。另外可以试试给求解器设置合理的运行参数。比如CPLEX的mipgap从默认值放松到1%求解时间经常能缩短一半以上而结果差异在可接受范围内。科研场景下如果只需观察趋势而非追求极致精度这个方法性价比很高。最后再分享一个我调试时常用的小技巧先把模型里所有整数变量清空把它当成线性规划求解一次把松弛解打印出来。这个连续松弛解往往能暴露模型里约束方向写反、变量符号定义错误这类低级问题而且排查速度比直接看复杂MILP结果快得多。等你把连续松弛调顺了再恢复整数变量求精确解思路会清晰很多。这套项目如果后续想继续扩展可以考虑加入V2G双向充电、屋顶光伏和储能系统让代理商在定价时同时调度放电功率和储能充放策略博弈框架不变只是下层用户的决策空间更丰富。先把这个基础模型吃透后面的扩展都是水到渠成的事。

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

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

免费获取报价 →
↑