[Apollo 연구 노트] - 라우팅 모듈

라우팅 모듈 기능

아폴로의 라우팅 모듈은 고정밀 지도의 원본 정보를 읽어 입력 RoutingRequest정보 base_map에 따라 내비게이션 트랙의 시작점과 끝점으로 가장 근접한 일치점을 선택하고 , 기준에 따라 base_map생성된 routing_map동작을 읽는 데 사용됩니다. topo_graph, 그런 다음 Astar 알고리즘을 사용하여 토폴로지 맵에서 검색하고 연결합니다. 시작점까지의 최적 경로를 RoutingResponse출력으로 보냅니다.

라우팅 모듈의 주요 단계

지도에서 두 지점 사이의 최단 거리를 찾을 때 도로 교차점(분기점)을 노드(Node)로, 도로의 길이를 가장자리(Edge)로 사용하고 최단 경로 검색 알고리즘(Astar)을 사용할 수 있습니다. /BFS/DFS... ), 두 지점 사이의 최단 경로를 찾아 출력합니다.

일반적으로 우리가 얻은 지도 형식은 최단 경로 탐색에 적합한 위상 형식이 아니므로 지도를 변환해야 합니다. 따라서 라우팅 모듈은 다음 단계로 요약할 수 있습니다.

  1. 지도, 노드 및 도로 정보의 원시 데이터를 가져옵니다.
  2. 위의 정보로 방향 그래프를 구성하십시오.
  3. 최단 경로 알고리즘을 사용하여 두 지점 사이의 최단 거리를 찾습니다.

TopoCreater 지도 작성

Apollo의 매핑으로 구성된 토폴로지 그래프의 노드는 위에서 언급한 전통적인 노드와 일치하지 않습니다. 자율 주행을 위해서는 도로 건설이 차선 수준에 도달해야 하며 도로를 가장자리로, 교차로를 노드로 사용하는 기존 방식이 적용되지 않습니다.

Apollo는 다음 정의 방법을 채택합니다. 점은 차선을 나타내고 가장자리는 차선 간의 상대적 관계입니다 . 아래 그림과 같습니다.
여기에 이미지 설명 삽입

아래 그림은 Apollo에서 도로와 차선의 개념을 보여줍니다.
여기에 이미지 설명 삽입

출처: https://zhuanlan.zhihu.com/p/65533164

Apollo에서 Node와 Edge는 modules/routing/proto/topo_graph.proto에 있는 protobuf에서 정의됩니다. 여기에 이미지 설명 삽입
출구 : 차선의 점선에 해당하는 부분 또는 사용자가 정의한 차선 변경이 가능한 도로 구간 도로 구간
비용 : 속도 제한 또는 회전 도로 구간은 비용이 증가하며 비용 계수는 Centerlinerouting_config.pb.txt 에서 정의 : 가상, 기준선 생성에 사용
여기에 이미지 설명 삽입

매핑 프로세스

지도 작성을 위한 코드는 routing/topo_creator아래와 같으며 파일 구조는 다음과 같습니다.

.
├── BUILD
├── edge_creator.cc            // 建边 
├── edge_creator.h
├── graph_creator.cc           // 建图
├── graph_creator.h
├── graph_creator_test.cc
├── node_creator.cc            // 建节点
├── node_creator.h
└── topo_creator.cc           // main函数

이 블로그( https://zhuanlan.zhihu.com/p/65533164 ) 의 구체적인 프로세스와 코드 설명이 아주 자세하게 설명되어 있습니다.

PS1 : apollo 지도 읽기에는 두 가지 형식이 있습니다. 하나는 OpendriveAdapter에서 읽는 Opendrive 형식이고 다른 하나는 apollo 자체에서 정의한 형식입니다.
PS2 : routing_map 파일, txt와 bin 두 가지 형식이 있습니다.
PS3 : 생성된 그래프의 흐름을 요약하고, 먼저 base_map에서 도로 정보를 읽은 다음 도로를 횡단하고, 먼저 노드를 생성한 다음 노드의 가장자리를 생성한 다음 그래프 변환(포인트와 에지의 정보)은 routing_map에 저장되므로 routing_map은 grap_protobuf 형식의 고체화이며 나중에 라우팅 모듈은 생성된 routing_map을 읽고 경로 계획에 Astar 알고리즘을 사용합니다.

노드 생성

노드 생성 프로세스는 주로 GetPbNode()다음 함수에서 구현됩니다.

void GetPbNode(const hdmap::Lane& lane, const std::string& road_id,
               const RoutingConfig& routingconfig, Node* const node) {
    
    
  // 1. 初始化节点信息
  InitNodeInfo(lane, road_id, node);
  // 2. 初始化节点代价
  InitNodeCost(lane, routingconfig, node);
}

노드 정보 초기화

void InitNodeInfo(const Lane& lane, const std::string& road_id,
                  Node* const node) {
    
    
  double lane_length = GetLaneLength(lane); // 长度
  node->set_lane_id(lane.id().id());  // 车道ID 
  node->set_road_id(road_id);   // 道路ID
  // 根据lane的边界,添加能够变道的路段
  AddOutBoundary(lane.left_boundary(), lane_length, node->mutable_left_out());
  AddOutBoundary(lane.right_boundary(), lane_length, node->mutable_right_out());
  node->set_length(lane_length);
  node->mutable_central_curve()->CopyFrom(lane.central_curve());
  node->set_is_virtual(true);
  if (!lane.has_junction_id() ||
      lane.left_neighbor_forward_lane_id_size() > 0 ||
      lane.right_neighbor_forward_lane_id_size() > 0) {
    
    
    node->set_is_virtual(false);
  }
}

노드 비용 초기화: 도로 길이 및 제한 속도를 기준으로 비용 계산

void InitNodeCost(const Lane& lane, const RoutingConfig& routing_config,
                  Node* const node) {
    
    
  double lane_length = GetLaneLength(lane);
  double speed_limit =
      lane.has_speed_limit() ? lane.speed_limit() : routing_config.base_speed();
  double ratio = speed_limit >= routing_config.base_speed()
                     ? std::sqrt(routing_config.base_speed() / speed_limit)
                     : 1.0;
  // 1. 根据道路长度和速度限制来计算代价
  double cost = lane_length * ratio;
  if (lane.has_turn()) {
    
    
    // left_turn_penalty: 50.0
    // right_turn_penalty: 20.0
    // uturn_penalty: 100.0
    if (lane.turn() == Lane::LEFT_TURN) {
    
    
      cost += routing_config.left_turn_penalty();
    } else if (lane.turn() == Lane::RIGHT_TURN) {
    
    
      cost += routing_config.right_turn_penalty();
    } else if (lane.turn() == Lane::U_TURN) {
    
    
      cost += routing_config.uturn_penalty();
    }
  }
  node->set_cost(cost);
}

가장자리 만들기

가장자리를 만드는 과정은 다음 기능에 있습니다 GetPbEdge().

void GetPbEdge(const Node& node_from, const Node& node_to,
               const Edge::DirectionType& type,
               const RoutingConfig& routing_config, Edge* edge) {
    
    
  // 设置起始,终止车道和类型
  edge->set_from_lane_id(node_from.lane_id());
  edge->set_to_lane_id(node_to.lane_id());
  edge->set_direction_type(type); 

  // 默认代价为0,即直接向前开的代价
  edge->set_cost(0.0);
  if (type == Edge::LEFT || type == Edge::RIGHT) {
    
    
    const auto& target_range =
        (type == Edge::LEFT) ? node_from.left_out() : node_from.right_out();
    double changing_area_length = 0.0;
    for (const auto& range : target_range) {
    
    
      changing_area_length += range.end().s() - range.start().s();
    }
    // 计算代价
    double ratio = 1.0;
    // base_changing_length: 50.0
    if (changing_area_length < routing_config.base_changing_length()) {
    
    
      ratio = std::pow(
          changing_area_length / routing_config.base_changing_length(), -1.5);
    }
    edge->set_cost(routing_config.change_penalty() * ratio);
  }
}

라우팅 구성 요소

Apollo의 각 모듈은 사이버에 의존하여 관련 정보를 등록하며, 각 모듈을 보기 위해서는 컴포넌트 파일부터 시작해야 합니다.

routing_component.h

#pragma once
#include <memory>
#include "modules/routing/routing.h"

namespace apollo {
    
    
namespace routing {
    
    

class RoutingComponent final
    : public ::apollo::cyber::Component<RoutingRequest> {
    
    
 public:
  // default用来控制默认构造函数的生成。显式地指示编译器生成该函数的默认版本。
  RoutingComponent() = default;
  ~RoutingComponent() = default;

 public:
  bool Init() override;
  // 收到routing request的时候触发
  bool Proc(const std::shared_ptr<RoutingRequest>& request) override;

 private:
  // routing消息发布handle
  std::shared_ptr<::apollo::cyber::Writer<RoutingResponse>> response_writer_ =
      nullptr;
  std::shared_ptr<::apollo::cyber::Writer<RoutingResponse>>
      response_history_writer_ = nullptr;
  // Routing类
  Routing routing_;
  std::shared_ptr<RoutingResponse> response_ = nullptr;
  // 定时器
  std::unique_ptr<::apollo::cyber::Timer> timer_;
  // 锁
  std::mutex mutex_;
};
// 在cyber框架中注册routing模块
CYBER_REGISTER_COMPONENT(RoutingComponent)

}  // namespace routing
}  // namespace apollo

routing_component.cc

#include "modules/routing/routing_component.h"
#include <utility>
#include "modules/common/adapters/adapter_gflags.h"
#include "modules/routing/common/routing_gflags.h"

DECLARE_string(flagfile);

namespace apollo {
    
    
namespace routing {
    
    

bool RoutingComponent::Init() {
    
    
  // 加载并检查config配置文件
  // 消息类型定义在modules/routing/proto/routing_config.proto中
  RoutingConfig routing_conf;  
  ACHECK(cyber::ComponentBase::GetProtoConfig(&routing_conf))
      << "Unable to load routing conf file: "
      << cyber::ComponentBase::ConfigFilePath();

  AINFO << "Config file: " << cyber::ComponentBase::ConfigFilePath()
        << " is loaded.";

  // 设置消息qos,控制流量,创建消息发布response_writer_
  apollo::cyber::proto::RoleAttributes attr;
  attr.set_channel_name(routing_conf.topic_config().routing_response_topic());
  auto qos = attr.mutable_qos_profile();
  qos->set_history(apollo::cyber::proto::QosHistoryPolicy::HISTORY_KEEP_LAST);
  qos->set_reliability(
      apollo::cyber::proto::QosReliabilityPolicy::RELIABILITY_RELIABLE);
  qos->set_durability(
      apollo::cyber::proto::QosDurabilityPolicy::DURABILITY_TRANSIENT_LOCAL);
  response_writer_ = node_->CreateWriter<RoutingResponse>(attr);

  // 历史消息发布,和response_writer_类似
  apollo::cyber::proto::RoleAttributes attr_history;
  attr_history.set_channel_name(
      routing_conf.topic_config().routing_response_history_topic());
  auto qos_history = attr_history.mutable_qos_profile();
  qos_history->set_history(
      apollo::cyber::proto::QosHistoryPolicy::HISTORY_KEEP_LAST);
  qos_history->set_reliability(
      apollo::cyber::proto::QosReliabilityPolicy::RELIABILITY_RELIABLE);
  qos_history->set_durability(
      apollo::cyber::proto::QosDurabilityPolicy::DURABILITY_TRANSIENT_LOCAL);
  response_history_writer_ = node_->CreateWriter<RoutingResponse>(attr_history);
  
  // 创建定时器
  std::weak_ptr<RoutingComponent> self =
      std::dynamic_pointer_cast<RoutingComponent>(shared_from_this());
  timer_.reset(new ::apollo::cyber::Timer(
      FLAGS_routing_response_history_interval_ms,
      [self, this]() {
    
    
        auto ptr = self.lock();
        if (ptr) {
    
    
          std::lock_guard<std::mutex> guard(this->mutex_);
          if (this->response_ != nullptr) {
    
    
            auto timestamp = apollo::cyber::Clock::NowInSeconds();
            response_->mutable_header()->set_timestamp_sec(timestamp);
            this->response_history_writer_->Write(*response_);
          }
        }
      },
      false));
  timer_->Start();

  // 执行Routing类
  return routing_.Init().ok() && routing_.Start().ok();
}

// 接收"RoutingRequest"消息,输出"RoutingResponse"响应。
bool RoutingComponent::Proc(const std::shared_ptr<RoutingRequest>& request) {
    
    
  auto response = std::make_shared<RoutingResponse>();
  // 响应routing_请求
  if (!routing_.Process(request, response.get())) {
    
    
    return false;
  }
  // 填充响应头部信息,并且发布
  common::util::FillHeader(node_->Name(), response.get());
  response_writer_->Write(response);
  {
    
    
    std::lock_guard<std::mutex> guard(mutex_);
    response_ = std::move(response);
  }
  return true;
}

}  // namespace routing
}  // namespace apollo

라우팅 구성 요소의 일반적인 기능을 이해한 후 라우팅의 특정 구현을 볼 수 있습니다.

라우팅 특정 구현

라우팅의 특정 구현은 routing.h및 에 있습니다 routing.cc.

라우팅 모듈의 정보 입력은 고정밀도 원본 지도정보(base_map)와 생성된 토폴로지 지도(routing_map), 외부입력 기점요청정보(RoutingRequest)의 두 가지 고정정보를 포함한다. 경로 탐색 결과를 출력합니다 .

라우팅 모듈 초기화

먼저 Routing의 초기화 기능을 살펴보십시오.

// 初始化函数
apollo::common::Status Routing::Init() {
    
    
  // 读取拓扑地图routing_map的文件位置信息
  const auto routing_map_file = apollo::hdmap::RoutingMapFile();
  AINFO << "Use routing topology graph path: " << routing_map_file;
  // 在Navigator类加载指定的graph图
  navigator_ptr_.reset(new Navigator(routing_map_file));

  // 通过map模块提供的功能包,读取原始地图信息,即包括点和边的信息
  // 据此查找routing request请求的点距离最近的lane,并且返回对应的lane id.
  hdmap_ = apollo::hdmap::HDMapUtil::BaseMapPtr();
  ACHECK(hdmap_) << "Failed to load map file:" << apollo::hdmap::BaseMapFile();

  return apollo::common::Status::OK();
}

네비게이터 초기화

라우팅은 Navigator내부적으로 경로를 검색합니다. 검색 경로가 필요하기 때문에 Navigator완전한 토폴로지 맵이 필요합니다.

Navigator::Navigator(const std::string& topo_file_path) {
    
    
  Graph graph;
  if (!cyber::common::GetProtoFromFile(topo_file_path, &graph)) {
    
    
    AERROR << "Failed to read topology graph from " << topo_file_path;
    return;
  }

  graph_.reset(new TopoGraph());
  if (!graph_->LoadGraph(graph)) {
    
    
    AINFO << "Failed to init navigator graph failed! File path: "
          << topo_file_path;
    return;
  }
  black_list_generator_.reset(new BlackListRangeGenerator);
  result_generator_.reset(new ResultGenerator);
  is_ready_ = true;
  AINFO << "The navigator is ready.";
}

생성자 에서 Navigator토폴로지 맵이 로드됩니다. BlackListRangeGenerator동시에 두 클래스 (블랙리스트 섹션 생성) 및 ResultGenerator(결과 생성) 의 개체 가 초기화됩니다 .

bool Navigator::Init(const RoutingRequest& request, const TopoGraph* graph,
                     std::vector<const TopoNode*>* const way_nodes,
                     std::vector<double>* const way_s) {
    
    
  Clear();
  // 获取routing请求,对应图中的节点
  if (!GetWayNodes(request, graph_.get(), way_nodes, way_s)) {
    
    
    AERROR << "Failed to find search terminal point in graph!";
    return false;
  }
    // 根据请求生成对应的黑名单lane
  black_list_generator_->GenerateBlackMapFromRequest(request, graph_.get(),
                                                     &topo_range_manager_);
  return true;
}

라우팅 요청에서 이러한 차선을 계산하지 않도록 블랙리스트 도로 및 차선을 라우팅 요청에 지정할 수 있습니다.

라우팅 프로세스 주요 프로세스

Process의 주요 프로세스를 실행하는 프로세스는 다음과 같습니다.

bool Routing::Process(const std::shared_ptr<RoutingRequest>& routing_request,
                      RoutingResponse* const routing_response) {
    
    
  CHECK_NOTNULL(routing_response);
  AINFO << "Get new routing request:" << routing_request->DebugString();
  
  // 找到routing_request节点最近的路
  // FillLaneInfoIfMissing从地图中选取最佳匹配点
  const auto& fixed_requests = FillLaneInfoIfMissing(*routing_request);
  double min_routing_length = std::numeric_limits<double>::max();
  for (const auto& fixed_request : fixed_requests) {
    
    
    RoutingResponse routing_response_temp;
    // 是否能够找到规划路径
    if (navigator_ptr_->SearchRoute(fixed_request, &routing_response_temp)) {
    
    
      const double routing_length =
          routing_response_temp.measurement().distance();
      if (routing_length < min_routing_length) {
    
    
        routing_response->CopyFrom(routing_response_temp);
        min_routing_length = routing_length;
      }
    }
    FillParkingID(routing_response);
  }
  if (min_routing_length < std::numeric_limits<double>::max() &&
      SupplementParkingRequest(routing_response)) {
    
    
    monitor_logger_buffer_.INFO("Routing success!");
    return true;
  }

  AERROR << "Failed to search route with navigator.";
  monitor_logger_buffer_.WARN("Routing failed! " +
                              routing_response->status().msg());
  return false;
}

FillLaneInfoIfMissing자세한 설명은 이 블로그 게시물 https://zhuanlan.zhihu.com/p/459954010 에 설명되어 있습니다.

네비게이터 주요 기능

네비게이터 자체는 경로 검색 알고리즘을 구현하지 않습니다. 라우팅 경로의 검색 프로세스를 완료하기 위해 다른 클래스를 사용합니다. 주요 기능은 다음과 같습니다 SearchRoute(이 블로그에서 자세히 설명함 https://paul.pub/apollo-routing/#id-routing_configproto ).
주로 다음 작업을 완료합니다:
1. 요청 매개변수 확인
2. 준비 상태인지 확인
3. 요청에 필요한 매개변수 초기화
4. 검색 알고리즘 실행
5. 검색 결과 수집

SearchRoute핵심은 이 블로그 SearchRouteByStrategy자세한 설명이 있습니다 https://zhuanlan.zhihu.com/p/65533164 주로
다음 작업을 완료합니다
. NodeSRange.검색, 추가, 정렬 및 병합 가능), 블랙리스트 추가, 시작점과 끝점에 따라 차선 분할, 하위 그래프 생성 (검색 알고리즘에서 사용), 시작점과 끝점 가져오기, AStar 경로를 통해 최상, 결과를 3에 저장. 경로 결과 병합
routing_requestTopoRangeManagerAddBlackMapFromTerminalSubTopoGraphnode_vec

AStar전략

Navigator::SearchRoute메서드는 SearchRouteByStrategy클래스 자체의 메서드를 호출합니다. 이 방법에서 AStarStrategy경로 검색은 의 도움으로 수행됩니다.

AStarStrategy클래스는 추상 클래스 Strategy하위 클래스입니다. 즉, AStar는 다른 알고리즘을 구현하여 대체할 수 있습니다.

AStar의 기본 원리는 다음 블로그를 참조할 수 있습니다: https://blog.csdn.net/sinat_52032317/article/details/127077625

AStar의 구체적인 구현 프로세스는 이 블로그 https://zhuanlan.zhihu.com/p/459954010
및 이
https://www.jianshu.com/p/2ee0d6b19b5f 를 참조하십시오.

// 输入:起点、终点、读取的拓扑地图以及根据起点终点生成的子拓扑图、到起点到达终点的点集:
bool AStarStrategy::Search(const TopoGraph* graph,
                           const SubTopoGraph* sub_graph,
                           const TopoNode* src_node, const TopoNode* dest_node,
                           std::vector<NodeWithRange>* const result_nodes) {
    
    
  Clear();
  AINFO << "Start A* search algorithm.";
  // 该优先列表将f最小的值作为最优点
  std::priority_queue<SearchNode> open_set_detail;
  // 将地图选取的起点作为搜索的第一个点,计算起点到终点的代价值(曼哈顿距离)并作为f
  SearchNode src_search_node(src_node);
  src_search_node.f = HeuristicCost(src_node, dest_node);
  // 将该点加入到开放列表之中
  open_set_detail.push(src_search_node);
  open_set_.insert(src_node);
  g_score_[src_node] = 0.0;
  enter_s_[src_node] = src_node->StartS();
  // 定义当前节点
  SearchNode current_node;
  std::unordered_set<const TopoEdge*> next_edge_set;
  std::unordered_set<const TopoEdge*> sub_edge_set;
  while (!open_set_detail.empty()) {
    
    
    // 若open集非空,选取最优的为当前节点,开始搜索
    current_node = open_set_detail.top();
    const auto* from_node = current_node.topo_node;
    // 若当前点等于目标点,结束搜索
    if (current_node.topo_node == dest_node) {
    
    
      // ReconstructL:从终点到起点进行反向搜索,获取轨迹点
      if (!Reconstruct(came_from_, from_node, result_nodes)) {
    
    
        AERROR << "Failed to reconstruct route.";
        return false;
      }
      return true;
    }
    // 将当前点从搜索点集中清除
    open_set_.erase(from_node);
    open_set_detail.pop();
    // 跳过closed集中添加的点
    if (closed_set_.count(from_node) != 0) {
    
    
      // if showed before, just skip...
      continue;
    }
    // 将当前点添加到closed集中
    closed_set_.emplace(from_node);

    // if residual_s is less than FLAGS_min_length_for_lane_change, only move
    // forward
    const auto& neighbor_edges =
        (GetResidualS(from_node) > FLAGS_min_length_for_lane_change &&
         change_lane_enabled_)
            ? from_node->OutToAllEdge()
            : from_node->OutToSucEdge();
    double tentative_g_score = 0.0;
    next_edge_set.clear();
    for (const auto* edge : neighbor_edges) {
    
    
      sub_edge_set.clear();
      sub_graph->GetSubInEdgesIntoSubGraph(edge, &sub_edge_set);
      next_edge_set.insert(sub_edge_set.begin(), sub_edge_set.end());
    }

    for (const auto* edge : next_edge_set) {
    
    
      const auto* to_node = edge->ToNode();
      // 跳过closed集中的点
      if (closed_set_.count(to_node) == 1) {
    
    
        continue;
      }
      // 跳过不能换到到达的点
      if (GetResidualS(edge, to_node) < FLAGS_min_length_for_lane_change) {
    
    
        continue;
      }
      // 将当前节点的g值和能够达到的点的cost值相加
      // GetCostToNeighbor返回相邻节点的cost和边edge的cost
      tentative_g_score =
          g_score_[current_node.topo_node] + GetCostToNeighbor(edge);
      // 若下一点需要换道才能到达,cost会减少部分值
      if (edge->Type() != TopoEdgeType::TET_FORWARD) {
    
    
        tentative_g_score -=
            (edge->FromNode()->Cost() + edge->ToNode()->Cost()) / 2;
      }
      // 计算能够达到点的f值
      double f = tentative_g_score + HeuristicCost(to_node, dest_node);
      if (open_set_.count(to_node) != 0 && f >= g_score_[to_node]) {
    
    
        continue;
      }
      // if to_node is reached by forward, reset enter_s to start_s
      if (edge->Type() == TopoEdgeType::TET_FORWARD) {
    
    
        enter_s_[to_node] = to_node->StartS();
      } else {
    
    
        // else, add enter_s with FLAGS_min_length_for_lane_change
        double to_node_enter_s =
            (enter_s_[from_node] + FLAGS_min_length_for_lane_change) /
            from_node->Length() * to_node->Length();
        // enter s could be larger than end_s but should be less than length
        to_node_enter_s = std::min(to_node_enter_s, to_node->Length());
        // if enter_s is larger than end_s and to_node is dest_node
        if (to_node_enter_s > to_node->EndS() && to_node == dest_node) {
    
    
          continue;
        }
        enter_s_[to_node] = to_node_enter_s;
      }

      g_score_[to_node] = f;
      // 初始化下一节点
      SearchNode next_node(to_node);
      next_node.f = f;
      open_set_detail.push(next_node);
      came_from_[to_node] = from_node;
      // 将能够达到的点加入open集
      if (open_set_.count(to_node) == 0) {
    
    
        open_set_.insert(to_node);
      }
    }
  }
  AERROR << "Failed to find goal lane with id: " << dest_node->LaneId();
  return false;
}

라우팅 모듈 전체 구조

여기에 이미지 설명 삽입

출처: https://paul.pub/apollo-routing/#id-routing_configproto

요약 : Routing 모듈은 고정밀 지도의 원본 정보를 먼저 읽고 TopoCreator생성된 점과 모서리를 사용하여 지형도를 생성합니다. RoutingRequest메시지(일련의 점)를 읽은 후, Navigator클래스는 토폴로지 맵을 사용하고 TopoRangeManagerAStar 알고리즘을 사용하여 토폴로지 맵에서 시작점을 연결하는 최적의 경로를 찾아 RoutingResponse출력으로 내보냅니다.

참고

[1] 아폴로가 소개한 라우팅 모듈 (6) https://zhuanlan.zhihu.com/p/65533164
[2] Apollo Spark 프로젝트 연구 노트 - 자율주행 계획 기술의 강의 7원칙 1 https://blog.csdn .net/sinat_52032317/article/details/128300053#t6
[3] 아폴로 내비게이션 모듈 기록(라우팅 모듈) https://zhuanlan.zhihu.com/p/459954010
[4] 사이버 RT 기본 소개 및 실습 https:/ /apollo .baidu.com/community/article/1093
[5] 시작하기 위한 필수 정보丨 Baidu Apollo의 Routing 모듈 분석 https://mp.weixin.qq.com/s?__biz=MzI1NjkxOTMyNQ== &mid=2247490369&idx=1&sn=3a3f1dafc46782da311a2fc910e609 5a&scene= 21#wechat_redirect
[6] Baidu Apollo의 라우팅 모듈 분석 https://paul.pub/apollo-routing/

추천

출처blog.csdn.net/sinat_52032317/article/details/131859640