1.两个相机之间的非线性优化

观测相机方程关于相机位姿与特征点的雅可比矩阵:

1.1 位姿:



1.2 3D特征点

  • fx,fy,fz为相机内参
  • X',Y',Z'为3D点在相机坐标系下的坐标
  • 该误差是观测值减去预测值,反过来,预测值减观测值时,去掉或加上负号即可
  • 姿态定义为先平移后旋转,如果定义为先旋转后平移,将该矩阵的前3列与后3列对调即可

2.vio滑动窗口的BA优化

1.相机:

相机误差仍然为重投影误差:

优化是在机体坐标系下完成,也就是imu系,所以多了一个相机到机体坐标的外参



根据链式法则,可以分两步走,第一步,误差对\(f_{cj}\)求导,最后再分别相乘即可

2.1 误差对\(f_{cj}\)求导:

2.2 \(f_{cj}\)对逆深度的求导:

2.3 \(f_{cj}\)对各时刻状态量的求导:

  • 对i时刻的位移求导:



    对i时刻的角度增量求导:

  • 对j时刻的位移求导;



    对j时刻的角度增量求导

2.4 \(f_{cj}\)对imu和相机的外参求导:

  • 对位移求导:

  • 对角度增量求导:

    分为两部分求导: \(f_{cj} = f_{cj}^{1} + f_{cj}^{2}\)

    第一部分:



    第二部分:



    最后相加即可。

注意:最后别忘了分别乘上误差对\(f_{cj}\)的求导

2.5 程序示例:

double inv_dep_i = verticies_[0]->Parameters()[0];

    VecX param_i = verticies_[1]->Parameters();  //i时刻位姿
Qd Qi(param_i[6], param_i[3], param_i[4], param_i[5]); //姿态
Vec3 Pi = param_i.head<3>(); //位移 VecX param_j = verticies_[2]->Parameters(); //j时刻位姿
Qd Qj(param_j[6], param_j[3], param_j[4], param_j[5]);
Vec3 Pj = param_j.head<3>(); VecX param_ext = verticies_[3]->Parameters();
Qd qic(param_ext[6], param_ext[3], param_ext[4], param_ext[5]);
Vec3 tic = param_ext.head<3>() Vec3 pts_camera_i = pts_i_ / inv_dep_i;
Vec3 pts_imu_i = qic * pts_camera_i + tic;
Vec3 pts_w = Qi * pts_imu_i + Pi;
Vec3 pts_imu_j = Qj.inverse() * (pts_w - Pj);
Vec3 pts_camera_j = qic.inverse() * (pts_imu_j - tic); double dep_j = pts_camera_j.z(); Mat33 Ri = Qi.toRotationMatrix();
Mat33 Rj = Qj.toRotationMatrix();
Mat33 ric = qic.toRotationMatrix();
Mat23 reduce(2, 3); //误差对f_cj求导
reduce << 1. / dep_j, 0, -pts_camera_j(0) / (dep_j * dep_j),
0, 1. / dep_j, -pts_camera_j(1) / (dep_j * dep_j);
// reduce = information_ * reduce; Eigen::Matrix<double, 2, 6> jacobian_pose_i;
Eigen::Matrix<double, 3, 6> jaco_i;
jaco_i.leftCols<3>() = ric.transpose() * Rj.transpose(); //位移求导
jaco_i.rightCols<3>() = ric.transpose() * Rj.transpose() * Ri * -
Sophus::SO3d::hat(pts_imu_i); //角度增量求导
jacobian_pose_i.leftCols<6>() = reduce * jaco_i; Eigen::Matrix<double, 2, 6> jacobian_pose_j;
Eigen::Matrix<double, 3, 6> jaco_j;
jaco_j.leftCols<3>() = ric.transpose() * -Rj.transpose();
jaco_j.rightCols<3>() = ric.transpose() * Sophus::SO3d::hat(pts_imu_j);
jacobian_pose_j.leftCols<6>() = reduce * jaco_j; Eigen::Vector2d jacobian_feature;
//逆深度求导
jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i_ * -1.0 / (inv_dep_i * inv_dep_i); //IMU和相机外参求导
Eigen::Matrix<double, 2, 6> jacobian_ex_pose;
Eigen::Matrix<double, 3, 6> jaco_ex;
jaco_ex.leftCols<3>() = ric.transpose() * (Rj.transpose() * Ri - Eigen::Matrix3d::Identity());
Eigen::Matrix3d tmp_r = ric.transpose() * Rj.transpose() * Ri * ric;
jaco_ex.rightCols<3>() = -tmp_r * Utility::skewSymmetric(pts_camera_i) + Utility::skewSymmetric(tmp_r * pts_camera_i) + Utility::skewSymmetric(ric.transpose() * (Rj.transpose() * (Ri * tic + Pi - Pj) - tic)); jacobian_ex_pose.leftCols<6>() = reduce * jaco_ex; jacobians_[0] = jacobian_feature; //2行1列
jacobians_[1] = jacobian_pose_i;
jacobians_[2] = jacobian_pose_j;
jacobians_[3] = jacobian_ex_pose;

2.IMU:

IMU的真实值为 w,a, 测量值为\(w^{~}, a^{~}\),则有:



其中: b为bias随机游走误差,n为白噪声。

预积分:

预积分仅仅与imu测量值有关,将一段时间的imu数据直接积分起来就得到了与积分量

则j时刻的PVQ积分积分方程为:



其中p为位移,v为速度,q为姿态,b为bias噪声

2.1 IMU的与积分误差:



其中,位移,速度,bias噪声的误差都是直接相减,第二项是关于四元数的旋转误差,后缀xyz代表取四元数的虚部(x, y, z)组成的三维向量。

void EdgeImu::ComputeResidual() {
VecX param_0 = verticies_[0]->Parameters();
Qd qi(param_0[6], param_0[3], param_0[4], param_0[5]);
Vec3 pi = param_0.head<3>();
SO3d ri(qi);
SO3d ri_inv = ri.inverse(); VecX param_1 = verticies_[1]->Parameters();
Vec3 vi = param_1.head<3>();
Vec3 bai = param_1.segment(3, 3);
Vec3 bgi = param_1.tail<3>(); VecX param_2 = verticies_[2]->Parameters();
Qd qj(param_2[6], param_2[3], param_2[4], param_2[5]);
Vec3 pj = param_2.head<3>(); VecX param_3 = verticies_[3]->Parameters();
Vec3 vj = param_3.head<3>();
Vec3 baj = param_3.segment(3, 3);
Vec3 bgj = param_3.tail<3>();
SO3d rj(qj); double dt = pre_integration_->GetSumDt();
double dt2 = dt * dt;
SO3d dr;
Vec3 dv;
Vec3 dp;
pre_integration_->GetDeltaRVP(dr, dv, dp); //获取预积分值 SO3d res_r = dr.inverse() * ri_inv * rj;
residual_.block<3, 1>(0, 0) = SO3d::log(res_r);
residual_.block<3, 1>(3, 0) = ri_inv * (vj - vi - gravity_ * dt) - dv;
residual_.block<3, 1>(6, 0) = ri_inv * (pj - pi - vi * dt - 0.5 * gravity_ * dt2) - dp;
residual_.block<3, 1>(9, 0) = baj - bai;
residual_.block<3, 1>(12, 0) = bgj - bgi;
}
2.2 IMU的误差雅可比矩阵:

基于泰勒展开的误差传递(EKF):

非线性系统\(x_{k} = f(x_{k-1}, u_{k-1})\) 的状态误差的线性递推关系为:



其中,F是状态量\(x_{k}\)对状态量\(x_{k-1}\)的雅可比矩阵,G是状态量\(x_{k}对输入量\)u_{k-1}$的雅可比矩阵。

IMU的误差传递方程为:





其中的系数为:

  • 速度预积分对各状态量的雅可比,为F的第三行,分别是:位移预积分,旋转预积分,速度预积分,陀螺仪bias噪声,加速度bias噪声

    f33: 速度预积分量对上一时刻速度预积分量的雅可比,为I

    f32: 速度预积分量对角度预积分量的雅可比

    f35: 速度预积分量对k时刻角速度bias噪声的雅可比

    f22: 前一时刻的旋转误差如何影响当前旋转误差
2.3 IMU相对于优化变量的雅可比矩阵:

在求解非线性方程式,我们需要知道IMU误差对两个关键帧i,j的状态p,q,v,\(b^{a}, b^{g}\)的雅可比

  • 对i时刻的位移:
  • 对i时刻的旋转:
  • 对i时刻的速度:
  • 对i时刻的加速度bias:

    IMU角度误差相对于优化变量的雅可比
  • 角度误差对i时刻的姿态求导:



    其中[]L 和[]R 为四元数转为左/右旋转矩阵的算子
  • 角度误差对j时刻姿态求导

  • 角度误差对i时刻陀螺仪bias噪声求导

void EdgeImu::ComputeJacobians() {

    VecX param_0 = verticies_[0]->Parameters();
Qd Qi(param_0[6], param_0[3], param_0[4], param_0[5]);
Vec3 Pi = param_0.head<3>(); VecX param_1 = verticies_[1]->Parameters();
Vec3 Vi = param_1.head<3>();
Vec3 Bai = param_1.segment(3, 3);
Vec3 Bgi = param_1.tail<3>(); VecX param_2 = verticies_[2]->Parameters();
Qd Qj(param_2[6], param_2[3], param_2[4], param_2[5]);
Vec3 Pj = param_2.head<3>(); VecX param_3 = verticies_[3]->Parameters();
Vec3 Vj = param_3.head<3>();
Vec3 Baj = param_3.segment(3, 3);
Vec3 Bgj = param_3.tail<3>(); double sum_dt = pre_integration_->sum_dt;
Eigen::Matrix3d dp_dba = pre_integration_->jacobian.template block<3, 3>(O_P, O_BA);
Eigen::Matrix3d dp_dbg = pre_integration_->jacobian.template block<3, 3>(O_P, O_BG); Eigen::Matrix3d dq_dbg = pre_integration_->jacobian.template block<3, 3>(O_R, O_BG); Eigen::Matrix3d dv_dba = pre_integration_->jacobian.template block<3, 3>(O_V, O_BA);
Eigen::Matrix3d dv_dbg = pre_integration_->jacobian.template block<3, 3>(O_V, O_BG); if (pre_integration_->jacobian.maxCoeff() > 1e8 || pre_integration_->jacobian.minCoeff() < -1e8)
{
// ROS_WARN("numerical unstable in preintegration");
} // if (jacobians[0])
{
Eigen::Matrix<double, 15, 6, Eigen::RowMajor> jacobian_pose_i;
jacobian_pose_i.setZero(); jacobian_pose_i.block<3, 3>(O_P, O_P) = -Qi.inverse().toRotationMatrix();
jacobian_pose_i.block<3, 3>(O_P, O_R) = Utility::skewSymmetric(Qi.inverse() * (0.5 * G * sum_dt * sum_dt + Pj - Pi - Vi * sum_dt)); Eigen::Quaterniond corrected_delta_q = pre_integration_->delta_q * Utility::deltaQ(dq_dbg * (Bgi - pre_integration_->linearized_bg));
jacobian_pose_i.block<3, 3>(O_R, O_R) = -(Utility::Qleft(Qj.inverse() * Qi) * Utility::Qright(corrected_delta_q)).bottomRightCorner<3, 3>();
jacobian_pose_i.block<3, 3>(O_V, O_R) = Utility::skewSymmetric(Qi.inverse() * (G * sum_dt + Vj - Vi)); if (jacobian_pose_i.maxCoeff() > 1e8 || jacobian_pose_i.minCoeff() < -1e8)
{
// ROS_WARN("numerical unstable in preintegration");
}
jacobians_[0] = jacobian_pose_i;
}
// if (jacobians[1])
{
Eigen::Matrix<double, 15, 9, Eigen::RowMajor> jacobian_speedbias_i;
jacobian_speedbias_i.setZero();
jacobian_speedbias_i.block<3, 3>(O_P, O_V - O_V) = -Qi.inverse().toRotationMatrix() * sum_dt;
jacobian_speedbias_i.block<3, 3>(O_P, O_BA - O_V) = -dp_dba;
jacobian_speedbias_i.block<3, 3>(O_P, O_BG - O_V) = -dp_dbg; jacobian_speedbias_i.block<3, 3>(O_R, O_BG - O_V) = -Utility::Qleft(Qj.inverse() * Qi * pre_integration_->delta_q).bottomRightCorner<3, 3>() * dq_dbg; jacobian_speedbias_i.block<3, 3>(O_V, O_V - O_V) = -Qi.inverse().toRotationMatrix();
jacobian_speedbias_i.block<3, 3>(O_V, O_BA - O_V) = -dv_dba;
jacobian_speedbias_i.block<3, 3>(O_V, O_BG - O_V) = -dv_dbg; jacobian_speedbias_i.block<3, 3>(O_BA, O_BA - O_V) = -Eigen::Matrix3d::Identity(); jacobian_speedbias_i.block<3, 3>(O_BG, O_BG - O_V) = -Eigen::Matrix3d::Identity(); jacobians_[1] = jacobian_speedbias_i;
}
// if (jacobians[2])
{
Eigen::Matrix<double, 15, 6, Eigen::RowMajor> jacobian_pose_j;
jacobian_pose_j.setZero(); jacobian_pose_j.block<3, 3>(O_P, O_P) = Qi.inverse().toRotationMatrix();
Eigen::Quaterniond corrected_delta_q = pre_integration_->delta_q * Utility::deltaQ(dq_dbg * (Bgi - pre_integration_->linearized_bg));
jacobian_pose_j.block<3, 3>(O_R, O_R) = Utility::Qleft(corrected_delta_q.inverse() * Qi.inverse() * Qj).bottomRightCorner<3, 3>(); jacobians_[2] = jacobian_pose_j; }
// if (jacobians[3])
{
Eigen::Matrix<double, 15, 9, Eigen::RowMajor> jacobian_speedbias_j;
jacobian_speedbias_j.setZero(); jacobian_speedbias_j.block<3, 3>(O_V, O_V - O_V) = Qi.inverse().toRotationMatrix(); jacobian_speedbias_j.block<3, 3>(O_BA, O_BA - O_V) = Eigen::Matrix3d::Identity(); jacobian_speedbias_j.block<3, 3>(O_BG, O_BG - O_V) = Eigen::Matrix3d::Identity(); jacobians_[3] = jacobian_speedbias_j; } }

视觉SLAM:VIO的误差和误差雅可比矩阵的更多相关文章

  1. RGB-D相机视觉SLAM

    RGB-D相机视觉SLAM Dense Visual SLAM for RGB-D Cameras 开源代码地址:  vision.in.tum.de/data/software/dvo 摘要 本文提 ...

  2. 转:SLAM算法解析:抓住视觉SLAM难点,了解技术发展大趋势

    SLAM(Simultaneous Localization and Mapping)是业界公认视觉领域空间定位技术的前沿方向,中文译名为“同步定位与地图构建”,它主要用于解决机器人在未知环境运动时的 ...

  3. (转) SLAM系统的研究点介绍 与 Kinect视觉SLAM技术介绍

          首页 视界智尚 算法技术 每日技术 来打我呀 注册     SLAM系统的研究点介绍 本文主要谈谈SLAM中的各个研究点,为研究生们(应该是博客的多数读者吧)作一个提纲挈领的摘要.然后,我 ...

  4. 经典视觉SLAM框架

    经典视觉SLAM框架 整个视觉SLAM流程包括以下步骤: 1. 传感器信息读取.在视觉SLAM中主要为相机图像信息的读取和预处理. 2. 视觉里程计(Visual Odometry,VO).视觉里程计 ...

  5. 高翔《视觉SLAM十四讲》从理论到实践

    目录 第1讲 前言:本书讲什么:如何使用本书: 第2讲 初始SLAM:引子-小萝卜的例子:经典视觉SLAM框架:SLAM问题的数学表述:实践-编程基础: 第3讲 三维空间刚体运动 旋转矩阵:实践-Ei ...

  6. 高博-《视觉SLAM十四讲》

    0 讲座 (1)SLAM定义 对比雷达传感器和视觉传感器的优缺点(主要介绍视觉SLAM) 单目:不知道尺度信息 双目:知道尺度信息,但测量范围根据预定的基线相关 RGBD:知道深度信息,但是深度信息对 ...

  7. 视觉SLAM漫淡

    视觉SLAM漫谈 1.    前言 开始做SLAM(机器人同时定位与建图)研究已经近一年了.从一年级开始对这个方向产生兴趣,到现在为止,也算是对这个领域有了大致的了解.然而越了解,越觉得这个方向难度很 ...

  8. 视觉SLAM中的深度估计问题

    一.研究背景 视觉SLAM需要获取世界坐标系中点的深度. 世界坐标系到像素坐标系的转换为(深度即Z): 深度的获取一共分两种方式: a)主动式 RGB-D相机按照原理又分为结构光测距.ToF相机 To ...

  9. 视觉SLAM漫淡(二):图优化理论与g2o的使用

    视觉SLAM漫谈(二):图优化理论与g2o的使用 1    前言以及回顾 各位朋友,自从上一篇<视觉SLAM漫谈>写成以来已经有一段时间了.我收到几位热心读者的邮件.有的希望我介绍一下当前 ...

  10. 第六篇 视觉slam中的优化问题梳理及雅克比推导

    优化问题定义以及求解 通用定义 解决问题的开始一定是定义清楚问题.这里引用g2o的定义. \[ \begin{aligned} \mathbf{F}(\mathbf{x})&=\sum_{k\ ...

随机推荐

  1. Java-数组工具类Arrays

    java.util.Arrays是一个与数组相关的工具类,里面提供了大量静态方法,用来实现数组常见的操作. toSting方法 public static String toString(数组):将参 ...

  2. YeserCMS

    这道题直接让我们查网站根目录的flag,我首先想到的是一句话木马,但是奈何找不到上传的接口啊,只好作罢, 在下载发现有个cmseasy的标识,明显是要提示我们这里是easycms,百度easycms的 ...

  3. ubuntu 20.04 / 22.04 运行32位程序

    sudo dpkg --add-architecture i386 sudo apt install libc6:i386 libstdc++6:i386 sudo apt-get update su ...

  4. chrome设置socket5代理

    利用自带的参数命令打破一个死循环. chrome可执行文件 --show-app-list --proxy-server="SOCKS5://127.0.0.1:1080"

  5. 【每日一题】【回溯backtrace】N皇后

    n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案. 每一种解法包含一个不同的 n 皇后问 ...

  6. org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents

    异常:org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to b ...

  7. 多进程TCP服务端并发- 进程join方法 - IPC机制

    目录 同步与异步 阻塞与非阻塞 综合使用 创建进程的多种方式 进程间数据隔离 进程join方法 IPC机制 生产者消费者模型 进程对象的多种方法 僵尸进程与孤儿进程 守护进程 多线程实现TCP服务端并 ...

  8. CCS选择器 选择器优先级 选择器常见属性

    目录 CSS前戏 1.css语法结构 2.css注释语法 3.引入css的多种方式 CSS基本选择器 1.标签选择器 2.类选择器 3.id选择器 4.通用选择器 CSS组合选择器 1.后代选择器(空 ...

  9. Django中ORM多对多三种创建方式(全自动-纯手动-半自动)

    一:多对多三种创建方式 1.全自动: 利用orm自动帮我们创建第三张关系表 class Book(models.Model): name = models.CharField(max_length=3 ...

  10. Surp Suite入门

    BurpSuite代理工具是以拦截代理的方式,拦截所有通过代理的网络流量,如客户端的请求数据.服务器端的返回信息等.Burp Suite主要拦截HTTP和HTTPS 协议的流量,通过拦截,Burp S ...