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,也证明算法的有效性

相关推荐
美酒没故事°19 小时前
Open WebUI安装指南。搭建自己的自托管 AI 平台
人工智能·windows·ai
云烟成雨TD19 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
小O的算法实验室19 小时前
2026年ASOC,基于深度强化学习的无人机三维复杂环境分层自适应导航规划方法,深度解析+性能实测
算法·无人机·论文复现·智能算法·智能算法改进
AI攻城狮19 小时前
用 Obsidian CLI + LLM 构建本地 RAG:让你的笔记真正「活」起来
人工智能·云原生·aigc
鸿乃江边鸟19 小时前
Nanobot 从onboard启动命令来看个人助理Agent的实现
人工智能·ai
lpfasd12319 小时前
基于Cloudflare生态的应用部署与开发全解
人工智能·agent·cloudflare
俞凡19 小时前
DevOps 2.0:智能体如何接管故障修复和基础设施维护
人工智能
comedate20 小时前
[OpenClaw] GLM 5 关于电影 - 人工智能 - 的思考
人工智能·电影评价
财迅通Ai20 小时前
6000万吨产能承压 卫星化学迎来战略窗口期
大数据·人工智能·物联网·卫星化学
liliangcsdn20 小时前
Agent Memory智能体记忆系统的示例分析
数据库·人工智能·全文检索