3. 基于欧式聚类的点云分割

任务目标

对去除地面后的LiDAR点云执行欧式聚类(Euclidean Cluster Extraction),将属于不同物体的点云分割为独立簇。输入为去地面后的点云,输出为每个物体对应的聚类索引及可视化结果。

环境依赖

需要 PCL 1.10+(点云库)与 CMake 3.10+。若已安装libpcl-dev,可直接编译运行;否则通过sudo apt install libpcl-dev安装。

核心实现

以下代码读取去地面后的PCD文件,使用pcl::EuclideanClusterExtraction进行聚类。关键参数:ClusterTolerance(邻域搜索半径)、MinClusterSize/MaxClusterSize(簇点数量范围)。聚类结果通过不同颜色渲染并保存为PCD文件,同时打印每个簇的点数与包围盒尺寸。

#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/common/common.h>
#include <iostream>
#include <vector>

int main(int argc, char** argv) {
    if (argc < 2) {
        std::cerr << "用法: " << argv[0] << " <去地面后的PCD文件>" << std::endl;
        return -1;
    }

    // 加载去地面后的点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) == -1) {
        std::cerr << "无法读取文件: " << argv[1] << std::endl;
        return -1;
    }
    std::cout << "输入点云点数: " << cloud->size() << std::endl;

    // 创建KdTree用于邻域搜索
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    tree->setInputCloud(cloud);

    // 配置欧式聚类参数
    pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
    ec.setClusterTolerance(0.3);        // 邻域搜索半径(米)
    ec.setMinClusterSize(50);           // 最小簇点数
    ec.setMaxClusterSize(25000);        // 最大簇点数
    ec.setSearchMethod(tree);
    ec.setInputCloud(cloud);

    // 执行聚类
    std::vector<pcl::PointIndices> cluster_indices;
    ec.extract(cluster_indices);
    std::cout << "检测到簇数量: " << cluster_indices.size() << std::endl;

    // 为每个簇生成随机颜色并保存可视化结果
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr colored_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
    colored_cloud->reserve(cloud->size());

    int cluster_id = 0;
    for (const auto& indices : cluster_indices) {
        // 计算当前簇的包围盒
        pcl::PointCloud<pcl::PointXYZ>::Ptr cluster_cloud(new pcl::PointCloud<pcl::PointXYZ>);
        for (int idx : indices.indices)
            cluster_cloud->push_back((*cloud)[idx]);

        pcl::PointXYZ min_pt, max_pt;
        pcl::getMinMax3D(*cluster_cloud, min_pt, max_pt);
        std::cout << "簇 " << cluster_id
                  << " | 点数: " << cluster_cloud->size()
                  << " | 尺寸(m): " << (max_pt.x - min_pt.x)
                  << " x " << (max_pt.y - min_pt.y)
                  << " x " << (max_pt.z - min_pt.z) << std::endl;

        // 生成随机颜色(RGB 0-255)
        uint8_t r = rand() % 256, g = rand() % 256, b = rand() % 256;
        for (int idx : indices.indices) {
            pcl::PointXYZRGB pt;
            pt.x = (*cloud)[idx].x;
            pt.y = (*cloud)[idx].y;
            pt.z = (*cloud)[idx].z;
            pt.r = r; pt.g = g; pt.b = b;
            colored_cloud->push_back(pt);
        }
        cluster_id++;
    }

    // 保存可视化结果
    pcl::io::savePCDFileBinary("clustered_result.pcd", *colored_cloud);
    std::cout << "结果已保存至 clustered_result.pcd" << std::endl;

    // 可视化
    pcl::visualization::PCLVisualizer viewer("欧式聚类结果");
    pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb_handler(colored_cloud);
    viewer.addPointCloud<pcl::PointXYZRGB>(colored_cloud, rgb_handler, "clusters");
    while (!viewer.wasStopped())
        viewer.spinOnce();

    return 0;
}

CMake配置

cmake_minimum_required(VERSION 3.10)
project(euclidean_clustering)
find_package(PCL 1.10 REQUIRED COMPONENTS common io segmentation search visualization)
add_executable(euclidean_clustering euclidean_clustering.cpp)
target_link_libraries(euclidean_clustering ${PCL_LIBRARIES})
add_definitions(${PCL_DEFINITIONS})

编译与运行

mkdir build && cd build
cmake ..
make -j4
./euclidean_clustering ../ground_removed.pcd

验证方法

运行程序后观察终端输出:

  1. 簇数量合理:对于典型城市场景(如KITTI去地面后),应输出5~20个簇,对应车辆、行人、树干等独立物体。
  2. 簇尺寸符合物体特征:车辆簇的包围盒约4m×2m×1.5m,行人簇约0.5m×0.5m×1.8m。若某簇尺寸异常(如超过10m),说明ClusterTolerance过大导致相邻物体被合并。
  3. 可视化检查:不同物体显示为不同颜色,同一物体内部颜色一致且无断裂。若单个物体被拆成多块,应增大ClusterTolerance或减小MinClusterSize;若不同物体粘连,应减小ClusterTolerance。

保存的clustered_result.pcd可直接用pcl_viewer打开复查:pcl_viewer clustered_result.pcd。

参数调优建议

  • ClusterTolerance:典型值0.2~0.5m。Velodyne VLP-16在10m处点间距约0.2m,取0.3m可兼顾近处与远处物体。
  • MinClusterSize:设为30~50可滤除少量离群噪声点。
  • MaxClusterSize:防止墙面等大型结构被识别为单簇,通常设为50000以内。