☰
基于Matlab蒙特卡洛模拟的电动汽车充电负荷曲线仿真方法
2026/9/26 18:46:57 网站建设 项目流程

电动汽车负荷曲线这件事,做配电网规划、充电站选址、有序充电控制的人几乎天天都要跟它打交道。我最早接触这个项目的时候,目标很直接:用Matlab跑蒙特卡洛模拟,把电动汽车分成“一充、二充、三充”三种充电行为,然后仿真出一天24小时的负荷曲线,看看不同充电习惯对配电网的影响到底有多大。做完之后发现,这套思路不仅适合写论文,对实际工程估算也特别有用,因为电网侧做容量评估、变压器选型的时候,缺的就是这种“有依据的负荷曲线”,而不是拍脑袋给个同时率。

这篇文章把整个项目从建模思路、概率参数设定、Matlab代码实现到结果分析的完整过程都记录下来。适合正在做电动汽车充电负荷仿真、微电网规划、或者想用蒙特卡洛方法做随机过程建模的读者参考。尤其是那些卡在“二充、三充怎么定义”“充电时间分布怎么取”“结果曲线毛刺太多怎么平滑”这些问题上的朋友,我的实测经验可以直接照搬。

1. 整体设计与思路拆解

1.1 为什么蒙特卡洛适合干这件事

电动汽车充电负荷的本质是一个随机过程:每辆车什么时候充、充多久、用多大功率、电池剩余电量多少,都不是固定值。如果你用“所有车辆统一晚7点开始充、统一充4小时”这种确定性的方法去估算,结果会和实际偏差很大,因为真实世界里的充电行为分散在一天中的各个时段。

蒙特卡洛模拟的思路恰恰就是应对这种随机性的:先给每个随机变量设定一个合理的概率分布,然后通过大量抽样模拟出尽可能接近现实的事件组合,最后把结果统计出来。它不追求“精确预测某一辆车的充电行为”,而是通过成百上千辆车的随机组合,把充电负荷的统计规律还原出来。

用生活化的比喻来说,这就相当于你调查1000个人的午饭时间。如果只问一个人然后推断所有人,误差会大得离谱;但如果把1000个人的吃饭时间都统计出来,你就能得到“12点前后是高峰、11点和13点相对较少”这样可靠的结论。蒙特卡洛做的事情,就是把这个“抽样统计”过程自动化、规模化。

1.2 一充、二充、三充分类的业务逻辑

把电动汽车按“一天充电次数”分成三类,背后是真实的用户行为画像:

  • 一充(每日单次充电):最常见。典型场景是通勤族,白天开车上班,晚上回家插上充电枪,充一夜,第二天满电出发。这类用户占大多数,充电时间高度集中在傍晚到夜间,正好叠加在居民用电晚高峰上。
  • 二充(每日两次充电):常见于单位停车场有充电桩、或者中午有固定补电习惯的用户。早上到公司插上充一两个小时,晚上回家再充满。这类用户的负荷会出现“小高峰+大晚高峰”的双峰特征,对写字楼配电系统影响比较大。
  • 三充(每日三次及以上充电):对应充电频繁的营运车辆(网约车、出租车、物流车)或通勤距离特别长的用户。这类用户充电时段比较分散,单次充电量不大,但对充电桩的占用时间长,负荷分布也更加随机。

这种分类最大的好处是:它能让你从“一辆车的充电行为”升级到“一群车的聚合负荷”。做电网侧分析的时候,你不需要关心具体某一辆车,只需知道这个区域内“一充、二充、三充用户各占多少”,就能推算出整体负荷曲线的大致形状。

这种分类逻辑还有一个工程上的实用价值:不同类别对电能质量的影响不一样。一充用户集中充电会拉高晚高峰峰值;二充用户在白天产生额外负荷,可能和光伏出力叠加或错开;三充用户则因为分散度高,反而可能“填谷”。搞清楚三类占比,配电网规划时就知道该在哪个时段预留多少容量。

1.3 为什么选Matlab而不是Python

Matlab在这个场景里确实比Python顺手,尤其是在做概率分布拟合、随机数生成和绘图这三件事上。fitdist、makedist、random这一套概率工具箱,几乎为蒙特卡洛量身定做。histogram出频次分布图,plot画负荷曲线,代码量远小于Python的matplotlib方案。

更关键的是,Simulink里做微网仿真时,很多时候需要把Matlab脚本生成的负荷曲线作为输入数据文件直接喂进去。这个流程在Matlab生态里是无缝衔接的,省去跨工具传递数据的麻烦。所以我最终选择了纯Matlab脚本实现整套蒙特卡洛仿真。

不过说实话,如果你对Python更熟,用numpy.random也能实现同样的效果。工具只是手段,核心还是搞清楚怎么给每辆车“抽签”。下面这几节内容,无论是用Matlab还是Python,思路都是通用的。

2. 三类充电行为的数学建模与参数设计

2.1 核心随机变量:起始充电时间、日行驶里程、充电功率

仿真前,我们得先明确要随机抽样哪些变量。我把影响充电负荷的核心变量归纳为三个:

第一是起始充电时间。它直接决定负荷落在哪个时段。晚7点开始充和晚10点开始充,对晚高峰的叠加效果完全不同。实操中,一充用户用正态分布拟合很贴切,均值约在19:00到19:30,标准差1到2小时;二充用户早上的起始时间集中在8:00到9:30,晚上的起始时间在19:00到20:30;三充用户起始时间则用均匀分布或分段分布更合理,因为他们的充电行为太散。

第二是日行驶里程。它决定了每次充电需要补充多少电量。工程上通常用对数正态分布来拟合。日均行驶里程一般在30到50公里之间,但方差很大——有每天只开10公里的,也有开上百公里的。里程越长,充电时长越长,负荷持续时间越久。

第三是充电功率。慢充桩常见7kW,快充桩功率可达60kW甚至更高。仿真时不能所有车都用7kW,否则结果会太理想化。我建议按用户类型混合设置:一充和二充用户以7kW为主,三充用户中可以混入一定比例的快充。

2.2 充电时长的推导逻辑

充电时长不是直接抽一个随机数,而是通过“需求电量÷充电功率”推导出来的,这样更符合物理规律:

  • 先由日行驶里程和百公里电耗算出日耗电量:D × e,其中e一般取15到20kWh/100km。
  • 再用日耗电量 ÷ 充电功率得到理论充电时长。
  • 但时长不能超过电池容量和可用SOC上下限的限制,否则要截断处理。

这里有一个容易犯的错误:直接用“路程除以功率”或者“时间直接抽个随机数”都会导致充电量不合理。比如一辆车只开了10公里,却充了4小时,在现实中几乎不会发生。所以我在代码里始终坚持“由里程计算需求电量,再由需求电量反推充电时长”的逻辑,这也是结果曲线更贴近实测的关键。

2.3 三类EV参数的对照表

为了让大家快速落地,我把三类的典型参数汇总成一张表。这里的数值来自我实测迭代后的经验值,和多项公开文献数据基本吻合,你可以直接抄作业,再根据自己所在城市的情况微调:

类别起始充电时间分布日行驶里程分布充电功率占比参考
一充正态分布,均值19:00,标准差1.5h对数正态,均值约35km,波动较大7kW慢充为主50%~70%
二充早峰8:30±0.8h,晚峰19:30±1.2h对数正态,均值约60km7kW慢充,少量11kW20%~35%
三充均匀分布或三段式分布,覆盖全天对数正态,均值约100km7kW慢充+60kW快充混合10%~20%

关于占比,别小看这个参数,它对结果影响非常大。如果是居民区为主的小区,一充比例可能达到70%以上;如果是商业区或机场附近,三充比例会明显上升。我建议仿真的第一件事,就是把“场景”和“占比”绑定起来思考,而不是盲目套一个默认值。

2.4 时间粒度的选择:15分钟还是1小时

负荷曲线的时间粒度,直接影响曲线平滑度和计算量。用1小时粒度算得快,但会掩盖充电负荷的突变特征,晚高峰的“尖峰”形状会被拉平;用1分钟粒度细节足够,但数据量和计算时间会大幅增加。

我的经验是15分钟粒度是性价比最高的选择。一天24小时分为96个时点,数据量适中,曲线形状又保留了足够的时序细节。实测下来,15分钟粒度下,晚高峰的起峰时刻、峰值大小和回落趋势都能清晰识别,这比1小时粒度精细得多。这个精度对于分析“晚高峰叠加效应”足够了。

3. Matlab仿真实现流程与核心代码

3.1 整体仿真框架

整个仿真的流程拆解下来,可以概括为五步:

  1. 初始化参数:设置车辆数、模拟次数、三类占比、电池参数。
  2. 对每辆车按概率抽取充电类型。
  3. 根据类型对应的概率分布,抽样起始充电时间和日行驶里程。
  4. 计算充电时长,把功率叠加到当天96个时点对应的负荷曲线上。
  5. 重复模拟多次,取平均得到最终的期望负荷曲线。

这里有一个值得强调的细节:最终负荷曲线应该用多次模拟的平均值,而不是单次模拟的结果。因为单次模拟的随机性太大,画出来的曲线毛刺很多,波动明显;多次模拟取平均之后,曲线才会收敛到一个稳定形状。这是蒙特卡洛方法的收敛思想在工程上的直接体现。

3.2 参数初始化与三类占比设置

%% 参数初始化 clear; clc; close all; rng(2024); % 固定随机种子,保证结果可复现 N = 1000; % 电动汽车数量 M = 100; % 蒙特卡洛模拟次数 T = 96; % 一天24小时,按15分钟粒度分为96个时点 dt = 0.25; % 每个时点对应0.25小时 % 三类充电用户占比(一充/二充/三充) ratio = [0.6, 0.3, 0.1]; % 电池参数 capacity = 60; % 电池容量,kWh soc_min = 0.1; % 最小允许SOC soc_max = 0.9; % 最大允许SOC(不追求充满到100%) e_consum = 0.18; % 百公里耗电,kWh/km % 充电功率设定(kW) P_slow = 7; % 慢充 P_fast = 60; % 快充

固定随机种子这一步,很多新手会忽略。如果不写rng(2024),每次运行代码生成的随机数都不同,结果曲线会“每次跑都不太一样”。论文复现、方案对比的时候,这个差异会让你非常被动。固定种子之后,至少同一台机器上结果可完全复现,调试和对比参数时也更方便。

3.3 对每辆车抽样充电类型与充电参数

%% 单日负荷累加容器 P_day = zeros(M, T); % 存放每次模拟的96点负荷曲线 for m = 1:M P_curve = zeros(1, T); % 当前模拟的负荷曲线 for i = 1:N % (1) 抽取充电类型 type = randsample([1, 2, 3], 1, true, ratio); % (2) 抽取起始充电时间(转换为时段索引1~96) if type == 1 % 一充:晚高峰充电,均值19:00,标准差1.5小时 start_hour = 19 + 1.5 * randn(); elseif type == 2 % 二充:早晚各一次,本次只模拟晚间的充电段,早上另算 start_hour = 19.5 + 1.2 * randn(); else % 三充:充电时间较分散,用均匀分布覆盖8:00~22:00 start_hour = 8 + 14 * rand(); end % (3) 抽取日行驶里程(单位:km),用对数正态分布 mileage = lognrnd(log(35), 0.5); if type == 3 mileage = lognrnd(log(100), 0.5); % 三充用户里程更长 end % (4) 由行驶里程计算所需充电量 energy_need = mileage * e_consum; % (5) 受SOC上下限约束的可用充电量 energy_max = capacity * (soc_max - soc_min); energy_need = min(energy_need, energy_max); if energy_need < 1 energy_need = 0; % 电量需求太小,认为今天不需要充 end % (6) 根据功率计算充电时长(小时) if type == 3 && rand() < 0.3 P_charge = P_fast; % 三充用户30%概率用快充 else P_charge = P_slow; end charge_hours = energy_need / P_charge; charge_slots = floor(charge_hours / dt); % 转为时段数 % (7) 将功率叠加到负荷曲线上 start_idx = floor(start_hour / dt) + 1; for k = 1:charge_slots idx = start_idx + k - 1; if idx <= T P_curve(idx) = P_curve(idx) + P_charge; else break; % 超出当天24:00的部分不再累计 end end end P_day(m, :) = P_curve; end

这段代码是整篇的核心,其中三充用户用了均匀分布模拟“分散充电”,其逻辑是这类用户的开机充电时间最没有规律。如果你实际数据显示三充用户集中在中午和傍晚各来一次,这里可以改成双峰分布,不一定非要均匀分布。

3.4 修正二充用户的早高峰

上面的代码有个偷懒之处:二充用户我只模拟了晚间充电。实际上二充用户的特征是早晚各充一次,只算晚充会漏掉白天的负荷峰值。完整的做法是早上也叠加一段充电功率:

%% 二充用户:额外叠加早高峰充电段 if type == 2 % 早上8:30左右到达单位开始充电,充电约2小时 start_morning = 8.5 + 0.8 * randn(); morning_slots = floor(2 / dt); % 充电2小时 start_idx_m = floor(start_morning / dt) + 1; for k = 1:morning_slots idx = start_idx_m + k - 1; if idx <= T P_curve(idx) = P_curve(idx) + P_slow; end end end

这段代码要放在上面的if type == 3语句之后、内层车辆循环结束之前。加入早高峰叠加后,二充用户的负荷曲线就会出现“上午小峰+晚间大峰”的双峰形态,这比只算晚充更真实。

我一开始做这个项目时偷懒没加早充电段,仿真出来的二充曲线和实际监测数据有明显偏差,一度以为是分布参数写错了。后来对比数据才发现,是漏了早充电段。你如果拿仿真结果去和实测数据做校验,一定要记得补上这一块。

3.5 结果统计与曲线绘制

%% 统计结果:多次模拟取平均 P_final = mean(P_day, 1); % 期望负荷曲线 % 转成小时数据 t_axis = (0.5:T) * dt; % 每个时点对应的小时数 %% 绘制负荷曲线 figure('Color', 'w'); plot(t_axis, P_final, 'LineWidth', 2); xlabel('时间 (小时)'); ylabel('总充电功率 (kW)'); title('电动汽车充电负荷曲线(蒙特卡洛模拟)'); grid on; xlim([0 24]); % 顺便看一下三类车辆的占比和平均值信息 fprintf('仿真车辆总数:%d\n', N); fprintf('24小时总充电量:%.1f kWh\n', sum(P_final) * dt); fprintf('峰值功率:%.2f kW,出现在第 %.1f 小时\n', max(P_final), t_axis(P_final == max(P_final)));

多说一句,P_final在纵轴上代表的是所有车的总功率叠加值。如果想看单车的平均负荷,用P_final / N就行。论文里通常展示总负荷曲线,工程上我们更关心总功率对变压器容量的影响,所以这个尺度一般不改。

3.6 代码执行性能优化

当车辆数N很大(比如5000辆以上)、模拟次数M也很大(比如500次以上)时,双循环加内层叠加会有点慢。我实测下来1000辆车、100次模拟在普通笔记本上大约需要10到20秒,还可以接受。但如果做参数扫描,比如对比10组不同的占比,总时间就会逼近几分钟。

有一个简单的提速方法:把最内层的“时点叠加”改为向量化的分段累加。在96个时点的范围里,也可以用P_curve(start_idx:end_idx) = P_curve(start_idx:end_idx) + P_charge来替代循环。不过96次循环本身的代价很低,性能瓶颈主要在车辆循环,所以实战中我一般直接跑循环,代码可读性更重要。

另外,模拟次数M不是越大越好。M增大到100以上之后,mean(P_day, 1)的结果变化非常小,计算时间却线性增加。我建议先用M=50跑一遍看曲线形状,确认逻辑没问题之后再加大到100或200。

4. 仿真结果分析与参数敏感性讨论

4.1 典型负荷曲线解读

我按ratio = [0.6, 0.3, 0.1]跑出来的典型曲线,形状大致是这样:早上8到11点出现一个平缓的小高峰,这是二充用户的早充电段和三充用户的分散充电共同贡献的;晚上19点到21点出现明显的主峰,这是一充和二充用户的晚间充电叠加效应;凌晨1点以后负荷逐渐回落,但依然有一小部分充电行为持续到深夜。

最值得关注的指标是峰值功率和峰值出现时间。晚高峰充电负荷叠加在居民用电晚高峰上,是配电变压器夏季最担心的问题。通过仿真,你可以定量知道“该区域晚19点30分的充电负荷峰值大约是多少”,从而判断变压器的剩余容量是否够用。

如果某天看到曲线在晚上19点出现“尖刺”,不用慌,这恰恰说明一充用户的充电时间高度集中。这时候研究有序充电策略——比如把部分一充用户从19点引导到22点再充电——就能有效削峰。蒙特卡洛仿真可以做“基线场景”,有序充电策略可以在它的基础上叠加。

4.2 不同参数下的曲线形态变化

参数敏感性分析是这个项目最有意思的部分。我建议你重点扫这几个参数:

车辆数量N。负荷曲线形状不变,幅值近似线性增长。100辆车峰值约120kW,200辆车峰值约240kW——但实际会因为随机性略有偏差。这个线性关系可以用作快速估算。

三类占比ratio。这是影响曲线形态最明显的因素。一充占比高,晚高峰越尖锐;二充占比高,白天会出现更明显的“商业区午间峰”;三充占比高,曲线变得更加平缓,峰值下降。我试着把三充占比从10%提到30%,晚高峰峰值下降了约15%,但白天的平均负荷明显上移。这对“充电桩应该布局在住宅区还是办公区”的决策很有参考价值。

充电功率P_charge。如果全部用快充60kW,车辆充电时长大幅缩短,部分负荷从夜间转移到傍晚甚至下午,峰值可能更高且更陡。但如果适度混入快充,总负荷曲线反而更平滑。这颠覆了不少人的直觉——快充不一定推高峰值,关键看充电时间是否错峰。

4.3 结果的工程校验方法

仿真的结果需要和现实对一下账,否则就是自嗨。我最常用的校验方法是:把24小时总充电量除以车辆数,看单车日均充电量是否合理。

比如上面那段代码跑出来的结果,单辆车日均充电量大概在13到18kWh之间,这和一辆日均行驶50公里、百公里耗电18kWh的通勤电车基本吻合。如果计算结果单车日均充电量到了30kWh甚至更高,那一定是里程抽样或者SOC限制参数出了问题。

另一个校验方式是看峰值负荷率和同时率。充电负荷的峰值同时率一般在0.25到0.6之间。如果算出同时率超过0.8,那说明你的充电时间分布太集中,大概率是标准差设小了,或者随机种子不够多样。

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

5.1 代码层面容易踩的坑

问题1:随机数和初始化顺序错乱

这是新手最容易犯的错误,写代码时把概率抽样的顺序搞错,比如先抽取了start_hour,再根据type判断用哪个分布。虽然代码能跑,但结果毫无意义。我的建议是严格按“先类型、后参数”的顺序:先抽取充电类型,再根据类型抽取对应的起始时间和里程。

问题2:时间段索引越界报错

当start_hour抽到22点以后,加上充电时长会超出96个时点的范围。如果没有if idx <= T的判断,Matlab会直接报错或自动扩展数组导致后续结果错乱。代码里一定要加边界条件判断,超出24:00的部分直接忽略。

问题3:正态分布抽到负数时间

19 + 1.5 * randn()理论上可能抽到16点多,也可能抽到22点多,不会抽到负数。但如果均值设成凌晨2点、标准差设成3小时,就可能抽到负值,结果出现“负时间”的充电起始点。出现这种异常,先检查起始时间的均值是否太小。

5.2 仿真结果的“毛刺”问题

如果你第一次跑出来的负荷曲线像锯齿一样毛刺丛生,不要怀疑代码写错了,这是单次模拟的随机波动。解决办法有两个:一个是增大车辆数N,车辆越多,随机波动相对越小;另一个是增大模拟次数M取平均,曲线会越来越光滑。

我试过N从100加到1000后,曲线峰值波动从±8%降到±2%左右;再结合M=100次平均,最后的曲线已经非常稳定。如果你画出来的曲线依然“毛毛糙糙”,优先检查N不是M。

5.3 随机种子导致的复现问题

rng(2024)这个固定种子的作用被很多人低估了。没有它,你每次运行得到的结果都不同,做参数对比时会怀疑“是参数变化导致差异,还是随机波动导致差异”。固定种子后,重复运行代码能得到完全一致的结果,这给调参和论文复现省了很多麻烦。

如果你要对比不同占比策略对负荷曲线的影响,请务必在每组参数前都用同一个rng()值,否则对比结果的差异性可能来自随机数而不是参数本身。我踩过一次这个坑,当时以为“两次仿真的差异是占比变化引起的”,结果重新固定种子之后发现差异小了一半,差点得出完全相反的结论。

5.4 不同EV类型划分的交叉验证

有时候你会发现仿真结果与某个地区实测数据不吻合,最常见的原因是分类占比设错了。比如某市实际以出租车、网约车居多,三充比例可能高达三成以上,而不是默认的一成。解决方法是手上先找一份目标区域的充电行为统计数据,哪怕是问卷调研或充电平台的一周日志,先拟合出本地化的分布参数,再跑仿真。没有实测数据的城市,也建议先做敏感性分析,看结果对参数变化是否稳健。

最后再多说一句

我个人的体会是,这类蒙特卡洛仿真的价值不在“精确预测明天每个小时有几辆车在充电”,而在于把不同充电行为对电网负荷的冲击量化出来。哪怕只是把三种充电类型的占比从“默认值”改成“某个小区的实测值”,你就能得到一条和该小区高度相关的负荷曲线,这对变压器容量评估、充电桩布局、有序充电策略设计都是非常实用的输入。

最后再分享一个小技巧:运行完仿真,顺手把P_final保存成.mat或.csv文件,后面接入Simulink微网仿真、或者和其他负荷数据做叠加分析时,直接读文件就行,不用每次重新跑一遍随机过程。如果你后续还想深入,可以在这个模型基础上加入温度修正系数(冬天电池效率下降、夏天空调耗电)、分时电价引导机制,以及充电桩数量限制条件,模型的工程价值会再上一个台阶。

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

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

立即咨询