4. OpenSCENARIO基础
OpenSCENARIO 是 ASAM 定义的动态场景描述标准,用于把“车辆在什么道路上、以什么状态、执行什么动作、在什么条件下切换动作”表达成机器可读的 XML 文件。它不描述静态地图几何,而是描述随时间演化的交通行为,因此常与 OpenDRIVE 配合使用:OpenDRIVE 提供道路与车道拓扑,OpenSCENARIO 提供参与者、动作与触发逻辑。理解 OpenSCENARIO 的关键,是抓住“文件结构—Storyboard 叙事—Actor 实体—Maneuver 动作—Trigger 触发—Parameter 参数化”这条主线。
一、文件结构:从 FileHeader 到 Storyboard
一个 OpenSCENARIO 文件通常由 FileHeader、ParameterDeclarations、CatalogLocations、RoadNetwork、Entities、Storyboard 等顶层元素组成。其中 FileHeader 声明版本、作者与地图引用;RoadNetwork 通过 LogicFile 指向 OpenDRIVE 地图;Entities 定义场景中所有参与者;Storyboard 则是整个场景的“剧本”,负责把实体、动作和触发条件编排成可执行的时间线。
<OpenSCENARIO>
<FileHeader revMajor="1" revMinor="2" description="Cut-in scenario"/>
<ParameterDeclarations>
<ParameterDeclaration name="EgoSpeed" parameterType="double" value="13.9"/>
</ParameterDeclarations>
<RoadNetwork>
<LogicFile filepath="highway.xodr"/>
</RoadNetwork>
<Entities> ... </Entities>
<Storyboard> ... </Storyboard>
</OpenSCENARIO>
二、Storyboard:场景的叙事骨架
Storyboard 是 OpenSCENARIO 的核心组织单元,它把场景拆成 Story、Act、ManeuverGroup、Maneuver、Event 等层级。一个 Story 可以包含多个 Act,Act 之间可以并行或串行;每个 Act 下挂 ManeuverGroup,ManeuverGroup 指定由哪些 Actor 执行;Maneuver 描述具体动作;Event 则把动作与触发条件绑定。这样分层的好处是:同一套动作可以在不同触发条件下复用,也便于把复杂场景拆成可维护的模块。
Storyboard 层级速记:Story → Act → ManeuverGroup → Maneuver → Event → Action。越往下越接近“具体谁在什么时候做什么”。
三、Actor、Maneuver 与 Trigger:谁、做什么、何时做
Actor 是场景中的参与者,通常通过 EntityRef 引用 Entities 中定义的车辆或行人。每个 Actor 可以拥有 Private 动作,也可以参与 ManeuverGroup 的协同动作。Maneuver 描述动作集合,常见动作包括 SpeedAction(速度变化)、LaneChangeAction(换道)、LaneOffsetAction(横向偏移)、LongitudinalDistanceAction(纵向距离保持)等。
Trigger 决定动作何时开始或结束,常见类型有 TimeToCollisionCondition、RelativeDistanceCondition、SpeedCondition、StoryboardElementStateCondition 等。Trigger 可以组合成 ConditionGroup,通过 and / or 表达复杂逻辑。例如“当自车与前车距离小于 30 米且自车速度大于 15 m/s 时触发换道”,就是典型的复合触发。
四、Parameter:让场景可复用、可批量生成
Parameter 是 OpenSCENARIO 中非常实用的机制。通过 ParameterDeclaration 声明变量,再在动作或触发条件中用 $参数名 引用,就能把“自车速度 13.9 m/s、跟车距离 30 m、触发时间 2 s”等具体数值抽离出来。这样同一份场景文件可以通过外部赋值生成大量变体,用于回归测试、边界测试和蒙特卡洛仿真。
<ParameterDeclaration name="EgoSpeed" parameterType="double" value="13.9"/>
<ParameterDeclaration name="Gap" parameterType="double" value="30.0"/>
<SpeedAction>
<SpeedActionDynamics dynamicsShape="step" value="0"/>
<SpeedActionTarget>
<AbsoluteTargetSpeed value="$EgoSpeed"/>
</SpeedActionTarget>
</SpeedAction>
五、案例:高速公路切入场景的 Storyboard 拆解
以“前车减速后自车向左换道”为例:Entities 中定义 Ego 和 Target 两辆车;Storyboard 中创建一个 Story,包含两个 Act。Act1 让 Target 以 SpeedAction 减速,触发条件为 SimulationTimeCondition 到达 2 s;Act2 让 Ego 执行 LaneChangeAction,触发条件为 RelativeDistanceCondition 小于 25 m。两个 Act 通过 StartTrigger 串联,形成清晰的因果链。若把距离、速度、换道持续时间参数化,就能快速生成不同激进程度的切入场景。
| 核心概念 | 作用 | 典型元素 | 常见误区 |
|---|---|---|---|
| Storyboard | 组织场景时间线与叙事层级 | Story / Act / ManeuverGroup | 把 Act 当成动作本身,忽略其容器属性 |
| Actor | 指定动作执行者 | EntityRef / Private | 忘记在 Entities 中先声明实体 |
| Maneuver | 描述具体驾驶动作 | SpeedAction / LaneChangeAction | 动作与触发条件混写在同一层 |
| Trigger | 控制动作开始或结束 | ConditionGroup / Condition | 条件逻辑 and/or 嵌套错误 |
| Parameter | 实现场景参数化与复用 | ParameterDeclaration / $变量 | 参数未声明就直接引用 |
实践建议:编写 OpenSCENARIO 时,先画 Storyboard 层级草图,再写 Entities,最后补 Trigger。这样能避免“动作写了但没人执行”或“触发条件永远不成立”两类高频错误。
常见排障:场景加载失败时,优先检查 LogicFile 路径是否正确、EntityRef 是否指向已声明实体、ParameterDeclaration 是否缺失,以及 Trigger 中的条件是否引用了不存在的实体或参数。
掌握 OpenSCENARIO 的结构、Storyboard、Actor、Maneuver、Trigger 与 Parameter,就相当于掌握了动态场景的“语法”。后续无论是做场景生成、工具链集成还是仿真验证,都可以围绕这套核心概念展开。