zoukankan      html  css  js  c++  java
  • 视觉十四讲:第十二讲_八叉树地图

    1.简介

    把三维空间建模为许多小方块,如果把每一个小方块的每一个面平均切为两片,那么这个小方块就会变为同样大小的八个小方块,这个步骤不断重复,直到最后的方块大小达到建模的最高精度,这个过程,就是一颗八叉树。

    上图显示了大立方体不断均分为八块,知道变成最小的方块为止,整个大方块可以看作根节点,最小的方块可以看作叶子节点。
    在八叉树中,当某个方块的一个子节点都被占据或者不被占据时,就没必要展开这个节点,例如一开始地图为空白,那么只需要一个根节点,不需要完整的树,当向地图中添加信息时,由于实际的物体经常连在一起,空白的地方也经常连在一起,所以大多数八叉树节点不需要展开到叶子层面,这就大大减小了存储空间。
    对于占据和空白来说,我们选择用概率的形式表达这件事情,设 (yin R) 为概率对数值,x为0到1之间的概率:


    可以看到,当y从负无穷变到正无穷时,x相应地从0变到了1。当y取0的时候,x取到了0.5。因此,我们不妨设y来表达节点是否被占据,当不断观测到占据时,让y增加一个值,否则就减小一个值。用数学形式来说,设某节点为n,观测数据为z。那么从开始到t时刻某点的概率对数值为 (L(n|z_{1:t})),那么t+1时刻为:

    有了对数概率,我们就可以根据RGB-D数据,更新整个八叉树地图了,假设我们在RGB-D图像中观测到了某个像素带有深度d,这说明一件事:我们在深度值对应的空间点上观测到了一个占据数据,并且,从相机光心出发,到这个点的线段上,是没有物体的,利用这个信息,就可以很好的对八叉树地图进行更新。

    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    
    #include <octomap/octomap.h>    // for octomap 
    
    #include <Eigen/Geometry>
    #include <boost/format.hpp>  // for formating strings
    
    int main(int argc, char **argv) {
        vector<cv::Mat> colorImgs, depthImgs;    // 彩色图和深度图
        vector<Eigen::Isometry3d> poses;         // 相机位姿
    
        ifstream fin("./data/pose.txt");
        if (!fin) {
            cerr << "cannot find pose file" << endl;
            return 1;
        }
    
        for (int i = 0; i < 5; i++) {
            boost::format fmt("./data/%s/%d.%s"); //图像文件格式
            colorImgs.push_back(cv::imread((fmt % "color" % (i + 1) % "png").str()));
            depthImgs.push_back(cv::imread((fmt % "depth" % (i + 1) % "png").str(), -1)); // 使用-1读取原始图像
    
            double data[7] = {0};
            for (int i = 0; i < 7; i++) {
                fin >> data[i];
            }
            Eigen::Quaterniond q(data[6], data[3], data[4], data[5]);
            Eigen::Isometry3d T(q);
            T.pretranslate(Eigen::Vector3d(data[0], data[1], data[2]));
            poses.push_back(T);
        }
    
        // 计算点云并拼接
        // 相机内参 
        double cx = 319.5;
        double cy = 239.5;
        double fx = 481.2;
        double fy = -480.0;
        double depthScale = 5000.0;
    
        cout << "正在将图像转换为 Octomap ..." << endl;
    
        // octomap tree 
        octomap::OcTree tree(0.01); // 参数为分辨率
    
        for (int i = 0; i < 5; i++) {
            cout << "转换图像中: " << i + 1 << endl;
            cv::Mat color = colorImgs[i];
            cv::Mat depth = depthImgs[i];
            Eigen::Isometry3d T = poses[i];
    
            octomap::Pointcloud cloud;  // the point cloud in octomap 
    
            for (int v = 0; v < color.rows; v++)
                for (int u = 0; u < color.cols; u++) {
                    unsigned int d = depth.ptr<unsigned short>(v)[u]; // 深度值
                    if (d == 0) continue; // 为0表示没有测量到
                    Eigen::Vector3d point;
                    point[2] = double(d) / depthScale;
                    point[0] = (u - cx) * point[2] / fx;
                    point[1] = (v - cy) * point[2] / fy;
                    Eigen::Vector3d pointWorld = T * point;
                    // 将世界坐标系的点放入点云
                    cloud.push_back(pointWorld[0], pointWorld[1], pointWorld[2]);
                }
    
            // 将点云存入八叉树地图,给定原点,这样可以计算投射线
            tree.insertPointCloud(cloud, octomap::point3d(T(0, 3), T(1, 3), T(2, 3)));
        }
    
        // 更新中间节点的占据信息并写入磁盘
        tree.updateInnerOccupancy();
        cout << "saving octomap ... " << endl;
        tree.writeBinary("octomap.bt");
        return 0;
    }
    
    
    cmake_minimum_required(VERSION 2.8)
    
    set(CMAKE_BUILD_TYPE Release)
    set(CMAKE_CXX_FLAGS "-std=c++11 -O2")
    
    # opencv 
    find_package(OpenCV REQUIRED)
    include_directories(${OpenCV_INCLUDE_DIRS})
    
    # eigen 
    include_directories("/usr/include/eigen3/")
    
    # pcl 
    find_package(PCL REQUIRED)
    include_directories(${PCL_INCLUDE_DIRS})
    add_definitions(${PCL_DEFINITIONS})
    
    # octomap 
    find_package(octomap REQUIRED)
    include_directories(${OCTOMAP_INCLUDE_DIRS})
    
    add_executable(octomap_mapping octomap_mapping.cpp)
    target_link_libraries(octomap_mapping ${OpenCV_LIBS} ${PCL_LIBRARIES} ${OCTOMAP_LIBRARIES})
    
    
  • 相关阅读:
    iOS APM性能统计
    iOS promise
    静态代码检测工具Infer的部署
    ES读写数据的工作原理
    关于 Elasticsearch 内存占用及分配
    Elasticsearch中数据是如何存储的
    ES中的选举机制
    .net core 3.1 webapi解决跨域问题 GET DELETE POST PUT等
    .net framework WEBAPI跨域问题
    Angular前端项目发布到IIS
  • 原文地址:https://www.cnblogs.com/penuel/p/13722998.html
Copyright © 2011-2022 走看看