国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

(02)Cartographer源碼無死角解析-(42) 2D柵格地圖→Submap、Submap2D、MapLimits

這篇具有很好參考價值的文章主要介紹了(02)Cartographer源碼無死角解析-(42) 2D柵格地圖→Submap、Submap2D、MapLimits。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

講解關(guān)于slam一系列文章匯總鏈接:史上最全slam從零開始,針對于本欄目講解(02)Cartographer源碼無死角解析-鏈接如下:
(02)Cartographer源碼無死角解析- (00)目錄_最新無死角講解:https://blog.csdn.net/weixin_43013761/article/details/127350885
?
文末正下方中心提供了本人 聯(lián)系方式, 點擊本人照片即可顯示 W X → 官方認證 {\color{blue}{文末正下方中心}提供了本人 \color{red} 聯(lián)系方式,\color{blue}點擊本人照片即可顯示W(wǎng)X→官方認證} 文末正下方中心提供了本人聯(lián)系方式,點擊本人照片即可顯示WX官方認證
?

一、前言

在上一篇博客中,對 src/cartographer/cartographer/mapping/2d/submap_2d.cc 文件中的類 ActiveSubmaps2D 各個成員函數(shù)都進行了介紹,其主要功能如下圖所示:
submap分析,# (02)Cartographer源碼無死角解析-免費,Cartographer,自動駕駛,無人機,增強現(xiàn)實,機器人
通過調(diào)用 ActiveSubmaps2D::InsertRangeData() 函數(shù)向子圖 submaps_ 中插入數(shù)據(jù),其會使得兩個連續(xù)的子圖之間的數(shù)據(jù)存在交集。如上圖的子圖1與子圖2存在交集,同時子圖2與子圖3也存在交集。

從類名可以輕易的分辨出,ActiveSubmaps2D 表示激活的子圖,其包含的成員變量 std::vector<std::shared_ptr<Submap2D>> submaps_ 表示的就是目前處于激活狀態(tài)的子圖(通常情況下是兩個子圖),如果 submaps_ 中的第一個子圖插入數(shù)據(jù)足夠了,則會被標記為完成,然后從 submaps_ 中擦除。

Submap2D 與 ActiveSubmaps2D 的成員函數(shù)都是在 src/cartographer/cartographer/mapping/2d/submap_2d.cc 文件中實現(xiàn),既然分析完了 ActiveSubmaps2D,那么就來看看 Submap2D。不過在分析 Submap2D 之前,先要看看其父類 Submap。

在前面的博客中已經(jīng)提及過,Submap2D 繼承于 Submap,Submap 在 src/cartographer/cartographer/mapping/submaps.h 文件中被聲明,主要定義了一些純虛函數(shù),以及一些成員變量。該些成員變量如下:

private:
  const transform::Rigid3d local_pose_; // 子圖原點在local坐標系下的坐標
  int num_range_data_ = 0; //子圖中數(shù)據(jù)的數(shù)目,初始為0
  bool insertion_finished_ = false; //是否為插入完成狀態(tài),初始為否。

由于這些屬性是私有的,所以無法被其派生類 Submap2D 繼承,不過沒有關(guān)系,因為提供了對該些屬性訪問或者操作的 public 接口,如下:

  // Pose of this submap in the local map frame.
  // 在local坐標系的子圖的坐標
  transform::Rigid3d local_pose() const { return local_pose_; }

  // Number of RangeData inserted.
  // 插入到子圖中雷達數(shù)據(jù)的個數(shù)
  int num_range_data() const { return num_range_data_; }
  void set_num_range_data(const int num_range_data) {
    num_range_data_ = num_range_data;
  }

  bool insertion_finished() const { return insertion_finished_; }
  // 將子圖標記為完成狀態(tài)
  void set_insertion_finished(bool insertion_finished) {
    insertion_finished_ = insertion_finished;
  }

另外,需要注意到的是,Submap 的構(gòu)造函數(shù)需要傳入 local_submap_pose 變量,完成對成員變量 local_pose_ 的初始化,其表示子圖在 local 坐標系下的位姿。也就是說,每創(chuàng)建一個子圖,都需要指定好該子圖在 local 坐標系下的位姿。
?

二、Submap2D

1、Submap2D::Submap2D()

Submap2D 繼承于 Submap,其存在兩個私有屬性:

private:
  std::unique_ptr<Grid2D> grid_; // 地圖柵格數(shù)據(jù)

  // 轉(zhuǎn)換表, 第[0-32767]位置, 存的是[0.9, 0.1~0.9]的數(shù)據(jù)
  ValueConversionTables* conversion_tables_;

后續(xù)對于這兩個屬性會進行詳細的分析,關(guān)于 Submap2D 的兩個重載構(gòu)造函數(shù)都會對這兩個屬性進行初始化。其第一個構(gòu)造函數(shù),直接接收 grid 與 conversion_tables 參數(shù),然后利用初始化列表直接賦值給 grid_ 與 conversion_tables_,代碼如下所示:

/**
 * @brief 構(gòu)造函數(shù)
 * 
 * @param[in] origin Submap2D的原點,保存在Submap類里
 * @param[in] grid 地圖數(shù)據(jù)的指針
 * @param[in] conversion_tables 地圖數(shù)據(jù)的轉(zhuǎn)換表
 */
Submap2D::Submap2D(const Eigen::Vector2f& origin, std::unique_ptr<Grid2D> grid,
                   ValueConversionTables* conversion_tables)
    : Submap(transform::Rigid3d::Translation(
          Eigen::Vector3d(origin.x(), origin.y(), 0.))),
      conversion_tables_(conversion_tables) {
  grid_ = std::move(grid);
}

還需要傳遞一個參數(shù) origin,其表示子圖的原點,也是就子圖在 local 坐標系下的位姿。除上述構(gòu)造函數(shù)外,還有另外一個構(gòu)造函數(shù),通過 proto 格式的數(shù)據(jù)構(gòu)建 ProbabilityGrid 或者 TSDF2D 對象指針賦值給 grid_。代碼就不再這里復(fù)制展示了。
?

2、Submap2D::InsertRangeData()

在 Submap2D 中,還有幾個成員函數(shù):Submap2D::ToProto(), Submap2D::UpdateFromProto(),Submap2D::ToResponseProto() 都與 proto 相關(guān),暫時不講解。先來看看看其中另外一個比較重要的函數(shù)Submap2D::InsertRangeData():

I n s e r t R a n g e D a t a ( ) = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = : {\color{Purple} InsertRangeData()======================================================================================================================================================}: InsertRangeData()======================================================================================================================================================:

功能 : {\color{Purple} 功能}: 功能: 把點云數(shù)據(jù)插入到子圖之中

輸入 : {\color{Purple} 輸入}: 輸入: 【參數(shù)①range_data】→需要被插入的點云數(shù)據(jù)?!緟?shù)②range_data_inserter】→負責(zé)數(shù)據(jù)插入的實例對象,為 RangeDataInserterInterface 的派生類。

返回 : {\color{Purple} 返回}: 返回:
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = : {\color{Purple} ================================================================================================================================================================}: ================================================================================================================================================================:

該函數(shù)實際上就是調(diào)用了 range_data_inserter->Insert(range_data, grid_.get()) 函數(shù),將數(shù)據(jù)寫入到柵格地圖 grid_ 之中。該函數(shù)注釋如下:

// 將雷達數(shù)據(jù)寫到柵格地圖中
void Submap2D::InsertRangeData(
    const sensor::RangeData& range_data,
    const RangeDataInserterInterface* range_data_inserter) {
  CHECK(grid_);
  CHECK(!insertion_finished());
  // 將雷達數(shù)據(jù)寫到柵格地圖中
  range_data_inserter->Insert(range_data, grid_.get());
  // 插入到地圖中的雷達數(shù)據(jù)的個數(shù)加1
  set_num_range_data(num_range_data() + 1);
}

從這里還可以看出每插入一幀數(shù)據(jù),num_range_data 才會 +1,因為 range_data 中存儲的并不是一個點云數(shù)據(jù),而是一幀。
?

3、Submap2D::Finish()

該函數(shù)比較簡單,其調(diào)用了 grid_->ComputeCroppedGrid() 函數(shù),該函數(shù)后續(xù)再進行分析,然后設(shè)置 insertion_finished_ 變量,標記當(dāng)前子圖為完成狀態(tài)。
?

三、MapLimits

結(jié)合前面的分析,可以知道 Submap2D 中的 Grid2D 實例對象 grid_ 是十分重要的組成部分,回到之前 ActiveSubmaps2D::AddSubmap() 函數(shù),存在如下代碼:

std::unique_ptr<Grid2D>(static_cast<Grid2D*>(CreateGrid(origin).release()))

由此可知,Grid2D 的構(gòu)建來自于 ActiveSubmaps2D::CreateGrid() 函數(shù),該函數(shù)會構(gòu)建 Grid2D 派生類對象 ProbabilityGrid 或者 TSDF2D 的獨占指針。需要注意,在函數(shù)中可以看到如下代碼:

          MapLimits(resolution,
                    // 左上角坐標為坐標系的最大值, origin位于地圖的中間
                    origin.cast<double>() + 0.5 * kInitialSubmapSize *
                                                resolution *
                                                Eigen::Vector2d::Ones(),
                    CellLimits(kInitialSubmapSize, kInitialSubmapSize)),

也就是是說,無論構(gòu)建 ProbabilityGrid 還是 TSDF2D 實例對象指針,都需要傳入MapLimits 對象作為實參。那么就來看看 MapLimits 代碼中是如何實現(xiàn)的,位于 src/cartographer/cartographer/mapping/2d/map_limits.h 文件中。從命名來看,地圖限制,其是限制了那些東西呢?

首先每個子圖 Submap2D 或者說都對應(yīng)的一個柵格(Grid),后續(xù)每個柵格都會再進一步劃分,劃分之后以以 cell 為單位,如下圖所示,每個小方格都表示一個一個 call:
submap分析,# (02)Cartographer源碼無死角解析-免費,Cartographer,自動駕駛,無人機,增強現(xiàn)實,機器人
既然要把子圖 Submap2D 或者 Grid2D 劃分成 call 形式,那么肯定需要指定每個 Grid2D 應(yīng)該被劃分成多少個 cell。先來看看 MapLimits 的構(gòu)造函數(shù)。

1、MapLimits::MapLimits
  /**
   * @brief 構(gòu)造函數(shù)
   * 
   * @param[in] resolution 地圖分辨率
   * @param[in] max 左上角的坐標為地圖坐標的最大值
   * @param[in] cell_limits 地圖x方向與y方向的格子數(shù)
   */
  MapLimits(const double resolution, const Eigen::Vector2d& max,
            const CellLimits& cell_limits)
      : resolution_(resolution), max_(max), cell_limits_(cell_limits) {
    CHECK_GT(resolution_, 0.);
    CHECK_GT(cell_limits.num_x_cells, 0.);
    CHECK_GT(cell_limits.num_y_cells, 0.);z
  }

該構(gòu)造函數(shù)首先指定了地圖的分辨率,該分辨率表示由 options_.grid_options_2d().resolution() 確定。這里我們約定兩個坐標系,如下:

???????①地圖坐標系→該坐標系以物理單位作為衡量。
???????②像素坐標系→該坐標系以像素為單位

約定了上述兩個坐標系之后,那么所謂的分辨率就表示地圖坐標系與是像素坐標系的比值,簡單的說就是柵格地圖中一個像素代表地圖坐標系多個個物理單位(米)。

第二個參數(shù) max,表示的地圖坐標的最大值,第三個參數(shù) cell_limits 表示每個子圖,或者說每個柵格x,y方向上包含了多少個 cell。
?

2、MapLimits::GetCellIndex()

該函數(shù)從命名可以看出來,其是獲得 cell 在 gred 中的索引。代碼如下所示:

  // Returns the index of the cell containing the 'point' which may be outside
  // the map, i.e., negative or too large indices that will return false for
  // Contains().
  // 計算物理坐標點的像素索引
  Eigen::Array2i GetCellIndex(const Eigen::Vector2f& point) const {
    // Index values are row major and the top left has Eigen::Array2i::Zero()
    // and contains (centered_max_x, centered_max_y). We need to flip and
    // rotate.
    return Eigen::Array2i(
        common::RoundToInt((max_.y() - point.y()) / resolution_ - 0.5),
        common::RoundToInt((max_.x() - point.x()) / resolution_ - 0.5));
  }

傳入的 point 是地圖坐標系的物理單位,計算方式也比較簡單,物理坐標除以分辨率即可,等價于把 地圖坐標 變換成 像素坐標。那么這里為什還要用 max_ 減去 point 呢? 如下所示:

/**
 * note: 地圖坐標系可視化展示
 * ros的地圖坐標系    cartographer的地圖坐標系     cartographer地圖的像素坐標系 
 * 
 * ^ y                            ^ x              0------> x
 * |                              |                |
 * |                              |                |
 * 0 ------> x           y <------0                y       
 * 
 * ros的地圖坐標系: 左下角為原點, 向右為x正方向, 向上為y正方向, 角度以x軸正向為0度, 逆時針為正
 * cartographer的地圖坐標系: 坐標系右下角為原點, 向上為x正方向, 向左為y正方向
 *             角度正方向以x軸正向為0度, 逆時針為正
 * cartographer地圖的像素坐標系: 左上角為原點, 向右為x正方向, 向下為y正方向
 */

其主要原因是因為 cartographer的地圖坐標系 與 cartographer地圖的像素坐標系 是不一樣的,像素坐標的原點是在左上角。根據(jù)對源碼的分析,像素坐標系中的一個像素代表地圖坐標的一個cell。
?

3、MapLimits::GetCellCenter()

該函數(shù)的作用可以與 MapLimits::GetCellIndex() 是相反的,其輸入一個像素索引,然后返回該像素對應(yīng)在地圖坐標系下的物理坐標:

  // Returns the center of the cell at 'cell_index'.
  // 根據(jù)像素索引算物理坐標
  Eigen::Vector2f GetCellCenter(const Eigen::Array2i cell_index) const {
    return {max_.x() - resolution() * (cell_index[1] + 0.5),
            max_.y() - resolution() * (cell_index[0] + 0.5)};
  }

這里返回的是地圖 cell 中心坐標。源碼計算過程還是比較簡單的,就是 MapLimits::GetCellIndex() 的逆操作。
?

4、MapLimits::Contains()

該函數(shù)輸入一個像素坐標索引,其會判斷該像素是否存于柵格地圖內(nèi)部,代碼注釋如下:

  // Returns true if the ProbabilityGrid contains 'cell_index'.
  // 判斷給定像素索引是否在柵格地圖內(nèi)部
  bool Contains(const Eigen::Array2i& cell_index) const {
    return (Eigen::Array2i(0, 0) <= cell_index).all() &&
           (cell_index <
            Eigen::Array2i(cell_limits_.num_x_cells, cell_limits_.num_y_cells))
               .all();
  }

?

四、結(jié)語

關(guān)于 MapLimits 還有一些成員函數(shù)沒有講解,如 MapLimits::ToProto() 不過這已經(jīng)不影響后續(xù)的分析了。再了解了 ActiveSubmaps2D、Submap、Submap2D、MapLimits 之后,接下來就要看一個大頭部分:Grid2D 與 ProbabilityGrid

?
?
?文章來源地址http://www.zghlxwxcb.cn/news/detail-590024.html

到了這里,關(guān)于(02)Cartographer源碼無死角解析-(42) 2D柵格地圖→Submap、Submap2D、MapLimits的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包