karto-slam解析

karto源代码在好几个月之前就已经看完了,但是一直没有整理,趁着周末来整理一下。

应用层

作为用户需要知道kartoslam是如何使用的。首先需要从github上git以下两个包:

git clone https://github.com/ros-perception/open_karto(开源的karto包,实现底层的kartoslam)

git clone https://github.com/ros-perception/slam_karto(ros层,也就是应用层的kartoslam接口)

将这些包放到工作目录,完成编译,我们就能尝试运行代码啦(当然你会发现,运行会有error)~~~~~~~

因为,在这之前我们需要为这个kartoslam定义一个tf变换关系才能保证代码的正确运行:

在karto_slam.launch中加入/base_link与laser的相对/tf关系(<node pkg="tf" type="static_transform_publisher" name="link_to_laser" args="0 0 0 0 0 0 1 /base_link /laser 10" />)(为什么呢?后面再讲~~)

如果你有一个激光雷达,一台turtlebot,还有一台电脑,那么你就可以利用kartoslam实现slam啦!当然,我们也可以在karto_slam.launch修改一下配置(比方说,我们希望的地图更新频率:map_update_interval,地图的分辨率:resolution等等)。

理论层

机器人在运行slam的过程中,需要知道几个参考坐标系之间的关系:

map_frame:世界坐标系中的固定frame。也就是说,这是一个全局的固定frame,它理论上不会随着机器人的移动或者测量的误差而产生漂移,一般设置系统的初始状态作为坐标原点。

odom_frame:里程计frame。它是移动机器人在移动过程中利用里程计(如车轮编码器,视觉里程计等)对机器人进行位姿估计的坐标系,一般设置机器人初始位置作为坐标原点,总所周知,里程计在长时间的使用中会出现一下累计误差

baselink_link_frame:机器人frame。它是机器人坐标系,是一个移动的坐标系。

laser_frame:激光frame。它是激光雷达的坐标系,固定在机器人上,与baselink_link_frame往往只存在一个平移变换量。

这4个坐标系是按照树状结构组织起来的,它们的对应关系如下图所示:


从frame图中可以看到,laser_frameaselink_link_frame的关系是由/link_to_laser得到的(也就是一开始我们在launch中添加的代码);baselink_link_frameodom_frame之间的关系是由底层的里程计信息topic输出得到的;而odom_framemap_frame之间的变换关系是由karto_slam得到的。karto_slam一直在做的事情就是:不断去修正里程计与固定的全局坐标系map_frame的误差,从而得到机器人相对于固定的世界坐标系更加精准的位姿信息。

代码层

代码从slam_karto.cpp开始,首先需要有接口函数

SlamKarto::addScan(karto::LaserRangeFinder* laser,const sensor_msgs::LaserScan::ConstPtr& scan,  karto::Pose2& karto_pose)
得到里程计和激光信息,并通过

  // create localized range scan
  karto::LocalizedRangeScan* range_scan = new karto::LocalizedRangeScan(laser->GetName(), readings);
range_scan->SetOdometricPose(karto_pose);
  range_scan->SetCorrectedPose(karto_pose);
将数据放入kato特定的数据结构当中。

(processed = mapper_->Process(range_scan))
Process是karto的入口函数,首先需要初始化:

      if (m_Initialized == false)
      {
        // initialize mapper with range threshold from device
        Initialize(pLaserRangeFinder->GetRangeThreshold());
      }

之后对满足关键帧要求的数据进行匹配

m_pSequentialScanMatcher->MatchScan(pScan,
                                            m_pMapperSensorManager->GetRunningScans(pScan->GetSensorName()),
                                                                                    bestPose,
                                                                                    covariance);
得到估计的位姿信息 bestPose和对应的协方差矩阵 convariance。
至于匹配的方法,具体就不细讲了。主要用的是map和frame的匹配,采用窗口法寻找最优的变换矩阵,并采用粗匹配(coarseSearch),精匹配(fineSearch)和建立一个lookupTable加快了寻优的速度,提高寻优精度。

之后将当前帧放入图优优化中,增加vertex和edges。

注:图的构造(感觉自己说的好绕口)

1, 只提取关键帧(两帧直接距离大于设定值)
2,当前帧相邻时间的关键帧相连
3,当前帧与runningScan中最近的帧相连 (runningScan可以理解为局部地图)
4,与当前帧相邻但不相连的帧链中最近的帧相连(闭环连接关系) 
5,闭环检测:将在一定范围内的不与当前帧相连且不与NearLinkedScans相连的帧连接起来组成闭环(也就是在一定范围内不与当前帧有任何连线的帧连接起来,构成闭环)

然后,尝试闭环检测

        if (m_pDoLoopClosing->GetValue())
        {
          std::vector<Name> deviceNames = m_pMapperSensorManager->GetSensorNames();
          const_forEach(std::vector<Name>, &deviceNames)
          {
            m_pGraph->TryCloseLoop(pScan, *iter);
          }
        }
最后更新corrected_pose,publish map_to_odom的tf关系(也就是里程计的漂移误差)。

参考文献

《Efficient Sparse Pose Adjustment for 2D Mapping》

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值