从零开始学习 SUMO:从环境配置到 Python 联合仿真
文章目录
写在前面
刚开始接触 SUMO 时,我最大的感受是:官方文档内容很多,但对于刚入门的人来说,很难马上判断哪些内容该先看、哪些内容可以后面用到再查。网上能找到的中文视频资源也不算多,而且真正能契合具体项目需求的教程更少。
简单来说,我的路线是:
- 先安装 SUMO,并配置好环境变量;
- 先理解 SUMO 仿真需要哪些核心文件;
- 根据项目需求,用 Python 自动生成 XML 文件;
- 用 SUMO 官方文档查具体属性;
- 学习 TraCI,用 Python 控制仿真并提取数据;
- 遇到具体问题时,再去公众号、CSDN 专栏和官方文档里搜索解决方案。
下面按这个顺序展开。
一、安装 SUMO:先把环境跑通
SUMO 的安装本身不算难,真正容易卡住的是环境变量和 Python 调用。尤其是后面要用 TraCI 与 Python 联动时,如果 SUMO_HOME 没有设置好,就很容易出现找不到 sumolib、找不到 traci、或者 Python 能运行但启动不了 SUMO 的问题。
1. 下载 SUMO
如果是 Windows 系统,一般选择官方页面里的 64-bit installer 即可。官方下载页面也提供 zip 版本和 extra 版本:
| 版本 | 适合情况 |
|---|---|
| 64-bit installer | 普通安装,适合大多数初学者 |
| 64-bit zip | 便携版本,适合没有管理员权限或不想安装到系统目录的情况 |
| 64-bit installer with all extras | 需要 shapefile、GeoTIFF、3D GUI、视频生成等额外功能时使用 |
| 64-bit zip with all extras | 便携版 extra 功能包 |
2. 安装后检查可执行程序
安装完成后,在安装目录里一般可以看到 bin 文件夹,其中常用程序包括:
| 程序 | 作用 |
|---|---|
sumo.exe |
命令行版本仿真程序 |
sumo-gui.exe |
图形界面版本,适合观察车辆运行 |
netedit.exe |
路网编辑器 |
netconvert.exe |
将 OSM、节点边文件等转换为 SUMO 路网 |
duarouter.exe |
根据需求文件生成路线 |
3. 配置环境变量
如果后面要用 Python 调用 SUMO,建议配置两个环境变量相关内容。
第一个是 SUMO_HOME,它指向 SUMO 的安装目录。例如:
SUMO_HOME = C:\Program Files (x86)\Eclipse\Sumo
这里要按自己的实际安装路径来设置。如果你安装到了别的目录,就写自己的路径。
第二个是把 SUMO 的 bin 目录加入系统 Path,例如:
%SUMO_HOME%\bin
配置完成后,可以在命令行中测试:
sumo-gui
netconvert --version
如果能打开 SUMO-GUI,并且 netconvert --version 能输出版本信息,说明环境变量基本配置成功。
4. Python 中导入 TraCI
使用 Python 调用 SUMO 时,常见写法是先检查 SUMO_HOME,再把 SUMO_HOME/tools 加入 Python 路径:
import os
import sys
if "SUMO_HOME" in os.environ:
tools = os.path.join(os.environ["SUMO_HOME"], "tools")
sys.path.append(tools)
else:
sys.exit("please declare environment variable 'SUMO_HOME'")
import traci
from sumolib import checkBinary
也可以通过 pip 安装 Python 包:
pip install traci sumolib
二、先搞清楚 SUMO 项目需要哪些文件
不建议一上来就研究所有配置项。可以先理解一个最小仿真项目大概由哪些文件组成。
我自己最开始主要关注这几类文件:
| 文件类型 | 作用 |
|---|---|
.nod.xml |
定义节点,例如路口、端点 |
.edg.xml |
定义道路边,包括起点、终点、车道数、速度等 |
.con.xml |
定义道路之间的连接关系 |
.net.xml |
由 netconvert 生成的 SUMO 路网文件 |
.rou.xml |
定义车辆类型、路线、车流等 |
.add.xml |
定义额外配置,例如检测器、信号灯、输出等 |
.sumocfg |
总配置文件,告诉 SUMO 加载哪些路网、路线和附加文件 |
简单理解就是:
节点 + 边 + 连接关系 -> 路网文件
车辆类型 + 车流 -> 交通需求
sumocfg -> 把路网、交通需求、输出配置组织起来
TraCI -> 用 Python 在仿真运行过程中读写数据
这个阶段只要知道每个文件负责什么即可。
三、路网构建:先理解几种常见方式
SUMO 的路网构建大概有几种方式:
- 使用
netedit手动画路网; - 从 OpenStreetMap 等真实地图导入;
- 用
.nod.xml、.edg.xml、.con.xml等文件描述路网,再通过netconvert生成.net.xml; - 用 Python 自动生成上述 XML 文件。
如果只是入门,netedit 很适合用来理解路网结构,因为它能直接看到节点、边、车道和连接关系。但我的项目是面向全 Python 开发的,所以我没有重点学习 netedit 的操作,只是大概了解了它的功能。
对于需要批量生成场景、修改参数、反复跑实验的项目来说,我更推荐尽早理解 XML 文件的生成逻辑。因为一旦场景数量多起来,手动画路网会比较麻烦,而 Python 自动生成文件更方便复现实验。
如果要导入 OSM 地图做大型路网,可以参考我之前写过的一篇 CSDN 教程:SUMO 大范围高速公路路网构建实践:从 OSM 数据获取到 net.xml 转换的踩坑记录
这篇文章就不展开 OSM 大型路网导入了,下面主要介绍适合入门理解的 XML 文件生成方式。
四、.nod.xml:定义节点
.nod.xml 用来定义路网中的节点。节点可以理解为路口、道路起终点、交通信号控制点等。
示例:
<nodes>
<node id="nd0" x="0" y="0" type="traffic_light"/>
<node id="nd1" x="-1000" y="0" type="priority"/>
<node id="nd2" x="1000" y="0" type="priority"/>
</nodes>
我当时的项目中,中间节点 nd0 设置为 traffic_light,表示这个节点由信号灯控制;两端节点设置为 priority,表示普通优先规则节点。
常用可修改属性:
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
id |
nd0 |
节点编号 | 需要在 .edg.xml 的 from、to 中引用,必须保持唯一 |
x |
0 |
节点 x 坐标,单位通常为 m | 决定节点在平面路网中的位置 |
y |
0 |
节点 y 坐标,单位通常为 m | 决定节点在平面路网中的位置 |
z |
0 |
节点高程,可选 | 三维或高程相关场景才会用到 |
type |
traffic_light |
节点类型 | 会影响路权、信号灯、交叉口控制方式 |
type 常见取值包括:
type |
说明 |
|---|---|
priority |
优先规则路口 |
traffic_light |
信号灯控制路口 |
right_before_left |
右方优先 |
unregulated |
无控制 |
allway_stop |
全向停车 |
traffic_light_right_on_red |
支持红灯右转的信号灯节点 |
五、.edg.xml:定义道路边
.edg.xml 用来定义道路。SUMO 中的 edge 是有方向的,从 from 节点指向 to 节点。
示例:
<edges>
<edge id="3lanes" from="nd1" to="nd0" numLanes="3" speed="40"/>
<edge id="2lanes" from="nd0" to="nd2" numLanes="3" speed="40"/>
</edges>
这里虽然第二条 edge 的 id 写成了 2lanes,但示例中 numLanes 仍然是 3。edge 的 id 只是编号,不会自动决定车道数,真正决定车道数的是 numLanes。
常用可修改属性:
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
id |
3lanes |
道路编号 | 后续路线、连接关系、TraCI 查询都可能用到 |
from |
nd1 |
起点节点 id | 决定道路方向 |
to |
nd0 |
终点节点 id | 决定道路方向 |
numLanes |
3 |
车道数 | 影响车辆通行能力、车道编号、连接关系 |
speed |
40 |
最大速度,单位为 m/s | 影响车辆速度上限 |
priority |
3 |
道路优先级 | 影响路权判断 |
length |
1000 |
道路长度 | 不写时可由几何形状计算 |
shape |
0,0 100,0 |
道路线形点 | 用于描述弯道或复杂几何 |
allow |
passenger bus |
允许通行的车辆类别 | 可限制某些车道/道路通行权限 |
disallow |
truck |
禁止通行的车辆类别 | 可用于禁行货车等场景 |
六、.con.xml:定义连接关系
.con.xml 用来定义道路之间、车道之间如何连接。比如某条车道能不能直行、左转、右转,或者从一条 edge 的哪个 lane 进入下一条 edge 的哪个 lane。
示例:
<connections>
<connection from="3lanes" to="2lanes" fromLane="0" toLane="0" dir="s" state="o"/>
<connection from="3lanes" to="2lanes" fromLane="1" toLane="1" dir="s" state="o"/>
<connection from="3lanes" to="2lanes" fromLane="2" toLane="2" dir="s" state="o"/>
</connections>
这里表示 3lanes 的 0、1、2 号车道分别连接到 2lanes 的 0、1、2 号车道。这里要注意的是,SUMO 中编号为 0 的车道是从车行方向看最右侧的车道,在连接的两条 edge 车道不一致的时候更要检查好哦车道连接。
常用可修改属性:
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
from |
3lanes |
来源 edge | 指定车辆从哪条道路驶出 |
to |
2lanes |
目标 edge | 指定车辆驶入哪条道路 |
fromLane |
0 |
来源车道编号 | 控制某一具体车道的连接 |
toLane |
0 |
目标车道编号 | 控制进入目标道路的哪条车道 |
dir |
s |
转向方向 | 可表示直行、左转、右转等方向信息 |
state |
o |
连接状态/路权状态 | 会影响路权和信号控制相关表现 |
tl |
nd0 |
交通信号灯 id,可选 | 将连接纳入某个信号灯控制 |
linkIndex |
0 |
信号灯控制的连接索引,可选 | 决定 tlLogic 中 state 字符串对应哪条连接 |
如果后续要做信号控制,tl 和 linkIndex 很重要。因为 SUMO 的信号灯状态字符串不是直接控制“第几条车道”,而是控制“信号控制下的第几个 link”。这个 link 通常对应某个 lane-to-lane connection。
七、.net.xml:由 netconvert 生成的路网文件
.net.xml 一般不是我们手写的,而是通过 netconvert 由 .nod.xml、.edg.xml、.con.xml 等文件生成。
命令示例:
netconvert --node-files=try.nod.xml --edge-files=try.edg.xml --connection-files=try.con.xml --output-file=try.net.xml
Python 中也可以直接调用:
import os
os.system(
"netconvert --node-files=try.nod.xml "
"--edge-files=try.edg.xml "
"--connection-files=try.con.xml "
"--output-file=try.net.xml"
)
这里需要注意:
| 注意点 | 说明 |
|---|---|
不建议手动改 .net.xml |
它是生成结果,手改容易出错 |
修改路网应优先改 .nod.xml、.edg.xml、.con.xml |
再重新运行 netconvert |
OSM 路网也会通过 netconvert 转成 .net.xml |
大型真实路网导入时尤其常见 |
.net.xml 中会包含 lane、junction、connection、traffic light link 等信息 |
后续 TraCI 查询和信号控制都依赖它 |
八、.rou.xml:定义车辆、路线和车流
路网有了以后,还需要定义车辆怎么进入路网。这个部分主要写在 .rou.xml 中。
一个常见思路是:先定义车辆类型,再定义车流。
示例:
<routes>
<vType id="car" vClass="passenger" accel="3.0" decel="4.5" sigma="0.5" length="5" minGap="1.5" maxSpeed="40"/>
<vType id="truck" vClass="truck" accel="2.7" decel="3.5" sigma="0.5" length="10" minGap="1.5" maxSpeed="33"/>
<flow id="car_0" from="3lanes" to="2lanes" color="255,255,0" begin="0" end="3600" vehsPerHour="5000" type="car" departLane="0"/>
<flow id="truck_0" from="3lanes" to="2lanes" color="0,255,0" begin="0" end="3600" vehsPerHour="2500" type="truck" departLane="0"/>
</routes>
1. vType 常用属性
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
id |
car |
车辆类型编号 | 后续 vehicle 或 flow 通过 type 引用 |
vClass |
passenger |
车辆类别 | 会影响道路/车道权限,如 passenger、truck、bus |
accel |
3.0 |
最大加速度 | 影响车辆加速能力 |
decel |
4.5 |
常规最大减速度 | 影响车辆减速行为 |
sigma |
0.5 |
驾驶随机性/不完美程度 | 数值越大,驾驶行为随机性越强 |
length |
5 |
车辆长度 | 影响排队、跟驰、占用空间 |
minGap |
1.5 |
最小车头间距 | 影响跟驰距离 |
maxSpeed |
40 |
最大速度 | 影响车辆速度上限 |
tau |
1.0 |
期望车头时距 | 跟驰模型中常用 |
carFollowModel |
IDM |
跟驰模型 | 用于更换车辆动力学/跟驰规则 |
2. flow 常用属性
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
id |
car_0 |
车流编号 | 生成车辆 id 时会作为前缀 |
type |
car |
车辆类型 | 引用前面定义的 vType |
from |
3lanes |
起点 edge | 车辆从哪条道路出发 |
to |
2lanes |
终点 edge | 车辆目标道路 |
begin |
0 |
车流开始时间 | 控制车辆开始进入路网的时刻 |
end |
3600 |
车流结束时间 | 控制车辆停止生成的时刻 |
vehsPerHour |
5000 |
每小时车辆数 | 控制交通流量 |
number |
100 |
总车辆数 | 与 vehsPerHour、period 等通常选一种使用 |
period |
2 |
发车间隔 | 每隔多少秒生成一辆车 |
probability |
0.2 |
每秒生成概率 | 适合随机流量 |
departLane |
0 |
出发车道 | 可指定车道,也可用 free、best 等 |
departSpeed |
max |
出发速度 | 控制进入路网时的初速度 |
color |
255,255,0 |
车辆颜色 | 方便在 GUI 中区分车辆类型 |
3. 车辆参数依据具体的情况而定
这一部分不建议只照抄模板。因为车辆参数会直接影响仿真结果,具体怎么设置,最好结合项目需求再去查 SUMO 官方文档。
比如 vehsPerHour=5000 在某些简单路网中可能会造成拥堵甚至车辆无法插入;maxSpeed=40 的单位是 m/s,不是 km/h;departLane 如果指定不合理,也可能导致车辆无法正常进入或产生不符合预期的分布。
九、.sumocfg:仿真的总入口
.sumocfg 可以理解为 SUMO 的入口文件。它告诉 SUMO:路网文件在哪里,路线文件在哪里,附加文件在哪里,仿真从什么时候开始,到什么时候结束,以及要输出哪些结果。
示例:
<configuration>
<input>
<net-file value="try.net.xml"/>
<route-files value="try.rou.xml"/>
<additional-files value="try.add.xml"/>
</input>
<time>
<begin value="0"/>
<end value="3600"/>
</time>
<processing>
<time-to-teleport value="-1"/>
</processing>
<output>
<write-license value="true"/>
<tripinfo-output value="tripinfos.xml"/>
<fcd-output value="fcd.xml"/>
</output>
</configuration>
常用可修改配置:
| 标签/属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
net-file |
try.net.xml |
路网文件 | 决定仿真使用哪套路网 |
route-files |
try.rou.xml |
路线/车流文件 | 决定车辆从哪里来、怎么走 |
additional-files |
try.add.xml |
附加文件 | 加载检测器、信号灯、输出等配置 |
begin |
0 |
仿真开始时间 | 控制起始仿真时刻 |
end |
3600 |
仿真结束时间 | 控制仿真持续时间 |
time-to-teleport |
-1 |
拥堵车辆传送设置 | -1 表示关闭 teleport,车辆不会因长时间堵塞被自动传送 |
tripinfo-output |
tripinfos.xml |
行程信息输出 | 输出每辆车的出发、到达、行程时间等 |
fcd-output |
fcd.xml |
浮动车数据输出 | 输出每个时间步车辆位置、速度等 |
1. .sumocfg 中常见输出文件
SUMO 可以输出的数据非常多,常用的有:
| 输出配置 | 输出内容 | 适合用途 | 官方文档 |
|---|---|---|---|
tripinfo-output |
每辆车的行程信息,如出发、到达、行程时间、延误等 | 统计车辆运行结果 | https://sumo.dlr.de/docs/Simulation/Output/TripInfo.html |
fcd-output |
每个时间步车辆的位置、速度、角度等浮动车数据 | 轨迹数据、车辆状态分析 | https://sumo.dlr.de/docs/Simulation/Output/FCDOutput.html |
summary-output |
每个时间步的仿真总体状态,如车辆数等 | 观察整体交通状态变化 | https://sumo.dlr.de/docs/Simulation/Output/Summary.html |
queue-output |
排队相关信息 | 分析排队长度、拥堵状态 | https://sumo.dlr.de/docs/Simulation/Output/QueueOutput.html |
lanechange-output |
车辆换道事件及原因 | 换道行为分析 | https://sumo.dlr.de/docs/Simulation/Output/Lanechange.html |
emission-output |
排放相关数据 | 能耗、排放研究 | https://sumo.dlr.de/docs/Simulation/Output/EmissionOutput.html |
collision-output |
碰撞信息 | 安全分析、异常排查 | https://sumo.dlr.de/docs/Simulation/Output/Collisions.html |
2. 输出应该按研究目标选择
如果只是想拿到车辆运行时间,可以先用 tripinfo-output。
如果需要每一帧车辆位置、速度、加速度等细粒度数据,可以使用 fcd-output 或 TraCI 自己写 CSV。
如果需要分析道路或车道层面的平均速度、密度、流量,可以在 .add.xml 中配置 edgeData 或 laneData。
十、.add.xml:附加配置,尤其是信号控制
.add.xml 中可以放很多附加配置,例如检测器、公交站、可变限速、边/车道统计输出、信号灯方案等。官方文档里也说明,additional file 可以加载 traffic light programs、detector definitions、variable speed signs、bus stops,以及一些输出对象。因为当时的项目主要关注车辆运行数据的生成和提取,并没有深入涉及检测器相关内容,所以本文不展开介绍检测器的配置。
我当时的项目没有重点涉及信号控制,所以代码中只是简单设置了一下固定相位信号灯和边数据输出。也就是说,下面这个例子只是一个入门模板,不代表完整的信号控制项目写法。
示例:
<additional>
<edgeData id="try" freq="60" file="edgeDATA.xml" begin="0" end="3600" excludeEmpty="true"/>
<tlLogic id="nd0" type="static" programID="1" offset="0">
<phase duration="30" state="GGG"/>
<phase duration="5" state="rrr"/>
</tlLogic>
<timedEvent type="SaveTLSStates" source="nd0" dest="States.xml" saveDetectors="false" saveConditions="true"/>
</additional>
官方文档 format_of_additional_files
1. edgeData:道路层面统计输出
edgeData 用于输出 edge 层面的聚合交通指标。
常用属性:
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
id |
try |
统计对象编号 | 用于区分多个输出定义 |
file |
edgeDATA.xml |
输出文件名 | 决定统计结果保存位置 |
freq / period |
60 |
聚合周期,单位 s | 每 60 秒输出一次统计结果 |
begin |
0 |
开始统计时间 | 控制统计起点 |
end |
3600 |
结束统计时间 | 控制统计终点 |
excludeEmpty |
true |
是否排除空道路 | 减少无车辆道路的输出 |
edges |
3lanes 2lanes |
指定统计道路,可选 | 只统计部分 edge |
官方文档 Lane-_or_Edge-based_Traffic_Measures
2. tlLogic:信号灯程序
tlLogic 是做信号控制时非常重要的部分。它定义某个信号灯 id 对应的控制程序,包括信号类型、相位序列、相位持续时间、每个信号 link 的灯色状态等。
<tlLogic id="nd0" type="static" programID="1" offset="0">
<phase duration="30" state="GGG"/>
<phase duration="5" state="rrr"/>
</tlLogic>
tlLogic 常用属性:
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
id |
nd0 |
信号灯 id | 通常与受控 junction id 一致,必须能在 .net.xml 中找到 |
type |
static |
信号控制类型 | static 为固定配时,actuated 可感应控制,delay_based 可基于延误 |
programID |
1 |
信号控制程序编号 | 同一信号灯可以有多个 program |
offset |
0 |
周期偏移 | 用于协调控制或设置初始相位偏移 |
phase 常用属性:
| 属性 | 示例 | 含义 | 修改影响 |
|---|---|---|---|
duration |
30 |
当前相位持续时间 | 控制该相位保持多久 |
state |
GGG |
当前相位每个信号 link 的灯色 | 控制各连接方向的通行状态 |
minDur |
10 |
最小绿灯时间 | 感应控制中常用 |
maxDur |
60 |
最大绿灯时间 | 感应控制中常用 |
name |
main_green |
相位名称 | 方便对应交通工程中的相位命名 |
next |
1 |
下一相位索引 | 可改变相位跳转顺序 |
3. state 字符串怎么理解
state 是信号控制中最容易让人困惑的地方。比如:
<phase duration="30" state="GGG"/>
<phase duration="5" state="rrr"/>
这里的 GGG 并不是简单表示“三条车道都是绿灯”,更准确地说,它表示该信号灯控制的 3 个 signal link 都是绿灯。每一个字符对应一个受信号控制的 link,而 link 往往来自 .net.xml 里的 connection 信息。
常见灯色字符含义:
| 字符 | 含义 |
|---|---|
r |
红灯 |
y |
黄灯 |
g |
绿灯,但需要减速/让行 |
G |
绿灯,优先通行 |
o |
关闭但仍有优先规则 |
O |
关闭且优先通行 |
所以做信号控制时,不能只看车道数量,还要看每个 link 的顺序。可以在 SUMO-GUI 的可视化设置中显示 link tls index,也可以查看 .net.xml 中 connection 的 linkIndex。
4. timedEvent:保存信号状态
我的代码里还写了:
<timedEvent type="SaveTLSStates" source="nd0" dest="States.xml" saveDetectors="false" saveConditions="true"/>
它的作用是保存信号灯状态,用于后续查看或评价信号控制过程。
常用属性:
| 属性 | 示例 | 含义 |
|---|---|---|
type |
SaveTLSStates |
保存信号灯状态 |
source |
nd0 |
要保存的信号灯 id |
dest |
States.xml |
输出文件 |
saveDetectors |
false |
是否保存检测器相关信息 |
saveConditions |
true |
是否保存条件信息 |
十一、用 Python 自动生成 XML 文件
我当时的代码写法其实比较基础,主要是通过 print() 一行一行写 XML 文件。现在回头看,这种方式不算优雅,但对于入门来说反而很直观:你能看到 Python 写出的每一行,对应 SUMO 文件里的哪一部分。
例如可以先定义一些公共参数:
PREFIX = "try"
SIMULATION_TIME = 3600
然后生成路网相关文件:
from Constants import PREFIX
import os
nodes = open(f"{PREFIX}.nod.xml", "w", encoding="utf-8")
edges = open(f"{PREFIX}.edg.xml", "w", encoding="utf-8")
connections = open(f"{PREFIX}.con.xml", "w", encoding="utf-8")
print("<nodes>", file=nodes)
print(' <node id="nd0" x="0" y="0" type="traffic_light"/>', file=nodes)
print(' <node id="nd1" x="-1000" y="0" type="priority"/>', file=nodes)
print(' <node id="nd2" x="1000" y="0" type="priority"/>', file=nodes)
print("</nodes>", file=nodes)
print("<edges>", file=edges)
print(' <edge id="3lanes" from="nd1" to="nd0" numLanes="3" speed="40"/>', file=edges)
print(' <edge id="2lanes" from="nd0" to="nd2" numLanes="3" speed="40"/>', file=edges)
print("</edges>", file=edges)
print("<connections>", file=connections)
for lane in range(3):
print(
f' <connection from="3lanes" to="2lanes" fromLane="{lane}" toLane="{lane}" dir="s" state="o"/>',
file=connections
)
print("</connections>", file=connections)
nodes.close()
edges.close()
connections.close()
os.system(
"netconvert --node-files=try.nod.xml "
"--edge-files=try.edg.xml "
"--connection-files=try.con.xml "
"--output-file=try.net.xml"
)
入门时可以先用这种方式写。后面如果代码变复杂,可以考虑用 xml.etree.ElementTree 这类库来生成 XML,代码会更规范,也不容易出现标签闭合错误。
十二、TraCI:从“能跑仿真”到“能控制仿真”
如果只是打开 SUMO-GUI 看车辆跑起来,很多基础教程已经够用了。但如果项目需要在仿真过程中读取车辆状态、控制车辆行为、修改信号灯、导出数据,就需要学习 TraCI。
TraCI 可以理解为 Python 和 SUMO 之间的接口。通过它,我们可以一步一步推进仿真,并在每个时间步读取或修改仿真对象。
一个基本结构如下:
import os
import sys
from sumolib import checkBinary
import traci
sumoBinary = checkBinary("sumo-gui")
traci.start([sumoBinary, "-c", "try.sumocfg"])
for step in range(3600):
traci.simulationStep()
simulation_time = traci.simulation.getTime()
vehicle_count = traci.vehicle.getIDCount()
vehicle_ids = traci.vehicle.getIDList()
for veh_id in vehicle_ids:
speed = traci.vehicle.getSpeed(veh_id)
position = traci.vehicle.getPosition(veh_id)
acceleration = traci.vehicle.getAcceleration(veh_id)
traci.close()
1. TraCI 常用读取内容
| 模块 | 常用方法 | 作用 |
|---|---|---|
traci.simulation |
getTime() |
获取当前仿真时间 |
traci.vehicle |
getIDList() |
获取当前路网中车辆 id |
traci.vehicle |
getSpeed(vehID) |
获取车辆速度 |
traci.vehicle |
getPosition(vehID) |
获取车辆坐标 |
traci.vehicle |
getAcceleration(vehID) |
获取车辆加速度 |
traci.vehicle |
getLaneID(vehID) |
获取车辆所在车道 |
traci.vehicle |
getRoadID(vehID) |
获取车辆所在 edge |
traci.vehicle |
getLeader(vehID) |
获取前车信息 |
traci.lane |
getLastStepVehicleNumber(laneID) |
获取车道上一步车辆数 |
traci.edge |
getLastStepMeanSpeed(edgeID) |
获取 edge 平均速度 |
traci.trafficlight |
getPhase(tlsID) |
获取当前信号相位 |
traci.trafficlight |
getRedYellowGreenState(tlsID) |
获取当前灯色字符串 |
2. TraCI 常用可修改属性
TraCI 不只可以读取数据,也可以在仿真运行过程中动态修改车辆或信号灯状态。
车辆相关:
| 方法 | 示例 | 作用 | 和 XML 修改的区别 |
|---|---|---|---|
setSpeed |
traci.vehicle.setSpeed(vehID, 10) |
设置车辆速度 | 运行过程中临时改变某辆车速度 |
slowDown |
traci.vehicle.slowDown(vehID, 5, 3) |
让车辆在指定时间内减速到目标速度 | 适合模拟干预、突发减速 |
setMaxSpeed |
traci.vehicle.setMaxSpeed(vehID, 20) |
修改车辆最大速度 | 只影响指定车辆,非全局车辆类型 |
changeLane |
traci.vehicle.changeLane(vehID, 1, 5) |
强制车辆在一定时间内换到指定车道 | 可用于控制个体换道 |
setLaneChangeMode |
traci.vehicle.setLaneChangeMode(vehID, 0) |
修改换道模式 | 可关闭或限制自动换道行为 |
setSpeedMode |
traci.vehicle.setSpeedMode(vehID, 31) |
修改速度安全检查模式 | 控制车辆是否遵守安全速度、加减速度、路权等限制 |
setColor |
traci.vehicle.setColor(vehID, (255, 0, 0)) |
修改车辆颜色 | 方便 GUI 中观察被控制车辆 |
setRoute |
traci.vehicle.setRoute(vehID, edgeList) |
修改车辆路线 | 运行中改变车辆路径 |
setType |
traci.vehicle.setType(vehID, typeID) |
修改车辆类型 | 可切换到已有的 vType |
信号灯相关:
| 方法 | 示例 | 作用 | 和 XML 修改的区别 |
|---|---|---|---|
setPhase |
traci.trafficlight.setPhase("nd0", 0) |
切换到指定相位索引 | 运行过程中切换相位 |
setProgram |
traci.trafficlight.setProgram("nd0", "1") |
切换信号控制程序 | 需要程序已经存在或已加载 |
setPhaseDuration |
traci.trafficlight.setPhaseDuration("nd0", 20) |
修改当前相位剩余时间 | 适合动态延长或缩短当前相位 |
setRedYellowGreenState |
traci.trafficlight.setRedYellowGreenState("nd0", "GGG") |
直接设置灯色字符串 | 最直接,但要非常清楚每一位对应的 link |
setProgramLogic |
traci.trafficlight.setProgramLogic(...) |
写入完整信号控制程序 | 用于更复杂的在线信号控制 |
官方文档 Change_Traffic_Lights_State
3. TraCI 修改和 XML 修改的区别
这是入门时很容易混淆的一点。
| 对比 | XML 文件修改 | TraCI 修改 |
|---|---|---|
| 修改时机 | 仿真开始前 | 仿真运行中 |
| 作用范围 | 通常是全局配置或初始配置 | 通常是某个时间步、某个对象的动态控制 |
| 典型对象 | 路网、车辆类型、初始车流、固定信号方案 | 某辆车、某个信号灯、某个相位、实时路径 |
| 是否适合批量实验 | 适合生成不同场景配置 | 适合在线控制和算法交互 |
| 是否写回 XML | 通常本身就是 XML 配置 | 一般不会自动写回 XML,除非自己额外保存 |
| 举例 | 在 .rou.xml 中设置 maxSpeed=40 |
用 traci.vehicle.setMaxSpeed(vehID, 20) 临时限制某辆车 |
| 举例 | 在 .add.xml 中定义固定信号周期 |
用 traci.trafficlight.setPhaseDuration() 动态调整绿灯时间 |
可以这样理解:
XML 文件:定义仿真开始前的“初始世界”
TraCI:在仿真运行过程中实时干预
如果只是固定场景仿真,很多内容写 XML 就够了。
如果要做自适应信号控制、车辆行为干预、强化学习控制、实时数据采集,就需要 TraCI。
十三、我是怎么使用学习资源的
我当时主要参考了几类资源。
1. CSDN 上的 SUMO 专栏
CSDN 上有一些比较系统的 SUMO 入门文章,比如 xml 文件、Python 联合、TraCI 基本应用等。对于刚开始不知道项目结构的人来说,这类文章比较友好。
可以先用 CSDN 专栏建立整体印象,知道 SUMO 项目里常见文件怎么组织、TraCI 大概怎么用,最后还是要回到官方文档确认。
2. SUMO 官方文档
官方文档内容很全,但刚入门直接看会比较吃力。我的用法是:不把它当教材从头读,而是把它当字典查。
例如:
- 不知道某个 XML 标签有哪些属性,就查官方文档;
- 不知道
netconvert支持哪些导入方式,就查官方文档; - 不知道 TraCI 某个模块有哪些函数,就查官方 TraCI 文档;
- 不确定参数单位或默认值时,也以官方文档为准。
这种方式会更适合项目驱动学习。
3. “云鹤烟波”公众号
这个公众号里有一些作者做项目的实例和代码分享。它的价值不一定是从零教学,而是当你已经有一个具体问题时,可以去里面搜索有没有类似做法。
比如我当时遇到“怎么设置车道线实线”这类比较具体的问题,就是通过搜索相关内容找到思路的。
所以这类资源更适合放在“遇到问题时搜索”和“找项目实现思路”的阶段。
十四、总结
SUMO 入门时最容易卡住的地方,是资料比较分散:官方文档很全,但不够“新手友好”;中文教程有帮助,但不一定覆盖具体项目;视频资源相对较少,很多问题最后还是要靠搜索和查文档解决。
我的经验是:不要一开始就试图掌握 SUMO 的全部功能,而是先围绕项目目标建立最小闭环:
安装 SUMO
-> 跑通示例
-> 理解 XML 文件
-> 生成路网和车流
-> 写 sumocfg
-> 用 add.xml 补充信号、检测器和输出
-> 用 TraCI 运行仿真和采集数据
-> 根据项目需求查文档补细节
参考资料
-
公众号:云鹤烟波
更多推荐


所有评论(0)