
做配电网规划的朋友肯定都遇到过这类问题一片小区或园区要新增一批电动汽车充电桩供电容量到底够不够变压器会不会过载如果盲目按“所有车同时满功率充电”来配置投资浪费太严重如果拍脑袋减半又担心夏天晚上负荷高峰直接跳闸。这些年我做过好几个充电负荷预测的模拟项目最常用的方法就是蒙特卡洛法。核心思路不复杂先建立各类电动汽车在出行时间、行驶里程、充电时间上的概率模型再用计算机生成大量“虚拟车主”让每个虚拟车主按照概率模型随机出行、随机充电最后把所有车的充电行为叠加起来就能得到一条比较真实的区域充电负荷曲线。这篇文章我把这套方法的完整思路、建模细节、代码实现和踩坑经验都整理出来适合刚接触负荷预测的规划人员也适合想自己动手写仿真程序的研究者。1. 为什么是蒙特卡洛法这批负荷凭什么预测很多人第一次接触电动汽车负荷预测时直觉是用“平均法”每辆车每天跑多少公里、消耗多少电、充多久然后乘以车辆总数。这么做的问题很明显——真实世界里根本不存在“平均车主”有人早上七点出门有人中午才出门有人一天跑20公里有人跑100公里有人电量还剩一半就充有人非要跑到亮红灯才充。这些随机性叠加起来才是充电负荷真正的样子。蒙特卡洛法的本质就是用一个简单的逻辑应对复杂的随机性既然每个人的行为都是随机的那我就把几万个人的随机行为都模拟一遍看最终统计结果是什么。就像抛硬币单次结果完全不可预测但抛一万次之后正面朝上的比例会稳定在某个区间内。这就是大数定律在工程上的应用。具体到充电负荷预测蒙特卡洛法有几个无可替代的优势。第一它天然支持“异构车辆”。出租车、私家车、公交车、物流车的出行规律完全不同用单一公式套所有车型根本不现实。蒙特卡洛法可以按车型分别抽样、分别建模最后统一叠加。第二它能输出概率化的结果而不只是一个数字。你可以得到“95%概率下晚上八点的充电负荷不超过多少千瓦”这对电网规划来说比一个简单平均值有用得多。规划师关心的是上限不是均值。第三它能灵活接入不同影响因素。温度影响百公里电耗节假日影响出行距离油价或电费影响充电意愿——这些都可以作为参数塞进模型里重新跑一次模拟就能评估影响程度。所以整套技术路线的核心逻辑是这样的把所有电动汽车按用途分成几类对每一类分别建立出行时间、行驶里程、充电时间的概率分布模型然后通过蒙特卡洛随机抽样模拟每一辆车的充电过程最终聚合成区域级的充电负荷曲线。2. 三个概率模型出行时间、行驶里程、充电时间怎么建模这是整套方法最核心的部分。模型建得好不好直接决定预测结果靠不靠谱。我见过不少代码写得花团锦簇、结果却一塌糊涂的项目问题几乎都出在概率模型上——要么参数拍脑袋要么分布类型选错要么没区分车辆类型。2.1 出行时间先分场景再定分布出行时间包括两个关键节点出发时间和返程时间。两者的概率分布特征完全不同不能混在一起处理。对私人乘用车来说工作日的出发时间呈现明显的早高峰特征集中在7点到8点半之间常用正态分布或分段正态分布来拟合。我实际用下来截断正态分布在区间[6:00, 10:00]内、均值7:30到8:00、标准差0.8到1.2小时的效果最好。返程时间则集中在17:30到19:00均值18:00左右标准差稍大一些因为下班后有人直接回家有人要去超市或去接孩子。非工作日的出行时间就散得多出发时间往往从早上九点一直蔓延到下午双峰特征不那么明显直接用较宽的正态分布或者均匀分布拟合也能接受。出租车和网约车的规律完全不同。它们几乎全天都在路上跑出行时间没有明显的早晚高峰但存在早晚两个运营高峰期的“相对低谷”——因为这些车都在路上拉活没有停在固定地点充电。对出租车我通常不单独建模出发时间而是建模“返场充电时间”集中在下午交班的14点到16点以及凌晨收工后的2点到4点。公交车就更特殊了它的出行时间是固定的不需要用随机分布来描述。公交充电负荷预测的关键是班次间隔和充电窗口期比如夜间停运后有固定4到6小时的充电窗口。这类车辆更适合用确定性调度表而不是蒙特卡洛随机抽样来处理。2.2 行驶里程对数正态分布与不同车辆类型差异行驶里程是决定充电能耗的基础变量也是三个概率模型里最容易出错的。日常行驶里程的统计分布通常呈现明显的右偏特征大多数车每天跑的距离在20到40公里之间但小部分车会跑到80公里甚至100公里以上。这种“大部分车短途、少部分车长途”的形态用正态分布拟合效果很差对数正态分布才是标准选择。原因很直观行驶里程不可能为负且长尾拖在右侧对数正态分布恰好具备这两个特征。用对数正态分布时要注意参数不是直接给均值和标准差而是给取对数之后的均值和标准差。举个例子假设私家车日均行驶里程服从均值为30公里、标准差约15公里的分布转换成对数参数大概是对数均值3.3、对数标准差0.5。实际项目中我建议直接用现有交通统计数据进行参数拟合不要凭感觉给数。不同车辆类型的里程分布差异大得惊人。私家车日均30到40公里出租车一天能跑300到400公里但它的充电行为发生在交接班时段每天至少需要一次大电量补充物流车介于两者之间日均100到150公里且存在明显的“晚上统一回场充电”特征。一笔笔估算下来你会发现如果模型里把所有车都当成“日均30公里的私家车”那么充电负荷会被严重低估。反过来把私家车按出租车的里程分布算负荷又会虚高到离谱。所以建立分类概率模型不是可有可无的精细化处理而是保证结果可信的前提。2.3 充电时间不是抽出来的是算出来的这是初学者最容易理解偏的地方。很多人看到“充电时间的概率模型”就以为充电时间也是一个像行驶里程那样直接抽样的随机变量。实际上充电时间的分布不是独立的——它取决于到达时的剩余电量SOC、电池容量、充电功率和目标SOC是一个推导出来的量不是抽样出来的量。整个逻辑链条是这样的车辆一次出行消耗的能量由行驶里程和百公里电耗相乘得到。消耗的能量除以电池容量得到SOC的下降幅度。用1减去这个下降幅度就是到达目的地时的剩余电量。如果剩余电量低于车主设置的充电阈值通常取0.2到0.3这辆车就会接入充电桩。然后充电需求电量等于电池容量乘以目标SOC与剩余SOC的差值充电时间等于需求电量除以充电功率。这就是为什么需要先建立行驶里程的概率模型——它不仅描述出行行为还直接决定了充电需求。充电功率选择的差异也要考虑。常规慢充7千瓦适合在家过夜充电快充60到120千瓦适合白天在停车场或路边补电。充电功率选多少会显著改变充电时长进而影响负荷曲线的峰形。同样充30度电用7千瓦慢充需要4个多小时散布在晚上很长一段时间的时段里用80千瓦快充20多分钟就完成负荷高度集中在到达后的某个时间段内。另外要注意不是每辆车每次出行结束都会充电。如果剩余SOC还很高车主通常不会插枪。是否充电这个决策本身也可以做成概率事件——剩余电量越少充电概率越高。简化处理时可以用固定阈值精细处理时可以用一条随SOC变化的充电概率曲线。3. 蒙特卡洛模拟完整落地流程模型建好之后就是整个蒙特卡洛模拟的实现了。这个环节的问题通常不是“不会写代码”而是“流程不完整”或者“参数处理不好”导致结果看起来怪怪的。3.1 整体流程与数据准备标准的蒙特卡洛负荷预测流程分六步每一步都有需要注意的细节。第一步是确定分析对象和场景。你要做的是某个住宅小区的晚高峰负荷还是某个园区的全天负荷对象不同车辆构成和充电行为差异极大。我的习惯是先明确“要回答什么问题”——是校核变压器容量还是规划充电桩数量这两个问题对建模精度的要求不一样。第二步是确定车辆构成和数量。假设某小区有600辆电动汽车其中私人乘用车500辆、网约车60辆、物流车40辆。这个比例直接影响结果需要结合当地车辆登记数据来定。第三步是建立每类车的概率模型参数即上一节提到的出行时间、行驶里程、充电功率等分布参数。第四步是逐车辆抽样并计算充电过程。对每一辆车按照它的类型从对应分布中抽取出发时间、返程时间、行驶里程计算SOC变化判断是否充电再算出充电开始时间、充电功率和充电时长。第五步是累加所有车辆的充电负荷曲线。把一天按分钟或15分钟为步长切成若干个时间窗每一辆车在其充电时段内的每个时间窗上叠加对应的充电功率。第六步是重复模拟多次并统计结果。单次模拟是随机的多次模拟的统计结果才有参考价值。3.2 关键公式与参数计算这里把核心计算过程展开写清楚。假设一辆车的行驶里程为d公里百公里电耗为e千瓦时/百公里电池容量为C千瓦时则单次出行消耗电量 d × e / 100 起始SOC 1 - (d × e) / (100 × C) 充电需求电量 C × (目标SOC - 起始SOC) 充电时长 充电需求电量 / 充电功率举个例子。一辆电池容量60千瓦时的私家车百公里电耗15千瓦时某天行驶了40公里那么消耗电量6千瓦时起始SOC 1 - 6/60 0.9。如果充电阈值设为0.3这辆车剩余电量还很高当天根本不会充电。再换一个例子。同样一辆车行驶了200公里消耗电量30千瓦时起始SOC 1 - 30/60 0.5。如果车主习惯充到90%那么需求电量 60 × (0.9 - 0.5) 24千瓦时。用7千瓦慢充需要约3.4小时用60千瓦快充只要24分钟。这里有一个容易忽略的细节目标SOC通常不是100%因为大多数家用电动汽车充电到90%左右就停止了一方面保护电池另一方面最后10%的充电速度极慢性价比不高。这个细节会导致充电需求电量计算偏高或偏低调参时要留意。3.3 Python实现思路实现逻辑并不复杂关键是把流程写清楚。下面这个示例代码聚焦于核心逻辑车辆参数和分布参数都做了简化实际使用时需要替换成自己拟合的分布参数。import numpy as np import matplotlib.pyplot as plt # 模拟参数 day_minutes 24 * 60 # 一天1440分钟 time_step 15 # 统计步长15分钟 num_intervals day_minutes // time_step # 96个时间窗 # 车辆类型参数 # 每类车: 数量, 电池容量kWh, 百公里电耗kWh/100km, # 出发时间均值/标准差(小时), 返程时间均值/标准差(小时), # 行驶里程对数均值/对数标准差, 充电功率kW, 充电阈值, 目标SOC fleet_types { private: dict( count500, battery60, consumption15, depart_mean7.75, depart_std1.0, return_mean18.0, return_std1.2, mile_log_mean3.4, mile_log_std0.5, charge_power7, charge_threshold0.3, target_soc0.9 ), ride_hailing: dict( count60, battery60, consumption15, depart_mean10.0, depart_std2.0, return_mean14.5, return_std1.0, mile_log_mean4.5, mile_log_std0.4, charge_power60, charge_threshold0.5, target_soc0.95 ) } def sample_day_loads(): # 每个时间窗的累计充电功率单位kW load_curve np.zeros(num_intervals) for vtype, params in fleet_types.items(): for _ in range(params[count]): # 1. 抽样出行时间单位分钟 depart_min max(0, np.random.normal( params[depart_mean] * 60, params[depart_std] * 60)) return_min max(0, np.random.normal( params[return_mean] * 60, params[return_std] * 60)) if return_min depart_min: return_min depart_min 30 # 最少外出30分钟 # 2. 抽样行驶里程对数正态分布单位公里 mileage np.random.lognormal( params[mile_log_mean], params[mile_log_std]) # 3. 计算出发前/到达后的SOC energy_consumed mileage * params[consumption] / 100.0 soc_at_arrival 1.0 - energy_consumed / params[battery] # 4. 判断是否充电 # 简化处理到达时SOC低于阈值即充电 if soc_at_arrival params[charge_threshold]: continue # 5. 计算充电需求电量和时长 energy_needed params[battery] * ( params[target_soc] - soc_at_arrival) charge_duration energy_needed / params[charge_power] # 小时 duration_intervals int(round( charge_duration * 60 / time_step)) # 6. 充电开始时间 返程时间把充电功率叠加到负荷曲线上 start_interval int(return_min / time_step) end_interval min(num_intervals, start_interval duration_intervals) for idx in range(start_interval, end_interval): load_curve[idx] params[charge_power] return load_curve # 蒙特卡洛模拟重复模拟50次取平均 num_simulations 50 avg_load np.zeros(num_intervals) for sim in range(num_simulations): avg_load sample_day_loads() / num_simulations # 画出负荷曲线 time_axis np.arange(num_intervals) * time_step / 60.0 plt.plot(time_axis, avg_load) plt.xlabel(Time (hour)) plt.ylabel(Charging Load (kW)) plt.title(EV Charging Load Curve by Monte Carlo Simulation) plt.grid(True) plt.show()代码本身很简单但有三个地方值得提醒。一是SOC越界问题。当行驶里程特别长的时候计算出来的到达SOC可能变成负数。这种情况在现实中的含义是“电量耗尽抛锚了”但在模型中必须截断处理。我通常的做法是如果SOC小于0强制设为0.05并警告一次。不处理会出现充电需求电量大于电池容量的荒谬结果。二是充电时长与停留时长的匹配。理论上到达目的地后充完电才能离开但如果模拟出的充电时长比车主的停留时间还长就说明这趟充电行为不合理。比如一个出租车司机下午2点回场、4点必须出车模型却给了他5小时的充电需求——这就不符合实际。处理方式是限制最大充电时长为停留时间不够的那部分电量当作“下次再充”。三是充电时间窗的边界处理。如果充电开始时间是23:00充电时长3小时那么负荷会跨过午夜。简单模拟中可以直接让曲线从23:00跑到次日凌晨2:00但如果你需要输出“当日负荷曲线”就得决定这段跨日负荷算哪一天。我通常把一天定义为早6点到次日早6点让跨日负荷完整落在同一条曲线上。3.4 模拟次数与收敛性判断蒙特卡洛模拟的次数选多少没有绝对标准但有工程经验。单次模拟里已经有几百辆车参与已经体现了一定的统计平均效果。但为了消除随机种子带来的偶然波动我们通常重复模拟几十次到几百次把结果取平均或取分位数。我的判断方法是做“收敛性检验”分别跑10次、50次、200次对比输出曲线上最大负荷值的波动幅度。实践中当模拟次数达到50次以上时最大负荷的波动通常能控制在正负3%以内继续增加次数对结果改善不大但计算耗时线性增长。这里要特别提醒不要为了追求“精确”把模拟次数定得很大。蒙特卡洛法给的是概率估计不是精确解模拟到一定程度后精度边际收益极低。我在一个模拟项目里的经验是500辆车、96个时间窗、单次模拟耗时不到0.5秒跑200次也就一两分钟完全够用。4. 常见问题与避坑指南蒙特卡洛负荷预测做过一遍之后你大概率会遇到下面这些问题。我把它们整理成速查表每个问题都给出具体的排查思路和解决方式。现象可能原因解决方案负荷曲线出现“尖刺”某辆快充车的充电功率被叠加到同一个时间窗且时间窗较短检查充电开始时间是否聚集在固定整点考虑把开始时间按时间窗内部做均匀化处理充电负荷集中在凌晨后返程时间分布设置太晚核对分布的均值和标准差住宅区晚高峰应在18:00-20:00而不是午夜总充电负荷远超预期行驶里程对数分布参数设置偏大用实际交通数据重新拟合重点关注对数均值90%的车辆里程应落在合理区间总充电负荷低于预期充电阈值设置太低很多车主在剩余SOC 30%-50%就会充电阈值0.2会过滤掉大量真实充电行为模拟结果每次差异很大车辆数量太少或模拟次数不足增加每类车数量或提高迭代次数出现负SOC或大于1的SOC里程抽样极端值未处理对SOC做截断处理小于0改0.05大于1强制等于1负荷曲线没有早晚高峰差异所有车型共用一套参数区分私家车、出租车、物流车的出行时间和充电功率这里面最隐蔽的坑是“充电开始时间聚集效应”。蒙特卡洛抽样是均匀随机的但由于返程时间分布集中在某个时段导致大量车辆几乎在同一时间返回并开始充电。如果时间窗是15分钟几分钟内可能挤进上百辆车同时启动充电产生一个不真实的尖峰。这个问题在真实世界也存在就是所谓的“同时率”确实值得我们关注。但蒙特卡洛纯随机抽样会把它放大。我的处理方法是在确定充电开始时间后加上一点随机扰动比如在0到15分钟内均匀偏移一下这样曲线就平滑得多也更符合真实车主的充电行为习惯。另一个容易忽略的问题是没有区分“工作日”和“非工作日”的模型参数。我第一次做负荷预测时直接用同一套出行时间分布跑全年数据结果发现夏季工作日的晚高峰负荷和预测值差了一大截。后来意识到休息日大家的出行时间整体推后而且白天也有大量充电行为用电模式完全不一样。正确做法是对每个场景单独建模工作日、周末、节假日至少三套参数。5. 结果解读从负荷曲线到电网规划决策蒙特卡洛模拟跑完输出的是一条或一组负荷曲线但这条曲线不是终点真正的价值在于如何解读和使用它。单一模拟的负荷曲线只能看出大致趋势。更严谨的做法是多次模拟后统计每个时间窗的负荷分位数。比如第95百分位曲线代表“在95%的情况下负荷不会超过这个值”。给变压器容量校核时应该用95百分位或更高分位数的曲线而不是平均曲线。平均曲线相当于“大多数时间的典型场景”但如果按它来配置变压器高峰时段可能会过载。另一个有用的输出是日最大负荷和负荷率。日最大负荷直接决定了变压器容量下限负荷率平均负荷除以最大负荷反映了充电设施的利用效率。如果负荷率太低说明充电桩分布不合理高峰期拥堵、低谷期闲置这时应该考虑引入有序充电策略把部分充电需求引导到低谷时段。我在实际项目中做过一次校核案例某住宅区600辆电动汽车的蒙特卡洛预测结果显示在没有有序充电管理时晚高峰最大充电负荷约650千瓦而配变容量只有800千瓦叠加原有基础负荷后超载风险较高。加入“延迟充电到22点后”的措施后最大充电负荷降到300千瓦以下问题迎刃而解。这就是负荷预测对规划决策的价值得先有可信的负荷曲线才能谈后续的调度策略。扩展应用方面这套模型还能继续加东西。比如把环境温度作为一个参数影响百公里电耗能模拟冬季负荷比夏季高多少把公共充电桩和私人充电桩分开建模能分析不同充电场景的负荷分布加入充电价格信号后能模拟分时电价对充电行为的引导效果。最后说几句这套蒙特卡洛负荷预测方法难的不是算法本身而是参数标定和场景理解。同一个城市不同片区、不同车型构成跑出来的曲线可能差一倍以上。我做负荷预测这几年最大的体会是不要迷信任何一篇论文里给的“标准参数”那些参数是在特定地区、特定年份、特定车辆渗透率下得到的搬到自己项目里很可能失真。有条件就找本地交通数据重新拟合没条件至少做一轮参数敏感性分析看看哪个参数对结果影响最大优先把它校准。另一个小技巧模型写完先别急着看结果把抽样出来的行驶里程和出行时间打印出来画个直方图跟真实统计做对比。如果连抽样分布都和实际对不上负荷曲线再平滑也是白搭。这一眼检查能帮你省下大量排查“算法错误”的时间。