1. 程式人生 > 實用技巧 >octomap 3.simple_example.cpp 檔案解析

octomap 3.simple_example.cpp 檔案解析

部落格轉自:灰信網
Octomap庫實現了一種填充3D柵格的構圖方式,並提供了相應的資料結構和構圖演算法。整個地圖利用一個Octree的類來實現。

直接檢視本庫主要的類octomap::OcTree,以及其示例程式src/octomap/simple_example.cpp。如果你想要將單組測量資料整合到3D地圖中,請檢視OcTree::insertRay(…)函式;如果你想要將整個3D掃描資料(點雲)整合到地圖中,請檢視OcTree::insertPointCloud(…)函式。同時可以通過OcTree::search(...)OcTree::castRay(...)函式來進行地圖空間佔用的查詢。推薦使用迭代器的方式對Octree的節點等進行查詢,例如leaf_iterator,tree_iterator

leaf_bbx_iterator

示例程式碼分析

Github原始碼地址:simple_example.cpp

#include <octomap/octomap.h>
#include <octomap/OcTree.h>

using namespace std;
using namespace octomap;


void print_query_info(point3d query, OcTreeNode* node) {
  if (node != NULL) {
    cout << "occupancy probability at " << query << ":\t " << node->getOccupancy() << endl;
  }
  else 
    cout << "occupancy probability at " << query << ":\t is unknown" << endl;    
}

int main(int argc, char** argv) {

  cout << endl;
  cout << "generating example map" << endl;

  OcTree tree (0.1);  // create empty tree with resolution 0.1


  // insert some measurements of occupied cells

  for (int x=-20; x<20; x++) {
    for (int y=-20; y<20; y++) {
      for (int z=-20; z<20; z++) {
        point3d endpoint ((float) x*0.05f, (float) y*0.05f, (float) z*0.05f);
        tree.updateNode(endpoint, true); // integrate 'occupied' measurement
      }
    }
  }

  // insert some measurements of free cells

  for (int x=-30; x<30; x++) {
    for (int y=-30; y<30; y++) {
      for (int z=-30; z<30; z++) {
        point3d endpoint ((float) x*0.02f-1.0f, (float) y*0.02f-1.0f, (float) z*0.02f-1.0f);
        tree.updateNode(endpoint, false);  // integrate 'free' measurement
      }
    }
  }

  cout << endl;
  cout << "performing some queries:" << endl;
  
  point3d query (0., 0., 0.);
  OcTreeNode* result = tree.search (query);
  print_query_info(query, result);

  query = point3d(-1.,-1.,-1.);
  result = tree.search (query);
  print_query_info(query, result);

  query = point3d(1.,1.,1.);
  result = tree.search (query);
  print_query_info(query, result);


  cout << endl;
  tree.writeBinary("simple_tree.bt");
  cout << "wrote example file simple_tree.bt" << endl << endl;
  cout << "now you can use octovis to visualize: octovis simple_tree.bt"  << endl;
  cout << "Hint: hit 'F'-key in viewer to see the freespace" << endl  << endl;  

}

執行該程式碼後的執行結果:

這是用octovis檢視所生成的地圖simple_tree.bt:

下面針對一些關鍵語句進行分析:

OcTree tree(0.1)

建立OcTree地圖物件,並設定八叉樹解析度為0.1米,即訪問地圖的最小單位為0.1米;。

point3d endpoint( (float)x*0.02f-1.0f, (float)y*0.02f-1.0f, (float)z*0.02f-1.0f);
tree.updateNode( endpoint, false );

建立一個三維空間點,並將其新增到地圖當中;point3d建構函式的三個引數分別是三維點的x/y/z座標;使用updateNode()

函式將空間點資訊更新到地圖當中,第二個引數表示該空間點對應的節點未被佔用(false);

OcTreeNode* result = tree.search (query)

節點資訊查詢函式;函式宣告是這樣的

傳入引數為一個三維點物件,如果在這個地圖tree中,這個三維點對應的位置有節點(不管佔用與否),那麼將返回該位置的節點指標,否則將返回一個空指標;

cout << "occupancy probability at " << query<< ":\t " << node->getOccupancy() << endl;

如果查詢到有該位置上節點的資訊,則使用getOccupancy()函式輸出該節點佔用情況,那為什麼這個Occupancy是個小數呢?這是因為Octomap在描述一個柵格是否被佔用時,並不是單一的只描述為佔用和被佔用,而是用一個概率(Occupancy probability)來描述它,即這個柵格被佔用的概率是多少,通過這個概率來確定這個柵格被佔用的可能性。

Occupancy probability

關於這個佔用概率值,高博在一篇部落格中做出了和通俗易懂的解釋,我就不再複製貼上了,下面附上連線半閒居士
我要說的是,對於一個最小單位的柵格,如何判斷updateNode()函式對其做出了貢獻或者說更新。就比如說

point3d endpoint( 4.05f, 4.05f, 4.05f );
tree.updateNode( endpoint, true );

這兩行程式碼,由於解析度是0.1,並不能精確到0.01,所以要把這個(4.05,4.05,4.05)歸到(4.0,4.0,4.0)這個節點呢,還是(4.1,4.1,4.1)這個節點或者其他節點呢?經過我的多次測試,應當歸到(4.0,4.0,4.0)節點當中,也就是說,對於節點(4.0,4.0,4.0)來說,凡是同時滿足x=[4.0,4.1),y=[4.0,4.1),z=[4.0,4.1)的point,如果使用updateNode()函式將這個point更新到地圖當中,那麼必然會影響到節點(4.0,4.0,4.0)的Occupancy probability。
總結就是,對於一個點point(x,y,z),使用updateNode()函式將其更新到地圖當中,那麼Occupancy probability受到影響的節點將是

即小於point座標值的最大節點。同時當我們用search ()函式對point進行查詢時,返回的資訊也將是小於point座標值的最大節點資訊。另外,在對某個節點進行不同次數的更新之後,發現Occupancy probability最大值為0.971,最小值為0.1192,這也驗證了高博那篇博文中提到的最大值和最小值限制。

ColorOcTree

帶有色彩的八叉樹地圖ColorOcTree與基本的OcTree類似,需要知道如何向節點當中新增顏色資訊就好了。下面是test_color_tree.cpp的部分程式碼:

int main(int argc, char** argv)
{
 //解析度
double res = 0.05;  // create empty tree with resolution 0.05(different from default 0.1 for test)
 
//建立彩色地圖物件
 ColorOcTree tree (res);
 // insert some measurements of occupied cells
for (int x=-20; x<20; x++)
{
for (int y=-20; y<20; y++)
{
 for (int z=-20; z<20; z++)
 { 
point3d endpoint ((float)x*0.05f+0.01f, (float) y*0.05f+0.01f, (float) z*0.05f+0.01f);
  ColorOcTreeNode* n =tree.updateNode(endpoint, true);
  //設定節點顏色資訊的函式,每個地圖節點差五個畫素大小,漸變
  n->setColor(z*5+100,x*5+100,y*5+100);
  }
  }
 }
  // insert some measurements of free cells
  for (int x=-30; x<30; x++)
 {  
for (int y=-30; y<30; y++)
  {
  for (int z=-30; z<30; z++)
  {
  point3d endpoint ((float) x*0.02f+2.0f,(float) y*0.02f+2.0f, (float) z*0.02f+2.0f);
  ColorOcTreeNode* n =tree.updateNode(endpoint, false);
  //不被佔用的節點設定為黃色
 n->setColor(255,255,0); // set colorto yellow
  }
  }
 }
// set inner node colors
 tree.updateInnerOccupancy();
 }

  cout << endl;
  tree.writeBinary("simple_tree.bt");
  cout << "wrote example file simple_tree.bt" << endl << endl;
  cout << "now you can use octovis to visualize: octovis simple_tree.bt"  << endl;
  cout << "Hint: hit 'F'-key in viewer to see the freespace" << endl  << endl;  

}

這是執行該程式碼後的執行結果:

這是用octovis檢視所生成的地圖simple_color_tree.ot: