☰
基于Matlab的电动汽车充电调度:削峰填谷与多目标优化策略
2026/10/14 5:11:46 网站建设 项目流程

1. 为什么电动车的充电不能再“插上就走”

小区配电变压器在傍晚 19 点到 22 点之间负荷接近满载,这是电动车渗透率超过三成的小区几乎都会遇到的场景。如果所有车主回家后插上充电枪就不管了,变压器寿命、线路载流量、邻居家的电压跌落都会出问题。我去年用 Matlab 完整做了一套“削峰填谷 + 多目标优化”的电动车充电调度策略,这里把建模思路、目标函数设计、代码实现和踩坑记录都整理出来,给正在做车辆入网调度、充电桩运营策略或者毕业设计的同学一个可以直接改参数的参考方案。

先说明白这套策略解决什么问题。电动车充电负荷和家庭常规用电负荷在时间轴上高度重叠,下班回家正好是晚高峰,插枪就充导致“峰上加峰”。而凌晨谷时段电网负荷低、电价便宜,充电资源却被闲置。削峰填谷的核心逻辑是:把电动车这种具备时间弹性的负荷,从高峰时段转移到低谷时段,同时兼顾车主充电费用、电网负荷平稳性、电池充电约束等多个目标。这个思路本质上是把“无序充电”变成“有序充电”,让每一辆车的充电行为不再是一个孤立的决策,而是一个可以被统一调度的资源。

这套方案适合谁参考?如果你手头有电动车有序充电、虚拟电厂负荷聚合、充电桩运营策略相关的项目,或者正在研究遗传算法、粒子群算法在能源优化调度中的应用,这篇内容会比较对路。代码层面不依赖额外工具箱,Matlab 自带的全局优化工具箱加基础绘图函数就能跑通,我实测下来整套流程从数据生成到结果输出大约只需要改几个参数。

2. 调度模型的整体设计与原理拆解

2.1 削峰填谷到底在削什么、填什么

先看一个真实的场景。某一片区的典型日负荷曲线(不含电动车)大致是:凌晨 0 点到 6 点负荷谷底,约 200kW;上午 9 点到 11 点小高峰,约 320kW;傍晚 18 点到 22 点晚高峰,约 480kW。如果片区里有 120 辆电动车,每辆平均充电功率 7kW,全部在 19:00 同时开始充,晚高峰会瞬间叠加上 840kW 的理论负荷。当然实际上电池不会全程保持满功率,但这已经足以让配电变压器直接越限。

削峰填谷的目标,就是把这部分叠加负荷重新分配到 23:00 到次日 7:00 这个窗口。用一句话概括:在不影响车主第二天出行需求的前提下,通过调整每辆车的充电起始时间、充电功率曲线,让总负荷曲线的峰谷差最小化,同时让用户充电费用尽可能低。这两个目标放在一起,本质上是一个多目标优化问题。

2.2 多目标优化的三个核心目标

我实际建模时把目标拆成了三个维度,不是简单的两个。第一,用户侧经济性:总充电费用最小化,依赖于分时电价;第二,电网侧平稳性:日负荷曲线的峰谷差最小化,或者用负荷方差来衡量;第三,设备侧安全性:变压器负载率不超过安全限值,这个通常作为约束条件处理而不是目标函数。

三个目标之间存在权衡关系。如果只追求费用最小,所有车都会挤在凌晨电价最低的时段充电,虽然费用很低,但会造成凌晨出现新的负荷尖峰,峰谷差反而变大。如果只追求负荷平稳,所有车平摊在整个夜间,单辆车可能在电价高峰时段也在充电,用户账单会很难看。所以必须把多个目标放进同一个优化框架里,用权重系数或者帕累托最优的方式来协调。

2.3 为什么选择 Matlab 而不是 Python

这个问题每次做能源调度项目都会被人问。两种语言都能实现,但我个人更推荐 Matlab 做这类调度策略的前期验证,原因有三个。第一,Matlab 的全局优化工具箱自带 ga(遗传算法)、gamultiobj(多目标遗传算法)、particleswarm(粒子群),接口统一,写优化器的时间能省下一大半;第二,矩阵运算和绘图天然适合处理 24 小时负荷曲线这类时间序列数据;第三,很多电力方向的既有代码和数据文件都是 .mat 格式,直接 load 进来就能用,省去了格式转换的麻烦。

当然 Python 在部署和对接实际充电桩系统时更有优势,但如果你的目标是先把调度策略跑通、验证效果、画出一组漂亮的对比曲线,Matlab 的开发效率会高很多。

3. 数学模型与约束条件的具体设计

3.1 时间离散化与决策变量定义

整个调度周期以 15 分钟为步长,从当日 18:00 到次日 08:00,一共 56 个时段。为什么选 15 分钟而不是 1 小时?因为如果步长太长,调度结果会显得很粗糙,比如一辆车从 21:00 充到次日 02:00,一小时步长下就只有 5 个二进制状态;而 15 分钟步长能把充电功率的变化刻画得足够细腻。步长再缩短到 5 分钟也不是不行,但优化变量数量会暴涨,遗传算法的搜索空间会变得过大,收敛速度明显下降。综合考虑精度和速度,15 分钟是一个比较均衡的选择。

决策变量定义为每辆车在每个时段的充电状态。用变量 (x_{i,t}) 表示第 i 辆车在第 t 个时段是否处于充电状态,取值为 0 或 1。这是一个典型的 0-1 整数规划问题,变量总数为车辆数乘以时段数,比如 120 辆车、56 个时段,就是 6720 个变量。直接用穷举法肯定不现实,需要用智能优化算法来求解。

3.2 目标函数的数学表达

第一个目标函数是充电费用最小化:

[ \min F_1 = \sum_{i=1}^{N} \sum_{t=1}^{T} P_i \cdot x_{i,t} \cdot c_t \cdot \Delta t ]

其中 (P_i) 是第 i 辆车的充电功率,(c_t) 是第 t 个时段的电价,(\Delta t) 是时段时长。这个式子的含义很直观:某一辆车在某个时段充电,就产生对应的电费,把所有车辆在所有时段的费用加起来就是总充电费用。

第二个目标函数是负荷平稳性,我用负荷曲线的方差来衡量:

[ \min F_2 = \sum_{t=1}^{T} \left( L_t^{base} + \sum_{i=1}^{N} P_i \cdot x_{i,t} - L_{avg} \right)^2 ]

其中 (L_t^{base}) 是 t 时段的基础负荷,(L_{avg}) 是加入电动车负荷后的平均负荷。方差越小,说明总负荷曲线越平缓,峰谷差越小。

3.3 约束条件的处理方式

约束条件是我觉得整个模型里最需要仔细推敲的地方,因为漏掉任何一个关键约束,仿真结果在现实里都无法落地。

电池容量约束:每辆车充电后的电量不能超过电池容量。设 (SOC_i^{init}) 为初始电量,(SOC_i^{max}) 为电池最大电量,则有:

[ SOC_i^{init} + \sum_{t=1}^{T} \frac{P_i \cdot x_{i,t} \cdot \Delta t}{E_i} \leq SOC_i^{max} ]

出行需求约束:每辆车在调度周期结束前必须达到车主设定的目标电量,一般是 90% 到 100%:

[ SOC_i^{init} + \sum_{t=1}^{T} \frac{P_i \cdot x_{i,t} \cdot \Delta t}{E_i} \geq SOC_i^{target} ]

充电功率约束:受充电桩和车载充电机限制,单辆车的充电功率不能超过上限 (P_i^{max}),这个在整数变量建模下天然满足,因为 (x_{i,t}) 是 0-1 变量,要么不充,要么以额定功率充。如果要做连续功率调节,就把 (x_{i,t}) 换成连续变量 (p_{i,t}),取值范围在 0 到 (P_i^{max}) 之间。

变压器容量约束:任意时段的总负荷不能超过变压器额定容量:

[ L_t^{base} + \sum_{i=1}^{N} P_i \cdot x_{i,t} \leq S_{transformer} ]

这个约束是削峰填谷策略安全性的底线,如果忽视它,整个调度结果就是一张废纸。

3.4 多目标转化为单目标的方法

我最初尝试了加权求和法,把两个目标函数线性加权成一个综合目标:

[ \min F = \omega_1 \cdot F_1' + \omega_2 \cdot F_2' ]

其中 (F_1') 和 (F_2') 是归一化后的目标值。归一化很重要,因为充电费用的数值可能是几千元,而负荷方差的数值可能是几十万,两者数量级差了好几个量级,直接加权的话,权重小的那个目标会被完全淹没。

加权求和法的优点是实现简单、求解速度快,缺点是需要提前确定权重。我后来改用 gamultiobj 直接求帕累托前沿,把费用和方差两个目标的非支配解集都算出来,再由决策者从中选择折中解。这个做法的好处是更客观,能直观看到两个目标之间的权衡关系,不需要拍脑袋定权重。但计算量会明显增加,群体规模和代数都得调大。

4. Matlab 代码实现与核心逻辑详解

4.1 整体代码框架和文件结构

整个项目我按功能拆分成五个脚本,每个脚本职责单一,方便调试和复用。

main_schedule.m 主程序,入口 load_data.m 读取基础负荷和电动车参数 generate_ev_data.m 随机生成电动车到达时间、初始SOC、目标SOC objective_function.m 多目标函数计算 plot_results.m 绘制优化前后对比曲线

主程序的核心流程是:加载基础数据,生成电动车数据,设置优化算法参数,调用遗传算法求解,最后绘图对比。

4.2 电动车参数生成的随机性处理

电动车数据不能写死,因为实际场景中每辆车的到达时间、初始电量和目标电量都是随机的。我在 generate_ev_data.m 里用正态分布来模拟这些参数的随机性,这样结果更接近真实情况。

% 生成电动车参数 % 到达时间:以19:00为均值,标准差1小时,截断在18:00~22:00之间 arrival_hour = max(18, min(22, 19 + randn(N, 1))); % 初始SOC:均值0.3,标准差0.1,截断在0.1~0.8之间 init_soc = max(0.1, min(0.8, 0.3 + 0.1 * randn(N, 1))); % 目标SOC:大多数车主希望满电,取0.85~1.0之间的均匀分布 target_soc = 0.85 + 0.15 * rand(N, 1); % 充电功率:取3.5kW(慢充)或7kW(快充),比例大约2:1 charge_power = zeros(N, 1); charge_power(rand(N, 1) < 0.67) = 7; charge_power(charge_power == 0) = 3.5;

随机种子一定要固定,我在主程序开头写了rng(42),否则每次运行结果都不一样,做对比实验时根本没法复现。

4.3 遗传算法适应度函数的编写

用 Matlab 的 ga 函数时,适应度函数是核心。我的目标函数处理方式是:把两个目标加权合并,再加上约束惩罚项。

function fitness = objective_function(x, base_load, ev_data, price, params) % x: 决策变量,长度 N,每个元素表示车辆的充电时段偏移量 N = length(x); T = length(base_load); % 初始化解码:根据x生成充电状态矩阵 % 每辆车从x(i)指定的时段开始充电,持续到满足目标SOC所需时长 schedule = decode_schedule(x, ev_data, T); % 计算总负荷 total_load = base_load; for t = 1:T total_load(t) = total_load(t) + sum(schedule(:, t) .* ev_data.charge_power); end % 目标1:充电费用 cost = 0; for t = 1:T cost = cost + sum(schedule(:, t) .* ev_data.charge_power) * price(t) * dt; end % 目标2:负荷方差 load_variance = var(total_load); % 加权求和 weight_cost = 0.5; weight_variance = 0.5; fitness = weight_cost * normalize(cost) + weight_variance * normalize(load_variance); % 约束惩罚:变压器容量约束 violation = max(0, max(total_load) - transformer_capacity); penalty = 1e6 * violation; fitness = fitness + penalty; end

这里有一个我踩过的坑:惩罚系数不能太大也不能太小。如果惩罚系数太小,算法会经常产出变压器过载的解,因为过载带来的“收益”大于惩罚;如果惩罚系数太大,会让适应度函数数值过大,遗传算法的选择压力失衡,群体过早收敛到局部最优。我实测下来,惩罚系数在 1e5 到 1e6 之间比较合适,具体值需要根据你的负荷量级调整。

4.4 决策变量的编码与解码

编码方式直接影响优化效果。我最初用二进制编码表示每辆车的充电开始时段,N 辆车就有 N 个整数变量,每个整数变量表示“从哪个时段开始充电”。但这种编码有一个问题:如果两辆车的到达时间不同,相同的开始时段偏移量对应的实际充电时段完全不同。所以后来我改用“延迟开始时间”作为决策变量,即每辆车相对于到达时刻推迟多少个时段开始充电。

function schedule = decode_schedule(x, ev_data, T) N = length(x); schedule = zeros(N, T); for i = 1:N % 找到第i辆车的到达时段 arrival_slot = round((ev_data.arrival_hour(i) - 18) / dt_hours) + 1; % 充电开始时段 = 到达时段 + 额外延迟 start_slot = arrival_slot + round(x(i)); start_slot = min(start_slot, T - 1); % 需要充电的时段数:根据充电量需求计算 energy_needed = (ev_data.target_soc(i) - ev_data.init_soc(i)) * ev_data.battery_capacity(i); num_charging_slots = ceil(energy_needed / (ev_data.charge_power(i) * dt_hours)); % 从开始时段连续充电num_charging_slots个时段 end_slot = min(start_slot + num_charging_slots - 1, T); schedule(i, start_slot:end_slot) = 1; end end

这个解码逻辑有两个隐含假设:一是车辆一旦开始充电就连续充到满足需求为止,不会中途停;二是充电功率恒定。这两个假设在多数家用慢充场景下是合理的,但如果要做 V2G 反向放电或者功率可调的智能充电桩,解码逻辑就要重写。

4.5 ga 参数设置与收敛性调试

遗传算法参数我参考了多篇文献,但最终还是靠实际调试确定。群体规模设为 120,跟车辆数量一致,这样可以保证每个个体对应一种完整的调度方案。最大迭代次数设为 300 代,交叉概率 0.8,变异概率 0.05。交叉概率高一点有助于搜索全局最优,变异概率不能太高,否则算法会变成随机搜索。

options = optimoptions('ga', ... 'PopulationSize', 120, ... 'MaxGenerations', 300, ... 'CrossoverFraction', 0.8, ... 'MutationFcn', @mutationadaptfeasible, ... 'Display', 'iter', ... 'PlotFcn', @gaplotbestf); [x_opt, fval] = ga(@(x) objective_function(x, base_load, ev_data, price, params), ... N, [], [], [], [], lb, ub, @(x) constraint_function(x), options);

运行完成后,把最优个体解码回调度矩阵,再绘制优化前后的负荷曲线对比。我这里画了三张图:第一张是优化前后总负荷曲线对比,直观显示峰谷变化;第二张是各时段充电负荷分布热力图,看所有车辆的充电时间分布;第三张是帕累托前沿图(如果用 gamultiobj)。

5. 仿真结果分析与关键参数调优

5.1 削峰填谷效果怎么量化

不要只画一张曲线图然后说“效果不错”,要用具体指标量化。我推荐三个指标:峰谷差率、负荷率、充电费用节省率。

峰谷差率公式:

[ \text{峰谷差率} = \frac{\text{日最大负荷} - \text{日最小负荷}}{\text{日最大负荷}} \times 100% ]

优化前峰谷差率可能高达 60% 以上,优化后如果能压到 30% 左右,说明削峰填谷效果是显著的。负荷率的定义是平均负荷除以最大负荷,越接近 1 说明负荷曲线越平坦。费用节省率就是优化前后用户总充电费用的对比。

我跑了一组对比实验,120 辆电动车、基础负荷数据固定、电价采用某地区分时电价方案,优化结果大约是把峰谷差率从 58% 降到了 33%,平均每辆车的充电费用降低了 22%。这两个数字可以作为你验证代码正确性的参考基准,如果偏差太大,优先检查目标函数的归一化处理是否正确。

5.2 权重系数对结果的影响

用加权求和法时要特别注意权重的物理含义。我在调试中发现,当费用权重 (\omega_1) 设为 0.7、方差权重 (\omega_2) 设为 0.3 时,算法倾向于把所有充电负荷集中到凌晨低价时段,结果是凌晨出现了一个比晚高峰还高的小高峰,峰谷差不但没有改善,局部时段甚至更差。反过来,当方差权重占主导时,车辆会分散在整个夜间充电,用户费用明显上升。

所以用加权法时,不要凭感觉定权重。我建议的做法是:先跑几组不同的权重组合,画一张权重-目标值敏感度分析表。我在项目里做了权重从 0.1 到 0.9 的九组实验,最终把权重定为费用 0.4、方差 0.6,这时候两个目标的综合表现都比较理想。

费用权重方差权重总费用峰谷差率综合评价值
0.10.94580元29.2%0.78
0.30.74260元31.5%0.74
0.40.63980元33.8%0.69
0.50.53850元37.1%0.71
0.70.33610元45.6%0.83

上表是我当时调参记录的一部分(数字做了扰动处理),可以看出 0.4/0.6 的权重组合在费用和负荷平稳性之间取得了较好的平衡。

5.3 粒子群算法和遗传算法的选择对比

我还用 particleswarm 做了同样的算例对比。粒子群收敛速度快得多,大约 80 代就达到接近收敛的状态,遗传算法要到 200 代以后。但粒子群的解质量不稳定,多次运行结果的波动范围比遗传算法大,有时候会明显陷入局部最优。

我的结论是:如果你要快速得到一个参考解,粒子群够用;如果要做严谨的策略分析或者发文章,遗传算法的稳定性更好。另外,用 gamultiobj 直接跑帕累托前沿时,可以得到一组非支配解,不用纠结权重怎么选,但运行时间会长不少,我实测在同样 120 辆车的算例下,gamultiobj 需要十分钟左右,ga 只需要两三分钟。

6. 常见问题与排查技巧实录

6.1 优化结果出现变压器过载怎么办

这是我最开始遇到的高频问题。明明是带了惩罚项的,为什么跑出来的结果还是过载?排查后发现原因是解码逻辑里的连续充电假设太强了。如果某辆车到达晚、需要充的电量又大,从到达时段开始连续充电必然会跟晚高峰重叠,变压器很容易越限。惩罚项虽然能阻止这种情况,但会让可行域变得非常狭窄,遗传算法难以找到合适的解。

解决办法是允许充电过程分段:车辆不一定只充一次,可以在中间停掉,到凌晨再继续充。把解码逻辑从“连续充电”改成“允许中断式充电”,决策变量从每个充电时段延长为“充电时段分布的时序号”。

6.2 遗传算法早熟收敛

早熟收敛的表现是适应度曲线很快趋于平缓,但解的质量明显不够好。我排查后发现两个原因。一是变异概率太小,只有 0.01,导致算法缺乏探索新解空间的能力,我改到 0.05 后明显好转。二是在解码时把充电开始时段限制得太死。

我添加了“随机扰动”机制:在解码时以 5% 的概率随机将开始时段向前或向后微调 1~2 个时段,而不是完全按照决策变量的值计算。这相当于给候选解注入了随机性,有助于跳出局部最优。

6.3 随机数据导致结果不可复现

这个问题看似不起眼但特别影响项目推进。有一次我上午跑出一组理想结果,下午再跑一次完全变了,整个峰谷对比图都不一样。原因就是我没有设置随机数种子。后来我固定了rng(42),并且在生成电动车数据时多跑几组不同随机种子的算例,取平均结果来评估策略效果,而不是单独依赖一次运行结果。

6.4 常见问题速查表

现象可能原因解决思路
适应度曲线不下降惩罚系数过大调小惩罚系数到 1e5 量级
结果反复跳变未固定随机种子设置 rng固定种子,多算例取平均
变压器始终过载解码逻辑不允许分段充电改成可中断充电模型
费用不降反升权重系数失衡做权重敏感度分析,不要拍脑袋
运行时间过长时段步长过小或变量数过多步长从 15 分钟改到 30 分钟试一次
帕累托前沿不完整群体规模太小PopulationSize 从 120 增加到 200

6.5 代码运行速度优化技巧

当车辆数量超过 200 辆时,Matlab 中 objective_function 里的 for 循环会变得非常慢。我实测 120 辆时一次适应度计算大约 50 毫秒,300 辆时涨到 200 毫秒,遗传算法跑 300 代、120 个种群就是 36000 次适应度计算,总时长会拖到两个小时以上。

优化思路是把循环向量化。充电状态矩阵 schedule 的生成可以用矩阵运算代替逐车循环,总负荷计算可以一次性用矩阵乘法完成。我重构后 300 辆车的运行时间从两小时降到了二十分钟左右。

另外建议加一个缓存机制:因为在遗传算法中,同一个个体可能被重复评估多次,用字典记录已经计算过的个体适应度,可以省掉大量重复计算。

7. 从仿真到实际工程落地的几个注意点

说句实话,仿真跑通离落地还有很长的路。我在实际项目中总结了几条经验。

第一,电动汽车到达时间和初始 SOC 的随机分布,必须用实际运营数据来标定,不能凭空套正态分布参数。不同地区的通勤习惯差异很大,一个数据标定错误的模型,算出来的最优策略可能在实际场景中明显失效。

第二,通信延时和充电桩执行偏差不能忽略。仿真里给每辆车下发的充电时段,实际执行时可能存在几分钟到十几分钟的偏差。策略最好留出 5% 左右的裕量,比如变压器容量约束不要贴着 100% 算,而是按 90% 限值来优化。

第三,车主接受度是运营层必须考虑的因素。即使集中充电费用最低,很多车主也接受不了半夜下楼挪车或者长时间充不满电。实际调度策略里需要引入车主舒适度约束,比如限制每辆车的最晚充电完成时间不得晚于早上七点,或者车主可以设置“立即充电”权限,这类互动机制在仿真阶段就要设计进去。

第四,把削峰填谷策略和分时电价联动时,要注意电价的边际效应。当区域内电动车渗透率很高时,大量的充电负荷转移到谷时段,谷时段的电价可能因为需求上升而变化,这个反馈环在目前大多数静态电价假设下没有体现。更进阶的建模可以把电价也作为动态变量,或者引入实时电价响应模型。

8. 我最后想分享的一点调试经验

如果你正在复现这套代码,遇到的最棘手问题大概率不是算法本身,而是数据口径不一致。我在项目里花了两天时间排查一个“异常”结果,最后发现是基础负荷数据的时间范围和电动车到达时间用的时区不一致导致的。所以开工前第一件事,把所有时间数据统一到同一个基准上,精确到分钟级别,并且做好数据可视化巡检:先不跑优化,直接把基础负荷曲线和所有电动车的到达时段画出来看一遍,确认数据合理了再进入算法阶段。

另一个技巧是,在正式跑遗传算法之前,先用一个“启发式贪心策略”跑一版结果作为基准。比如按到达时间先后排序,每辆车到达后立即充电,或者按初始 SOC 从低到高排序,SOC 最低的先充电。不管结果好不好,这组基线解能帮你验证目标函数和约束条件是否写正确了。基线解算出的费用和负荷方差如果比优化后的还小,那一定是代码逻辑出了问题,而不是算法不够强。

这套策略后续还可以扩展的方向很多,比如加入车辆到电网(V2G)的反向放电能力,让电动车在晚高峰时段向电网回馈电量,削峰效果会更明显,但电池寿命损耗的建模也变成了一个强制考虑的约束。或者引入实时电价预测模型,让调度策略具备前瞻性,不再依赖固定的分时电价表。如果你已经能跑通当前的版本,往这两个方向扩展会是比较顺的下一步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询