cartographer 代码思想解读(3)- ceres优化库scan-match

cartographer 代码思想解读(3)- ceres优化库scan-match


前两节分析了cartographer 中的相关匹配思想和相关匹配优化快速实现,但cartographer之所以局部slam即前端匹配的准确度极高,因为最终采用了优化匹配的方法,即比栅格化的地图相关匹配准确度更高。而cartographer将匹配转换成最小二乘思想,并采用自家的ceres库完成优化匹配。

其详细的代码解释可参考他人博客:
基于Ceres库的扫描匹配器

ceres匹配简单总结

//采用ceres库求解
/*
input:
1.估计位置
2.初始位置
3.点云
4.栅格地图
输出:
1.最佳优化值
2.优化信息描述
 */
void CeresScanMatcher2D::Match(const Eigen::Vector2d& target_translation,
                               const transform::Rigid2d& initial_pose_estimate,
                               const sensor::PointCloud& point_cloud,
                               const Grid2D& grid,
                               transform::Rigid2d* const pose_estimate,
                               ceres::Solver::Summary* const summary) const {
    // 估计位置初始值
  double ceres_pose_estimate[3] = {initial_pose_estimate.translation().x(),
                                   initial_pose_estimate.translation().y(),
                                   initial_pose_estimate.rotation().angle()};
    //求解器
  ceres::Problem problem;
  CHECK_GT(options_.occupied_space_weight(), 0.);
  //两种类型
  switch (grid.GetGridType()) {
      // 概率地图
    case GridType::PROBABILITY_GRID:
        // 增加匹配的代价函数, 添加误差项
      problem.AddResidualBlock(
          CreateOccupiedSpaceCostFunction2D(
              options_.occupied_space_weight() /
                  std::sqrt(static_cast<double>(point_cloud.size())),
              point_cloud, grid),
          nullptr /* loss function */, ceres_pose_estimate);   
      break;
    case GridType::TSDF:
      problem.AddResidualBlock(
          CreateTSDFMatchCostFunction2D(
              options_.occupied_space_weight() /
                  std::sqrt(static_cast<double>(point_cloud.size())),
              point_cloud, static_cast<const TSDF2D&>(grid)),
          nullptr /* loss function */, ceres_pose_estimate);
      break;
  }
  CHECK_GT(options_.translation_weight(), 0.);
  // 增加平移权重,代价函数, 平移代价,即优化的位置与target_translation,????不理解,理论上迭代初始值不应该是预测的值吗
  problem.AddResidualBlock(
      TranslationDeltaCostFunctor2D::CreateAutoDiffCostFunction(
          options_.translation_weight(), target_translation),
      nullptr /* loss function */, ceres_pose_estimate);
  CHECK_GT(options_.rotation_weight(), 0.);
  // 增加旋转权重,代价函数,????,和优化本身比较,有什么意义?????
  problem.AddResidualBlock(
      RotationDeltaCostFunctor2D::CreateAutoDiffCostFunction(
          options_.rotation_weight(), ceres_pose_estimate[2]),
      nullptr /* loss function */, ceres_pose_estimate);

    // 优化器求解
  ceres::Solve(ceres_solver_options_, &problem, summary);

  *pose_estimate = transform::Rigid2d(
      {ceres_pose_estimate[0], ceres_pose_estimate[1]}, ceres_pose_estimate[2]);
}

由于采用了ceres库进行优化求解,其流程较为简单,仅简单描述下其模型。将匹配转换成最小二乘的问题,即需建立最小二乘等式。scan-match包含3个代价函数,可配置其相关权重。

  1. 点云在栅格地图的匹配程度,期望:匹配度越高;
  2. 优化的pose与估计的target_pose偏移程度,期望:偏移量越小;
  3. 优化的angle与init_pose的angle偏移程度,期望:偏移量越小;

其中2和3较为简单,可自行看源码,注意:第一节中的一个疑问,相关匹配后的结果也会考虑其与初始位置偏差进行权重化, 说明cartographer认为其匹配后的值应该与初始估计值相差不大。而点云在栅格地图的匹配为主要代价函数。

扫描匹配OccupiedSpaceCostFunction2D

点云在栅格地图的匹配程度如相关匹配方法一致,即将点云转换至地图坐标后,统计所有点云在栅格grid图中的概率值,越大表明匹配程度越高。由于为代价函数,因此期望匹配越高,则代价越低,故采用grid中的CorrespondenceCost替代probability值(CorrespondenceCost = 1-probability )。
由于栅格地图中坐标是根据默认0.05m的分辨率进行采样得到,即地图坐标相对来说较为稀疏,之所以其优化精度高于相关匹配,Cartographer将grid进行了双三次插值,即可认为更高分辨率的栅格地图。采用了ceres自带的双三插值器,十分方便,其代码注释如下。

  // 输入:权重, 点云, 栅格地图
  OccupiedSpaceCostFunction2D(const double scaling_factor,
                              const sensor::PointCloud& point_cloud,
                              const Grid2D& grid)
      : scaling_factor_(scaling_factor),
        point_cloud_(point_cloud),
        grid_(grid) {}
  
  // 类型模板
  template <typename T>
  // pose为输入待优化量, residual为参差
  bool operator()(const T* const pose, T* residual) const {
    // 平移矩阵
    Eigen::Matrix<T, 2, 1> translation(pose[0], pose[1]);
    // 旋转向量
    Eigen::Rotation2D<T> rotation(pose[2]);
    // 旋转矩阵
    Eigen::Matrix<T, 2, 2> rotation_matrix = rotation.toRotationMatrix();
    // 2维转移矩阵, 即当前位置在世界坐标系下的转移矩阵
    Eigen::Matrix<T, 3, 3> transform;
    transform << rotation_matrix, translation, T(0.), T(0.), T(1.);

    // 重新定义grid
    const GridArrayAdapter adapter(grid_);
    // 这里将构造时传入的概率栅格图(local submap)加载到一个双三次插值器中
    // Grid2D还可以利用BiCubicInterpolator实现双三次插值,它相对于双线性插值的优点是能实现自动求导
    ceres::BiCubicInterpolator<GridArrayAdapter> interpolator(adapter);
    const MapLimits& limits = grid_.limits();

    for (size_t i = 0; i < point_cloud_.size(); ++i) {
      // Note that this is a 2D point. The third component is a scaling factor.
      const Eigen::Matrix<T, 3, 1> point((T(point_cloud_[i].position.x())),
                                         (T(point_cloud_[i].position.y())),
                                         T(1.));
      // 将点云转换为世界坐标
      const Eigen::Matrix<T, 3, 1> world = transform * point;
      // 迭代评价函数
      // 将坐标转换为栅格坐标,双三次插值器自动计算中对应坐标的value
      interpolator.Evaluate(
          (limits.max().x() - world[0]) / limits.resolution() - 0.5 +
              static_cast<double>(kPadding),
          (limits.max().y() - world[1]) / limits.resolution() - 0.5 +
              static_cast<double>(kPadding),
          &residual[i]);
      // 所有参差加入同一权重
      residual[i] = scaling_factor_ * residual[i];
    }
    return true;
  }

总结

采用ceres优化匹配为cartographer 算法中前端核心匹配算法,而相关匹配和快速相关匹配则可作为第一步的预测匹配,可为优化匹配提供一个较好的初始值。其中真正前端中相关匹配方法可以进行配置不使能。

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在cartographer中,使用2D点云进行扫描匹配时,可以使用ceresscanmatch功能。这个功能是基于Ceres Solver实现的。Ceres Solver是一个非线性优化,用于解决各种最小化问题。在cartographer中,ceresscanmatch被用于解决2D点云匹配的问题。 具体来说,ceresscanmatch用于匹配两个相邻帧的2D点云。在进行扫描匹配时,需要先对数据进行滤波处理,然后使用ceres进行优化,找到两个点云之间的最佳匹配。在这个过程中,需要使用一种优化算法来最小化匹配误差,这个误差是通过计算点云之间的距离来得到的。 相比于其他扫描匹配方法,ceresscanmatch的优势在于它能够进行非常精准的匹配。这是因为它使用了一个非线性优化算法,能够处理复杂的误差函数和约束条件。此外,ceresscanmatch还支持使用多种不同的误差函数,以适应不同的应用场景。 总之,ceresscanmatchcartographer中用于2D点云扫描匹配的一个非常重要的功能,它能够让我们更加准确、稳定地进行扫描匹配,并且支持广泛的应用场景。 ### 回答2: 本文将继续介绍cartographer中的ceres扫描匹配部分,ceres扫描匹配是利用Ceres Solver进行的位姿优化,可以准确估计机器人运动的姿态。 ceres扫描匹配部分主要包括ceres_scan_matcher.cc和ceres_scan_matcher.h两个文件。其中ceres_scan_matcher.cc包含了ceres扫描匹配算法的具体实现,而ceres_scan_matcher.h则是相关的头文件。 ceres_scan_matcher.cc中的函数主要有两个,分别是CeresScanMatcher::Match()和CeresScanMatcher::MatchFullSubmap()。其中,CeresScanMatcher::Match()函数用于实现一次扫描匹配,输入参数为当前激光数据和候选的位姿,输出参数为匹配的位姿和评估值。 在CeresScanMatcher::Match()函数中,先通过叶芽上下文来获取轨迹和submap,然后将当前激光数据转换为点云,并对点云进行滤波和预处理,接着定义优化问题和相关的参数,其中优化问题使用ceres::Problem类来定义,相关参数则定义在CeresScanMatcherOptions结构体中,最后通过ceres::Solve()函数进行位姿优化CeresScanMatcher::MatchFullSubmap()函数则用于在整个submap上进行匹配,并返回匹配的位姿和评估值。它的实现与CeresScanMatcher::Match()函数类似,只是输入参数为整个submap的信息。 综上所述,ceres扫描匹配部分利用Ceres Solver进行位姿优化,可以准确估计机器人运动的姿态,是cartographer中重要的功能之一。 ### 回答3: cartographer是一款开源的SLAM系统,其源代码完整透明,方便研究和理解。其中,2D点云扫描匹配是cartographer中的一个重要功能,而这一功能又是由ceres扫描匹配实现的。 ceresscanmatchcartographer中的一个重要模块,用于实现2D点云的扫描匹配。在这个模块中,ceres solver被用来进行优化过程。具体来说,ceresscanmatch会将已知位姿下的实测点云与预测的点云进行匹配,得到匹配误差。随后,ceres solver会对这些匹配误差进行非线性优化,最终得到最优位姿。这样,就能够实现快速准确的2D点云扫描匹配,从而提高了SLAM系统的性能和精度。 在详细研究ceresscanmatch之前,首先需要了解一下ceres solverceres solver是一个基于C++的非线性优化,用于解决复杂的数值优化问题。在cartographer中,ceres solver被用来进行扫描匹配的优化过程,应用目标函数和求解器来寻求最优解。其中,目标函数是由误差项和状态变量构成的,求解器则用来解决这个目标函数并确定状态变量的最优化值。 具体而言,在cartographer中,扫描匹配的目标函数是根据传感器数据得出的,其包括一系列误差项和参考帧的相对位姿。在每个迭代步骤中,ceres solver会计算目标函数的梯度和海森矩阵,并利用这些值来更新参考帧的位姿。当误差项最小化时,相对位姿就能够得到最优解。 总之,ceresscanmatchcartographer中的一个重要模块,用于实现2D点云的扫描匹配。借助ceres solver进行优化,可以实现高效准确的扫描匹配,为SLAM系统的实现提供了重要的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值