Apollo中Routing算法之navigator模块

本文背景

Apollo是无人驾驶相关的开源框架,GitHub地址为https://github.com/ApolloAuto/apollo,在决策部分主要具有Perception(感知),Prediction(预测),Routing(路由寻径),Planning(轨迹规划),Control(控制)。由于最近在看Routing相关的代码,所以主要针对Routing内容的一个个人总结。

本文是对Routing策略算法中的navigator模块的介绍。

Navigator介绍

在Routing模块中,当所有条件就绪时,就会使用高精地图搜索指定路径,其中在Routing代码中搜索路径的代码为

navigator_ptr_->SearchRoute(fixed_request, &routing_response)

该代码的主要目标是在收到request请求后调用navigator指针中的SearchRoute方法,如果成功找到路径,就把结果赋值给routing_response;如果失败就返回错误。所以SearchRoute是navigator中关键的方法,本文也主要会针对该方法做一个介绍。

数据结构及所有函数介绍

ShowRequestInfo:  展示请求的详细信息,主要展示waypoint以及blacklist

GetWayNodes:将request中的node以及node的s加入

SetErrorCode:报错信息

PrintDebugData:debug信息

Navigator:构造函数,查看是否已经准备好导航

IsReady:就绪标记函数

Clear:清除拓扑图管理数据

Init:初始化函数

MergeRoute:路径结合

SearchRouteByStrategy:利用Strategy中的算法来做路径规划,这里用的是Astar

SearchRoute:搜索路径,如果有路径就返回True,并赋值,如果没有就返回False

主要函数详解

主要函数包含有SearchRoute、Init、SearchRouteByStrategy、MergeRoute,这里对着几个函数分别详细介绍。

Init

bool Navigator::Init(const RoutingRequest& request, const TopoGraph* graph,
                     std::vector<const TopoNode*>* const way_nodes,
                     std::vector<double>* const way_s) { // 初始化函数,传入请求,图,路径节点集合,路径s
  Clear(); // 清除缓存变量,主要是拓扑图管理变量
  if (!GetWayNodes(request, graph_.get(), way_nodes, way_s)) { // 获取request的相关参数
    AERROR << "Failed to find search terminal point in graph!";
    return false;
  }
  black_list_generator_->GenerateBlackMapFromRequest(request, graph_.get(),
                                                     &topo_range_manager_); // 加入黑名单节点到拓扑图管理变量中
  return true;
}

代码主要功能为将请求加入路径节点,节点的s,这两个参数对于搜索路径是有帮助的。第一个参数是node,也就是节点,第二个参数是开始位置。然后将路径中的不可达黑名单加入管理变量中。

SearchRoute

bool Navigator::SearchRoute(const RoutingRequest& request,
                            RoutingResponse* const response) { // 搜索算法,输入request,response作为输出
  if (!ShowRequestInfo(request, graph_.get())) { // 如果request详情没能展示,说明有误,返回错误
    SetErrorCode(ErrorCode::ROUTING_ERROR_REQUEST,
                 "Error encountered when reading request point!",
                 response->mutable_status());
    return false;
  }

  if (!IsReady()) { // 如果构造函数初始化没有成功,就没能ready,返回错误
    SetErrorCode(ErrorCode::ROUTING_ERROR_NOT_READY, "Navigator is not ready!",
                 response->mutable_status());
    return false;
  }
  std::vector<const TopoNode*> way_nodes; // 定义路径节点vector
  std::vector<double> way_s; // 定义路径的S值vector
  if (!Init(request, graph_.get(), &way_nodes, &way_s)) { // 初始化数据,这里加入了请求的节点和S值
    SetErrorCode(ErrorCode::ROUTING_ERROR_NOT_READY,
                 "Failed to initialize navigator!", response->mutable_status());
    return false;
  }

  std::vector<NodeWithRange> result_nodes;// 这就是需要的结果
  if (!SearchRouteByStrategy(graph_.get(), way_nodes, way_s, &result_nodes)) { // 这里调用了使用策略算法来搜索路径
    SetErrorCode(ErrorCode::ROUTING_ERROR_RESPONSE,
                 "Failed to find route with request!",
                 response->mutable_status());
    return false;
  }
  if (result_nodes.empty()) {// 如果没有找到合适的结果
    SetErrorCode(ErrorCode::ROUTING_ERROR_RESPONSE, "Failed to result nodes!",
                 response->mutable_status());
    return false;
  }
  result_nodes.front().SetStartS(request.waypoint().begin()->s()); // 结果的前项S为请求的开始点
  result_nodes.back().SetEndS(request.waypoint().rbegin()->s()); // 结果的后项S的结束点为请求的结束点

  if (!result_generator_->GeneratePassageRegion( // 根据搜索得到的结果集设置路径
          graph_->MapVersion(), request, result_nodes, topo_range_manager_,
          response)) {
    SetErrorCode(ErrorCode::ROUTING_ERROR_RESPONSE,
                 "Failed to generate passage regions based on result lanes",
                 response->mutable_status());
    return false;
  }
  SetErrorCode(ErrorCode::OK, "Success!", response->mutable_status()); // 设置正确log

  PrintDebugData(result_nodes); // 打印出搜索的结果节点
  return true;
}

SearchRoute模块主要调用ShowRequestInfo,IsReady,Init分别初始化和检验,如果通过了就调用SearchRouteByStrategy函数来利用策略搜索路径,如果最后没有找到合适的结果或者最后结果无法更新路径,则返回错误。所以其中的SearchRouteByStrategy就是比较关键的策略函数。

SearchRouteByStrategy

 
bool Navigator::SearchRouteByStrategy(
    const TopoGraph* graph, const std::vector<const TopoNode*>& way_nodes,
    const std::vector<double>& way_s,
    std::vector<NodeWithRange>* const result_nodes) const { //输入图,节点集,s,目标结果vector
  std::unique_ptr<Strategy> strategy_ptr; // 策略指针
  strategy_ptr.reset(new AStarStrategy(FLAGS_enable_change_lane_in_result)); // 这里装的是AStar算法,所以后面使用的Search也是A*

  result_nodes->clear(); // 清空结果
  std::vector<NodeWithRange> node_vec; // 存储路径的一个容器
  for (size_t i = 1; i < way_nodes.size(); ++i) { // 对于节点集的所有路径
    const auto* way_start = way_nodes[i - 1]; // 路径开始节点
    const auto* way_end = way_nodes[i]; // 路径结束节点
    double way_start_s = way_s[i - 1]; // 路径开始的S
    double way_end_s = way_s[i]; // 路径结束的S

    TopoRangeManager full_range_manager = topo_range_manager_; // 对于该全连接管理器
    black_list_generator_->AddBlackMapFromTerminal(
        way_start, way_end, way_start_s, way_end_s, &full_range_manager); // 黑名单加入全连接管理器

    SubTopoGraph sub_graph(full_range_manager.RangeMap()); // 全连接管理器赋值给sub_graph
    const auto* start = sub_graph.GetSubNodeWithS(way_start, way_start_s); // 获取sub_graph的起始节点
    if (start == nullptr) { // 如果起始节点为空,说明没有找到
      AERROR << "Sub graph node is nullptr, origin node id: "
             << way_start->LaneId() << ", s:" << way_start_s;
      return false;
    }
    const auto* end = sub_graph.GetSubNodeWithS(way_end, way_end_s); // 获取sub_graph的终止节点
    if (end == nullptr) {// 如果终止节点为空,说明没有找到
      AERROR << "Sub graph node is nullptr, origin node id: "
             << way_end->LaneId() << ", s:" << way_end_s;
      return false;
    }

    std::vector<NodeWithRange> cur_result_nodes; // 结果集
    if (!strategy_ptr->Search(graph, &sub_graph, start, end,
                              &cur_result_nodes)) { // 使用A*算法来搜索路径
      AERROR << "Failed to search route with waypoint from " << start->LaneId()
             << " to " << end->LaneId();
      return false;
    }

    node_vec.insert(node_vec.end(), cur_result_nodes.begin(),
                    cur_result_nodes.end()); // 节点集放入上一个节点的结束,当前路径的开始和结束
  }

  if (!MergeRoute(node_vec, result_nodes)) { // 这段的路径结合起来
    AERROR << "Failed to merge route.";
    return false;
  }
  return true;
}

该函数主要目标是完成输入图以及节点集,得到搜索路径。对于节点集的每一对节点请求都需要做路径的规划,最后把路径之间merge起来。其中使用到了strategy中的算法A*来计算前后的路径。注:前后的节点对都来源于request请求。所以在求得了多个单条路径之后需要merge。

MergeRoute

bool Navigator::MergeRoute(
    const std::vector<NodeWithRange>& node_vec,
    std::vector<NodeWithRange>* const result_node_vec) const {  // 输入为路径集合,目标结果集合
  for (const auto& node : node_vec) { // 对于所有的路径集合
    if (result_node_vec->empty() ||
        result_node_vec->back().GetTopoNode() != node.GetTopoNode()) { // 如果结果集合为空(第一个循环)或者结果的最后一个节点不等于此次节点
      result_node_vec->push_back(node);// 把节点merge进去
    } else { // 说明节点重复了
      if (result_node_vec->back().EndS() < node.StartS()) { // 已有结果node节点的结束时的S与此时的S之间还有距离,说明已经有间断
        AERROR << "Result route is not coninuous";
        return false;
      } else { // 说明虽然节点重复了,但是两个节点是重叠的,所以合并成一个节点。这样让结果节点的back等于此时节点的结束。
        result_node_vec->back().SetEndS(node.EndS());
      }
    }
  }
  return true;
}

MergeRoute的主要工作就是把得到的路径,一段一段的连接在一起。连接的时候注意是否具有重复和间隔,最后得到的就是首尾可以连接的路径。

Navigator总结

Navigator相对条理比较清晰,首先是一些辅助的判断函数作为request的判断,如果一切检验合格后就会根据request的节点对来调用决策函数,最后把每一次计算得到的节点对路径merge起来得到最终的路径计算,返回回去。

猜你喜欢

转载自blog.csdn.net/a15082671703/article/details/80051756
今日推荐