4. OpenDRIVE文件结构
OpenDRIVE 文件本质上是一个遵循 XML 规范的文本文件,其全部道路网络信息都组织在一棵具有严格层级关系的元素树中。理解这棵树的根、主干与分支,是后续进行道路几何建模、交叉口设计、信号控制配置以及仿真验证的基础。本节从 XML 根元素出发,逐层剖析 header、road、junction、controller、station 等主要节点的作用、属性与嵌套关系,帮助读者建立“文件即结构、结构即语义”的整体认知。
一、核心原理:OpenDRIVE 的 XML 树形结构
OpenDRIVE 采用单一根元素 <OpenDRIVE> 包裹全部内容,根元素下通常包含 <header> 以及若干条 <road>、<junction>、<controller> 等顶层节点。这些顶层节点之间并非简单并列,而是通过 id、junction、link 等属性相互引用,形成一张“树 + 图”的混合结构:树形层级保证文件可解析,引用关系保证道路网络可连通。
从语义上看,<header> 描述整个数据集的身份与坐标基准;<road> 描述一条道路的参考线、车道、高程与附属物;<junction> 描述多条道路交汇形成的连接区域;<controller> 描述信号灯等控制设备的逻辑;<station> 则用于沿道路参考线定义里程桩号或特定位置。它们共同构成一个可被仿真器、规划器或可视化工具直接消费的道路网络描述。
关键理解:OpenDRIVE 的层级关系不是“谁包含谁”那么简单,而是“谁定义几何、谁定义连接、谁定义控制”的职责分离。road 负责几何与车道,junction 负责连接,controller 负责控制,header 负责全局基准。
二、主要节点层级与职责
下面按从根到叶的顺序,梳理各主要节点的层级关系与核心职责。
- <OpenDRIVE>:根元素,声明文件版本与命名空间,所有其他元素均在其内部。
- <header>:全局头信息,包含 revMajor、revMinor、name、version、date、north、south、east、west、geoReference 等属性,定义地图范围与坐标参考系。
- <road>:道路主体,包含
planView(参考线几何)、elevationProfile(高程)、lateralProfile(横断面)、lanes(车道)、objects(附属物)、signals(信号)、surface(路面)等子节点。 - <junction>:交叉口,包含
connection子节点,每个 connection 通过 incomingRoad 与 connectingRoad 描述进入道路与连接道路,并通过 contactPoint 与 laneLink 描述车道级连接。 - <controller>:控制器,通常与 junction 配合,包含
control子节点,用于将信号灯组与具体控制逻辑关联。 - <station>:里程桩号或站点,通常出现在 road 内部或作为独立节点,用于沿参考线定位特定位置,如公交站、检测点、事件点。
这些节点之间的引用关系可以概括为:header 提供全局坐标基准;road 通过 id 被 junction 的 connection 引用;controller 通过 control 引用 signal;station 通过 s 坐标定位在 road 上。理解这些引用,才能正确解析一个完整的 OpenDRIVE 文件。
三、方法流程:如何阅读一个 OpenDRIVE 文件
面对一个实际的 OpenDRIVE 文件,建议按以下流程逐层阅读,避免一开始就陷入几何细节。
- 先看根元素与 header:确认版本号、地图范围、geoReference 坐标参考系,判断该文件使用地理坐标还是局部投影坐标。
- 统计 road 数量与 id:每条 road 都有唯一 id,记录其长度、junction 属性(是否为交叉口内部道路)。
- 查看 junction 与 connection:理解哪些 road 在何处相交,connection 的 incomingRoad 与 connectingRoad 如何配对,laneLink 如何连接车道。
- 查看 controller 与 control:确认信号灯组与 junction 的关联,理解控制逻辑的归属。
- 查看 station 与 s 坐标:定位公交站、检测点、事件点等沿路设施的位置。
- 最后回到 road 内部:按需查看 planView、lanes、elevationProfile 等几何细节。
实用技巧:在文本编辑器中折叠 XML 节点,先展开 header 和所有 road 的 id 与 length,再展开 junction 的 connection,可以快速建立道路网络的拓扑骨架。
四、案例与场景:一个简单十字交叉口的文件结构
假设有一个由两条道路组成的十字交叉口,文件结构大致如下:
<OpenDRIVE>
<header revMajor="1" revMinor="6" name="CrossDemo"
version="1.0" date="2024-01-01"
north="100.0" south="-100.0" east="100.0" west="-100.0">
<geoReference><![CDATA[+proj=utm +zone=50 +ellps=WGS84]]></geoReference>
</header>
<road id="1" name="EastWest" length="200.0" junction="-1">
<planView> ... </planView>
<lanes> ... </lanes>
</road>
<road id="2" name="NorthSouth" length="200.0" junction="-1">
<planView> ... </planView>
<lanes> ... </lanes>
</road>
<junction id="100" name="MainCross">
<connection id="1" incomingRoad="1" connectingRoad="2" contactPoint="start">
<laneLink from="1" to="-1"/>
</connection>
<connection id="2" incomingRoad="2" connectingRoad="1" contactPoint="start">
<laneLink from="1" to="-1"/>
</connection>
</junction>
<controller id="200" name="SignalCtrl">
<control signalId="300" type="0"/>
</controller>
<station id="400" name="BusStop" s="80.0" roadId="1"/>
</OpenDRIVE>
在这个例子中,road 1 与 road 2 是两条独立道路,junction 100 通过 connection 描述它们之间的连接关系,controller 200 负责信号控制,station 400 在 road 1 的 s=80.0 处标记了一个公交站。可以看到,所有节点都围绕 road 的 id 和 s 坐标展开,这正是 OpenDRIVE 结构设计的核心思想。
五、常见误区与复盘
| 误区 | 表现 | 正确理解 |
|---|---|---|
| 把 junction 当作道路 | 在 junction 内直接写 planView 几何 | junction 只描述连接关系,几何由 connectingRoad 承担 |
| 忽略 header 的 geoReference | 坐标数值看似合理但无法与地图对齐 | geoReference 决定坐标基准,必须优先确认 |
| controller 与 junction 脱节 | 信号灯组未通过 control 关联到具体交叉口 | controller 需与 junction 配合,control 引用 signalId |
| station 位置理解错误 | 把 station 当作独立道路节点 | station 是沿 road 参考线的 s 坐标定位点 |
| road id 重复或缺失 | junction 引用不到对应道路 | 每条 road 必须有唯一 id,且被正确引用 |
排障提示:如果仿真器报“找不到 incomingRoad”或“laneLink 无效”,优先检查 road 的 id 是否与 junction 中引用的 id 一致,以及 contactPoint 是否与道路方向匹配。多数结构类错误都源于 id 引用不一致,而非几何计算错误。
掌握 OpenDRIVE 的 XML 根元素、header、road、junction、controller、station 等主要节点及其层级关系,就相当于拿到了整张道路网络的“骨架图”。后续无论是进行道路几何编辑、交叉口渠化,还是信号配时与仿真验证,都可以在这棵结构树上找到对应的位置和操作入口。