
“仿真跑完的那一刻很多人以为万事大吉其实真正的工作才刚刚开始。”这是我在带新人用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], binsrange(0, 3601, 600)) hourly_stat df.groupby(depart_bin, observedTrue).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/s50km/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.periodperiod我通常设成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的结果分析没有“一键出图”的魔法按钮它就是一个不断重复“数据清洗、指标计算、参数反推、校核验证”的过程。跑通仿真不稀罕能从结果文件里读出故事、提出方案、给出结论才是这个工具真正值钱的地方。这套流程如果你能完整走通一遍后续换任何路网、任何方案都会像条件反射一样知道下一步该看什么数据、该信什么结论。希望这篇复盘能帮你少走几个我当年走过的弯路把时间花在真正的交通问题分析上而不是耗在文件格式和数据异常里反复折腾。