☰
基于蒙特卡洛的Matlab电动汽车充电负荷模拟全流程解析
2026/9/25 8:47:25 网站建设 项目流程

做配电网规划,或者做电动汽车接入影响分析的工程师,应该都被同一个问题折腾过:怎么把一片区域的充电负荷曲线估出来。用户规模、出行习惯、电池容量、充电功率全都在随机波动,指望用一条解析函数去推,根本不现实。这时候,用蒙特卡洛算法在Matlab里做随机抽样模拟,就成了最经典也最容易落地的一套做法。我把自己做这块仿真的完整套路理一遍,包括概率模型怎么建、参数怎么定、代码怎么写、结果怎么收敛,以及一些文档里不会写的坑。不管你是研究生开题做负荷预测,还是项目里需要出一版负荷曲线用于配电网评估,这套流程基本都能直接抄作业。

1. 蒙特卡洛在充电负荷模拟中的定位与整体思路

1.1 为什么这个场景非用随机抽样不可

充电负荷本质上是一堆随机事件叠加出来的结果:每个用户今天开多少公里、几点回小区、回家插不插枪、用多大功率充电,全部是随机变量。单独看任何一辆车,你几乎无法预测它今晚到底充不充电,但放到群体层面,规律就出来了——日行驶里程近似服从对数正态分布,回家时刻集中在傍晚到夜间,充电功率取决于充电桩类型,充电比例大概率落在某条调研曲线附近。

蒙特卡洛的核心思路,就是把这些“有规律但不确定”的随机事件,按概率分布大量抽样,生成成千上万个“虚拟用户的一天”,再把他们的充电过程累加起来。打个比方,你在靶场上随机射箭,单支箭落在哪完全随机,但射得足够多之后,弹孔的整体分布形状会稳定下来,和真实的概率分布越来越接近。蒙特卡洛收敛的本质就是大数定律:样本量大了,统计特征趋近于理论真值。

我自己早期也尝试过直接用典型日负荷曲线乘以渗透率来估,那属于“拍脑袋+修正系数”,做宏观估算勉强能用,一旦要落到具体台区、具体季节、具体充电策略,偏差会大到没法交代。改用蒙特卡洛之后,每一个假设都有概率模型支撑,最后还能顺手给出置信区间和分位数,这让结果在讨论“最坏场景”时非常有说服力。

1.2 从“单辆车”到“区域曲线”的完整模拟流程

整个模拟框架并不复杂,核心链路是这样的:

  1. 确定模拟对象与总量:模拟多少辆车、什么车型、电池多大、续航多少、慢充快充比例如何。
  2. 从概率分布中抽样生成单辆车的出行与充电参数,比如日行驶里程、回家时刻、是否充电、充电功率。
  3. 根据车辆状态计算单辆车的充电功率时段序列,本质上是个随时间变化的矩形脉冲。
  4. 把区域内所有车辆的充电序列按时间对齐后叠加,得到当日总负荷曲线。
  5. 重复执行多次蒙特卡洛迭代,每次迭代都会得到一条“可能出现的当日曲线”。
  6. 对多次迭代结果逐点求平均、求分位数,得到期望曲线和置信区间。

这个流程之所以要“先单辆、再叠加”,是因为所有不确定性都在单辆车层面发生,叠加过程只是线性求和。你不需要构造一个超大规模的解析方程组,只要能把单辆车的充电过程写清楚,后面就是循环、累加、统计。

我习惯先在纸面上把这六步画出来,再动手写代码。因为后面每次调整参数,都是在改第一步和第二步的分布或常量,改完就知道结果会受到什么影响。如果一上来就闷头写循环,很容易把逻辑绕晕。

2. 充电负荷模型的关键模块与数学描述

2.1 车辆出行参数:日行驶里程与回家时刻

充电负荷模型的第一个关键模块,是车辆当天到底消耗了多少电。我们通常用“日行驶里程”来推算耗电量:行驶越多,电池剩余越少,回家后需要充的电就越多。

日行驶里程在实测数据里呈右偏分布,大部分车主每天只开二三十公里,少部分跑长途的能开到一两百公里。对这种数据,最常用的拟合分布是对数正态分布:

f(x) = 1 / (xσ√(2π)) · exp(−(ln x − μ)² / (2σ²))

实际取参数时,μ=3.3、σ=0.6 对应的均值大约在 exp(3.3 + 0.6²/2) ≈ 33 公里,比较接近城市通勤场景。如果你手上有本地交通调查数据,直接用 fitdist 拟合实测里程会更准,这一点后面会再强调。

回家时刻是第二个关键变量。绝大多数家用车充电发生在回家之后,因此回家时刻直接决定了充电负荷的起始位置。最常用的做法是用截断正态分布:均值取 17~18 点,标准差取 1.5 小时,再截断到 [16, 24] 区间,避免抽样出现凌晨三四点回家这种与场景不符的情况。如果你的研究区域是大型购物中心或高速服务区,回家时刻分布就得换成对应的停车时长分布模型,不能再套住宅区参数。

2.2 充电行为参数:充电时刻、功率与目标SOC

有了当天耗电量,还得确定车辆是否充电、什么时候开始充、用多大功率充、充到多少停。这四个参数对应着四个概率模型。

是否充电用充电概率来表示。住宅区夜间充电概率一般在 0.8 到 0.9 之间,商业区则低很多,可能只有 0.3 左右。开始充电时刻通常是回家时刻加一个随机延迟,延迟可以用 0~0.5 小时的均匀分布,也可以按实测用指数分布,模拟“回家先歇一会儿再插枪”的真实情况。

充电功率不能一概而论。慢充桩常见的功率有 3.5kW、7kW、11kW、22kW 几档,未来快充渗透率提高后还会有 60kW 以上直流桩参与。我一般通过概率加权抽样的方式为每辆车分配功率:比如 3.5kW 占 20%,7kW 占 60%,11kW 占 15%,22kW 占 5%。目标 SOC 取 0.9 比较常见,部分车主只充到 0.85 就拔枪,这也可以做成一个分布。

充电持续时长则由起始 SOC、目标 SOC、电池容量和充电功率共同决定:

T_charge = (SOC_target − SOC_start) × BatteryCap / P_charge

这里 SOC_start = 1 − D/Range,D 是日行驶里程,Range 是续航里程。如果 D/Range 接近 1,SOC_start 会很低,充电时长就会很长,这种情况需要设置物理下限,比如 SOC_start 最低取 0.2,防止出现“电池完全跑空还继续开”的荒谬场景。

2.3 为什么群体叠加之后曲线会自然变光滑

单辆车的充电负荷其实非常难看,就是几个矩形脉冲:从插枪瞬间开始,功率跳到 7kW,持续两三个小时,再突然跳回 0。但如果直接把几百上千辆车叠加起来,曲线会出人意料地光滑。

原因是大量脉冲的起始时刻、持续时长、功率大小互相错开,错位叠加把单车的“棱角”抹平了。这就像食堂打饭,单个窗口的排队人数波动很大,但整个食堂中午的总人流量曲线是相对稳定的。理解这一点很重要,它解释了为什么蒙特卡洛模拟不要求精确预测每一辆车的行为——群体统计特性决定了总负荷曲线的基本形态。

从仿真实现角度看,这还意味着你不需要用秒级步长。把时间离散成 15 分钟一个点,每天 96 个点,对充电持续两小时以上的慢充场景来说精度完全够。时间步长取得太小,计算量成倍增加,曲线并不会因此更准确,因为主要的误差来源是概率模型本身,而不是时间分辨率。

3. Matlab代码实现与参数配置

3.1 先把所有参数集中到文件头部

我写这类仿真代码有个习惯,所有可调参数全部集中放在脚本开头,做成一个“参数区”。这样每次换场景,只需要改参数区的数值,不需要在几十行代码里翻找。

参数区至少包含这些内容:模拟车辆数、蒙特卡洛迭代次数、时间步长、电池容量、续航里程、目标SOC、充电概率、功率等级与概率、回家时刻分布均值与标准差、日行驶里程分布参数。

车辆数和迭代次数是两个非常关键的规模参数。车辆数少了,单次模拟曲线会有明显的锯齿;迭代次数少了,均值曲线在不同随机种子下跳动很大。我常用的组合是 1000 辆车 × 1000 次迭代,得到的结果已经足够稳定,Matlab 大约几十秒能跑完。如果只是演示或教学,500×500 也能看趋势。

3.2 核心循环的代码实现与逻辑拆解

下面是一段可以直接复用的核心代码,我按照“先抽样,再计算,后叠加”的顺序写,方便理解:

rng(2025); % 固定随机数种子,保证结果可复现 N_Veh = 1000; % 模拟车辆数 N_MC = 1000; % 蒙特卡洛迭代次数 T_step = 15; % 时间步长,单位分钟 nSteps = 24 * 60 / T_step; % 每天96个点 Cap = 60; % 电池容量,kWh Range = 400; % 续航里程,km SOC_target = 0.9; % 目标充电SOC P_levels = [3.5, 7, 11, 22]; % 充电功率等级,kW P_probs = [0.2, 0.6, 0.15, 0.05]; % 对应概率 Curves = zeros(N_MC, nSteps); for iter = 1:N_MC % 1. 日行驶里程,对数正态分布 D = lognrnd(3.3, 0.6, N_Veh, 1); % 2. 回家时刻,截断正态分布在16到24点之间 T_home = 17 + 1.5 * randn(N_Veh, 1); T_home = max(16, min(24, T_home)); % 3. 起始SOC,设0.2下限防止极端情况 SOC0 = max(0.2, 1 - D / Range); % 4. 是否当天充电 isCharging = rand(N_Veh, 1) < 0.85; % 5. 按概率分配充电功率 P = datasample(P_levels, N_Veh, 'Weights', P_probs); curve = zeros(1, nSteps); for i = 1:N_Veh if ~isCharging(i) continue; end % 回家后延迟0~0.5小时开始充电 startTime = T_home(i) * 60 + 0.5 * rand * 60; if startTime >= 24 * 60 continue; end % 计算充电持续时长,单位小时,最长限制8小时 hChg = (SOC_target - SOC0(i)) * Cap / P(i); hChg = min(hChg, 8); % 充电结束时刻,限定在当天24点内,跨天部分归到次日 endTime = min(startTime + hChg * 60, 24 * 60); % 将功率叠加到对应时间步上 tIdx = floor(startTime / T_step) + 1; eIdx = floor(endTime / T_step); curve(tIdx:eIdx) = curve(tIdx:eIdx) + P(i); end Curves(iter, :) = curve; end meanCurve = mean(Curves, 1); p95Curve = prctile(Curves, 95, 1);

这段代码有几个设计点说一下。

日行驶里程和回家时刻的抽样,我直接用向量化操作一次生成 1000 个样本,比在单车循环里逐辆 randn 快得多。充电起始 SOC 的计算也完全向量化了,但真正叠加负荷时还是需要逐辆车做,因为每辆车的开始时间和结束时刻都不同,没法用一次矩阵运算直接解决。

叠加时这里采用的是一个工程近似:充电从 tIdx 步开始,到 eIdx 步开始前结束,中间所有整步都按全额功率计。因为 15 分钟步长相对慢充时长来说足够细,这一步近似带来的误差通常在 1% 以内,为了计算效率完全值得。

代码最后对 Curves 沿第一维取平均值和 95 分位数,得到的结果既能看期望场景,也能看较恶劣场景。

3.3 核心参数速查表与调参逻辑

把常用参数整理成一张表,方便对照设置:

参数含义取值/分布说明
N_Veh模拟车辆数500~2000太少曲线锯齿明显,太多计算变慢
N_MC蒙特卡洛迭代次数500~2000决定均值曲线稳定性
T_step时间步长10~30分钟慢充场景15分钟足够
Cap / Range电池容量/续航60kWh / 400km按主流车型调整
SOC_target目标电量0.85~0.95反映用户充电习惯
充电概率当日充电比例0.75~0.9住宅高、商业低
功率档位桩的功率分布3.5~22kW按实际充电桩普查数据
回家时刻到家时间分布N(17, 1.5) 截断可换成实测直方图

调参有一条经验:优先改“充电概率”和“功率分布”,因为它们对曲线峰值和形状的影响最显著。日行驶里程分布参数会影响总充电量的绝对值,但峰值的出现时段主要由回家时刻和充电开始延迟决定。抓主要矛盾,别的细节先放一放。

4. 结果分析、收敛性判断与场景扩展

4.1 怎么判断你的模拟结果收敛了

蒙特卡洛模拟最怕的情况是:结果还没收敛,你就拿着均值曲线去写报告了。判断收敛不能靠肉眼看曲线变得平滑,因为曲线平滑是车辆数多导致的,不代表多次迭代之间的重复性足够好。

我常用的办法是跑一组“迭代次数递增”的对比实验:分别用 100、300、500、1000 次迭代去跑同一组参数,观察某几个关键指标——比如晚高峰峰值、总充电电量——随迭代次数增加的波动幅度。当峰值从 900kW 变成 912kW,再变成 915kW,1000 次时还在 915kW 上下小幅波动,基本可以认为曲线收敛了。

更正规一点的量化指标是峰值变异系数:

CV = std(Peaks) / mean(Peaks)

其中 Peaks 是每次迭代模拟得到的当日负荷峰值序列。CV 降到 0.03 以下,通常说明峰值估计已经比较稳定。如果你需要更严格的统计精度,就继续增加迭代次数,或者把每次迭代的车辆数加大,两者的效果略有不同:加大车辆数能压低单次模拟的抽样噪声,增加迭代次数能提高均值估计的稳定性。

一个容易被忽视的点:不同时刻的收敛速度不一样。负荷高峰时段因为样本量大、波动被“平均”得比较好,收敛快;凌晨低谷时段样本稀疏,相对波动大,收敛慢。因此你看到的均值曲线可能在高峰段非常稳定,低谷段却还在轻微抖动,这并不奇怪,分析时心里有数就行。

4.2 除了均值曲线,更要看分位数场景

做工程评估的人往往会问一句:你这曲线是“平均情况”还是“最坏情况”?平均值会抹掉极端场景,而配电网规划又特别关心极端场景——变压器容量裕度、线路负载率,都要按可能出现的高负荷去校核。

所以我在给出均值曲线的同时,一定会输出分位数曲线。上面的代码里已经有一行 p95Curve = prctile(Curves, 95, 1),它的含义是:在 1000 次模拟中,每个时刻取 95% 分位数的负荷值。把所有时刻的 95% 分位数值连起来,就得到一条“有 95% 置信把握不会超过”的负荷曲线上包络。

实操中,P50 曲线用于做电量估算和全年能耗分析,P95 曲线用于设备选型和容量校验。如果项目特别强调极端风险,还可以看 P99。需要提醒的是,P95 是逐点统计的,不代表“同一天内所有时刻都同时达到 95% 分位”的场景,真要做联合场景分析,需要从 Curves 矩阵里抽取若干条完整曲线作为典型场景,再做潮流计算。这一点容易被忽略,但做规划评估时非常关键。

4.3 三种场景扩展玩法,试过都说好用

基础版本跑通之后,扩展方向其实很灵活。我试过三种最有实用价值的玩法。

第一种,研究不同区域类型。把充电概率从 0.85 调到 0.6,回家时刻分布换成商业区模式,就能模拟商业办公区白天充电为主的负荷形态;换成高速服务区场景,则要新增快充功率档位和高功率比例。区域变了,其实只改几个分布参数,代码框架完全不用动。

第二种,研究快充渗透率提升的影响。把功率等级从 [3.5, 7, 11, 22] 改成 [7, 22, 60, 120],并调整概率权重,就能看到负荷峰值明显抬高、峰谷差拉大的趋势。这种场景对充电桩规划特别有用,可以直接回答“未来三年快充桩占比上升到 30% 以后,台区还扛不扛得住”这类问题。

第三种,进阶一点的多日连续模拟。单日模型假设每天都是从“满电或接近满电”状态出发,但实际上智能充电、有序充电应用下,车辆连续多日的 SOC 状态是有记忆的。把当天结束时的 SOC 作为次日初始 SOC 的边界条件,连续模拟一周甚至一个月,就变成了一个马尔可夫链式的迭代过程。这样可以评估夜间谷电时段充电策略的效果,也是当前有序充电研究里常见的做法。

5. 常见问题排查与工程落地建议

5.1 五个容易踩的建模坑与排查思路

先说一个最常见的坑:起始SOC出现大量0.2的堆叠平台。这个问题几乎每个人都遇到过。原因是日行驶里程分布参数没调好,或者续航里程设置偏小,导致 D/Range 频繁超过 0.8,SOC0 被下限 0.2 截断到同一起点。结果就是充电时长扎堆,负荷曲线出现不合理的“同步充电”现象。排查方法是画出 D/Range 的分布直方图,看看尾部是否过厚;如果尾部很厚,要么把对数正态参数调小,要么给 SOC0 下限换个更合理的取值。

第二个坑是凌晨时段负荷异常地低甚至趋近于零。原因通常是开始充电时刻被截断在 24 点内,跨天的充电负荷被直接扔掉。实际中很多车是半夜还在充的,凌晨 1~5 点恰恰是慢充负荷的重要时段。我的处理方式是把 endTime 截断到 24 点没错,但如果你需要分析次日凌晨的负荷,就得把时间轴扩展到次日早晨,或者把“跨天未完成部分”记录到单独的变量里,再做拼接。单纯把凌晨数据舍弃,会让总充电量偏低,均值曲线在 0~6 点这一段失真。

第三个坑是结果每次跑都不一样,复现不了。解决方案就是代码第一行写的 rng 固定种子。有时候你会在循环里用随机数,一旦固定了种子,整条随机链就固定了。调试时固定一个种子,批量跑场景时再放开,这样可复现和场景多样性都能兼顾。

第四个坑是跑得很慢。一个常见原因是把时间轴写成了最内层循环,比如对每辆车、每个15分钟都做一次 if 判断,光循环开销就大得惊人。优化方向是把内层循环里的逻辑尽量移到向量化抽样阶段,负荷叠加部分用矩阵索引批量赋值,就像我上面代码写的 curve(tIdx:eIdx)。如果车辆数超过 5000,还可以把外层 for iter 换成 parfor,配合 Parallel Computing Toolbox,速度提升非常明显。注意 parfor 里不能动态修改共享变量,我的做法是把每次迭代结果先存到临时变量,循环结束再一次写入 Curves。

第五个坑是充电时长的物理合理性。有些车起始 SOC 很低,功率又只有 3.5kW,算出来的充电时长可能超过 12 小时甚至 20 小时。如果不对充电时长做上限限制,深夜负荷会被拉出一长条平顶,明显不真实。我一般设 8 小时上限,也可以根据小区停车场管理规则设成 10 小时,但必须要有上限。

把常见问题和排查建议整理成一张速查表,方便现场对照:

现象可能原因排查建议
SOC0 大量等于 0.2D/Range 分布尾部过厚检查里程分布参数与续航设置
凌晨负荷偏低跨天充电被丢弃扩展时间轴或拼接跨天电量
多次运行结果差异大未固定随机数种子在脚本开头加 rng 语句
计算耗时过长循环层级设计不合理向量化抽样加批量索引叠加
深夜出现异常平顶充电时长未设上限增加最长充电时间限制

5.2 从模拟结果到报告输出的整理套路

模拟跑完,数据怎么交付也是有讲究的。我不会直接把 1000×96 的 Curves 矩阵丢给下游,而是整理成三类输出。

第一类是关键的统计曲线,包括 P50、P90、P95 三条曲线,保存成 CSV 文件,列是时间点,行是不同分位。第二类是单次模拟的典型场景曲线,比如从 1000 次迭代里挑出“峰值最高的一天”“总电量最大的一天”“最接近均值的一天”三个典型场景,方便给规划人员做潮流计算或储能配置分析。第三类是聚合指标,包括日总充电电量、峰值负荷、峰谷差、平均负荷率,直接汇总成一个表格。

绘图方面,Matlab 自带的 plot 就够用了。我会把 P50 画成实线,P95 画成虚线,再用 fill 命令把 P5 到 P95 之间的区域填充成半透明色,一眼就能看出负荷带的范围。图例、坐标轴标签、单位这些细节别偷懒,报告里直接能用的图,比再加工省很多事。

如果项目允许,尽量拿实际台区数据做一次校验:用实测的日行驶里程分布和回家时刻分布替换默认对数正态参数,重跑一遍模拟,把输出均值曲线和台区采集到的实际充电负荷曲线叠加对比。偏差在可接受范围内,这个模型才算真正在这个场景里落地了。

5.3 把参数当变量,把场景当输入

这套框架真正值钱的地方,不在某一次模拟的精确度,而在于把“充电负荷估算”从一次性的计算,变成了一个可以反复回答问题的工具。

我个人的习惯是写一个 runScenario 函数,把上面所有参数作为输入结构体传入,输出统计曲线和指标。这样每个新项目只需要调整参数区,模拟代码完全复用。从一开始就按这个思路组织代码,几个月后你会感谢自己当初没把参数硬编码到循环里。

做蒙特卡洛模拟这些年,最大的体会是:别追求“参数绝对准确”,因为充电行为本身就是分布式的、动态变化的。更务实的做法是明确告诉使用者,这套结果是基于哪些假设得到的,参数变化时曲线如何响应。有了这种敏感性分析的意识,仿真模型才真正具备工程价值,而不是一个只会在固定参数下输出一条曲线的黑盒。

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

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

立即咨询