PCL (Point Cloud Library) 是当前最主流的开源点云处理库,而 common.h 作为其核心基础文件,包含了点云处理中最基础的数学运算和几何计算工具。这个文件不依赖于任何复杂的数据结构,而是为整个 PCL 库提供最底层的支持。
common.h 采用模块化设计,主要包含以下几类功能:
文件开头的预处理指令体现了对硬件加速的支持:
cpp复制#ifdef __SSE__
#include <xmmintrin.h> // 用于 __m128 类型(SSE指令集)
#endif
#ifdef __AVX__
#include <immintrin.h> // 用于 __m256 类型(AVX指令集)
#endif
common.h 中的函数具有以下特点:
getAngle3D 函数是计算两个3D向量夹角的核心工具,提供两种重载版本:
cpp复制inline double getAngle3D(const Eigen::Vector4f &v1, const Eigen::Vector4f &v2, bool in_degree = false);
inline double getAngle3D(const Eigen::Vector3f &v1, const Eigen::Vector3f &v2, bool in_degree = false);
函数内部处理流程:
关键点:数值稳定性处理确保在向量平行或反平行时仍能正确计算
PCL 针对现代CPU提供了SIMD优化版本:
cpp复制#ifdef __SSE__
inline __m128 acos_SSE(const __m128 &x);
inline __m128 getAcuteAngle3DSSE(...);
#endif
#ifdef __AVX__
inline __m256 acos_AVX(const __m256 &x);
inline __m256 getAcuteAngle3DAVX(...);
#endif
| 版本 | 并行度 | 相对速度 |
|---|---|---|
| 标量 | 1 | 1x |
| SSE | 4 | 3.5-4x |
| AVX | 8 | 7-8x |
getMeanStd 函数实现:
cpp复制inline void getMeanStd(const std::vector<float> &values, double &mean, double &stddev)
{
// 输入检查
if(values.empty()) {
PCL_THROW_EXCEPTION(BadArgumentException, "Input array must have at least 1 element.");
}
// 计算逻辑
double sum = 0, sq_sum = 0;
for(const float &value : values) {
sum += value;
sq_sum += value * value;
}
mean = sum / values.size();
double variance = (sq_sum - sum*sum/values.size()) / (values.size()-1);
stddev = sqrt(variance);
}
computeMedian 使用快速选择算法(nth_element)实现O(n)复杂度:
cpp复制template<typename IteratorT, typename Functor>
inline auto computeMedian(IteratorT begin, IteratorT end, Functor f) noexcept
{
const std::size_t size = std::distance(begin, end);
const std::size_t mid = size/2;
if(size%2==0) { // 偶数个元素
std::nth_element(begin, begin + (mid-1), end);
return (f(begin[mid-1]) + f(*(std::min_element(begin + mid, end)))) / 2.0;
}
else { // 奇数个元素
std::nth_element(begin, begin + mid, end);
return f(begin[mid]);
}
}
getMinMax3D 系列函数用于计算点云的包围盒:
cpp复制template <typename PointT>
inline void getMinMax3D(const pcl::PointCloud<PointT> &cloud,
Eigen::Vector4f &min_pt, Eigen::Vector4f &max_pt)
{
min_pt.setConstant(std::numeric_limits<float>::max());
max_pt.setConstant(std::numeric_limits<float>::lowest());
if(cloud.is_dense) {
for(const auto& point: cloud.points) {
const pcl::Vector4fMapConst pt = point.getVector4fMap();
min_pt = min_pt.cwiseMin(pt);
max_pt = max_pt.cwiseMax(pt);
}
}
else {
// 处理含NaN点的情况
}
}
getPointsInBox 实现边界框内点查询:
cpp复制template <typename PointT>
inline void getPointsInBox(const pcl::PointCloud<PointT> &cloud,
Eigen::Vector4f &min_pt, Eigen::Vector4f &max_pt,
Indices &indices)
{
indices.resize(cloud.size());
int l = 0;
for(std::size_t i = 0; i < cloud.size(); ++i) {
if(!cloud.is_dense && !std::isfinite(cloud[i].x)) continue;
if(cloud[i].x >= min_pt[0] && cloud[i].x <= max_pt[0] &&
cloud[i].y >= min_pt[1] && cloud[i].y <= max_pt[1] &&
cloud[i].z >= min_pt[2] && cloud[i].z <= max_pt[2]) {
indices[l++] = i;
}
}
indices.resize(l);
}
calculatePolygonArea 使用叉积法计算平面多边形面积:
cpp复制template<typename PointT>
inline float calculatePolygonArea(const pcl::PointCloud<PointT> &polygon)
{
float area = 0.0f;
Eigen::Vector3f va,vb,res(0,0,0);
for(int i = 0; i < polygon.size(); ++i) {
int j = (i + 1) % polygon.size();
va = polygon[i].getVector3fMap();
vb = polygon[j].getVector3fMap();
res += va.cross(vb);
}
return res.norm() * 0.5f;
}
getCircumcircleRadius 实现三角形外接圆计算:
cpp复制template <typename PointT>
inline double getCircumcircleRadius(const PointT &pa, const PointT &pb, const PointT &pc)
{
Eigen::Vector4f p1(pa.x, pa.y, pa.z, 0);
Eigen::Vector4f p2(pb.x, pb.y, pb.z, 0);
Eigen::Vector4f p3(pc.x, pc.y, pc.z, 0);
double a = (p2 - p1).norm();
double b = (p3 - p2).norm();
double c = (p1 - p3).norm();
double s = (a + b + c) / 2.0;
double area = sqrt(s * (s-a) * (s-b) * (s-c));
return (a * b * c) / (4.0 * area);
}
结合OpenMP或TBB实现多线程加速:
cpp复制#pragma omp parallel for
for(size_t i = 0; i < cloud.size(); ++i) {
// 处理每个点
}
利用getPointsInBox实现区域裁剪:
cpp复制pcl::PointCloud<pcl::PointXYZ>::Ptr cropBox(
const pcl::PointCloud<pcl::PointXYZ>::Ptr &input,
const Eigen::Vector4f &min_pt,
const Eigen::Vector4f &max_pt)
{
pcl::Indices indices;
pcl::getPointsInBox(*input, min_pt, max_pt, indices);
auto output = std::make_shared<pcl::PointCloud<pcl::PointXYZ>>();
pcl::copyPointCloud(*input, indices, *output);
return output;
}
在FPFH特征计算中使用角度计算:
cpp复制void computeFPFHFeature(const pcl::PointCloud<pcl::PointXYZ> &cloud,
const pcl::Indices &indices,
pcl::FPFHSignature33 &feature)
{
// 计算法向量间角度
for(size_t i = 0; i < indices.size(); ++i) {
double angle = pcl::getAngle3D(
cloud[indices[i]].getNormalVector4fMap(),
cloud[indices[(i+1)%indices.size()]].getNormalVector4fMap());
// 更新特征直方图
// ...
}
}
问题现象:acos计算返回NaN
解决方案:
cpp复制double rad = v1.normalized().dot(v2.normalized());
rad = std::max(-1.0, std::min(1.0, rad)); // 强制限制范围
检查CPU支持:
cpp复制#include <cpuid.h>
bool hasAVX() {
unsigned int eax, ebx, ecx, edx;
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
return (ecx & bit_AVX) ? true : false;
}
示例:添加球面距离计算:
cpp复制inline double getSphericalDistance(const Eigen::Vector3f &p1,
const Eigen::Vector3f &p2,
double radius)
{
double central_angle = getAngle3D(p1, p2);
return radius * central_angle;
}
使用编译器内置函数实现新操作:
cpp复制#ifdef __AVX2__
inline __m256 customAVXOp(__m256 a, __m256 b)
{
return _mm256_fmadd_ps(a, b, _mm256_set1_ps(1.0f));
}
#endif
通过深入理解common.h的设计和实现,开发者可以更高效地使用PCL库,并在需要时进行定制化扩展。这些基础函数虽然简单,但构成了点云处理的核心基石。