ARTICLE DETAIL

资讯详情

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

SUMO仿真结果分析:从XML输出到交通延误评估

SUMO仿真结果分析:从XML输出到交通延误评估 最近在整理自己折腾SUMO的笔记发现一个很有意思的现象很多人卡在安装和跑通demo这关装完之后在GUI里看到小汽车跑来跑去就觉得自己已经“会”交通仿真了。可真到了要出结论、写报告、对比方案的时候对着导出的那一堆XML文件反而不知道从哪里下手。这篇就专门聊仿真结果分析。我会把SUMO跑完之后到底能导出什么、每个文件里的指标该怎么解读、拿到数据之后怎么做统计分析以及我在实际处理数据时踩过的坑一次性说清楚。适合刚装好SUMO、把demo跑通但面对结果文件发懵的朋友也适合已经跑了很多次仿真、但总觉得分析结论不够有说服力的从业者。内容不涉及复杂的数学推导但全是实打实的操作经验。1. 仿真跑完只算完成一半先搞清楚SUMO吐出了哪些结果文件1.1 一张表看懂SUMO的六类常见输出SUMO默认情况下不会主动存任何统计结果。你得在启动命令或配置文件里明确告诉它“我要什么输出”仿真结束后才会生成对应文件。很多人跑完仿真发现目录里空空的就是漏了这一步。我用一个表格先给你把最常见的输出类型梳理清楚输出类型常见参数文件内容主要分析用途车辆旅程信息--tripinfo-output每辆车的出发、到达、行程时间、等待时间、延误时间等评估出行体验最常用全局步长统计--summary-output每个仿真时刻的全网车辆数、平均速度、排队数等看路网整体运行趋势车道级检测数据--lane-output每条车道每个时刻的车辆数、平均速度、排队长度评估单点瓶颈、车道利用率单车轨迹数据--fcd-output每辆车每个时刻的坐标、速度、所在车道回放轨迹、画时空热力图排放数据--emission-output每辆车的油耗、CO2、NOx等排放指标环境影响评估路径选择信息--vehroute-output每辆车最终选择的路径和经过的边分析路径选择行为这里面最常用的是前面三种尤其是tripinfo几乎是我每次仿真必开的一项。fcd非常吃硬盘空间除非要做轨迹级别分析否则不建议随便开。1.2 输出配置的两种标准写法输出配置可以在命令行里加参数也可以写在.sumocfg文件里。我的习惯是写在配置文件里这样方便复用也不怕命令行参数漏掉。一个最基础的配置长这样configuration input net-file valuenetwork.net.xml/ route-files valueroutes.rou.xml/ /input time begin value0/ end value3600/ /time output tripinfo-output valuetripinfo.xml/ summary-output valuesummary.xml/ fcd-output valuefcd.xml/ /output /configuration注意两点。第一fcd输出建议加周期参数比如--fcd-output.period 5意思是每5秒记录一次轨迹而不是默认的每个仿真步都记。否则一个1000辆车跑1小时的仿真fcd文件能轻松超过几个GB打开都费劲。第二命令行同样可以覆盖配置比如sumo -c scenario.sumocfg --tripinfo-output tripinfo_v2.xml这个写法方便在不改动配置文件的情况下快速切换输出文件名或参数。我经常用这个方法来批量跑不同随机种子的实验。2. 核心指标逐个看延误、等待时间、速度到底怎么定义2.1 tripinfo.xml字段拆解车辆级别的核心数据tripinfo文件是结果分析的起点。打开它你会看到类似下面的记录tripinfo idveh0 depart0.00 departLanegneE0_0 departPos5.00 departSpeed0.00 arrival45.60 arrivalLanegneE7_0 arrivalPos190.00 arrivalSpeed0.00 duration45.60 routeLength398.00 waitingTime4.00 timeLoss8.50 speedFactor0.95/每个字段分别说明什么我直接列重点depart和arrival这辆车的出发仿真时间和到达仿真时间单位是秒。两者相减就是duration即实际行程时间。routeLength车辆实际行驶的距离单位是米。waitingTime车辆速度低于0.1m/s的时间总和单位秒。这个指标主要是捕捉“完全停住不动”的时间比如在排队、在信号灯前等待。timeLoss实际行程时间与“理想状态下”行程时间之差。这个“理想状态”是指车辆始终保持道路限速行驶不考虑信号灯、其他车辆干扰等情况。speedFactor车辆实际行驶速度与道路限速的比值这个值小于1说明车辆没有跑满限速可能受拥堵影响。很多人会把waitingTime和timeLoss混用其实两者差别很大。waitingTime只统计速度低于0.1m/s的完全静止时段而timeLoss包含了一切偏离理想速度的时间损耗包括跟车减速、过弯减速、信号灯前减速再加速的过程。打个比方一段路限速60km/h你一路上没停过车但因为前方车多实际平均只开到40km/h。此时你的waitingTime可能是0但timeLoss却不少。所以在评价路网整体运行效率时timeLoss比waitingTime更全面我通常优先看它。arrivalSpeed这个字段也值得你多留个心眼。如果大量车辆的arrivalSpeed明显低于道路限速说明车辆很可能到了目的地附近还在堵车而不是顺利进场。这在分析大型枢纽或停车场出入口时很管用。2.2 summary.xml的全局步长统计适合看整体演变趋势summary.xml是另一个高频使用的文件。它的形式是这样的step time0.00 loaded10 inserted5 running5 waiting1 ended0 arrived0 collisions0 meanWaitingTime0.00 meanTravelTime0.00 meanSpeed0.00 meanSpeedRelative0.00/每个step元素对应一个仿真时刻的整体路网快照。最关心的几个字段loaded已经加载的车辆总数也就是需求总量。inserted已经成功进入路网的车辆数。running当前仍在路网中行驶的车辆数。waiting当前处于等待状态的车辆数注意这是一个瞬时值不是累计时长。ended已经结束旅程的车辆数。arrived已经成功到达目的地的车辆数。collisions发生碰撞的车辆数正常情况下应为0。meanSpeed当前路网内所有运行车辆的平均速度单位是m/s。我最常拿summary.xml来做两件事。一是检查仿真是否“跑稳了”看running曲线是否在某个时间段后进入平台期如果一直在爬升说明需求大于路网容量拥堵可能在不断积累。二是快速对比不同方案的整体效率把meanSpeed按时间画出来看哪个方案的速度曲线掉得更少。有一点必须提醒summary里的meanSpeed是瞬时的算术平均把停在路口的车和高速行驶的车混在一起取平均不代表某个路段或某部分车辆的体验。它适合看趋势不适合做绝对评估。要精确评估某条路还得回到lane级别或车辆级别的数据。3. 从一堆XML里挖出结论实操分析流程3.1 数据清洗第一课单位、缺失值和不完整记录拿到tripinfo.xml直接开搞统计多半会掉进几个坑里。我一个个说。第一个坑是单位。SUMO里所有时间字段depart、arrival、duration、waitingTime、timeLoss单位都是秒速度单位是米每秒。但有些老教程和第三方工具会显示毫秒或km/h你要是没注意算出来的延误能差到姥姥家。我的习惯是写脚本时/1000或*3.6的地方全部注释标明防止过两周自己回来看也懵。第二个坑是未完成旅程的车辆。默认情况下如果仿真设定的结束时间到了仍有一些车辆没跑到目的地这些车不会出现在tripinfo.xml中。这意味着仿真的最后一段时间统计到的行程时间样本量会偏小这些样本还偏偏大部分是“跑得快”的车导致平均延误被严重低估。解决办法有两个。要么把仿真结束时间拉长确保所有车都完成旅程要么新版本SUMO支持加这个参数--tripinfo-output.write-unfinished true这样没跑完的车也会输出一条记录你可以在分析时单独标记这些车辆。第三个坑是vaporized字段。某些车辆在仿真过程中会因为碰撞、被移除等原因消失这类车的tripinfo记录里会带vaporized属性。如果你统计延误最好先把这些异常车辆剔除否则会出现“一辆车行程时间几十秒但它的延误数据莫名巨大”之类的脏数据。我写了一个最简版本的解析脚本用的Python标准库加pandasimport xml.etree.ElementTree as ET import pandas as pd tree ET.parse(tripinfo.xml) root tree.getroot() rows [] for ti in root.iter(tripinfo): if ti.get(vaporized) is not None: continue rows.append({ id: ti.get(id), depart: float(ti.get(depart)), arrival: float(ti.get(arrival)), duration: float(ti.get(duration)), waitingTime: float(ti.get(waitingTime)), timeLoss: float(ti.get(timeLoss)), routeLength: float(ti.get(routeLength)), arrivalSpeed: float(ti.get(arrivalSpeed)), }) df pd.DataFrame(rows) # 把出发时间转成小时方便按小时分桶统计 df[depart_hour] df[depart] // 3600 print(df[[duration, waitingTime, timeLoss]].describe())这段脚本跑完后你会得到每项指标的平均值、标准差、分位数等基础描述统计。但这里我要特别强调一句不要只看平均值。原因很简单交通延误数据的分布往往是偏态的。举个例子10辆车里有1辆因为事故堵了600秒另外9辆各花了10秒平均值是69秒但绝大多数人体验的是10秒。这时候看P50中位数也就是10秒才更能代表大多数人的真实感受。所以我每次统计都会输出P50、P85、P95这几个分位数很少单独用均值下结论。for col in [duration, waitingTime, timeLoss]: print(col, P50, df[col].quantile(0.5), P85, df[col].quantile(0.85), P95, df[col].quantile(0.95))在很多交通评价标准里P85是一个重要参考值因为它代表“大多数人都能享受到的服务水平”。如果你在汇报里只说均值一旦碰到个别极端堵点结论很容易被怀疑。3.2 用Python把结果变成图表延误曲线和速度分布解析出DataFrame之后画图就顺理成章了。我一般会画两类图。第一类是summary级别的全网运行曲线用来判断仿真过程是否健康。比如画运行车辆数和平均速度随时间的变化import matplotlib.pyplot as plt tree ET.parse(summary.xml) root tree.getroot() steps [] for step in root.iter(step): steps.append({ time: float(step.get(time)), running: int(step.get(running)), waiting: int(step.get(waiting)), meanSpeed: float(step.get(meanSpeed)), }) summary_df pd.DataFrame(steps) fig, ax1 plt.subplots(figsize(8, 4)) ax1.plot(summary_df[time] / 3600, summary_df[running], labelrunning vehicles) ax1.set_xlabel(simulation time (h)) ax1.set_ylabel(running count) ax2 ax1.twinx() ax2.plot(summary_df[time] / 3600, summary_df[meanSpeed] * 3.6, colororange, labelmean speed (km/h)) ax2.set_ylabel(mean speed (km/h)) plt.tight_layout() plt.savefig(network_overview.png, dpi150)从这张图上你能直观看到路网从0开始“灌车”的过程。如果曲线一直在上升没有稳定平台就说明仿真时长不够或者需求强度远超容量这时候分析延误数据意义不大得先解决模型本身的问题。第二类是tripinfo级别的延误分布箱线图用来对比不同方案的效果df.boxplot(columntimeLoss, bydepart_hour, figsize(8, 4)) plt.xlabel(depart hour) plt.ylabel(time loss (s)) plt.tight_layout() plt.savefig(timeloss_by_hour.png, dpi150)这张图能清楚展示早高峰时段的延误有多严重比单纯列一个平均延误数字更有说服力。3.3 从时间到空间FCD轨迹数据与路网热力tripinfo能告诉你“车花了多长时间”但很难告诉你“堵在哪里、怎么蔓延的”。这时候就要用fcd也就是浮动车轨迹数据。fcd文件的结构很简单每个时刻一个timestep里面每个车辆一条坐标记录timestep time5.00 vehicle idveh0 x12.34 y56.78 angle90.00 speed10.20 pos22.10 lanegneE0_0 slope0.00/ /timestep拿到这些坐标后你可以把车辆按路段网格聚合画出拥堵热力图。SUMO官方tools里有个fcd2shp.py能直接把fcd转成shapefile方便导入GIS或kepler.gl这类工具做可视化python $SUMO_HOME/tools/fcd2shp.py -n network.net.xml -o fcd_output.shp fcd.xml具体参数因版本而异建议先跑一下--help确认。对于个人分析来说我更常用的是直接解析fcd文件把每个路段上的车辆速度和数量按时间窗口聚合来判断排队回溢的范围和持续时间。这个步骤如果手写代码量偏大我一般在Jupyter Notebook里现算现画不会写成一劳永逸的脚本。4. 结果分析最容易踩的坑版本、统计与仿真随机性4.1 SUMO版本不同结果字段会有差异这一条很多人容易忽略。我见过不止一次有人拿着网上找的旧代码跑新版本SUMO结果解析脚本直接报错。SUMO版本更新很快输出字段有过多次调整node和edge的坐标格式、tripinfo的属性命名、fcd的坐标轴定义在不同版本之间都有细微差别。这就要说到“sumo安装”了。不管你是用官方安装包、conda、还是直接解压zip包不同渠道拿到的版本很可能不一样。我自己的习惯是装完先跑一下sumo --version然后把版本号写进项目文档里。分析脚本如果是跨版本使用的我会先用一条车的小场景跑一遍打印出tripinfo和fcd的实际字段确认无误后再批量分析。4.2 别只看均值拥堵评估要用分位数和置信区间前文提到过分位数的问题这里再展开说一个特别容易被忽视的点单次仿真结果不等于稳定结论。SUMO的随机性主要来自两个地方一是车辆出发时间的随机扰动二是路径选择决策的随机因素。即使输入文件完全不变只是改了随机种子--seed两次仿真跑出来的平均延误也会差不少尤其在路网接近饱和的时候。我做过实测一个轻度拥堵的网格不同seed下全网平均timeLoss能相差20%以上。所以严谨一点的方案对比至少跑3到5次不同seed然后看平均值的区间而不是拿单次结果下结论。成本允许的话跑10次取均值和标准差画个误差条报告会硬气很多。同时对比两个方案时尽量用成对的seed做配对比较。也就是方案A和方案B在同一批seed下各跑一遍然后用每对seed的差值来判断优劣这样能抵消掉很多随机噪声。4.3 仿真预热期与统计口径跑10分钟和跑1小时的结论完全不同最后一个大坑是“预热期”。仿真刚开始的几分钟路网里车辆很少平均速度很高延误很低。如果你把这段数据也统计进去它会拉低整体延误。这在对比短时仿真时影响尤其明显。正确做法是先确定一个预热窗口丢弃掉前5分钟或前10分钟的数据只统计稳定期的指标。如果路网很大、需求加载时间很长预热期要相应加长。你可以在分析代码里简单做一个过滤条件df df[df[depart] 600] # 丢弃前600秒出发的车辆同样重要的是不同方案之间必须保证用同一个统计时间段。有些朋友改了一个交叉口的配时就把整个仿真时间的平均延误拿出来对比这没问题但前提是两次仿真的需求加载过程和统计窗口完全一致否则对比没有意义。另外说一句关于信号控制的话如果你仿真的路网有固定配时信号灯统计窗口最好覆盖完整的信号周期整数倍否则不同相位下车流的通过率差异会导致结果出现偏差。我一般会把统计时间设置成信号周期2倍以上的长度并确保对比方案使用相同起止时间。5. 结果文件之外的隐藏用法和工作习惯最后分享几个我长期用下来的小经验不成体系但每一条都是实际项目中验证过的。第一把输出配置单独抽成一个配置文件。我通常在场景目录下建一个output_config.xml专门放所有输出相关参数然后在主cfg里用命令行的方式引入它。这样跑不同实验时只需要复制粘贴输出配置不会因为漏了一条tripinfo-output而白跑一小时。第二跑完仿真先看summary的最后几行。如果ended数等于总的loaded数说明所有车辆都在仿真结束前完成了旅程tripinfo数据是完整的。如果ended数小于loaded数那你得掂量一下是延长仿真时间还是开启--tripinfo-output.write-unfinished记录未完成车辆。这个习惯能帮我第一时间判断结果的可信度而不是等分析完了才发现样本缺了一块。第三分析脚本先在小区块上调试。SUMO跑大场景很耗时真正的大网络跑一次可能半小时以上。不要拿着全量数据边跑边调脚本先在一个只有几十辆车的小网格上验证解析逻辑确认字段、单位、过滤条件都正确了再用大场景全量跑。这一步能节省大量时间。第四fcd文件不要轻易开。默认的fcd输出粒度很细文件体积膨胀极快。如果只是想看大致拥堵分布把输出周期调到10秒甚至30秒完全够用如果要做轨迹级微观分析再考虑开1秒粒度。我见过有人跑一个5000辆车的场景忘了改fcd粒度结果一次仿真生成了60GB文件连打开都困难。我自己现在做结果分析标准动作就是跑完仿真先看summary确认数据完整性再解析tripinfo算分位数和延误指标最后按需用fcd做空间热力。这套流程虽然土但每一步都经得起推敲出问题也能快速定位到模型还是统计的问题写报告的时候心里也踏实。
返回列表