fastlio中ikdtree代码解析

引言

本文结合代码对ikdtree进行介绍,ikdtree是fastlio中用来管理地图的数据结构。kdtree是一种常用的树形结构。ikdtree在kdtree的基础上增加了较为快速的添加删除及重构树的操作,以便于对地图的动态管理。以下会结合fastlio中ikd_Tree.h和ikd_Tree.cpp两个文件进行介绍。

基础

下面对基础的数据结构和算法进行介绍,是后续了解ikdtree的基础

队列(queue)

在ikd_Tree.h中,定义了ikdtree类,ikdtree类中,定义了MANUAL_Q结构,代码如下。

这是一个比较标准的队列应用,队列中存储的元素是Operation_Logger_Type,实际上是针对树的一些操作,这些操作如果因为多线程阻塞的原因没有被执行,就会保存到队列中,等多线程释放后,再对新的树进行对应的操作。

    class MANUAL_Q
    {
    private:
        int head = 0, tail = 0, counter = 0;
        Operation_Logger_Type q[Q_LEN];         // 一个长度较大的数组,用来存放对ikdtree的操作
        bool is_empty;

    public:
        void pop()                              // 扔出一个元素,注意这里只是改变了指针的位置,没有清理元素本身
        {
            if (counter == 0)                   // 先判断元素个数
                return;
            head++;
            head %= Q_LEN;                      // 对长度取模,可以看做一个循环队列
            counter--;
            if (counter == 0)
                is_empty = true;
            return;
        }
        Operation_Logger_Type front()           // 拿到当前队列的第一个元素
        {
            return q[head];
        }
        Operation_Logger_Type back()            // 拿到当前队列最后一个元素
        {
            return q[tail];
        }
        void clear()                            // 清空队列,实际就是指向位置的初始化
        {
            head = 0;
            tail = 0;
            counter = 0;
            is_empty = true;
            return;
        }
        void push(Operation_Logger_Type op)     // 队列尾部添加一个元素,也是循环添加
        {
            q[tail] = op;
            counter++;
            if (is_empty)
                is_empty = false;
            tail++;
            tail %= Q_LEN;
        }
        bool empty()                            // 判断是否为空
        {
            return is_empty;
        }
        int size()                              // 返回元素个数
        {
            return counter;
        }
    };

堆(heap)

代码中采用了max-heap。其中比较难理解的是插入和删除操作。具体可以参考

https://www.geeksforgeeks.org/insertion-and-deletion-in-heaps/

这个链接中的实现方式和ikdtree中实现方式略有不同,但整体思路是一致的。

以下是经过注释的代码

    class MANUAL_HEAP
    {

    public:
        MANUAL_HEAP(int max_capacity = 100)

        {
            cap = max_capacity;
            heap = new PointType_CMP[max_capacity];     // 最大容量
            heap_size = 0;
        }

        ~MANUAL_HEAP()
        {
            delete[] heap;
        }
        void pop()
        {
            if (heap_size == 0)
                return;
            heap[0] = heap[heap_size - 1];              // 弹出最大值,然后把队列最后的值放上来
            heap_size--;
            MoveDown(0);                                // 将放上来的新值下沉,找到合适的位置
            return;
        }
        PointType_CMP top()                             // 拿到最大值
        {
            return heap[0];
        }
        void push(PointType_CMP point)
        {
            if (heap_size >= cap)   // 太多了新的就不放了
                return;
            heap[heap_size] = point;                       // 新插入的点放到最后
            FloatUp(heap_size);                             // 然后将最后一个点上浮到合适为止
            heap_size++;
            return;
        }
        int size()
        {
            return heap_size;
        }
        void clear()
        {
            heap_size = 0;
            return;
        }

    private:
        PointType_CMP *heap;
        void MoveDown(int heap_index)                       // 下沉过程
        {
            int l = heap_index * 2 + 1;                     // 找到左子节点的index
            PointType_CMP tmp = heap[heap_index];           // 复制出一个当前值
            while (l < heap_size)                           // 没有循环超过数组大小,如果heap_size是0,那么直接退出while,放入第一个点
            {                                               // 如果heap_size是1,也是直接退出。
                if (l + 1 < heap_size && heap[l] < heap[l + 1])     // 从左子节点和右子节点中选出一个大的来
                    l++;
                if (tmp < heap[l])                          // 将当前值和这个大的值比较,如果小,就交换,保证大的值在上面
                {
                    heap[heap_index] = heap[l];
                    heap_index = l;
                    l = heap_index * 2 + 1;                 // 然后继续循环
                }
                else
                    break;
            }
            heap[heap_index] = tmp;
            return;
        }   
        void FloatUp(int heap_index)                            // 上浮过程
        {
            int ancestor = (heap_index - 1) / 2;                // 找到父节点
            PointType_CMP tmp = heap[heap_index];
            while (heap_index > 0)                              // 保证在范围内
            {
                if (heap[ancestor] < tmp)                       // 如果父节点小
                {
                    heap[heap_index] = heap[ancestor];          // 就进行交换
                    heap_index = ancestor;
                    ancestor = (heap_index - 1) / 2;            // 直到循环完成
                }
                else
                    break;
            }
            heap[heap_index] = tmp;
            return;
        }
        int heap_size = 0;
        int cap = 0;
    };

kdtree

关于kdtree本身的知识,主要参考Wikipedia:https://en.wikipedia.org/wiki/K-d_tree

这里主要讲kdtree的构建、添加点、删除点和最近邻搜索

kdtree的构建

构建函数的伪代码如下。

主要步骤包括:1. 选择分割轴;2. 沿分割轴排序,并找到中值;3. 构造节点,同时用中值将所有元素分成两个部分,分别是左右子树,进行递归构建。

function kdtree (list of points pointList, int depth)
{
    // Select axis based on depth so that axis cycles through all valid values
    // 选择一个分割轴,可以按照最大方差选取,也可以按顺序直接分
    var int axis := depth mod k;

    // Sort point list and choose median as pivot element
    // 将所有点按分割轴排序,并找到其中的中值
    select median by axis from pointList;

    // Create node and construct subtree
    // 构造节点,然后对左右子树进行递归循环构建
    node.location := median;
    node.leftChild := kdtree(points in pointList before median, depth+1);
    node.rightChild := kdtree(points in pointList after median, depth+1);
    return node;
}

添加元素

添加元素伪代码:

function insert_kdtree (node, point, depth)
{
    // Base case: If the node is null, create a new node here
    // 处理递归结束情况,如果是空节点,就新建一个
    if (node is null)
    {
        node := create new node;
        node.location := point;
        node.leftChild := null;
        node.rightChild := null;
        return node;
    }

    // Calculate current dimension (axis) based on depth
    // 选择分割轴
    var int axis := depth mod k;

    // Compare the new point with the current node's point along the current axis
    看看是在当前节点,沿着分割轴的左侧还是右侧
    if (point[axis] < node.location[axis])
    {
        // Recur on the left subtree
        // 如果是左侧就接着递归左侧
        insert_kdtree(node.leftChild, point, depth+1);
    }
    else
    {
        // Recur on the right subtree
        // 如果是右侧就接着递归右侧
        insert_kdtree(node.rightChild, point, depth+1);
    }
}

删除元素

删除kdtree中的元素的方法在Wikipedia中给了两个:

  1. 将剩下的点直接重建
  2. 找个点替代这个点,比如从左子树中找沿着当前轴最大的点,或从右子树中找沿着当前轴最小的点。

下面给出方法2的一个伪代码。

function delete_kdtree(node, point, depth)
{
    // Base case: If node is null, the point is not found
    // 处理空节点
    if (node is null)
        return null;

    // Calculate current dimension (axis) based on depth
    // 找到分割轴
    var int axis := depth mod k;

    // If the point to be deleted is found at the current node
    // 如果当前点就是要删除的点
    if (node.location == point)
    {
        // Case 1: Node is a leaf node
        // 如果是叶节点,就直接删
        if (node.leftChild is null and node.rightChild is null)
            return null; // Simply remove the leaf node
        
        // Case 2: Node has a right child
        // 如果不是叶节点,从右子树中找一个最小值
        if (node.rightChild is not null)
        {
            // Find the minimum point in the right subtree along the current axis
            var PointType minPoint := find_min(node.rightChild, axis, depth + 1);
            
            // Replace current node with the minimum point found
            node.location := minPoint;
            
            // Recursively delete the minimum point from the right subtree
            // 同时还要递归删除刚刚找到的那个点
            node.rightChild := delete_kdtree(node.rightChild, minPoint, depth + 1);
        }
        // Case 3: Node only has a left child
        // 或者从左子树中找一个最大值,
        else if (node.leftChild is not null)
        {
            // Find the maximum point in the left subtree along the current axis
            var PointType maxPoint := find_max(node.leftChild, axis, depth + 1);
            
            // Replace current node with the maximum point found
            node.location := maxPoint;
            
            // Recursively delete the maximum point from the left subtree
            // 同时还要递归删除刚刚找到的那个点
            node.leftChild := delete_kdtree(node.leftChild, maxPoint, depth + 1);
        }

        return node;
    }

    // Recursively search for the point in the left or right subtree
    // 如果不是当前点,就接着递归寻找
    if (point[axis] < node.location[axis])
    {
        // Go to left subtree
        node.leftChild := delete_kdtree(node.leftChild, point, depth + 1);
    }
    else
    {
        // Go to right subtree
        node.rightChild := delete_kdtree(node.rightChild, point, depth + 1);
    }

    return node;
}

function find_min(node, axis, depth)
{
    if (node is null)
        return null;

    // Calculate current axis
    var int current_axis := depth mod k;

    // If current axis matches the target axis
    if (current_axis == axis)
    {
        // The minimum is in the left subtree
        if (node.leftChild is null)
            return node.location;
        return find_min(node.leftChild, axis, depth + 1);
    }

    // Otherwise, find the minimum across both subtrees
    // 递归遍历找最小值
    var PointType leftMin := find_min(node.leftChild, axis, depth + 1);
    var PointType rightMin := find_min(node.rightChild, axis, depth + 1);

    // Return the smallest among node, leftMin, and rightMin
    return min(node.location, leftMin, rightMin);
}

function find_max(node, axis, depth)
{
    if (node is null)
        return null;

    // Calculate current axis
    var int current_axis := depth mod k;

    // If current axis matches the target axis
    if (current_axis == axis)
    {
        // The maximum is in the right subtree
        if (node.rightChild is null)
            return node.location;
        return find_max(node.rightChild, axis, depth + 1);
    }

    // Otherwise, find the maximum across both subtrees
    var PointType leftMax := find_max(node.leftChild, axis, depth + 1);
    var PointType rightMax := find_max(node.rightChild, axis, depth + 1);

    // Return the largest among node, leftMax, and rightMax
    return max(node.location, leftMax, rightMax);
}

最近邻搜索

Visit(root):
    d= distance(target, root)	// 计算和当前点的距离
    if d < r:					// 比最近的距离还近
        r= d					// 记录这个距离
        closest= root			// 记录这个点

    if root.left and root.x - target.x < r:	// x在这里就是对应root的分割轴,
        Visit(root.left)
    if root.right and target.x - root.x < r:
        Visit(root.right)

多线程锁

pthread_mutex_init:初始化一个互斥锁。

pthread_mutex_lock:以阻塞方式加锁互斥锁。

pthread_mutex_trylock:以非阻塞方式尝试加锁互斥锁。

pthread_mutex_unlock:解锁互斥锁。

pthread_mutex_destroy:销毁一个互斥锁。

ikdtree

接口API

以下从Fastlio的laserMapping.cpp中,选出用到的ikdtree的api。从这里可以看出ikdtree的用法,进而可以推断出ikdtree本身实现所需要包含的功能。

ikdtree是进行点云地图管理的,由于点云数量庞大,并且fastlio仅维护当前位置附近的局部地图,因此,ikdtree需要能快速的完成插入点删除点的动作。

同时,为了完成点云匹配计算,需要ikdtree能完成最近的K个点的查询功能。具体如下:

  1. 定义:
KD_TREE<PointType> ikdtree;
  1. 初始化构造:
            if(ikdtree.Root_Node == nullptr)								// 如果没有根节点,说明没构造
            {
                if(feats_down_size > 5)
                {
                    ikdtree.set_downsample_param(filter_size_map_min);
                    feats_down_world->resize(feats_down_size);
                    for(int i = 0; i < feats_down_size; i++)
                    {
                        pointBodyToWorld(&(feats_down_body->points[i]), &(feats_down_world->points[i]));
                    }
                    ikdtree.Build(feats_down_world->points);				// 给它一串点,构造初始的树
                }
                continue;
            }
            int featsFromMapNum = ikdtree.validnum();						// 读取一些树的特性
            kdtree_size_st = ikdtree.size();								// 读取树中节点的个数
  1. 删除点:

随着车辆的移动,需要删除某些区域的点,这样不至于地图过大,只保留激光雷达附近的局部地图即可

if(cub_needrm.size() > 0) kdtree_delete_counter = ikdtree.Delete_Point_Boxes(cub_needrm);
  1. 添加点:

随着车辆移动,需要新加入点

    add_point_size = ikdtree.Add_Points(PointToAdd, true);
    ikdtree.Add_Points(PointNoNeedDownsample, false); 
  1. 查询top-K最近点

在进行点云匹配时,会选择最近的K个点,组成平面,进行点面匹配计算

ikdtree.Nearest_Search(point_world, NUM_MATCH_POINTS, points_near, pointSearchSqDis);

主要功能

构造

主要构造过程函数

树的构造过程主要是看BuildTree函数。在rebuild功能中,也会调用这个函数完成数的创建。

/**
 * 树的构造过程函数,大概分成4部分
 * 1. 递归结束条件
 * 2. 根据x,y,z三个轴上的分布情况,选定分割轴
 * 3. 创建当前点,并且对左右子树递归循环创建
 * 4. 更新当前节点属性参数
*/
template <typename PointType>
void KD_TREE<PointType>::BuildTree(KD_TREE_NODE **root, int l, int r, PointVector &Storage)
{
    if (l > r)                  // 终止条件
        return;
    *root = new KD_TREE_NODE;   // 分配一块新的node内存出来,给root所指向的地址
    InitTreeNode(*root);
    int mid = (l + r) >> 1;     // 等价于 (l + r) / 2
    int div_axis = 0;
    int i;
    // Find the best division Axis
    float min_value[3] = {INFINITY, INFINITY, INFINITY};
    float max_value[3] = {-INFINITY, -INFINITY, -INFINITY};
    float dim_range[3] = {0, 0, 0};
    for (i = l; i <= r; i++)                                // 对所有点循环遍历,找到当前这组点中“范围最大的”一个维度作为分割轴
    {
        min_value[0] = min(min_value[0], Storage[i].x);
        min_value[1] = min(min_value[1], Storage[i].y);
        min_value[2] = min(min_value[2], Storage[i].z);
        max_value[0] = max(max_value[0], Storage[i].x);
        max_value[1] = max(max_value[1], Storage[i].y);
        max_value[2] = max(max_value[2], Storage[i].z);
    }
    // Select the longest dimension as division axis
    for (i = 0; i < 3; i++)
        dim_range[i] = max_value[i] - min_value[i];
    for (i = 1; i < 3; i++)
        if (dim_range[i] > dim_range[div_axis])
            div_axis = i;
    // Divide by the division axis and recursively build.

    (*root)->division_axis = div_axis;
    switch (div_axis)                   // 这是在进行排序并找到中位数的点
    {
    case 0:     // nth_element 是c++标准库中的一个函数
        nth_element(begin(Storage) + l, begin(Storage) + mid, begin(Storage) + r + 1, point_cmp_x);
        break;
    case 1:
        nth_element(begin(Storage) + l, begin(Storage) + mid, begin(Storage) + r + 1, point_cmp_y);
        break;
    case 2:
        nth_element(begin(Storage) + l, begin(Storage) + mid, begin(Storage) + r + 1, point_cmp_z);
        break;
    default:
        nth_element(begin(Storage) + l, begin(Storage) + mid, begin(Storage) + r + 1, point_cmp_x);
        break;
    }
    (*root)->point = Storage[mid];      // 将中位数的点记录到当前node中
    KD_TREE_NODE *left_son = nullptr, *right_son = nullptr;
    BuildTree(&left_son, l, mid - 1, Storage);      // 然后继续遍历左右子节点
    BuildTree(&right_son, mid + 1, r, Storage);
    (*root)->left_son_ptr = left_son;               // 记录新的左右子节点
    (*root)->right_son_ptr = right_son;
    Update((*root));                    //更新当前节点的一些属性
    return;
}
节点属性参数更新函数
/**
 * Update 函数在 KD-Tree 中的作用是更新每个节点的统计信息和元数据。
 * 具体来说,它会更新节点的大小(TreeSize)、删除标记(tree_deleted)、空间范围(node_range_x, node_range_y, node_range_z),
 * 以及父子节点之间的关系。这个函数在构建树时被调用,以确保每个节点的相关信息都是最新的,尤其是在递归创建子树之后。

*/
template <typename PointType>
void KD_TREE<PointType>::Update(KD_TREE_NODE *root)
{
    KD_TREE_NODE *left_son_ptr = root->left_son_ptr;
    KD_TREE_NODE *right_son_ptr = root->right_son_ptr;
    float tmp_range_x[2] = {INFINITY, -INFINITY};       //用于存储当前节点的空间范围(即该节点的最小值和最大值)
    float tmp_range_y[2] = {INFINITY, -INFINITY};
    float tmp_range_z[2] = {INFINITY, -INFINITY};
    // Update Tree Size
    if (left_son_ptr != nullptr && right_son_ptr != nullptr)    // 左右子节点都存在的情况
    {
        root->TreeSize = left_son_ptr->TreeSize + right_son_ptr->TreeSize + 1;          // 更新当前节点属性,利用子节点的属性
        root->invalid_point_num = left_son_ptr->invalid_point_num + right_son_ptr->invalid_point_num + (root->point_deleted ? 1 : 0);
        root->down_del_num = left_son_ptr->down_del_num + right_son_ptr->down_del_num + (root->point_downsample_deleted ? 1 : 0);
        root->tree_downsample_deleted = left_son_ptr->tree_downsample_deleted & right_son_ptr->tree_downsample_deleted & root->point_downsample_deleted;
        root->tree_deleted = left_son_ptr->tree_deleted && right_son_ptr->tree_deleted && root->point_deleted;
        if (root->tree_deleted || (!left_son_ptr->tree_deleted && !right_son_ptr->tree_deleted && !root->point_deleted))    // 根据是否要删除树进行分类
        {
            tmp_range_x[0] = min(min(left_son_ptr->node_range_x[0], right_son_ptr->node_range_x[0]), root->point.x);
            tmp_range_x[1] = max(max(left_son_ptr->node_range_x[1], right_son_ptr->node_range_x[1]), root->point.x);        // 计算范围
            tmp_range_y[0] = min(min(left_son_ptr->node_range_y[0], right_son_ptr->node_range_y[0]), root->point.y);
            tmp_range_y[1] = max(max(left_son_ptr->node_range_y[1], right_son_ptr->node_range_y[1]), root->point.y);
            tmp_range_z[0] = min(min(left_son_ptr->node_range_z[0], right_son_ptr->node_range_z[0]), root->point.z);
            tmp_range_z[1] = max(max(left_son_ptr->node_range_z[1], right_son_ptr->node_range_z[1]), root->point.z);
        }
        else
        {
            if (!left_son_ptr->tree_deleted)        // 如果左子树不删,就和左子树计算一次边界
            {
                tmp_range_x[0] = min(tmp_range_x[0], left_son_ptr->node_range_x[0]);
                tmp_range_x[1] = max(tmp_range_x[1], left_son_ptr->node_range_x[1]);
                tmp_range_y[0] = min(tmp_range_y[0], left_son_ptr->node_range_y[0]);    // 计算范围
                tmp_range_y[1] = max(tmp_range_y[1], left_son_ptr->node_range_y[1]);
                tmp_range_z[0] = min(tmp_range_z[0], left_son_ptr->node_range_z[0]);
                tmp_range_z[1] = max(tmp_range_z[1], left_son_ptr->node_range_z[1]);
            }
            if (!right_son_ptr->tree_deleted)       // 如果右子树不删,再和右子树计算一次边界
            {
                tmp_range_x[0] = min(tmp_range_x[0], right_son_ptr->node_range_x[0]);
                tmp_range_x[1] = max(tmp_range_x[1], right_son_ptr->node_range_x[1]);
                tmp_range_y[0] = min(tmp_range_y[0], right_son_ptr->node_range_y[0]);
                tmp_range_y[1] = max(tmp_range_y[1], right_son_ptr->node_range_y[1]);
                tmp_range_z[0] = min(tmp_range_z[0], right_son_ptr->node_range_z[0]);
                tmp_range_z[1] = max(tmp_range_z[1], right_son_ptr->node_range_z[1]);
            }
            if (!root->point_deleted)               // 和自身位置计算一次边界
            {
                tmp_range_x[0] = min(tmp_range_x[0], root->point.x);
                tmp_range_x[1] = max(tmp_range_x[1], root->point.x);
                tmp_range_y[0] = min(tmp_range_y[0], root->point.y);
                tmp_range_y[1] = max(tmp_range_y[1], root->point.y);
                tmp_range_z[0] = min(tmp_range_z[0], root->point.z);
                tmp_range_z[1] = max(tmp_range_z[1], root->point.z);
            }
        }
    }
    else if (left_son_ptr != nullptr)       // 同样道理处理左子树非空,但是右子树空的情况
    {
        root->TreeSize = left_son_ptr->TreeSize + 1;
        root->invalid_point_num = left_son_ptr->invalid_point_num + (root->point_deleted ? 1 : 0);
        root->down_del_num = left_son_ptr->down_del_num + (root->point_downsample_deleted ? 1 : 0);
        root->tree_downsample_deleted = left_son_ptr->tree_downsample_deleted & root->point_downsample_deleted;
        root->tree_deleted = left_son_ptr->tree_deleted && root->point_deleted;
        if (root->tree_deleted || (!left_son_ptr->tree_deleted && !root->point_deleted))
        {
            tmp_range_x[0] = min(left_son_ptr->node_range_x[0], root->point.x);
            tmp_range_x[1] = max(left_son_ptr->node_range_x[1], root->point.x);
            tmp_range_y[0] = min(left_son_ptr->node_range_y[0], root->point.y);
            tmp_range_y[1] = max(left_son_ptr->node_range_y[1], root->point.y);
            tmp_range_z[0] = min(left_son_ptr->node_range_z[0], root->point.z);
            tmp_range_z[1] = max(left_son_ptr->node_range_z[1], root->point.z);
        }
        else
        {
            if (!left_son_ptr->tree_deleted)
            {
                tmp_range_x[0] = min(tmp_range_x[0], left_son_ptr->node_range_x[0]);
                tmp_range_x[1] = max(tmp_range_x[1], left_son_ptr->node_range_x[1]);
                tmp_range_y[0] = min(tmp_range_y[0], left_son_ptr->node_range_y[0]);
                tmp_range_y[1] = max(tmp_range_y[1], left_son_ptr->node_range_y[1]);
                tmp_range_z[0] = min(tmp_range_z[0], left_son_ptr->node_range_z[0]);
                tmp_range_z[1] = max(tmp_range_z[1], left_son_ptr->node_range_z[1]);
            }
            if (!root->point_deleted)
            {
                tmp_range_x[0] = min(tmp_range_x[0], root->point.x);
                tmp_range_x[1] = max(tmp_range_x[1], root->point.x);
                tmp_range_y[0] = min(tmp_range_y[0], root->point.y);
                tmp_range_y[1] = max(tmp_range_y[1], root->point.y);
                tmp_range_z[0] = min(tmp_range_z[0], root->point.z);
                tmp_range_z[1] = max(tmp_range_z[1], root->point.z);
            }
        }
    }
    else if (right_son_ptr != nullptr)      // 同样道理处理右子树非空,左子树空的情况
    {
        root->TreeSize = right_son_ptr->TreeSize + 1;
        root->invalid_point_num = right_son_ptr->invalid_point_num + (root->point_deleted ? 1 : 0);
        root->down_del_num = right_son_ptr->down_del_num + (root->point_downsample_deleted ? 1 : 0);
        root->tree_downsample_deleted = right_son_ptr->tree_downsample_deleted & root->point_downsample_deleted;
        root->tree_deleted = right_son_ptr->tree_deleted && root->point_deleted;
        if (root->tree_deleted || (!right_son_ptr->tree_deleted && !root->point_deleted))
        {
            tmp_range_x[0] = min(right_son_ptr->node_range_x[0], root->point.x);
            tmp_range_x[1] = max(right_son_ptr->node_range_x[1], root->point.x);
            tmp_range_y[0] = min(right_son_ptr->node_range_y[0], root->point.y);
            tmp_range_y[1] = max(right_son_ptr->node_range_y[1], root->point.y);
            tmp_range_z[0] = min(right_son_ptr->node_range_z[0], root->point.z);
            tmp_range_z[1] = max(right_son_ptr->node_range_z[1], root->point.z);
        }
        else
        {
            if (!right_son_ptr->tree_deleted)
            {
                tmp_range_x[0] = min(tmp_range_x[0], right_son_ptr->node_range_x[0]);
                tmp_range_x[1] = max(tmp_range_x[1], right_son_ptr->node_range_x[1]);
                tmp_range_y[0] = min(tmp_range_y[0], right_son_ptr->node_range_y[0]);
                tmp_range_y[1] = max(tmp_range_y[1], right_son_ptr->node_range_y[1]);
                tmp_range_z[0] = min(tmp_range_z[0], right_son_ptr->node_range_z[0]);
                tmp_range_z[1] = max(tmp_range_z[1], right_son_ptr->node_range_z[1]);
            }
            if (!root->point_deleted)
            {
                tmp_range_x[0] = min(tmp_range_x[0], root->point.x);
                tmp_range_x[1] = max(tmp_range_x[1], root->point.x);
                tmp_range_y[0] = min(tmp_range_y[0], root->point.y);
                tmp_range_y[1] = max(tmp_range_y[1], root->point.y);
                tmp_range_z[0] = min(tmp_range_z[0], root->point.z);
                tmp_range_z[1] = max(tmp_range_z[1], root->point.z);
            }
        }
    }
    else
    {
        root->TreeSize = 1;
        root->invalid_point_num = (root->point_deleted ? 1 : 0);
        root->down_del_num = (root->point_downsample_deleted ? 1 : 0);
        root->tree_downsample_deleted = root->point_downsample_deleted;
        root->tree_deleted = root->point_deleted;
        tmp_range_x[0] = root->point.x;
        tmp_range_x[1] = root->point.x;
        tmp_range_y[0] = root->point.y;
        tmp_range_y[1] = root->point.y;
        tmp_range_z[0] = root->point.z;
        tmp_range_z[1] = root->point.z;
    }
    memcpy(root->node_range_x, tmp_range_x, sizeof(tmp_range_x));       // 把计算出来的包络范围赋值给当前节点
    memcpy(root->node_range_y, tmp_range_y, sizeof(tmp_range_y));
    memcpy(root->node_range_z, tmp_range_z, sizeof(tmp_range_z));
    float x_L = (root->node_range_x[1] - root->node_range_x[0]) * 0.5;
    float y_L = (root->node_range_y[1] - root->node_range_y[0]) * 0.5;
    float z_L = (root->node_range_z[1] - root->node_range_z[0]) * 0.5;
    root->radius_sq = x_L*x_L + y_L * y_L + z_L * z_L;          // 大概计算一个半径的平方
    if (left_son_ptr != nullptr)
        left_son_ptr->father_ptr = root;            // 赋值父节点
    if (right_son_ptr != nullptr)
        right_son_ptr->father_ptr = root;
    if (root == Root_Node && root->TreeSize > 3)
    {
        KD_TREE_NODE *son_ptr = root->left_son_ptr;
        if (son_ptr == nullptr)
            son_ptr = root->right_son_ptr;
        float tmp_bal = float(son_ptr->TreeSize) / (root->TreeSize - 1);        // 判断树平衡的一些条件,
        root->alpha_del = float(root->invalid_point_num) / root->TreeSize;      // 只在根节点进行计算
        root->alpha_bal = (tmp_bal >= 0.5 - EPSS) ? tmp_bal : 1 - tmp_bal;
    }
    return;
}

在构造树的过程中,生成完每个节点的坐标点,以及所有递归子树后,会进行update更新,此时更新的属性都是默认值,都是“不需要删除树”之类的值。此时的树非常“干净”没有什么特别的属性(删除相关属性)。

添加功能

Add_Points函数

里面调用了Add_by_point 和 Delete_by_range函数

template <typename PointType>
int KD_TREE<PointType>::Add_Points(PointVector &PointToAdd, bool downsample_on)
{
    int NewPointSize = PointToAdd.size();               // 要添加的点的个数
    int tree_size = size();
    BoxPointType Box_of_Point;                          // 定义一些临时变量
    PointType downsample_result, mid_point;
    bool downsample_switch = downsample_on && DOWNSAMPLE_SWITCH;    // 是否降采样
    float min_dist, tmp_dist;                           // 临时变量
    int tmp_counter = 0;
    for (int i = 0; i < PointToAdd.size(); i++)         // 对每个要添加的点都进行循环
    {
        if (downsample_switch)                          // 判断是否进行降采样
        {                                               // 如果要进行
            Box_of_Point.vertex_min[0] = floor(PointToAdd[i].x / downsample_size) * downsample_size;    // 生成一个立方体边框的长宽高
            Box_of_Point.vertex_max[0] = Box_of_Point.vertex_min[0] + downsample_size;
            Box_of_Point.vertex_min[1] = floor(PointToAdd[i].y / downsample_size) * downsample_size;
            Box_of_Point.vertex_max[1] = Box_of_Point.vertex_min[1] + downsample_size;
            Box_of_Point.vertex_min[2] = floor(PointToAdd[i].z / downsample_size) * downsample_size;
            Box_of_Point.vertex_max[2] = Box_of_Point.vertex_min[2] + downsample_size;
            mid_point.x = Box_of_Point.vertex_min[0] + (Box_of_Point.vertex_max[0] - Box_of_Point.vertex_min[0]) / 2.0;     // 立方体边框的中心点
            mid_point.y = Box_of_Point.vertex_min[1] + (Box_of_Point.vertex_max[1] - Box_of_Point.vertex_min[1]) / 2.0;
            mid_point.z = Box_of_Point.vertex_min[2] + (Box_of_Point.vertex_max[2] - Box_of_Point.vertex_min[2]) / 2.0;
            PointVector().swap(Downsample_Storage);
            Search_by_range(Root_Node, Box_of_Point, Downsample_Storage);   // 看看都有多少点在边框内部
            min_dist = calc_dist(PointToAdd[i], mid_point);                 // 计算要添加的点到中心店的距离
            downsample_result = PointToAdd[i];
            for (int index = 0; index < Downsample_Storage.size(); index++) // 对每个方框内的点进行循环 
            {
                tmp_dist = calc_dist(Downsample_Storage[index], mid_point); // 计算方框内的每个点到中心线的距离
                if (tmp_dist < min_dist)                                    // 找到最小的那个点
                {
                    min_dist = tmp_dist;
                    downsample_result = Downsample_Storage[index];
                }
            }
            if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != Root_Node)        // 如果没有在rebuild
            {
                if (Downsample_Storage.size() > 1 || same_point(PointToAdd[i], downsample_result))
                {
                    if (Downsample_Storage.size() > 0)
                        Delete_by_range(&Root_Node, Box_of_Point, true, true);  // 删除原先方框内的点
                    Add_by_point(&Root_Node, downsample_result, true, Root_Node->division_axis);    // 新增那个距离中心最近的点
                    tmp_counter++;
                }
            }
            else                                                            // 如果在rebuild
            {
                if (Downsample_Storage.size() > 1 || same_point(PointToAdd[i], downsample_result))
                {
                    Operation_Logger_Type operation_delete, operation;
                    operation_delete.boxpoint = Box_of_Point;
                    operation_delete.op = DOWNSAMPLE_DELETE;
                    operation.point = downsample_result;
                    operation.op = ADD_POINT;
                    pthread_mutex_lock(&working_flag_mutex);
                    if (Downsample_Storage.size() > 0)
                        Delete_by_range(&Root_Node, Box_of_Point, false, true);
                    Add_by_point(&Root_Node, downsample_result, false, Root_Node->division_axis);
                    tmp_counter++;
                    if (rebuild_flag)
                    {
                        pthread_mutex_lock(&rebuild_logger_mutex_lock);
                        if (Downsample_Storage.size() > 0)
                            Rebuild_Logger.push(operation_delete);          // 缓存删除以及添加操作
                        Rebuild_Logger.push(operation);
                        pthread_mutex_unlock(&rebuild_logger_mutex_lock);
                    }
                    pthread_mutex_unlock(&working_flag_mutex);
                };
            }
        }
        else        // 如果不进行下采样
        {
            if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != Root_Node)
            {
                Add_by_point(&Root_Node, PointToAdd[i], true, Root_Node->division_axis);    // 就直接添加
            }
            else
            {
                Operation_Logger_Type operation;
                operation.point = PointToAdd[i];
                operation.op = ADD_POINT;
                pthread_mutex_lock(&working_flag_mutex);
                Add_by_point(&Root_Node, PointToAdd[i], false, Root_Node->division_axis);
                if (rebuild_flag)
                {
                    pthread_mutex_lock(&rebuild_logger_mutex_lock);
                    Rebuild_Logger.push(operation);                                     // 或者缓存后添加
                    pthread_mutex_unlock(&rebuild_logger_mutex_lock);
                }
                pthread_mutex_unlock(&working_flag_mutex);
            }
        }
    }
    return tmp_counter;
}
Add_by_point函数

/**
 * 这个函数是一个点一个点的添加,通过递归的方式完成遍历,找到点需要在的位置,然后添加
 * 添加完成后要不断update来更新信息
*/
template <typename PointType>
void KD_TREE<PointType>::Add_by_point(KD_TREE_NODE **root, PointType point, bool allow_rebuild, int father_axis)
{
    if (*root == nullptr)       // 迭代遍历终止条件
    {
        *root = new KD_TREE_NODE;
        InitTreeNode(*root);
        (*root)->point = point;
        (*root)->division_axis = (father_axis + 1) % 3; 
        Update(*root);          // 添加完点后要反向遍历一下
        return;
    }
    (*root)->working_flag = true;
    Operation_Logger_Type add_log;
    struct timespec Timeout;
    add_log.op = ADD_POINT;
    add_log.point = point;
    Push_Down(*root);
    if (((*root)->division_axis == 0 && point.x < (*root)->point.x) || ((*root)->division_axis == 1 && point.y < (*root)->point.y) || ((*root)->division_axis == 2 && point.z < (*root)->point.z))
    {           // 需要添加在左子树的情况
        if ((Rebuild_Ptr == nullptr) || (*root)->left_son_ptr != *Rebuild_Ptr)
        {
            Add_by_point(&(*root)->left_son_ptr, point, allow_rebuild, (*root)->division_axis);
        }
        else
        {
            pthread_mutex_lock(&working_flag_mutex);
            Add_by_point(&(*root)->left_son_ptr, point, false, (*root)->division_axis);
            if (rebuild_flag)
            {
                pthread_mutex_lock(&rebuild_logger_mutex_lock);
                Rebuild_Logger.push(add_log);
                pthread_mutex_unlock(&rebuild_logger_mutex_lock);
            }
            pthread_mutex_unlock(&working_flag_mutex);
        }
    }
    else
    {           // 需要添加在右子树的情况
        if ((Rebuild_Ptr == nullptr) || (*root)->right_son_ptr != *Rebuild_Ptr)
        {
            Add_by_point(&(*root)->right_son_ptr, point, allow_rebuild, (*root)->division_axis);
        }
        else
        {
            pthread_mutex_lock(&working_flag_mutex);
            Add_by_point(&(*root)->right_son_ptr, point, false, (*root)->division_axis);
            if (rebuild_flag)
            {
                pthread_mutex_lock(&rebuild_logger_mutex_lock);
                Rebuild_Logger.push(add_log);
                pthread_mutex_unlock(&rebuild_logger_mutex_lock);
            }
            pthread_mutex_unlock(&working_flag_mutex);
        }
    }
    Update(*root);      // 每个循环遍历退出之后,都进行一下标志位自下而上的更新
    if (Rebuild_Ptr != nullptr && *Rebuild_Ptr == *root && (*root)->TreeSize < Multi_Thread_Rebuild_Point_Num)
        Rebuild_Ptr = nullptr;
    bool need_rebuild = allow_rebuild & Criterion_Check((*root));   // 同时根据平衡条件判断要不要进行树的重建
    if (need_rebuild)
        Rebuild(root);      // 重建,重建以当前节点为root的树,不是从头都重建
    if ((*root) != nullptr)
        (*root)->working_flag = false;
    return;
}

删除功能

Delete_Point_Boxes函数
/**
 * 对外部的删除接口,给入一串方框,要求把方框中的点都删除
*/
template <typename PointType>
int KD_TREE<PointType>::Delete_Point_Boxes(vector<BoxPointType> &BoxPoints)
{
    int tmp_counter = 0;
    for (int i = 0; i < BoxPoints.size(); i++)          // 对方框进行循环
    {
        if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != Root_Node)    // 如果没有在进行rebuild,或者只是局部rebuild
        {
            tmp_counter += Delete_by_range(&Root_Node, BoxPoints[i], true, false);  // 调用Delete_by_range函数进行删除,
        }                                                                           // 由于此时没有rebuild 任务,所以allow rebuild= true
        else                                            // 如果正在进行rebuild,说明树的结果会发生变化
        {
            Operation_Logger_Type operation;
            operation.boxpoint = BoxPoints[i];
            operation.op = DELETE_BOX;
            pthread_mutex_lock(&working_flag_mutex);
            tmp_counter += Delete_by_range(&Root_Node, BoxPoints[i], false, false); // 但是这里依然调用了Delete_by_range,由于
                                                                                    // 此时有rebuild任务,所以allow rebuild = false
            if (rebuild_flag)                           // 如果正在进行多线程rebuild
            {
                pthread_mutex_lock(&rebuild_logger_mutex_lock);
                Rebuild_Logger.push(operation);         // 那就先把删除操作缓存一下
                pthread_mutex_unlock(&rebuild_logger_mutex_lock);
            }
            pthread_mutex_unlock(&working_flag_mutex);
        }
    }
    return tmp_counter;
}

Delete_by_range函数
/**
 * 这个函数执行具体的delete操作,具体的执行过程就是反复递归,然后对节点的是否删除属性进行赋值,然后
 * 根据重建标准,判定是否重建,也就是这里是lazy 删除,不是当场就删除,而是要等待合适的时机,通过
 * 重建树来执行彻底的删除操作
*/
template <typename PointType>
int KD_TREE<PointType>::Delete_by_range(KD_TREE_NODE **root, BoxPointType boxpoint, bool allow_rebuild, bool is_downsample)
{
    if ((*root) == nullptr || (*root)->tree_deleted)
        return 0;
    (*root)->working_flag = true;
    Push_Down(*root);           // 将当前节点状态传递给子节点
    int tmp_counter = 0;
    if (boxpoint.vertex_max[0] <= (*root)->node_range_x[0] || boxpoint.vertex_min[0] > (*root)->node_range_x[1])    // 确定是否在包络上有相交
        return 0;                                                                                                   // 没有相交,说明不用删除直接返回
    if (boxpoint.vertex_max[1] <= (*root)->node_range_y[0] || boxpoint.vertex_min[1] > (*root)->node_range_y[1])
        return 0;
    if (boxpoint.vertex_max[2] <= (*root)->node_range_z[0] || boxpoint.vertex_min[2] > (*root)->node_range_z[1])
        return 0;
    if (boxpoint.vertex_min[0] <= (*root)->node_range_x[0] && boxpoint.vertex_max[0] > (*root)->node_range_x[1] && boxpoint.vertex_min[1] <= (*root)->node_range_y[0] && boxpoint.vertex_max[1] > (*root)->node_range_y[1] && boxpoint.vertex_min[2] <= (*root)->node_range_z[0] && boxpoint.vertex_max[2] > (*root)->node_range_z[1])
    {   // 如果有相交就要删除,这个if不仅是相交,是包含,如果时包含,就直接把这个点下所有的点都删掉
        (*root)->tree_deleted = true;       // 所以这里要删除整个树
        (*root)->point_deleted = true;
        (*root)->need_push_down_to_left = true;     // 需要把这个删除状态传递给子节点
        (*root)->need_push_down_to_right = true;    // 那么什么时候把这个信息向下pushdown呢?
        tmp_counter = (*root)->TreeSize - (*root)->invalid_point_num;
        (*root)->invalid_point_num = (*root)->TreeSize;
        if (is_downsample)
        {
            (*root)->tree_downsample_deleted = true;
            (*root)->point_downsample_deleted = true;
            (*root)->down_del_num = (*root)->TreeSize;
        }
        return tmp_counter;
    }
    if (!(*root)->point_deleted && boxpoint.vertex_min[0] <= (*root)->point.x && boxpoint.vertex_max[0] > (*root)->point.x && boxpoint.vertex_min[1] <= (*root)->point.y && boxpoint.vertex_max[1] > (*root)->point.y && boxpoint.vertex_min[2] <= (*root)->point.z && boxpoint.vertex_max[2] > (*root)->point.z)
    {   // 如果是相交,就要先把当前点删了,然后再去下面遍历其他的子树
        (*root)->point_deleted = true;
        tmp_counter += 1;
        if (is_downsample)
            (*root)->point_downsample_deleted = true;
    }
    Operation_Logger_Type delete_box_log;
    struct timespec Timeout;
    if (is_downsample)
        delete_box_log.op = DOWNSAMPLE_DELETE;
    else
        delete_box_log.op = DELETE_BOX;         // 构造一个操作,然后看看是不是需要根据rebuild状态进行操作缓存。
    delete_box_log.boxpoint = boxpoint;
    if ((Rebuild_Ptr == nullptr) || (*root)->left_son_ptr != *Rebuild_Ptr)      // 这里先接着遍历左树
    {
        tmp_counter += Delete_by_range(&((*root)->left_son_ptr), boxpoint, allow_rebuild, is_downsample);
    }
    else
    {
        pthread_mutex_lock(&working_flag_mutex);
        tmp_counter += Delete_by_range(&((*root)->left_son_ptr), boxpoint, false, is_downsample);   // 这个意思是,我现在原来的树里标注删除
        if (rebuild_flag)                                                                           // 然后等新树建好了,同样的动作再执行一遍
        {
            pthread_mutex_lock(&rebuild_logger_mutex_lock);
            Rebuild_Logger.push(delete_box_log);
            pthread_mutex_unlock(&rebuild_logger_mutex_lock);
        }
        pthread_mutex_unlock(&working_flag_mutex);
    }
    if ((Rebuild_Ptr == nullptr) || (*root)->right_son_ptr != *Rebuild_Ptr)     // 同样遍历右子树删除
    {
        tmp_counter += Delete_by_range(&((*root)->right_son_ptr), boxpoint, allow_rebuild, is_downsample);
    }
    else
    {
        pthread_mutex_lock(&working_flag_mutex);
        tmp_counter += Delete_by_range(&((*root)->right_son_ptr), boxpoint, false, is_downsample);
        if (rebuild_flag)
        {
            pthread_mutex_lock(&rebuild_logger_mutex_lock);
            Rebuild_Logger.push(delete_box_log);
            pthread_mutex_unlock(&rebuild_logger_mutex_lock);
        }
        pthread_mutex_unlock(&working_flag_mutex);
    }
    Update(*root);          // 前面是pushdown,根据父节点确定子节点,现在update,根据子节点再反馈给父节点 保持一致。
    if (Rebuild_Ptr != nullptr && *Rebuild_Ptr == *root && (*root)->TreeSize < Multi_Thread_Rebuild_Point_Num)
        Rebuild_Ptr = nullptr;
    bool need_rebuild = allow_rebuild & Criterion_Check((*root));
    if (need_rebuild)
        Rebuild(root);      // 判断一下,是不是需要重新构造树
    if ((*root) != nullptr)
        (*root)->working_flag = false;
    return tmp_counter;
}

查找功能

Nearest_Search函数

这个是对外的接口函数,内部会调用Search函数

/**
 * Top-K个最近点的搜索
 * 
*/
template <typename PointType>
void KD_TREE<PointType>::Nearest_Search(PointType point, int k_nearest, PointVector &Nearest_Points, vector<float> &Point_Distance, float max_dist)
{
    MANUAL_HEAP q(2 * k_nearest);                               // 首先构造一个堆,
    q.clear();                                                  // 通常利用树来完成最近邻搜索都是配合一个堆来进行
    vector<float>().swap(Point_Distance);
    if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != Root_Node)    // 如果没有重建
    {
        Search(Root_Node, k_nearest, point, q, max_dist);       // 就直接search
    }
    else                                                        // 如果正在重建
    {
        pthread_mutex_lock(&search_flag_mutex);
        while (search_mutex_counter == -1)                      // 就循环等待
        {
            pthread_mutex_unlock(&search_flag_mutex);
            usleep(1);
            pthread_mutex_lock(&search_flag_mutex);
        }
        search_mutex_counter += 1;
        pthread_mutex_unlock(&search_flag_mutex);
        Search(Root_Node, k_nearest, point, q, max_dist);       // 等待结束直接search
        pthread_mutex_lock(&search_flag_mutex);
        search_mutex_counter -= 1;
        pthread_mutex_unlock(&search_flag_mutex);
    }
    int k_found = min(k_nearest, int(q.size()));
    PointVector().swap(Nearest_Points);
    vector<float>().swap(Point_Distance);
    for (int i = 0; i < k_found; i++)                           // 把找到的结果赋给输出变量
    {
        Nearest_Points.insert(Nearest_Points.begin(), q.top().point);
        Point_Distance.insert(Point_Distance.begin(), q.top().dist);
        q.pop();
    }
    return;
}


/**
 * 该函数是具体的search过程
*/
template <typename PointType>
void KD_TREE<PointType>::Search(KD_TREE_NODE *root, int k_nearest, PointType point, MANUAL_HEAP &q, float max_dist)
{
    if (root == nullptr || root->tree_deleted)
        return;
    float cur_dist = calc_box_dist(root, point);
    float max_dist_sqr = max_dist * max_dist;
    if (cur_dist > max_dist_sqr)
        return;
    int retval;
    if (root->need_push_down_to_left || root->need_push_down_to_right)
    {
        retval = pthread_mutex_trylock(&(root->push_down_mutex_lock));
        if (retval == 0)
        {
            Push_Down(root);                                        // 先进性一下属性向下传递
            pthread_mutex_unlock(&(root->push_down_mutex_lock));    // 保证比如,已经删了点不要search等
        }
        else
        {
            pthread_mutex_lock(&(root->push_down_mutex_lock));
            pthread_mutex_unlock(&(root->push_down_mutex_lock));
        }
    }
    if (!root->point_deleted)
    {
        float dist = calc_dist(point, root->point);                 // 判断当前节点和查询点之间的距离,
        if (dist <= max_dist_sqr && (q.size() < k_nearest || dist < q.top().dist))  // 和堆上的第一个点进行比较,
        {
            if (q.size() >= k_nearest)                              // 如果小,就说明是top-k,就加到堆里
                q.pop();
            PointType_CMP current_point{root->point, dist};
            q.push(current_point);
        }
    }
    int cur_search_counter;
    float dist_left_node = calc_box_dist(root->left_son_ptr, point);        // 计算和左子节点的距离
    float dist_right_node = calc_box_dist(root->right_son_ptr, point);      // 计算和有子节点
    if (q.size() < k_nearest || dist_left_node < q.top().dist && dist_right_node < q.top().dist)    // 如果heap没满肯定要搜索
    {                                                                                               // 如果左右距离都比较小,肯定也要接着搜索
        if (dist_left_node <= dist_right_node)                              // 如果左侧距离小,肯定就先找左侧
        {
            if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != root->left_son_ptr)   // 处理多线程rebuild情况
            {
                Search(root->left_son_ptr, k_nearest, point, q, max_dist);  // 递归找左侧
            }
            else
            {
                pthread_mutex_lock(&search_flag_mutex);
                while (search_mutex_counter == -1)                          // 如果正在重建就阻塞
                {
                    pthread_mutex_unlock(&search_flag_mutex);
                    usleep(1);
                    pthread_mutex_lock(&search_flag_mutex);
                }
                search_mutex_counter += 1;
                pthread_mutex_unlock(&search_flag_mutex);
                Search(root->left_son_ptr, k_nearest, point, q, max_dist);  // 还是找左边
                pthread_mutex_lock(&search_flag_mutex);
                search_mutex_counter -= 1;
                pthread_mutex_unlock(&search_flag_mutex);
            }
            if (q.size() < k_nearest || dist_right_node < q.top().dist)     // 如果还没找满,或者右侧有更小的
            {
                if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != root->right_son_ptr)
                {
                    Search(root->right_son_ptr, k_nearest, point, q, max_dist); // 就去找右边
                }
                else
                {
                    pthread_mutex_lock(&search_flag_mutex);                 // 同样处理多线程rebuild的情况
                    while (search_mutex_counter == -1)
                    {
                        pthread_mutex_unlock(&search_flag_mutex);
                        usleep(1);
                        pthread_mutex_lock(&search_flag_mutex);
                    }
                    search_mutex_counter += 1;
                    pthread_mutex_unlock(&search_flag_mutex);
                    Search(root->right_son_ptr, k_nearest, point, q, max_dist);
                    pthread_mutex_lock(&search_flag_mutex);
                    search_mutex_counter -= 1;
                    pthread_mutex_unlock(&search_flag_mutex);
                }
            }
        }
        else                    //这个是右子树距离小的情况,此时先找右子节点,找不满或左侧有更小的再看左侧
        {
            if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != root->right_son_ptr)
            {
                Search(root->right_son_ptr, k_nearest, point, q, max_dist);
            }
            else
            {
                pthread_mutex_lock(&search_flag_mutex);
                while (search_mutex_counter == -1)
                {
                    pthread_mutex_unlock(&search_flag_mutex);
                    usleep(1);
                    pthread_mutex_lock(&search_flag_mutex);
                }
                search_mutex_counter += 1;
                pthread_mutex_unlock(&search_flag_mutex);
                Search(root->right_son_ptr, k_nearest, point, q, max_dist);
                pthread_mutex_lock(&search_flag_mutex);
                search_mutex_counter -= 1;
                pthread_mutex_unlock(&search_flag_mutex);
            }
            if (q.size() < k_nearest || dist_left_node < q.top().dist)
            {
                if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != root->left_son_ptr)
                {
                    Search(root->left_son_ptr, k_nearest, point, q, max_dist);
                }
                else
                {
                    pthread_mutex_lock(&search_flag_mutex);
                    while (search_mutex_counter == -1)
                    {
                        pthread_mutex_unlock(&search_flag_mutex);
                        usleep(1);
                        pthread_mutex_lock(&search_flag_mutex);
                    }
                    search_mutex_counter += 1;
                    pthread_mutex_unlock(&search_flag_mutex);
                    Search(root->left_son_ptr, k_nearest, point, q, max_dist);
                    pthread_mutex_lock(&search_flag_mutex);
                    search_mutex_counter -= 1;
                    pthread_mutex_unlock(&search_flag_mutex);
                }
            }
        }
    }
    else        // 如果heap找满了,并且查询点到左右都有点远,或者右一个优点远
    {
        if (dist_left_node < q.top().dist)  // 对小的那一侧进行搜索
        {
            if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != root->left_son_ptr)
            {
                Search(root->left_son_ptr, k_nearest, point, q, max_dist);
            }
            else
            {
                pthread_mutex_lock(&search_flag_mutex);
                while (search_mutex_counter == -1)
                {
                    pthread_mutex_unlock(&search_flag_mutex);
                    usleep(1);
                    pthread_mutex_lock(&search_flag_mutex);
                }
                search_mutex_counter += 1;
                pthread_mutex_unlock(&search_flag_mutex);
                Search(root->left_son_ptr, k_nearest, point, q, max_dist);
                pthread_mutex_lock(&search_flag_mutex);
                search_mutex_counter -= 1;
                pthread_mutex_unlock(&search_flag_mutex);
            }
        }
        if (dist_right_node < q.top().dist)
        {
            if (Rebuild_Ptr == nullptr || *Rebuild_Ptr != root->right_son_ptr)
            {
                Search(root->right_son_ptr, k_nearest, point, q, max_dist);
            }
            else
            {
                pthread_mutex_lock(&search_flag_mutex);
                while (search_mutex_counter == -1)
                {
                    pthread_mutex_unlock(&search_flag_mutex);
                    usleep(1);
                    pthread_mutex_lock(&search_flag_mutex);
                }
                search_mutex_counter += 1;
                pthread_mutex_unlock(&search_flag_mutex);
                Search(root->right_son_ptr, k_nearest, point, q, max_dist);
                pthread_mutex_lock(&search_flag_mutex);
                search_mutex_counter -= 1;
                pthread_mutex_unlock(&search_flag_mutex);
            }
        }
    }
    return;
}

rebuild功能


/**
 * 重建树的线程执行函数
*/
template <typename PointType>
void KD_TREE<PointType>::multi_thread_rebuild()
{
    bool terminated = false;
    KD_TREE_NODE *father_ptr, **new_node_ptr;
    pthread_mutex_lock(&termination_flag_mutex_lock);
    terminated = termination_flag;
    pthread_mutex_unlock(&termination_flag_mutex_lock);
    while (!terminated)                                         // 是否收到了外部的停止指令,收到就停止
    {
        pthread_mutex_lock(&rebuild_ptr_mutex_lock);
        pthread_mutex_lock(&working_flag_mutex);
        if (Rebuild_Ptr != nullptr)                 // 重建指针不是空的
        {
            /* Traverse and copy */
            if (!Rebuild_Logger.empty())            // 这个不能是空的?
            {
                printf("\n\n\n\n\n\n\n\n\n\n\n ERROR!!! \n\n\n\n\n\n\n\n\n");
            }
            rebuild_flag = true;                    // 表明正在重建
            if (*Rebuild_Ptr == Root_Node)          // 看看是不是从根节点重建
            {
                Treesize_tmp = Root_Node->TreeSize; // 一些参数赋值
                Validnum_tmp = Root_Node->TreeSize - Root_Node->invalid_point_num;
                alpha_bal_tmp = Root_Node->alpha_bal;
                alpha_del_tmp = Root_Node->alpha_del;
            }
            KD_TREE_NODE *old_root_node = (*Rebuild_Ptr);   // 局部变量,记录一下要重建的节点地址
            father_ptr = (*Rebuild_Ptr)->father_ptr;        // 记录一下父节点
            PointVector().swap(Rebuild_PCL_Storage);        // 初始化,swap 是为了高效的清理内存数据
            // Lock Search
            pthread_mutex_lock(&search_flag_mutex);         // 这个while的设计是等待其他线程中的条件满足,再向下进行
            while (search_mutex_counter != 0)               // 如果不满足,就会一直在这里阻塞
            {
                pthread_mutex_unlock(&search_flag_mutex);
                usleep(1);
                pthread_mutex_lock(&search_flag_mutex);
            }
            search_mutex_counter = -1;
            pthread_mutex_unlock(&search_flag_mutex);
            // Lock deleted points cache
            pthread_mutex_lock(&points_deleted_rebuild_mutex_lock);
            flatten(*Rebuild_Ptr, Rebuild_PCL_Storage, MULTI_THREAD_REC);   // 保存所有有效点,同时记录要删除的点
            // Unlock deleted points cache
            pthread_mutex_unlock(&points_deleted_rebuild_mutex_lock);
            // Unlock Search
            pthread_mutex_lock(&search_flag_mutex);
            search_mutex_counter = 0;                       // 重新置位?
            pthread_mutex_unlock(&search_flag_mutex);
            pthread_mutex_unlock(&working_flag_mutex);
            /* Rebuild and update missed operations*/
            Operation_Logger_Type Operation;
            KD_TREE_NODE *new_root_node = nullptr;
            if (int(Rebuild_PCL_Storage.size()) > 0)        // 有效点数大于0
            {
                BuildTree(&new_root_node, 0, Rebuild_PCL_Storage.size() - 1, Rebuild_PCL_Storage);      // 重新构造树
                // Rebuild has been done. Updates the blocked operations into the new tree
                pthread_mutex_lock(&working_flag_mutex);
                pthread_mutex_lock(&rebuild_logger_mutex_lock);
                int tmp_counter = 0;
                while (!Rebuild_Logger.empty())
                {
                    Operation = Rebuild_Logger.front();                             // 取出缓存的操作
                    max_queue_size = max(max_queue_size, Rebuild_Logger.size());
                    Rebuild_Logger.pop();
                    pthread_mutex_unlock(&rebuild_logger_mutex_lock);
                    pthread_mutex_unlock(&working_flag_mutex);
                    run_operation(&new_root_node, Operation);                       // 对树执行缓存操作
                    tmp_counter++;
                    if (tmp_counter % 10 == 0)
                        usleep(1);
                    pthread_mutex_lock(&working_flag_mutex);
                    pthread_mutex_lock(&rebuild_logger_mutex_lock);
                }
                pthread_mutex_unlock(&rebuild_logger_mutex_lock);
            }
            /* Replace to original tree*/
            // pthread_mutex_lock(&working_flag_mutex);
            pthread_mutex_lock(&search_flag_mutex);
            while (search_mutex_counter != 0)
            {
                pthread_mutex_unlock(&search_flag_mutex);
                usleep(1);
                pthread_mutex_lock(&search_flag_mutex);
            }
            search_mutex_counter = -1;
            pthread_mutex_unlock(&search_flag_mutex);
            if (father_ptr->left_son_ptr == *Rebuild_Ptr)   // 将这个新的子树,放到原先的整个树中
            {
                father_ptr->left_son_ptr = new_root_node;
            }
            else if (father_ptr->right_son_ptr == *Rebuild_Ptr)
            {
                father_ptr->right_son_ptr = new_root_node;
            }
            else
            {
                throw "Error: Father ptr incompatible with current node\n";
            }
            if (new_root_node != nullptr)
                new_root_node->father_ptr = father_ptr;
            (*Rebuild_Ptr) = new_root_node;
            int valid_old = old_root_node->TreeSize - old_root_node->invalid_point_num;
            int valid_new = new_root_node->TreeSize - new_root_node->invalid_point_num;
            if (father_ptr == STATIC_ROOT_NODE)
                Root_Node = STATIC_ROOT_NODE->left_son_ptr;
            KD_TREE_NODE *update_root = *Rebuild_Ptr;
            while (update_root != nullptr && update_root != Root_Node)
            {
                update_root = update_root->father_ptr;
                if (update_root->working_flag)
                    break;
                if (update_root == update_root->father_ptr->left_son_ptr && update_root->father_ptr->need_push_down_to_left)
                    break;
                if (update_root == update_root->father_ptr->right_son_ptr && update_root->father_ptr->need_push_down_to_right)
                    break;
                Update(update_root);            // 由于子树发生了更新,要把更新结果传递回原先的树中
            }
            pthread_mutex_lock(&search_flag_mutex);
            search_mutex_counter = 0;
            pthread_mutex_unlock(&search_flag_mutex);
            Rebuild_Ptr = nullptr;
            pthread_mutex_unlock(&working_flag_mutex);
            rebuild_flag = false;
            /* Delete discarded tree nodes */
            delete_tree_nodes(&old_root_node);
        }
        else
        {
            pthread_mutex_unlock(&working_flag_mutex);
        }
        pthread_mutex_unlock(&rebuild_ptr_mutex_lock);
        pthread_mutex_lock(&termination_flag_mutex_lock);
        terminated = termination_flag;
        pthread_mutex_unlock(&termination_flag_mutex_lock);
        usleep(100);
    }
    printf("Rebuild thread terminated normally\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值