FilterCloud()所包括的功能:
1.首先使用体素滤波(相当于做稀释减少点的数量)(体素网格过滤将创建一个立方体网格, 过滤点云的方法是每个体素立方体内只留下一个点, 因此立方体每一边的长度越大, 点云的分辨率就越低. 但是如果体素网格太大, 就会损失掉物体原本的特征。)
2.过滤掉给定立方体之外的点云数据。(定义感兴趣区域, 并删除感兴趣区域外的任何点. 感兴趣区域的选择两侧需要尽量覆盖车道的宽度, 而前后的区域要保证你可以及时检测到前后车辆的移动。 在最终结果中, 我们使用pcl CropBox 查找自身车辆车顶的点云数据索引, 然后将这些索引提供给 pcl ExtractIndices 对象删除, 因为这些对于我们分析点云数据没有用处。)(相当于将自身车辆作为坐标轴的中心点(原点),然后以自身为中心 ,圈出一个立方体的范围(该立方体只需要知道对角线上的两个点AB坐标即可确定),成为每一次运动时候的感兴趣区域,也就是只关心区域内点的聚类等后续操作。如下图:立方体之外的就不要了。)
3.提取车身周围小范围内的所有的点,并将提取到的所有点保存在indices中。相当于在上图立方体内再扣掉一个立方体(扣掉的这个立方体即为车身范围内的即把车这部分没采取到的盲区给去掉。)如上图右图。(整个过程就是先踢掉左图中黑色点,剩下红色立方体。再从红色立方体中再扣点右图中铅笔画的那部分。)
下面会有以单个pcd为例进行的展示。
原始函数声明在processPointCloud.h
原始函数定义在processPointCloud.cpp
函数调用在environment.cpp中的cityBlock()函数中
本部分涉及到的知识:
1、Eigen是可以用来进行线性代数、矩阵、向量操作等运算的C++库,它里面包含了很多算法。
Eigen:矩阵(Matrix)类的介绍及使用(在Eigen中,所有矩阵和向量均为Matrix模板类的对象,向量是矩阵的行(或列)为1是的特殊情况。)
1.1、矩阵的三参数模板
Matrix类有六个模板参数,其中三个有默认值,因此只要学习三个参数就足够了。
/* 强制性的三参数模板的原型 (三个参数分别表示:标量的类型,编译时的行,编译时的列) */
Matrix
/* 用typedef定义了很多模板,例如:Matrix4f 表示 4×4 的floats 矩阵 */
typedef Matrix Matrix4f;
1.2、向量(Vectors)
向量是矩阵的特殊情况,也是用矩阵定义的。
typedef Matrix Vector3f; //"Vector3f"直接定义了一个维度为3的列向量,3行1列
typedef Matrix RowVector2i;//行向量1行2列
1.3、特殊动态值(special value Dynamic)
Eigen的矩阵不仅能够在编译是确定大小(fixed size),也可以在运行时确定大小,就是所说的动态矩阵(dynamic size)。
typedef Matrix MatrixXd; //行列都不固定
typedef Matrix VectorXi; //行不限制固定列
/* 也可使用‘行’固定‘列’动态的矩阵 */
Matrix
1.4、构造函数(Constructors)
可以使用默认的构造函数,不执行动态分配内存,也没有初始化矩阵参数:
Matrix3f a; // a是3-by-3矩阵,包含未初始化的 float[9] 数组
MatrixXf b; // b是动态矩阵,当前大小为 0-by-0, 没有为数组的系数分配内存
/* 矩阵的第一个参数表示“行”,数组只有一个参数。根据跟定的大小分配内存,但不初始化 */
MatrixXf a(10,15); // a 是10-by-15阵,分配了内存,没有初始化
VectorXf b(30); // b是动态矩阵,当前大小为 30, 分配了内存,没有初始化
/* 对于给定的矩阵,传递的参数无效 */
Matrix3f a(3,3);
/* 对于维数最大为4的向量,可以直接初始化 */
Vector2d a(5.0, 6.0);
Vector3d b(5.0, 6.0, 7.0);
Vector4d c(5.0, 6.0, 7.0, 8.0);
1.5、系数访问
系数都是从0开始,矩阵默认按列存储
#include
#include
using namespace std;
using namespace Eigen;
int main()
{
MatrixXd m(2, 2);
m(0, 0) = 3;
m(1, 0) = 2.5;
m(0, 1) = -1;
m(1, 1) = m(1, 0) + m(0, 1);
cout << "Here is the matrix m:" << endl;
cout << m << endl;
VectorXd v(2);
v(0) = 4;
v[1] = v[0] - 1; //operator[] 在 vectors 中重载,意义和()相同
cout << "Here is the vector v:" << endl;
cout << v << endl;
getchar();
getchar();
}
1.6、逗号分隔的初始化
Matrix3f m;
m << 1, 2, 3, 4, 5, 6, 7, 8, 9;
cout << m;
1.7、Resizing
可以用rows(), cols() and size() 改变现有矩阵的大小。这些类方法返回行、列、系数的数值。也可以用resize()来改变动态矩阵的大小。
矩阵与向量间的点成叉乘转置以及矩阵求逆等见https://zhuanlan.zhihu.com/p/36772345
2、CropBoxfilterr---------过滤指定立方体内的点
参照博客:https://blog.csdn.net/ethan_guo/article/details/80359313
#include //要包含的头文件。要包含哪些头文件,需要去查官方文档对该类的介绍。
pcl::PointCloud::Ptr body {new pcl::PointCloud};
pcl::PointCloud::Ptr filtered_body {new pcl::PointCloud};//指针还是对象,有时候只能指针,有时候都行。报错就换。
pcl::CropBox box_filter;//滤波器对象
box_filter.setMin(Eigen::Vector4f(x_min, y_min, z_min, 1.0));//Min和Max是指立方体的两个对角点。每个点由一个四维向量表示,通常最后一个是1.(不知道为什么要有四个,大神知道的给解答一下下)
box_filter.setMax(Eigen::Vector4f(x_max, y_max, z_max, 1.0));
clipper.setNegative(false);//是保留立方体内的点而去除其他点,还是反之。false是将盒子内的点去除,默认为false
box_filter.setInputCloud(body);//输入源
box_filter.filter(*filtered_body);//滤它!
以单个pcd文件进行滤波操作展示:
原始点云图片如下图:
滤波后点云图片如下图:
代码(只是针对于滤波代码)以及注释如下:
filterRes
是体素网格的大小, minPoint/maxPoint
为感兴趣区域的最近点和最远点.
我们首先执行VoxelGrid减少点云数量, 然后设置最近和最远点之间的感兴趣区域, 最后再从中删除车顶的点云.
头文件:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace pcl;
//1、点云滤波
pcl::PointCloud::Ptr FilterCloud(pcl::PointCloud::Ptr cloud, float filterRes, Eigen::Vector4f minPoint, Eigen::Vector4f maxPoint);//声明滤波点云函数,参数1原始点云cloud,参数2体素滤波中的叶子大小,参数3和4"Vector4f"直接定义了一个维度为4的列向量表示的是定义的立方体对角坐标大小
源文件:
#include "filter.h"
pcl::PointCloud::Ptr FilterCloud(pcl::PointCloud::Ptr cloud, float filterRes, Eigen::Vector4f minPoint, Eigen::Vector4f maxPoint)
{
//1.使用体素滤波下采样
pcl::VoxelGrid vg; //创建滤波对象
pcl::PointCloud::Ptr cloudFiltered(new pcl::PointCloud); //创建保存体素滤波后的点对象cloudFiltered
vg.setInputCloud(cloud);//输入cloud
vg.setLeafSize(filterRes, filterRes, filterRes);//设置叶子大小(这么大个叶子节点内只提取一个点)
vg.filter(*cloudFiltered);//滤波后的点云保存在cloudFiltered
//2.过滤掉在用户给定立方体内的点云数据
//理解:将自身车辆作为坐标轴的中心点,然后在身边自身为中心 ,圈出一个立方体的范围,成为每一次运动时候的感兴趣区域,也就是只关心区域内点的聚类等后续操作
pcl::PointCloud::Ptr cloudRegion(new pcl::PointCloud);
pcl::CropBox region(true);//滤波器对象
region.setMin(minPoint);
region.setMax(maxPoint);
region.setInputCloud(cloudFiltered);
region.filter(*cloudRegion);
//3.提取车身周围范围内的所有的点,并将提取到的所有点保存在indices中
std::vector indices;
pcl::CropBox 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 extract;
extract.setInputCloud(cloudRegion);
extract.setIndices(inliers);
extract.setNegative(true); //false 提取内点也就是提取车身周围的几个点, true提取出了车身周围的点
extract.filter(*cloudRegion);
pcl::PCDWriter writer;
writer.write("F:\\SFND_Lidar_Obstacle_Detection\\SFND_Lidar_Obstacle_Detection\\data\\pcd\\filter.pcd", *cloudRegion);
return cloudRegion;
}
主函数:
#include "filter.h"
int main(int argc, char **argv)
{
// 1、滤波 滤波后点云存入filteredCloud
pcl::PointCloud::Ptr inputCloud(new pcl::PointCloud);
pcl::PCDReader reader;
reader.read("F:\\SFND_Lidar_Obstacle_Detection\\SFND_Lidar_Obstacle_Detection\\data\\pcd\\data_1\\0000000006.pcd", *inputCloud);
float filterRes = 0.4;
Eigen::Vector4f minpoint(-10, -6.5, -2, 1);
Eigen::Vector4f maxpoint(30, 6.5, 1, 1);
pcl::PointCloud::Ptr cloudRegion(new pcl::PointCloud);
cloudRegion=FilterCloud(inputCloud, filterRes, minpoint, maxpoint);
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("显示窗口")); //窗口显示点云
viewer->addPointCloud(cloudRegion, "*cloud");
viewer->resetCamera();
system("pause");
return (0);
}