【Apollo自動駕駛原始碼解讀】車道線的感知和高精地圖融合

鈴靈狗發表於2022-03-10

模式選擇

modules/map/relative_map/conf/relative_map_config.pb.txt檔案中對模式進行修改:

lane_source: OFFLINE_GENERATED

存在兩種模式:OFFLINE_GENERATEDPERCPTION
當使用前者時 高精地圖和感知進行融合
使用後者時 只是用感知生成相對地圖
本文只討論前者

函式入口

檔案路徑:modules/map/relative_map/navigation_lane.cc
函式名:bool NavigationLane::GeneratePath() 這個是重點分析的函式
下面進入這個函式

邏輯分析

在這裡進行了模式檢查:

if (config_.lane_source() == NavigationLaneConfig::OFFLINE_GENERATED && navigation_line_num > 0)

現在進入這個if中

for (int i = 0; i < navigation_line_num; ++i) {
      auto current_navi_path = std::make_shared<NavigationPath>();
      auto *path = current_navi_path->mutable_path();
      if (ConvertNavigationLineToPath(i, path)) {
        current_navi_path->set_path_priority(
            navigation_info_.navigation_path(i).path_priority());
        navigation_path_list_.emplace_back(
            i, default_left_width_, default_right_width_, current_navi_path);
      }
    }

這裡根據導航資料對 navigation_path_list_ 進行了填充 接下來馬上進行判斷:

if (navigation_path_list_.empty()) {
      generate_path_on_perception_func();
      return true;
    }

如果沒有資料 只能使用感知了
如果資料不為空 我們繼續
這裡對導航路線進行排序 還有設定當前車輛所在路徑
其中 current_navi_path_tuple_ 這個變數記錄了當前車所在的道路的索引 下面我們將這個變數稱為當前車道

//根據車的方向 將導航路線從左到右排序
    navigation_path_list_.sort(
        [](const NaviPathTuple &left, const NaviPathTuple &right) {
          double left_y = std::get<3>(left)->path().path_point(0).y();
          double right_y = std::get<3>(right)->path().path_point(0).y();
          return left_y > right_y;
        });

    // Get which navigation path the vehicle is currently on.
    double min_d = std::numeric_limits<double>::max();
    for (const auto &navi_path_tuple : navigation_path_list_) {
      int current_line_index = std::get<0>(navi_path_tuple);
      ADEBUG << "Current navigation path index is: " << current_line_index;
      double current_d = std::numeric_limits<double>::max();
      auto item_iter = last_project_index_map_.find(current_line_index);
      if (item_iter != last_project_index_map_.end()) {
        current_d = item_iter->second.second;
      }
      if (current_d < min_d) {
        min_d = current_d;
        //設定當前車輛所在路徑
        current_navi_path_tuple_ = navi_path_tuple;
      }
    }

接下來 將當前車道進行融合 而不是將所有車道進行融合!

//融合感知和導航車道線
    // Merge current navigation path where the vehicle is located with perceived
    // lane markers.
    auto *path = std::get<3>(current_navi_path_tuple_)->mutable_path();
    MergeNavigationLineAndLaneMarker(std::get<0>(current_navi_path_tuple_),
                                     path);

進入融合函式

void NavigationLane::MergeNavigationLineAndLaneMarker(const int line_index, common::Path *const path) 

進入之後直接進行判斷
如果點的數量少於2 則首先生成導航的車道線

if (path->path_point_size() < 2) {
    path->Clear();
    ConvertNavigationLineToPath(line_index, path);
  }

如果還是點的數量過少 那隻能使用感知的了 並直接return 不用融合了

  //如果路徑點還是小於2 則根據感知生成導航車道線
  // If the size of "path" points is still smaller than 2, just generate a
  // navigation path based on perceived lane markers.
  if (path->path_point_size() < 2) {
    path->Clear();
    ConvertLaneMarkerToPath(perception_obstacles_.lane_marker(), path);
    return;
  }

  common::Path lane_marker_path;
  ConvertLaneMarkerToPath(perception_obstacles_.lane_marker(),
                          &lane_marker_path);

  // If the size of lane marker path points is smaller than 2, merging is not
  // required.
  if (lane_marker_path.path_point_size() < 2) {
    return;
  }

下面進行融合
前提是隻對當前車道進行車道線的融合 因為傳入的就只是當前車道
融合原理大概是預先設定一個權重 目前感覺這個權重應該是不變的 這裡存個疑
然後遍歷導航中這條線每個點 在導航中找到這個點對應的點 根據權重進行融合
程式碼展示

int lane_marker_index = 0;
  double navigation_line_weight = 1.0 - config_.lane_marker_weight();
  for (int i = 0; i < path->path_point_size(); ++i) {
    auto *point = path->mutable_path_point(i);
    double s = point->s();
    //計算s距離之後的匹配點 並返回匹配點的索引
    //lane_marker_path為感知發來的道路
    auto lane_maker_point = GetPathPointByS(lane_marker_path, lane_marker_index,
                                            s, &lane_marker_index);
    // For the beginning and ending portions of a navigation path beyond the
    // perceived path, only the y-coordinates in the FLU coordinate system are
    // used for merging.
    const int marker_size = lane_marker_path.path_point_size();
    if (lane_marker_index < 0 || lane_marker_index > (marker_size - 1)) {
      point->set_y(navigation_line_weight * point->y() +
                   (1 - navigation_line_weight) * lane_maker_point.y());
      lane_marker_index = 0;
      continue;
    }
    *point = common::util::GetWeightedAverageOfTwoPathPoints(
        *point, lane_maker_point, navigation_line_weight,
        1 - navigation_line_weight);
  }

然後回到之前的函式
下面就是各種設定寬度了
先提取了感知的道路寬度
遍歷導航中所有的道路(這裡不僅僅是處理當前道路)
如果遍歷到了當前道路 就設定為感知到的道路寬度
程式碼展示

	//設定導航道路的寬度
    // Set the width for the navigation path which the vehicle is currently on.
	//注意感知車道線寬度是在這裡儲存的
    double left_width = perceived_left_width_ > 0.0 ? perceived_left_width_
                                                    : default_left_width_;
    double right_width = perceived_right_width_ > 0.0 ? perceived_right_width_
                                                      : default_right_width_;
    if (!IsFloatEqual(left_width, default_left_width_) &&
        !IsFloatEqual(right_width, default_right_width_)) {
      left_width = left_width > default_left_width_ ? left_width - min_d
                                                    : left_width + min_d;
      right_width = right_width > default_right_width_ ? right_width - min_d
                                                       : right_width + min_d;
    }

    ADEBUG << "The left width of current lane is: " << left_width
           << " and the right width of current lane is: " << right_width;
	
    std::get<1>(current_navi_path_tuple_) = left_width;
    std::get<2>(current_navi_path_tuple_) = right_width;
    auto curr_navi_path_iter = std::find_if(
        std::begin(navigation_path_list_), std::end(navigation_path_list_),
        [this](const NaviPathTuple &item) {
          return std::get<0>(item) == std::get<0>(current_navi_path_tuple_);
        });
    if (curr_navi_path_iter != std::end(navigation_path_list_)) {
      std::get<1>(*curr_navi_path_iter) = left_width;
      std::get<2>(*curr_navi_path_iter) = right_width;
    }
    // Set the width between each navigation path and its adjacent path.
    // The reason for using average of multiple points is to prevent too much
    // interference from a singularity.
    // If current navigation path is the path which the vehicle is currently
    // on, the current lane width uses the perceived width.
    //設定每個導航路徑與其相鄰路徑之間的寬度。
    //使用多個點的平均值是為了防止奇點產生過多干擾。
    //如果當前導航路徑是車輛當前所在的路徑,則當前車道寬度使用感知寬度。
    int average_point_size = 5;
    for (auto iter = navigation_path_list_.begin();
         iter != navigation_path_list_.end(); ++iter) {
      const auto &curr_path = std::get<3>(*iter)->path();

      // Left neighbor
      auto prev_iter = std::prev(iter);
      if (prev_iter != navigation_path_list_.end()) {
        const auto &prev_path = std::get<3>(*prev_iter)->path();
        average_point_size = std::min(
            average_point_size,
            std::min(curr_path.path_point_size(), prev_path.path_point_size()));
        double lateral_distance_sum = 0.0;
        for (int i = 0; i < average_point_size; ++i) {
          lateral_distance_sum +=
              fabs(curr_path.path_point(i).y() - prev_path.path_point(i).y());
        }
        double width = lateral_distance_sum /
                       static_cast<double>(average_point_size) / 2.0;
        width = common::math::Clamp(width, config_.min_lane_half_width(),
                                    config_.max_lane_half_width());

        auto &curr_left_width = std::get<1>(*iter);
        auto &prev_right_width = std::get<2>(*prev_iter);
        if (std::get<0>(*iter) == std::get<0>(current_navi_path_tuple_)) {
          prev_right_width = 2.0 * width - curr_left_width;
        } else {
          curr_left_width = width;
          prev_right_width = width;
        }
      }
      // Right neighbor
      auto next_iter = std::next(iter);
      if (next_iter != navigation_path_list_.end()) {
        const auto &next_path = std::get<3>(*next_iter)->path();
        average_point_size = std::min(
            average_point_size,
            std::min(curr_path.path_point_size(), next_path.path_point_size()));
        double lateral_distance_sum = 0.0;
        for (int i = 0; i < average_point_size; ++i) {
          lateral_distance_sum +=
              fabs(curr_path.path_point(i).y() - next_path.path_point(i).y());
        }
        double width = lateral_distance_sum /
                       static_cast<double>(average_point_size) / 2.0;
        width = common::math::Clamp(width, config_.min_lane_half_width(),
                                    config_.max_lane_half_width());

        auto &curr_right_width = std::get<2>(*iter);
        auto &next_left_width = std::get<1>(*next_iter);
        if (std::get<0>(*iter) == std::get<0>(current_navi_path_tuple_)) {
          next_left_width = 2.0 * width - curr_right_width;
        } else {
          next_left_width = width;
          curr_right_width = width;
        }
      }

總結

如果權重是動態變化的還請大家評論糾正我
感覺apollo的車道線也就是高精地圖和感知通過權重進行融合 沒有很特別很精妙的演算法 如有不對之處還請指教

相關文章