今天学习这段关于基于惯性矩和偏心率的描述子的内容,从理论到代码逐部分拆解:
文章目录
一、整体功能与目标
这段代码的作用是:用 pcl::MomentOfInertiaEstimation
类,计算点云的 惯性矩、偏心率 等描述子,同时提取 轴对齐包围盒(AABB)和有向包围盒(OBB) ,最后可视化点云、包围盒和特征向量( eigen vectors )。
简单说,就是分析点云的几何形状,用惯性矩、偏心率描述它的 “形状特点”,用包围盒框住它,还能可视化这些结果,帮你理解点云的整体几何特征~
二、理论基础(Theoretical Primer )
1. 关键概念
- 惯性矩(Moment of Inertia):描述点云绕某个轴的 “惯性”,反映点云在该方向的分布情况(值越大,分布越广 );
- 偏心率(Eccentricity):描述点云投影的 “椭圆扁度”,反映点云在某个方向的伸展程度;
- AABB(Axis-Aligned Bounding Box):轴对齐包围盒,边与坐标轴平行,计算简单但可能不够贴合;
- OBB(Oriented Bounding Box):有向包围盒,边与点云的主方向对齐,更贴合形状但计算稍复杂;
- 特征向量(Eigen Vectors):点云的主方向,代表点云延伸最长、次长、最短的三个方向。
2. 核心思路
-
协方差矩阵与特征分解:先算点云的协方差矩阵,提取它的 特征值和特征向量。你可以认为所得特征向量是归一化的,并且总是形成右手坐标系(主特征向量代表 X 轴,次特征向量代表 Z 轴);
-
迭代旋转与不变性:通过旋转主特征向量(保持旋转顺序不变 ),让描述子对 点云整体旋转不敏感(不管点云怎么转,描述子差不多 );
-
惯性矩与偏心率计算:对每个旋转后的主方向,计算惯性矩;同时把点云投影到该方向,计算投影的偏心率;
-
包围盒提取:AABB 是轴对齐的长方体,OBB 是根据特征向量方向对齐的长方体(更贴合点云形状 )。
三、代码结构与流程拆解
#include <vector>
#include <thread>
#include <pcl/features/moment_of_inertia_estimation.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
using namespace std::chrono_literals;
int main (int argc, char** argv)
{
if (argc != 2)
return (0);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ> ());
if (pcl::io::loadPCDFile (argv[1], *cloud) == -1)
return (-1);
pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;
feature_extractor.setInputCloud (cloud);
feature_extractor.compute ();
std::vector <float> moment_of_inertia;
std::vector <float> eccentricity;
pcl::PointXYZ min_point_AABB;
pcl::PointXYZ max_point_AABB;
pcl::PointXYZ min_point_OBB;
pcl::PointXYZ max_point_OBB;
pcl::PointXYZ position_OBB;
Eigen::Matrix3f rotational_matrix_OBB;
float major_value, middle_value, minor_value;
Eigen::Vector3f major_vector, middle_vector, minor_vector;
Eigen::Vector3f mass_center;
feature_extractor.getMomentOfInertia (moment_of_inertia);
feature_extractor.getEccentricity (eccentricity);
feature_extractor.getAABB (min_point_AABB, max_point_AABB);
feature_extractor.getOBB (min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
feature_extractor.getEigenValues (major_value, middle_value, minor_value);
feature_extractor.getEigenVectors (major_vector, middle_vector, minor_vector);
feature_extractor.getMassCenter (mass_center);
pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
viewer->setBackgroundColor (0, 0, 0);
viewer->addCoordinateSystem (1.0);
viewer->initCameraParameters ();
viewer->addPointCloud<pcl::PointXYZ> (cloud, "sample cloud");
viewer->addCube (min_point_AABB.x, max_point_AABB.x, min_point_AABB.y, max_point_AABB.y, min_point_AABB.z, max_point_AABB.z, 1.0, 1.0, 0.0, "AABB");
viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "AABB");
Eigen::Vector3f position (position_OBB.x, position_OBB.y, position_OBB.z);
Eigen::Quaternionf quat (rotational_matrix_OBB);
viewer->addCube (position, quat, max_point_OBB.x - min_point_OBB.x, max_point_OBB.y - min_point_OBB.y, max_point_OBB.z - min_point_OBB.z, "OBB");
viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "OBB");
pcl::PointXYZ center (mass_center (0), mass_center (1), mass_center (2));
pcl::PointXYZ x_axis (major_vector (0) + mass_center (0), major_vector (1) + mass_center (1), major_vector (2) + mass_center (2));
pcl::PointXYZ y_axis (middle_vector (0) + mass_center (0), middle_vector (1) + mass_center (1), middle_vector (2) + mass_center (2));
pcl::PointXYZ z_axis (minor_vector (0) + mass_center (0), minor_vector (1) + mass_center (1), minor_vector (2) + mass_center (2));
viewer->addLine (center, x_axis, 1.0f, 0.0f, 0.0f, "major eigen vector");
viewer->addLine (center, y_axis, 0.0f, 1.0f, 0.0f, "middle eigen vector");
viewer->addLine (center, z_axis, 0.0f, 0.0f, 1.0f, "minor eigen vector");
while(!viewer->wasStopped())
{
viewer->spinOnce (100);
std::this_thread::sleep_for(100ms);
}
return (0);
}
代码分 点云加载、特征计算、结果提取、可视化 几个步骤,逐个讲:
1. 点云加载
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ> ());
if (pcl::io::loadPCDFile (argv[1], *cloud) == -1)
return (-1);
作用:从命令行参数指定的 .pcd
文件加载点云数据。
2. 特征计算
pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;
feature_extractor.setInputCloud (cloud);
feature_extractor.compute ();
- 实例化
MomentOfInertiaEstimation
类,设置输入点云,调用compute
计算惯性矩、偏心率、包围盒、特征向量等。
3. 结果提取
std::vector <float> moment_of_inertia; // 惯性矩
std::vector <float> eccentricity; // 偏心率
pcl::PointXYZ min_point_AABB, max_point_AABB; // AABB 的最小、最大点
pcl::PointXYZ min_point_OBB, max_point_OBB, position_OBB; // OBB 的参数
Eigen::Matrix3f rotational_matrix_OBB; // OBB 的旋转矩阵
float major_value, middle_value, minor_value; // 特征值(主、中、次 )
Eigen::Vector3f major_vector, middle_vector, minor_vector; // 特征向量
Eigen::Vector3f mass_center; // 质心
// 从 feature_extractor 中提取结果
feature_extractor.getMomentOfInertia (moment_of_inertia);
feature_extractor.getEccentricity (eccentricity);
feature_extractor.getAABB (min_point_AABB, max_point_AABB);
feature_extractor.getOBB (...);
feature_extractor.getEigenValues (...);
feature_extractor.getEigenVectors (...);
feature_extractor.getMassCenter (mass_center);
- 这些变量存储了计算出的形状描述子、包围盒参数、特征值和特征向量。
4. 可视化
pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
viewer->setBackgroundColor (0, 0, 0); // 黑色背景
viewer->addCoordinateSystem (1.0); // 添加坐标系
viewer->initCameraParameters (); // 初始化相机参数
viewer->addPointCloud<pcl::PointXYZ> (cloud, "sample cloud"); // 添加点云
// 添加 AABB(黄色线框 )
viewer->addCube (min_point_AABB.x, max_point_AABB.x, ..., "AABB");
viewer->setShapeRenderingProperties(..., PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "AABB");
// 添加 OBB(红色线框 )
Eigen::Quaternionf quat (rotational_matrix_OBB); // 从旋转矩阵转四元数
viewer->addCube (position, quat, ..., "OBB");
viewer->setShapeRenderingProperties(..., PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "OBB");
// 添加特征向量(红、绿、蓝线 )
pcl::PointXYZ center (mass_center (0), ...); // 质心
pcl::PointXYZ x_axis (major_vector (0)+mass_center (0), ...); // 主特征向量末端点
viewer->addLine (center, x_axis, 1.0f, 0.0f, 0.0f, "major eigen vector"); // 红线
// 同理添加中、次特征向量(绿、蓝线 )
// 保持可视化窗口显示
while(!viewer->wasStopped())
{
viewer->spinOnce (100);
std::this_thread::sleep_for(100ms);
}
-
AABB:用黄色线框显示,边与坐标轴平行;
-
OBB:用红色线框显示,边与点云主方向对齐,更贴合形状;
-
特征向量:从质心出发,沿主、中、次特征向量方向画红、绿、蓝线,直观展示点云的主方向。
四、编译与运行
运行命令
./moment_of_inertia lamppost.pcd
lamppost.pcd
是点云文件名,替换成你自己的点云路径即可;- 运行后会弹出 3D 窗口,显示点云、AABB(黄色 )、OBB(红色 )和特征向量(红、绿、蓝 )。
五、总结
这段代码完整流程是:
加载点云 → 计算惯性矩、偏心率、包围盒、特征向量 → 提取结果 → 可视化点云、包围盒、特征向量
核心是理解:
- 惯性矩和偏心率是描述点云形状的 “数值特征”,能区分不同几何分布的点云;
- AABB 简单但可能宽松,OBB 更贴合点云形状,但计算依赖特征向量;
- 特征向量代表点云的主方向,可视化后能直观看到点云 “延伸最长、次长、最短” 的方向。
你可以换不同的点云(比如立方体、球体 ),观察惯性矩、偏心率和包围盒的变化,感受这些描述子对形状的刻画能力~
Ps.
内容源自pcl官方文档Introduction — Point Cloud Library 0.0 documentation
本文为基于 PCL 官方文档的学习整理与个人理解