Sophus使用记录

2023-11-09

sophus库是一个基于Eigen的C++李群李代数库,可以用来方便地进行李群李代数的运算。

头文件

主要用到以下两个头文件

#include <sophus/so3.h>
#include <sophus/se3.h>

SO(3)的使用

SO(3)的定义

// Sophus::SO3可以直接从旋转矩阵构造
Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d(0,0,1)).toRotationMatrix();
Sophus::SO3d SO3_R(R); 
// 亦可从旋转向量构造
Sophus::SO3d SO3_v = Sophus::SO3d::exp({0, 0, M_PI/2}); 
// 或者四元数
Eigen::Quaterniond q(R); 
Sophus::SO3d SO3_q(q);

SO(3)的运算

// 旋转矩阵
Eigen::Matrix3d R0 = SO3_R.matrix();
// 旋转向量,也即李代数
Eigen::Vector3d so3 = SO3_R.log();
// hat为向量到反对称矩阵
Eigen::Matrix3d so3_hat = Sophus::SO3d::hat(so3);
// 相对的,vee为反对称到向量
Eigen::Vector3d so3_vee = Sophus::SO3d::vee(so3_hat);
// 更新
Eigen::Vector3d update_so3(1e-4, 0, 0);
Sophus::SO3d SO3_updated = Sophus::SO3d::exp(update_so3)*SO3_R;

// 两个SO(3)可以直接相乘
Sophus::SO3d SO3_mult = SO3_R * SO3_q;
// 也可以用对数映射
Eigen::Vector3d so3_mult = SO3_R.log() + SO3_q.log();
// 对数映射的反操作
Sophus::SO3d SO3_mult2 = Sophus::SO3d::exp(so3_mult);

SE(3)的使用

SE(3)的定义

// 从旋转矩阵和平移向量构造
Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d(0,0,1)).toRotationMatrix();
Eigen::Vector3d t(1,0,0);
Sophus::SE3d SE3_Rt(R, t);
// 从四元数构造
Eigen::Quaterniond q(R);
Sophus::SE3d SE3_qt(q, t);

SE(3)的运算

// 李代数se(3)是一个六维向量,方便起见先typedef一下
typedef Eigen::Matrix<double, 6, 1> Vector6d;
// SE(3)是一个4*4的矩阵,方便起见先typedef一下
typedef Eigen::Matrix<double, 4, 4> Matrix4d;
// 用对数映射获得它的李代数
Vector6d se3 = SE3_Rt.log();
// hat为向量到反对称矩阵
Matrix4d se3_hat = Sophus::SE3d::hat(se3);
// 相对的,vee为反对称到向量
Vector6d se3_vee = Sophus::SE3d::vee(se3_hat);
// 更新
Vector6d update_se3; update_se3.setZero(); // 平移在前,旋转在后
update_se3(0, 0) = 1e-4;
Sophus::SE3d SE3_updated = Sophus::SE3d::exp(update_se3)*SE3_Rt;

SE(3)、SO(3)与Eigen类型的相互转换

Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d(0,0,1)).toRotationMatrix();
Sophus::SO3d SO3_R(R);
Eigen::Vector3d t(1,0,0);
Sophus::SE3d SE3_Rt(R, t);
// 从SO(3)获得单位四元数
Eigen::Quaterniond q0 = SO3_R.unit_quaternion();
// 从SO(3)获得旋转矩阵
Eigen::Matrix3d R0 = SO3_R.matrix();
// 从SE(3)获得旋转矩阵
Eigen::Matrix3d R1 = SE3_Rt.matrix().block<3,3>(0,0);
// 从SE(3)获得单位四元数
Eigen::Quaterniond q1 = SE3_Rt.unit_quaternion();
// 从SE(3)获得平移向量
Eigen::Vector3d t1 = SE3_Rt.translation();

坐标变换

Eigen::Vector3d p1(1, 0, 0); // 令p1=(1,0,0)
Eigen::Vector3d p2 = SE3_Rt * p1; // 相当于R*p1+t

优化问题

空间中存在一组三维点云,已知在世界坐标系下的坐标 P w P_w Pw,以及这些点云在相机坐标系下的坐标 P c P_c Pc,求解相机相对于世界坐标系的位姿 T c w T_c^w Tcw

残差定义:

r i = R p i + t − q i \begin{aligned} r_{i} &=R p_{i}+t-q_{i} \end{aligned} ri=Rpi+tqi

目标函数如下:

min ⁡ R , t ∑ i = 1 N ∥ R p i + t − q i ∥ 2 2 \begin{aligned} \min _{R, t} \sum_{i=1}^{N}\left\|R p_{i}+t-q_{i}\right\|_{2}^{2} \end{aligned} R,tmini=1NRpi+tqi22

使用右扰动模型计算得到残差的雅克比矩阵如下:

∂ r ∂ δ θ = − R p ^ ∧ ∂ r ∂ δ p = I 3 × 3 \begin{aligned} \frac{\partial r}{\partial \delta \theta} &=-R \hat{p}^{\wedge} \\ \\ \frac{\partial r}{\partial \delta p} &=I_{3 \times 3} \\ \end{aligned} δθrδpr=Rp^=I3×3

使用左扰动模型计算得到残差的雅克比矩阵如下:

∂ r ∂ δ θ = − ( R p ^ ) ∧ ∂ r ∂ δ p = I 3 × 3 \begin{aligned} \frac{\partial r}{\partial \delta \theta} &=-(R\hat{p})^{\wedge} \\ \\ \frac{\partial r}{\partial \delta p} &=I_{3 \times 3} \\ \end{aligned} δθrδpr=(Rp^)=I3×3

使用Guass-Newton迭代求解即可,程序如下:

#include <iostream>

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>

#include "sophus/se3.hpp"
#include "sophus/so3.hpp"

int main(void){
    // 优化变量为李代数se(3)的平移向量
    typedef Eigen::Matrix<double, 6, 1> Vector6d;
    // 数据点
    std::vector<Eigen::Vector3d> pts1, pts2;
    // 随机数生成器
    std::default_random_engine generator;
    std::normal_distribution<double> noise(0., 1.);
    // 构造相机位姿,作为参考位姿
    Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d(0,0,1)).toRotationMatrix();
    Eigen::Vector3d t(1,0,0);
    Sophus::SE3d SE3_Rt(R, t);
    // 观测数据
    for (int i = 0; i < 100; ++i) {
        double x = noise(generator), y = noise(generator), z = noise(generator);
        pts1.push_back(Eigen::Vector3d(x, y, z)); // 相机坐标系下的点
        pts2.push_back(SE3_Rt * pts1[i]); // 世界坐标系下的点
    }
    // 开始Gauss-Newton迭代,初始位姿为参考位姿+扰动
    Sophus::SE3d SE3_estimate(R*Eigen::AngleAxisd(0.1, Eigen::Vector3d::UnitZ()).toRotationMatrix(), 
                            t+Eigen::Vector3d(0.1, 0.0, 0.0));
    enum {
        DLEFT = 0, // 左扰动
        DRIGHT = 1, // 右扰动
    };
    int disturb = DRIGHT;
    for (int iter = 0; iter < 100; ++iter) {
        Eigen::Matrix<double, 6, 6> H = Eigen::Matrix<double, 6, 6>::Zero();
        Eigen::Matrix<double, 6, 1> b = Eigen::Matrix<double, 6, 1>::Zero();
        double cost = 0;
        for (int i = 0; i < pts1.size(); ++i) {
            // compute cost for pts1[i] and pts2[i]
            Eigen::Vector3d p = pts1[i], pc = pts2[i]; // p为相机坐标系下的点,pc为世界坐标系下的点
            Eigen::Vector3d pc_est = SE3_estimate * p; // 估计的世界坐标系下的点
            Eigen::Vector3d e = pc_est - pc; // 残差
            cost += e.squaredNorm() / 2;
            // compute jacobian
            Eigen::Matrix<double, 3, 6> J = Eigen::Matrix<double, 3, 6>::Zero();
            if(disturb == DRIGHT){
                // 右扰动
                J.block<3,3>(0,0) = Eigen::Matrix3d::Identity();
                J.block<3,3>(0,3) = -SE3_estimate.rotationMatrix() * Sophus::SO3d::hat(p);
            } else{
                // 左扰动
                J.block<3,3>(0,0) = Eigen::Matrix3d::Identity();
                J.block<3,3>(0,3) = -Sophus::SO3d::hat(SE3_estimate.rotationMatrix() * p);
            }
            // compute H and b
            H += J.transpose() * J;
            b += -J.transpose() * e;
        }
        // solve dx 
        Vector6d dx = H.ldlt().solve(b);
        if (dx.norm() < 1e-6) {
            break;
        }
        // update estimation
        if(disturb == DRIGHT){
            // 右乘更新
            SE3_estimate.so3() = SE3_estimate.so3() * Sophus::SO3d::exp(dx.block<3,1>(3,0));
            SE3_estimate.translation() += dx.block<3,1>(0,0);
        } else{
            // 左乘更新
            SE3_estimate.so3() = Sophus::SO3d::exp(dx.block<3,1>(3,0)) * SE3_estimate.so3();
            SE3_estimate.translation() += dx.block<3,1>(0,0);
        }

        std::cout << "iteration " << iter << " cost=" << cost << std::endl;
    }
    std::cout << "estimated pose: \n" << SE3_estimate.matrix() << std::endl;
    std::cout << "ground truth pose: \n" << SE3_Rt.matrix() << std::endl;
}

bug记录

右乘se3的exp映射时,结果有问题,而左乘没问题

初步定位到问题,在求导时,不是对SE3求导,而是对平移向量和旋转向量分别求导,然后再组合起来,这和SE3求导结果不同.

暂时不管了,SE3右乘雅可比有点复杂,高翔书中也是分开求导和更新的,就这样吧。

// se3右乘更新, 有问题
SE3_estimate = SE3_estimate * Sophus::SE3d::exp(dx); 
// 分开更新,没问题
SE3_estimate.so3() = SE3_estimate.so3() * Sophus::SO3d::exp(dx.block<3,1>(3,0));
SE3_estimate.translation() += dx.block<3,1>(0,0);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Sophus使用记录 的相关文章

  • vscode_c++_slambook 编译配置

    工作目录 配置文件 launch json version 0 2 0 configurations name slamBook程序调试 type cppdbg request launch program fileDirname buil
  • ROS STAGE教程2(地图定义和GMAPPING建图)

    目前用在ROS Kinetic上的stage版本为4 1 官方教程http rtv github io Stage modules html 用户可以用stage或者gazebo来创建地图和机器人 传感器模型来进行仿真 并与自己的SLAM模
  • LIO-SAM:在高斯牛顿法求解过程中用SO3代替欧拉角

    LIO SAM发表于IROS2020 是一个效果非常好的惯性 激光紧耦合里程计 我打算给我们的机器人搞一个激光里程计 于是打算把LIO SAM改一改搞过来 修改过程中发现一个问题 在里程计求解 mapOptimization的LMOptim
  • 基于深度相机的三维重建技术

    本文转载自http www bugevr com zblog id 14 原创作者bugeadmin 转载至我的博客 主要是为了备份 日后查找方便 谢谢原创作者的分享 三维重建 3D Reconstruction 技术一直是计算机图形学和计
  • 各向异性(anisotropic)浅提

    文章目录 各向异性 anisotropic 定义 哪种物体具有各向异性反射 什么导致各向异性反射 总结 各向异性 anisotropic 定义 它指一种存在方向依赖性 这意味着在不同的方向不同的特性 相对于该属性各向同性 当沿不同轴测量时
  • rtabmap安装与使用

    参考 ubuntu18 04安装Rtabmap 具体详细步骤 教你手把手运行基于ZED的rtab map ZED入门 利用RTAB MAP做SLAM ubuntu16 04 ROS Kinetic rtabmap 源码 非ros版本 安装运
  • 视觉SLAM实践入门——(20)视觉里程计之直接法

    多层直接法的过程 1 读图 随机取点并计算深度 2 创建图像金字塔 相机内参也需要缩放 并计算对应点的像素坐标 3 应用单层直接法 使用G N L M等方法 或者使用g2o ceres库 进行优化 源码中有一些地方会引起段错误 修改方法见下
  • 经典坐标变换案例代码剖析

    题目 设有小萝卜一号和小萝卜二号位于世界坐标系中 记世界坐标系为W 小萝卜们的坐标系为R1和 R2 小萝卜一号的位姿为q2 0 35 0 2 0 3 0 1 T t1 0 3 0 1 0 1 T 小萝卜二号的位姿为q2 0 5 0 4 0
  • 单目视觉里程记代码

    在Github上发现了一个简单的单目vo 有接近500星 链接如下 https github com avisingh599 mono vo 这个单目里程计主要依靠opencv实现 提取fast角点并进行光流跟踪 然后求取本质矩阵并恢复两帧
  • LeGO-LOAM论文翻译(内容精简)

    LeGO LOAM是一种在LOAM之上进行改进的激光雷达建图方法 建图效果比LOAM要好 但是建图较为稀疏 计算量也更小了 本文原地址 wykxwyc的博客 github注释后LeGO LOAM源码 LeGO LOAM NOTED 关于代码
  • 关于GPS、惯导、视觉里程计的几个定义

    1 首先写几个定义 惯性导航系统 Inertial Navigation System INS 全球定位卫星系统 Global Navigation Satellite System GNSS GNSS 包括全球定位系统 Global Po
  • 视觉SLAM技术及其应用(章国锋--复杂环境下的鲁棒SfM与SLAM)

    SLAM 同时定位与地图构建 机器人和计算机视觉领域的基本问题 在未知环境中定位自身方位并同时构建环境三维地图 应用广泛 增强现实 虚拟现实 机器人 无人驾驶 SLAM常用的传感器 红外传感器 较近距离感应 常用与扫地机器人 激光雷达 单线
  • IMU预积分的一些理解

    IMU预积分 算是比较简单的一个算法 无奈网上找到的资料都讲的晦涩难懂 看明白了也觉得不过如此 讲一下我的理解 整体流程 1 推导IMU离散运动方程 2 根据离散运动方程 进行预积分 并将预积分的误差项拆分出来 因为我们在定义误差的时候 有
  • PnP 问题

    欢迎访问我的博客首页 PnP 问题 1 DLT 2 P3P 3 G2O 求解 PnP 3 1 单目 3 2 双目 4 自定义顶点与边优化内参 4 1 二元边 4 2 三元边 4 3 总结 5 参考 PnP Perspective n Poi
  • Eigen::aligned_allocator

    如果STL容器中的元素是Eigen库数据结构 例如这里定义一个vector容器 元素是Matrix4d 如下所示 vector
  • ORB-SLAM2:基于可识别特征的自主导航与地图构建

    目录 ORB SLAM2 基于可识别特征的自主导航与地图构建 简介 地图 A 地图特征点或3D ORB B 关键帧 C 可视化图像 位置识别 A 图像识别数据库 B 高效优化的ORB匹配 C 视觉一致性 自主导航追踪 A ORB特征获取 B
  • LeGO-LOAM代码详细注释版

    学习LeGO LOAM时 写的代码注释github代码链接 一部分注释来自github用户wykxwyc 一部分来自网上查阅 还有一部分是自己的理解 持续更新中
  • Ceres Solver从零开始手把手教学使用

    目录 一 简介 二 安装 三 介绍 四 Hello Word 五 导数 1 数值导数 2解析求导 六 实践 Powell函数 一 简介 笔者已经半年没有更新新的内容了 最近学习视觉SLAM的过程中发现自己之前学习的库基础不够扎实 Ceres
  • 简洁直观的飞行器数学模型推导

    运动学方程 动力学方程 值得注意的是 对于非定轴和定轴转动 h r
  • Todesk突然高速通道使用已结束

    今天使用Todesk直接报出如下错误 好像对于海外用户需要付费购买海外会员 大家有没有什么可以替换的远程控制软件的吗 能分享一下吗

随机推荐

  • 嵌入式入门基础知识有哪些?

    嵌入式系统是指在特定应用领域内为满足特定要求而设计的计算机系统 通常被嵌入到设备中 具有实时性 可靠性 低功耗等特点 嵌入式系统应用广泛 例如 智能家居 智能手表 汽车控制系统 医疗设备等 在本篇博客中 我们将讨论嵌入式入门基础知识 包括嵌
  • 狂神说Mybatis笔记(全网最全)

    Mybatis 环境说明 jdk 8 MySQL 5 7 19 maven 3 6 0 IDEA 学习前需要掌握 JDBC MySQL Java 基础 Maven Junit 1 Mybatis简介 1 1 什么是MyBatis MyBat
  • 认识区块链,认知区块链— —数据上链

    上周末参加一次长沙本地胡子互联网俱乐部举办的区块链分享会 颇受启发 同时感谢俱乐部提供的这个交流平台 祝好 好吧 还是先把前些天对区块链的一点理解简单整理下 再回顾下上周末的参会纪要比较好 下篇给大家分享出来 个人区块链思考第一篇 认识区块
  • Yolov3计算准确率、误报率、漏检率等

    思想很简单 将标注的yolo数据转下格式 转为 类别 xmin ymin xmax ymax 转换valid后的信息 两个信息进行对比 完事 具体的 在终端执行 darknet detector valid cfg voc data cfg
  • 【SSM框架】之Spring

    SSM框架笔记 自用 Spring Spring Framework系统架构 Spring程序开发步骤 核心概念 IoC Inversion of Control 控制反转 使用对象时 由主动new产生对象转换为由外部提供对象 此过程中对象
  • 计算机毕业设计看这篇就够了(二)毕设流程

    本篇将为大家介绍计算机专业毕业设计流程 提前了解毕设流程可以让同学们从宏观角度去看毕设要做些什么样的事情 大概知道每个阶段要去做哪些工作 为后续毕设任务的真正开展打下心理预期 也不至于一脸懵 计算机毕设分为以下主流程 选题 确定导师 完成前
  • 【Proteus仿真】【STM32单片机】基于stm32的智能书桌设计

    文章目录 一 功能简介 二 软件设计 三 实验现象 联系作者 一 功能简介 系统运行后 默认为手动模式 当检测有人 可通过K2键开关灯 如果姿势不对 警示灯亮 否则灭 可通过K3和K4键调节桌子高度 按下K1键切换为自动模式 此时有人 且光
  • Sentinel原理与Demo

    Sentinel 是什么 随着微服务的流行 服务和服务之间的稳定性变得越来越重要 Sentinel 以流量为切入点 从流量控制 熔断降级 系统负载保护等多个维度保护服务的稳定性 Sentinel 具有以下特征 丰富的应用场景 Sentine
  • 【FreeRTOS(三)】任务状态

    文章目录 任务状态 任务挂起 vTaskSuspend 取消任务挂起 vTaskResume 挂起任务调度器 vTaskSuspendAll 取消挂起任务调度器 xTaskResumeAll 代码示例 任务挂起 取消任务挂起 代码示例 挂起
  • Docker help帮助文档

    1 查看 docker help 帮助 docker help 2 用法 docker 选项 命令 3 选项 客户端配置文件的配置字符串位置 默认为 root docker D 启用调试模式 H 要连接的主机列表守护进程套接字 l 设置日志
  • Centos7.3安装和配置Mysql5.7

    第一步 获取mysql YUM源 进入mysql官网获取RPM包下载地址 https dev mysql com downloads repo yum 点击 下载 右击 复制链接地址 https dev mysql com get mysq
  • 源码剖析transformer、self-attention

    原文链接 首先给大家引入一个github博客 这份代码是我在看了4份transformer的源码后选出来的 这位作者的写法非常易懂 代码质量比较高 GitHub Separius BERT keras Keras implementatio
  • 一步一步教你用idea上交代码到gitee(图文解释)

    一步一步教你用idea上交代码到gitee 图文解释 文章目录 一步一步教你用idea上交代码到gitee 图文解释 工具准备 具体操作 结语 工具准备 首先 我们进行代码的提交需要两个工具包 在我的上一篇中有讲 大家可以自行去提取 2条消
  • .net 批量注册服务

    假设我们需要注册xxxQuery服务 例如下图中的BarQuery和FooQuery 传统的做法是 services TryAddScoped
  • vscode 运行和调试 javascript 代码

    安装node 安装vscode 扩展包 code runer 配置vs code下有关F5的操作的文件 参考地址
  • 【Zabbix实战之运维篇】Zabbix监控Docker容器配置方法

    Zabbix实战之运维篇 Zabbix监控Docker容器配置方法 一 检查Zabbix监控平台状态 1 检查Zabbix各组件容器状态 2 奸诈Zabbix server状态 二 下载监控模板 1 进入Zabbix官网下载页面 2 查看下
  • 微信小程序中识别html标签的方法

    rich text组件 在微信小程序中有一个组件rich text可以识别文本节点或是元素节点 具体入下 需要识别的数据放在data中 然后放在nodes属性中即可
  • 编写程序:5类员工有对应封装类,创建Employee数组,若干不同的Employee对象,并实现增删改查功能(《黑马程序员》P144编程题加强版)

    文章目录 Employee类 SalariedEmployee类 HourlyEmployee类 SalesEmployee类 BasePlusSalesEmployee类 Test类 实现增删改查 原题 1 Employee 这是所有员工
  • 【python】深入了解Selenium-PageObject

    1 PageObject 定义 Page Object 简称PO 模式 是Selenium实战中最为流行 并且是自动化测试中最为熟悉和推崇的一种设计模式 在设计自动化测试时 把页面元素和元素的操作方法按照页面抽象出来 分离成一定的对象 然后
  • Sophus使用记录

    sophus库是一个基于Eigen的C 李群李代数库 可以用来方便地进行李群李代数的运算 头文件 主要用到以下两个头文件 include