【SLAM中的点云处理:从基础到实战】

最近一直在学SLAM算法,发现点云处理是非常非常重要的,我就再认真学了一遍关于点云处理的内容(看了高翔老师的一本书——《自动驾驶与机器人中的SLAM技术:从理论到实践》,写得非常好,还有配套的代码),这篇博客就作为我的点云处理学习笔记,分享给大家!

1. 引言

  • 点云在SLAM中的核心作用:激光雷达SLAM(如LOAM)、三维重建、自动驾驶感知。
  • 四大基础任务
    • 最近邻搜索(数据关联、特征匹配)。
    • 几何拟合(平面/直线拟合、曲面重建)。
    • 滤波与降采样(去噪、简化数据)。
    • 配准与特征提取(ICP、FPFH)。
  • 工具链:PCL、Open3D、Eigen。

2. 点云基础理论

2.1 数据结构与数学表示

  • 点云定义:三维点的集合,每个点包含坐标(x, y, z),可能附带RGB、强度信息。
    // PCL中的点云结构
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    
  • 刚体变换
    p ′ = R ⋅ p + t , R ∈ S O ( 3 ) , t ∈ R 3 p' = R \cdot p + t, \quad R \in SO(3), t \in \mathbb{R}^3 p=Rp+t,RSO(3),tR3
    // Eigen实现旋转平移
    Eigen::Matrix3f R = Eigen::AngleAxisf(angle, axis).toRotationMatrix();
    Eigen::Vector3f t(1.0, 0.0, 0.0);
    p_transformed = R * p + t;
    

2.2 最近邻搜索(Nearest Neighbor Search)

  • 问题定义:快速查找点云中距离查询点最近的点(K近邻或半径近邻)。

  • 算法对比

    方法 复杂度 适用场景
    暴力搜索 O(N) 小规模点云
    KD-Tree O(log N) 低维数据(PCL默认)
    Octree O(log N) 非均匀分布点云
  • PCL实现

    pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
    kdtree.setInputCloud(cloud);
    std::vector<int> point_indices;
    std::vector<float> distances;
    kdtree.nearestKSearch(query_point, k, point_indices, distances); // K近邻
    kdtree.radiusSearch(query_point, radius, point_indices, distances); // 半径搜索
    

2.3 几何拟合(Plane/Line Fitting)

最小二乘法拟合平面

  • 目标:最小化点到平面的距离平方和。

  • 求解步骤

    1. 计算点云中心 μ = 1 N ∑ p i \mu = \frac{1}{N}\sum p_i μ=N1pi
    2. 构建协方差矩阵 C = ∑ ( p i − μ ) ( p i − μ ) T C = \sum (p_i - \mu)(p_i - \mu)^T C=(piμ)(piμ)T
    3. C C C 做PCA,最小特征值对应特征向量即为法向量。
  • PCL实现(RANSAC鲁棒拟合)

    pcl::SACSegmentation<pcl::PointXYZ> seg;
    seg.setModelType(pcl::SACMODEL_PLANE);
    seg.setMethodType(pcl::SAC_RANSAC);
    seg.setDistanceThreshold(0.01);
    seg.segment(*inliers, *coefficients); // 输出内点和平面方程系数
    

直线拟合

  • 模型:方向向量 d {d} d 和点 p 0 {p}_0 p0
  • 求解:类似平面拟合,PCA最大特征值对应方向向量。

3. 点云处理核心算法

3.1 滤波(Denoising & Downsampling)

体素网格滤波(Voxel Grid)

pcl::VoxelGrid<pcl::PointXYZ> voxel;
voxel.setLeafSize(0.1f, 0.1f, 0.1f); // 体素大小
voxel.filter(*cloud_downsampled);

统计离群点去除(Statistical Outlier Removal)

pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setMeanK(50);      // 邻域点数
sor.setStddevMulThresh(1.0); // 阈值倍数
sor.filter(*cloud_clean);

3.2 点云配准(ICP算法)

  • ICP原理:迭代优化变换矩阵,最小化点对距离。
    min ⁡ R , t ∑ ∥ ( R ⋅ p i + t ) − q i ∥ 2 \min_{R, t} \sum \|(R \cdot p_i + t) - q_i\|^2 minR,t(Rpi+t)qi2
  • PCL实现
    pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
    icp.setMaxCorrespondenceDistance(0.05);
    icp.align(*aligned_cloud);
    std::cout << "变换矩阵:\n" << icp.getFinalTransformation() << std::endl;
    

3.3 特征提取(法向量与FPFH)

法向量估计

pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setKSearch(20); // 邻域点数
ne.compute(*normals);

FPFH特征描述子

pcl::FPFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh;
fpfh.setRadiusSearch(0.05); // 搜索半径
fpfh.compute(*fpfh_features);

4. 实战案例:从点云中提取桌面物体

步骤流程

  1. 读取点云pcl::io::loadPCDFile("scene.pcd", *cloud);
  2. 滤波:体素网格 + 统计离群点去除。
  3. 平面分割:RANSAC拟合桌面平面。
  4. 物体提取:提取非平面点。
  5. 可视化:PCL或Open3D展示结果。

代码片段

// 平面分割
pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.segment(*inliers, *coefficients);

// 提取物体
pcl::ExtractIndices<pcl::PointXYZ> extract;
extract.setNegative(true);
extract.filter(*object_cloud);

5. 总结

  • 关键点
    • 最近邻和几何拟合是基础,滤波和配准是核心,特征是高级应用的桥梁。
    • RANSAC和KD-Tree是提升鲁棒性和效率的关键工具。
  • 学习资源
    • 书籍:高翔《自动驾驶与机器人中的SLAM技术:从理论到实践》第5、6、7章。
    • 代码库:PCL官方教程、Open3D示例。

你可能感兴趣的:(SLAM算法,自动驾驶,自主导航,算法,自动驾驶,ubuntu,c++,笔记)