SLAM中的非线性优-3D图优化之地平面约束(十五)

本节再详细讲解下地面约束的理论跟具体实现,本节以ceres-solver的解析雅克比进行验证,接下来情况具体实现;

一. 问题描述

二. 数学推导

二.结果展示

复制代码
  bool Evaluate(double const* const* parameters,
                    double* residuals,
                    double** jacobians) const override {

    Eigen::Map<const Eigen::Matrix<double, 3, 1>> p_a(parameters[0]);
    Eigen::Map<const Eigen::Quaterniond> q_a(parameters[1]);

    const Eigen::Matrix<double, 3, 3> G_R_O = q_a.toRotationMatrix();
    const Eigen::Matrix<double, 3, 1> G_p_O = p_a;
        
    // 提取平面参数
    const Eigen::Matrix<double, 3, 3> pi_R_G = Eigen::Matrix<double, 3, 3>::Identity();
    const double pi_z_G = 0.0;
        
    // 选择矩阵:取向量的前两个分量
    Eigen::Matrix<double, 2, 3> Lambda;
    Lambda << 1.0, 0.0, 0.0,
              0.0, 1.0, 0.0;
        
    // Z 轴单位向量
    const Eigen::Matrix<double, 3, 1> e3(double(0.0), double(0.0), double(1.0));

    Eigen::Map<Eigen::Matrix<double, 3, 1>> residual(residuals);    
 
    // 前两个残差:方向约束
    // O 坐标系的 Z 轴在平面坐标系下的投影应该为零(即与平面法线对齐)
    residual.head<2>() = -Lambda * pi_R_G * G_R_O * e3;
        
    // 第三个残差:高度约束
    // 位姿原点在平面法线方向上的投影应该等于平面高度
    residual[2] = pi_z_G + e3.transpose() * pi_R_G * G_p_O;

    residual = sqrt_information_ * residual;
    
    if (!jacobians) return true; 

    auto skew = [](const Eigen::Vector3d& v) -> Eigen::Matrix3d {
        Eigen::Matrix3d S;
        S << 0., -v.z(), v.y(),
            v.z(), 0., -v.x(),
            -v.y(), v.x(), 0.;
        return S;
    };

    // 注意:这里使用左乘的so(3)表示,因为残差前两行与旋转有关

    if (jacobians[0]) {
        Eigen::Map<Eigen::Matrix<double, 3, 3>, Eigen::RowMajor> jacobian_pos(jacobians[0]);
        Eigen::Matrix<double, 3, 3> jacobian = Eigen::Matrix<double, 3, 3>::Zero();
        
        // 对位置的雅可比(只有高度约束与位置有关)
        jacobian.block<2, 3>(0, 0).setZero();  // 方向约束与位置无关
        jacobian.block<1, 3>(2, 0) = e3.transpose() * pi_R_G;

        jacobian_pos = sqrt_information_ * jacobian;
    }

    // 对四元数的雅可比计算
    if (jacobians[1]) {
        Eigen::Map<Eigen::Matrix<double, 3, 4>, Eigen::RowMajor> jacobian_quat(jacobians[1]);
        
        // 计算残差对四元数的雅可比
        // 首先计算残差对旋转矩阵的导数
        Eigen::Matrix<double, 3, 3> dres_dR = Eigen::Matrix<double, 3, 3>::Zero();
        // 前两个残差对旋转的导数
        dres_dR.block<2, 3>(0, 0) = -Lambda * pi_R_G * skew(G_R_O * e3);
        // 第三个残差与旋转无关
        dres_dR.block<1, 3>(2, 0).setZero();
        
        // 然后计算旋转矩阵对四元数的导数 (3x4)
        // 四元数 q = [w, x, y, z],旋转矩阵 R(q)
        Eigen::Quaterniond q = q_a;
        Eigen::Matrix<double, 3, 4> dR_dq = Eigen::Matrix<double, 3, 4>::Zero();
        
        // 旋转矩阵对四元数的导数公式
        // dR/dq = [dR/dw, dR/dx, dR/dy, dR/dz]
        double w = q.w(), x = q.x(), y = q.y(), z = q.z();

        // 对 w 的导数(第0列)
        // ∂R/∂w = 2 * [0, -z, y; z, 0, -x; -y, x, 0]
        dR_dq(0, 0) = 0;      dR_dq(0, 1) = -2*z; dR_dq(0, 2) = 2*y;
        dR_dq(1, 0) = 2*z;    dR_dq(1, 1) = 0;    dR_dq(1, 2) = -2*x;
        dR_dq(2, 0) = -2*y;   dR_dq(2, 1) = 2*x;  dR_dq(2, 2) = 0;

        // 对 x 的导数(第1列)
        // ∂R/∂x = 2 * [0, y, z; y, -2x, -w; z, w, -2x]
        dR_dq(0, 3) = 0;      dR_dq(0, 4) = 2*y;   dR_dq(0, 5) = 2*z;
        dR_dq(1, 3) = 2*y;    dR_dq(1, 4) = -4*x;  dR_dq(1, 5) = -2*w;
        dR_dq(2, 3) = 2*z;    dR_dq(2, 4) = 2*w;   dR_dq(2, 5) = -4*x;

        // 对 y 的导数(第2列)
        // ∂R/∂y = 2 * [-2y, x, -w; x, 0, z; -w, z, -2y]
        dR_dq(0, 6) = -4*y;   dR_dq(0, 7) = 2*x;   dR_dq(0, 8) = -2*w;
        dR_dq(1, 6) = 2*x;    dR_dq(1, 7) = 0;     dR_dq(1, 8) = 2*z;
        dR_dq(2, 6) = -2*w;   dR_dq(2, 7) = 2*z;   dR_dq(2, 8) = -4*y;

        // 对 z 的导数(第3列)
        // ∂R/∂z = 2 * [-2z, -w, x; w, -2z, y; x, y, 0]
        dR_dq(0, 9) = -4*z;   dR_dq(0, 10) = -2*w; dR_dq(0, 11) = 2*x;
        dR_dq(1, 9) = 2*w;    dR_dq(1, 10) = -4*z; dR_dq(1, 11) = 2*y;
        dR_dq(2, 9) = 2*x;    dR_dq(2, 10) = 2*y;  dR_dq(2, 11) = 0;

        // 链式法则:dres/dq = dres/dR * dR/dq
        Eigen::Matrix<double, 3, 4> dres_dq = dres_dR * dR_dq;
        
        // 应用信息矩阵
        jacobian_quat = sqrt_information_ * dres_dq;
    }

    return true;
  }

注意上述残差与之前基本无区别,雅克比部分与之前章节也区别不大,唯一区别较大的地方是,对四元素求雅克比,这里展开下

也可以

上述公式唯一要注意的点是旋转矩阵的表示形式

式(56)实际上是哈密顿约定(Hamilton Convention): 四元数表示为:q = [w, x, y, z] ,其中 w 是实部

本文用的是JPL约定(Shuster Convention): 四元数表示为:q = [x, y, z, w] ,其中 w 是实部但放在最后

两种形式完全是等价的,可以利用单位四元数的模长是1,将两个的旋转矩阵的每个元素来一一转换

效果展示

总结

本节利用ceres-solver解析解进行平面约束求解,实际结果证明,再z轴方向确实能将结果拉成接近于0,也证明算法的有效性

相关推荐
星越华夏3 小时前
计算机视觉:YOLOv12安装环境
人工智能·yolo·计算机视觉
Yolanda945 小时前
【人工智能】《从零搭建AI问答助手项目(九):Prompt优化》
人工智能·prompt
wj3055853785 小时前
课程 9:模型测试记录与 Prompt 策略
linux·人工智能·python·comfyui
小和尚同志5 小时前
深入使用 skill-creator:结合真实生产级实践
人工智能·aigc
DevSecOps选型指南5 小时前
安全419专访悬镜安全 | 穿越周期在 AI 浪潮中定义数字供应链安全新范式
人工智能
沪漂阿龙5 小时前
面试题详解:GraphRAG 全面解析——知识图谱增强 RAG、Local Search、Global Search、社区摘要、工程落地与评估指标一次讲透
人工智能·知识图谱
WangN25 小时前
Unitree RL Lab 学习笔记【通识】
人工智能·机器学习
吃好睡好便好5 小时前
在Matlab中绘制横直方图
开发语言·学习·算法·matlab
haina20195 小时前
海纳AI亮相《科创中国》,解码招聘“智”变之路
人工智能·ai面试·ai招聘
阿星AI工作室5 小时前
刘润年中大课笔记:一句话说清AI落地之战的本质
大数据·人工智能·创业创新·商业