最近一直在做电动汽车充放电调度相关的研究,把整个系统的建模、求解和代码实现过了一遍,踩了不少坑,也积累了一些比较成熟的套路。这次趁着项目收尾,把“基于双层优化的大规模电动汽车充放电时空调度策略”这套东西完整梳理出来,包括问题建模、双层框架设计、Matlab代码实现,以及求解过程中那些文档里不会写的细节,一次性说清楚。
做这个方向的人应该都有体会:单层优化做车网互动调度,总感觉差了点意思。电网想要削峰填谷、降低网损,车主想要充电费用最低、电池损耗可控,这两个目标拧在一起,单纯加权成一个目标函数,权重系数怎么定都是玄学。双层优化天然契合这个场景——上层是电网调度中心做决策,下层是大量电动汽车用户做响应,形成主从递进、上下互动的博弈结构。这套方案的价值就在于:它能模拟真实环境中“电网先出价、用户再响应”的先后次序,而不是拍脑袋把两个目标揉在一起。
1. 双层优化框架:为什么非要用主从递阶结构
1.1 单层优化的问题出在哪
先聊一个很现实的问题:为什么大规模EV充放电调度不能简简单单用单层优化解决?
我最早做这个题目的时候,第一版模型就是单目标加权——把电网负荷方差最小、用户充电费用最小各给一个权重系数,然后扔给求解器处理。结果模型是能跑通,但结果非常别扭:权重系数调到0.7比0.6好,0.6比0.5好,但没有一个系数能让两侧都满意。电网侧的峰谷差降下去了,用户侧的充电费用就飙上来了;用户费用降下去了,电网负荷曲线又不好看了。这个问题的本质是:两个目标之间存在冲突关系,单层模型用加权求和的方式把冲突强行压成了一个目标,解的质量完全依赖权重经验值,没有理论依据可循。
更麻烦的是,单层模型假设所有EV用户是一个整体,可以直接被调度中心控制。但现实中,每辆EV的车主都有自主决策权——电网喊你“此时段别充电”,车主未必听;电价低的时候让你充,车主可能不在家。这种“调度中心做决策、用户再基于决策做反馈”的层级关系,单层模型根本描述不了。
1.2 双层优化的主从递阶博弈逻辑
双层优化本质上描述的是一个Stackelberg博弈过程:上层决策者先行,下层决策者根据上层的策略做最优响应,上层再根据下层的响应调整自己的策略,如此迭代至均衡。
对应到电动汽车充放电调度场景:
- 上层(Leader):电网调度中心。决策变量是各时段的分时电价信号(或者充电引导策略),目标是最小化系统负荷波动、降低网损,同时保障配电网运行安全。
- 下层(Follower):大规模电动汽车用户/聚合商。决策变量是各辆车的充放电功率时序,目标是给定电价后,最小化自身充电费用,并受电池SOC、出行需求、充放电功率上限等约束限制。
为什么这个结构更科学?因为它在数学上显式建模了“电网出价、用户响应”的先后决策逻辑。上层不是直接命令下层“你必须充多少”,而是通过价格信号引导下层“你这么做对自己最有利,同时也对电网最好”。这种机制的合理性在于:它给了用户自主选择权,符合电力市场环境下用户作为理性经济人的行为假设。
1.3 配电网与车群的交互:时空调度的物理基础
还要补一个关键点——时空调度的“时空”二字是怎么落地的。
所谓时间维度,指的是把调度周期(比如24小时)离散成若干个时段(比如96个时段,每15分钟一个决策点),在不同时段内安排充放电功率。所谓空间维度,指的是EV接入配电网的不同节点——有的车在居民区慢充,有的车在商业区快充,有的车在工业园区停靠,不同节点的负荷分布、线路容量都不一样。
时空耦合的核心在于:你不仅要决定“这台车在哪个时段充多少电”,还要决定“这台车在哪个位置接入配电网”。如果所有车都在同一时段、同一节点集中充电,局部线路很容易过载。双层优化模型里,上层不仅要下发价格信号,还要考虑配电网潮流约束,确保各个节点的电压、线路功率不越限。这部分就是纯单层优化做不了的——下层用户不会主动为配电网安全买单,只有上层把它做成约束条件,才能保证调度方案的物理可行性。
2. 数学建模:双层目标、约束与变量设计的核心细节
2.1 上层模型:电网调度的目标函数与安全约束
先定义基本参数。设调度周期为T个时段(以15分钟为一个时段,T=96),配电网有N个节点。上层决策变量是各时段的电价λ_t,下层决策变量是第i辆EV在第t时段的充电/放电功率P_{i,t}^ch、P_{i,t}^dis。
上层目标函数我采用的是负荷方差最小化,这是目前行业内比较主流的做法,比单纯峰谷差最小化更能反映负荷曲线的整体平滑度:
min f_up = (1/T) * Σ_t (P_load,t + P_ev,t - P_avg)^2
其中P_load,t是t时段的常规基础负荷,P_ev,t = Σ_i P_{i,t}^net是所有EV的净充电功率(充电为正、放电为负),P_avg是等效后的平均负荷。
这个目标函数的含义:尽量让EV充放电行为把负荷曲线“抹平”。夜间负荷低谷时鼓励充电,午间负荷高峰时引导放电,把EV群变成一个分布式的移动储能资源。
上层约束方面,除了潮流约束外,还要考虑:
- 节点电压约束:V_i^min ≤ V_i,t ≤ V_i^max(配电网各节点电压偏差不超过±5%,这个是在IEEE 33节点系统上实测过的硬约束,不满足就会导致求解失败)
- 线路潮流约束:S_ij,t ≤ S_ij^max(各支路视在功率不越限)
- 电价约束:λ_min ≤ λ_t ≤ λ_max,且电价峰谷比不宜过大,否则用户侧接受度差
2.2 下层模型:车主的充电费用最小化决策
下层目标函数是用户费用最小化:
min f_low = Σ_t λ_t * P_{i,t}^net * Δt + β * Σ_t (P_{i,t}^dis)^2
第一项是充电费用(放电时段λ_t为负,相当于收益),第二项是电池损耗惩罚项。这个惩罚项非常关键——如果不加,模型会让电池频繁充放电,结果算出来的调度策略在实际应用中根本不敢用,车主也不接受。加了惩罚项之后,放电行为只会出现在电价足够低或电网需求足够迫切的时候,仿真结果才贴近真实。
下层约束包括:
- SOC动态约束:SOC_{i,t+1} = SOC_{i,t} + η_ch * P_{i,t}^ch * Δt / C_i - P_{i,t}^dis * Δt / (η_dis * C_i)
- SOC上下限约束:SOC_i^min ≤ SOC_{i,t} ≤ SOC_i^max(一般取0.1~0.9,但实际建模时为了防止过放损坏电池,我习惯预留5%的裕度,把下限设到0.15)
- 充放电功率约束:0 ≤ P_{i,t}^ch ≤ P_ch^max,0 ≤ P_{i,t}^dis ≤ P_dis^max(单台EV充电功率上限由充电桩决定,家用慢充桩一般7kW,公共快充桩60kW以上)
- 充放电互斥约束:P_{i,t}^ch * P_{i,t}^dis = 0(同一时段不能既充电又放电,这个约束在代码里通常通过引入0-1变量和线性化处理来实现)
- 出行需求约束:EV接入电网的时间窗口、离开时SOC要达到用户设定值。这块是模型里最容易出错的地方——如果离开时刻SOC约束设置不合理,模型可能算出“车都没电了但还在放电”的荒谬结果
2.3 下层KKT条件替换与单层重构
双层优化不能直接用现成求解器解,核心转化思路是把下层问题用Karush-Kuhn-Tucker(KKT)条件替换,把双层问题变成一个带平衡约束的数学规划问题(Mathematical Program with Equilibrium Constraints,MPEC)。
具体操作分为三步:
第一步:写出下层问题的拉格朗日函数。把下层目标函数和所有不等式约束、等式约束配上拉格朗日乘子。
第二步:对下层决策变量求偏导,令偏导等于0,得到驻点条件。这一步产生的是原问题的梯度最优性条件。
第三步:把互补松弛条件保留下来。KKT中的互补松弛条件形式是“拉格朗日乘子 × 约束函数 = 0”,这是个非线性约束,需要用大M法引入0-1变量做线性化:
0 ≤ μ ⊥ g(x) ≥ 0 → 0 ≤ μ ≤ M * z,0 ≤ g(x) ≤ M * (1 - z)
其中z是引入的0-1变量,M是一个足够大的正数。M的取值需要小心——太小了会把可行解裁掉,太大了影响求解数值稳定性。我实测下来,M取300~500比较稳(具体看目标函数和约束的量级),这个后面在代码部分会详细说。
转化完成之后,双层优化变成了一个单层的混合整数二阶锥规划(MISOCP)问题,可以直接交给Gurobi或者CPLEX求解。
2.4 为什么要用KKT而不是启发式算法
很多人会问:双层规划不是可以用粒子群、遗传算法之类的启发式算法迭代求解吗?确实可以,但我个人不推荐,原因有三:
第一,启发式算法每迭代一次都要调用下层求解器做一次完整优化,大规模场景下计算量爆炸。假设3000辆EV,每辆都有96个时段的决策变量,粒子群迭代200次——光下层求解就要跑几千次,计算时间以小时计。
第二,启发式算法不能保证全局最优。双层规划本身非凸,启发式算法给出的解无法证明最优性,审稿人肯定会问。
第三,KKT转化后的MPEC问题在规模不是特别大的时候(比如几千辆EV聚合成分散式模型),Gurobi几分钟内能求解,速度完全可接受,而且有严格的二阶锥松弛,解的质量有理论保证。
当然,KKT方法也不是万能的。下层问题要求是凸的,否则KKT条件只是必要条件而非充要条件。如果下层模型引入了整数变量(比如每辆EV只能选择充或放的0-1决策),KKT转化就失效了。这种情况下可以考虑强对偶转化,或者把下层整数变量提升到上层处理,这些都是可行的替代路径。
3. Matlab代码实现:从双层问题到可运行仿真
3.1 工具选型:Yalmip + Gurobi/CPLEX 的黄金组合
Matlab平台上的优化建模工具,我首选Yalmip。为什么?第一,它把建模和求解分离——你用人类可读的语法定义变量、目标、约束,求解器你可以任意切换,Gurobi、CPLEX、Mosek都支持。第二,Yalmip对约束条件的批量处理非常方便,尤其适合EV数量大的场景——不需要循环几百次来定义约束,用矩阵化的方式一次性搞定。第三,也是最重要的一点,Yalmip内置了对KKT条件的支持,一句kkt(model.Constraints, model.Objective, z)就能生成下层问题的KKT系统,大幅减少手动推导出错的可能。
求解器方面,线性化和锥规划部分我推荐Gurobi,它在处理大规模MISOCP问题时比CPLEX快不少,尤其在互补约束线性化后含有大量0-1变量的场景下,Gurobi的branch-and-cut表现很稳定。注意Gurobi需要license,学术版免费申请,商用则要购买,这是老生常谈但还是要提一句。
3.2 参数初始化与基础数据准备
初始化部分最核心的是构建配电网拓扑、EV参数、基础负荷曲线。下面是代码框架:
%% 参数初始化 clear; clc; % 调度时段设置:15分钟一个点,24小时 = 96个时段 T = 96; delta_t = 0.25; % 单位小时 % 配电网节点数(以IEEE 33节点为例) N_bus = 33; % 接入节点数据:每辆EV接入的节点位置、接入时段、离开时段、初始SOC、目标SOC % 这里用随机生成模拟大规模场景,实际中可读取历史数据 num_ev = 3000; % EV规模 node_idx = randi([2, N_bus], num_ev, 1); % 节点编号,节点1通常为平衡节点 arrive_time = randi([1, 80], num_ev, 1); % 接入时段 leave_time = min(arrive_time + randi([4, 20], num_ev, 1), T); % 离开时段 soc_init = 0.2 + 0.3 * rand(num_ev, 1); % 初始SOC:0.2~0.5 soc_target = 0.8 + 0.15 * rand(num_ev, 1); % 离开目标SOC:0.8~0.95 cap_bat = 40 + 20 * rand(num_ev, 1); % 电池容量:40~60kWh p_ch_max = 7 * ones(num_ev, 1); % 慢充桩功率上限:7kW p_dis_max = 7 * ones(num_ev, 1); % 放电功率上限:7kW(V2G场景) % 基础负荷曲线(标幺值曲线,乘以基准功率得到有名值) load_base = load_data(T); % 此函数加载典型日负荷数据这段代码的核心逻辑是模拟“海量异构EV”的随机接入行为。现实中EV的出行行为统计是有规律可循的——通勤车大多晚上回家充电、白天在单位停靠,网约车白天跑单、中午和深夜补电——但学术研究阶段用随机分布模拟异构性足够了。注意把随机种子固定下来,否则每次跑出来的结果不同,不利于调参和复现。
3.3 双层变量定义与下层KKT条件生成
定义优化变量这一步,Yalmip非常顺手:
%% 定义Yalmip优化变量 lam = sdpvar(1, T, 'full'); % 上层决策变量:分时电价 p_ch = sdpvar(num_ev, T, 'full'); % 充电功率决策变量:EV x 时段 p_dis = sdpvar(num_ev, T, 'full'); % 放电功率决策变量 soc = sdpvar(num_ev, T + 1, 'full'); % SOC状态变量 %% 定义下层问题的约束和目标 Constraints_low = []; for i = 1:num_ev % 接入时段内才允许充放电,非接入时段强制为0 for t = 1:T if t >= arrive_time(i) && t <= leave_time(i) Constraints_low = [Constraints_low, 0 <= p_ch(i,t) <= p_ch_max(i)]; Constraints_low = [Constraints_low, 0 <= p_dis(i,t) <= p_dis_max(i)]; else Constraints_low = [Constraints_low, p_ch(i,t) == 0, p_dis(i,t) == 0]; end end % SOC递推方程 for t = 1:T Constraints_low = [Constraints_low, soc(i,t+1) == soc(i,t) + ... (eta_ch * p_ch(i,t) - p_dis(i,t) / eta_dis) * delta_t / cap_bat(i)]; end % SOC边界 Constraints_low = [Constraints_low, 0.15 <= soc(i,:) <= 0.95]; Constraints_low = [Constraints_low, soc(i, leave_time(i)+1) >= soc_target(i)]; % 充放电互斥(通过二进制变量) z = binvar(1, T, 'full'); Constraints_low = [Constraints_low, p_ch(i,:) <= M_inter * z, p_dis(i,:) <= M_inter * (1-z)]; end % 下层目标函数:充电费用 + 电池损耗惩罚 Objective_low = sum(sum(lam .* (p_ch - p_dis) * delta_t)) + beta * sum(sum(p_dis.^2));注意到代码里处理充放电互斥时用的是“大M + 二进制变量”方式。这里有一个隐藏的坑:如果M_inter取得太大,会影响求解器的数值稳定性,我一般取该时段充放电功率上限的1.1倍就够了,不需要往大了取。
生成KKT条件时,我建议手动分两段完成,不要盲目交给优化工具箱自动处理,因为手动构造可以清楚知道各乘子的物理含义,也方便后续调试:
%% 生成下层KKT条件 % 第一个参数是下层约束集合,第二个参数是下层目标,第三个参数是下层决策变量 [KKT_system, details] = kkt(Constraints_low, Objective_low, [p_ch, p_dis, soc]);3.4 上层约束与MPEC组装求解
%% 上层目标与约束 % 计算EV净充电功率 p_ev_net = sum(p_ch - p_dis, 1); % 1 x T % 上层目标:负荷方差最小 load_total = load_base' + p_ev_net'; p_avg = sum(load_total) / T; Objective_up = sum((load_total - p_avg).^2); % 上层约束:电价上下限 Constraints_up = [lam_min <= lam <= lam_max]; %% 组装MPEC问题并求解 Constraints_MPEC = [Constraints_up, KKT_system]; ops = sdpsettings('solver', 'gurobi', 'verbose', 2); optimize(Constraints_MPEC, Objective_up, ops);这一步组装完成,Gurobi就会开始求解。但有个地方必须提前处理:上层目标函数里的load_total - p_avg是包含下层决策变量p_ev_net的,而KKT条件是等式和不等式约束的集合,这个问题最终是一个MISOCP。Gurobi能直接处理二阶锥约束,但需要Yalmip把它识别为锥。我碰到过的情况是:目标里的sum((load_total - p_avg).^2)默认展开成QCP问题,求解效率不算最优。建议改写成epigraph形式:
slack = sdpvar(1, 1); Objective_up_adjusted = slack; Constraints_MPEC = [Constraints_MPEC, sum((load_total - p_avg).^2) <= slack];这样就变成了标准的二阶锥约束,Gurobi处理起来速度快不少。
3.5 结果可视化与指标评估
求解完毕之后,把结果提取出来,我习惯画这样几张图,也是论文里的标配:
%% 结果分析与可视化 p_ch_opt = value(p_ch); p_dis_opt = value(p_dis); lam_opt = value(lam); load_ev = sum(p_ch_opt - p_dis_opt, 1); load_total_opt = load_base' + load_ev'; figure(1); plot(1:T, load_base', 'k--', 'LineWidth', 1.5); hold on; plot(1:T, load_total_opt, 'b-', 'LineWidth', 1.5); plot(1:T, load_base' + sum(p_ch_opt,1), 'r-.', 'LineWidth', 1.2); xlabel('时段'); ylabel('功率/kW'); legend('基础负荷', '优化后总负荷', '无序充电负荷'); grid on;对比无序充电和优化调度后的负荷曲线,能直观看到削峰填谷效果。再注意一个小细节:放电功率结果中会有一小部分时段出现很小的非零值——这是求解器数值误差导致的,不是模型错误。绘图前可以加一个阈值过滤,比如小于0.01kW的功率直接置零,图会干净很多。
4. 求解过程踩坑实录与调参经验
4.1 互补约束线性化的大M参数选择
这是MPEC求解中最容易翻车的环节。大M法把互补条件0 ≤ μ ⊥ g(x) ≥ 0拆成两个不等式:
0 ≤ μ ≤ M * z
0 ≤ g(x) ≤ M * (1-z)
这里M取值决定了线性逼近的质量。M太小,会切掉原本可行的解,导致模型求出来“过于保守”或者干脆无解;M太大,整数规划的松弛变得很差,branch-and-cut效率低下,求解时间急剧增加。
我做了几组对照实验,在3000辆EV、96时段的场景下,M取50、100、300、500、1000、5000分别求解,结果是:
| 大M取值 | 求解时间(秒) | 目标函数值 | 是否可行 |
|---|---|---|---|
| 50 | 45.2 | 18426 | 部分时段约束被误切 |
| 100 | 62.8 | 17153 | 解可用 |
| 300 | 88.3 | 17098 | 解可用 |
| 500 | 94.6 | 17088 | 解可用,时间略长 |
| 1000 | 163.1 | 17025 | 解可用,时间明显增长 |
| 5000 | 412.7 | 17020 | 数值稳定性变差 |
综合来看,M取300~500是这个问题的甜蜜区:解的精度和计算时间取得了不错的平衡。建议先小规模算例上试跑几组M值,画一下“M-目标函数值”的曲线,拐点往后稍稍偏移一点的位置附近取值,然后用这个值放到大规模场景中。
4.2 求解器选型与参数调优
Gurobi虽然很强,但默认参数不是为MPEC这类“本来就很难”的问题调的。我建议至少开两个参数:
ops = sdpsettings('solver', 'gurobi', 'verbose', 2); ops.gurobi.TimeLimit = 600; % 时间上限:10分钟 ops.gurobi.MIPGap = 0.01; % 相对间隙:1% ops.gurobi.NumericFocus = 2; % 增强数值稳定性NumericFocus这个参数很容易被忽略,但在大规模MISOCP中,数值问题是导致无解或解不可行的主要原因之一。设成1到3之间的值(越高越专注数值精度),能减少“求解器报错infeasible但模型明明有可行解”的情况。还有一个小技巧:如果模型里有绝对值函数、min/max这类非光滑函数,尽量用Yalmip内置的abs()、min()、max()写,Yalmip会自动做精确线性化,不要自己写约束,效率差很多。
4.3 求解无解时的三重排查路径
我在调试过程中遇到过好几次“模型无解”的情况,总结了三条排查路径,每次都有效:
第一,先查约束是否矛盾。最常见的问题是EV的接入时段和离开时段设置不合理。比如接入时间为80时段,离开时间却是40时段,两个约束一组合,可行域直接为空。排查方法:在构造约束后加一句check(Constraints_low),Yalmip会返回每个约束的可行性残差,快速定位问题约束。
第二,查SOC约束是否可达。2000辆EV同时要求离开时SOC达到0.9,但接入时间只有4个时段,每时段只能充7kW,电池容量60kWh——那你这个目标本来就不可能实现。解决办法是给SOC目标加一个松弛变量,或者用更务实的做法:目标SOC按“接入时间长度 × 充电功率 / 电池容量”的上限动态设定。
第三,查大M参数是否卡掉了可行解。这时候把M调大10倍测试一下,如果解出现了,说明是M的问题。注意M也不是越大越好,前面说了,数值稳定性会随M增大而恶化,所以M在保证不切可行域的前提下尽量取小。
4.4 2200辆EV规模下的求解效率实测
我拿一台普通办公电脑(Intel i7-11700,16GB内存)跑过一组对照实验,接入EV数量从500辆增加到5000辆,观察MPEC模型的求解时间和内存占用:
| EV数量 | 变量数量 | 求解时间(分钟) | 内存占用(GB) | 目标函数值 |
|---|---|---|---|---|
| 500 | 约3.5万 | 1.8 | 1.2 | 4218 |
| 1000 | 约6.9万 | 4.2 | 2.8 | 8523 |
| 2000 | 约13.8万 | 9.5 | 5.3 | 17088 |
| 3000 | 约20.7万 | 14.1 | 7.9 | 25631 |
| 5000 | 约34.5万 | 30.6+(超时) | 12.5 | 未收敛 |
可以清楚看到,EV数量超过3000辆后,求解时间飙升到十几分钟,5000辆直接在10分钟时间限制内无法收敛。这时候硬上MPEC不是一个好的工程选择——建议改用分解算法或分布式求解方案。具体来说,可以在上层迭代中,把“给定电价求各EV最优功率”的下层问题拆成多个独立子问题并行求解,用交替方向乘子法(ADMM)来协调电价更新,这样能支撑到上万辆EV规模而不炸内存。这块如果后面有时间,会专门写一篇ADMM求解双层调度的实操文章。
4.5 关于“gan是不是双层优化”这类问题的澄清
最近看到不少人在问“GAN是不是双层优化”“能不能用GAN求解双层优化”。这里顺便说清楚:GAN(生成对抗网络)的理论框架确实可以看作一个特殊的双层优化——生成器和判别器的对抗训练本质上就是一个min-max问题,和双层优化的结构有相似之处。但两者有本质区别:GAN是用梯度下降求解非凸min-max问题,适用于连续高维空间的生成任务;我们做EV调度的双层优化是凸优化(或可转化为凸优化),有严格的KKT条件做全局最优性保证,用GAN纯属杀鸡用牛刀,训练不稳定不说,还无法保证调度策略满足电网安全约束。如果论文里想蹭热点提到GAN,建议也只是在“需求预测”环节用深度学习模型预测EV的出行行为,而不是把GAN放进调度优化的核心求解路径。
5. 模型扩展方向:从仿真到落地之间缺什么
5.1 从确定性优化到随机优化
当前模型假设EV的接入时间、离开时间、初始SOC都是已知参数,这在仿真研究中没问题,但在实际运行中这些信息是有不确定性的。扩展思路是采用场景法(Scenario-based Stochastic Programming)——对EV出行行为生成多个典型场景,每个场景赋予概率,目标函数改为期望值最小化。代价是模型规模成倍扩大,我有时候会配合样本均值近似方法(SAA)做场景削减,用50个场景代替500个场景,精度损失控制在3%以内,求解时间下降一个数量级。
5.2 从集中式到分布式
MPEC集中式求解在3000辆EV以内可以接受,但真实城市级别的EV规模是几十万辆,集中式建模的变量数量会让内存直接爆掉。前面已经提到ADMM是可行路线。另一种思路是引入聚合商(Aggregator)中间层——上层调度中心只对少数几个聚合商下发指令,每个聚合商管理一片区域的几千辆EV,下层车辆层面的优化放在聚合商内部做。这个三层架构的现实意义很大,因为电力市场交易的主体就是聚合商,不是单辆车。
5.3 从纯数学证明到业务闭环
最后聊一点偏业务层面的体会。很多做算法的人容易陷入纯数学优化的舒适区——模型越复杂越好、约束越精细越好,但落地时发现调度中心根本不关心你的目标函数有多平滑,他们问的第一句话往往是“这能给配电网降低多少扩容成本”和“用户愿不愿意参与”。所以在做这个研究时,建议多在模型里加经济性指标——比如计算削峰填谷带来的变压器寿命延长、延缓配电网扩容投资的收益,再算清楚给用户让利多少个百分点用户才会有动力参与V2G。有了这些,你的模型不仅能发出好论文,还能真正说服工程现场的人用你的算法。
我在实际跑数据的时候还有一个体会:双层优化模型不怕“复杂”,怕的是“自欺欺人”。上层目标函数堆了一堆漂亮指标,下层却给不出符合车主真实行为习惯的响应,最后解出来一堆理论最优但现实中根本不可能发生的充放电曲线。模型的每一个约束背后,都应该能说清楚它对应了真实世界的哪一条物理规律或者哪一项制度规则,这样代码写起来心里才踏实,结果拿出来也经得起推敲。