首页 / 资讯中心 / 文章详情

基于蒙特卡洛与Matlab的电动汽车充电需求仿真分析

基于蒙特卡洛与Matlab的电动汽车充电需求仿真分析 ★ FEATURED ARTICLE
电动汽车的“里程焦虑”和“充电效率”这两件事这几年我前前后后做了不少仿真分析。说实话刚接触这个领域的时候我习惯用车企公布的那组WLTP续航和快充功率曲线做估算结果一到实际场景就各种对不上。后来把蒙特卡洛方法引入进来配合Matlab把行驶里程和充电功率当成随机变量来建模整个分析的思路一下就打开了。这篇博文我会完整讲清楚为什么这个场景适合蒙特卡洛而不是单纯的平均值估算行驶里程和充电功率这两个核心变量怎么拆解、怎么选概率分布以及Matlab从零搭建一套可复现的蒙特卡洛仿真框架的具体代码和参数。内容覆盖从基础建模到小区充电负荷评估的实战案例适合正在做电动汽车能耗分析、充电设施规划或者电网负荷预测的朋友参考新手照着做也能跑通。1. 为什么用蒙特卡洛啃这块硬骨头1.1 电动汽车的真实使用场景远比想象复杂我一开始犯过的错就是拿一个固定的“典型用户”来代表所有人然后用标称续航去估算充电需求。结果做出来的结论要么偏乐观要么偏悲观放不到实际项目里用。真正的问题在于每个车主每天开多少公里、什么时候回家、插上什么功率的充电枪、电池还剩多少电这些全是随机变量。行驶里程看着是个简单数字实际上背后叠加了一大堆影响因素。驾驶风格差异很大同样一段路有人开空调加急加速能耗能高出30%以上。环境温度更明显冬季低温下电池活性下降再加上座舱加热实际续航经常只剩标称值的六到七成。道路工况也不一样城市拥堵走走停停高速巡航风阻大能耗特性完全是两回事。再加上车辆负载、胎压、雨雪天气这些因素真实的行驶里程本质上是一个分布不是一个点。充电功率同样复杂。家用慢充桩有3.3kW、7kW、11kW几种常见规格公共快充桩从50kW到350kW都有而车辆实际能“吃”进去的功率还取决于电池的SOC状态和BMS策略。电池电量低的时候充电功率高电量接近80%以后功率会大幅下降这是电池保护机制决定的不是桩的问题。所以你要评估一个车主的充电时长或者一个小区夜间的总负荷光靠单个“平均功率”去推肯定不准必须把场景的随机性建到模型里。1.2 蒙特卡洛方法在这个场景里的独特价值蒙特卡洛的核心思想特别简单既然我很难用解析公式算出所有随机因素叠加后的结果那我就用计算机大量生成符合概率分布的随机样本逐一把每个“虚拟场景”跑一遍最后统计这些结果得到近似真实的分布特征。你可以把它理解成掷骰子理论上我知道每个面出现的概率是六分之一但真正想验证这个结论最直接的方式就是掷几万次然后数每个面出现的次数。放在电动汽车场景里一次“掷骰子”就是生成一个完整的虚拟用户一天/一夜包括日行驶里程、回家时间、剩余电量、使用哪种充电桩。跑一万次你就得到了一万种可能的路网和电网交互场景。然后统计这一万个结果能回答很多过去很难回答的问题这个小区如果电车渗透率到30%夜间变压器峰值负荷会到多少某根快充桩在周末高峰时段有多少概率排队如果气温骤降区域充电需求会增长多少蒙特卡洛的另一个价值在于捕捉极端情况。平均值法最怕的就是“尾部风险”比如一百个人里九十九个回家后错峰充电就一个人恰好赶上晚高峰同时段插枪这一个人的行为单独看无所谓但聚合成一群人的时候峰值叠加效应就出来了。蒙特卡洛天然能把这种小概率但高影响的事件采样出来这是它比确定性估算最核心的优势。1.3 Matlab做蒙特卡洛的天然优势Matlab不是我唯一用过的工具Python也能做但在这个具体任务上Matlab确实有几点顺手的地方。第一是概率建模生态完整。Statistics and Machine Learning Toolbox里直接有makedist、fitdist、lognrnd、normrnd这一批函数数据拟合、分布定义、随机抽样一条龙不用自己手写逆变换采样。第二是向量化计算能力。蒙特卡洛的本质是海量重复抽样Matlab的矩阵运算恰好适合一次性生成一万个样本、批量计算配合parfor还能直接吃满多核CPU。第三是可视化省事。做完一万次模拟画直方图、累计分布、箱线图就是几行代码的事拿来出报告特别方便。当然工具再顺手模型思路不对照样白搭。下面把行驶里程和充电功率这两个主角先拆清楚再进入代码。2. 建模第一课把行驶里程和充电功率拆成概率问题2.1 行驶里程不是固定值它是一堆随机变量的叠加在蒙特卡洛模型里单辆车的日行驶里程我最常用对数正态分布lognormal distribution。为什么选它而不是普通正态分布因为行驶里程有两个天然约束最小值不可能为负而且大多数人的日里程集中在某个区间少数人可能跑很长距离。对数正态正好天然满足“非负右偏”这两个特征用来描述日行驶里程比正态分布合理得多。参数怎么定如果你手头有车队的实际行驶数据直接用fitdist做最大似然拟合是最靠谱的。没有实测数据时可以参考一些公开的出行统计一般城市通勤车日均行驶里程在30到50公里左右标准差大致20到30公里。换算到对数正态的两个参数mu和sigma我习惯的做法是先定均值E(X)和标准差Std(X)再反推% 根据期望和标准差构造对数正态分布参数 meanDist 40; % 日均行驶里程期望值, km stdDist 20; % 标准差, km mu log(meanDist^2 / sqrt(meanDist^2 stdDist^2)); sigma sqrt(log(1 stdDist^2 / meanDist^2));能耗率也一样不是常数。常温下表显能耗可能稳定在15 kWh/100km附近冬季开暖风或者夏季开制冷能耗率会明显上移。我通常用正态分布来描述能耗率均值按季节切换比如夏季14.5、冬季18.5单位kWh/100km标准差给到2到3。这样每次模拟抽样时能耗和里程两个变量一乘就是一次出行的总耗电量。SOC变化的核心公式很简单但要注意单位换算socUsed dailyDist / 100 * energyConsume; % 本次耗电量, kWh socArrival socDepart - socUsed / battCap; % 到家剩余SOC, 0-1这里的socDepart是出发时的电量battCap是电池总容量。如果socArrival小于0说明这个虚拟用户当天没撑到家属于“里程焦虑”事件蒙特卡洛跑完之后可以专门统计这部分概率这个数字对充电桩布局很有参考价值。2.2 充电功率也不是恒定值快充曲线的坑很多刚做仿真的人容易把充电功率当成一个定值这是最大的坑。真实充电过程尤其是直流快充功率随SOC呈明显的下降曲线也就是常说的“CC-CV”两阶段恒流阶段电量低电流顶格跑功率接近峰值电池电压抬到一定水平后转恒压阶段电流逐步减小功率一路下滑。我在模型里做了一个简化的分段处理SOC在0%到80%之间功率近似按额定快充功率的90%输出SOC超过80%以后线性衰减到额定功率的20%左右。这个处理足够工程使用也能反映“充得越满越慢”的真实体验。慢充桩因为功率本身低恒流阶段的占比很大简化成恒定功率基本没问题但快充必须按曲线来否则单次充电时长会严重低估。充电功率本身的离散随机性也要建模。一个车主可能家里有7kW的慢充桩也可能只在外面用50kW的快充。我通常给充电功率设定一个离散概率分布chargePower [3.3, 7, 11, 50]; % kW, 充电枪类型 powerProb [0.30, 0.35, 0.20, 0.15]; % 对应使用比例 idx randsample(numel(chargePower), numSim, true, powerProb); chargePowerUsed chargePower(idx);这套离散抽样的含义是在这个虚拟用户样本里30%的人用3.3kW慢充15%的人用50kW快充。如果你做的是小区场景快充比例可以调低如果是高速服务区场景快充比例就得大幅调高。参数完全跟着研究目的走。2.3 概率分布选型与参数标定很多朋友会纠结“到底用什么分布”我的经验是别过度纠结先用直观合理的分布跑通再拿实测数据回来校准。除了前面说的对数正态还有几个常见选择到家时间插枪时刻一般晚高峰集中在18点到21点可以用正态分布或者混合正态分布建模均值设19点半、标准差1.5小时左右。离家时间早上6点到9点之间同样用正态分布均值7点半。初始SOC这个变量受前一天充电行为影响简单做法是均匀分布在0.3到0.9之间精确做法是把它和充电行为耦合让模型“自己跑出”SOC分布。环境温度如果研究季节影响可以用历史温度数据的经验分布或者直接按冬季/夏季两个典型工况分别跑。参数标定上有历史数据就用fitdist拟合没数据就用公开统计的均值方差反推千万别为了显得精确而瞎给一个看起来很细的参数。蒙特卡洛的价值在于捕捉随机性和波动范围跟精确到小数点的“伪精确”本来就是矛盾的。3. Matlab实操从零跑通一个完整的蒙特卡洛模拟3.1 环境准备与随机数控制我用的是Matlab 2023bStatistics and Machine Learning Toolbox和Parallel Computing Toolbox是主要依赖。版本低于R2020a问题不大但有些函数名和功能有差异建议至少用R2020a之后的版本。写蒙特卡洛代码第一件事就是控制随机数种子。这一步看似简单实际非常容易忽略。如果没有rng(42)固定种子每次运行结果都不同调试的时候会非常痛苦因为你没法判断结果变化是代码改动的效果还是随机噪声。固定种子之后同样的代码每次跑出来的结果完全一致这样才能做对比实验。%% 基础参数配置 rng(42); % 固定随机数种子结果可复现 numSim 10000; % 模拟次数1万 battCap 60; % 电池容量, kWh targetSOC 0.9; % 插枪后续充目标SOC chargerEff 0.92; % 充电效率关于模拟次数很多人一上来就跑十万次其实没必要。蒙特卡洛的标准误差大致和根号N成反比N从1000提到10000精度提升约3倍而N从10000提到100000精度只再提升3倍计算量却涨了10倍。我一般先用10000次跑通画出结果分布观察稳定性和收敛性再按需调高。3.2 完整代码框架与关键函数讲解下面这套代码是一个最小可用版本覆盖日行驶里程、到家SOC、充电时间的完整蒙特卡洛流程。%% 输入随机变量抽样 % 1. 日行驶里程 — 对数正态分布 meanDist 40; stdDist 20; muDist log(meanDist^2 / sqrt(meanDist^2 stdDist^2)); sigmaDist sqrt(log(1 stdDist^2 / meanDist^2)); dailyMileage lognrnd(muDist, sigmaDist, numSim, 1); % 2. 能耗率 — 正态分布冬季工况 energyConsume normrnd(18.5, 2.5, numSim, 1); % 3. 到家时刻 — 正态分布, 均值19:30 arriveHour normrnd(19.5, 1.5, numSim, 1); arriveHour max(min(arriveHour, 24), 0); % 截断到0-24h % 4. 充电功率 — 离散分布 chargePower [3.3, 7, 11, 50]; powerProb [0.30, 0.35, 0.20, 0.15]; idxPower randsample(numel(chargePower), numSim, true, powerProb); chargePowerUsed chargePower(idxPower); %% 核心计算: 耗电与充电时长 consumedKwh dailyMileage / 100 .* energyConsume; % 本次行驶耗电 socArrival 0.8 - consumedKwh / battCap; % 假设出发时80%电量 socArrival max(socArrival, 0); % 低于0说明途中耗尽 % 需要充电的电量充到90% needKwh (targetSOC - socArrival) * battCap; needKwh max(needKwh, 0); % 充电时长估算快充按分段功率近似 chargeHour zeros(numSim, 1); for i 1:numSim if chargePowerUsed(i) 20 % 慢充按恒定功率 chargeHour(i) needKwh(i) / (chargePowerUsed(i) * chargerEff); else % 快充考虑CC-CV两阶段 socStart socArrival(i); pPeak chargePowerUsed(i) * 0.9; if socStart 0.8 ePhase1 (0.8 - socStart) * battCap; tPhase1 ePhase1 / (pPeak * chargerEff); else tPhase1 0; end ePhase2 (targetSOC - max(socStart, 0.8)) * battCap; pTail pPeak * 0.4; tPhase2 ePhase2 / (pTail * chargerEff); chargeHour(i) tPhase1 tPhase2; end end %% 结果统计 fprintf(平均日行驶里程: %.2f km\n, mean(dailyMileage)); fprintf(平均充电需求电量: %.2f kWh\n, mean(needKwh)); fprintf(平均单次充电时长: %.2f h\n, mean(chargeHour)); fprintf(里程焦虑事件概率(SOC耗尽): %.2f%%\n, ... sum(socArrival 0) / numSim * 100); % 分位数结果——看尾部风险 q quantile(chargeHour, [0.5 0.9 0.95 0.99]); fprintf(充电时长P50%.2fh, P90%.2fh, P95%.2fh, P99%.2fh\n, q);这段代码有几点要说明。lognrnd和normrnd是Matlab内置的随机数生成函数运行效率很高。randsample里我用了true参数代表有放回抽样离散分布抽样用它最简单。慢充和快充分开计算是因为前面说过快充功率不是恒定值分段近似能保留这个特性同时计算量又不会太大。循环里的快充分支只对快充样本执行其余样本完全向量化跑了1万次也只要一两秒。3.3 结果可视化与统计量输出光看fprintf输出的几个均值不够蒙特卡洛的精华在分布不在均值。我每次跑完至少会画三张图充电时长直方图、日行驶里程累计分布、SOC到达分布。这三张图分别对应“要等多久”“要跑多远”“到家剩多少电”是分析和汇报的核心素材。%% 可视化 figure; subplot(2,2,1); histogram(chargeHour, 50, FaceColor, #4C72B0); xlabel(单次充电时长 (h)); ylabel(频次); title(充电时长分布); subplot(2,2,2); histogram(dailyMileage, 50, FaceColor, #DD8452); xlabel(日行驶里程 (km)); ylabel(频次); title(行驶里程分布); subplot(2,2,3); histogram(socArrival, 30, FaceColor, #55A868); xlabel(到家SOC); ylabel(频次); title(到家剩余电量分布); subplot(2,2,4); ecdf(chargeHour); xlabel(充电时长 (h)); ylabel(累计概率); title(充电时长累计分布);直方图能直观看出分布形态和拖尾情况累计分布图适合读“概率小于某阈值”。比如从ECDF曲线上你能直接读出P90充电时长是多少小时这个数字对充电场站设计特别重要它意味着“90%的情况充电时长不超过这个值只有10%会更久”。我还建议算一下置信区间。蒙特卡洛本身是随机抽样输出结果也是一个随机变量所以mean(chargeHour)并不是一个确定值而是围绕真值波动的估计。标准误差近似等于std(chargeHour) / sqrt(numSim)跑一次可以顺手输出se std(chargeHour) / sqrt(numSim); ci95 mean(chargeHour) [-1.96 1.96] * se; fprintf(平均充电时长 95%%置信区间: [%.3f, %.3f] h\n, ci95);这个置信区间能直接回答“样本量够不够”的问题。区间宽度如果小于你项目的要求精度就不用再增加样本库了。4. 进阶案例实战小区夜间充电负荷评估4.1 场景设定与参数表单车模型跑通之后最有价值的应用是评估一个区域内大量电动汽车同时充电对电网的影响。我先说一个真实做过的案例背景一个中型居民小区规划100个车位未来电动化渗透率到30%。要评估小区变压器在夜间高峰时段的负荷情况以及是否需要扩容。这个案例和单车模型最大的区别是必须把时间维度拉进来。每辆车的到家时间、开始充电时间、需要的充电时长各不相同而变压器负荷是这些充电行为的时域叠加。我按15分钟一个时间步长从18点到次日8点共56个时步把每辆车的充电功率按时间段累加得到总负荷曲线。参数表如下参数取值说明模拟车辆数100小区总车位数渗透率30%电动汽车占比到家时间正态分布 N(19.5, 1.5^2)傍晚集中回家日行驶里程对数正态均值40km标准差20km通勤场景能耗率正态分布 N(16, 2.5^2) kWh/100km常温工况充电功率3.3kW占30%7kW占50%11kW占20%慢充为主目标SOC90%充满后自动停止充电效率92%交流慢充线损和车载充电机损耗小区场景和上文的单车场景有一点关键不同充电功率概率分布里大胆去掉了50kW快充。原因是住宅区本身很少配备大功率快充强行加进来会把负荷曲线拉得很难看也不符合实际。场景变了离散概率分布必须跟着调整这是建模的基本素养。4.2 模拟结果的分析方法核心逻辑是先算出每辆车的“充电功率时间序列”再逐时步累加。代码框架如下%% 小区负荷模拟 numVehicle 100; evNum round(numVehicle * 0.3); % 电动汽车数量 timeGrid 18:0.25:8; % 18:00到次日8:00, 步长15分钟 nStep length(timeGrid); loadCurve zeros(nStep, 1); rng(7); % 固定不同种子 arriveHour normrnd(19.5, 1.5, evNum, 1); arriveHour max(min(arriveHour, 24), 0); for k 1:evNum mile lognrnd(muDist, sigmaDist); ec normrnd(16, 2.5); consumed mile / 100 * ec; socArr max(0.8 - consumed / battCap, 0); need max((0.9 - socArr) * battCap, 0); p chargePower(randsample(...)); % 按离散概率取充电枪 tCharge need / (p * 0.92); if tCharge 0, continue; end % 将充电行为映射到时间网格 startIdx round((arriveHour(k) - 18) / 0.25) 1; chargeSteps ceil(tCharge / 0.25); endIdx min(startIdx chargeSteps - 1, nStep); loadCurve(startIdx:endIdx) loadCurve(startIdx:endIdx) p; end这里每辆车挨个循环是出于代码清晰度考虑。实际跑项目时可以把内层逻辑向量化或者用parfor替代for在40辆电车×10000次场景下能压到几秒内出结果。但注意parfor里要保证每个迭代独立随机数和累加逻辑要仔细处理新手阶段先用普通循环跑通即可。跑完后画负荷曲线横轴是时间纵轴是变压器总负荷。你会看到一条典型的“双高峰”曲线一条在傍晚回家后另一条在后半夜——但后半夜这个峰是“充电结束时间被SOC目标强制拉长”造成的。这个信息很关键它说明如果小区物业把目标SOC限制到80%而不是90%夜间变压器峰值负荷能明显降下来。4.3 蒙特卡洛结果的置信度评估多车叠加之后每个时间点的负荷值本身也服从一个分布。我通常跑完单次模拟后再外套一层循环重复M次得到每个时点的负荷分布比如对19:00这个时刻收集M次模拟的负荷值画直方图、算均值、算P95。这M次“场景外循环”和N次“用户内循环”构成了双重蒙特卡洛代价是计算量翻倍但结果的说服力强很多汇报时可以说“19点整的变压器负荷有95%概率落在XX到XX kW之间”。判断样本量够不够就看P95曲线的摆动幅度。如果两次独立重复的P95曲线差异超过5%说明外层循环次数不够把M调大到200或500。实操中我发现样本量翻倍带来的精度提升曲线非常平缓够用就行别为追求完美浪费算力。5. 常见问题与排查技巧实录5.1 模拟结果不收敛怎么办蒙特卡洛最常见的翻车现场就是“每次跑结果都不一样而且差异很大”。这里有三个层次的问题。第一是随机种子没固定。这个最简单rng(固定数)加上就完事。第二是样本量不够。判断方法有两种看置信区间宽度或者跑两个半量样本对比。比如用5000样本跑一次再用10000样本跑一次如果均值差异小于你要求精度的1/3基本可以收敛。第三是输入分布本身就有问题。特别典型的是日行驶里程用了正态分布导致抽出了负里程或者参数设得太大尾部出现日里程1000公里的荒唐样本这会严重拉高均值。对数正态分布天然非负能避开第一个坑但参数不合理照样会出现离谱尾部所以画直方图检查抽样结果非常必要。5.2 随机数种子的作用与陷阱很多教程让你“务必固定种子”但没说清楚为什么。固定种子的真正价值不是让结果“正确”而是让实验“可复现、可对比”。你优化了某个参数后想确认效果如果两次跑的随机噪声不同很难判断提升是真实的还是噪声固定种子就切断了这种混淆。陷阱在于固定同一个种子跑不同场景对比时实际上所有场景都用了同一批随机数如果两个场景差异很小结论可能被随机数顺序干扰。稳妥的做法是场景对比时用不同的种子然后各跑几个不同种子取平均。我自己做敏感分析时一般跑5个种子×每个种子2000次共1万次这样既平滑了随机噪声又保留了复现的能力。5.3 参数敏感性分析的三种做法蒙特卡洛模型建立在很多假设参数之上汇报结果之前一定要做敏感性分析否则别人问“你怎么知道日均里程40公里就对”的时候会很难回答。我常用的三种做法第一种是单变量扫描固定其他参数把目标参数从低到高调几个档位观察输出指标的变化范围。比如日均里程从30km调到50km看充电时长的P95从多少涨到多少。第二种做法是计算敏感性指数也就是对每个输入参数做微小扰动看输出变化比例用来定位“哪个输入参数对结果影响最大”这个思路接近Sobol指数但实现简单很多。第三种是直接做场景对比冬季能耗率18.5和夏季14.5各跑一遍对比极端工况之间的结果跨度。敏感性分析的价值不仅仅是应付质疑它能帮你找到模型的“主导变量”。我记得有次做快充场站排队分析发现影响最大的不是充电功率而是车主到站SOC的分布。如果平均SOC高很多车根本不需要充满充电时长会大幅缩短。这个结论直接影响了场站运营策略比盲目加桩有效多了。6. 个人实操体会与后续扩展建议6.1 我踩过的几个坑第一个坑是过度拟合分布。早期我拿到一批实测数据后会想着用复杂的混合分布把数据拟合得严丝合缝结果蒙特卡洛跑出来倒是“好看”可一旦换一个数据集模型立刻失灵。现在我会先用简单的对数正态或正态分布作为基线只有当直方图形态明显不符合时才考虑混合分布或经验分布。第二个坑是忽略SOC的时域耦合。很多初始建模把到家SOC当作独立的均匀分布这短时间跑着方便但一到涉及充电策略优化的场景就会失真因为SOC的实际分布取决于昨天的行驶和充电行为是一天一天演化出来的。更稳的做法是把模型改成多日循环让SOC自己“跑”到一个稳态分布而不是拍脑袋给个数。第三个坑是充电效率参数。很多人直接拿充电桩标称功率乘时间当耗电量忽略了车载充电机转换效率、电池内阻损耗和线缆损耗算出来的充电时长会明显偏短。实际交流慢充效率大约85%到93%快充也差不多这个系数不到10%的差异在大量样本聚合后会显著影响负荷峰值估算。6.2 这套框架还能怎么扩展现在的模型已经能跑通单车充电需求和小区负荷评估继续往下走的方向很多。把电池衰减加进来让电池容量随循环次数衰减可以评估“用了三年之后的电车充电需求变化”。把V2G车到网加进来充电行为就变成双向夜间可以放电补贴电网蒙特卡洛框架不需要大改只需在时域累加时加一个负功率项。把智能充电策略加进来比如让车辆根据电价峰谷错峰充电就能评估“低谷充电能省多少钱、对变压器是否友好”。我还试过把蒙特卡洛和机器学习结合用蒙特卡洛生成的数据集训练一个快速评估代理模型后续做实时估算时就不用再跑海量采样。这个方向挺有意思但那是后话了。先把这套蒙特卡洛框架吃透后续扩展都是水到渠成的事。最后分享一个实操小技巧每次跑完蒙特卡洛我都会把随机数种子、参数版本、输出统计量和关键图一并存档文件名用日期加参数hash命名。做仿真最怕的是三个月后拿着一个结果图却完全说不清当时用的哪组参数。养成存档习惯整个分析流程的可信度会提升一大截。
阅读完成 · 觉得有帮助?
咨询建站