第4章:场景构建基础:OpenDrive高精地图、场景元素(道路、交通标志、动态障碍物)
场景构建,说白了就是给自动驾驶系统搭一个「虚拟考场」。你想想看,没有这个考场,你怎么敢让车直接上路跑?我刚开始做仿真时,总觉得随便画几条路就能跑,结果第一次测试就撞了虚拟护栏——嗯,从那以后我再也不敢轻视场景构建了。
4.1 OpenDrive高精地图:场景的「骨架」
高精地图不是普通导航地图。普通地图告诉你「前方500米右转」,高精地图告诉你「前方500米右转,车道宽度3.5米,曲率半径200米,坡度2%」。我个人习惯把OpenDrive比作场景的骨架——没有它,所有元素都是散的。
4.1.1 OpenDrive的核心结构
OpenDrive用XML描述道路网络。它的核心概念其实就几个:
- 道路(Road):最基本的单元,包含车道、参考线
- 车道(Lane):每条道路由多个车道组成,有编号和类型
- 连接(Junction):道路之间的交叉口
- 参考线(Reference Line):道路的几何中心线
我遇到过最坑的事是什么?有一次导入第三方地图,发现车道编号全乱了。后来查了半天,原来是参考线方向定义反了。所以这里要提醒你:参考线的方向决定了车道编号的正负,这是最容易踩的坑。
关键点:OpenDrive中,车道编号以参考线为界,右侧为正(1,2,3...),左侧为负(-1,-2,-3...)。千万别搞反了!
4.1.2 一个简单的OpenDrive示例
来看一段实际代码。这是一条双向两车道的道路:
<?xml version="1.0" encoding="UTF-8"?>
<OpenDrive>
<header revMajor="1" revMinor="4" name="demo_map"/>
<road name="Road_0" length="100.0" id="0" junction="-1">
<planView>
<geometry s="0.0" x="0.0" y="0.0" hdg="0.0" length="100.0">
<line/>
</geometry>
</planView>
<lanes>
<laneSection s="0.0">
<left>
<lane id="-1" type="driving" level="false">
<width sOffset="0.0" a="3.5" b="0.0" c="0.0" d="0.0"/>
</lane>
</left>
<right>
<lane id="1" type="driving" level="false">
<width sOffset="0.0" a="3.5" b="0.0" c="0.0" d="0.0"/>
</lane>
</right>
</laneSection>
</lanes>
</road>
</OpenDrive>
这段代码定义了一条100米长的直路,左右各一条3.5米宽的车道。你看,其实不复杂。但实际项目中,一条城市道路可能有几十个车道段,每个段的宽度、坡度都在变化。
小技巧:我建议初学者先用OpenDrive官方提供的编辑器(如ODR Viewer)可视化地图,确认几何正确后再写代码。肉眼检查比调试代码快得多。
4.2 场景元素:给骨架「长肉」
有了地图骨架,接下来要填充场景元素。我把它们分成三类:道路本身、交通标志、动态障碍物。
4.2.1 道路元素
道路不只是路面。它还包括:
- 车道标线:实线、虚线、双黄线
- 路肩:硬路肩、软路肩
- 人行横道:位置、宽度、方向
- 减速带:高度、长度
我曾经在一个项目中,忽略了人行横道的方向属性。结果仿真时,车辆在人行横道上横着刹车——嗯,行人看了都得愣住。所以每个元素的属性都要仔细核对。
4.2.2 交通标志
交通标志在OpenDrive中用<signal>标签表示。核心属性包括:
| 属性 | 说明 | 示例值 |
|---|---|---|
| id | 唯一标识 | "1001" |
| s | 沿道路的纵向位置 | 50.0 |
| t | 横向偏移(正为右侧) | 2.0 |
| type | 标志类型 | "100"(限速) |
| value | 标志数值 | 60(km/h) |
举个例子,在道路50米处放置一个限速60km/h的标志:
<signal s="50.0" t="2.0" id="1001" type="100" value="60" dynamic="no"/>
注意:交通标志的坐标系是沿道路的s-t坐标系,不是全局x-y坐标系。很多新手在这里栽跟头——标志位置看起来对,但实际在仿真中飘在半空中。我建议先用可视化工具确认位置。
4.2.3 动态障碍物
动态障碍物是场景中最难搞的部分。它包含:
- 车辆:轿车、卡车、自行车
- 行人:行走、奔跑、静止
- 动物:狗、猫(别笑,真遇到过)
每个动态障碍物需要定义:初始位置、速度、轨迹、行为模式。我个人习惯用OpenSCENARIO来定义动态行为,它和OpenDrive是天生一对。
举个简单的例子,一辆以10m/s匀速行驶的车辆:
<Scenario>
<Entity name="Ego">
<Position x="0.0" y="0.0" z="0.0"/>
<Speed value="10.0"/>
</Entity>
<Entity name="Target">
<Position x="50.0" y="0.0" z="0.0"/>
<Speed value="5.0"/>
<Trajectory>
<Waypoint time="0.0" x="50.0" y="0.0"/>
<Waypoint time="10.0" x="100.0" y="0.0"/>
</Trajectory>
</Entity>
</Scenario>
这里Ego车是自车,Target车是目标障碍物。你看,Target车从50米处出发,10秒后到达100米处,速度5m/s。这就是一个最简单的跟车场景。
4.3 知识体系总览
说了这么多,我画了一张图帮你理清思路。场景构建的核心就是「骨架+血肉+灵魂」:
4.4 避坑指南:我踩过的那些坑
最后,分享几个实战中容易踩的坑:
- 坐标系混淆:全局坐标系和s-t坐标系混用。我曾经把交通标志的t值当成了全局y坐标,结果标志跑到了路对面。
- 车道宽度突变:OpenDrive允许车道宽度在相邻段突变,但实际道路不会这样。仿真时车辆会「跳」一下,看着特别假。
- 动态障碍物轨迹不连续:两个路径点之间速度变化太大,车辆会瞬移。我建议每个路径点之间时间间隔不超过1秒。
- 忽略高程:平面地图看着没问题,一加上坡度就全乱了。记得检查z轴数据。
我的习惯:每次构建完场景,先用可视化工具跑一遍,再让自车以低速跑一遍,最后才上高速测试。这个流程虽然慢,但能省掉后面90%的调试时间。
好了,场景构建的基础就这些。记住:地图是骨架,静态元素是血肉,动态元素是灵魂。三者配合好了,你的仿真场景才能真实、可靠。