本文主要讲解routing和planning模块中的reference line,我之前一直搞不明白这个reference line是如何生成的,有什么作用,和routing以及planning的关系。现在有了一些心得打算梳理一下:
决策规划模块负责生成车辆的行驶轨迹。要做到这一点,决策规划模块需要从宏观到局部经过三个层次来进行决策。
参考线是整个决策规划算法的基础。在Planning模块的每个计算循环中,都会先生成参考线,然后在这个基础上进行后面的处理,例如:交通规则逻辑,障碍物投影,路径优化,速度决策等等。可以说,参考线贯穿了整个Planning模块的实现。下面就看下reference line如何在routing和plannin中承前启后的:
Routing模块正如其名称所示,其主要作用就是根据请求生成路由信息。
模块输入:
模块输出:
2.1 Topo地图
为了计算路由路径,在Routing模块中包含一系列的类用来描述Topo地图的详细结构。这些类的定义位于modules/routing/graph/
目录下。它们的说明如下:
简单来说,Topo地图中最重要的就是节点和边,本质上是一系列的Topo节点以及它们的连接关系,节点对应了道路,边对应了道路的连接关系,这个在Apollo开发者社区的技术文章中着重讲过。如下图所示:
Routing模块需要的地图结构通过TopoGraph来描述,而TopoGraph的初始化需要一个地图文件。但该地图文件与其他模块需要的地图文件并不一样,这里的地图文件是Proto结构导出的数据。之所以这样做是因为:Routing模块不仅需要地图的Topo结构,还需要知道每条路线的行驶代价。在Proto结构中包含了这些信息。在下面的内容中,我们将看到这个行驶代价是从哪里来的。
很显然,两个地点的导航路径结果通常会有多个。而计算导航路径的时候需要有一定的倾向,这个倾向就是行驶的代价越小越好。我们很自然的想到,影响行驶代价最大的因素就是行驶的距离。
但实际上,影响行驶代价的因素远不止距离这一个因素。距离只是宏观上的考虑,而从微观的角度来看,行驶过程中,需要进行多少次转弯,多少次掉头,多少变道,这些都是影响行驶代价的因素。所以,在计算行驶代价的时候,需要综合考虑这些因素。
再从另外一个角度来看,(在路线已经确定的情况下)行驶的距离是一个物理世界客观存在的结果,这是我们无法改变的。不过,对于行驶过程中,有多在意转弯,掉头和变道,每个人或者每个场景下的偏好就不一样了。而这,就是配置文件“/apollo/modules/routing/conf/routing/config.pb.txt“存在的意义了。这里面配置了上面提到的这些动作的惩罚基数,而这些基数会影响路线时的计算代价。
routing的请求接口是下面这个:
bool Routing::Process(const std::shared_ptr<RoutingRequest> &routing_request, RoutingResponse* const routing_response);
这个接口只有很简洁的两个参数:一个是描述请求的输入参数routing_request
,一个是包含结果的输出参数routing_response
。它们都是在proto文件中定义的。
2.2 RoutingRequest
message LaneWaypoint {
optional string id = 1;
optional double s = 2;
optional apollo.common.PointENU pose = 3;
}
message LaneSegment {
optional string id = 1;
optional double start_s = 2;
optional double end_s = 3;
}
message RoutingRequest {
optional apollo.common.Header header = 1;
repeated LaneWaypoint waypoint = 2;
repeated LaneSegment blacklisted_lane = 3;
repeated string blacklisted_road = 4;
optional bool broadcast = 5 [default = true];
optional apollo.hdmap.ParkingSpace parking_space = 6;
}
2.3 RoutingResponse:
message RoutingResponse {
optional apollo.common.Header header = 1;
repeated RoadSegment road = 2;
optional Measurement measurement = 3;
optional RoutingRequest routing_request = 4;
optional bytes map_version = 5;
optional apollo.common.StatusPb status = 6;
}
message RoadSegment {
optional string id = 1;
repeated Passage passage = 2;
}
message Passage {
repeated LaneSegment segment = 1;
optional bool can_exit = 2;
optional ChangeLaneType change_lane_type = 3 [default = FORWARD];
}
message LaneSegment {
optional string id = 1;
optional double start_s = 2;
optional double end_s = 3;
}
enum ChangeLaneType {
FORWARD = 0;
LEFT = 1;
RIGHT = 2;
};
message Measurement {
optional double distance = 1;
}
RoutingResponse中的属性说明如下:
这里的RoadSegment road
是最重要的数据。这个数据其实是一个三层的结构体嵌套,它们的说明如下:
RoadSegment road
:描述道路,一条道路可能包含了并行的几条通路(Passage)。Passage
:描述通路,通路是直连不含变道的可行驶区域。一个通路可能包含了前后连接的多个车道。(对应了pac_map中的RouteSegments
)LaneSegment
:描述车道,车道是道路中的一段,自动驾驶车辆会尽可能沿着车道的中心线行驶。pnc全称是Planning And Control,是Planning用来对接Routing搜索结果的子模块,但他比较重要,这里我把它单独拎出来梳理下。
PncMap
类负责对接Routing搜索结果的更新:会根据车辆当前位置,提供车辆周边的RouteSegments
信息供ReferenceLineProvider
生成ReferenceLine
。而这里的RouteSegments
对应了routing模块里RoutingResponse
里的Passage
结构,它其中会包含若干个车道信息。这个RouteSegments类继承自std::vector。RouteSegments中有如下一些方法值得关注:
在Planning模块中有以下三个数据结构将是本文关注的重点:
ReferenceLine
:原始参考线,源码位于planning/reference_line/
目录下。根据Routing的搜索结果生成。ReferenceLineInfo
:源码位于planning/reference_line/
目录下。Planning实现中,逻辑计算的基础数据结构,很多操作都会在这个数据结构上进行(例如:交通规则逻辑,障碍物投影,路径优化,速度决策等)。本文中的“参考线”一词将不区分ReferenceLine
和ReferenceLineInfo
两个结构。Trajectory
:下文中我们将看到,有好几个结构用来描述轨迹。它们在不同的场合下使用。这其中,ADCTrajectory
是Planning模块的输出。它是Planning模块一次计算循环中,处理了所有逻辑的最终结果,包含了车辆行驶需要的所有信息。因此,这个数据将直接影响到自动驾驶车辆的行车行为。解析百度Apollo之Routing模块
解析百度Apollo之参考线与轨迹
开发者说丨离散点曲线平滑原理
直播回顾丨Apollo自动驾驶论坛①规划模块算法解析
Apollo Planning Frame
Apollo ReferenceLineProvider
Apollo EMPlanner(v 3.0)
Apollo 6.0 规划算法解析
Apollo 6.0 规划模块算法解析2