作用
这个类为planning模块提供平滑的参考线
- ReferenceLineProvider是规划模块中候选路径(即参考线)的提供和管理者
- ReferenceLineProvider以Routing模块输出的高精度路径(从起点到终点的Lane片段集)为输入数据,经优化处理后生成平滑的参考线给给后续的动作规划子模块使用。
- ReferenceLineProvider类是采用c++11标准提供的多线程类,每次调用ReferenceLineProvider::Start()函数后,该类会在内部开启一个新线程,执行参考线的优化
- ReferenceLineProvider类是使用宏DECLARE_SINGLETON(ReferenceLineProvider)定义的单实例类,获取该类对象请使用ReferenceLineProvider::instance()。
这个类的主要作用:
成员变量
数据类型 | 成员变量 | 说明 |
---|---|---|
bool | is_initialized_ = false; | 初始化是否完成标志 |
std::atomic<bool> | is_stop_{false}; | 当前线程是否停止 |
std::unique_ptr<ReferenceLineSmoother> | smoother_; | 这是一个参考线平滑类对象,用来执行参考线平滑任务 |
ReferenceLineSmootherConfig | smoother_config_; | 读取二次规划样条参考线平滑配置的类对象 |
std::mutex | pnc_map_mutex_; | 运行规划控制地图在多线程环境下安全使用的互斥类对象 |
std::unique_ptr< hdmap::PncMap> | pnc_map_; | 规划控制模块使用的地图数据类对象 |
std::shared_ptr< relative_map::MapMsg> | relative_map_; | 如果是导航模式则启动相对地图,如果不是则启动pnc_map |
std::mutex | vehicle_state_mutex_; | 允许车辆状态在多线程环境下安全使用的互斥体类对象 |
common::VehicleState | vehicle_state_; | 当前的车辆状态 |
std::mutex | routing_mutex_; | 允许路由寻径响应在多线程环境下安全使用的互斥体类对象 |
routing::RoutingResponse | routing_; | Routing模块输出的路由寻径数据 |
bool | has_routing_ = false; | 从起点到终点是否具备路由 |
std::mutex | reference_lines_mutex_; | 允许参考线在多线程环境下安全使用的互斥体类对象 |
std::list< ReferenceLine> | reference_lines_; | 存储多条候选路径 |
std::list< hdmap::RouteSegments> | route_segments_; | 存储多条路由片段 |
double | last_calculation_time_ = 0.0; | 上次计算平滑参考线耗时 |
std::queue<std::list< ReferenceLine>> | reference_line_history_; | 用于检索历史路径片段的ID |
std::queue<std::list< hdmap::RouteSegments>> | route_segments_history_; | 存储多条历史路由路径片段 |
std::future< void> | task_future_; | |
std::atomic< bool> | is_reference_line_updated_{true}; | |
const common::VehicleStateProvider* | vehicle_state_provider_ = nullptr; |
成员函数
该类的重要成员函数包括:构造函数、Start、Stop、GenerateThread、CreateReferenceLine、UpdateReferenceLine,其他函数都是辅助性的功能函数
构造函数
初始化各种成员变量
-
初始化车辆状态提供者
-
如果是导航模式则启动相对地图,如果不是则启动pnc_map
- 初始化平滑器
-
先使用配置文件初始化smoother_config_
modules/planning/common/planning_gflags.cc中定义了(没有这个文件)
modules/planning/conf/planning.conf中定义了(是这个文件)
-
然后使用配置初始化平滑器
-
- 最后(注意,is_initialized_只在这里设置了。感觉是个没有用的变量,因为只要申请成功了就一定会设置它为true)
开始线程:ReferenceLineProvider::start
-
如果是导航模式直接返回,不做任何实际工作。
-
若启用参考线提供者线程,则以ReferenceLineProvider:: GenerateThread为入口函数创建一个新线程并返回true
- FLAGS_enable_reference_line_provider_thread定义在modules/planning/common/planning_gflags.cc中
- FLAGS_enable_reference_line_provider_thread定义在modules/planning/common/planning_gflags.cc中
小结:只有不是导航模式&&设置了FLAGS_enable_reference_line_provider_thread才会生成参考线
接下来我们看下线程入口
线程入口:ReferenceLineProvider::GenerateThread
这是ReferenceLineProvider类线程的入口函数。
- 首先入口就是一个循环函数,只要没有设置is_stop_,那么就会一直循环执行(这个is_stop_在 ReferenceLineProvider::Stop()中设置)
- 如果没有设置is_stop_,那么先让当前线程暂停,并且休眠50ms,将调度机会留给其他线程,当操作系统空闲时执行后续代码
- 休眠回来之后,看是否重新routing了,如果没有,说明可以复用之前的参考线,那么重新sleep
- 如果发现重新routing了(当发生车祸修路时会重新routing),那么先调用CreateReferenceLine生成参考线和短期路由,然后UpdateReferenceLine更新参考线,最后计算此次参考线生成所消耗的时间
开启线程之后通过下面2个方法,刷新routing请求和车辆状态。也就是说参考线通过实时的routing请求和车辆状态来生成参考线
bool UpdateRoutingResponse(const routing::RoutingResponse& routing);
void UpdateVehicleState(const common::VehicleState& vehicle_state);
生成一条参考线:ReferenceLineProvider::CreateReferenceLine
-
获取当前车辆状态
-
获取当前全局的导航路径
- 先获取当前的在apollo中,routing模块从base map地图获取道路的拓扑信息,并根据RoutingRequest中包含起点和终点位置,使用A star算法搜索出最终的路由线路,放在RoutingResponse中。
- 接着调用pnc_map_->IsNewRouting(routing)判断当前路由是否为新路由, 若是则调用pnc_map_->UpdateRoutingResponse(routing)更新路由
- PncMap对接了Routing的搜索结果。
- 如果Routing的路线变了,这里需要进行更新。
- 先获取当前的在apollo中,routing模块从base map地图获取道路的拓扑信息,并根据RoutingRequest中包含起点和终点位置,使用A star算法搜索出最终的路由线路,放在RoutingResponse中。
-
之后,调用CreateRouteSegments函数来创建路由片段
- 在行驶过程中,车辆的位置一直会变动(vehicle_state中包含了这个信息)。
- CreateRouteSegments方法中会调用pnc_map_->GetRouteSegments(vehicle_state, segments)来获取车辆当前位置周边范围的RouteSegment。
- 如果Routing的结果需要变道,则segments将是多个,否则就是一个(直行的情况)。
-
创建路由判断之后,判断是否需要粘合参考线
- 若不需粘合参考线(!FLAGS_enable_reference_line_stitching),则调用SmoothRouteSegment函数来平滑各路由片段列表segments,并将平滑后的路由片段存储到参考线列表reference_lines中,同时将不能平滑的路由片段从segments中删除;
- 如果是新的routing,对于新的Routing,则根据segments生成ReferenceLine,两者的数量是对应的。并且,ReferenceLine将直接从RouteSegment里面获取到道路的点的信息。
- 若需粘合参考线(FLAGS_enable_reference_line_stitching),则调用ExtendReferenceLine函数来合并不同参考线片段的重合部分,并将粘合后的路由片段保存到参考线列表reference_lines中,同时将不能粘合的路由片段从列表segments中删除。
- 大部分情况下,在车辆行驶过程中,会不停的根据车辆的位置对ReferenceLine进行长度延伸。ReferenceLine的长度是200多米的范围(往后30米左右,往前180米或者250米左右)。
- 若不需粘合参考线(!FLAGS_enable_reference_line_stitching),则调用SmoothRouteSegment函数来平滑各路由片段列表segments,并将平滑后的路由片段存储到参考线列表reference_lines中,同时将不能平滑的路由片段从segments中删除;
小结: 参考线生成是由CreateReferenceLine函数实现的。
- 首先由UpdateVehicleState函数获取车辆的状态
- 再由UpdateRoutingResponse函数获取Routing的结果,生成RouteSegment。
- routing是新生成的,则对其进行平滑与分割;若不是,则沿用上一个周期的参考线,并对其进行扩展与延伸。
更新一条参考线:UpdateReferenceLine
判断新参考线各线段是否与原参考线对应线段相同,若相同则忽略,否则更新。
结束线程:Stop
将线程停止标志is_stop_置为true,判断当前线程是否完成,若未完,则调用thread_->join()强制完成线程任务。
获取所有参考线:ReferenceLineProvider::GetReferenceLines
通过GetReferenceLines
来获取参考线。实际上并发模式和不是并发模式执行的函数都是一样,只不过并发模式下另外的线程已经计算好了,因此可以直接赋值。
bool ReferenceLineProvider::GetReferenceLines(
std::list<ReferenceLine> *reference_lines,
std::list<hdmap::RouteSegments> *segments) {
...
// 1. 如果有单独的线程,则直接赋值
if (FLAGS_enable_reference_line_provider_thread) {
std::lock_guard<std::mutex> lock(reference_lines_mutex_);
if (!reference_lines_.empty()) {
reference_lines->assign(reference_lines_.begin(), reference_lines_.end());
segments->assign(route_segments_.begin(), route_segments_.end());
return true;
}
} else {
double start_time = Clock::NowInSeconds();
// 2. 否则,创建并且更新参考线
if (CreateReferenceLine(reference_lines, segments)) {
UpdateReferenceLine(*reference_lines, *segments);
double end_time = Clock::NowInSeconds();
last_calculation_time_ = end_time - start_time;
return true;
}
}
AWARN << "Reference line is NOT ready.";
if (reference_line_history_.empty()) {
AERROR << "Failed to use reference line latest history";
return false;
}
// 3. 如果失败,则采用上一次的规划轨迹
reference_lines->assign(reference_line_history_.back().begin(),
reference_line_history_.back().end());
segments->assign(route_segments_history_.back().begin(),
route_segments_history_.back().end());
AWARN << "Use reference line from history!";
return true;
}
从上面代码可以得出,参考线的生成主要集中在2个函数中CreateReferenceLine和UpdateReferenceLine。