Lidar系列文章
传感器融合是将多个传感器采集的数据进行融合处理,以更好感知周围环境;这里首先介绍激光雷达的相关内容,包括激光雷达基本介绍(本节内容),激光点云数据处理方法(点云数据显示,点云分割,点云聚类,障碍物识别实例)等。
系列文章目录
1. 激光雷达基本介绍
2. 激光点云数据显示
3. 基于RANSAC的激光点云分割
4. 点云分割入门级实例学习
5. 激光点云目标物聚类
6. 基于PCL实现欧式聚类提取
7. 激光雷达障碍物识别
目前我已经实现点云分割,分离出障碍物点云。如果可以将这些障碍物进行区分,分离出同一物体反射的点云,则将会对我们的多目标跟踪(车辆,行人,自行车等)非常有帮助。本节主要介绍激光点云聚类算法。
欧氏聚类
聚类:分离出属于同一障碍物的激光点云,比如车辆,行人等;
欧氏聚类:Euclidean clustering,根据点之间的距离进行分组关联;
KD-Tree:通过将搜索空间分块并将点云分类进不同的区域,减小了不相邻点云间的距离计算,KD-Tree数据结构通常可以将查找时间从O(n)缩短到O(log(n)),实现快速的最近邻搜索(nearest neighbors search)。
KD-Tree
KD-Tree是一种以二叉树形式表示的数据结构,在k维空间(如三维空间X, Y , Z维度)中进行维度切换对比将点插入二叉树进行存储,以便对其进行快速检索。通过区域分割来划分区域,使用欧氏聚类等算法进行聚类时最近邻搜索将极大加速。
我们将以下图包含11点的2D点云聚类进行介绍,可以看到包含3组距离较近的点。
首先需要将点插入KD-Tree中,主要步骤如下:
- 插入任一点(-6.2,7)作为树的根节点,分割x方向,下图绿线所示;
- 插入下一点 (-6.3, 8.4),如果其x值小于根节点,插入根节点左侧,否则根节点右侧;该点分割y区域,下图红线所示;
- 继续插入另外两点(-5.2, 7.1) ,(-5.7, 6.3),重新分割x区域。
这里用于对比的(x,y)维度是根据其深度进行判断。
下图是通过直线分割好的KD-Tree,绿色划分x轴区域,红色划分y轴区域。
为改善KD-Tree的结构,可以通过X/Y交替插入中值点的方法,使树结构更加均匀,缩短搜索时间。
以下是KD-Tree点插入的代码实现。这里通过递归调用insertHelper
函数实现插入点及更新树的节点。
void insertHelper(Node** node, unsigned int depth, std::vector<float> point, int id)
{
//Tree is empty
if(*node == NULL)
*node = new Node(point,id);
else
{
//Calculate current dim
uint cd = depth%2; //use 3 if we are working in 3 dimension(x,y,z) kdtree
if(point[cd]<((*node)->point[cd]))
insertHelper(&((*node)->left),depth+1,point,id);
else
insertHelper(&((*node)->right),depth+1,point,id);
}
}
void insert(std::vector<float> point, int id)
{
// TODO: Fill in this function to insert a new point into the tree
// the function should create a new node and place correctly with in the root
insertHelper(&root,0,point,id);
}
KD-Tree中搜索点
所有点插入到KD-Tree中后,下一步是通过与给定的目标对比搜索最近的点。距离目标点给定距离distanceTol
(目标点附近2*distanceTol
区域)的点可视为可能的临近点,再计算其距离判定是否作为近邻点。KD-Tree通过将区域进行分割可以忽略部分区域的点,加快最近邻搜索过程。
具体实现如下:
void searchHelper(std::vector<float> target, Node* node,int depth, float distanceTol, std::vector<int>& ids)
{
if(node!=NULL)
{
//Below check 3 dimensions if we are working in 3 dimension(x,y,z) kdtree
if((node->point[0]>=(target[