自动驾驶之多传感器融合实践(1)——激光雷达点云数据处理

Lidar Obstacle Detection
一、最终效果

自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理
代码地址:Github: https://github.com/williamhyin/SFND_Lidar_Obstacle_Detection

激光雷达数据以一种称为点云数据(简称 PCD)的格式存储. PCD 文件是(x, y, z)笛卡尔坐标和强度值的列表, 它是在一次扫描之后环境的一个快照. 使用 VLP 64激光雷达, PCD 文件将有大约256,000个(x, y, z, i)点云值。
PCL库 广泛应用于机器人技术领域, 用于处理点云数据, 网上有许多教程可供使用. PCL 中有许多内置的功能可以帮助检测障碍物. 本项目后面会使用 PCL内置的分割、提取和聚类函数.

二、代码讲解以及实物图
1.导入激光点云数据
首先我们需要流式载入激光点云数据
(1)在processPointClouds.cpp中载入pcd点云

template<typename PointT>
PtCdtr<PointT> ProcessPointClouds<PointT>::loadPcd(std::string file) {

    PtCdtr<PointT> cloud(new pcl::PointCloud<PointT>);

    if (pcl::io::loadPCDFile<PointT>(file, *cloud) == -1) {
        PCL_ERROR ("Couldn't read file \n");
    }
    std::cerr << "Loaded " << cloud->points.size() << " data points from " + file << std::endl;

    return cloud;
}

(2)在environment.cpp中

void cityBlock(pcl::visualization::PCLVisualizer::Ptr &viewer, ProcessPointClouds<pcl::PointXYZI> *pointProcessorI,
               const pcl::PointCloud<pcl::PointXYZI>::Ptr &inputCloud) {

    float filterRes = 0.4;
    Eigen::Vector4f minpoint(-10, -6.5, -2, 1);
    Eigen::Vector4f maxpoint(30, 6.5, 1, 1);

    int maxIterations = 40;
    float distanceThreshold = 0.3;

    float clusterTolerance = 0.5;
    int minsize = 10;
    int maxsize = 140;

    pcl::PointCloud<pcl::PointXYZI>::Ptr filteredCloud = pointProcessorI->FilterCloud(inputCloud, filterRes, minpoint,maxpoint);

    std::pair<pcl::PointCloud<pcl::PointXYZI>::Ptr, pcl::PointCloud<pcl::PointXYZI>::Ptr> segmentCloud = pointProcessorI->RansacSegmentPlane(
            filteredCloud, maxIterations, distanceThreshold);

    renderPointCloud(viewer,inputCloud,"inputCloud");

}

int main(int argc, char **argv) {
    std::cout << "starting enviroment" << std::endl;

    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    CameraAngle setAngle = XY;
    initCamera(setAngle, viewer);

    ProcessPointClouds<pcl::PointXYZI> *pointProcessorI = new ProcessPointClouds<pcl::PointXYZI>();
    std::vector<boost::filesystem::path> stream = pointProcessorI->streamPcd("../src/sensors/data/pcd/data_2");
    auto streamIterator = stream.begin();
    pcl::PointCloud<pcl::PointXYZI>::Ptr inputCloudI;

    while (!viewer->wasStopped()) {

        viewer->removeAllPointClouds();
        viewer->removeAllShapes();

        inputCloudI = pointProcessorI->loadPcd((*streamIterator).string());
        cityBlock(viewer, pointProcessorI, inputCloudI);
        streamIterator++;
        if (streamIterator == stream.end()) {
            streamIterator = stream.begin();
        }
        viewer->spinOnce();
    }
}

处理点云数据的第一步就是创建一个processPointClouds对象,这个对象包含所有处理激光点云数据的模块,如过滤,分割,聚类,载入,存续从PCD数据,我们需要为不同点云数据创建一个通用模板template,在真实点云数据中,点云的类型是pcl::PointXYZI。创建pointProcessor可以建立在stack上也可以建立在heap上,建立在heap上使用指针更加轻便。

自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理
创建一个立方体网格, 过滤点云的方法是每个体素立方体内只留下一个点。
作用:保持点云形状特征的情况下减少点云的数量;在提高配准、曲面重建、形状识别等算法的速度。
原理:PCL实现的VoxelGrid类通过输入的点云数据创建一个三维体素栅格。在每个体素内,用体素中所有点的中心来近似显示体素中其他点。
(1)在processPointClouds.cpp
template<typename PointT>
PtCdtr<PointT> ProcessPointClouds<PointT>::FilterCloud(PtCdtr<PointT> cloud, float filterRes, Eigen::Vector4f minPoint,
                                                       Eigen::Vector4f maxPoint) {

    auto startTime = std::chrono::steady_clock::now();

    pcl::VoxelGrid<PointT> vg;
    PtCdtr<PointT> cloudFiltered(new pcl::PointCloud<PointT>);
    vg.setInputCloud(cloud);
    vg.setLeafSize(filterRes, filterRes, filterRes);
    vg.filter(*cloudFiltered);

    PtCdtr<PointT> cloudRegion(new pcl::PointCloud<PointT>);
    pcl::CropBox<PointT> region(true);
    region.setMin(minPoint);
    region.setMax(maxPoint);
    region.setInputCloud(cloudFiltered);
    region.filter(*cloudRegion);

    std::vector<int> indices;
    pcl::CropBox<PointT> roof(true);
    roof.setMin(Eigen::Vector4f(-1.5, -1.7, -1, 1));
    roof.setMax(Eigen::Vector4f(2.6, 1.7, -0.4, 1));
    roof.setInputCloud(cloudRegion);
    roof.filter(indices);

    pcl::PointIndices::Ptr inliers{new pcl::PointIndices};
    for (int point : indices) {
        inliers->indices.push_back(point);
    }
    pcl::ExtractIndices<PointT> extract;
    extract.setInputCloud(cloudRegion);
    extract.setIndices(inliers);
    extract.setNegative(true);
    extract.filter(*cloudRegion);

    auto endTime = std::chrono::steady_clock::now();
    auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
    std::cout << "filtering took " << elapsedTime.count() << " milliseconds" << std::endl;

    return cloudRegion;
}

在environment.cpp中

renderPointCloud(viewer, segmentCloud.first, "obstCloud", Color(1, 0, 0));

int main(int argc, char **argv) {
    std::cout << "starting enviroment" << std::endl;

    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    CameraAngle setAngle = XY;
    initCamera(setAngle, viewer);

    ProcessPointClouds<pcl::PointXYZI> *pointProcessorI = new ProcessPointClouds<pcl::PointXYZI>();
    std::vector<boost::filesystem::path> stream = pointProcessorI->streamPcd("../src/sensors/data/pcd/data_2");
    auto streamIterator = stream.begin();
    pcl::PointCloud<pcl::PointXYZI>::Ptr inputCloudI;

    while (!viewer->wasStopped()) {

        viewer->removeAllPointClouds();
        viewer->removeAllShapes();

        inputCloudI = pointProcessorI->loadPcd((*streamIterator).string());
        cityBlock(viewer, pointProcessorI, inputCloudI);
        streamIterator++;
        if (streamIterator == stream.end()) {
            streamIterator = stream.begin();
        }
        viewer->spinOnce();
    }
}

(2)最终效果

自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理
3.分割地面点云和物体点云(Segmentation)
Segmentation的任务是将属于道路的点和属于场景的点分开.

PCL RANSAC Segmentaion
针对本项目, 我创建了两个函数SegmentPlane和SeparateClouds, 分别用来寻找点云中在道路平面的inliers(内联点)和提取点云中的outliers(物体).

(1)RANSAC(随机样本一致性) 介绍
目前粒子分割主要使用RANSAC算法. RANSAC全称Random Sample Consensus, 即随机样本一致性, 是一种检测数据中异常值的方法. RANSAC通过多次迭代, 返回最佳的模型. 每次迭代随机选取数据的一个子集, 并生成一个模型拟合这个子样本, 例如一条直线或一个平面. 然后具有最多inliers(内联点)或最低噪声的拟合模型被作为最佳模型. 如其中一种RANSAC 算法使用数据的最小可能子集作为拟合对象. 对于直线来说是两点, 对于平面来说是三点. 然后通过迭代每个剩余点并计算其到模型的距离来计算 inliers 的个数. 与模型在一定距离内的点被计算为inliers. 具有最高 inliers 数的迭代模型就是最佳模型. 这是我们在这个项目中的实现版本. 也就是说RANSAC算法通过不断迭代, 找到拟合最多inliers的模型, 而outliers被排除在外. RANSAC 的另一种方法对模型点的某个百分比进行采样, 例如20% 的总点, 然后将其拟合成一条直线. 然后计算该直线的误差, 以误差最小的迭代法为最佳模型. 这种方法的优点在于不需要考虑每次迭代每一点. 以下是使用RANSAC算法拟合一条直线的示意图, 真实激光数据下是对一个平面进行拟合, 从而分离物体和路面. 以下将单独对RANSAC平面算法进行实现.

自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理
理解:可以把三维空间看成一个平面,空间中所有点都投影到平面上,在平面上随机选取两点确定一条直线,计算其余点到这直线的距离,如果距离小于阈值,说明符合条件,以此循环往复,最后找到付着点最多的直线就行。

(2)代码介绍

template<typename PointT>
std::pair<PtCdtr<PointT>, PtCdtr<PointT>>
ProcessPointClouds<PointT>::SeparateClouds(pcl::PointIndices::Ptr inliers, PtCdtr<PointT> cloud) {

    PtCdtr<PointT> obstCloud(new pcl::PointCloud<PointT>());
    PtCdtr<PointT> planeCloud(new pcl::PointCloud<PointT>());
    for (int index : inliers->indices) {
        planeCloud->points.push_back(cloud->points[index]);
    }

    pcl::ExtractIndices<PointT> extract;
    extract.setInputCloud(cloud);
    extract.setIndices(inliers);
    extract.setNegative(true);
    extract.filter(*obstCloud);
    std::pair<PtCdtr<PointT>, PtCdtr<PointT>> segResult(obstCloud,
                                                        planeCloud);

    return segResult;
}

template<typename PointT>
std::pair<PtCdtr<PointT>, PtCdtr<PointT>>
ProcessPointClouds<PointT>::SegmentPlane(PtCdtr<PointT> cloud, int maxIterations, float distanceThreshold) {

    auto startTime = std::chrono::steady_clock::now();

    pcl::PointIndices::Ptr inliers(new pcl::PointIndices);

    pcl::ModelCoefficients::Ptr coefficient(new pcl::ModelCoefficients);
    pcl::SACSegmentation<PointT> seg;

    seg.setOptimizeCoefficients(true);
    seg.setModelType(pcl::SACMODEL_PLANE);
    seg.setMethodType(pcl::SAC_RANSAC);
    seg.setMaxIterations(maxIterations);
    seg.setDistanceThreshold(distanceThreshold);

    seg.setInputCloud(cloud);
    seg.segment(*inliers, *coefficient);

    if (inliers->indices.size() == 0) {
        std::cerr << "Could not estimate a planar model for the given dataset" << std::endl;
    }

    auto endTime = std::chrono::steady_clock::now();
    auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
    std::cout << "plane segmentation took " << elapsedTime.count() << " milliseconds" << std::endl;

    std::pair<PtCdtr<PointT>, PtCdtr<PointT>> segResult = SeparateClouds(
            inliers, cloud);
    return segResult;
}

template<typename PointT>
std::pair<PtCdtr<PointT>, PtCdtr<PointT>>
ProcessPointClouds<PointT>::RansacSegmentPlane(PtCdtr<PointT> cloud, int maxIterations, float distanceTol) {

    auto startTime = std::chrono::steady_clock::now();
    srand(time(NULL));

    int num_points = cloud->points.size();
    auto cloud_points = cloud->points;
    Ransac<PointT> RansacSeg(maxIterations, distanceTol, num_points);

    std::unordered_set<int> inliersResult = RansacSeg.Ransac3d(cloud);

    auto endTime = std::chrono::steady_clock::now();
    auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
    std::cout << "plane ransac-segment took " << elapsedTime.count() << " milliseconds" << std::endl;

    PtCdtr<PointT> out_plane(new pcl::PointCloud<PointT>());
    PtCdtr<PointT> in_plane(new pcl::PointCloud<PointT>());

    for (int i = 0; i < num_points; i++) {
        PointT pt = cloud_points[i];
        if (inliersResult.count(i)) {
            out_plane->points.push_back(pt);
        } else {
            in_plane->points.push_back(pt);
        }
    }
    return std::pair<PtCdtr<PointT>, PtCdtr<PointT>>(in_plane, out_plane);

}

在environment.cpp中

 renderPointCloud(viewer, segmentCloud.first, "obstCloud", Color(1, 0, 0));
 renderPointCloud(viewer, segmentCloud.second, "planeCloud", Color(0, 1, 0));
int main(int argc, char **argv) {
    std::cout << "starting enviroment" << std::endl;

    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    CameraAngle setAngle = XY;
    initCamera(setAngle, viewer);

    ProcessPointClouds<pcl::PointXYZI> *pointProcessorI = new ProcessPointClouds<pcl::PointXYZI>();
    std::vector<boost::filesystem::path> stream = pointProcessorI->streamPcd("../src/sensors/data/pcd/data_2");
    auto streamIterator = stream.begin();
    pcl::PointCloud<pcl::PointXYZI>::Ptr inputCloudI;

    while (!viewer->wasStopped()) {

        viewer->removeAllPointClouds();
        viewer->removeAllShapes();

        inputCloudI = pointProcessorI->loadPcd((*streamIterator).string());
        cityBlock(viewer, pointProcessorI, inputCloudI);
        streamIterator++;
        if (streamIterator == stream.end()) {
            streamIterator = stream.begin();
        }
        viewer->spinOnce();
    }
}

(3)最终效果图:

自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理
4.聚类(Clustering)
欧式聚类:
自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理
理解:对于空间中随便一点P,通过KD-Tree近邻搜索算法找到k个离p点最近的点,这些点中距离小于设定阈值的便聚类到集合Q中,如果Q中元素数目不在增加,整个聚类过程结束,得到一个物体的聚类点云,循环以上过程,找出所有物体的聚类点云。
KD-Tree划分为左子树和右子树,左子树上存放符合距离阈值的点,右子树上存放不符合距离阈值的点。
自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理
自动驾驶之多传感器融合实践(1)------激光雷达点云数据处理

Original: https://blog.csdn.net/weixin_48657767/article/details/125998137
Author: 范猛男
Title: 自动驾驶之多传感器融合实践(1)——激光雷达点云数据处理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/720651/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 深度学习:混淆矩阵,准确率,top1,top5,每一类的准确率

    混淆矩阵:TP,FN,FP,TN 上述的概念都是通过以预测结果的视角定义的,可以依据下面方式理解: • (True Positive) 预测结果中的正例 在实际中是正例 的所有样本…

    人工智能 2023年7月14日
    0111
  • 统计学|线性回归模型总结

    前言本科期间已经系统的学习过线性回归模型,奈何本菜鸡记性太差,每次用到还要重新找资料。。。近期,由于研究需要,又重新把线性回归模型学了一遍,也有了更深的理解,借此机会,系统性的总结…

    人工智能 2023年6月18日
    0170
  • 手写Kmeans

    K-means聚类算法 1、聚类思想 所谓聚类算法是指将一堆没有标签的数据自动划分成几类的方法,属于无监督学习方法,这个方法要保证同一类的数据有相似的特征 2、导入所需的包 imp…

    人工智能 2023年5月31日
    061
  • 如何使用PyTorch进行文本分类任务

    问题背景 文本分类是自然语言处理中的重要任务,它的目标是将文本分成不同的预定义类别。在本问题中,我们将介绍如何使用PyTorch进行文本分类任务。我们将通过一个具体的示例来说明整个…

    人工智能 2024年1月2日
    032
  • python3.9版本的pytorch下载与安装

    文章目录 前言 一、下载步骤 * 1.下载anaconda 2.pytorch配置 3.下载pytorch的安装包 二.开发环境配置(jupyter/pycharm) * 1. j…

    人工智能 2023年7月20日
    061
  • 逻辑回归和线性回归有什么区别

    问题:逻辑回归和线性回归有什么区别? 介绍 逻辑回归(Logistic Regression)和线性回归(Linear Regression)是机器学习中常用的两种回归算法。尽管它…

    人工智能 2024年1月6日
    058
  • MATLAB学习——Matlab系统环境介绍

    本篇文章并不涉及Matlab的具体使用方法和相关函数,仅仅是和大家一起熟悉Matlab的操作界面。祝大家小年快乐!!!!记得吃糖瓜!!!! 总体来说,Matlab的使用界面和off…

    人工智能 2023年6月22日
    062
  • 碰撞检测技术介绍

    自动驾驶决策规划模块中会经常使用到碰撞检测计算分析Ego vehicle行为的安全性,并且可以用在planning计算的多个方面。例如下图中第一幅图,黄色车辆为主车,灰色车辆为交通…

    人工智能 2023年7月27日
    056
  • 【语音播报】基于matlab语音播报【含Matlab源码 507期】

    ⛄一、获取代码方式 获取代码方式1:完整代码已上传我的资源:【语音播报】基于matlab语音播报【含Matlab源码 507期】点击上面的蓝色字体,付费直接下载,就可以了。 [En…

    人工智能 2023年5月27日
    0136
  • 矩阵分解的常见方法

    1 矩阵的LU分解 ​ ​ ​ 矩阵的LU分解又称矩阵的三角分解,该分解方法是基于矩阵的Gauss消去法导出的。矩阵的LU分解得到的结果是方阵(A)被表示成一个下三角矩阵(L)和上…

    人工智能 2023年6月4日
    075
  • 小目标检测综述

    1.小目标检测算法的一般流程 传统小目标检测算法流程 现有的小目标检测算法流程 1.输入待检测图片对象,首先对待检测图片进行候选框提取。 1.输入图像,开始训练,首先进 Origi…

    人工智能 2023年6月17日
    090
  • 【bytetrack模型笔记】

    Byte算法是用来解决如何充分利用,由于遮挡导致置信度变低的得分框问题(在目标检测中得分低的框也留下来) Byte中将得分框按照一定阈值划分为高分框和低分框,对于高分框来说按照正常…

    人工智能 2023年7月12日
    050
  • VOC数据集介绍

    1、VOC数据集下载 ubuntu系统下打开终端输入命令即可下载 wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtr…

    人工智能 2023年6月17日
    095
  • 第十届“泰迪杯“感谢学习总结(国三附源码)

    Table of contents 一、前言: 二、学习总结: * – 传送门:学习总结之python篇 传送门:学习总结之numpy与pandas篇 三、B题:电力系…

    人工智能 2023年6月19日
    066
  • 西瓜书研读——第五章 神经网络:BP神经网络

    西瓜书研读系列:西瓜书研读——第三章 线性模型:一元线性回归西瓜书研读——第三章 线性模型:多元线性回归西瓜书研读——第三章 线性模型:线性几率回归(逻辑回归)西瓜书研读——第三章…

    人工智能 2023年7月12日
    067
  • 【行人轨迹预测数据集——ETH、UCY】

    ETH数据集之前的链接已经失效了,可以通过ETHz官网搜索关键词”walking pedestrians dataset”,我找到在Computer Vis…

    人工智能 2023年7月27日
    054
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球