ROS::点云PCL(1)kd-tree

理解

通过3D相机(雷达、激光扫描、立体相机)获取到的点云,一般数据量较大,分布不均匀,数据主要表征了目标物表面的大量点的集合,这些离散的点如果希望实现基于邻域关系的快速查找比对功能,就必须对这些离散的点之间建立拓扑关系。常见的空间索引一般是自上而下逐级划分空间的各种索引结构,包括BSP树,k-d tree、KDB tree、R tree、CELL tree、八叉树等。有了这些关系,我们就可以实现点云的降采样,计算特征向量,点云匹配,点云拆分等功能。

原理概述
k-d tree( k-dimensional tree)是计算机科学中用于在k维空间中一些点建立关系的数据结构。它是一个包含特定约束的二叉搜索树。k-d tree对于范围搜索和最近邻居搜索非常有用。我们通常只处理三维空间的点云,因此我们所有的k-d树都是三维空间的。

k-d树的每个级别都使用垂直于相应轴的超平面沿特定维度拆分所有子级。在树的根部,所有子项都将根据第一维进行拆分(即,如果第一维坐标小于根,则它将位于左子树中,如果大于根,则显然位于右边的子树)。树中向下的每个级别都在下一个维度上划分,其他所有元素都用尽后,将返回到第一个维度。他们构建k-d树的最有效方法是使用一种分区方法,例如快速排序所用的一种方法,将中值点放置在根上,所有具有较小一维值的东西都放置在根部,而右侧则更大。然后,在左右两个子树上都重复此过程,直到要分区的最后一棵树仅由一个元素组成。

方式一

#include <ros/ros.h> 
#include <pcl/point_cloud.h> 
#include <pcl_conversions/pcl_conversions.h> 
#include <sensor_msgs/PointCloud2.h> 

#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>

#include <pcl/kdtree/kdtree_flann.h>
#include <iostream>
#include <vector>
#include <ctime>

main (int argc, char **argv) 
{ 

    ros::init (argc, argv, "pcl_create"); 

    ros::NodeHandle nh; 
    ros::Publisher pcl_pub = nh.advertise<sensor_msgs::PointCloud2> ("pcl_output", 1);  
    
    // 用系统时间初始化随机种子
    srand(time(NULL));
    pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
    sensor_msgs::PointCloud2 output; 

    int point_num;
    float table[8]={-2,-1.5,-1,-0.5,0.5,1,1.5,2};

    cloud->width = 512;
    cloud->height = 1; 
    cloud->points.resize(cloud->width * cloud->height); 

    for(int a=0;a<8;++a){
        float width = table[a];
        for(int i=0;i<8;++i){
	    float length = table[i];
  	    for(int c=0;c<8;++c){
		point_num = a*64+i*8+c;
		cloud->points[point_num].x = width; 
		cloud->points[point_num].y = length; 
		cloud->points[point_num].z = table[c];
		std::cout <<"x="<< cloud->points[point_num].x <<"   y="<< cloud->points[point_num].y <<"   z="<< cloud->points[point_num].z  << std::endl;
		if(width==-0.5 && length==-0.5 && table[c]==-0.5){
		    cloud->points[point_num].r=0;
		    cloud->points[point_num].g=255;
		    cloud->points[point_num].b=255;
		}else{
		   cloud->points[point_num].r=0;
		   cloud->points[point_num].g=0;
		   cloud->points[point_num].b=255;
   		}

	    }
	}
    }

    pcl::KdTreeFLANN<pcl::PointXYZRGBA> kdtree;
    kdtree.setInputCloud(cloud);

    pcl::PointXYZRGBA searchPoint;
    searchPoint.x = -0.5;
    searchPoint.y = -0.5;
    searchPoint.z = -0.5;
    // 方式一:搜索K个最近邻居

    // 创建K和两个向量来保存搜索到的数据
    // K = 10 表示搜索10个临近点
    // pointIdxNKNSearch        保存搜索到的临近点的索引
    // pointNKNSquaredDistance  保存对应临近点的距离的平方
    int K = 50;
    std::vector<int> pointIdxNKNSearch(K);
    std::vector<float> pointNKNSquaredDistance(K);

    std::cout << "K nearest neighbor search at (" << searchPoint.x
              << " " << searchPoint.y
              << " " << searchPoint.z
              << ") with K=" << K << std::endl;
   //参数依次为:需查询的点、领域个数、搜索完成对应点的索引、结果中每个点对应查询点的欧氏距离(欧几里德度量)
   if (kdtree.nearestKSearch(searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance) > 0) {
        for (size_t i = 0; i < pointIdxNKNSearch.size(); ++i){
			
			if(cloud->points[pointIdxNKNSearch[i]].x==-0.5 && cloud->points[pointIdxNKNSearch[i]].y==-0.5 && cloud->points[pointIdxNKNSearch[i]].z==-0.5){
			    cloud->points[pointIdxNKNSearch[i]].r=0;
			    cloud->points[pointIdxNKNSearch[i]].g=255;
			    cloud->points[pointIdxNKNSearch[i]].b=0; 
			}else{
			    cloud->points[pointIdxNKNSearch[i]].r=255;
			    cloud->points[pointIdxNKNSearch[i]].g=0;
			    cloud->points[pointIdxNKNSearch[i]].b=0; 
			}

            std::cout << "    " << cloud->points[pointIdxNKNSearch[i]].x
                      << " " << cloud->points[pointIdxNKNSearch[i]].y
                      << " " << cloud->points[pointIdxNKNSearch[i]].z
                      << " (欧氏距离: " << pointNKNSquaredDistance[i] << ")" << std::endl;
		}
   }else{
       std::cout << "nothing......" << std::endl;
   }

    pcl::toROSMsg(*cloud, output); 
    output.header.frame_id = "odom"; 
    ros::Rate loop_rate(1); 

    while (ros::ok()) { 
	pcl_pub.publish(output);
	ros::spinOnce(); 
	loop_rate.sleep(); 
    } 
    return 0;
}

output

K nearest neighbor search at (-0.5 -0.5 -0.5) with K=10
    -0.5 -0.5 -0.5 (欧氏距离: 0)
    -0.5 -0.5 -1 (欧氏距离: 0.25)
    -0.5 -1 -0.5 (欧氏距离: 0.25)
    -1 -0.5 -0.5 (欧氏距离: 0.25)
    -1 -1 -0.5 (欧氏距离: 0.5)
    -0.5 -1 -1 (欧氏距离: 0.5)
    -1 -0.5 -1 (欧氏距离: 0.5)
    -1 -1 -1 (欧氏距离: 0.75)
    -0.5 -0.5 -1.5 (欧氏距离: 1)
    -0.5 -1.5 -0.5 (欧氏距离: 1)

在这里插入图片描述

欧氏距离

欧氏距离(EUCLIDEAN DISTANCE)
欧氏距离定义: 欧氏距离( Euclidean distance)是一个通常采用的距离定义,它是在m维空间中两个点之间的真实距离。

在二维和三维空间中的欧式距离的就是两点之间的距离,二维的公式是
d = sqrt((x1-x2)+(y1-y2))
三维的公式是
d=sqrt(x1-x2)+(y1-y2)+(z1-z2)^)
推广到n维空间,欧式距离的公式是
d=sqrt( ∑(xi1-xi2)^ ) 这里i=1,2…n
xi1表示第一个点的第i维坐标,xi2表示第二个点的第i维坐标
n维欧氏空间是一个点集,它的每个点可以表示为(x(1),x(2),…x(n)),其中x(i)(i=1,2…n)是实数,称为x的第i个坐标,两个点x和y=(y(1),y(2)…y(n))之间的距离d(x,y)定义为上面的公式.

其实就是我们学的最简单的公式:a2+b2=c2,因此,两点距离其实就是:sqrt((x1-x2)+(y1-y2)
参考:http://www.blogjava.net/spec-second/archive/2008/08/17/222609.html

方式二

    pcl::KdTreeFLANN<pcl::PointXYZRGBA> kdtree;
    kdtree.setInputCloud(cloud);

    pcl::PointXYZRGBA searchPoint;
    searchPoint.x = -0.5;
    searchPoint.y = -0.5;
    searchPoint.z = -0.5;
    // 方式二:通过指定半径搜索

    std::vector<int> pointIdxRadiusSearch;
    std::vector<float> pointRadiusSquaredDistance;
    // 创建一个随机[0,256)的半径值
    float radius = 1.2;

    std::cout << "Neighbors within radius search at (" << searchPoint.x
              << " " << searchPoint.y
              << " " << searchPoint.z
              << ") with radius=" << radius << std::endl;

    if (kdtree.radiusSearch(searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0) {
        for (size_t i = 0; i < pointIdxRadiusSearch.size(); ++i){
			if(cloud->points[pointIdxRadiusSearch[i]].x==-0.5 && cloud->points[pointIdxRadiusSearch[i]].y==-0.5 && cloud->points[pointIdxRadiusSearch[i]].z==-0.5){
			    cloud->points[pointIdxRadiusSearch[i]].r=0;
			    cloud->points[pointIdxRadiusSearch[i]].g=255;
			    cloud->points[pointIdxRadiusSearch[i]].b=0; 
			}else{
			    cloud->points[pointIdxRadiusSearch[i]].r=255;
			    cloud->points[pointIdxRadiusSearch[i]].g=0;
			    cloud->points[pointIdxRadiusSearch[i]].b=0; 
			} 
            std::cout << "    " << cloud->points[pointIdxRadiusSearch[i]].x
                      << " " << cloud->points[pointIdxRadiusSearch[i]].y
                      << " " << cloud->points[pointIdxRadiusSearch[i]].z
                      << " (欧氏距离:: " << pointRadiusSquaredDistance[i] << ")" << std::endl;
		}
    }
Neighbors within radius search at (-0.5 -0.5 -0.5) with radius=1.2
    -0.5 -0.5 -0.5 (欧氏距离:: 0)
    -1 -0.5 -0.5 (欧氏距离:: 0.25)
    -0.5 -1 -0.5 (欧氏距离:: 0.25)
    -0.5 -0.5 -1 (欧氏距离:: 0.25)
    -1 -1 -0.5 (欧氏距离:: 0.5)
    -1 -0.5 -1 (欧氏距离:: 0.5)
    -0.5 -1 -1 (欧氏距离:: 0.5)
    -1 -1 -1 (欧氏距离:: 0.75)
    -1.5 -0.5 -0.5 (欧氏距离:: 1)
    -0.5 -1.5 -0.5 (欧氏距离:: 1)
    -0.5 -0.5 -1.5 (欧氏距离:: 1)
    -0.5 -0.5 0.5 (欧氏距离:: 1)
    -0.5 0.5 -0.5 (欧氏距离:: 1)
    0.5 -0.5 -0.5 (欧氏距离:: 1)
    -1.5 -1 -0.5 (欧氏距离:: 1.25)
    -1.5 -0.5 -1 (欧氏距离:: 1.25)
    -1 -1.5 -0.5 (欧氏距离:: 1.25)
    -1 -0.5 -1.5 (欧氏距离:: 1.25)
    -1 -0.5 0.5 (欧氏距离:: 1.25)
    -1 0.5 -0.5 (欧氏距离:: 1.25)
    -0.5 -1.5 -1 (欧氏距离:: 1.25)
    -0.5 -1 -1.5 (欧氏距离:: 1.25)
    -0.5 -1 0.5 (欧氏距离:: 1.25)
    -0.5 0.5 -1 (欧氏距离:: 1.25)
    0.5 -1 -0.5 (欧氏距离:: 1.25)
    0.5 -0.5 -1 (欧氏距离:: 1.25)

在这里插入图片描述

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值