点云平面检测

本文介绍了如何使用PCL库中的SAC_RANSAC算法在给定的点云数据中检测地面并分离出地面点和外点,同时使用最小二乘法拟合平面方程。代码展示了如何处理点云数据,去除离群点,以及对地面和平面外点的处理过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于PCL-SAC_RANSAC检测: my_SAC_RANSAC
基于最小二乘法检测: my_plane

#include <iostream>
#include <string>
#include <cmath>
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>    // 去除离散点
#include <pcl/visualization/pcl_visualizer.h>
#include <ctime>
#include <pcl/common/transforms.h>
#include <pcl/segmentation/sac_segmentation.h>//基于采样一致性分割的类的头文件
#include <pcl/filters/extract_indices.h>//索引提取

#include <Eigen/Core>
#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void my_view(pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud_in, int time)
{
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewers(new pcl::visualization::PCLVisualizer("3D Viewer"));
    pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointXYZ> fildColor(cloud_in, "z"); // 按照z字段进行渲染
    viewers->addPointCloud<pcl::PointXYZ>(cloud_in, fildColor, "sample cloud");
    viewers->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud"); // 设置点云大小

    while (!viewers->wasStopped())
    {
        viewers->spinOnce(time);
    }
}

/**
 * @brief my_SAC_RANSAC     提取点云地面及外点
 * @param cloud_src         输入原始点云
 * @param dis               输入地面高度
 * @param cloud_plane       输出地面点云
 * @param cloud_out         输出外点点云
 * @return
 */
bool my_SAC_RANSAC(pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud_src, float dis, pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud_plane, pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud_out)
{
    // 创建平面模型的分割对象,并设置所有参数
    pcl::SACSegmentation<pcl::PointXYZ> seg;
    pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
    pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
    seg.setOptimizeCoefficients (true);
    seg.setModelType (pcl::SACMODEL_PLANE);
    seg.setMethodType (pcl::SAC_RANSAC);
    seg.setMaxIterations (500);            // 迭代次数
    seg.setDistanceThreshold (dis);
    seg.setInputCloud (cloud_src);
    seg.segment (*inliers, *coefficients);

    // 从输入云中提取平面模型内点
    pcl::ExtractIndices<pcl::PointXYZ> extract;
    extract.setInputCloud (cloud_src);
    extract.setIndices (inliers);
    extract.setNegative (false);
    // 得到与平面相关的点
    extract.filter (*cloud_plane);

    extract.setNegative (true);
    // 得到与平面外相关的点
    extract.filter (*cloud_out);

    return 0;
}



bool my_plane(pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud_src, float& A, float& B, float& C, float& D)
{
    pcl::PointXYZ center(0.0, 0.0, 0.0);

    for(int i=0; i<cloud_src->size(); i++)
    {
        center.x += cloud_src->at(i).x;
        center.y += cloud_src->at(i).y;
        center.z += cloud_src->at(i).z;
    }

    center.x /= cloud_src->size(); center.y /= cloud_src->size(); center.z /= cloud_src->size();
    Eigen::MatrixXf points_center(cloud_src->size(), 3);

    for(int i=0; i<cloud_src->size(); i++)
    {
        points_center(i, 0) = cloud_src->at(i).x - center.x;
        points_center(i, 1) = cloud_src->at(i).y - center.y;
        points_center(i, 2) = cloud_src->at(i).z - center.z;
    }

    Eigen::JacobiSVD<Eigen::MatrixXf> svd(points_center, Eigen::ComputeThinV);
    A = svd.matrixV()(0, 2);
    B = svd.matrixV()(1, 2);
    C = svd.matrixV()(2, 2);
    D = -(A * center.x + B * center.y + C * center.z);

    return true;
}

int main()
{
    // FOV视场角范围
    float M_pi = 3.1415926535;
    // 角度限制
    float angle_x = tan(80.0/360.0*M_pi*0.9);        
    float angle_y = tan(50.0/360.0*M_pi*0.9);         
    cout << angle_x << "," << angle_y << endl;

    float plan = 0;

    string src_path = "./data/";
    vector<cv::String> file_vec;
    cv::glob(src_path + "*.pcd", file_vec, false);
    for (std::string file_name : file_vec)
    {
       std::cout << "文件路径:" << file_name << std::endl;
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ori(new pcl::PointCloud<pcl::PointXYZ>());
//        if (pcl::io::loadPLYFile(file_name, *cloud_ori) < 0)
        if (pcl::io::loadPCDFile(file_name, *cloud_ori) < 0)
        {
            std::cout << "文件打开失败。错误编码:001" << std::endl;
        }

        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
        copyPointCloud(*cloud_ori, *cloud);

        cloud_ori->clear();
        vector<cv::Point3f> points;
        for(int i=0; i<cloud->size(); i++)
        {
            pcl::PointXYZ point;
            point.x = cloud->at(i).x * 1000;
            point.y = cloud->at(i).y * 1000;
            point.z = cloud->at(i).z * 1000;

            if(abs(point.x/point.z) < angle_x && abs(point.y/point.z) < angle_y)
            {
                cloud_ori->push_back(point);
            }
        }

        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZ>());
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out(new pcl::PointCloud<pcl::PointXYZ>());
        my_SAC_RANSAC(cloud_ori, 10.0, cloud_plane, cloud_out);

        // 利用最小二乘法拟合平面方程
        float A, B, C, D;
        my_plane(cloud_plane, A, B, C, D);
//        cout << A << "," << B << "," << C << "," << D << endl;
//        cout << atan(A/C)/M_pi*180.0 << "," << atan(B/C)/M_pi*180.0 << endl;

        plan += (float)cloud_plane->size()/(float)cloud_ori->size();
//        cout << "点云占比:" << (float)cloud_plane->size()/(float)cloud_ori->size() << endl;

        float z_min = 90000;
        float z_max = -100;
        for(int i=0; i<cloud_plane->size(); i++)
        {
            if(cloud_plane->at(i).z < z_min)
            {
                z_min = cloud_plane->at(i).z;
            }

            if(cloud_plane->at(i).z > z_max)
            {
                z_max = cloud_plane->at(i).z;
            }
        }

        cout << cloud_plane->size() << "," << D << "," << z_min << "," << z_max << "," << (float)cloud_plane->size()/(float)cloud_ori->size()
             << "," << atan(A/C)/M_pi*180.0 << "," << atan(B/C)/M_pi*180.0 << endl;
//        my_view(cloud_ori, 10);

    }
    plan /= (float)file_vec.size();
    cout << plan << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值