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 小时前
技术白皮书:AI驱动下的光伏电站智能化运维新范式
运维·人工智能·物联网
北京宇音天下19 小时前
VTX316语音合成芯片:低功耗高自然度,开启TTS语音新未来
人工智能·语音识别
Stardep19 小时前
算法入门19——二分查找算法——X的平方根
算法·leetcode·二分查找算法
We་ct19 小时前
LeetCode 135. 分发糖果:双向约束下的最小糖果分配方案
前端·算法·leetcode·typescript
minhuan19 小时前
大模型应用:联邦学习融合本地大模型:隐私合规推荐的核心流程与实践.62
大数据·人工智能·大模型应用·联邦学习推荐系统·推荐系统案例
宇钶宇夕19 小时前
CoDeSys入门实战一起学习(十三):函数(FUN)深度解析:自定义、属性与实操案例
运维·算法·自动化·软件工程
落叶,听雪19 小时前
性价比高的软著助手供应商选哪家
大数据·人工智能·python
懒羊羊吃辣条19 小时前
充分利用未来已知信息:DAG 用双因果结构把 TSF-X 时序预测推到新高度
人工智能·深度学习·机器学习
l1t19 小时前
对clickhouse给出的二分法求解Advent of Code 2025第10题 电子工厂 第二部分的算法理解
数据库·算法·clickhouse
易晨 微盛·企微管家19 小时前
汽车经销服务实战案例解析|企业微信AI SCRM助力实现咨询标准化与即时化
人工智能