SLAM本质剖析-GTSAM

举报
Hermit_Rabbit 发表于 2022/09/21 20:24:48 2022/09/21
【摘要】 0. 前言在深入剖析了Ceres、Eigen、Sophus、G2O后,以V-SLAM为代表的计算方式基本已经全部讲完,但是就L-SLAM而言我们还差一块,那就是PCL、GTSAM点云计算部分我们还没有详细的去写,正好就这个时间,我想把这块坑给填完,来形成一整个系列,方便自己回顾以及后面的人一起学习。 GTSAM系统认知我们在一般的认知,G2O和GTSAM都做着后端图优化的功能,而他们当中的...

0. 前言

在深入剖析了CeresEigenSophusG2O后,以V-SLAM为代表的计算方式基本已经全部讲完,但是就L-SLAM而言我们还差一块,那就是PCL、GTSAM点云计算部分我们还没有详细的去写,正好就这个时间,我想把这块坑给填完,来形成一整个系列,方便自己回顾以及后面的人一起学习。

GTSAM系统认知

我们在一般的认知,G2O和GTSAM都做着后端图优化的功能,而他们当中的全局优化器的性能却是不尽相同。

  • Ceres是针对所有种类的函数的优化,所以只能用最速梯度下降法。
  • G2o是求解的一个标准的最小二乘
    在这里插入图片描述
  • GTSAM准确的说不是解的优化方程。而是先把一个概率图的联合概率的信息矩阵和信息向量表示出来
    在这里插入图片描述
    但具有物理意义的是均值和协方差。所以通过信息矩阵和协方差矩阵的换算计算出均值。这个换算里面有求逆,等于是在解方程。
    如图所示,我们在下图轨迹中进行evo误差估计,发现G2O效果其实略好于GTSAM,但是为了我们系统的完整性,我们仍然需要讲一下GTSAM。
    在这里插入图片描述

在这里插入图片描述

2. GTSAM/ISAM2示例

iSAM2 将图优化问题转换成 Bayes tree 的建立、更新和推理问题。是GTSAM函数库中常用的一种方法(LeGO-LOAM中使用的即为ISAM2)。下面我们针对GTSAM和ISAM2来从两个层面理清楚写法上的差异。这部分的代码参考了这篇文章
在这里插入图片描述
GTSAM

#include <gtsam/geometry/Pose2.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/Values.h>
#include <gtsam/inference/Symbol.h>
#include <gtsam/slam/PriorFactor.h>
#include <gtsam/slam/BetweenFactor.h>
#include <gtsam/nonlinear/GaussNewtonOptimizer.h>
#include <gtsam/nonlinear/Marginals.h>
using namespace std;
using namespace gtsam;
int main(int argc, char** argv) 
{
    NonlinearFactorGraph graph;
 
    Values initials;
    initials.insert(Symbol('x', 1), Pose2(0.2, -0.3, 0.2));
    initials.insert(Symbol('x', 2), Pose2(5.1, 0.3, -0.1));
    initials.insert(Symbol('x', 3), Pose2(9.9, -0.1, -M_PI_2 - 0.2));
    initials.insert(Symbol('x', 4), Pose2(10.2, -5.0, -M_PI + 0.1));
    initials.insert(Symbol('x', 5), Pose2(5.1, -5.1, M_PI_2 - 0.1));
    initials.print("\nInitial Values:\n"); 
    //固定第一个顶点,在gtsam中相当于添加一个先验因子
    noiseModel::Diagonal::shared_ptr priorModel = noiseModel::Diagonal::Sigmas(Vector3(1.0, 1.0, 0.1));
    graph.add(PriorFactor<Pose2>(Symbol('x', 1), Pose2(0, 0, 0), priorModel));
 
    //二元位姿因子
    noiseModel::Diagonal::shared_ptr odomModel = noiseModel::Diagonal::Sigmas(Vector3(0.5, 0.5, 0.1));
    graph.add(BetweenFactor<Pose2>(Symbol('x', 1), Symbol('x', 2), Pose2(5, 0, 0), odomModel));
    graph.add(BetweenFactor<Pose2>(Symbol('x', 2), Symbol('x', 3), Pose2(5, 0, -M_PI_2), odomModel));
    graph.add(BetweenFactor<Pose2>(Symbol('x', 3), Symbol('x', 4), Pose2(5, 0, -M_PI_2), odomModel));
    graph.add(BetweenFactor<Pose2>(Symbol('x', 4), Symbol('x', 5), Pose2(5, 0, -M_PI_2), odomModel));
 
    //二元回环因子
    noiseModel::Diagonal::shared_ptr loopModel = noiseModel::Diagonal::Sigmas(Vector3(0.5, 0.5, 0.1));
    graph.add(BetweenFactor<Pose2>(Symbol('x', 5), Symbol('x', 2), Pose2(5, 0, -M_PI_2), loopModel));
    graph.print("\nFactor Graph:\n"); 
 
 
    GaussNewtonParams parameters;
    parameters.setVerbosity("ERROR");
    parameters.setMaxIterations(20);
    parameters.setLinearSolverType("MULTIFRONTAL_QR");
    GaussNewtonOptimizer optimizer(graph, initials, parameters);
    Values results = optimizer.optimize();
    results.print("Final Result:\n");
 
    Marginals marginals(graph, results);
    cout << "x1 covariance:\n" << marginals.marginalCovariance(Symbol('x', 1)) << endl;
    cout << "x2 covariance:\n" << marginals.marginalCovariance(Symbol('x', 2)) << endl;
    cout << "x3 covariance:\n" << marginals.marginalCovariance(Symbol('x', 3)) << endl;
    cout << "x4 covariance:\n" << marginals.marginalCovariance(Symbol('x', 4)) << endl;
    cout << "x5 covariance:\n" << marginals.marginalCovariance(Symbol('x', 5)) << endl;
 
    return 0;
}

ISAM

…详情请参照古月居

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。