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

相关推荐
一楼的猫7 分钟前
茄子写作助手——品牌搜索突破9万后的技术型品牌认知与官网入口指南
人工智能·学习·机器学习·chatgpt·ai写作
ZeroNews内网穿透8 分钟前
NAS部署Hermes AI Agent + 零讯内网穿透,实现远程可管理的AI助手
人工智能·安全·ai·内网穿透
隔窗听雨眠21 分钟前
原生一体化多模态大模型技术研究:从拼接到统一的架构革命
人工智能·架构
羊羊小栈31 分钟前
Uplift营销供应链协同决策系统(基于Uplift因果推断与运筹优化算法)
前端·人工智能·算法·毕业设计·大作业
苏州邦恩精密34 分钟前
江苏三维扫描仪厂家如何选择合适的工业测量方案?
人工智能·科技·机器学习·3d·自动化·制造
humors22134 分钟前
100种社会实践
人工智能·程序人生
保卫大狮兄39 分钟前
什么是WBS项目管理?WBS有哪些核心功能?
大数据·人工智能
标书畅畅行41 分钟前
钛投标:全流程企业级AI标书解决方案,重构投标数字化生产力
大数据·人工智能
叫我:松哥1 小时前
基于深度卷积神经网络的水果图片分类算法设计与实现,有ResNet50的迁移学习模型,准确率达95%
人工智能·python·神经网络·机器学习·分类·cnn·迁移学习
大囚长1 小时前
大模型API的上下文缓存(Contextual Cache)
人工智能·缓存