Ubuntu 18.04.06 PCL C++学习记录(三)

@[TOC]PCL中K-Dtree模块的学习

学习背景

参考书籍:《点云库PCL从入门到精通》以及官方代码PCL官方代码链接。PCL版本为1.8.1,CMake版本为3.10.2

学习内容

使用K-Dtree找到具体点或空间位置的K近邻,以及学习如何找到某一半径内的所有近邻

K-Dtree原理

构建的过程,就是用一堆离散的点云,构建一颗二叉搜索树,以二维举例

  1. 计算点集中每个维度的方差,选择方差最大的维度(X或者Y)进行接下来的切分或者直接轮流选择,前者每次划分的过程中需要额外计算方差信息,增加了计算量。故应视具体情况选择。
  2. 在切分维度上(比如X维度)选择中位数的点作为切分点坐标小于切分点的放到左子树,大的放到右子树。
  3. 进行切分,值小的放左子树的点集里,值大的放右子树的点集里。
  4. 分别对左子树和右子树的点集进行(1-3步)上的切分。这样一直递归下去直到所有的点都被分完。
    在这里插入图片描述

源代码及所用函数

源代码

#include<iostream>
#include<ctime>//时间相关函数和类型的头文件
#include<vector>
#include<pcl/point_cloud.h>//点云类定义头文件
#include<pcl/kdtree/kdtree_flann.h>//提供基于FLANN的KDTree实现。

int main()
{
    srand(time(NULL));//初始化随机数生成器的种子,注意只需执行一次。
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);//随机三维点云生成
    cloud->width = 1000;//点云个数
    cloud->height = 1;//无序点云
    cloud->points.resize(cloud->width*cloud->height);//点云所占内存大小
    //填充点云数据
    /* --------------- 使用循环为点云中的每个点赋予随机的 x、y、z 坐标值,范围在 0 到 1024 之间 -------------- */
    for(size_t i = 0;i<cloud->points.size();++i)
    {
        cloud->points[i].x=1024.0f*rand()/(RAND_MAX+1.0f);//RAND_MAX是一个常量表示rand()可以生成的最大随机数。
        cloud->points[i].y = 1024.0f*rand()/(RAND_MAX+1.0f);//+1.0f 是为了避免除以 0 的情况发生。
        cloud->points[i].z = 1024.0f*rand()/(RAND_MAX+1.0f);
    }
    /* ----------------------------- 创建KDtreeFLANN对象 ---------------------------- */
    pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
    kdtree.setInputCloud(cloud);//将点云设置为输入
    pcl::PointXYZ SearchPoint;
    //为其赋予随机坐标值    
    SearchPoint.x = 1024.0f*rand()/(RAND_MAX+1.0f);
    SearchPoint.y = 1024.0f*rand()/(RAND_MAX+1.0f);
    SearchPoint.z = 1024.0f*rand()/(RAND_MAX+1.0f);
    //k近邻搜索
    int K =10;
    std::vector<int>pointIdxKnnSearch(K);//存储查询点近邻索引
    std::vector<float>PointKnnsquareDistence(K);//存储近邻点对应平方距离
    std::cout<<"K 近邻搜寻(" << SearchPoint.x << "  " <<SearchPoint.y << "  " << SearchPoint.z << ")和K点" << K << std::endl;
    //将10个近邻打印出来
    if (kdtree.nearestKSearch(SearchPoint,K,pointIdxKnnSearch,PointKnnsquareDistence)>0)
    {
        for (size_t i = 0; i < pointIdxKnnSearch.size(); ++i)
        {
            std::cout<<" "<<cloud->points[pointIdxKnnSearch[i]].x<<" "<<cloud->points[pointIdxKnnSearch[i]].y<<" "<<cloud->points[pointIdxKnnSearch[i]].z<<" (空间距离"<<PointKnnsquareDistence[i]<<")"<<std::endl;
        }
        
    }
    
    std::vector<int>PointIdxRadiusSearch;//存储近邻索引
    std::vector<float>PointRadiusSquareDistance;//存储邻近点相应平方距离
    float radius = 256.0f*rand()/(RAND_MAX+1.0f);
    if (kdtree.radiusSearch(SearchPoint,radius,PointIdxRadiusSearch,PointRadiusSquareDistance)>0)
    {
        for (size_t i = 0; i < PointIdxRadiusSearch.size(); ++i)
        {
            std::cout<<" "<<cloud->points[PointIdxRadiusSearch[i]].x<<" "<<cloud->points[PointIdxRadiusSearch[i]].y<<""<<cloud->points[PointIdxRadiusSearch[i]].z<< " (空间距离"<<PointRadiusSquareDistance[i]<<")"<<std::endl;
        }
        
    }
    
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.1 FATAL_ERROR)#指定CMake的最低版本要求为3.1
project(kdtree_search)#设置项目名称
set(CMAKE_CXX_STANDARD 11)#设置C++编译器版本为C++11
find_package(PCL 1.7 REQUIRED)#查找PCL库,要求版本为1.7或更高。
include_directories(${PCL_INCLUDE_DIRS})#将PCL库的头文件目录添加到包含路径中
link_directories(${PCL_LIBRARY_DIRS})#将PCL库的库文件目录添加到链接器搜索路径中。
add_definitions(${PCL_DEFINITIONS})#添加PCL库的编译器定义
add_executable (kdtree_search kdtree_search.cpp)#需要修改
target_link_libraries (kdtree_search ${PCL_LIBRARIES})#将PCL库链接到可执行文件目标。

函数

  • #include <ctime>:C++标准库中的时间相关函数和类型的头文件
    如果需要使用与时间相关的函数或类型,就必须包含 ctime 头文件。如果需要使用与时间相关的函数或类型,就必须包含 ctime 头文件。其常用以下函数
time()函数,返回从公元197011000秒起到现在所经过的秒数。
clock()函数,返回程序启动到现在所经过的处理器时钟节拍数。
difftime()函数,计算两个时间值之差。
mktime()函数,将tm结构体转换为日历时间。
localtime()gmtime()函数,将日历时间转换为tm结构体
  • srand(time(NULL));其作用是初始化随机数生成器的种子,
    在 C++ 中,rand() 函数用于生成伪随机数。但是如果不对随机数生成器进行初始化,每次运行程序时 rand() 生成的随机数序列将完全相同。为了避免这种情况,我们需要为随机数生成器提供一个"种子(seed)"值,作为生成随机数序列的起点。 time(NULL) 函数返回的是从 Unix 纪元时间(1970年1月1日 00:00:00 UTC)到现在所经过的秒数。由于每次运行程序的时间戳都不同,time(NULL) 返回的值也不同。
    总的来说其作用是使用当前时间作为种子来初始化随机数生成器。这样一来,每次运行程序时,rand() 生成的随机数序列就会不同。
  • kdtree.nearestKSearch()KdTreeFLANN 类的一个成员函数,用于在 k-d 树中查找给定查询点的 K 个最近邻点。
//原函数但是不这样用,一般提前确定输入的点
int nearestKSearch(const pcl::PointCloud<PointT> &cloud, int k, std::vector<int> &pointIdxKnnSearch, std::vector<float> &pointKnnSquaredDistances, const PointT &searchPoint)
//像这样
kdtree.setInputCloud(cloud);//将点云设置为输入
kdtree.nearestKSearch(SearchPoint,K,pointIdxKnnSearch,PointKnnsquareDistence
//cloud: 输入点云数据。
//k: 要查找的最近邻点的数量。
//pointIdxKnnSearch: 存储查找到的最近邻点在点云中的索引。
//pointKnnSquaredDistances: 存储查找到的最近邻点到查询点的平方距离。
//searchPoint: 查询点。

  • kdtree.radiusSearch:在k维空间内搜索指定点周围一定半径范围内的所有点。
kdtree.radiusSearch(SearchPoint,radius,PointIdxRadiusSearch,PointRadiusSquareDistance)
//SearchPoint是要搜索的中心点,通常表示为一个k维坐标数组或向量。
//radius是搜索半径,指定了从SearchPoint开始要搜索的最大距离范围。
//PointIdxRadiusSearch是一个输出参数,是一个数组或向量,用于存储在指定半径范围内搜索到的点的索引。
//PointRadiusSquareDistance也是一个输出参数,是一个数组或向量,用于存储SearchPoint与搜索到的每个点之间的平方欧几里得距离。

  • 23
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值