13.1概述
之前所有的讲的,包括前端,后端,位姿图估计,路标点的地图,回环检测,都是定位问题,而不是建图。都是定位了相机的位姿。而没有建图。
要建图,首先来看对建图的需求。slam是一种底层技术,要看上层应用需要slam做什么事情。如果上层是一个机器人,应用层的开发者希望用slam来做全局定位,并让机器人在地图中导航。如果上层是一个增强现实设备,那么开发者希望将虚拟物体叠加在现实物体之中,还可能需要处理虚拟物体和现实物体的遮挡关系。
应用层对定位的需求就是slam只要能够提供相机或者搭载相机的物体的位姿就可以了。原来以为建图是为了做定位,但明显对建图还有其他的需求。
例如
1.基本功能:定位,可以用局部地图中用路标来进行定位,也可以在回环检测中,用全局描述子来进行定位(看相似度),继而希望可以保存地图,下一次机器人一开机就可以定位。
2.导航:导航就是在地图中,在两个地图点间规划路径,然后控制自己到达目标点的过程。如果要实现导航,必须知道哪些点是可以通过的,哪些点是不可以通过的,稀疏特征点地图做不了,所以之前建的地图(用路标点做的)也做不了,至少是一个稠密的地图。
3.避障,更关注与局部的动态的障碍物的处理,同样,如果仅有特征点的话,不能判断这个特征点是否为障碍物。
4.重建,重建周围环境地图,展示给别人看,所以要舒服、美观。点云稠密地图不完全满足要求,因为没有带纹理的平面。也可以用于通信,比如三维视频通话,网上购物等。当然地图必须是稠密的。
5.交互,增强现实,放置虚拟物体在地图中,通过虚拟的网页浏览器观看视频,像虚拟的强投掷物体。机器人中,人与人,人与地图的交互。比如人对机器人说,去拿起桌子上的报纸。需要识别什么是桌子,什么是报纸,什么是之上,语义地图。
稀疏路标地图只能用于定位,而稠密地图可以用于导航、避障、重建,而语义地图可以用来做交互。而稠密地图是相对于稀疏地图来说的,稀疏地图只建模感兴趣的部分,而稠密地图则建模它所有看到的部分。所以入如果只看到桌子的四个角就可以用于定位,但只有四个角是做不到了导航,避障和重建的。
目前RGBD 还无法很好地用于室外和大场景,所以这时候用立体视觉也就是单目双目进行稠密重建是有意义的。立体视觉是依赖于纹理的,如果是纯色,就坑了。
13.5RGBD稠密建图
RGBD有很大的优势就是深度可以直接由传感器硬件测量得到,而且由于它的结构光或飞时原理,深度测量跟纹理无关,纯色物体也可以测深度。
点云地图就是根据估计的相机位姿,然后把相机上的所有点坐标转换成世界坐标,再加上b,g,r表示颜色,然后再拼凑在一起就是地图。当然也要做一些筛选,主要是对深度,深度为0的点或深度太大的点是不要的,深度为0表示没有测量到,而深度太大吧说明不稳定。这样就是一个简单的地图了,当然最好对这样的点云地图做一下滤波,这里用了统计滤波器和体素滤波器,统计滤波器去除的是孤立点,具体是统计每个点与它周围的N个点的距离分布,把距离均值过大的点给去除。感觉就是保存了粘在一起的点,去除了孤立的噪声点。
统计滤波器statistical_filter.还有体素滤波器voxel filter.体素滤波器是对点进行降采样,保证在某个一定大小的立方体(体素)内仅有一个点,是为了节省存储空间来着。
但是这样的点云地图是不能做什么的,如果像导航,可以通过体素来建立占据网格地图。如果要求外观,可以在点云地图的基础上,用三角网格(mesh)或面片(surfel)建图。还有GPU建图。
实践:点云部分
1.每个图像生产成点云
做点云用的基本上都是pcl
先定义类型pcl::PointXYZRGB为PointT,因为点云的每个点都有6个值,这6个值就是x,y,z,r,g,b,这里的PointT也是用来定义点云的每个点的类型的。
定义点云类型pcl::PointCloud<PointT>为PointCloud,这里点云的每个点类型为PointT.
对于每张图像,定义点云指针current.对于每个图像的每个像素点,先读取对应的深度d,
d=depth.ptr<unsigned short>(v)[u],v是列,u是行。
删除d=0和d>7000的点。对剩下来的点,计算x,y,z,再读取b,g,r.
p.b=color.data[v*color.step+u*color.channels()],g就是坐标+1,r是再加1.
然后把这些点放入点云current中。
2.用统计滤波器进行滤波
定义tmp点云指针,定义pcl的统计滤波器statistical_filter
pcl::StatisticalOutlierRemoval<PointT> statistical_filter;
3个设置,一个设置setMeanK(50),应该是附近50个点,一个设置setStddevMulThresh(1.0),应该是聚类吧。另一个就是把当前点云current给放进去setInputCloud(current),然后把滤波后的结果赋值给*tmp,statistical_filter.filter(*tmp),
然后把滤波后的点云*tmp拼接。
(*pointCloud)+=*tmp
统计滤波器是对每个图像生成的点云做的,而体素滤波器是对拼接之后的点云做的。
设置点云is_dense为false
pointCloud->is_dense=false;
3.用体素滤波器进行滤波
设置体素滤波器pcl::VoxelGrid<PointT> voxel_filter;同统计滤波器,点云的,每个点类型都为PointT.
它只需要设置两项,一个voxel_filter.setLeafSize(0.01,0.01,0.01),意思是每立方厘米放一个点,这个设置的分辨率还是比较高的。一个还是把点云放进去,不过这里是拼接后的点云。
voxel_filter.setInputCloud(pointCloud).
把滤波后的点云记为*tmp.voxel_filter.filter(*tmp);
然后tmp->swap(*pointCloud);
然后保存点云文件为pcd格式。
pcl::io::savePCDFileBinary("map.pcd",*pointCloud);
点云的优势就是可以直接由RGBD图像生成,滤波操作也很直观,而且处理效率也还可以接受。
当点云表达地图是非常初级的。如果是对于定位需求,首先原来基于特征点,不能用,因为点云里没有存储特征点,当然点云的ICP也是可以做的,但是精度要好,这里没有对点云进行优化,不行。
对于导航和避障,不行,因为没有说明哪里可以通过,哪里不可以通过。要做导航,基于点云地图,做占据网格地图等。
可视化和交互,可以基本看到,但是物体没有纹理信息,可以通过这个物体看到背后的物体,这些都不符合日常经验。
得到物体表面信息,可以通过sfM的泊松重建,surfel也可以了。