尧图精选

SUMO交通仿真实战:需求生成、sumocfg配置与输出解析

🕒 发布时间:2026/10/1 5:09:09 📁 来源:尧图网络
路网能跑通了、车也能动了结果一看输出文件全是空的——这大概是每个用 SUMO 的人都会经历的第三阶段。前面两篇我们把 SUMO 装好、把路网从 OSM 或者手写节点的方式建出来了net.net.xml躺在目录里看着挺像回事可一旦开始跑仿真要么路网上一辆车都没有要么车全堵在路口表演行为艺术。这篇就专门讲这一段的破局思路sumo 仿真环境从能打开到能出数据之间到底缺了哪几块拼图。这一篇的定位是系列里的需求与运行环节核心围绕三件事展开交通需求怎么从零生成、sumocfg配置文件到底该怎么写、仿真跑完之后那些 xml 输出怎么读。适合已经能把路网导入进来、但卡在跑不出有意义结果这一步的朋友也适合想从 GUI 点点点升级到脚本批量化跑仿真的同学。整套流程我用下来是稳定的但踩坑的地方相当多尤其是需求生成脚本的参数和路由计算这两块后面会重点展开。需要说明的是文中涉及的参数取值和目录组织方式是基于城市道路通行场景的常见实践补全的你在具体项目里需要按自己的路网尺度微调。1. 第三篇要解决的问题从路网到仿真的最后一段路1.1 前两篇留下的半成品到底缺什么跑过前两篇的朋友应该有个共同体验netconvert或netgenerate生成的net.net.xml是完整的SUMO 能加载它、能画出来、能看到车道线和路口。但这时候你点下运行按钮仿真时间在走路上却一辆车没有。这不是软件坏了而是 SUMO 的设计哲学决定的——路网和需求是彻底解耦的。路网只描述物理空间也就是哪里有条路、这条路几条车道、限速多少、哪个路口和哪个路口相连。它完全不知道上面该有多少车、这些车从哪来、要到哪去。需求文件才负责描述交通流包括车辆从哪条边出发、走哪条路径、什么时候进入、开多快。这两者最后由一个配置文件粘在一起。所以缺的拼图就是两块需求车流和配置把路网、需求、时间、输出串起来的东西。很多人第一次用 SUMO 会想当然地觉得路网建好了仿真就能跑这个直觉来自其他一些集成度更高的仿真软件它们把路网和交通流放在一个工程里管理。SUMO 走的是 UNIX 那套每个工具只干一件事的路线好处是灵活、可脚本化、适合批量做场景代价就是初次上手要理解这个拼装逻辑。我个人的体会是一旦理解了这个解耦思想后面做参数扫描和批量实验会非常舒服因为你可以只换需求文件、复用同一个路网跑几十组对比实验。1.2 需求、路网、配置三者的依赖关系先把依赖关系理清楚后面所有操作都能对号入座。路网是基础提供边edge的 id、车道数、连接关系connection。需求依赖路网因为车辆出发时要说清楚在哪个 edge 上出现路径也要沿着路网里真实存在的连接走写错了 SUMO 直接报错说这条边不存在或者这两条边不连通。配置依赖前两者它是仿真启动时的总入口告诉 SUMO 去哪个文件加载路网、去哪个文件加载车辆、仿真从第几秒跑到第几秒、输出写到哪里。这个依赖链有个很实际的推论需求文件不能脱离路网单独生成。你没法先随便编一堆车的路径再去找一个路网来配它。正确顺序永远是先有路网、再基于路网生成需求、最后写配置。我见过有同学从别处拷了一份routes.rou.xml过来结果里面引用的 edge id 跟自己路网的完全对不上一运行满屏报错排查半天才发现是文件压根不配套。还有一点容易被忽略路口连接关系会影响路由可行性。比如路网里有条边 A 和边 B物理上看着是接着的但如果net.net.xml里的 connection 没定义 A 到 B 的转向路由算法就不会给出经过 A→B 的路径。这个问题在手工编辑路网时特别常见输出表现为某几辆车找不到路径或者路径绕了很远。所以生成需求前建议先确认路网是连通的。1.3 工程目录怎么组织才不返工这一条是纯经验教科书上不会写。我强烈建议在动手之前先把目录结构定下来否则做到后面文件版本一多你自己都分不清哪个是最新的。我的习惯是按阶段分目录大致长这样project/ ├── net/ # 路网相关 │ └── net.net.xml ├── demand/ # 需求相关 │ ├── trips.trips.xml │ ├── routes.rou.xml │ └── vtypes.add.xml ├── config/ # 仿真配置 │ └── sim.sumocfg ├── output/ # 仿真输出 │ ├── tripinfo.xml │ ── summary.xml ── scripts/ # 脚本 └── run.py这样做有几个好处。第一路网和需求分离换需求做对比实验时不用动路网。第二输出单独放一个目录方便批量清理跑完一轮删掉 output 里的内容就行不会误删源文件。第三脚本独立出来后面要做参数扫描或者接 TraCI直接在这里扩展。提示全流程里所有的相对路径都是相对于你启动 SUMO 时的工作目录不是相对于配置文件所在目录。这是个非常经典的踩坑点配置文件里写net.net.xml而实际文件在net/子目录下结果就是一句could not access file。2. 交通需求建模让路网上真正跑起车来2.1 trips、flows、routes、OD 四种需求写法怎么选SUMO 的需求描述有好几种格式新手最容易在这里犯迷糊。我按抽象层次从低到高捋一遍你按手头数据的精度选。routes路径级是最直接的每辆车明确写清楚出发时间、出发边、完整路径。适合你已经知道每辆车的确切路径或者用于调试和教学。vehicle idv0 depart0.00 route edgesE0 E3 E7 E12/ /vehicletrips起讫级只给出起点边和终点边路径交给路由工具算。适合你有 OD 信息但没有具体路径的场景。trip idt0 depart0.00 fromE0 toE12/flows流量级是在 trips 基础上加了流量概念用number或period指定一段时间内发多少车。做宏观流量验证时用它最省事。flow idf0 begin0 end3600 number600 fromE0 toE12/OD 矩阵是最宏观的一个矩阵描述各交通小区之间的出行量。它需要配合od2trips转成 trips再配合duarouter算路径。适合从交通调查数据出发做大尺度场景。选型逻辑很简单数据精度到什么程度就用什么格式。只有路口转向流量用 flows 或者 OD有完整的路径调查用 routes。我日常做场景验证大多用 flows因为它写起来快、改起来方便改一个 number 就能调流量强度。2.2 randomTrips.py 关键参数逐个拆解手上没有真实数据时SUMO 自带的randomTrips.py是最常用的造数据工具它在tools/目录下。这个脚本参数多得吓人但真正天天用到的就那么十来个我把最关键的几个按重要性排一下。参数作用常用取值-n指定路网文件net.net.xml-o输出 trips 文件trips.trips.xml-r输出路由文件内部调用 duarouterroutes.rou.xml--begin/--end仿真起止时间0/3600--period平均发车间隔秒1表示每秒一辆--fringe-factor边缘边吸引力倍数10让车多走边界--seed随机种子42保证可复现--trip-attributes给车辆附加属性departLanebest先说--period这是控制流量强度的核心。它表示平均每隔多少秒产生一辆车--period 1就是每秒一辆一小时 3600 辆--period 5就是每小时 720 辆。注意这里的平均是随机的实际发车间隔是围绕这个均值的随机分布不是精确等距。如果你想要精确的发车间隔改用--insertion-rate或者直接在 flows 里写period。--fringe-factor这个参数很有意思值得单独说。SUMO 路网里的边分两类边缘边fringe edge是只连了一头的、类似城市出入口的路内部边是两头都连着的。默认情况下车辆起终点在城市内部随机撒会导致大量短途出行、车刚上路就到终点了。把--fringe-factor调大就会让边缘边作为起终点的权重更高车就更倾向于从城市一头开到另一头跑出长途出行。我做城市级仿真时一般设到 10 以上。--trip-attributes是新手最容易忽略但特别有用的参数。它的写法有点绕需要转义引号python $SUMO_HOME/tools/randomTrips.py \ -n net/net.net.xml \ -r demand/routes.rou.xml \ --begin 0 --end 3600 \ --period 2 \ --fringe-factor 10 \ --seed 42 \ --trip-attributes departLane\best\ departSpeed\max\departLanebest表示让车选最合适的那条车道出发而不是默认的第 0 条车道。这个参数的作用在下面常见问题里会展开简单说就是避免车辆在出发边就发生挤兑。注意randomTrips.py内部调用 duarouter 时如果找不到路径会静默丢弃这些车所以最终车辆数往往少于你按 period 算出来的理论值。想要知道实际生成了多少车跑完后统计一下 rou 文件里的 vehicle 数量。2.3 duarouter 路由计算与算法选择如果randomTrips.py带了-r参数它内部已经帮你调了duarouter你不用手动再来一遍。但很多场景下我是分开跑的先用randomTrips.py只生成 trips再单独跑duarouter这样方便在中途对 trips 做二次处理也方便观察哪些 OD 对算不出路径。duarouter \ -n net/net.net.xml \ --route-files demand/trips.trips.xml \ -o demand/routes.rou.xml \ --ignore-errors true \ --routing-algorithm dijkstra--routing-algorithm有三个选项dijkstra、astar、CH。Dijkstra 是最稳妥的保证最短路径速度对中小路网完全够用。A* 在有大范围路网时更快但需要额外的启发式。CHContraction Hierarchies是预处理型的第一次跑会慢之后查询极快适合超大规模路网做反复查询。我一般在路网小于几千条边时直接用 Dijkstra省心。--ignore-errors true建议加上。路网里难免有些孤立的边或者某些 OD 对之间确实不连通不加这个参数 duarouter 遇到一个算不出路径的 trip 就可能中断整个流程。加上之后它会跳过这些 OD 对并给出警告你跑完看警告数量就知道路网连通性有没有问题——警告特别多的时候八成是路网本身有问题而不是需求的问题。duarouter 算出来的路径是不是物理上合理还有个细节值得看是否绕行。有时你会发现某辆车从相隔很近的两个点之间路径却绕了大半个城市。这通常不是算法的问题而是路网本身缺连接。排查方法是在 GUI 里加载这个路径文件打开车辆轨迹显示看它到底走了哪条线然后回到路网里检查那几个关键路口的 connection。2.4 车辆类型 vType跟驰模型参数怎么定需求文件里除了车的行程还得定义车的性格也就是vType。这是需求建模里技术含量最高的部分因为它直接决定仿真的行为真实度。vType idcar vClasspassenger accel2.6 decel4.5 sigma0.5 length5.0 minGap2.5 maxSpeed16.67 carFollowModelKrauss lcModelLC2013 speedFactornormc(1.0,0.1,0.2,2.0)/逐个解释关键参数。accel是最大加速度单位 m/s²普通小汽车 2.6 左右是常见的标定值。decel是舒适减速度取正值 4.5 表示减速能力注意这里填正数SUMO 内部处理方向。sigma是驾驶员不完美程度0 表示完美跟驰、1 表示非常随机的驾驶行为取 0.5 是比较折中的默认值。maxSpeed单位是 m/s16.67 对应 60 km/h。这里有个坑这个值是车辆自身的限速路网每条边也有一个限速两者取小。很多人设了maxSpeed却发现车开得比预期慢就是因为路段限速更低把车卡住了。speedFactor是我最推荐加的参数用normc分布给每辆车分配一个期望速度因子。上面这行表示均值 1.0、标准差 0.1、截断在 0.2 到 2.0 之间的正态分布。有了它仿真里车流速度就会自然分层不会出现所有车速度一模一样的机器人队伍效果通行能力和实际的差异会小很多。carFollowModel默认就是 Krauss这是 SUMO 的招牌模型参数少、稳定、够用。如果做精细的能耗或排放研究可以换成 IDM它的加减速曲线更平滑。换模型的时候要注意参数是跟着模型走的IDM 有自己的参数集直接把 Krauss 的参数套过去不会正常工作。提示vType既可以写在路由文件里也可以单独放在一个additional文件里通过配置引入。我推荐后者因为车辆类型在多个场景间通常是复用的单独一个vtypes.add.xml方便维护。3. 仿真配置与运行从能跑到跑得对3.1 sumocfg 文件结构拆解sumocfg是 XML 格式把所有输入输出串起来。它的结构是按功能分块我按块说明。configuration input net-file valuenet/net.net.xml/ route-files valuedemand/routes.rou.xml/ additional-files valuedemand/vtypes.add.xml/ /input time begin value0/ end value3600/ step-length value1.0/ /time output tripinfo-output valueoutput/tripinfo.xml/ summary-output valueoutput/summary.xml/ vehroute-output valueoutput/vehroutes.xml/ /output processing time-to-teleport value300/ ignore-route-errors valuetrue/ /processing random_number seed value42/ /random_number /configurationinput块里route-files可以写多个文件用逗号隔开这对多类车辆场景很实用。additional-files用来加载信号配时、公交站、检测器这些附加定义它和主路网是两个入口很多新手会忘了这个块导致信号配时没生效。processing块里的time-to-teleport是必须理解的参数。SUMO 里如果一辆车在同一位置堵超过这个秒数会被瞬移到下游默认是 300 秒。这个机制本意是防止死锁但它会悄悄改变你的拥堵统计结果。做拥堵研究时我一般把它设成 -1 关闭瞬移让车真正堵在那里否则你研究的拥堵其实是瞬移前的拥堵数据会失真。当然关掉之后要小心局部的死锁把整个仿真拖死需要配合观察。3.2 时间步长、随机种子与可复现性step-length是仿真步长默认 1 秒。这个值直接决定计算量和精度。步长 1 秒时高速行驶的车在一步内移动十几米跟驰模型的计算精度会打折。做高速场景或者细致的排放研究时我会把它降到 0.1 甚至更小代价是仿真时间成倍增长。seed是随机种子这个值的重要性怎么强调都不为过。SUMO 里车辆的发车时刻、speedFactor 的随机抽样、车道选择等等全都受这个种子控制。同一个配置、同一个种子跑两次结果应该完全一致。这个特性是做对比实验的生命线改一个参数其他全固定这样结果差异才能归因到那个参数上。我的习惯是每组对比实验跑 5 到 10 个不同种子然后取平均值和方差。只跑一个种子就下结论很容易被随机波动带偏。种子建议用固定的几个数字比如 42、123、456这样整个项目组跑出来的结果都可比。注意step-length改了之后--period或者 flows 里的发车时刻不用改因为它们是按仿真时间算的不是按步数。但如果你在脚本里用步数当循环条件要注意换算。3.3 命令行运行与 GUI 调试的分工sumo是无界面版本sumo-gui是带界面版本两者接受完全一样的参数。日常使用我的分工是调试用 GUI出数据用命令行。命令行跑sumo -c config/sim.sumocfg --tripinfo-output output/tripinfo.xml命令行跑的优势是快没有渲染开销批量跑几十组实验时差距非常明显。而且它能直接嵌进 shell 或者 Python 脚本里循环。GUI 跑sumo-gui -c config/sim.sumocfgGUI 的价值在于可视化排查。几个我觉得最实用的功能把车辆颜色按速度映射一眼就能看出哪段路在堵打开显示车辆路线选项能看到每辆车的完整轨迹用来验证路由是否合理调慢仿真速度看路口转向的行为细节。GUI 里还有一个隐藏技巧延迟时间设置。在界面上把 delay 设成 100ms 以上你可以肉眼看清每辆车的换道过程和路口排队形成过程。对于要写报告或者给非技术人员演示的场景这个功能救过我好几次。3.4 输出文件解读tripinfo、summary、edgeData仿真跑完输出目录里会有一堆 xml第一次看很容易懵。我挑三个最常用的说清楚它们各自能回答什么问题。summary.xml是每个时间步一条记录包含当前在路网上的车辆数、平均速度、总行驶里程、总等待时间等汇总指标。做宏观趋势分析、画仿真过程曲线用它。它粒度粗但数据量小适合看整体。tripinfo.xml是每完成一次旅程一条记录包含这辆车的行驶时间、等待时间、行驶距离、平均速度等。做单车级别的统计分析、算平均延误用它。这是做交通评价最常用的文件。tripinfo idv0 depart12.00 arrival245.30 duration233.30 routeLength1850.40 waitingTime38.20 timeLoss62.10 avgSpeed7.93/timeLoss这个字段特别值得关注它表示这辆车相对理想自由流状态损失了多少时间本质就是延误。做信号配时优化时这个值就是你要优化的目标函数。waitingTime只统计速度接近零的时间和timeLoss是两回事别混用。edgeData.xml是每条边一条记录统计这条边上的车流量、平均速度、密度、排队长度等。做路段级的通行能力分析、画热力图用它。这个文件默认不输出需要在配置里显式加edgeData定义或者用--edgedata-output参数。输出文件粒度主要用途summary.xml每时间步整体趋势、过程曲线tripinfo.xml每辆车延误统计、行程时间分析edgeData.xml每条边路段流量、密度、排队vehroute.xml每辆车路径核查、路由验证4. 进阶玩法TraCI 接口与信号配时4.1 TraCI 能做什么为什么值得学TraCI 全称 Traffic Control Interface是 SUMO 提供的一个在仿真运行过程中实时读写状态的接口。前面所有内容都是配置好一次性跑完TraCI 让你能在每一仿真步里做决策。它的典型用途有三类信号灯的动态控制、车辆路径的实时重规划、以及把 SUMO 当成一个环境来训练控制算法。为什么值得学因为静态配置能做的实验是有上限的。比如你想验证一个自适应信号控制策略用静态的tlLogic根本没法表达根据排队长度动态切换相位这个逻辑必须用 TraCI 在运行中读取检测器数据、计算、再下发控制指令。这套流程跑通之后SUMO 就从交通仿真软件变成了交通控制实验平台玩法完全不一样了。Python 是本领域使用最广泛的接入方式因为 SUMO 官方的traci库就是 Python 的而且配合 numpy、pandas 做数据分析特别顺。下面给一个最小可运行的模板。4.2 Python 控制实战一个最小可运行脚本import traci import sumolib # 启动仿真注意 sumoBinary 用无界面版本提速 sumo_binary sumolib.checkBinary(sumo) traci.start([sumo_binary, -c, config/sim.sumocfg]) step 0 while traci.simulation.getMinExpectedNumber() 0: traci.simulationStep() step 1 # 示例读取某条边的排队车辆数 queue traci.edge.getLastStepHaltingNumber(E0) # 示例读取所有车辆的当前速度 for veh_id in traci.vehicle.getIDList(): speed traci.vehicle.getSpeed(veh_id) # 示例仿真中期动态改一辆车的路线 if step 500: traci.vehicle.changeTarget(v0, E20) traci.close()这段代码里有几个点值得展开。sumolib.checkBinary是官方推荐的方式它会去环境变量里找 SUMO 的安装路径比硬编码路径可移植性好得多。getMinExpectedNumber()返回的是路网上还有多少辆车加上还有多少辆没发出来用这个当循环条件比固定跑 3600 步更合理仿真会自然结束。traci.vehicle.getIDList()每步都要调用的话在大规模场景下会有性能开销。如果你的场景车辆上万建议不要每步全量遍历改成只查询你关心的边或者区域或者每隔几步查一次。提示TraCI 脚本里所有的距离单位是米、速度单位是 m/s、时间单位是秒。到这一步千万别再想着用什么 km/h界面上显示的是 km/h接口里返回的全是 m/s单位搞混是初学者最常犯的错。4.3 tlLogic 信号配时方案的手写与调试信号配时是仿真里绕不开的东西静态的tlLogic定义长这样tlLogic idC0 typestatic programID0 offset0 phase duration42 stateGGrrGGrr/ phase duration3 stateyyrryyrr/ phase duration30 staterrGGrrGG/ phase duration3 staterryyrryy/ /tlLogicstate字符串的每一位对应一个车道或者一个连接G是绿灯、y是黄灯、r是红灯。这个字符串的长度必须和这个路口被信号控制的车道数严格匹配写长了写短了 SUMO 都会报错。新手最容易在这里翻车。怎么知道该写多长在 GUI 里点开这个路口的信号灯信息面板或者用netconvert --sumo-net-file配合检查连接数。更稳的办法是先用netconvert自动生成信号配时加--tls.default-type参数把生成结果当模板改比自己从零数位数靠谱得多。offset参数用于做干线协调。相邻信号灯设置不同的 offset让车流能一路绿灯通过这就是所谓的绿波带。这是做干线优化的核心手段调 offset 的过程用 TraCI 自动化做效率最高手动调基本不可能找到最优解。配时字段含义调试要点duration相位持续秒数总和为一个周期state各位灯色位数必须与连接数一致offset周期偏移用于干线协调绿波programID方案编号多方案切换时索引5. 踩坑实录常见报错与排查思路5.1 报错速查表下面这些是我实际踩过、并且反复见到别人踩的报错整理成速查表。报错信息关键词根本原因解决方向could not access file相对路径错位检查工作目录统一用相对项目根的路径Edge xxx is not known需求里的边 id 与路网不符比对 rou 文件和 net 文件的 idNo connection between edge路网缺转向连接用 netconvert 重建或手工补 connectionVehicle xxx has no valid route路由算不出路径检查连通性用 duarouter 单独验证Invalid position for vehicle出发车道越界用小车道数或改 departLaneThe traffic light xxx has no programtlLogic 未定义补 additional 文件里的信号定义could not access file出现频率最高。它的本质是 SUMO 不关心你的配置文件放在哪它只关心你启动命令时所在的目录。解决办法是要么每次都从项目根目录启动要么在脚本里用os.chdir切到项目根再做相对路径引用。我现在的习惯是脚本一开头就切目录从不依赖命令行位置。5.2 仿真结果不合理的几种典型症状程序不报错不代表结果对。下面几种症状我遇到太多次都是能跑但结果离谱的类型。第一种是所有车速度偏低。八成是maxSpeed和路段限速的双重限制在起作用或者sigma设太大导致跟驰行为过于保守。排查方法是单独放一辆车在空旷路段跑看它能不能跑到期望速度。第二种是大量车在路口莫名消失。这通常是time-to-teleport在起作用车堵太久被瞬移了。把它设成 -1 再看如果瞬间出现严重排队甚至死锁说明路口的通行能力设计有问题需要检查信号配时或者转向连接。第三种是通行量明显低于理论值。检查车辆插入是否成功有没有出现无法插入的警告。发车太密集时插入边被占满后面的车会插入失败被丢弃实际流量就上不去。解决办法是给出发边留足够车道、用departLanebest、或者降低发车频率。第四种是tripinfo 里 timeLoss 全都巨大。这往往不是仿真问题而是路网限速设置不合理或者你把begin时间设成负数导致车在外面排队了很久。先确认基础时空参数再看行为。5.3 性能与规模的取舍经验最后聊聊规模。SUMO 跑多大路网、多少车是可行的这个问题没有标准答案但我可以给一些经验值。普通笔记本跑几千辆车、几千条边的路网实时比大概在 1:5 到 1:20 之间也就是仿真 1 小时实际算几分钟。上到几万辆车就需要考虑关掉不必要的输出、加大 step-length、用 CH 路由这些优化手段了。输出文件的体积经常比计算时间更让人头疼。netstate-dump这种每步全量车辆状态的输出会爆炸式增长除非做轨迹研究否则别开。我一般只保留 tripinfo 和 summary需要路段数据时再加 edgeData按需输出。还有一个容易忽略的点仿真跑不完不一定是卡死。如果路网上有严重的瓶颈车辆持续累积getMinExpectedNumber()一直大于 0仿真永远不会自然结束。这种情况要么设个仿真时间上限强行结束要么去解决那个瓶颈。我个人的习惯是在脚本里加一个硬性的步数上限作为兜底防止批处理任务挂在某一组参数上整夜不结束。踩过几次坑之后我逐渐形成一个习惯任何一组新参数先跑 600 秒看趋势没问题再拉长到完整时长。600 秒足够暴露需求生成、路由、信号配时这三块的基础问题能省下大量全时长跑废的时间。规模这件事没有银弹真的是靠一轮轮试出来的手感。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →