五大模块,看完等于点云入门~(dog)
输入激光雷达获得的点云
pcl::PointCloud
header:
seq:序列长度
stamp:获取点云的时刻
frame_id:坐标系名称
points:保存点云的容器,类型为std::vector
width:类型为uint32_t,表示点云的宽度,即一行点云的数量
height:类型为uint32_t,若为1,代表无序点云,则width代表点云数量;大于1,则有序
is_dense:bool类型,若点云中数据是有限的,则为true;否则为false
剔除距离激光雷达过近的点云(水平方向,XY平面,360度剔除)
对点云进行体素下采样操作
点云体素下采样代码
pcl::VoxelGrid sor; //设置下采样滤波器
sor.setInputCloud(in_cloud_ptr); //输入点云数据(指针)
sor.LeafSize((float) in_leaf_size, (float) in_leaf_size, (float) in_leaf_size); //设置体素栅格大小(长、宽、高)
sor.filter(*out_cloud_ptr); //设置输出点云容器(非指针)
操作步骤
剔除距离激光雷达过低或过高的点云(垂直方向,Z轴,360度剔除)
剔除距离激光雷达左右两侧过远的点云
通过点云的Y坐标是否满足限制条件判断,通常取 -1.5 <= Y <= 1.5
应用函数pcl::PointIndices::Ptr
、pcl::ExtractIndices
pcl:PointIndices::Ptr
保存点云索引,用于后续点云查找、提取、删除等操作
普通数组也可以保存点云索引,但是无法应用pcl库中其他函数直接对索引进行处理,所以还是需要利用pcl::PointIndices::Ptr
记录点云索引
该函数最主要的部分indices
成员变量,类型为std::vector
pcl::PointIndices::Ptr far_indices(new pcl::PointIndices);
far_indices->indices.push_back((unsigned int)index);
pcl::ExtractIndices
根据作者的设置,输出是否位于索引内的点云 ——>滤波器
pcl::ExtractIndices extract;
extract.setInputCloud(in_cloud_ptr); //输入点云指针
extract.setIndices(far_indices); //输入待处理的点云索引
//设置为true,则去掉索引值代表的点云再进行输出;设置为false,则直接输出索引值代表的点云
extract.setNegative(true);
extract.filter(*out_cloud_ptr); //输出点云(非指针)
进行地面点云滤波(去除地面点云)
总体流程
重要部分:地面点云分割——>几何分割方法(RANSAC:随机采样一致算法),
算法步骤原理如下:
代码设置函数 为何设置迭代次数为100,链接:
pcl::SACSegmentation seg; //pcl库中自带的采样一致分割算法,即几何分割算法
pcl::PointIndices::Ptr inliers(new Pcl::PointIndices); //创建点云索引指针
pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients); //创建模型参数
seg.setOptimizaCoefficients(true); //进行参数优化
//============对要分割的平面模型进行设置============
seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE); //设置分割模型,此时设置的分割模型为垂直于轴线的平面模型
seg.setAxis(Eigen::Vector3f(0 ,0, 1)); //设置分割模型垂直的轴线。因此要对地面进行滤波,所以此处平面模型设置为垂直于Z轴
seg.setEpsAngle(0.1); //允许平面模型法线与Z轴的最大偏差为0.1弧度,也就是说大于0.1弧度的平面模型不考虑
//==============================================
seg.setMethodType(pcl::SAC_RANSAC); //设置分割方法为随机采样一致算法
seg.setMaxIteration(100); //设置最大迭代次数为100
seg.setDistanceThreshold(0.2); //设置点云到平面的最大距离阈值。若小于该阈值,则认定为内点
seg.setOptimizeCoefficients(true); //再次进行参数优化
seg.setInputCloud(in_cloud_ptr); //输入待处理点云集合
seg.segment(*inliers, *coefficients); //向分割算法中输入模型参数,输出分割模型的点云索引对象
滤除特征不明显的点云
判定特征是否明显的原理:点云曲率越大,对应弧的弯曲程度越大,采样点越多,特征越明显;反之,特征越不明显
核心操作:去除曲率小于0.5的点云数据
操作流程:
核心函数:
建立KD搜索树对象:pcl::search::KdTree
建立KD搜索树:tree->setInputCloud(int_cloud_ptr)
进行法向量粗估计(OMP代表多线程估计,更快):pcl::NormalEstimationOMP
normal_estimation.setSearchMethod(tree)
normmal_estimation.setViewPoint(0, 0, 0)
normal_estimation.setRadiusSearch(3)
normal_estimation.compute(*normal_point_ptr)
进行法向量精估计:基于法向量微分的点云分割(对点云的大小半径做差)–>pcl::DiffenceOfNormalEstimation
diffnormals_estimator
中的PointXYZ
、PointNormal
、PointNormal
分别代表待分割的点云数据、大半径点云法向量、小半径点云法向量diffnormals_estimator.setInputCloud(in_cloud_ptr)
diffnormals_estimator.setNormalScaleLarge(normal_large_ptr)
diffnormals_estimator.setNormalScaleSmall(normal_small_ptr)
diffnormals_estimator.initcompute()
diffnormals_estimator.computeFeature(*out_cloud_ptr)
out_cloud_ptr
中的法线和曲率,其数据类型为:PointNormal
构建点云条件滤波对象:pcl::ConditionalRemoval
构建滤波条件对象:pcl::ConditionOr
构建条件滤波(点云曲率大于0.5保留):range_cond->addComparison(pcl::FieldComparison
点云数据复制
pcl::PointCloud::ptr diffnormals_cloud(new pcl::PointCloud)
pcl::copyPointCloud(*in_cloud_ptr, *diffnormals_cloud) //将in_cloud_ptr中的点云XYZ数据复制到diffnormals_cloud中点云pcl::PointNormal的XYZ上
点云欧式聚类,获得聚类信息
简述欧式聚类原理:
实际流程:
获取点云的二维信息,即x,y坐标,进行二维空间的点云欧式聚类
利用二维空间的点云聚类结果找到对应的三维点云,即不考虑点云z坐标,只要点云(x,y)坐标属于一个聚类物体,那个该三维点云就属于同一个聚类物体。
根据聚类物体的三维点云坐标获取聚类物体的信息
聚类物体的长宽高
聚类物体的顶点
注:聚类结果显示为矩形。上下顶点只有Z坐标不同(只需求出上顶点或下顶点即可)
上下顶点的计算使用凸包算法获得
利用凸包算法获得聚类物体在二维平面中的凸点,如下所示,即将最外层点连接
根据上述凸包,计算凸包的最小内接矩形,得到如下图所示,则紫色矩形的4个顶点,即为聚类物体的顶点,加上高度信息,即得到了聚类物体的8个顶点。
聚类物体的姿态
利用凸包算法计算得到内接矩阵的angle,如下图所示,angle应该为 θ 1 \theta_1 θ1.angle选择两个角度中绝对值较小的角度
创造聚类物体的四元数tf::createQuaternionFromRPY(0.0, 0.0, θ1)
,完成聚类物体的姿态获取
将过近的聚类物体合并成一个聚类物体
输出聚类结果,完成点云3D聚类