在计算几何领域,凸包(Convex Hull) 是一个基础且重要的概念。给定二维或三维空间中的点集,凸包是能够包含所有点的最小凸多边形(2D)或凸多面体(3D)。PCL 提供了 pcl::ConvexHull 类,借助 Qhull 库实现凸包计算,广泛应用于碰撞检测、形状分析、物体识别等场景。
1. 凸包
凸包的核心思想是用最小的凸多边形/凸多面体包围所有输入点。所谓“凸”是指:对于集合内的任意两点,连接它们的线段完全位于集合内部。

凸包具有以下特点:
- 最小包围性:凸包是包含所有点的最小凸集;
- 顶点子集:凸包的顶点是原始点集的子集;
- 唯一性:给定点集的凸包是唯一的;
- 维度依赖:2D凸包是多边形,3D凸包是多面体。
2. ConvexHull
pcl::ConvexHull 类用于实现凸包计算能力,分析源码可知,算法执行步骤如下:
- 维度判断:当维度为默认值0时,会根据输入点云的维度自动判断是2D(共面)还是3D;建议主动设置维度
- 数据准备:将点云数据转换为Qhull所需的坐标数组;
- Qhull计算:调用Qhull库计算凸包;
- 结果提取:提取凸包顶点和面片信息;
- 面积体积:若启用,计算凸包的面积和体积。
2.1 接口
主要接口为:
//计算凸包(仅输出顶点)
//@param points 凸包顶点点云
void reconstruct(PointCloud &points);
//计算凸包(输出顶点和面片)
//@param points 凸包顶点点云
//@param polygons 凸包面片集合。2D凸包仅1个面片(凸多边形),3D凸包为多个三角面片,
// 每个面片的vertices存储顶点索引,用于描述凸包表面的拓扑结构
void reconstruct(PointCloud &points, std::vector<pcl::Vertices> &polygons);
//设置是否计算面积和体积
//@param value true表示计算面积和体积,false表示不计算
//注意:启用此选项时,Qhull会输出信息到控制台
void setComputeAreaVolume(bool value);
//获取凸包总面积
//@return 凸包面积(2D为多边形面积,3D为表面积)
double getTotalArea() const;
//获取凸包总体积
//@return 凸包体积(仅3D有效,2D返回0)
double getTotalVolume() const;
//设置输入数据维度
//@param dimension 维度,2表示2D,3表示3D
//若不设置,将自动判断
//仅支持2/3,其他将会报错
void setDimension(int dimension);
//获取输入数据维度
//@return 维度(2或3)
int getDimension() const;
//获取凸包顶点在原始点云中的索引
//@param hull_point_indices 凸包顶点索引
void getHullPointIndices(pcl::PointIndices &hull_point_indices) const;
在应用 ConvexHull 时,需要注意以下事项:
- 输入点云:必须包含至少3个点,否则计算结果无意义;
- 维度设置:虽然该函数可以自动进行维度判断,但是仍旧建议手动设置维度,以避免自动判断错误;当前仅支持2D和3D凸包计算。
- 计算开销:凸包计算时间与点云规模成正比,对大规模点云需谨慎使用。
2.2 对比记忆
为了更清晰地理解 ConvexHull 的特点,将2D凸包与3D凸包进行全面对比:
| 对比维度 |
OpenCV凸包 |
2D凸包 |
3D凸包 |
| 输出形状 |
凸多边形 |
凸多边形 |
凸多面体 |
| 面片数量 |
1个(单个多边形) |
1个(单个多边形) |
多个(三角面片) |
| 面积含义 |
轮廓面积 |
多边形面积 |
表面积 |
| 体积含义 |
不支持 |
0 |
实际体积 |
| 投影需求 |
不需要 |
需要(投影到XY/YZ/XZ平面) |
不需要 |
| 输入类型 |
轮廓点(vector) |
点云(PointCloud) |
点云(PointCloud) |
| 输出类型 |
顶点索引 |
点云+面片索引 |
点云+面片索引 |
| 底层算法 |
Sklansky算法 |
Qhull |
Qhull |
| 应用场景 |
图像处理、轮廓分析、手势识别 |
平面轮廓提取 |
物体包围盒、碰撞检测 |
3. 代码示例
为了更直观地展示 ConvexHull 的使用方法,整理示例代码如下:
3.1 基本示例:3D凸包计算
#include <pcl/surface/convex_hull.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
int main(){
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("input.pcd", *cloud_in);
pcl::ConvexHull<pcl::PointXYZ> chull;
chull.setInputCloud(cloud_in);
chull.setDimension(3);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_hull(new pcl::PointCloud<pcl::PointXYZ>);
std::vector<pcl::Vertices> polygons;
chull.reconstruct(*cloud_hull, polygons);
std::cout << "Convex hull has " << cloud_hull->size() << " points" << std::endl;
std::cout << "Convex hull has " << polygons.size() << " polygons" << std::endl;
pcl::io::savePCDFile("hull.pcd", *cloud_hull);
return 0;
}
3.2 进阶示例:计算凸包面积和体积
#include <pcl/surface/convex_hull.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
int main(){
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("input.pcd", *cloud_in);
pcl::ConvexHull<pcl::PointXYZ> chull;
chull.setInputCloud(cloud_in);
chull.setDimension(3);
chull.setComputeAreaVolume(true);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_hull(new pcl::PointCloud<pcl::PointXYZ>);
chull.reconstruct(*cloud_hull);
std::cout << "Convex hull area: " << chull.getTotalArea() << std::endl;
std::cout << "Convex hull volume: " << chull.getTotalVolume() << std::endl;
pcl::io::savePCDFile("hull.pcd", *cloud_hull);
return 0;
}
3.3 高级示例:输出PolygonMesh格式
#include <pcl/surface/convex_hull.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/vtk_io.h>
int main(){
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("input.pcd", *cloud_in);
pcl::ConvexHull<pcl::PointXYZ> chull;
chull.setInputCloud(cloud_in);
chull.setDimension(3);
pcl::PolygonMesh mesh;
chull.reconstruct(mesh);
std::cout << "Mesh has " << mesh.cloud.width * mesh.cloud.height << " points" << std::endl;
std::cout << "Mesh has " << mesh.polygons.size() << " polygons" << std::endl;
pcl::io::saveVTKFile("convex_hull.vtk", mesh);
return 0;
}
4. 总结
本文重点介绍了 pcl::ConvexHull 类的核心原理、主要参数及使用方法。作为一种基础的计算几何算法,凸包在算法设计中占有重要地位。通过对比分析,展示了2D和3D凸包计算的区别,并提供了详细的C++代码示例,希望能帮助你在3D点云处理项目中更有效地应用该功能,实现精确的碰撞检测或物体识别。