“仿真跑完的那一刻,很多人以为万事大吉,其实真正的工作才刚刚开始。”这是我在带新人用SUMO做交通仿真时常说的第一句话。项目标题是“SUMO_(14).仿真结果分析”,也就是说,你能走到这一步,说明已经完成了路网搭建、流量配置、仿真运行这些前面的环节,手里面应该攒下了一堆输出文件——tripinfo.xml、fcd.xml、summary.xml这些。标题看着平平无奇,但它的水分很大:仿真结果分析根本不是“看一眼动画,绿波带没堵就完事”的活儿,而是从数据里把路网的问题、方案的优劣、参数的对错全部挖出来的过程。这篇文章就围绕“SUMO仿真跑完之后怎么分析结果”这件事,把数据从哪来、每个文件怎么读、关键指标怎么算、踩过的坑怎么排,完整地捋一遍。
如果你是刚把SUMO装好、能跑通第一个仿真,但又对着输出文件一脸懵的初学者,这篇文章能帮你把“结果分析”这扇门推开。如果你是做方案对比的从业者,这里也有一些关于多场景批量分析的方法论可以参考。默认你已经配置好了可用的SUMO环境——如果还没装好,网上有大量安装教程可参考,我这边直接跳过去。
1. 仿真结果分析的整体思路
1.1 核心需求的拆解:分析到底在分析什么
仿真结果分析这件事,看起来像是“跑完sumo之后打开GUI看一眼”,但实际上它的核心需求分三层。第一层是验证性分析:路网有没有bug、信号灯逻辑有没有错、车辆有没有非法路径,这一步靠时间损失指标和可视化逐车排查。第二层是评价性分析:给定一套流量数据或者信号配时方案,整个路网跑下来效果怎么样,平均行程时间多少、排队多长、哪个路口是瓶颈,这一步靠聚合统计指标。第三层是决策性分析:对比多个方案,选出最优设计,这一步靠多场景批量仿真和统计检验。
理解这三层需求很重要,因为很多人一上来就盯着“平均速度”一个数字猛看,结果路网里有辆车一直在死循环里打转,平均速度被拉低了你都不知道是为什么。分析的顺序应该是从微观到宏观:先保证单辆车的行为正常,再做全网指标统计,最后才是方案之间的横向对比。我在做实际项目时,几乎总是按这个顺序走,跳过前两层直接谈方案优劣的,结论基本站不住脚。
1.2 数据载体的三种形态:文件、可视化、日志
SUMO的结果分析,数据载体其实有三种,不能只盯着一类。第一种是输出文件,也就是在sumo配置里通过--tripinfo-output、--fcd-output这些参数生成的数据文件,这是分析的“硬通货”,所有量化结论都从这里来。第二种是运行时可视化,也就是sumo-gui里看到的动态画面,它的作用是给人看的,适合快速发现异常,比如某个路口莫名其妙堆成一团、某辆车路线绕远路,这些靠肉眼扫一遍就有概念,但你没法从可视化的画面里精确提取“平均延误5.3秒”这种结论。
第三种容易被忽略:运行日志。SUMO在跑的过程中会往命令行窗口输出warning和error信息,比如Warning: Teleporting vehicle或者Error: Could not build route。这些日志看似烦人,却是最直接的排查线索。我的习惯是每次仿真都保留一份运行日志,分析结果之前先grep一遍warning,如果warning数量超过正常范围,输出文件的可靠性就要打问号。几年前我调一套公交优先信号方案,仿真的排队指标一直异常,最后发现日志里大量公交车在站点处“teleport”,也就是车辆被系统强制跳转,等于公交车根本没老老实实开完路线,所有指标自然失真——这类坑藏在日志里,可视化是看不出端倪的。
1.3 指标先行:先想清楚要什么,再决定开什么输出
这是我最想强调的一点:输出文件不是开得越多越好。每种输出文件都会显著增加仿真耗时和磁盘占用,比如fcd文件(浮动车数据)在做细粒度轨迹分析时很有用,但它按时间步记录每辆车的坐标、速度,跑一个一小时仿真的fcd文件轻轻松松上GB,如果只是看全网平均速度,开fcd就是灾难。
正确做法是“指标先行”。做交通仿真,先列出你要回答的问题:要评估信号配时是否合理?那核心指标是平均行程时间、平均时间损失(timeLoss)和排队长度,对应开的是tripinfo和queue输出。要做排放分析?那开emission输出。要找拥堵瓶颈?那开edgeData输出。不要一把梭把所有输出全开,我见过有人一次性开了六种输出文件,仿真本来跑10分钟,开到fcd之后跑了快一个小时,而且大部分数据根本用不上。动手跑仿真之前,花三分钟列清楚“我需要哪些指标”,这能省你后面几个小时。
2. 核心输出文件的字段含义与读取方法
2.1 tripinfo.xml:逐车辆的全生命周期记录
tripinfo文件是SUMO里最常用、信息密度最高的输出文件。它的每一行对应一辆车的一条完整出行记录,包含从出发到抵达的全生命周期数值。字段看着多,但核心就几类:时间类(depart出发时刻、arrive到达时刻、duration总行程时间、timeLoss时间损失、waitingTime等待时间)、空间类(routeLength路线长度、departPos出发位置、arrivePos到达位置)、速度偏差类(speedFactor、meanSpeed等)。
这里有两个字段特别容易看岔。duration是车辆从出发到抵达的总耗时,包括停车等待时间;timeLoss是车辆在行驶过程中因为拥堵、信号灯、跟车等原因损失的时间,它的计算逻辑是“实际行程时间-理想行程时间”,等于把环境干扰从总时间里剥离了出来。区别在于:如果一辆车在红灯前等了30秒,duration和timeLoss都会增加;如果一辆车在路上正常开但被前车挡着慢速跟了100米,timeLoss会变大而waitingTime未必变。做信号配时评价时,timeLoss是最敏感的指标之一,因为它直接反映“我的方案让车辆多花了多少不该花的时间”。
读取tripinfo文件,我的建议是不要用文本编辑器硬看。文件大一点之后,几万辆车就是几万行,必须用脚本解析。Python里用xml.etree.ElementTree或者pandas的read_xml(需要较新版本的pandas)都能快速读入。解析完之后,按路段、按方向、按出发时间段做聚合,才能真正看出问题。比如把早高峰9:00-10:00出发的车的平均timeLoss单独拉出来,跟平峰时段对比,信号配时是否适应潮汐交通立刻就有数了。
2.2 fcd.xml与summary.xml:从微观到宏观的数据视图
fcd文件的定位是“微观轨迹”,全称Floating Car Data,记录的是每辆车在每个仿真时间步的位置、速度、角度、车道等快照信息。它的用途有两个:一是做高精度的轨迹回放分析,比如研究车辆变道行为、跟车间距;二是做路段级别的速度/密度分析,把fcd数据按路段切片求平均速度,就能得到任意时段的路段拥堵演化情况,这个信息tripinfo文件给不了,因为tripinfo只有起点终点和总耗时。fcd的缺点上面说过——文件巨大。如果你只想做10个路段的速度分析,没必要开全网的fcd,后面会讲怎么用edgeData替代。
summary.xml的逻辑恰好相反,它按固定时间间隔输出全网或者指定区域的聚合统计值,包括当前运行中的车辆数、平均速度、总等待时间、拥堵比例等。它是“宏观仪表盘”,跑一批方案做快速对比时,看summary比汇总tripinfo快得多。我常做的一种操作是:跑完仿真后,如果只是粗糙判断方案好坏,先看summary里全网的meanWaitingTime和meanSpeed两个字段;如果结论方向明确,再回到tripinfo做精细统计。这两类文件一个看全过程、一个看某个瞬间,配合起来正好覆盖宏观与微观两个尺度。
2.3 edgeData.xml与emissions.xml:专项分析的数据基础
edgeData是路段级别的统计输出,它把每个路段的流量、平均速度、密度、排队时长按时间间隔汇总成记录,是交通瓶颈识别的利器。它的优势在于:不用解析海量fcd,直接得到“哪条路在哪个时段最堵”。开启方式是配置里加--edge-output加--edge-data-output参数,再配合--edge-output.period控制统计时间片长度。实际操作中我会把统计片设成5分钟或15分钟,太细了噪音大,太粗了看不出拥堵演化过程。
emissions.xml则是环保向分析的基础,它输出每辆车在每个时间步的CO2、CO、NOx、PMx排放量。这类数据在做绿波优化、限速方案评估时很有价值:调整一组信号配时,不仅看行程时间变化,还看总排放能不能同步下降。排放计算依赖SUMO内置的HBEFA模型,模型参数在additional文件里可以通过<vType>的排放类别设置,比如把普通小客车设置成HBEFA3/LDV_G_EU4,就能得到符合欧四标准的轻型汽油车排放特征。这个文件同样巨大,如果你只想算全网总排放,我建议直接用summary文件里自带的emission总量汇总字段,别去解析emissions.xml的逐车逐秒记录。
3. 数据清洗与指标计算的完整实操
3.1 配置仿真输出参数的完整流程
分析之前先要看输出到底配置对了没有。SUMO输出配置写在.sumocfg文件里,或者直接在启动命令里加参数。以我常用的启动命令为例:
sumo -c my_scenario.sumocfg \ --tripinfo-output tripinfo.xml \ --summary-output summary.xml \ --edge-output edge.xml \ --edge-data-output edgedata.xml \ --fcd-output fcd.xml \ --tripinfo-output.write-unix-timestamps true短短几行参数,后面藏着好几个坑。第一,--edge-output和--edge-data-output是两个不同的文件,前者输出每个时间片的道路状态,后者输出聚合统计数据,很多人搞混,开了前者以为就有路段均速,结果字段对不上。第二,如果跑的是大路网,我建议在配置里加上--no-step-log和--no-duration-log,否则命令行会被仿真进度刷屏,日志文件也会膨胀。第三,--tripinfo-output.write-unix-timestamps这个参数能把时间戳从仿真秒转换成真实时间戳,做跟检测器数据对比时必须开,否则两边时间轴对不上,你没法判断仿真里的“600秒”是上午几点。
跑批处理脚本时,不要开GUI模式,直接用sumo命令行跑,速度能快一个量级。我测试过一个中等规模路网,GUI模式跑2000秒仿真大约要15分钟,同样的配置用cli模式只要3分钟。对于要做几十组方案比选的项目,这个时间差距是决定性的。GUI只在调试单次仿真、确认逻辑正确时用,跑数据产出坚决切回cli。
3.2 用sumo-gui可视化校验结果的三个技巧
可视化不是用来“看图说话”的,它的正确定位是校验。我在分析前一定会用sumo-gui做三个检查。第一个是车辆路径合法性检查:开启Options > Simulation > Teleport的显示开关,如果地图上出现显眼的车辆跳变轨迹,说明车辆因为无法到达目的地被系统强制传送了,这种情况一旦存在,所有相关车辆的tripinfo数据全部无效,相当于这辆车“作弊”了,必须排查路网或者路由逻辑。第二个是信号灯相位检查:把仿真停在一个周期内,逐秒盯着关键交叉口看信号相位切换顺序和配时跟你的方案是否一致,这一步能发现“相位写反了”“绿灯冲突”这类低级但致命的错误。第三个是排队长度观测:把鼠标悬停在路段上,SUMO会显示当前排队车辆数和排队长度,观察几个关键路段在高峰期的排队演化,对比仿真结果里排队显著超出了现实观测的临界点,说明路网容量参数可能设错了。
这三个检查都是“结果分析”不能跳过的前置步骤。有人会问,我直接拿tripinfo算出平均延误,结果特别好看,是不是能跳过可视化?答案是绝对不能。数据好看有可能是因为流量输入就偏小,或者信号灯设置成全程绿灯了,这些愚蠢错误的代价就是全盘返工。可视化校验收的是“整个仿真过程合不合理”,输出文件统计回答的是“方案效果好不好”,两件事缺一不可。
3.3 Python数据解析与指标计算示例
下面给出一段我在项目里反复用的Python脚本雏形,作用是解析tripinfo.xml并计算核心指标。代码不长,但结构可以直接套用在你的场景里。
import xml.etree.ElementTree as ET import pandas as pd tree = ET.parse('tripinfo.xml') root = tree.getroot() records = [] for trip in root.findall('tripinfo'): records.append({ 'veh_id': trip.get('id'), 'depart': float(trip.get('depart')), 'arrive': float(trip.get('arrive')), 'duration': float(trip.get('duration')), 'timeLoss': float(trip.get('timeLoss')), 'waitingTime': float(trip.get('waitingTime')), 'routeLength': float(trip.get('routeLength')), 'averageSpeed': float(trip.get('averageSpeed')), 'speedFactor': float(trip.get('speedFactor')), }) df = pd.DataFrame(records) # 全样本核心指标 print("总车辆数:", len(df)) print("平均行程时间(s):", df['duration'].mean()) print("平均时间损失(s):", df['timeLoss'].mean()) print("平均等待时间(s):", df['waitingTime'].mean()) print("平均行驶速度(km/h):", df['averageSpeed'].mean() * 3.6) # 按出发时段分桶统计 df['depart_bin'] = pd.cut(df['depart'], bins=range(0, 3601, 600)) hourly_stat = df.groupby('depart_bin', observed=True).agg( avg_duration=('duration', 'mean'), avg_timeLoss=('timeLoss', 'mean'), cnt=('veh_id', 'count') ) print(hourly_stat) # 找出时间损失最大的20辆车(重点嫌疑对象) worst = df.nlargest(20, 'timeLoss')[['veh_id', 'depart', 'duration', 'timeLoss', 'routeLength']] print(worst)这段脚本的产出是非常关键的三张表:全网聚合指标、分时段指标变化趋势、单辆车的异常名单。分时段统计尤其有用,它能看出早高峰到底从几点开始恶化、几点回落。再看异常车辆名单,如果top20的timeLoss远远甩开其他车,比如平均时间损失是50秒,却有车损失了500秒,这类离群值会明显干扰平均值,排查时优先定位它们,往往能发现路网某个路段或者某个路口存在极端的排队回溢现象。
3.4 一个完整的信号配时对比案例
纸上谈兵到此为止,我用一个实际做过的简化案例演示完整的分析流程。假设要给一个T型交叉口做信号配时优化,现状周期60秒,绿灯时间东进口30秒、南进口20秒。方案A是把周期改成80秒,绿灯时间东进口40秒,南进口30秒;方案B是引入感应控制(用actuatedTrafficSignal逻辑)。流量数据固定,随机种子固定,分别跑三次仿真,输出tripinfo。
流程先走可视化校验:三个方案跑出来的车辆轨迹都没有teleport,信号相位图标跳转正常,基本可信。然后跑Python脚本,得到三个方案的对比:
- 现状方案:平均行程时间38.2秒,平均timeLoss 12.1秒
- 方案A:平均行程时间34.6秒,平均timeLoss 9.5秒,下降约21%
- 方案B:平均行程时间33.9秒,平均timeLoss 9.1秒,下降约25%
如果到这里就下结论“B最优”,那还是太急了。接下来要做的才是结果分析的精髓——看分化程度。计算三个方案timeLoss的标准差:现状方案标准差是6.8秒,方案A是4.2秒,方案B是4.7秒。发现没有,B虽然均值最低,但波动比A大,说明B的感应控制对部分来车方向更友好,但也引入了更多不确定性。再结合后续对南进口方向单独统计,发现B方案南进口的排队长度在高峰期末端出现回溢风险。最终方案选择就不能只看一个均值,而是结合“整体效率”和“稳定性”两个维度的权衡。
这个案例是想说明:结果分析不是算一个数字就交差,而是从多个维度反复切割数据,找出数字背后的路网行为模式,这样才能支撑真正的交通决策。
4. 常见问题与排查技巧实录
4.1 高频问题速查表
仿真结果分析阶段,我遇到的高频问题基本集中在下面几个,做成速查表方便直接定位:
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 跑完仿真却找不到tripinfo.xml | 配置里没开--tripinfo-output | 检查sumocfg或者启动命令参数 |
| tripinfo.xml生成了但缺少部分车辆 | 这些车被teleport了,或者根本没发出 | 看日志里Teleporting和route related warnings,优先排查路网连通性 |
| 结果平均速度异常低(低于5km/h) | 往往路网里存在死锁或者大量排队回溢 | 用GUI查看死锁交叉口,检查是否缺少让行规则或信号灯相位冲突 |
| 时间对不上:仿真跑了600秒,数据里到达时间却大于600 | 部分车辆在仿真结束后还没到达,它们不会出现在tripinfo里 | 拉长仿真时长,或者检查车辆出发时间分布是否合理 |
| 数据显示平均speedFactor接近0 | 路网的限速设置有问题 | 检查edge的speed属性,很多默认路网限速是13.89m/s(50km/h),如果被误设成0.1就全乱套 |
| 多跑几次结果波动特别大 | 随机种子影响路线选择和驾驶行为 | 固定--seed,或者多次仿真取平均值再做方案对比 |
| 输出文件巨大(GB级) | fcd或者emissions输出粒度太细 | 减小输出范围,或改用--fcd-output.time-period加大时间间隔 |
这些问题我在前面多轮项目里几乎全踩过一遍。其中最坑的是“部分车辆没出现在tripinfo里”这种,数据缺失是隐蔽的,表面看所有统计都能正常算出来,但结果偏乐观,因为没到达的车恰恰是最堵的那批,把它们漏掉等于把拥堵“优化”没了。每次分析前检查一下tripinfo里的车辆数量跟输入的总车辆数是否一致,这是个15秒的检查,能挽回好几小时的方向性错误。
4.2 结果不可信的根源排查
如果排除了文件配置问题,结果依然看起来不对劲,就要往下深挖仿真本身的保真度。第一要查路网:SUMO可以在地图数据上二次编辑,导入的路网如果拓扑有问题、连接关系错误,车流会莫名其妙绕路,体现在数据上就是某些路段流量奇高而另一些几乎没有车,这种结果再漂亮也没有意义。第二要查流量输入:OD矩阵或者车流定义是否覆盖了高峰期的真实交通需求,如果输入流量本身就比现实少了30%,得到的平均速度自然比实测好看,这是典型的“garbage in, garbage out”。第三要查驾驶行为参数:SUMO默认的跟车模型参数(比如accel、decel、sigma)并不适配所有场景,跑高速路段和跑城市拥堵路段,对驾驶行为参数的标定需求完全不同。我做过一个快速路仿真,默认参数下车速标准差明显偏大,导致最内侧车道频繁出现幽灵拥堵,调整sigma和tau之后结果才趋于合理。
另外要提一个方法论层面的判断准则:仿真结果的绝对值不可迷信,相对值更有参考价值。SUMO仿真的绝对数值依赖太多假设(路网细节、驾驶行为、流量分布),但在控制变量的前提下,方案A比方案B好多少这件事,同一套仿真环境下是比较可信的。因此项目报告里我写结论的时候,习惯输出“方案A的timeLoss相对方案B降低了18%”,而不是“方案B的平均行程时间只有34秒”——后者经不起现实检验,前者是同一系统内的相对比较,更有说服力。
4.3 批量仿真的脚本化技巧
做方案对比绕不开批量仿真,一条一条命令手动跑既慢又容易出错。我习惯写一个简单的shell循环,把方案名作为变量传入:
for seed in 1 2 3 4 5; do for scenario in base planA planB; do sumo -c ${scenario}.sumocfg \ --tripinfo-output results/${scenario}_seed${seed}_tripinfo.xml \ --summary-output results/${scenario}_seed${seed}_summary.xml \ --seed $seed done done跑完之后用Python把所有tripinfo文件汇总进一个DataFrame,用两列记录scenario和seed,再做分组统计。这里有个细节:不同seed的结果差异相当于模拟了交通流随机波动,它本身就是方案稳定性的评估素材,不应当被“平均一下”抹掉。正确做法是统计每个方案的均值±标准差,方案选择时不仅看均值高低,也看方差大小。一个均值略差但波动极小的方案,在实际工程里往往比均值好看却忽好忽坏的方案更值得推荐。
5. 从仿真数据到交通决策:结果分析的扩展应用
5.1 瓶颈识别的路段级分析流程
前面提到过edgeData是瓶颈识别的利器,这里补充一段具体操作流程。开启模式是--edgedata-output和--edgedata-output.period,period我通常设成300秒。跑完仿真后用Python读取edgedata文件,按edge_id和时间片分组,计算每个时间片的平均速度与最大排队长度。把结果按时间片排序,找出速度持续低于20km/h的路段和时间段,这些就是瓶颈候选点。
再进一步做根因分析:把瓶颈路段和它上游路段的流量数据放一起看,如果上游流量正常、瓶颈路段排队却不断累积,问题大概率出在瓶颈路段下游的通行能力上,比如出口车道太少、信号灯绿灯时间不足。这一步的价值是数据指向了“哪里堵”,让你能带着假设去GUI里验证,而不是漫无目的地看动画。我在做片区路网优化时,瓶颈列表基本决定了整个方案的优先级——先解决几个主要拥堵点,再谈全局协调,这是最经济的优化路径。
5.2 多方案比选时的统计口径统一
多方案比选最容易出现的争议是指标口径不统一。比如方案A报告里用了“平均行程时间”,方案B报告里用了“平均出行速度”,这两个指标看着都能说明问题,但计算口径不同,比较起来就会有偏差。我建议项目内部强制统一一组核心指标集:平均行程时间、平均timeLoss、平均等待时间、总排放、最大排队长度,所有方案必须输出这五个指标,缺一个都要打回重算。另外时间范围也要统一,早高峰仿真就统一统计7:00-9:00出发的车辆,平峰就统一统计10:00-16:00,绝不允许某个方案单独多算了一段低峰数据来拉低均值——这是报告里红线的级别,数据严谨性不能在这方面让步。
5.3 仿真与实测数据的校核闭环
结果分析做到位,最终要回答一个灵魂拷问:仿真里的数字,现实里存在吗?如果项目里有实测数据,比如交叉口检测器的流量和排队数据,一定要拿出来跟仿真输出做校核。校核方法不复杂:把同一时段的实测流量和仿真流量画在一张折线图上,观察峰值时段是否吻合、总量差异是否在10%以内。如果仿真流量峰值明显超前或滞后于实测,路网出发时间分布或路径选择概率需要回头修正。这类校核做一轮之后,仿真的结果可信度会显著上升,后续优化方案的结论也更能经得起技术评审的推敲。
我在实际项目里最深的一个体会是:SUMO的结果分析没有“一键出图”的魔法按钮,它就是一个不断重复“数据清洗、指标计算、参数反推、校核验证”的过程。跑通仿真不稀罕,能从结果文件里读出故事、提出方案、给出结论,才是这个工具真正值钱的地方。这套流程如果你能完整走通一遍,后续换任何路网、任何方案,都会像条件反射一样知道下一步该看什么数据、该信什么结论。希望这篇复盘能帮你少走几个我当年走过的弯路,把时间花在真正的交通问题分析上,而不是耗在文件格式和数据异常里反复折腾。