ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于蒙特卡洛模拟的电动汽车充电负荷曲线生成方法

基于蒙特卡洛模拟的电动汽车充电负荷曲线生成方法 做配电网规划或者充电设施规划的朋友应该都碰过这种场景只知道某片区大概有几百辆电动汽车却要估算它们晚上八点同时充电会产生多大负荷。拍脑袋套一个同时率或者直接拿别的小区充电曲线来平移误差经常大到无法交代。我现在的做法是用蒙特卡洛去生成电动汽车充电负荷曲线——把每辆车的日行驶里程、起始SOC、充电开始时间、充电功率都当成随机变量抽样几百上千次后叠加成一条有统计意义的负荷序列。这个项目不需要完整的充电桩台账也不需要假设所有车主行为一致能把个体差异原样保留下来。尤其适合正在做充电负荷预测、配电网承载力评估、充电站容量设计的朋友参考。下面我把建模思路、Python实现、参数标定和验证过程完整拆开讲一遍。1. 项目思路拆解蒙特卡洛到底模拟了什么1.1 为什么充电负荷不能用一个简单的“同时率”算出来很多早期的配网规划里电动汽车充电负荷就是用“总车辆数 x 单桩功率 x 同时率”估的比如1000辆车、7kW慢充、同时率0.3得出2100kW。这个方法看着简便实际有两个大问题一是不同时段的同时率完全不一样晚上7点和凌晨4点肯定不是同一个数二是它没法回答“晚高峰叠加了多少快充功率”“边界情况下会不会过载”这类问题。规划需要的是负荷曲线不是一个孤零零的数字。充电负荷本质上是一大批独立随机过程的叠加。每辆车开多少公里、什么时候回家、电池还剩多少电、用多粗的桩充都有明显的随机性。做规划时我要的是这些随机性叠加之后呈现出的统计规律比如高峰期可能出现的时间窗口、平均单车的充电功率、峰值负荷的分布区间。蒙特卡洛在这里就相当于一个虚拟沙盒先把每辆车的“当天行为”按概率模型抽样一遍再把它们逐个叠加到时间轴上最后得到一条或一组负荷曲线。1.2 蒙特卡洛解决问题的基本流程标准的流程可以归纳成四步。第一步是确定核心随机变量通常包括日行驶里程、起始SOC、充电开始时间、充电功率和电池容量。第二步是构建每个变量的概率分布这一步最花时间因为后面所有结果都取决于分布参数选得靠不靠谱。第三步是逐车抽样把一辆车的状态算出来得到它在一天内每个时间断面是否在充电、以多大功率在充然后累加到总负荷序列上。第四步是重复抽样并做统计处理用多次模拟的均值、分位数、峰值的波动情况来评估结果。这套流程不需要车辆调度的具体时间表也不需要知道谁在哪分钟插枪只需要知道群体的统计特征。所以哪怕只有一个片区的车辆数、大致车型比例、车主通勤习惯也能先跑一版结果出来。对于数据不足的规划项目这是最大的价值。注意蒙特卡洛输出的是“概率意义上的负荷曲线”不是“明天晚上8点一定是多少千瓦”的确定性预测。它更适合做规划级分析、方案比选和边界条件探讨。1.3 建模之前先定边界哪些因素暂时先不纳入做任何仿真都要先划定边界不然越做越深、越做越乱。我这个项目第一版做了几个简化每辆车一天只考虑一次充电行为且默认前一天晚上是充满电出发的暂不考虑温度对电池能耗和充电功率的影响暂时不考虑V2G反向放电和有序充电策略用“车辆类型”而不是“车主个体画像”来区分快充和慢充。这些简化不是偷懒而是为了让模型先跑通、先能被验证。实际做下来第一版模型已经能反映晚高峰充电负荷的典型特征后续再逐步把跨日充电、温度修正、分时电价响应这些因素加进去每加一项都跟基线对比才能知道新增因素到底有多大影响。2. 核心模型哪几个随机变量决定了结果准不准2.1 日行驶里程的对数正态分布日行驶里程是整条负荷链的源头变量它直接决定起始SOC进而决定充电时长和充电能量。对私家车来说日行驶里程的分布通常用对数正态分布拟合而不是正态分布。原因不难理解行驶里程不可能为负而且大部分车主每天开30到40公里少数人一天跑一两百公里分布右侧拖着一条长尾这跟对数正态的形状天然吻合。建模时我一般是先确定里程均值μ_d和标准差σ_d再换算成对数正态用的参数μ_ln和σ_ln换算公式是这样的mu_ln ln(mu_d^2 / sqrt(mu_d^2 sigma_d^2)) sigma_ln sqrt(ln(1 sigma_d^2 / mu_d^2))举例说某个城市私家车日均行驶里程均值35km、标准差25km代入公式后得到的μ_ln约3.39σ_ln约0.66。用这个分布抽样大部分车落在10到60km之间也有小部分车超过100km跟真实交通调查结果比较接近。如果你手头有本地出行数据直接重新标定这两个数就行代码不用改。2.2 起始SOC的换算公式有了日行驶里程就能估算车主到家时的起始SOC。假设车主前一天晚上充满电出发电池容量为E_batkWh每公里平均电耗为ekWh/km那么soc_start 1 - (e * distance) / E_bat比如一辆电池容量50kWh的车平均能耗0.18kWh/km一天开了36km那么soc_start 1 - (0.18 * 36)/50 ≈ 0.87。这个剩余电量对应需要的充电能量不多充满也就是6.5kWh。反过来如果一辆运营车一天跑了220km算出来soc_start是个负数这时候必须截断成0因为电池没有“负电量”这条截断也会在后续代码里体现。这个公式隐含的前提是“满充满放”简化模型实际车主可能两天一充起始SOC不会这么高。所以第一版代码里我还会加一个充电触发概率逻辑放在后面第3节。真实项目里如果有车联网数据可以用历史充电起始SOC直接拟合分布替换掉这个计算公式。2.3 充电触发概率与充电开始时间的用户行为建模实际操作中“每天到家必定充电”只适用于一部分车主。很多用户电量还多的时候会选择不插枪等到SOC降到某个阈值才充。我的做法是用分段概率来控制SOC低于0.2时强制要充否则第二天根本开不了SOC在0.2到0.5之间按70%的概率充电SOC高于0.5默认当天不充电。这几个阈值可以按小区车主调研来调整但框架比“每辆都充”要真实得多。充电开始时间也需要分场景。家用慢充车的主要充电时段集中在傍晚到深夜用均值19点、标准差2小时的正态分布来近似比较典型但会有部分车在上午或者下午插枪所以要限制在8点到23点之间。营运快充车则集中在白天尤其10点到14点会出现一个小高峰这个特征在办公区、商业区尤其明显。模拟时只要给这两种车分别设置起始时间分布叠加出来的曲线就会自然呈现出“晚高峰为主、午间小峰为辅”的形态。2.4 充电功率与电池容量用离散分布模拟现实差异不能假设所有车都用同一个7kW慢充桩。现实里私人桩有3.5kW、7kW目的地交流桩有11kW、22kW直流快充桩更是从60kW到120kW不等。电池容量也一样A0级小车可能只有40kWh中大型轿车能到60kWh以上纯电运营车会到75甚至90kWh。我在模型里处理的方法是给车辆类型设定占比再按类型抽电池容量和充电功率车辆类型典型占比充电功率电池容量家用慢充车60%~80%3.5~11 kW40~60 kWh营运快充车20%~30%60~120 kW60~90 kWh如果规划区域里网约车和物流车占比高就把营运快充车的比例调高功率分布也相应拉高。这组参数决定了曲线的“量级”用户行为分布决定了曲线的“形状”两者要分开标定别混在一起。3. 完整实操Python Numpy 生成充电负荷3.1 参数初始化与固定随机种子代码我习惯用Python加Numpy实现不依赖太多第三方库方便在工程机上直接跑。时间轴用15分钟一个断面一天就是96个点既能看清晚高峰的变化又不至于让计算量失控。模拟开始前必须先固定随机种子。蒙特卡洛模拟的随机性如果不固定每次跑出来的曲线都不一样你没法判断结果差异是参数变化引起的还是纯粹随机波动引起的。用default_rng(202405)这样的方式固定种子同时保证后续每次运行可以复现。import numpy as np rng np.random.default_rng(202405) T 1440 # 一天按分钟算 STEP 15 # 时间断面15分钟 num_points T // STEP N 1000 # 抽样车辆数可先跑300快速验证再加大 # 车辆类型比例0家用慢充1营运快充 car_type_p [0.8, 0.2] P_slow 7.0 P_fast 60.0 # 电池容量池及抽样概率 battery_pool np.array([40.0, 50.0, 60.0]) battery_p np.array([0.3, 0.5, 0.2]) # 平均电耗 kWh/km consumption_per_km 0.18 # 日行驶里程均值与标准差换算成对数正态参数 mu_dist 35.0 sigma_dist 25.0 mu_ln np.log(mu_dist**2 / np.sqrt(mu_dist**2 sigma_dist**2)) sigma_ln np.sqrt(np.log(1 sigma_dist**2 / mu_dist**2))3.2 逐车抽样与累加循环核心循环分三步抽样日行驶里程并算出起始SOC判断是否触发充电再把充电功率按时间断面累加到负荷数组里。这里有个常见误区充电功率并不是每天都满载传输只有当车辆确实在充电时才有功率。所以判断是否充电的条件要写在累加之前。car_types rng.choice([0, 1], sizeN, pcar_type_p) distances_km rng.lognormal(meanmu_ln, sigmasigma_ln, sizeN) battery_capacities rng.choice(battery_pool, sizeN, pbattery_p) soc_start 1 - distances_km * consumption_per_km / battery_capacities load_15 np.zeros(num_points) for i in range(N): # 1. 确定充电功率 P P_slow if car_types[i] 0 else P_fast # 2. 起始SOC截断 soc max(0.0, soc_start[i]) # 3. 充电触发判断 if soc 0.2: need_charge True elif soc 0.5: need_charge rng.random() 0.7 else: need_charge False if not need_charge: continue # 4. 计算本车充电时长并折算成断面数 energy_needed_kwh battery_capacities[i] * (1 - soc) duration_h energy_needed_kwh / P duration_step int(np.ceil(duration_h * 60 / STEP)) # 5. 抽样充电开始时间 if car_types[i] 0: start_min int(np.clip(rng.normal(19 * 60, 120), 8 * 60, 23 * 60)) else: start_min int(np.clip(rng.normal(12 * 60, 120), 6 * 60, 22 * 60)) start_idx start_min // STEP end_idx min(start_idx duration_step, num_points) load_15[start_idx:end_idx] P avg_load_kw load_15 / N这里用min(start_idx duration_step, num_points)是防止充电时长跨越午夜后索引越界。家用慢充从19点开始、7kW充满50kWh电池大概需要5到6小时通常会持续到凌晨一两点跨日截断会损失一部分能量。第一版模型先接受这个误差如果要更精细可以把时间轴加长到36小时或48小时只取前24小时做分析。3.3 输出平均曲线与折合总负荷跑完循环后load_15保存的是N辆车的总负荷但不同项目的车辆规模不一样直接用它会误导。我习惯先转成“平均单辆电动汽车充电负荷”再按实际规划车辆数放大。比如规划片区有500辆车那总负荷曲线就是avg_load_kw * 500。绘图用Matplotlib很简单横轴是0到24小时纵轴是平均单辆充电功率import matplotlib.pyplot as plt time_h np.arange(0, 24, 0.25) plt.plot(time_h, avg_load_kw, markero, ms3) plt.xlabel(时刻时) plt.ylabel(平均单辆EV充电负荷kW) plt.grid(alpha0.3) plt.xlim(0, 24) plt.show()第一次跑通后别急着加复杂度先看曲线形状对不对。时间集中度、晚高峰峰值、快充车的影响都会在图上直观暴露出来。4. 结果验证与典型现象解读4.1 曲线形状的三个定性检查模拟跑完第一件事就是看曲线长什么样。正常的充电负荷曲线应该有这几个特征凌晨时段负荷很低早上到下午有零星慢充傍晚18点后负荷开始爬升19点到21点形成主峰之后负荷逐步回落。如果家用慢充占比高主峰应该在20点到22点之间而不是在中午。第二个特征是快充车会在午间形成一个小峰。营运车白天跑单中午抽空去快充站补电10点到14点会有一个宽度较窄的负荷突起。这个突起不明显时往往说明快充比例或功率设置过低。第三个特征是曲线应该平滑但不至于完全光滑。由于蒙特卡洛抽样引入随机性曲线会有一定程度的波动这是正常的。但如果95%以上的车都在同一时间开始充电曲线会变成一个矩形脉冲那是起始时间分布设置太窄要放宽标准差。4.2 用能量守恒做定量复核只看形状还不够还要从能量角度做定量验证。模拟累计的充电能量应该和所有车辆日行驶里程对应的能耗基本匹配再考虑充电过程中的能量损失。我做验证的代码一般是这样的sim_energy np.sum(load_15) * STEP / 60 # 单位kWh expected_energy np.sum(distances_km) * consumption_per_km print(模拟充电能量: {:.2f} kWh.format(sim_energy)) print(行驶能耗需求: {:.2f} kWh.format(expected_energy)) print(能量比值: {:.2f}.format(sim_energy / expected_energy))能量比值正常应该在1.0到1.2之间因为充电有损耗还需要额外克服电池内阻发热。如果比值明显小于1说明很多车辆没充上电可能触发充电概率阈值设得太严。如果比值超过1.3说明过度充电比如所有车都每天必充而且起始SOC普遍偏低需要检查SOC计算有没有出错。这一步廉价但有效能直接拦住参数标定中的低级错误。4.3 样本数N怎么定从收敛性看规模蒙特卡洛模拟最常被问的问题就是“抽多少辆车才够”。我的经验是先用200到300做快速调试确认代码和参数没问题后再用1000到2000出正式结果。判断收敛不能只看均值要看峰值负荷的稳定性。把同一参数跑5组不同随机种子统计峰值负荷的变异系数如果变异系数小于5%基本可以认为N够了。如果N太小曲线会出现明显的毛刺峰值位置也不稳定。如果N过大计算耗时线性增长但精度提升越来越有限。规划项目里N取1000是个性价比很高的平衡点单次循环在普通笔记本上也就是几秒钟的事完全没必要盲目加大。注意N是抽样车辆数不是实际片区车辆数。不要为了追求“计算精度”把N加到10万最后还要除以10万才能得到平均曲线纯粹浪费算力。5. 常见问题与排查技巧实录5.1 高频故障排查速查表蒙特卡洛模型跑起来不难但参数之间互相耦合一旦结果不对定位起来需要点耐心。以下是我项目里最容易遇到的几类问题整理成一个速查表症状最常见原因检查思路主峰出现在中午而不是晚上家用慢充起始时间分布错误或快充比例过高检查car_type_p和慢充的rng.normal(19*60,120)参数负荷曲线严重偏小充电触发阈值太严大量SOC高于0.5的车不充电统计need_charge为True的比例正常应在60%~80%凌晨负荷异常高充电起始时间分布下限没限制抽样到了深夜检查是否用了np.clip下限是否设为8点曲线骤降像被切断充电时长跨日被截断把时间轴扩展到36小时再截取前24小时复现结果每次不同没有固定随机种子或种子在循环中被重置统一用rng np.random.default_rng(固定值)峰值功率超过所有桩功率之和累加时分辨率不够多个充电事件挤在同一个断面检查是否误把小时的充电时长按分钟累加了5.2 几个我实测容易忽略的细节跨日充电截断是第一个容易踩的坑。家用慢充车晚7点开始充5到6小时肯定超过零点如果只是简单把超过数组索引的部分丢掉损失的能量会让整体结果偏低。我第一版跑出来的能量比只有0.8排查了半天才发现问题在截断。后来改用“时间轴扩展到48小时累加完只保留前24小时”的做法一下子就正常了。第二个细节是随机数调用顺序会影响可复现性。循环内部每次调用rng.random()都会推进随机状态如果你在调试时往循环里加了一段测试代码后面的抽样序列全变了之前定位好的参数可能又对不上。遇到这种情况不必慌张固定种子重新跑一遍即可但要记住代码结构和随机调用顺序本身也算“结果的一部分”。第三个细节是充电损耗。充电桩输出到电池的功率不等于电池真正吸收的能量中间有AC-DC转换损耗和电池内阻损耗一般按8%~15%估算。如果你拿模拟充电能量和电池实际增加能量对比记得留这个余量否则能量比永远落在0.9附近会让你误以为模型有问题。6. 从0到1的个人经验与下一步扩展6.1 先跑通再细化别一开始就上复杂模型这个项目最值得说的经验是版本从简到繁每一步都要能自洽。我先用“每天必充、固定7kW、晚7点统一开始”的版本跑通结果是一个很丑的矩形波然后加上充电起始时间的随机分布曲线立刻有了晚高峰爬坡的形状再加充电触发概率总能耗才和行驶里程对得上。整个过程只用了一个下午。如果你一上来就加入温度修正、V2G、峰谷电价响应、多日连续模拟结果出现问题的时候根本分不清是哪个模块导致的。控制变量是蒙特卡洛项目调试的第一原则哪怕模型简单一点只要边界清楚、能量守恒可验证它就是一个能支撑规划结论的工具。6.2 扩展方向从静态曲线到动态互动第一版模型把所有车当作独立个体叠加没有考虑充电桩数量限制也没有考虑车与桩的排队关系。后续扩展可以先加入充电站容量约束比如一个站只有20个桩同一时间最多只有20辆车在充超过就要排队或顺延到下一个断面。这个改动能让模型更贴近真实场站运营。再进一步可以在时间分布里引入分时电价因素把充电起始时间分布从正态改成“电价低谷前移”就能评估有序充电对负荷削峰填谷的效果。温度修正也一样冬天能耗上升、快充功率受限直接调整每公里电耗和功率随温度变化的系数即可。这些扩展都不需要推翻现有框架只改对应模块的参数或逻辑。我在实际项目里体会最深的一点是不要试图一开始就建立最完美的模型先用这套蒙特卡洛流程产出一条能让同事和规划人员看到“晚高峰有多高、大概在几点”的可用曲线远比在参数细节里无限打磨更重要。负荷生成是手段帮助做决策才是目的。参数可以后续标定框架可以先跑起来这是所有仿真类项目通用的节奏。
返回列表