3、地图与路网:加载不同地图、理解OpenDRIVE路网结构、在编辑器中可视化路网
好,咱们进入第三章。这一章我打算聊聊地图和路网。说实话,这是整个场景编辑的基石。你想想看,没有地图,车往哪儿开?没有路网,车怎么知道该走哪条道?
我在做第一个完整的仿真项目时,就吃过地图的亏。当时随便加载了一个地图就开始写逻辑,结果车一直往绿化带上撞。后来才发现,地图的路网根本没加载对。嗯,从那以后,我每次都会先花十分钟把路网可视化出来看一眼。
3.1 加载不同地图
Carla 自带了几张官方地图,比如 Town01 到 Town10。每张地图的侧重点都不一样。我个人习惯把地图分成三类:
| 地图 | 特点 | 适合场景 |
|---|---|---|
| Town01 / Town02 | 基础网格布局,路网简单 | 新手练习、基础功能测试 |
| Town03 / Town04 | 有环岛、高架、多车道 | 复杂交通流、变道测试 |
| Town05 / Town06 | 高速公路、匝道、长直路 | 高速场景、紧急避让 |
| Town07 / Town10 | 乡村、工业区、不规则路口 | 非结构化道路测试 |
加载地图的代码其实很简单。但我要提醒你一点——切换地图时,一定要先销毁之前的客户端连接。我曾经犯过这个错,连续加载三次地图后,内存直接爆了。
import carla
# 连接到服务器
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)
# 加载地图
world = client.load_world('Town03')
# 检查是否加载成功
print(world.get_map().name)
你看,就这么几行。但实际项目中,我一般会加一个重试机制。因为服务器偶尔会卡住,尤其是地图比较大的时候。
3.2 理解 OpenDRIVE 路网结构
好,地图加载完了。但地图到底是什么?说白了,Carla 里的地图就是一份 OpenDRIVE 文件。这是一种 XML 格式的路网描述标准。
我第一次打开 OpenDRIVE 文件时,整个人是懵的。满屏的 XML 标签,什么 <road>、<lane>、<geometry>,完全不知道谁是谁。后来我总结了一个规律——你只需要关注三个核心概念:
- 道路(Road):路网的基本单元,每条路都有一个唯一的 ID
- 车道(Lane):道路上的每一条车道,有左车道、右车道之分
- 连接(Junction / Connection):道路之间的连接关系,比如路口、匝道
举个例子。你有一条路,ID 是 10。这条路有 3 条左车道和 3 条右车道。在 OpenDRIVE 里,它会这样描述:
<road name="Main Street" length="200.0" id="10" junction="-1">
<lanes>
<laneSection s="0.0">
<left>
<lane id="-1" type="driving">
<width a="3.5" b="0.0" c="0.0" d="0.0"/>
</lane>
<lane id="-2" type="driving">
<width a="3.5" b="0.0" c="0.0" d="0.0"/>
</lane>
</left>
<right>
<lane id="1" type="driving">
<width a="3.5" b="0.0" c="0.0" d="0.0"/>
</lane>
</right>
</laneSection>
</lanes>
</road>
注意看,左车道的 ID 是负数,右车道是正数。这是 OpenDRIVE 的一个约定。我当时不知道这个,写代码时一直拿正数去查左车道,结果查出来全是空的。
3.3 在编辑器中可视化路网
光看 XML 文件太抽象了。我建议你直接在 Carla 的编辑器中把路网可视化出来。这样一眼就能看出问题。
Carla 提供了一个调试工具,叫 carla.DebugHelper。你可以用它画出路网的所有车道线。代码大概是这样:
# 获取地图和调试对象
carla_map = world.get_map()
debug = world.debug
# 获取所有路点
waypoints = carla_map.generate_waypoints(2.0)
# 画出每个路点的位置
for wp in waypoints:
debug.draw_point(
wp.transform.location,
size=0.1,
color=carla.Color(0, 255, 0),
life_time=0.0
)
这段代码跑起来后,你会看到地图上出现密密麻麻的绿色小点。每个点代表一个路点,间隔 2 米。我个人习惯把间隔设成 1 米,这样路网看起来更连续。
但光画点还不够。我一般还会画出车道边界线。这样能直观地看到每条车道的走向:
# 画出车道边界
for wp in waypoints:
# 获取左边界和右边界
left_boundary = wp.get_left_lane()
right_boundary = wp.get_right_lane()
if left_boundary:
debug.draw_line(
wp.transform.location,
left_boundary.transform.location,
thickness=0.1,
color=carla.Color(255, 0, 0),
life_time=0.0
)
红色线就是左边界。跑起来后,你会看到整个路网像一张红色的网铺在地图上。嗯,这时候你就能一眼看出哪些地方的路网有问题了。
3.4 实战:加载自定义地图
官方地图用多了,你肯定会想用自己的地图。Carla 支持加载自定义的 OpenDRIVE 文件。方法很简单:
# 加载自定义 OpenDRIVE 文件
with open('my_map.xodr', 'r') as f:
opendrive_content = f.read()
# 使用自定义路网
world = client.generate_opendrive_world(
opendrive_content,
carla.OpendriveGenerationParameters(
vertex_distance=2.0,
max_road_length=500.0,
wall_height=0.0,
additional_width=0.0,
smooth_junctions=True,
enable_mesh_visibility=True
)
)
这里有几个参数我要特别说一下:
- vertex_distance:路网生成的精度。越小越精细,但生成时间也越长。我一般设 2.0
- max_road_length:单条道路的最大长度。超过这个值会被截断
- smooth_junctions:是否平滑路口。建议打开,不然路口会很难看
我曾经用这个功能导入过一个从网上下载的 OpenDRIVE 文件。结果生成出来的路网全是断的。后来发现是文件里的坐标单位不对——它用的是厘米,Carla 默认用米。所以,导入前一定要检查单位。
好了,这一章的内容就这些。地图加载、路网结构、可视化,这三步走完,你就能对场景有一个全局的把握。下一章我们会聊天气和光照,那个更有意思。