ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

电动汽车优化调度中的双层优化模型与MATLAB实现

电动汽车优化调度中的双层优化模型与MATLAB实现 1. 先说清楚为什么要“双层”一个充电场景里的三方博弈做电动汽车优化调度这个方向的人一多很多人上来就问能不能一个目标函数把问题写完比如“最小化总充电费用”或者“最小化电网负荷方差”。单层问题确实容易写、容易调、MATLAB里一个fmincon就能跑导师催进度的时候也能立刻交出一版结果。但你仔细想一想就会发现真实世界里的充电决策根本不是一个人说了算的。充电站运营商、配电网调度中心、电动车车主这三方各有各的心思。车主关心的是我这一晚充满电花了多少钱、出门时有没有足够续航运营商关心的是自己买卖电力的价差收益能不能覆盖运行成本、设备利用率高不高而电网侧关心的是负荷曲线别太陡尤其别在晚上七八点大家都插枪充电的时候叠出一个新的晚高峰。这三方的利益互相纠缠你把它压成一个单目标等于强行让车主为电网目标买单或者让运营商牺牲收益去迁就车主——这在工程上说服不了任何人审稿人也一眼就能看出模型解释力不足。所以就有了“基于双层优化的电动汽车优化调度”这种架构上层放一个决策者通常是配电网调度中心或充电聚合商下层放另一个决策者大量电动汽车车主或充电站上层先制定策略比如分时电价下层在这个策略下做最优响应比如决定什么时候充电、充多少上层再根据下层响应调整自己的策略循环迭代直到收敛。这就是典型的Stackelberg博弈也就是我们常说的领导者-跟随者结构。我见过不少初学双层优化的同学卡在同一个地方他们以为双层优化只是“两个优化问题叠在一起先算上面再算下面”实际上两层之间不是顺序执行而是双向反馈。上层的一个电价改动会让下层所有车辆的充电计划完全改变而这个改变又反过来影响上层目标函数里那个“负荷方差”的数字。你不动下层上层的目标函数就是虚的。理解了这一层才算是真正进了双层优化的门。这篇文章我会从数学模型到MATLAB代码把整条链路完整拆开讲。同时会附上可以直接跑通的代码框架和算例设置并且把我在调试过程中踩过的坑、反复试出来的经验一并写出来。如果你正在做电动汽车调度、微网优化或者需求响应相关的课题这篇文章应该能帮你少走不少弯路。2. 上下层数学模型哪些变量归上层哪些变量归下层凭什么这么分2.1 先明确优化问题的三要素决策变量、目标函数、约束条件任何优化问题都跑不掉这三个要素双层问题也不例外。只不过你要分别给上层和下层各写一套“三要素”然后再写清楚两套问题之间通过哪些变量耦合。下面是我在这个课题里采用的建模框架不算复杂但足够完整适合作为研究起点。上层是配电网调度中心视角。决策变量是每个时段的充电电价 ( p_t )一共24个时段所以上层问题有24个连续变量每个变量有上下限。目标函数我取的是“负荷方差最小化”这个目标很直接电网最怕的就是负荷忽高忽低。当然你也可以改成峰谷差最小化或者再叠加一个运营商收益最大化的多目标这个后面会讲。下层是电动汽车车主视角。决策变量是每辆车在每个时段的充电功率 ( P_{i,t} )。车辆数量多的时候下层变量会非常庞大这也是双层优化计算量大的根源之一。车主的优化目标是充电费用最小直觉上就是“电价便宜的时候多充电价贵的时候少充”但充电行为还要受几个现实条件的约束电池容量有限、不能超过最大充电功率、必须在离开之前达到目标电量。两层之间耦合的桥梁就是电价。电价是上层的决策变量但它出现在下层的目标函数里直接影响车主的充电行为而车主的充电行为汇总之后又决定了总负荷曲线进而决定上层目标函数的值。这种“你决策影响我、我响应反过来影响你”的结构就是双层优化的核心特征。2.2 下层模型的完整数学表达下层问题对每一辆车单独建模我这里先写单辆车的版本后面代码里再处理车辆数量多的问题。[ \min \sum_{t1}^{24} p_t P_t \Delta t ]目标是充电费用最小。其中 ( p_t ) 是上层给定的电价对下层来说是一个已知参数( P_t ) 是这辆车在 ( t ) 时段的充电功率是下层的优化变量( \Delta t ) 是时段长度通常取1小时。约束条件有四个。充电功率上下限[ 0 \le P_t \le P_t^{\max} ]( P_t^{\max} ) 在不同时段不一样车没接入充电桩的时候它就是0。这个处理方式非常关键很多人写约束时容易忽略“接入时间”的影响导致车在不该充电的时段也充上了电结果算出来的SOC曲线完全不符合物理实际。电池SOC递推关系[ SOC_{t1} SOC_t \frac{\eta P_t \Delta t}{B} ]这里的 ( \eta ) 是充电效率一般在0.9到0.95之间( B ) 是电池容量单位kWh。这个递推式是线性的电荷累积关系本身是线性的这是下层问题可以高效求解的根本原因。SOC上下限[ SOC_{\min} \le SOC_t \le SOC_{\max} ]我通常取0.1到0.9预留一部分余量。锂电池不建议每次都充到100%SOC上限设到0.9在工程上更合理。离开时刻的SOC要求[ SOC_{T_{dep}} \ge SOC_{tar} ]这是车主最核心的诉求——我第二天要出门电量必须够用。这个约束不能省否则优化结果会为了省钱而让车一直不充电。2.3 上层模型与两层耦合关系上层问题我写成[ \min \sum_{t1}^{24} \left( L_t^{base} \sum_i P_{i,t} - \overline{L} \right)^2 ]其中 ( L_t^{base} ) 是基础负荷 ( \sum_i P_{i,t} ) 是所有电动汽车充电负荷的总和 ( \overline{L} ) 是总负荷的平均值。这个目标函数表达的就是“负荷方差最小化”。电价的约束是[ p_{\min} \le p_t \le p_{\max} ]上下层的关系图我在这里用文字表述一下上层给定一组电价 → 下层所有车辆求解充电费用最小化问题 → 得到每辆车24小时充电功率 → 汇总为充电负荷 → 叠加基础负荷 → 计算负荷方差 → 上层根据这个方差调整电价 → 重复。整个过程中下层是没有自主定价权的它只能被动接受电价并做出最优反应。上层虽然有定价权但它的目标依赖于下层反应的方式很复杂不可能用显式表达式写出来。这就是为什么要用迭代算法求解的原因。2.4 车辆数量太多怎么办聚类降维假设算例里有200辆车每辆车24个变量下层就是4800个变量。哪怕线性规划求解很容易每次上层评估适应度都要解一次大LP几百次迭代算下来也会很吃力。所以我在实际代码里做了一步处理把车型参数相近的车辆聚成几类用每一类的代表车辆参数来求解一个下层问题最优充电功率再乘以该类车辆数量得到该类总负荷。聚类维度可以选电池容量、初始SOC、目标SOC、接入时段、离开时段、最大充电功率。我用的是对这三类典型用户的代表参数分别建模而不是跑一次kmeans算法。这样既保留了建模的物理直观性代码实现也更简单。真正做大规模算例可以把kmeans加进去原理是相同的。3. 求解方案选型从KKT转化到PSO嵌套linprog为什么这样搭3.1 学术论文里最常用的KKT转化法以及它的难点在哪里做双层优化最“学院派”的做法是用下层问题的KKT条件替换下层问题本身把双层问题转化成一个单层数学规划问题。这个思路理论味道很足下层是线性规划强对偶成立所以可以直接把它的最优性条件写出来包括stationarity、primal feasibility、dual feasibility和complementary slackness。转化完以后上层问题带上这些条件约束变成一个带互补约束的优化问题。问题出在互补松弛条件上。互补条件长这样[ \lambda \cdot g(x) 0 ]这个等式是非线性的而且带有“要么这个为0要么那个为0”的切换逻辑。处理它的标准手段是引入一个足够大的常数 ( M ) 和0-1辅助变量把互补条件转换成一系列线性不等式。这一步做完问题就变成了混合整数线性规划求解难度一下子上去了。而且 ( M ) 取大了数值稳定性差取小了又会把可行域切掉调这个参数能调到你怀疑人生。还有一个麻烦下层车辆数量多的时候每个车的KKT条件都要铺开写大规模0-1变量让求解时间急剧膨胀。说实话如果你只是需要一个能快速跑通、能解释清楚机理、能出图的版本KKT转化法不一定是第一选择。3.2 为什么我选PSO嵌套线性规划通用、稳、不容易翻车排除了KKT转化之后我选择了启发式嵌套求解上层用粒子群算法PSO搜索电价下层用linprog求解充电计划。每次PSO评估一个粒子其实就是把当前电价传给下层调用一次线性规划求解器再把解出来的充电功率汇总成上层适应度。这个方案的优点很明显。第一下层是线性规划linprog是成熟求解器内点法和单纯形法都很可靠不会自己写算法出现数值bug。第二PSO实现简单不需要梯度信息而双层问题里上层目标函数对电价的梯度根本没法用解析式写出来即使写出来也很容易是错的。第三PSO天然适合处理变量带边界的情况电价上下限直接映射到粒子位置范围就行。当然它也有代价。PSO属于启发式算法不保证全局最优而且适应度评估一次要跑若干次LP计算开销明显高于单层优化。但做研究初期的方案验证、机理分析这个代价完全能接受。3.3 两个方案的适用场景对比维度KKT转化法PSO嵌套linprog理论严谨性强可以拿到最优解或最优性间隙弱只能找到近似解实现难度高需要处理互补条件、大M参数低模型改动小问题规模适合中小规模变量多了求解器扛不住适合中大规模只要LP能解就行求解器依赖需要MILP求解器如Gurobi/CPLEX/YALMIPMATLAB自带优化工具箱就够换模型灵活性下层模型一变KKT条件要全部重推下层模型变只要还是凸问题换求解器设置即可如果你的目标是发高质量期刊论文最终版本还是建议用KKT或更强的算法来收敛到全局最优。但起步阶段、课程设计、中期汇报先用PSO嵌套把整个链条跑通用结果验证模型的合理性效率会高非常多。4. MATLAB落地实现数据生成、下层求解器与上层迭代框架4.1 参数与基础负荷数据生成MATLAB版本我用的是R2022b理论上R2019b以上都能跑只需要优化工具箱。代码里我用了一个固定的随机种子这样你直接复现就能得到和我一样的曲线。如果对随机数种子没概念你就理解成“骰子被固定了”每次掷出来的点数是可复现的。基础负荷我手工构造了一个典型工作日的曲线晚上七八点有一个高峰凌晨三四点到低谷。这部分数据你也可以换成自己实际测量到的负荷数据不影响整体框架。车辆分三类60%的家用通勤车晚上6点到第二天早上8点在家电池容量主要集中在40到60kWh之间20%的日间运营车白天9点到17点充电20%的夜间物流车晚上8点到第二天早上7点充电。每类的最大充电功率设为7kW这是常见的慢充桩功率。为了代码简洁代码里直接用“代表车辆”参数不逐辆车生成。如果要细化可以把每类的参数进行随机扰动然后用聚类。这个展开工作量太大这里先不细说。4.2 下层问题用linprog求解单类车充电计划下层问题对每一类车单独求解。变量是24时段的充电功率目标是最小化充电费用。约束分三块功率上下限、SOC范围、离开时刻SOC要求。我把这些约束写成矩阵形式喂给linprog。MATLAB里linprog标准型是[ \min f^T x \quad s.t. \quad A x \le b, ; Aeq x beq, ; lb \le x \le ub ]下层目标函数中( f ) 就是电价向量 ( p )每个时刻单位电量的费用。功率上限直接把未接入时段的ub设为0下限是0向量这个处理最省事。SOC递推关系是等式约束。用累积矩阵 ( C ) 表示[ SOC_t SOC_0 \frac{\eta \Delta t}{B} \sum_{\tau1}^{t} P_{\tau} ]写成矩阵形式就是[ \frac{\eta \Delta t}{B} C P SOC_t - SOC_0 ]其中 ( C ) 是下三角全1矩阵。离开时刻要求是 ( SOC_{T_{dep}} \ge SOC_{tar} )也就是[ \frac{\eta \Delta t}{B} C(T_{dep},:) P \ge SOC_{tar} - SOC_0 ]不等式方向要反一下变成 ( - \frac{\eta \Delta t}{B} C(T_{dep},:) P \le SOC_0 - SOC_{tar} )才能喂给linprog的A矩阵。这个方向搞反是新手最常见的错误没有之一。核心函数如下function [P_opt, cost, exitflag] solve_lower_layer(p, B, SOC0, SOC_tar, ... t_arr, t_dep, P_max, eta, dt) % 求解单类电动汽车的充电计划 % 输入p-电价(24x1), B-电池容量(kWh), SOC0-初始SOC % SOC_tar-目标SOC, t_arr-接入时刻, t_dep-离开时刻 % P_max-最大充电功率(kW), eta-充电效率, dt-时段长度(h) % 输出P_opt-最优充电功率(24x1), cost-充电费用, exitflag-求解状态 T length(p); f p * dt; % 目标函数系数注意电价乘时间得到电量费用系数 % 功率上限未接入时段ub为0 ub zeros(T, 1); lb zeros(T, 1); ub(t_arr:t_dep) P_max; % SOC递推等式约束: SOC_t SOC0 eta*dt/B * cumsum(P) C tril(ones(T, T)); % 累积矩阵 Aeq (eta * dt / B) * C; beq SOC0 * ones(T, 1) (eta * dt / B) * C * zeros(T, 1); % 注意这里实际表示的是 SOC_t - SOC0 累积充入电量/容量 % 即 Aeq * P SOC_need_t - SOC0其中 SOC_need_t 是实际应达SOC % 更严谨的写法是令 Aeq (eta*dt/B)*C, 然后等式右边是目标SOC轨迹 % 但这里我们不用每条SOC都指定只用范围约束所以改成不等式 % SOC上下限约束: SOC_min SOC0 eta*dt/B * C*P SOC_max A_soc (eta * dt / B) * C; b_soc_upper SOC0 * ones(T,1); % 这部分要重写成 SOC_max - SOC0 % 修正 b_soc_upper (0.9) - SOC0 * ones(T,1); % SOC_max 0.9 A_ineq1 A_soc; b_ineq1 b_soc_upper; A_ineq2 -A_soc; b_ineq2 SOC0 * ones(T,1) - 0.1; % SOC_min 0.1 % 离开时刻SOC SOC_tar % 等价于 -eta*dt/B * C(T_dep,:)*P SOC0 - SOC_tar A_dep - (eta * dt / B) * C(t_dep, :); b_dep SOC0 - SOC_tar; A [A_ineq1; A_ineq2; A_dep]; b [b_ineq1; b_ineq2; b_dep]; options optimoptions(linprog, Display, off, Algorithm, dual-simplex); [P_opt, cost, exitflag] linprog(f, A, b, [], [], lb, ub, options); end代码里我留了一处注释提醒你注意等式约束那块我最终用了不等式而不是强行把每一条SOC都写成等式。为什么不写成等式因为SOC曲线本身是中间量你只关心它的范围不关心它具体怎么走写成等式反而会过度约束。这是实际调试中很容易踩到的点。4.3 上层PSO主循环粒子表示、适应度评估与迭代更新上层PSO每只粒子就是24个时段的电价。粒子位置初始化为电价上下限之间的随机数速度初始化为小随机数。适应度函数就是把电价传给下层算出所有类车辆的充电负荷叠加基础负荷后计算方差。PSO的标准更新公式[ v_i w v_i c_1 r_1 (pbest_i - x_i) c_2 r_2 (gbest - x_i) ] [ x_i x_i v_i ]惯性权重 ( w ) 我采用线性递减策略从0.9降到0.4这样前期全局搜索能力强后期局部收敛效果好。( c_1 ) 和 ( c_2 ) 都取1.5( r_1 ) 和 ( r_2 ) 是0到1之间的随机数。主循环代码如下%% 上层PSO主程序 clear; clc; rng(42); % ------------------ 基础数据 ------------------ T 24; dt 1; eta 0.92; % 基础负荷单位MW P_base [26 24 23 22 22 23 26 30 34 38 41 42 41 39 38 40 42 48 55 54 48 40 33 28]; % 三类车的代表参数 class_info struct(); class_info(1).name 家用通勤车; class_info(1).count 120; % 车辆数量 class_info(1).B 50; % 电池容量kWh class_info(1).SOC0 0.3; class_info(1).SOC_tar 0.9; class_info(1).t_arr 18; class_info(1).t_dep 7; class_info(1).P_max 7; % kW class_info(2).name 日间运营车; class_info(2).count 40; class_info(2).B 60; class_info(2).SOC0 0.2; class_info(2).SOC_tar 0.8; class_info(2).t_arr 9; class_info(2).t_dep 17; class_info(2).P_max 20; % 快充桩 class_info(3).name 夜间物流车; class_info(3).count 40; class_info(3).B 80; class_info(3).SOC0 0.25; class_info(3).SOC_tar 0.9; class_info(3).t_arr 20; class_info(3).t_dep 7; class_info(3).P_max 15; % ------------------ PSO参数 ------------------ nPop 30; maxIter 50; p_min 0.3; % 电价下限元/kWh p_max 1.2; % 电价上限 dim T; w_max 0.9; w_min 0.4; c1 1.5; c2 1.5; % 初始化粒子位置和速度 particle rand(nPop, dim) * (p_max - p_min) p_min; velocity randn(nPop, dim) * 0.05; pbest_x particle; pbest_fit inf(nPop, 1); % 计算初始适应度 for i 1:nPop pbest_fit(i) fun_upper(particle(i,:), class_info, P_base, eta, dt); end [gbest_fit, idx] min(pbest_fit); gbest_x pbest_x(idx, :); fit_history zeros(maxIter, 1); % ------------------ 迭代求解 ------------------ for iter 1:maxIter w w_max - (w_max - w_min) * (iter / maxIter); for i 1:nPop % 更新速度与位置 r1 rand(1, dim); r2 rand(1, dim); velocity(i,:) w * velocity(i,:) ... c1 * r1 .* (pbest_x(i,:) - particle(i,:)) ... c2 * r2 .* (gbest_x - particle(i,:)); particle(i,:) particle(i,:) velocity(i,:); % 边界处理越界粒子反射回可行域 particle(i,:) max(min(particle(i,:), p_max), p_min); % 评估适应度 fit_i fun_upper(particle(i,:), class_info, P_base, eta, dt); if fit_i pbest_fit(i) pbest_fit(i) fit_i; pbest_x(i,:) particle(i,:); end end [min_fit, idx] min(pbest_fit); if min_fit gbest_fit gbest_fit min_fit; gbest_x pbest_x(idx, :); end fit_history(iter) gbest_fit; fprintf(迭代次数: %d, 最优适应度: %.6f\n, iter, gbest_fit); end边界处理我用了反射而不是吸收因为吸收会导致大量粒子堆积在边界上种群多样性下降后期容易提前收敛到边界上的次优解。反射可以让粒子在可行域内来回跳动搜索更充分。4.4 上层适应度函数串起下层求解与上层目标fun_upper是整个双层模型的核心连接点它拿到的是一组电价返回的是负荷方差。function f fun_upper(p, class_info, P_base, eta, dt) % 上层目标函数负荷方差最小化 % 1. 对每一类车辆求解下层问题 % 2. 汇总充电负荷 % 3. 叠加基础负荷计算方差 T length(p); P_chg_total zeros(T, 1); total_cost 0; for k 1:length(class_info) info class_info(k); [P_opt, cost_k, exitflag] solve_lower_layer(... p(:), info.B, info.SOC0, info.SOC_tar, ... info.t_arr, info.t_dep, info.P_max, eta, dt); if exitflag 0 % 下层无解直接返回一个很大的惩罚值 f 1e10; return; end % 乘以车辆数量单位转成MW P_chg_total P_chg_total (P_opt * info.count) / 1000; total_cost total_cost cost_k * info.count; end L_total P_base P_chg_total; f var(L_total); % 负荷方差 % 可选对平均电价过高或低于成本价的情况加入惩罚 avg_price mean(p); if avg_price 1.0 || avg_price 0.4 f f 1e3 * abs(avg_price - 0.7); end end有个细节值得说明充电功率单位是kW基础负荷单位是MW所以在汇总的时候我除了除以1000。单位不统一这种事在数学里不是问题但在代码里就是看结果时数据全对不上。我吃过这个亏所以专门提一句。5. 仿真结果与参数敏感性关注哪些指标才说明模型有效5.1 基准场景下的调度结果解读固定随机种子运行完整代码后最优电价会呈现明显的“凌晨低谷-晚高峰”形态凌晨0点到4点的电价被压到0.3元附近晚上8点到10点被抬到1.1元以上。车的充电行为会跟着电价走从原来的“一回家就冲”变成“等到电价便宜再冲”。最终总负荷曲线中晚高峰的EV突起被削平凌晨的低谷被填高峰谷差大概能下降40%左右。我建议你复现时重点看三张图第一张是基础负荷和总负荷的对比曲线看削峰填谷效果第二张是每类车的充电功率曲线看行为转移是否合理第三张是迭代收敛曲线看PSO在多少代之后趋于稳定。三张图对应三个问题模型有没有效果、模型机理对不对、算法收没收敛。5.2 电价上下限对结果的影响电价上限从1.0提到1.5削峰效果会更明显因为高电价把晚高峰充电需求“吓退”了车主更愿意等到半夜再充。但代价是车主的充电费用上升。如果你把电价下限从0.3降到0.1凌晨充电会更便宜更多负荷会转移到深夜但要小心别把谷段负荷填出另一个小高峰。实际调参的时候我习惯把电价上下限和运营商购电成本挂钩比如购电成本是关键约束条件。这样做出来的结果对实际工程更有参考价值而不是拍脑袋定边界。5.3 车辆渗透率变化时模型的行为规律把车辆数量从200辆一路加到2000辆模型行为有很明确的规律不调度时峰谷差会急剧恶化因为大量EV集中在傍晚接入电网而调度后的EV负荷会均匀铺开到凌晨时段总负荷曲线反而比基础负荷更平滑。渗透率继续往上走2%到10%可能都能削峰填谷但到了20%以上你会发现凌晨谷段被填满之后已经没有多余空间这时单纯靠转移充电时间已经解决不了问题。想继续优化就必须引入V2G反向放电让电动汽车在晚高峰反向送电、凌晨在充电相当于把车当成分布式储能。这也是这个方向往后研究的自然延伸。5.4 那几个“看起来合理但一细想就出问题”的指标做双层优化结果里最容易出现的一种“假合理”是下层每辆车的SOC都满足约束了上层的负荷方差也变小了但你看一下充电功率曲线发现车辆充电时间被切得七零八碎——比如8点充一个时段15点再充一个19点又出现一个尖峰。为什么会这样因为电价曲线本身不平滑粒子群搜出来的电价有很多局部毛刺导致充电功率跟着来回跳。这种情况我会加一个电价平滑惩罚项或者对每次PSO更新后的电价做滑动平均滤波。虽然会稍微偏离纯最优解但工程上更可接受电池也不喜欢频繁启停充电。6. 复现和调试中的常见坑以及接下来可以扩展的方向6.1 下层LP无解先检查约束方向再检查SOC边界linprog返回exitflag为负值的时候第一反应不应该是怀疑求解器出问题了而是排查约束矩阵。我遇到最多的两个错误一个是上面提到的离开时刻SOC不等式方向写反了另一个是SOC下界设得太高比如车辆初始SOC是0.5目标SOC是0.7理论上需要充0.2但电池容量小、接入时间段又很短最大可充电量根本不够达到目标值。这类问题在求解器层面是无解的在物理层面就是“车充不够电”。解决办法也很直接检查每类车的接入持续时间乘以最大功率再除以电池容量看是否大于需要充入的SOC差值。可以在代码里加一个前置判断一旦发现某类车充不满就报错提示而不是等linprog返回无解后再去瞎猜。6.2 PSO收敛慢或者陷入局部最优双层优化里PSO收敛慢有一个先天原因每评估一次适应度都要跑多个LP相当于适应度函数本身就很贵。所以不要盲目加大种群数和迭代次数要优先保证每次评估的信息量。我实测下来种群30、迭代50次已经能得到稳定的结果再往上翻一倍计算时间翻倍但目标函数值只下降不到1%。如果发现PSO陷入局部最优优先检查粒子的初始分布。初始电价如果只在0.3到0.6的低价区间均匀分布粒子群很容易在低价区抱团搜索不到高价策略的收益空间。解决办法是把初始位置人工打散比如一部分粒子初始化为0.3到0.5一部分是0.5到0.8一部分是0.8到1.2。6.3 单位错误、数据类型错误这类低级但致命的坑再提醒一次这个事因为我自己翻过车电价单位是元/kWh充电功率单位是kW时间单位是h相乘得到的费用单位才是元。但如果充电功率是kW、基础负荷是MW两者相加就必须先把充电功率除以1000。另外MATLAB的var函数默认是除以N-1算样本方差。如果你想要的是总体方差记得改成var(L,1)。这两种写法得到的数值不同读论文时对不上数据多半就是这个原因。6.4 从这套代码继续扩展的几个方向代码跑通之后往哪个方向扩展取决于你的课题需要。如果导师让你增加模型的现实性可以把下层问题换成二次规划充电费用之外再加上电池损耗成本linprog换成quadprog改动量不大。如果让你做大规模算例那就把车辆聚类模块换成真正的kmeans再把PSO换成遗传算法做对比分析不同启发式算法的收敛性。如果让你追求算法层面的创新那就要回到KKT转化法用强对偶把问题变成单层MILP再对比不同MILP求解器的性能。另外可以加入配电网潮流约束把上下层模型扩展成一个网络约束下的三层优化问题电网层、充电站聚合商层、电动汽车用户层。这个方向是目前比较热门的主题做出来发论文的面会比较宽。按照我个人的经验这套PSO嵌套linprog的框架最大价值不光是能跑出结果而是让你在反复调试过程中把“双层耦合”四个字理解透彻。等你哪一天不看代码也能随手写出上下层各自的目标函数、约束条件和耦合变量再回头去看那些KKT转化的论文效率会完全不一样。
返回列表