pcl包围盒

1. 简介

计算一个点云团的包围盒是日常经常用到的功能,如被应用至:

  • 体积测量
  • 碰撞检测

包围盒有两种:

  • AABB:包围立方体的长宽高与点云坐标系的xyz轴相平行
  • OBB:包围立方体是可旋转的

2. 基本用法



pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;

feature_extractor.setInputCloud(cloud);
feature_extractor.compute();

// 惯性矩
std::vector <float> moment_of_inertia;
// 偏心率
std::vector <float> eccentricity;
Eigen::Vector3f mass_center;

feature_extractor.getMomentOfInertia(moment_of_inertia);
feature_extractor.getEccentricity(eccentricity);
feature_extractor.getMassCenter(mass_center);

bool is_AABB = true;

if(is_AABB) {

    // AABB包围盒
    pcl::PointXYZ min_point_AABB;
    pcl::PointXYZ max_point_AABB;

    feature_extractor.getAABB(min_point_AABB, max_point_AABB);
    
    Eigen::Vector3f center((min_point_AABB.x + max_point_AABB.x) / 2., 
                            (min_point_AABB.y + max_point_AABB.y) / 2.,
                            (min_point_AABB.z + max_point_AABB.z) / 2.);

} else {
    // OBB包围盒
    pcl::PointXYZ min_point_OBB;
    pcl::PointXYZ max_point_OBB;
    pcl::PointXYZ position_OBB;
    Eigen::Matrix3f rotation;

    feature_extractor.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotation);

    Eigen::Vector3f center(position_OBB.x, position_OBB.y, position_OBB.z);
}

注意点:

  • 上面的MomentOfInertiaEstimation类方法只有compute是真正执行运算的,其他get前缀的类方法仅仅获取,不占计算量,所以getOBB不会比getAABB耗时;
  • 注意区别 质心几何中心.

3. 包围算法原理

3.1 PCA降维

参考链接:https://zhuanlan.zhihu.com/p/37777074

PCA常用于数据降维处理

3.1.1 什么是降维?

简单来说,假如在一个q维空间,有n个样本数据,举个例子:
在这里插入图片描述

上图红色的是2维空间的数据,直观上看这些样本能够被"拟合"成一条直线,这里所谓的"拟合"就是用线空间(1维空间)去表达这组样本的所在平面空间(2维空间),这样做以后就可以把原本2维的数据降维1维.

当然这个例子仅从几何角度解析了降维,但实际应用中,数据的维度不局限于空间维度,比如用户的身高/体重/饮食量/健康情况等组成一个抽象的高维空间,我们可以对这个高维空间的数据进行压缩,提取更加精简的指标数据以达到接近的描述!

3.1.2 协方差的引出

先讲个很直观的例子.
日常中如何对首富们的资产进行评估?国际上普遍用某某某有多少"美元"的资产作为衡量指标.但是现实世界中不仅仅只有"美元"一种货币,为什么我们能够仅靠这个指标就能和中国/日本的首富比较?正常说应该用一个向量(美元,日元,人民币)来描述一个首富的资产情况,但我们不需要这么做,因为我们有汇率这个东西,任何货币都可以转换为美元

饶了这么远,只是想说明一个问题,数据空间之所以能被压缩是因为数据本身存在冗余度,或者说是数据空间本身存在相关性.提到相关性,那么很自然想到概率论中的协方差

协方差越大,说明相关性越大,那么可以通过其中一个维度即可表达另一个维度.如上面的"直线拟合"例子,本质上就是因为样本数据在x与y轴上呈现线性相关,所以使用1条线,然后将样本投影在其上面则可以描述这组数据的特性.

3.2 MomentOfInertiaEstimation源码分析

通过阅读pcl的MomentOfInertiaEstimation源码可知,OBB的计算流程如下:

  1. 计算点云质心:点云3维的均值;
  2. 计算点云的3轴的协方差(3*3矩阵);
  3. 对协方差矩阵进行分解(由于是方阵,所以就是求特征向量和特征值);
  4. 特征值大小说明该特征向量(点云包围盒的正交坐标基)的权重(成分占比大小);
  5. 除此以外还做了 惯性矩 曲率 等与包围盒无关的特征,实测发现这些特征的计算十分耗时!因此可以按照pcl的源码进行裁剪

4. 裁剪MomentOfInertiaEstimation实现包围盒

实验发现pcl的MomentOfInertiaEstimation类方法只有compute十分耗时,在边缘计算的板上运行不能够实时性较差,因此阅读pcl的源码发现其进行一些其它操作(惯性矩/曲率),因此如果只需要计算AABB/OBB的包围盒,可以进行裁剪.

CloudProcessor.hpp

class CloudProcessor {

    public:
        using PointT = pcl::PointXYZ;

        /**
         * 获取点云的AABB包围盒
         * @param min_point[out]: 包围盒最小角点
         * @param max_point[out]: 包围盒最大角点
        */
        void ComputeAABB(PointT& min_point, PointT& max_point);

        /**
         * 获取点云的OBB包围盒
         * @param min_point[out]: 包围盒最小角点
         * @param max_point[out]: 包围盒最大角点
         * @param position[out]:  包围盒几何中心
         * @param rotational_matrix[out]:  包围盒的旋转矩阵
        */
        void ComputeOBB(PointT& min_point, PointT& max_point, Eigen::Vector3f& position, Eigen::Matrix3f& rotational_matrix);



        /**
         * 设置待处理点云
         * @param cloud[in]: 默认使用整个点云
        */
        void SetInputCLoud(const pcl::PointCloud<PointT>::ConstPtr& cloud)
        {
            if(!cloud)
                return;
            
            mpCloud = cloud;
            
            mpIndices.reset(new pcl::Indices);
            mpIndices->resize(cloud->size());

            for (size_t i = 0; i < mpIndices->size (); ++i) {
                (*mpIndices)[i] = static_cast<int>(i); 
            }
        }

        /**
         * 设置待处理点云
         * @param cloud[in]: 点云指针
         * @param indices[in]: 待处理的子点云索引数组
        */
        void SetInputCLoud(const pcl::PointCloud<PointT>::ConstPtr& cloud, const pcl::IndicesPtr& indices)
        {
            if(!cloud || !indices)
                return;

            mpCloud = cloud;
            mpIndices = indices;
        }

    private:
        void ComputeMeanValue(Eigen::Vector3f& mean_value, PointT& aabb_min_point, PointT& aabb_max_point);

        void ComputeCovarianceMatrix(const Eigen::Vector3f& mean_value, Eigen::Matrix <float, 3, 3>& covariance_matrix);

        void ComputeEigenVectors(const Eigen::Matrix <float, 3, 3>& covariance_matrix,
            Eigen::Vector3f& major_axis, Eigen::Vector3f& middle_axis, Eigen::Vector3f& minor_axis, 
            float& major_value, float& middle_value, float& minor_value);

        pcl::PointCloud<PointT>::ConstPtr mpCloud;
        pcl::IndicesPtr mpIndices;
};

CloudProcessor.cpp

void CloudProcessor::ComputeAABB(PointT& min_point, PointT& max_point)
{
    Eigen::Vector3f mean_value;
    PointT aabb_min_point;
    PointT aabb_max_point;

    ComputeMeanValue(mean_value, aabb_min_point, aabb_max_point);
}

void CloudProcessor::ComputeOBB(PointT& min_point, PointT& max_point, Eigen::Vector3f& position, Eigen::Matrix3f& rotational_matrix)
{
    Eigen::Vector3f mean_value;
    PointT aabb_min_point, aabb_max_point;

    ComputeMeanValue(mean_value, aabb_min_point, aabb_max_point);

    Eigen::Matrix <float, 3, 3> covariance_matrix;
    ComputeCovarianceMatrix(mean_value, covariance_matrix);

    Eigen::Vector3f major_axis, middle_axis, minor_axis;
    float major_value, middle_value, minor_value;

    ComputeEigenVectors(covariance_matrix, major_axis, middle_axis, minor_axis, 
        major_value, middle_value, minor_value);
    
    PointT obb_min_point, obb_max_point;

    obb_min_point.x = std::numeric_limits <float>::max ();
    obb_min_point.y = std::numeric_limits <float>::max ();
    obb_min_point.z = std::numeric_limits <float>::max ();

    obb_max_point.x = std::numeric_limits <float>::min ();
    obb_max_point.y = std::numeric_limits <float>::min ();
    obb_max_point.z = std::numeric_limits <float>::min ();

    auto number_of_points = static_cast <unsigned int> (mpIndices->size ());
    for (unsigned int i_point = 0; i_point < number_of_points; i_point++)
    {
        float x = ((*mpCloud)[(*mpIndices)[i_point]].x - mean_value (0)) * major_axis (0) +
                  ((*mpCloud)[(*mpIndices)[i_point]].y - mean_value (1)) * major_axis (1) +
                  ((*mpCloud)[(*mpIndices)[i_point]].z - mean_value (2)) * major_axis (2);
        float y = ((*mpCloud)[(*mpIndices)[i_point]].x - mean_value (0)) * middle_axis (0) +
                  ((*mpCloud)[(*mpIndices)[i_point]].y - mean_value (1)) * middle_axis (1) +
                  ((*mpCloud)[(*mpIndices)[i_point]].z - mean_value (2)) * middle_axis (2);
        float z = ((*mpCloud)[(*mpIndices)[i_point]].x - mean_value (0)) * minor_axis (0) +
                  ((*mpCloud)[(*mpIndices)[i_point]].y - mean_value (1)) * minor_axis (1) +
                  ((*mpCloud)[(*mpIndices)[i_point]].z - mean_value (2)) * minor_axis (2);

        if (x <= obb_min_point.x) obb_min_point.x = x;
        if (y <= obb_min_point.y) obb_min_point.y = y;
        if (z <= obb_min_point.z) obb_min_point.z = z;

        if (x >= obb_max_point.x) obb_max_point.x = x;
        if (y >= obb_max_point.y) obb_max_point.y = y;
        if (z >= obb_max_point.z) obb_max_point.z = z;
    }

    rotational_matrix << major_axis (0), middle_axis (0), minor_axis (0),
                         major_axis (1), middle_axis (1), minor_axis (1),
                         major_axis (2), middle_axis (2), minor_axis (2);

    Eigen::Vector3f shift (
        (obb_max_point.x + obb_min_point.x) / 2.0f,
        (obb_max_point.y + obb_min_point.y) / 2.0f,
        (obb_max_point.z + obb_min_point.z) / 2.0f);

    obb_min_point.x -= shift (0);
    obb_min_point.y -= shift (1);
    obb_min_point.z -= shift (2);

    obb_max_point.x -= shift (0);
    obb_max_point.y -= shift (1);
    obb_max_point.z -= shift (2);

    position = mean_value + rotational_matrix * shift;

    min_point = obb_min_point;
    max_point = obb_max_point;
}


void CloudProcessor::ComputeMeanValue(Eigen::Vector3f& mean_value, PointT& aabb_min_point, PointT& aabb_max_point)
{
    mean_value (0) = 0.0f;
    mean_value (1) = 0.0f;
    mean_value (2) = 0.0f;

    aabb_min_point.x = std::numeric_limits <float>::max ();
    aabb_min_point.y = std::numeric_limits <float>::max ();
    aabb_min_point.z = std::numeric_limits <float>::max ();

    aabb_max_point.x = -std::numeric_limits <float>::max ();
    aabb_max_point.y = -std::numeric_limits <float>::max ();
    aabb_max_point.z = -std::numeric_limits <float>::max ();

    auto number_of_points = static_cast <unsigned int> (mpIndices->size ());
    for (unsigned int i_point = 0; i_point < number_of_points; i_point++)
    {
        mean_value (0) += (*mpCloud)[(*mpIndices)[i_point]].x;
        mean_value (1) += (*mpCloud)[(*mpIndices)[i_point]].y;
        mean_value (2) += (*mpCloud)[(*mpIndices)[i_point]].z;

        if ((*mpCloud)[(*mpIndices)[i_point]].x <= aabb_min_point.x) aabb_min_point.x = (*mpCloud)[(*mpIndices)[i_point]].x;
        if ((*mpCloud)[(*mpIndices)[i_point]].y <= aabb_min_point.y) aabb_min_point.y = (*mpCloud)[(*mpIndices)[i_point]].y;
        if ((*mpCloud)[(*mpIndices)[i_point]].z <= aabb_min_point.z) aabb_min_point.z = (*mpCloud)[(*mpIndices)[i_point]].z;

        if ((*mpCloud)[(*mpIndices)[i_point]].x >= aabb_max_point.x) aabb_max_point.x = (*mpCloud)[(*mpIndices)[i_point]].x;
        if ((*mpCloud)[(*mpIndices)[i_point]].y >= aabb_max_point.y) aabb_max_point.y = (*mpCloud)[(*mpIndices)[i_point]].y;
        if ((*mpCloud)[(*mpIndices)[i_point]].z >= aabb_max_point.z) aabb_max_point.z = (*mpCloud)[(*mpIndices)[i_point]].z;
    }

    if (number_of_points == 0)
        number_of_points = 1;

    mean_value (0) /= number_of_points;
    mean_value (1) /= number_of_points;
    mean_value (2) /= number_of_points;
}

void CloudProcessor::ComputeCovarianceMatrix(const Eigen::Vector3f& mean_value, Eigen::Matrix <float, 3, 3>& covariance_matrix)
{
    covariance_matrix.setZero ();

    auto number_of_points = static_cast <unsigned int> (mpIndices->size ());
    float factor = 1.0f / static_cast <float> ((number_of_points - 1 > 0)?(number_of_points - 1):1);
    for (unsigned int i_point = 0; i_point < number_of_points; i_point++)
    {
        Eigen::Vector3f current_point (0.0f, 0.0f, 0.0f);
        current_point (0) = (*mpCloud)[(*mpIndices)[i_point]].x - mean_value (0);
        current_point (1) = (*mpCloud)[(*mpIndices)[i_point]].y - mean_value (1);
        current_point (2) = (*mpCloud)[(*mpIndices)[i_point]].z - mean_value (2);

        covariance_matrix += current_point * current_point.transpose ();
    }

    covariance_matrix *= factor;
}

void CloudProcessor::ComputeEigenVectors(const Eigen::Matrix <float, 3, 3>& covariance_matrix,
    Eigen::Vector3f& major_axis, Eigen::Vector3f& middle_axis, Eigen::Vector3f& minor_axis, 
    float& major_value, float& middle_value, float& minor_value)
{
    Eigen::EigenSolver <Eigen::Matrix <float, 3, 3> > eigen_solver;
    eigen_solver.compute (covariance_matrix);

    Eigen::EigenSolver <Eigen::Matrix <float, 3, 3> >::EigenvectorsType eigen_vectors;
    Eigen::EigenSolver <Eigen::Matrix <float, 3, 3> >::EigenvalueType eigen_values;
    eigen_vectors = eigen_solver.eigenvectors ();
    eigen_values = eigen_solver.eigenvalues ();

    unsigned int temp = 0;
    unsigned int major_index = 0;
    unsigned int middle_index = 1;
    unsigned int minor_index = 2;

    if (eigen_values.real () (major_index) < eigen_values.real () (middle_index))
    {
        temp = major_index;
        major_index = middle_index;
        middle_index = temp;
    }

    if (eigen_values.real () (major_index) < eigen_values.real () (minor_index))
    {
        temp = major_index;
        major_index = minor_index;
        minor_index = temp;
    }

    if (eigen_values.real () (middle_index) < eigen_values.real () (minor_index))
    {
        temp = minor_index;
        minor_index = middle_index;
        middle_index = temp;
    }

    major_value = eigen_values.real () (major_index);
    middle_value = eigen_values.real () (middle_index);
    minor_value = eigen_values.real () (minor_index);

    major_axis = eigen_vectors.col (major_index).real ();
    middle_axis = eigen_vectors.col (middle_index).real ();
    minor_axis = eigen_vectors.col (minor_index).real ();

    major_axis.normalize ();
    middle_axis.normalize ();
    minor_axis.normalize ();

    float det = major_axis.dot (middle_axis.cross (minor_axis));
    if (det <= 0.0f)
    {
        major_axis (0) = -major_axis (0);
        major_axis (1) = -major_axis (1);
        major_axis (2) = -major_axis (2);
    }
}

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PCL(点云库)提供了计算点云包围的函数,可以方便地计算点云的边界框和轴对齐的边界框。下面是一个使用PCL计算点云包围的示例代码: ```cpp #include <iostream> #include <pcl/point_types.h> #include <pcl/features/moment_of_inertia_estimation.h> int main() { // 生成一个简单的点云数据 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); cloud->width = 5; cloud->height = 1; cloud->points.resize(cloud->width * cloud->height); for (std::size_t i = 0; i < cloud->points.size(); ++i) { cloud->points[i].x = rand() / (RAND_MAX + 1.0); cloud->points[i].y = rand() / (RAND_MAX + 1.0); cloud->points[i].z = rand() / (RAND_MAX + 1.0); } // 计算点云的包围 pcl::MomentOfInertiaEstimation<pcl::PointXYZ> feature_extractor; feature_extractor.setInputCloud(cloud); feature_extractor.compute(); // 获取点云包围的中心和尺寸 pcl::PointXYZ min_point, max_point, pos, rot_axis; Eigen::Matrix3f rot_mat; feature_extractor.getAABB(min_point, max_point); feature_extractor.getOBB(pos, rot_mat, rot_axis, min_point, max_point); // 打印点云包围的中心和尺寸 std::cout << "AABB min point: " << min_point << std::endl; std::cout << "AABB max point: " << max_point << std::endl; std::cout << "OBB center: " << pos << std::endl; std::cout << "OBB size: " << max_point - min_point << std::endl; return 0; } ``` 在这个示例中,我们使用PCL生成了一个简单的点云数据,并使用MomentOfInertiaEstimation类计算了点云的包围。通过调用getAABB和getOBB函数,我们可以获取点云包围的中心和尺寸。其中,AABB表示轴对齐的边界框,OBB表示任意方向的边界框。最后,我们将点云包围的中心和尺寸打印出来。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值