SLAM中的非线性优-3D图优化之相对位姿Between Factor位姿图优化(十三)

本节是相对位姿图优化,最后一篇实战篇,将之前理论讲解用代码形式展现,此处只展现了左扰动跟右扰动,至于SO3/t与四元数的形式,还没有完全调试好,并且利用G2O作为优化器,ceres-sover优化也作了实现,但是效果不太稳定,时好时坏,待后续升级,G2O版本的左右扰动较为稳定 ,接下来看实际效果

完整代码依然存储在码云上https://gitee.com/zl_vslam/slam_optimizer/blob/master/3d_optimize/apps/main_ch13.cpp

一.效果展示

如上图所示,仿真生成1km的矩形路径,包含了帧间边跟回环边,然后利用之前左右雅克比的推导,实现位姿图优化,效果如下

上图中绿色为真值,红色为里程计,蓝色为优化后的位姿,紫色为回环约束,左右雅克比去掉真值显示如下

可以看到经过位姿图优化后被重新拉成了环型;

二.代码展示

复制代码
    void computeJacobianLeftDisturbance(Matrix6d& jacobian_i, Matrix6d& jacobian_j, const Sophus::SE3d& v1, Sophus::SE3d& v2) {
        Matrix6d J = JRInv(SE3d::exp(_error));
        jacobian_i = -J*v2.inverse().Adj();
        jacobian_j = J*v2.inverse().Adj();
    }

    void computeJacobianRightDisturbance(Matrix6d& jacobian_i, Matrix6d& jacobian_j, const Sophus::SE3d& v1, Sophus::SE3d& v2) {
        Matrix6d J = Matrix6d::Identity();
        // J = JRInv(SE3d::exp(_error));
        jacobian_i = -J* (v2.inverse()*v1).Adj();
        jacobian_j = J * Matrix6d::Identity();
    }

    void computeJacobianRightDisturbanceGTSAM(Matrix6d& jacobian_i, Matrix6d& jacobian_j, const Sophus::SE3d& v1, Sophus::SE3d& v2) {
        // 转换为 GTSAM Pose3
        gtsam::Pose3 gtsam_pose_i(gtsam::Rot3(v1.rotationMatrix()), 
                            gtsam::Point3(v1.translation()));
        gtsam::Pose3 gtsam_pose_j(gtsam::Rot3(v2.rotationMatrix()), 
                            gtsam::Point3(v2.translation()));
        gtsam::Pose3 measured(gtsam::Rot3(_measurement.rotationMatrix()), 
                            gtsam::Point3(_measurement.translation()));
    
        // 创建 between factor(这里噪声模型不重要,因为我们只调用 evaluateError)
        auto model = gtsam::noiseModel::Isotropic::Sigma(6, 1.0);
        gtsam::BetweenFactor<gtsam::Pose3> factor(
            (static_cast<VertexSE3LieAlgebra*> (_vertices[0]))->id(), 
            (static_cast<VertexSE3LieAlgebra*> (_vertices[1]))->id(), 
            measured, 
            model);
    
        // 直接调用 evaluateError 计算残差和雅可比
        gtsam::Matrix gtsam_jacobian_i, gtsam_jacobian_j;
        gtsam::Matrix gtsam_residual;
    
        boost::optional<gtsam::Matrix&> H1(gtsam_jacobian_i);
        boost::optional<gtsam::Matrix&> H2(gtsam_jacobian_j);
        // 调用 evaluateError
        gtsam_residual = factor.evaluateError(gtsam_pose_i, gtsam_pose_j, H1, H2);

        /////////////////////////////////////////////////
        /////////////////////////////////////////////////
        /////////////////////////////////////////////////
        /////////////////////////////////////////////////
        std::cout << " gtsam_jacobian_i : " << (static_cast<VertexSE3LieAlgebra*> (_vertices[0]))->id() << " \n" 
                  << gtsam_jacobian_i << std::endl;
        std::cout << " gtsam_jacobian_j : " << (static_cast<VertexSE3LieAlgebra*> (_vertices[1]))->id() << " \n" 
                  << gtsam_jacobian_j << std::endl;
        std::cout << " gtsam_residual : " << gtsam_residual.transpose() << std::endl;


        Matrix6d J = Matrix6d::Identity();
        // J = JRInv(SE3d::exp(_error));
        std::cout << " jacobian_i : " << (static_cast<VertexSE3LieAlgebra*> (_vertices[0]))->id() << " \n" 
                  << -J* (v2.inverse()*v1).Adj() << std::endl;
        std::cout << " jacobian_j : " << (static_cast<VertexSE3LieAlgebra*> (_vertices[1]))->id() << " \n" 
                  << J * Matrix6d::Identity() << std::endl;
        std::cout << " residual : " << _error.transpose() << std::endl;

        /////////////////////////////////////////////////
        /////////////////////////////////////////////////
        /////////////////////////////////////////////////
        /////////////////////////////////////////////////

        jacobian_i = gtsam_jacobian_i;
        jacobian_j = gtsam_jacobian_j;


        // 交换左上角 3x3 块和右下角 3x3 块
        Eigen::Matrix3d temp_i_top = jacobian_i.block<3, 3>(0, 3);  // 右上角
        Eigen::Matrix3d temp_i_bottom = jacobian_i.block<3, 3>(3, 0);  // 左下角

        // 交换
        jacobian_i.block<3, 3>(0, 3) = temp_i_bottom;
        jacobian_i.block<3, 3>(3, 0) = temp_i_top;

        // 对 jacobian_j 做同样的操作
        Eigen::Matrix3d temp_j_top = jacobian_j.block<3, 3>(0, 3);
        Eigen::Matrix3d temp_j_bottom = jacobian_j.block<3, 3>(3, 0);

        jacobian_j.block<3, 3>(0, 3) = temp_j_bottom;
        jacobian_j.block<3, 3>(3, 0) = temp_j_top;

        std::cout << " final gtsam_jacobian_i : " << (static_cast<VertexSE3LieAlgebra*> (_vertices[0]))->id() << " \n" 
                  << jacobian_i << std::endl;
        std::cout << " final gtsam_jacobian_j : " << (static_cast<VertexSE3LieAlgebra*> (_vertices[1]))->id() << " \n" 
                  << jacobian_j << std::endl;
    }

完整代码请自行看代码吧,上述代码就是左右雅可比

总结

本节展示了完整版位姿图优化效果,用实践证明了理论的正确性,其中G2O优化效果较好,因为其对位姿的表示天然支持SE3,而ceres-solver效果较差,其对四元数支持较好,此处暂时并未实现,本节博客后期会持续更新修复这些问题,修复ceres-solver优化问题,相对位姿雅克比已经花费较多时间,下一讲讲解,位姿图中的平面约束,用以解决地面机器人Z方向飘移问题;

相关推荐
一次旅行19 分钟前
DeepSeek‑V4.1‑Flash‑1017完整解读:模型升级+Search/Fetch搜索API开放,Agent开发者要注意哪些坑
前端·网络·人工智能
Jsundoku29 分钟前
第三章 机器学习 -- 特征工程
人工智能·机器学习
GIS数据转换器2 小时前
地下空间综合管廊运营管理平台
大数据·开发语言·人工智能·经验分享·perl
dozenyaoyida8 小时前
AI与大模型新闻日报 | 2026-10-17
人工智能·ai·chatgpt·新闻
遇码8 小时前
流式调用中途断连别直接报错:瞬态故障识别、退避重试与中文根因诊断的搭建方法
大数据·人工智能·ai·rust·数据分析·大模型·agent
救救孩子把8 小时前
AI面试八股文-21_21-多模态Agent面试题-Vision-Language-Agent
人工智能·面试·职场和发展·llava·多模态agent
今年下半年8 小时前
AI智能体项目实战记录(02):初始化 Python 项目
开发语言·人工智能·python·fastapi
Jlzn88888 小时前
华东电芯分选通道数怎么配才不堵线
java·人工智能·架构·自动化·制造
HPtiger8 小时前
数据结构——选择排序
数据结构·算法·排序算法