驾驶场景为自动驾驶系统的测试和验证提供了一个强大的框架。本文介绍了表征场景的基本元素和时空关系,并逐步构建了一种表示它们的表现力语言。
驾驶通常被描述为机动车辆的受控操作,通常用于人员或货物的运输,但也会作为一种活动来享受。驾驶需要掌握一些技能,例如车道保持、交通合流、红灯停车、避让行人等。这些技能中的每一种都与我们在路上发现自己所处的某些情况或"场景"有关,需要我们以适当的方式做出反应。识别和表征这些驾驶场景对于实现自动驾驶汽车开发和安全操作的几个过程非常重要。
当面临艰巨的技术挑战时,我们经常在没有充分考虑问题空间的情况下潜入寻找解决方案。乍一看,自动驾驶似乎是一个典型的机器人问题,感知、规划和控制是关键因素。虽然这些功能确实构成了此问题的任何解决方案的基础,但你很快就会意识到问题的复杂性源于系统必须运行的条件的可变性。如果不花时间规划出在路上可能遇到的不同情况,并特意针对这些情况测试系统,那么你就有可能开发一个仅适用于少数情况的解决方案。
弄清楚更广阔的可能场景有助于我们采取更全面的方法——了解问题有多大以及其多样化,划分出我们准备解决的问题的有意义的子集,然后设计一个满足需求的适当解决方案。这也有助于我们弄清楚在所需系统中需要开发哪些功能并进行相应的计划。
确定适用于所选域的方案是此过程的第一步。例如,如果你正在建造一辆自动穿梭巴士,以便在封闭的校园内运行,则相关场景集将与你在公共道路上可能期望的场景不同。然后,可以根据对你重要的因素来组织场景,例如车道驾驶与交叉路口、让行与停车标志、领先/跟随与切入等。这种对方案进行编目的方式可能足以满足某些用例的需求,例如根据验证计划测试和验证系统。但是,通过将每个方案分解为其构成元素,可以执行更多操作。
先前的工作建议将场景的元素组织成不同的"层",例如道路或地图信息,场景中的参与者,天气和其他环境条件。通常还区分一般描述或"逻辑"场景,以及表示特定实例的更"具体"变体。这些也是我们在场景建模方法中采用的一些指导原则。但我们的重点是另一个方面——我们如何最好地表现不同元素之间的关系?
答案可能取决于你的最终目标是什么,例如,你可能希望完全按照其播放方式重现一个场景(通常称为"重新模拟"),或者你可能希望获得场景的不同潜在变体(有些人称之为"场景模糊化")。或者,也许你只想将相关方案聚集在一起,以了解问题空间。无论目前的用例是什么,我们都会假设,能够满足所有这些需求的场景表示可能会更具表现力,并且对未来的使用也很健壮。
解决不同用例(包括提取,重新模拟和模糊测试)的常见场景表示将更具表现力和健壮性。
这种思路将我们引向了一种公理化的场景建模方法,我们试图根据其突出特征来表征每个场景,而不会过度拟合特定的最终应用。让我们从一个简单的例子开始 - 一辆车在阳光明媚的日子里在一段路上行驶。
请考虑以下事项:
如果我们要对这种情况进行最小描述,则对以下信息进行编码可能就足够了。
- weather: sunny
- road: R1
- vehicle: V1
position: R1
actions:
- drive:
road: R1
speed: 20
如果我们有两辆车在一条路上行驶,一辆跟着另一辆呢?
这一次,让我们稍微迭代一下格式,并为全球/环境因素(例如天气)、地图元素(例如道路、车道)和参与者(例如车辆、行人)以及其初始位置和操作创建不同的部分。此外,现在我们有两辆车在同一条道路上,因此需要额外的措施来区分它们沿着道路的位置。这通常被称为纵向位置或"station"(s坐标)。
environment:
- weather: sunny
map:
- road: R1
actors:
- vehicle: V1
position:
reference: R1
station: S1
actions:
- drive:
road: R1
speed: 20
- vehicle: V2
position:
reference: R1
station: S2
actions:
- drive:
road: R1
speed: 20
在这里没有捕捉到的是两辆车的位置之间的空间关系,即V2(S2)领先于V1(S1)的事实。我们可以在单独的一节中表达如下:
...
relationships:
- S2 > S1
或者,我们可以根据S1重写V2的位置(例如S2 = S1 + 10)。一个更简洁的方法可能是直接表达一个Actor相对于另一个Actor的位置。
...
- vehicle: V2
position:
reference: V1
station: +10
actions:
- drive:
road: R1
speed: 20
这基本上表明,无论V1的位置如何,V2都应该位于V1前方10个单位(米)处,沿着相同的道路元素。
现在,让我们考虑一个常见的"切入"场景,其中一辆车在另一辆车前面改变车道。
我们需要捕获的两辆车之间存在明显的空间关系,例如,我们可以引入横向偏移(l坐标)来测量一辆车与另一辆车的距离。但在这种情况下,对车辆最初行驶的两条车道之间的拓扑关系进行编码可能更有意义。假设我们将这些车道称为R1_1和R1_2。这将使场景更容易推广到不同的情况,例如,弯曲的道路或更宽/更窄的车道。
拓扑关系允许我们利用道路结构,并推广到具有不同空间几何形状的不同情况。
我们可以将此图表示为地图部分中节点和边的集合。符号"X =type=> Y"只是语法糖,用于表示具有特定类型标签的边。此时,我们还应该从更通用的"drive"操作到"lane_follow"进行细微更改(然后是V2的"lane_change")。
environment:
- weather: sunny
map:
- nodes:
- road: R1
- lane: R1_1
- lane: R1_2
- edges:
- R1 =contains=> R1_1
- R1 =contains=> R1_2
- R1_1 =left=> R1_2
- R1_2 =right=> R1_1
actors:
- vehicle: V1
position:
reference: R1_2
station: S1
actions:
- lane_follow:
reference: R1_2
speed: 20
- vehicle: V2
position:
reference: R1_1
station: S2
actions:
- lane_follow:
reference: R1_1
speed: 25
- lane_change:
target: R1_2
station: S3
和以前一样,S3可以重写为"S1 +一些常量",或者可以相对于V1创建lane_change的目标(这将确保V2尝试在V1之前切入)。请注意,在这种情况下,V2 的初始车道跟随速度故意保持在略高于 V1 (20) 的 (25)。我们也可以把它变成一个相对数量。
最后,让我们考虑一个更复杂的场景,其中两辆车正在接近由停车标志控制的十字路口,并且需要相互协商才能安全通过。车辆V1试图左转,而V2则沿着直行。哦,现在天气开始转多云了!
在这种情况下,相关的地图元素包括交汇点 (J1) 和停车标志 (SS1、SS2)。我们还引入了一个道路"路段"作为车道元素的中间容器,例如,道路R1由3个路段(R1_1,R1_2和R1_3)组成,每个路段包含一个车道元素或"lanelet"(R1_1_1,R1_2_1,R1_3_1)。这些元素之间的联系可能很多,在手工构建这样的网络时很容易出错(部分在下面指定)。幸运的是,这种分层结构是从已建立的路线图表示格式(如OpenDRIVE和Lanelet2)改编而来的,可以从更简单的表示中推断出来,也可以直接从现有映射中提取。
environment:
- weather: cloudy
actors:
- vehicle: V1
position:
reference: R1_1_1
station: S1
actions:
- lane_follow:
reference: R1_1_1
speed: 15
- turn_left:
junction: J1
exit: R2_3_1
- vehicle: V2
position:
reference: R2_1_1
station: S2
actions:
- lane_follow:
reference: R2_1_1
speed: 15
- go_straight:
junction: J1
exit: R2_3_1
map:
- nodes:
- road: R1
- section: R1_1
- ...
- lanelet: R1_1_1
- ...
- road: R2
- section: R2_1
- ...
- lanelet: R2_1_1
- ...
- junction: J1
- stop_sign: SS1
- stop_sign: SS2
- edges:
- R1 =contains=> R1_1
- ...
- R2 =contains=> R2_1
- ...
- R1 =crosses=> R2
- R1_1 =enters=> J1
- ...
- R1_3 =exits=> J1
- ...
- SS1 =controls=> R1_1_1
- SS2 =controls=> R2_1_1
可以想象,此方案的不同实例化可能会产生完全不同的结果,特别是基于每个参与者的起始位置、其接近速度、主动性等。所有这些都是可以进一步丰富方案描述的参数。
现在考虑一下 - 如果你想捕捉特定的事件顺序,例如,当等待的行人(P1)决定开始行走时,车辆(V1)即将到达人行横道(CW1), 该怎么办?
请注意,这与车辆已经通过人行横道,然后行人开始行走的情况不同。
有几种方法可以表达这种时间关系,例如在行人行动之前使用等待条件,或者基于与车辆的距离。这在模拟中执行场景时很有用,但在其他用例中却没有实际帮助,例如从真实世界数据中提取场景。因此,我们采用了一种基于艾伦区间代数的更通用的方法,该方法以与我们可能考虑任何一维域的跨度相同的方式表达事件之间的时间关系。
在当前场景中,我们首先将场景中每个可识别的情况标记为"事件",时间具有定义其间隔的开始和结束时间(假设瞬时事件具有相同的开始和结束时间)。这包括车辆的lane_follow动作,行人的步行动作以及车辆通过人行横道(这本身不一定是动作,但仍然是可观察的事件)。
时态关系可以用作查找匹配方案时的约束,也可以在模拟方案时用作执行计划中的操作。
每个事件也可能具有一些关联的元数据,例如与事件最相关的地图元素的标识符(R1、CW1 等)。地图元素可用于分组两个事件,例如,在本例中,我们想说车辆中的人行横道和行人事件是相同的,因此我们使用相同的标识符(CW1)对其进行寻址。
然后,我们可以指定所需的时间关系作为条件:
...
actors:
- vehicle: V1
position:
reference: R1
station: S1
actions:
- lane_follow:
id: E1
reference: R1
speed: 20
- pedestrian: P1
position:
reference: CW1
station: S2
actions:
- walk:
id: E2
reference: CW1
speed: 2
events:
- crosswalk:
id: E3
actor: V1
reference: CW1
conditions:
- E2 =before=> E3
- E3 =during=> E1
请注意,在上述条件下,"Y之前的X"被解释为X必须在Y开始之前开始;我们故意允许事件之间的一些重叠(例如,车辆可能会在安全的情况下开始过马路,甚至在行人完全离开人行横道之前)。
让我们回顾一下迄今所讨论的内容。我们首先确定了驾驶场景的不同组成部分,例如地图元素、环境因素、参与者及其操作。这些组件可以写在方案的结构化描述的单独部分中。然后,我们继续开发表示形式,以捕获这些组件之间的不同类型的关系:
这是我们表达驾驶场景所需的词汇,也是Ridecell场景语言(RSL)的基础。我们在团队中使用RSL进行场景建模,迭代语言规范本身,并围绕它构建工具和库,以支持我们已经确定的一些关键功能,包括地图相对坐标,参数分布和actor行为树。在即将发表的文章中,我们将分享有关 RSL 格式及其可以启用的特定用例的更多详细信息。
RSL使用地图相对坐标、参数分布和参与者行为树为场景建模提供丰富的词汇表。
熟悉其他场景描述格式的人可能已经认识到,这在结构上并没有太大的不同,但包括某些关键元素,使其更加灵活和可扩展。这是有意为之,因为我们希望保留根据需要轻松转换为其他格式的能力,以便与具有特定依赖关系的系统进行交互。例如,我们可以将具体的RSL场景导出到OpenSCENARIO v1.0中,许多现有的模拟器都使用该场景。事实上,我们还选择 YAML 作为通用文件格式来写出 RSL 模板,而不是特定于域的语言,这样就不会有解析或解释的障碍。
RSL的一个应用是作为一个模板,我们可以根据该模板比较现实世界的驾驶路段,并找到匹配的场景。匹配方案实质上为模板中指定的所有变量提供特定值,同时具有兼容的结构。使用此类模板库,你可以开始捕获整个操作域。下一个合乎逻辑的问题是,如何量化场景覆盖范围?也就是说,你观察真实数据中各种场景的频率。一种显而易见的方法是计算每个单独方案模板的出现次数。但这很容易变得无法控制,因为根据需要考虑的不同因素及其组合,可能的场景总数可能会爆炸式增长。
根据不同的情景因素来表达情景覆盖率,使我们能够分析不同的切片和维度,而不会被从测试或生产车辆收集的大量数据所淹没。
相反,我们建议根据因素本身量化场景覆盖范围,以便你可以在有意义的结构中组织它们的组合,并研究与你相关的任何"切片"。例如,要考虑的两个重要方案因素是地图元素和对象。地图元素包括车道、交叉路口、人行横道、停车标志等。对象可以根据感知系统的能力进行分类,例如车辆,自行车,行人等。如果我们专注于场景空间的这两个因素或"维度",我们可以使用二维直方图或"热图"来可视化场景覆盖范围。每个单元格的颜色表示该特定组合的计数或出现频率,边距为你提供每个维度的分布。
能够随着时间的推移监控这些分布可以带来许多好处。如果你正在从测试队列中收集数据,它们可以通知你覆盖范围中的差距,并帮助指导数据收集工作更有意义。如果要构建碰撞或接近碰撞驾驶事件的模型,则这些分布可以用作场景发生的先验概率。这种基于场景的方法还可以帮助决定自动驾驶中的哪些问题应该集中精力,以产生最大的影响。
除了场景发生之外,场景的相同结构分解还可以帮助你通过不同的视角查看系统中的指标。与场景中其他参与者的最小距离是需要密切关注的重要指标。在这里,我们可视化了相对于演员类型的最小距离(以米为单位)以及自我车辆的动态状态。任何意外情况,例如在硬加速/减速阶段与行人和自行车的最小距离较低,都可以进一步检查,并改进相应的运动控制算法/参数以避免这种情况。
当我们考虑场景的分布和可变性时,除了离散分类之外,我们还必须考虑这种连续变量。速度就是这样一个变量,可以显著改变场景的性质或结果。让我们以切入场景为例,从现实世界的驾驶日志中找到此模板的所有示例,并在发生切入时测量我们自我车辆的平均速度。
如果从这些场景中收集所有相对速度样本,那么我们可以得出一个反映真实世界观察结果的分布。例如,从自己的测试车辆中,我们观察到它具有大致正态或高斯分布,平均值为11.2 m / s(大约25 mph),标准偏差为3.8。这样的分布可以很容易地用RSL编码。
...
actors:
- vehicle: ego
position:
reference: R1_2
station: S1
actions:
- lane_follow:
reference: R1_2
speed: Gaussian(11.2, 3.8)
...
这在提取场景时没有太大影响(尽管你可以提出一个似然值来量化观测值与分布的匹配程度),但在生成同一场景的不同变体时,它起着重要作用。它构成了推导Actor行为的真实模型的基础。例如,下面是从分布生成的具体方案:
...
actors:
- vehicle: ego
position:
reference: R1_2
station: S1
actions:
- lane_follow:
reference: R1_2
speed: 8.6
...
从此类分布中抽样,并在仿真中播放这些结果场景,使你能够更可靠地测试系统的性能。同时,它避免了产生不太可能基于观测数据或完全难以置信的变化。还可以反转此采样分布,以生成更多罕见场景的实例(仍以实际数据为基础)。如果你正在训练使用机器学习的系统,这尤其有用,因为它们因过度拟合最常见的情况而声誉不佳。
从真实世界数据计算的连续分布有助于建立逼真的Actor模型,找到罕见的场景,利用重要性采样并防止过度拟合。
总之,我们在本文中开发的是用于驱动场景的抽象表示形式,以及一组可用于从中获取价值的流程。这种基于场景的数据收集、训练、测试和验证方法可以帮助组织和分解自动驾驶系统的开发,否则这将是一项艰巨的任务。目前正在开发的Nemo,将实施这些想法并解决不同的工作流程。
这种方法的应用也扩展到其他领域,例如汽车保险的风险建模,车队监控和优化,异常事件检测等。我们期待在即将发表的文章中更详细地解释它们,并与我们的行业合作伙伴一起深入研究。
原文链接:自动驾驶场景剖析 — BimAnt