Apollo多传感器融合定位(MSF Location)——创建点云地图

Apollo为了实现点云与惯性导航的融合定位这个最终的功能,开放了一系列的工具,包括点云数据过滤,与GNSS的时间对齐,生成点云地图,使用点云地图与Ins惯性导航系统做融合定位,可视化定位效果,以及对最终的定位结果的评估等等这一些列的工具套件,从整体上来看,这些工具可以分为两个大类别,一是创建点云地图,二是使用点云地图与Ins(惯性导航系统)做融合定位。

下图描述了创建点云地图的主要流程,

数据提取 -> 位置对齐 -> 创建无损地图 -> 创建有损地图

创建点云地图是进行点云融合定位的第一步,作为先验性的地图信息参考。

要想创建一个点云地图,需要两个重要的信息,一个是点云信息,另一个是定位信息。

点云信息,来自激光LiDAR,是对环境扫描的点云信息,

包括XYZIT(X,Y,Z,强度,时间戳),

是相对于车身的相对坐标。

定位信息,来自全球定位导航系统(GNSS),GNSS融合了GPS+RTK+IMU的数据,能够精准定位(精度在10cm以内),

包括Position(位置),Orientation(姿态)和Vel(速度)信息,在创建点云地图时只需要Position和Orientation信息。

最终点云地图中的每一个点,是包含了准确的定位信息的点,所以,点云地图实际上就是对点云数据与定位数据的一个融合的结果。

接下来我们就来深入的探讨一下,这个融合过程是怎么实现的。

数据采集与解析

首先我们来看数据提取与解析。

输入数据:使用Cyber_record录制的数据包

输出数据:LiDAR数据, odometry数据

要创建某个区域的地图,首先需要对这个区域进行数据采集,主要采集LiDAR和GNSS数据,可以是用cyber_record库对这两个topic消息进行解析,对于LiDAR点云数据,我们需要使用PCL库将每一帧的点云数据其转换成PCD文件并保存,同时也要记录每一帧的索引文件,每行记录的内容是:index(pcd索引值),timestamp(时间戳),保存到索引文件pcd_timestamp.txt。

对于Odometry定位数据,也同样提取一份索引文件,没行内容为:index(位置索引),timestamp(时间戳),位置(

x, y, z) ,姿态(qx, qy, qz, qw) ,方差(std_x, std_y, std_z) ,并保存至文件odometry_loc.txt。

此外,在此次解析过程中,还有三个输出,分别是gnss_loc.txt,lidar_loc.txt和fusion_loc.txt,

这三个文件分别保存融合后的数据输出,所以,在第一次创建时,都为空。

有了pcd_timestamp.txt和odometry_loc.txt这两个索引文件以及所有的点云pcd数据,就可以做点云与GNSS时间对齐了。

点云数据与GNSS定位融合

点云数据是由若干帧组成的,我们的激光雷达采集频率是10Hz,即每秒10帧,每一帧是由若干个离散的点组成,目前每一帧原始点大概10万个点左右。为了能够方便有效的对这些点进行计算,通常采用pcd格式的文件对点云数据进行存储,每一帧存储为一个pcd文件。

而在算法层面,对于点云的处理主要依赖于PCL库,这个库提供了大部分公开的点云处理算法,在融合过程中使用的关键性的算法,均出自PCL库。

地理位置信息和点云数据是相对独立的,要想把它们融合在一起,就需要找到它们的关联点,唯一能把它们关联起来的,只有时间戳,莫急,我们一步一步往下看。

时间对齐与坐标转换插值

输入:GNSS定位数据索引文件 , 点云pcd数据 , 激光雷达外参

输出:时间对齐后的位置

GNSS定位信息的更新频率是100Hz,点云的更新频率是10Hz,显然不在一个频段上。也就是说,GNSS在各个时刻与点云的各个时刻是没有重合点的,要想获取激光雷达在某一时刻的位置,必须先要将激光雷达与定位系统做时间对齐,找到激光雷达距离定位系统最近的时间点,在通过激光雷达附近的定位点做插值计算,最终得到激光点云所在的位置信息。

我们先来看下准备工作:

遍历odometry_loc.txt定位文件{

对Pos(x,y,z)做坐标平移+旋转变换得到仿射矩阵

获取GNSS位置的时间戳作为对齐参考时间

和 方差

std::vector<Eigen::Affine3d>* poses

...

Eigen::Translation3d

trans(Eigen::Vector3d(x, y, z));

    Eigen::Quaterniond quat(qr, qx, qy, qz);

    poses->push_back(trans * quat);

    timestamps→push_back(timestamp);

Eigen::Vector3d std;

    std << std_x, std_y, std_z;

    stds->push_back(std);

}

这段简单的处理无疑是必要的准备,它可以得到任意时刻每个点odometry(车所在的位置)的仿射变换矩阵,那么,之后我们只要获取对应时刻的点云局部坐标值,乘以这一时刻的仿射矩阵,就可以把点云坐标转换到WGS84坐标系下,OK:-)

具体到算法上主要有以下几个步骤,

一是,在坐标平移的方向根据时间戳做线性插值,即找到距离第一帧点云数据最近并且在点云时间之前的定位信息时间,也就是保证第一帧pcd的时间在两个连续的pos时间之间,得到线性插值系数delta_t = (t – t0)/(t1-t0),

(

输入位置:odometry,

输入位置时间戳:odometry pos timestamps

参考时间戳:pcd的时间戳,

double

t = (cur_timestamp - ref_timestamp) / (cur_timestamp –

pre_timestamp)

)

根据线性插值系数delta_t,即可算出坐标平移x,y,z三个方向上的坐标值,

  Eigen::Translation3d re_transd;

      re_transd.x() = pre_transd.x() * t + cur_transd.x() * (1 - t);

      re_transd.y() = pre_transd.y() * t + cur_transd.y() * (1 - t);

      re_transd.z() = pre_transd.z() * t + cur_transd.z() * (1 - t);

二是,在坐标旋转方向上做插值计算,为了实现平滑插值的效果,使用的是四元数球面线性插值(slerp)算法,具体是

Eigen::Quaterniond

res_quatd = pre_quatd.slerp(1 - t, cur_quatd);

函数来实现的,

out_poses->push_back(re_transd

* res_quatd);

Eigen::Affine3d

pose_tem = out_poses_[i] * velodyne_extrinsic_;

通过时间对齐之后,对点云的每一帧进行坐标平移、旋转插值变换,再补上激光雷达的外参,就得到了每一帧点云的pcd在某一时刻的基础仿射变换矩阵,之后就可以从pcd中获取当前帧的每一点的位姿信息,乘以这个放射转换矩阵,最终将点云坐标转换到车的全局坐标系下(WGS84)。

做好每一帧点云时间对齐之后的索引数据保存至corrected_poses.txt文件中,内容包括:

帧索引(indexes),帧时间戳(timestamp),位置坐标(transd.x() transd.y()  transd.z()),姿态四元数(qx  qy  qz  qr )。

创建点云地图

至此,我们准备好了以下几个数据:

点云的每一帧的pcd文件

点云帧索引文件corrected_poses.txt,包含了每一帧索引和当前帧的精确base定位信息

激光雷达的外参

有了这两个数据,我们现在就可以创建地图了。

分为无损地图和有损地图两个部分。

创建无损地图

输入:pcd文件路径, 点云索引文件

输出: 无损地图配置文件, 无损地图

总结起来,创建地图也只有两个关键的动作

一是点云滤波

二是点云特征平面分割

点云滤波,使用的是体素网格滤波(VoxelGrid),简单说来的就是将每一帧的点云空间划分成若干个小空间,每个小空间包含了一定数量的点,最终计算出这些点的重心点来代表这个小空间,其他的点被过滤掉,从而实现滤波的作用。此处使用的具体算法是PCL::VoxelGrid算法。

特征平面分割,知行者中,采用的是采样一致(Ransac)算法,通过提取点云特征平面信息,并结合点云的平均高程信息,最终确认点云的地面特征,至此,根据过滤后的结果可以保存此地图数据,为无损地图(lossless

map)。

对每一帧做过滤波之后的点云数据被放入到无损地图队列中,等待保存处理。

那么点云地图又是怎么存储这些点云数据的呢,实际上就是做了一个空间上的映射,

首先点云地图使用Node节点来作为管理单元,可以根据当前需要创建点云的总数来创建Node节点总数,然后根据每个点的坐标值来计算Node的索引值,这样就建立起点云与存储之间的映射关系了。

最后每个点云数据存放在LosslessMapMatrix节点中,但此时还没完,因为对于水平位置x,y和高程z是分开来处理的,高程值z与点云强度值是按照给定的阈值范围内分层次来保存的,也就是说不同范围的高程数据保存在点云地图的不同层上。

而对于有损地图,就是在每个点云地图的NodeCell中,获取平均高程和灰度的方差值来代表当前cell的数据。

至此,点云的有损地图创建完成,也就代表点云地图的创建完成。

当前,Apollo基于多传感器融合定位的算法源码并没有开源,但是提供了算法的二进制库可以使用,可以在已有的库基础上做一些验证工作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容