简介这份资源是面向交通仿真初学者与交通工程方向学生的SUMO入门实例包围绕四个无信号灯十字路口与多车流场景展开帮助读者理解微观交通模拟的基本建模流程。压缩包共14个文件约72KB以XML配置为主涵盖路网、车流、路由等核心定义文件另含少量tr与文档类文件便于对照阅读与修改。实例基于SUMO 0.19.0版本通过分析ex4相关配置可学习如何用XML描述道路网络、车辆流与交通规则并观察车辆在无信号交叉口中的行驶行为。目前已有2178人学习下载说明该实例在入门阶段具有较高参考价值。读者可借此掌握SUMO场景搭建、参数调整与模拟结果分析的基本方法并在此基础上尝试引入信号控制、调整交通需求或优化规则为后续交通系统研究与智能交通应用打下基础。1. 从一份 SUMO 实例代码说起路网、车流与信号配时到底怎么跑通如果你刚接触交通仿真打开 SUMO 官网看到一堆 XML 和 Python 脚本大概率会愣住——文档写得很全但真要从零跑出一个能看的场景中间隔着一堆没写清楚的细节。我手里这份 SUMO 简单实例代码就是干这个用的它把路网生成、车流定义、信号配时、仿真执行这条链路完整串了一遍代码量不大但每个文件都对应仿真里一个绕不开的环节。适合两类人一是交通工程方向的学生需要快速搭一个能出图、能改参数的仿真环境二是做路径规划或信号控制的开发者想拿 SUMO 当验证平台但不想在环境配置上耗太久。这份代码不涉及复杂路网就是最基础的十字路口加几条车道但正因为简单反而容易看清每个参数到底在控制什么。2. 拆开这份实例路网、车流、信号三个文件怎么配合2.1 路网文件 net.xml 的节点与边到底在描述什么SUMO 的路网不是画出来的是算出来的。你写一个 nod.xml 定义节点坐标再写一个 edg.xml 定义哪些节点之间连边、几条车道、限速多少然后用 netconvert 一跑才生成最终的 net.xml。这份实例代码里节点就四个围成一个矩形路口边一共八条每个方向一进一出。看起来简单但这里有个容易翻车的地方节点坐标的坐标系是笛卡尔坐标单位是米不是经纬度。很多人第一次写的时候直接拿地图上的经纬度填进去结果路网要么缩成一个点要么拉成一条线。!-- nod.xml四个节点围成十字路口 -- nodes node idA x0 y0 typepriority/ node idB x200 y0 typepriority/ node idC x200 y200 typepriority/ node idD x0 y200 typepriority/ /nodes这段代码里typepriority 表示无信号灯控制的路口先让右方来车。如果你后面要加信号灯这里得改成 traffic_light否则信号配时文件加载不进去。x 和 y 就是平面坐标200 米差不多是一个标准城市交叉口的间距太小了车流会挤在一起太大了仿真看起来像在跑空旷高速。边文件 edg.xml 里要指定 from 和 to 节点还有 numLanes 和 speed。这份实例给了每条边两条车道限速 13.89 米每秒也就是 50 公里每小时城市道路常见值。这里有个参数叫 priority默认是 -1如果你不写netconvert 会自动算但算出来的优先级可能跟你预期不一样导致某些方向的车死活不让行。我一般会显式写上直行方向给高一点转弯方向给低一点。!-- edg.xml定义八条边每个方向一进一出 -- edges edge idAB fromA toB numLanes2 speed13.89 priority2/ edge idBA fromB toA numLanes2 speed13.89 priority2/ edge idBC fromB toC numLanes2 speed13.89 priority2/ edge idCB fromC toB numLanes2 speed13.89 priority2/ edge idCD fromC toD numLanes2 speed13.89 priority2/ edge idDC fromD toC numLanes2 speed13.89 priority2/ edge idDA fromD toA numLanes2 speed13.89 priority2/ edge idAD fromA toD numLanes2 speed13.89 priority2/ /edges写完这两个文件跑一条命令就能生成 net.xmlnetconvert --node-files nod.xml --edge-files edg.xml --output-file net.xml如果报错说节点重复或者边找不到先检查 id 有没有拼错再检查 from 和 to 是不是都在 nod.xml 里定义了。netconvert 的报错信息还算清楚但有时候它会自动修正一些错误反而让你以为写对了结果仿真跑起来车不动。所以生成之后最好用 sumo-gui 打开看一眼路网形状对不对车道数对不对一眼就能看出来。2.2 车流文件 rou.xml 里 departSpeed 和 departLane 的坑车流文件是很多人卡住的地方。这份实例代码里定义了两条车流一条从 A 到 C一条从 B 到 D都是直行发车频率用 period 控制每 2 秒发一辆。车型用的是默认的 passenger长度 5 米最大速度 13.89跟路网限速一致。!-- rou.xml两条直行车流每 2 秒发一辆 -- routes vType idcar accel2.6 decel4.5 length5 maxSpeed13.89/ route idrouteAC edgesAB BC/ route idrouteBD edgesBC CD/ flow idflowAC typecar routerouteAC begin0 end600 period2 departSpeedmax departLanebest/ flow idflowBD typecar routerouteBD begin0 end600 period2 departSpeedmax departLanebest/ /routes这里有两个参数值得单独说。departSpeedmax 表示车辆以路网允许的最大速度发车如果不写默认是 0车会从静止开始加速路口通行效率会明显下降。departLanebest 是让 SUMO 自己选一条最空的车道发车如果你指定了具体车道号比如 departLane0那所有车都挤在第一条车道上另一条车道空着仿真结果会很难看。我见过有人为了“控制变量”把所有车都固定在一条车道上结果排队长度比实际高出一大截这就是参数没选对。period 和 vehsPerHour 是两种发车方式period 是每隔多少秒发一辆vehsPerHour 是每小时发多少辆。这份实例用的是 period因为简单直观。但要注意period 是均匀发车实际交通流更接近泊松分布如果你要做通行能力分析最好换成 vehsPerHour 或者用 probability 参数做随机发车。2.3 信号配时文件 tls.xml 的相位与时长怎么对应信号配时是这份实例代码里最有价值的部分因为它把相位、时长、黄灯这些概念都落到了具体数字上。文件里定义了一个信号灯四个相位南北直行、南北左转、东西直行、东西左转。每个相位有 duration 和 statestate 是一串字符每个字符对应一条车道G 表示绿灯y 表示黄灯r 表示红灯。!-- tls.xml四相位信号配时 -- tlLogic idC typestatic programID0 offset0 phase duration30 stateGGrrGGrr/ phase duration3 stateyyrryyrr/ phase duration30 staterrGGrrGG/ phase duration3 staterryyrryy/ /tlLogicstate 字符串的长度必须和路口连接数一致写错了 SUMO 会直接报错。这份实例里每个方向两条车道四个方向一共八条连接所以 state 是 8 个字符。前两个字符对应南北直行接着两个对应南北左转然后东西直行最后东西左转。如果你加了右转专用道state 长度要相应增加不然相位对不上车会乱走。offset 参数控制相位偏移做绿波协调的时候会用到。这份实例里是 0表示从仿真开始就进入第一个相位。如果你要做干线协调把下游路口的 offset 设成上游路口行程时间就能形成绿波。但要注意offset 的单位是秒而且只在 typestatic 时有效动态信号控制不用这个参数。3. 跑起来才算数仿真执行、输出配置与结果查看3.1 sumocfg 配置文件里必须写对的三个路径SUMO 跑仿真需要一个配置文件通常叫 sumocfg里面指定路网文件、车流文件、信号配时文件和输出文件。这份实例代码里sumocfg 写得很简洁但每个路径都是相对路径所以你必须从 sumocfg 所在目录执行命令否则会报找不到文件。!-- sumocfg仿真配置文件 -- configuration input net-file valuenet.xml/ route-files valuerou.xml/ additional-files valuetls.xml/ /input time begin value0/ end value600/ /time output tripinfo-output valuetripinfo.xml/ summary-output valuesummary.xml/ /output /configurationinput 里的三个文件路径最容易出问题。net-file 是 netconvert 生成的route-files 是车流文件additional-files 是信号配时文件。注意 additional-files 可以放多个文件用逗号隔开但信号配时文件必须放在这里不能放到 route-files 里否则 SUMO 会忽略它。我见过有人把 tls.xml 写到 route-files 里结果信号灯一直不工作车流堵成一锅粥排查了半天才发现是文件放错了位置。time 里的 begin 和 end 控制仿真起止时间单位是秒。这份实例跑了 600 秒也就是 10 分钟。如果你要看稳态交通流建议至少跑 3600 秒前 600 秒当预热后面的数据才稳定。output 里的 tripinfo-output 记录每辆车的行程时间、等待时间、停车次数summary-output 记录每个时间步的车辆数、平均速度、排队长度。这两个文件是后面做分析的基础不写的话仿真跑完什么数据都没有。3.2 命令行执行与 sumo-gui 可视化排查配置文件写好后用 sumo 命令跑无界面仿真用 sumo-gui 跑可视化仿真。无界面适合批量跑数据可视化适合排查问题。# 无界面仿真适合批量跑数据 sumo -c sumocfg.sumocfg # 可视化仿真适合排查路网和车流问题 sumo-gui -c sumocfg.sumocfg跑 sumo-gui 的时候如果看到车在路口突然消失或者车叠在一起多半是路网连接没写对。netconvert 默认会生成连接但有时候转弯连接缺失车走到路口发现没有下一段路就直接被移除了。这时候要在 netconvert 命令里加 --no-turnarounds 或者手动写 con.xml 补连接。另一个常见问题是车流发车时找不到车道报错说 departLane 无效这时候把 departLane 改成 free 或者 random 试试别死磕 best。仿真跑完后tripinfo.xml 里每辆车都有 id、depart、arrival、duration、waitingTime、waitingCount 这些字段。如果你要看平均行程时间用 Python 读一下就行import xml.etree.ElementTree as ET tree ET.parse(tripinfo.xml) root tree.getroot() total_duration 0 count 0 for trip in root.findall(tripinfo): total_duration float(trip.get(duration)) count 1 print(f平均行程时间: {total_duration / count:.2f} 秒)这段代码把所有车的 duration 加起来除以车辆数得到平均行程时间。如果你要分方向统计就按 route 或者 departLane 分组逻辑一样。注意 tripinfo 里的 duration 是秒不是仿真步数别搞混了。3.3 输出文件里哪些指标值得看哪些是噪音tripinfo.xml 和 summary.xml 里字段很多但真正有用的就那么几个。tripinfo 里我一般只看 duration、waitingTime、waitingCount 和 routeLength。duration 是总行程时间waitingTime 是等待时间waitingCount 是停车次数routeLength 是行驶距离。这四个指标基本能反映路网通行效率。summary.xml 里看 meanSpeed、meanWaitingTime、halting 这几个halting 是停车车辆数排队长度直接看这个就行。有些字段看起来有用其实很坑比如 timeLoss它把等待时间和减速损失都算进去了数值偏大不适合直接做对比。还有 CO2 排放和燃油消耗SUMO 默认的排放模型比较粗糙除非你专门标定了排放参数否则这些数据只能看趋势不能当绝对值用。我一般会把这些字段忽略掉专注在行程时间和等待时间上。4. 避坑与排查这份实例代码最容易翻车的五个地方4.1 路网生成报错“节点未定义”现象跑 netconvert 的时候报错说某个节点找不到或者边文件里的 from/to 节点不存在。原因通常是 nod.xml 里节点 id 拼写错误或者 edg.xml 里引用了不存在的节点。还有一种情况是 nod.xml 和 edg.xml 的编码不一致一个用 UTF-8 一个用 GBK中文注释会导致解析失败。解决方法是先检查 id 是否完全一致包括大小写再把两个文件的编码统一成 UTF-8注释尽量用英文。4.2 车流发车后原地不动现象仿真跑起来车流发了车但车停在原地不动或者过很久才起步。原因多半是 departSpeed 没设成 max默认是 0车要从静止加速。另一个可能是 departLane 指定了错误的车道号车发到了不存在或者被占用的车道上。解决方法是把 departSpeed 改成 maxdepartLane 改成 best 或 free让 SUMO 自己选车道。如果还不行检查路网限速是不是设成了 0speed0 会导致车永远动不了。4.3 信号灯不切换相位现象仿真跑起来信号灯一直停在第一个相位后面的相位不执行。原因通常是 tls.xml 没有放到 additional-files 里或者 tlLogic 的 id 跟路网里的节点 id 不一致。还有一种可能是 state 字符串长度跟连接数不匹配SUMO 解析失败后直接忽略了这个信号灯。解决方法是确认 tls.xml 在 additional-files 里tlLogic 的 id 跟 nod.xml 里 typetraffic_light 的节点 id 一致state 长度用 netconvert 生成的 net.xml 里的连接数核对一遍。4.4 仿真跑完 tripinfo.xml 是空的现象仿真正常结束但 tripinfo.xml 里没有数据或者只有几辆车。原因可能是仿真时间太短车还没跑完就被截断了或者 output 里没写 tripinfo-output。还有一种情况是车流发车时间在仿真结束之后begin 设成了 600end 也是 600车根本没发出来。解决方法是把 end 设大一点至少让所有车都能到达终点同时确认 tripinfo-output 路径正确文件有写入权限。4.5 可视化界面卡顿或闪退现象sumo-gui 打开后界面卡住或者跑几秒就闪退。原因通常是路网太大或者车辆太多显卡扛不住。这份实例代码路网很小一般不会卡但如果你改了参数把车辆数调到几千sumo-gui 就会很吃力。解决方法是先用 sumo 无界面跑一遍确认逻辑没问题再用 sumo-gui 看局部。如果非要看大规模仿真把 GUI 的延迟调高或者用 --start 参数从中间某个时间点开始看。5. 从这份实例往外延伸参数扫描与批量仿真的具体做法这份实例代码跑通之后最有价值的用法不是盯着一个场景看而是拿它做参数扫描。比如你想知道信号周期从 60 秒变到 120 秒行程时间怎么变手动改 tls.xml 再跑一遍太慢写个脚本批量跑才是正路。我一般用 Python 的 subprocess 调 sumo每次改一个参数跑完读 tripinfo.xml把结果存到 CSV 里。import subprocess import xml.etree.ElementTree as ET import csv results [] for cycle in [60, 90, 120, 150]: # 修改 tls.xml 里的相位时长这里简化处理实际用模板替换 with open(tls_template.xml, r) as f: content f.read() content content.replace(DURATION, str(cycle // 4)) with open(tls.xml, w) as f: f.write(content) # 跑仿真 subprocess.run([sumo, -c, sumocfg.sumocfg], checkTrue) # 读结果 tree ET.parse(tripinfo.xml) root tree.getroot() total_duration sum(float(t.get(duration)) for t in root.findall(tripinfo)) count len(root.findall(tripinfo)) avg_duration total_duration / count if count 0 else 0 results.append({cycle: cycle, avg_duration: avg_duration}) # 写 CSV with open(results.csv, w, newline) as f: writer csv.DictWriter(f, fieldnames[cycle, avg_duration]) writer.writeheader() writer.writerows(results)这段代码的逻辑很简单循环不同的信号周期改 tls.xml跑仿真读平均行程时间存 CSV。注意 tls_template.xml 里用 DURATION 占位实际替换成周期除以相位数。这里假设四个相位均分周期实际配时可能不是均分你可以按比例改。跑完之后用 pandas 画个图就能看出周期多长时行程时间最短。还有一个进阶用法是加检测器。SUMO 支持 e1 检测器感应线圈和 e2 检测器区域检测器可以输出每个车道的流量、占有率、平均速度。这份实例代码没带检测器但你可以自己在 additional-files 里加一个 det.xml定义检测器位置和输出文件。检测器数据比 tripinfo 更细适合做信号控制算法验证。我一般会在路口停车线前 10 米放一个 e1 检测器采样周期设成 60 秒输出流量和占有率用来评估不同配时方案的效果。最后说一个我踩过的坑批量仿真的时候如果 sumo 报错但 subprocess 没捕获脚本会继续跑结果后面全是空数据。所以 subprocess.run 一定要加 checkTrue报错就停别让错误累积。还有每次跑完记得清空 tripinfo.xml不然 SUMO 会追加写入数据混在一起根本没法分析。从那以后我每次批量跑仿真前都强制删一遍输出文件这个习惯帮我省了很多排查时间。希望帮到你。本文还有配套的精品资源点击获取