充电服务商在定价上最爱用的一套打法是什么?很多人第一反应是:峰时卖贵一点,谷时卖便宜一点,两头一拉,利润自然就出来了。但这个经验法则放在智能电网和电动汽车充电场景里,经常撞得头破血流。我接手充电定价这个项目之前,也是这么想的,结果被几个仿真结果教育了一整轮。你把谷时电价压到最低,用户确实趋之若鹜,但代价是谷时段出现新的负荷尖峰,变压器容量直接报警;你把峰时电价抬高,又可能把用户推到隔壁充电站,单站收益不升反降。后来我才意识到,这事本质上是一个定价方和用电方之间的主从博弈问题——充电站先出价,用户再按价决定怎么充,双方目标不一致,但又互相依赖。
这篇文章把我从建模到求解、再到仿真验证的全过程做一个完整复盘,包括主从博弈模型的数学结构、双层优化怎么用KKT和强对偶转化成可求解形式、以及仿真里踩到的几个反直觉坑。不管你是做充电运营、搞电力系统优化,还是正在写相关方向论文的研究生,这里面讲的思路和代码级细节,应该都能帮你少走不少弯路。
1. 充电定价为什么会上博弈论——从"定个价"到"两难问题"
1.1 充电站和用户之间其实是"追价博弈",不是单向定价
传统定价模型里,充电站是价格的制定者,用户是价格的接受者,看起来是一个自上而下的单向过程。但现实里用户没有那么被动——他可以在不同时段之间选择,可以在不同充电站之间选择,甚至可以决定"今天不充了,明天到单位再充"。也就是说,你定的电价会影响用户行为,用户行为反过来又决定你的收入和电网负荷曲线。
这就是 Stackelberg 博弈的标准结构。充电服务商作为领导者(Leader),先公布分时价格;电动汽车用户作为跟随者(Follower),在给定价格下优化自己的充电计划。两者不是同时决策,而是有先后顺序的,领导者必须把跟随者的最优反应提前算进自己的决策里。这个问题和传统定价还有一个关键区别:充电站的购电成本不是固定的,它从电力市场买电,随时间和负荷变化。用户响应导致负荷集中,购电成本上升,站内收益就会被侵蚀。所以充电站定价时面对的其实是两个叠加的博弈:一个是对用户的定价博弈,另一个是对电力市场的购电博弈。
1.2 固定分时电价为什么不够用:静态曲线的两宗罪
很多充电站现在用的静态分时电价(TOU)看起来已经在引导用户错峰了,早高峰贵、午间平、夜间便宜。但它的根本问题是"静态"——价格曲线事先定死,不会随用户实际响应而调整。我见过一个真实的运营数据曲线:某站点推行谷时优惠电价后,凌晨 0 点到 4 点的充电量暴涨了三倍,结果变压器在凌晨 2 点接近满载,而原本预期的"削峰"效果却因为用户争相挤入谷时段而大打折扣。这就是静态 TOU 的第一个罪状——它没有考虑用户的聚合响应会改变负荷分布,造成峰谷倒挂。
第二个罪状是它无法体现充电站的利润空间。固定价格下,充电站赚的钱基本等于售电收入减去购电成本,如果购电成本曲线波动剧烈,最优售价也应该跟着变。静态 TOU 预先钉死价格,等于放弃了对购电成本波动的对冲能力。而主从博弈定价的核心价值,就是让价格曲线"内生"出来——它不再是人拍脑袋定的几个时段数字,而是求解一个优化问题得到的最优响应策略。价格本身变成了一个调度工具,既能引导负荷、又能保护收益。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主从博弈模型搭建:谁先出牌,谁跟牌,约束条件怎么理
2.1 上层领导者建模:收益最大化下的购电与定价约束
先假设一个足够聚焦的场景:单个充电站,服务一群电动汽车用户,所有车都停在这个站的充电位或者受这个站调度。上层决策者是充电服务商,它的目标是在满足配电约束的前提下最大化总收益。
我要先定义几个变量。设时段集合为 T,每个时段长度为 Δt。上层决策变量有两个:每个时段的充电服务价格 p_t(元/kWh),以及每个时段从配电网购入的功率 P_t^buy(kW)。上层的目标函数可以写成:
max Σ_t [ p_t × L_t - c_t × P_t^buy ]
其中 L_t 是该时段所有用户的总充电负荷,c_t 是时段 t 的购电价格。这里注意 L_t 不是上层直接控制的变量,它是用户对价格 p_t 的整体响应。约束条件包括:购电功率不能超过变压器容量上限 P_max;充电服务价格有上下限,p_min ≤ p_t ≤ p_max;购电量等于充电负荷加上充电站自身的基础损耗和基础负荷。
这个模型直观上就是在问:我定什么价格,才能让"收入减去购电成本"最大,同时不超过电网给我的容量红线?但如果把 L_t 当成普通变量直接塞进目标函数,那就完全错了。L_t 背后是每个用户在既定价格下的最优决策,这个决策由下层问题决定。
2.2 下层用户建模:一个必须保持线性的充电成本最小化问题
下层是用户群。每个用户 n 有自己的车辆参数:电池容量 Cap_n、最大充电功率 P_n^max、到达时段 a_n、离开时段 d_n、到达时的 SOC 和目标 SOC。用户会在停车窗口内决定每个时段充不充、充多少,目标是最小化自己的充电成本。
用户 n 的优化问题可以写成:
min Σ_t p_t × x_{n,t} × Δt
s.t. 0 ≤ x_{n,t} ≤ P_n^max × avail_{n,t}
Σ_t x_{n,t} × Δt ≥ E_n
SOC_min ≤ SOC_{n,t} ≤ SOC_max
这里的 x_{n,t} 是用户在时段 t 的充电功率,avail_{n,t} 表示该用户是否停在站内(只有 a_n 到 d_n 之间为 1),E_n 是完成这次充电需要的总电量。SOC 的递推关系可以写成 SOC_{n,t}=SOC_{n,t-1}+x_{n,t}×Δt/Cap_n,这也是线性约束。
这里有一个我在实际建模时反复强调的点:下层问题必须保持为连续变量的线性规划(LP)。如果往里面加"用户必须在离开前充满"这样的逻辑,一般不会破坏线性;但如果加入"充电过程不可中断"或者"只能选择快充或慢充"这样的 0-1 变量,下层就变成混合整数规划(MILP),后面整套 KKT 转化方案就直接失效。这是个非常隐蔽的建模陷阱。我见过好几篇论文为了追求精细,把下层建模成 MILP,结果求解时只能用启发式迭代,完全丧失最优性保证。如果场景确实需要不可中断约束,我建议先做松弛近似,或者把时段粒度放大到可接受的近似范围。
2.3 双层耦合既然躲不掉,就把它看成一个带均衡约束的问题
上下两层之间的耦合非常直接:上层目标里的总负荷 L_t = Σ_n x_{n,t},但 x_{n,t} 是下层用户对电价 p_t 的最优响应。政策的难点就在于这个循环依赖:上层想让用户少在峰时充电,于是提高峰时价格;但用户看到价格提高后,会把充电需求搬到谷时;谷时负荷增加,谷时的购电成本上升,上层谷时段的利润空间又被压缩。
这就是典型的带均衡约束的数学规划问题(MPEC),也叫双层优化。它不是两个独立优化问题的简单叠加,而是一个问题嵌套在另一个问题里面。如果你尝试用一个迭代算法反复"上层定价→下层响应→上层再调价",运气好能收敛,运气不好就是在两个解之间来回震荡。所以更可靠的路线是把它一次性转成单层模型,用商业求解器直接求解,这也是下一章要展开的核心内容。
3. 把双层问题真正解出来:KKT、强对偶、Big-M的实战配合
3.1 用KKT条件把下层"降维"成约束集合
解决双层优化的经典思路,是把下层问题用它的 KKT 条件替换掉。因为下层是线性规划,KKT 条件既是必要的也是充分的,下层的最优解完全等价于满足 KKT 条件的一组变量。这样一来,原本嵌套的下层优化问题就变成了上层问题的附加约束集合。
下层 LP 的 KKT 条件包含四个方面:稳定性条件(stationarity)、原始可行性、对偶可行性、互补松弛条件。其中前三个都是线性约束,真正麻烦的是互补松弛条件——它要求对偶变量和约束松弛量至少有一个为 0,也就是两个非负量的乘积等于 0,这是一个非线性约束。
以用户 n 的功率上限约束为例。功率约束可以写成 x_{n,t} ≤ P_n^max,其对应的对偶变量是 μ_{n,t},互补松弛条件就是 μ_{n,t} × (P_n^max - x_{n,t}) = 0,同时 μ_{n,t} ≥ 0。这个乘积形式的约束没法直接丢给线性规划求解器,解决它的标准手段是引入一个 0-1 辅助变量 z,用 Big-M 把它线性化:
μ_{n,t} ≤ M × z
P_n^max - x_{n,t} ≤ M × (1 - z)
当 z=1 时,μ 可以不为 0,但功率松弛量被强制为 0;当 z=0 时,功率松弛量可以不为 0,但 μ 被强制为 0。这样就精确表达了"两者至少一个为 0"的逻辑。
3.2 双线性项与强对偶替换:这是一个绕不开的痛点
KKT 转化完成之后,你以为模型已经可以丢给求解器了?还差一步。上层目标函数里有一个惩罚性的双线性项:p_t × L_t。L_t 是用户最优响应的函数,经过 KKT 变换后,x_{n,t} 会和对偶变量、0-1 变量一起出现在模型里。于是目标函数里就出现了 p_t × x_{n,t} 这种两个连续变量相乘的项,模型变成非凸的二次约束规划,Gurobi 和 CPLEX 对这种问题会很痛苦,全局最优性也无法保证。
解决这个问题的利器是强对偶定理。因为下层用户问题是线性规划,在强对偶成立的条件下,用户的最小充电费用等于其对偶问题的最大值。也就是说,用户 n 的充电支出可以用对偶变量来表示,而不再需要显式出现 p_t × x_{n,t}。具体来说,用户充电问题的对偶目标函数可以写成:
Σ_t p_t × x_{n,t} × Δt = E_n × α_n + Σ_t P_n^max × μ_{n,t} × Δt
这里的 α_n 是用户充电需求约束 E_n 对应的对偶变量,μ_{n,t} 是功率上限约束对应的对偶变量。代入上层目标之后,p_t × x_{n,t} 就被替换成了只含对偶变量的表达式,双线性项彻底消失。整个模型变成一个混合整数线性规划(MILP),可以交给任意主流 MILP 求解器求全局最优解。
这个替换是整篇文章最关键的技巧。我最初实现时就是漏了这一步,模型丢给 Gurobi 后,求解器一直报目标函数非凸,卡了很久。后来翻了几篇文献才意识到,强对偶替换不是可选项,而是必经之路。
3.3 Big-M取值和数值容差:模型对却解错的常见根源
Big-M 的取值看起来是个小细节,实际却是导致"模型正确但结果错误"的最大嫌疑犯。如果 M 取得太小,会切掉真实的可行域,导致部分对偶变量永远达不到真实值,模型解出来的"最优"是假的;如果 M 取得太大,求解器在数值容差内会把互补松弛条件直接放行,也可能产生无意义的解。M 的取值范围需要结合对偶变量的量纲来判断。以上面的功率上限约束为例,μ 的单位是元/kWh,和电价同量级,所以 M 取 100 到 1000 范围内的值通常足够安全;但如果你额外引入了一些目标罚项或者网损项,对偶变量量级可能会变化,这时就需要重新评估。
我自己的实践习惯分两步走:第一步,先不加互补松弛条件,只解一个松弛版本,看各对偶变量的大致量级;第二步,根据这个量级设定 M,一般取对偶变量最大值的 10 到 100 倍。如果求解过程中出现数值病态警告,第一反应不是调求解器容差,而是检查 M 是否过大。另外,Gurobi 支持用 SOS1 约束直接表达互补松弛,不需要 Big-M,小规模问题用起来数值上更干净,但大规模场景下 Big-M 加适当 M 值还是更高效。
3.4 模型自检:回代校验和原始双层对账
单层模型求解完成后,最容易被忽略的一步是验证:这个解真的是原双层问题的均衡解吗?我在项目里吃过一次大亏——单层模型跑出一个非常高收益的解,但把价格固定后单独求解每个用户的下层问题,发现用户根本不接受那个充电计划,模型的 x_{n,t} 解和下层重新优化的结果差异巨大。原因就是 M 取值太小,切掉了一个关键的可行区域,导致互补松弛条件没有被真正满足。
所以无论模型多么漂亮,求解之前一定要做回代校验。具体做法是:把上层解出的最优价格 p_t 固定住,代入原始的下层用户问题,用任意 LP 求解器重新求一遍每个用户的最优充电计划 x_{n,t}^*,然后和单层模型里得到的 x_{n,t} 对比。如果两者的差超过 1e-4,就说明单层模型的某些约束没有正确生效。另外一个更严格的校验是检查"无单方面偏离激励":把下层反馈的负荷固定住,再验证上层在这个负荷下是否有动机改变价格。如果提价或降价都不能改善收益,那才算真正达到了 Stackelberg 均衡。
4. 仿真结果里的反直觉现象:峰谷倒挂与"涨价反而少赚"
4.1 仿真场景与数据设计:不是跑数,是搭一个可信的试验台
建好模型之后,仿真设计要从"把流程跑通"升级到"让结果有说服力"。我搭的场景是这样的:一个小区级充电站,配电网变压器容量 1000 kW,基础负荷用典型城网日负荷曲线,峰时约 800 kW。充电站服务的电动汽车规模按场景设置,基准场景取 200 辆。车辆参数按现实分布:电池容量 40 到 60 kWh,慢充功率 7 kW,部分快充桩 50 kW,初始 SOC 均匀分布在 0.2 到 0.5,目标 SOC 设为 0.9。停车窗口分两种模式:通勤型早 8 点到晚 18 点,居住型晚 18 点到次日早 8 点。
购电成本曲线参考典型分时电价:峰时段(10:00-12:00、18:00-20:00)0.8 元/kWh,平时段 0.5 元/kWh,谷时段(0:00-6:00)0.3 元/kWh。上层价格约束设为 0.3 到 1.5 元/kWh。求解方面用 Python 调 Gurobi,MILP 的 MIPGap 设为 0.5%,单次求解时长控制在 120 秒内。每个场景跑完还要做蒙特卡洛采样——用户到达时间、初始 SOC 这些参数有随机性,单次结果不能说明问题,一般跑 300 个样本取平均。
4.2 三套对比方案的结果:定价曲线到底差在哪里
我比较了三种定价方案:固定电价、静态分时电价、主从博弈定价。固定电价最简单,全天一个价,用户没有任何错峰动力;静态分时电价就是我之前说的那种拍脑袋定峰谷价格;主从博弈定价则是模型内生算出来的价格曲线。
一组典型日结果对比如下:
| 方案 | 充电站日收益(元) | 用户平均电费(元/kWh) | 峰谷差(kW) | 变压器峰值负载率 |
|---|---|---|---|---|
| 固定电价 | 4120 | 0.68 | 520 | 91% |
| 静态分时电价 | 4360 | 0.71 | 430 | 84% |
| 主从博弈定价 | 4870 | 0.73 | 310 | 76% |
主从博弈定价相比固定电价,充电站日收益提升了约 18%,峰谷差下降了 40%,变压器峰值负载率也从 91% 降到了 76%。用户平均电费略有上升,但幅度可以接受。这个结果符合预期:价格引导确实能激励用户主动错峰,充电站也能从更平稳的负荷曲线里拿到收益。
最有意思的是算出来的价格曲线形状,它和传统 TOU 差别很大。传统 TOU 是几个阶梯型时段,而主从博弈解出来的价格曲线是平滑浮动的,晚高峰时段价格抬到接近上限,但与普通 TOU 不同的是,它会刻意在次日凌晨留出一个"略高于最低价"的肩峰段,而不是把价格直接压到谷时最低价。
4.3 第一个反常结果:谷时电价不是越低越好
我第一次看到定价结果时,有个反直觉的地方始终想不通:为什么夜间谷时段的电价不是全程压到最低,而是在凌晨 2 点到 4 点之间反而高出最低价 0.1 元?
后来仔细拆解才明白,谷时段如果有大量用户涌入,充电站的购电功率会大幅增加。虽然谷时购电单价低,但充电站为了服务这些用户,需要在配电网侧支付的容量费用和其他隐性成本也会上升。而且更关键的是,当谷时段价格被压到极低时,所有用户都往这个时段挤,充电站实际上没有赚到多少差价——收入低、购电量却大。主从博弈模型算出来的均衡价格,其实是在"吸引用户错峰"和"避免用户过度集中"之间做了一个权衡。所以谷时价格不是越低越好,它要保证用户分散到多个时段,而不是像羊群一样全挤在同一扇门里。
这个结果也解释了为什么很多静态 TOU 在实际运营中效果不如预期:静态 TOU 把谷时价格钉死在最低点,用户自然趋之若鹜,结果谷时变成了新的峰,完全背离了削峰填谷的初衷。而主从博弈定价让价格成为内生决策变量,它会自动规避这种"新峰"的产生。
4.4 第二个反常结果:价格提高,收益反而下降
另一个反直觉现象来自敏感性分析。我一开始有一个执念:在峰时段把价格定得越高,单价利润越高,收益应该越大。于是我把峰时段价格上限从 1.2 元/kWh 调到 1.5 元/kWh,结果发现总收益不升反降。
原因在于用户的价格弹性。当峰时价格超过一定阈值,用户会大幅减少该时段的充电需求,一部分需求被搬到其他时段,另一部分干脆放弃在这个站充电。充电站虽然单时段利润率高,但销量萎缩得更快,总收益反而下降。从数学上说,这就是上层收益函数作为价格的函数存在一个极值点——在弹性足够大时,最优价格一定低于理论上限。
我后来用这个结果做成了一张"价格-收益-负荷"三维关系图,发现最优价格基本落在购电成本的 1.3 到 1.8 倍区间内,超过这个区间,收益开始下滑。这对实际运营有直接的指导意义:定价不是越高越好,必须结合用户响应曲线去算最优价格点,而不是拍脑袋决定涨幅。
5. 从单站到多站、从理想到工程:分布式求解与落地路线
5.1 多充电站场景:从单领导者走向多领导者均衡
单站点模型跑通后,下一步自然要扩展到多站点。多个充电站同时为用户提供充电服务,用户会在不同站点之间选择,这就变成了多领导者多跟随者的博弈问题。每个充电站都是独立的决策者,它们之间通过共享的用户需求池和配电网节点电压/容量约束耦合在一起。
多领导者博弈的复杂度比单领导者高了一个量级,因为每个站的定价决策都会影响用户到其他站的流量,进而影响其他站的最优定价。严格意义上,这个问题的解是纳什均衡而不是 Stackelberg 均衡——没有任何一个站能单方面调整价格来改善自己的收益。
求解多领导者博弈,项目里最常用的方法是列对角线化(Diagonalization)算法。思路很直接:固定其他站的定价,当前站求解自己的单层主从模型,得到最优响应价格;然后依次轮换更新每个站的价格,反复迭代直到所有站都不再有单方面调价动机。这个算法实现简单,但收敛性需要靠仿真验证。我遇到的主要问题是迭代后期两个站互相"抬价压价",陷入小幅震荡。解决办法是给每次价格更新加一个阻尼因子:
p_{new} = (1 - α) × p_old + α × p_candidate
这个表达式里的 p_candidate 是当前迭代步求解出的最优响应价格,α 从 1 衰减到 0.1。加了阻尼之后,震荡幅度明显收敛,通常在 20 到 40 次迭代内达到近似纳什均衡。
5.2 迭代求解的震荡问题和三个稳定化手段
如果你选择用"上层定价→下层响应→上层调价"这种原始迭代方法来求解单站主从模型,一定会碰到震荡问题。我在实验里跑过:第一轮把谷时价格定低,用户全部拥入谷时;第二轮模型看到谷时负荷尖峰,就把谷时价格抬上去,用户又鸟兽散。这样来回震荡,根本收敛不了。
稳定化手段基本有三种。第一种是前面提到的阻尼因子/逐次平均法,让每次价格修正幅度变小。第二种是给用户子问题添加一个"惯性惩罚"项,在用户目标函数里加一个与上次充电方案差值的距离项:min Σ_t p_t × x_{n,t} + γ × Σ_t (x_{n,t} - x_{n,t}^prev)^2。这相当于告诉用户"不要因为价格微调就大幅改变计划",物理上对应真实用户不愿意频繁调整充电行为的习惯。第三种是收敛判据不要只看价格变化,还要看负荷变化。价格变化 1% 可能看起来已经收敛了,但用户响应可能会出现一个时段的跳变,负荷变化幅度很大,说明还没有到达稳定点。
踩过这些坑之后,我的建议是:能用单层 MILP 一次求解就别用迭代法。迭代法只适合大规模问题下的近似求解,或者作为单层模型正确性的交叉验证手段。
5.3 用户不是"理想理性人":如何引入价格弹性行为
主从博弈模型有一个隐含假设:用户是完全理性的,他们会对价格做精确的最优化响应。但真实用户的行为远没有这么理性。我做过一个小规模用户问卷调研,发现大部分车主对价格差小于 0.2 元/kWh 几乎没有反应,只有当价差超过 0.5 元/kWh 时,才会有超过 60% 的用户愿意改变充电时段。
所以如果直接把完全理性模型的定价结果拿到真实环境里,效果会打折扣。更务实的做法是引入 Logit 选择模型来描述用户的时段选择概率:用户在几个候选时段之间按概率选择,选择某个时段的概率与费用的负指数成正比:
Pr(选择时段 s) = exp(-β × 费用_s) / Σ_s' exp(-β × 费用_s')
这个公式里 β 是价格敏感系数,β 越大,用户越理性;β 越小,用户行为越随机。把 Logit 模型嵌到下层之后,下层不再是严格的最优化问题,而是一个固定点问题。求解时可以用外循环迭代:假设一个用户选择概率分布,计算每个时段的期望负荷,充电站平台再根据期望负荷调整价格,反复迭代直到选择概率和负荷之间不再变化。
我在实际项目里用这个扩展后的模型跑了一轮,发现它算出的最优价格比完全理性模型温和得多——不会出现那种极端的价格尖峰。原因是用户行为有惯性,定价方必须预留更大的价格空间才能引导用户行动。这告诉我们,博弈模型的"理性程度"直接决定了价格曲线的激进程度,落地时要根据实际用户数据标定 β,而不是拍脑袋设一个。
5.4 工程落地的关键选型:预测、优化求解器与云边协同
从论文模型走到工程系统,中间还有好几步要走。第一个问题是预测——主从博弈定价需要提前知道明天的用户需求分布。这个可以基于历史充电记录做负荷预测,用 LSTM 或者梯度提升树都行,预测误差控制在 10% 以内时,定价结果和完美信息下的差距很小。第二个问题是求解性能。单站 96 时段、500 个用户、加上 Big-M 线性化后的 0-1 变量,Gurobi 求解时间在我机器上大概是几十秒到几分钟,这个速度对日前定价完全够用;但如果是实时滚动定价,就需要离线预计算价格策略库,线上根据实时状态查表加边际微调,或者用启发式方法先给出初值再做局部搜索。
第三个问题是系统架构。我推荐的模式是云边协同:云端运行主从博弈模型,生成未来 24 小时的基准价格曲线;边缘侧(充电桩本地控制器)根据现场排队状态、车辆 SOC 和用户紧急程度,在基准价格基础上做分钟级的微调。这样既保留了博弈模型的全局最优性,又具备边缘端的实时响应能力。
如果场景要扩展到 V2G(车辆到电网),车辆可以反向放电,用户的决策就变成"充还是放、充多少、放多少"的双向优化,上层目标函数也变成双向定价。这类模型的复杂度会再上一个台阶,需要在目标函数里分段处理充放电功率变量,并加入电池寿命衰减成本约束。但基本的建模框架、KKT 转化、强对偶替换这套方法论依然适用。
最后说一个我做这个项目后的真实体会。很多人觉得博弈论是纯理论工具,实际上它落地价值恰恰在建模时逼你把价格机制想清楚——价格为什么定这个数、用户为什么会接受、电网为什么不会过载,每一个决策都有明确的逻辑支撑。定价策略一旦能自解释,工程团队的接受度和用户的理解成本都会大幅下降。我的建议是不要一上来就做多站、多时段、V2G 的大全套,先把单站 96 时段的主从模型完整跑通,回代校验通过,再一步步往里加现实约束。这个顺序我走了不少弯路才摸出来,照着走会省掉大量排查问题的时间。
