lio-sam中雅克比推导

2023-05-16

lio-sam做的是scan-map,点变到世界系下,优化本帧在是世界系下的位姿,和loam有所不同。
符号:
本帧特征点云(相对机体系) P s c a n l i d a r P_{scan}^{lidar} Pscanlidar , 本帧点云变换至世界系 P s c a n P_{scan} Pscan,局部地图中匹配的点云 P m a p P_{map} Pmap(世界系),位姿 X = { R , T } X=\{R,T\} X={R,T},点到对应线、面的距离残差 f ( X ) f(X) f(X),围绕机体 x x x轴旋转的角度为 r x rx rx s i n ( r x ) 、 c o s ( r x ) sin(rx)、cos(rx) sin(rx)cos(rx)记作 s x 、 c x s_x、c_x sxcx,其他轴同理。

坐标变换关系:
在这里插入图片描述
待优化的误差函数:
其实没有 P m a p P_{map} Pmap这个点,误差函数是点到线、面的距离,这里认为 P m a p P_{map} Pmap是线、面上垂直 P s c a n P_{scan} Pscan的点(后面和 P m a p P_{map} Pmap没有关系)。
(打错了,是 f = d i s t ( P s c a n , P m a p ) f=dist(P_{scan},P_{map}) f=dist(Pscan,Pmap)
在这里插入图片描述
误差函数的雅可比 J J J
在这里插入图片描述
1、分步求导第一项:
在这里插入图片描述
即为特征点到对应线面的距离的单位向量 [ l a , l b , l c ] [la,lb,lc] [la,lb,lc](行向量),在求误差 f f f时,便已经得到,存放在coeff.xyz中。
2、分步求导第二项:
(应该是 [ r x , r y , r z , x , y , z ] [rx,ry,rz,x,y,z] [rx,ry,rz,x,y,z],下面写法括号里的 T T T不该带转置的)
在这里插入图片描述

R R R是机体在世界系下的位姿,先围绕 z z z轴旋转的角度为 r z rz rz,后围绕 x x x轴旋转的角度为 r x rx rx,最后围绕 y y y轴旋转的角度为 r y ry ry R = R y R x R z R=RyRxRz R=RyRxRz, 表示为(推导过程可以见附录中wiki截图):
在这里插入图片描述
2.1 我们先只看对 r x rx rx的求导部分:
在这里插入图片描述
其中, P s c a n P_{scan} Pscan即点的坐标,存储在pointOri.xyz,这一块的雅可比为:
在这里插入图片描述

对应代码中的:

float arx = (crx*sry*srz*pointOri.x + crx * crz*sry*pointOri.y - srx * sry*pointOri.z) * coeff.x
    + (-srx * srz*pointOri.x - crz * srx*pointOri.y - crx * pointOri.z) * coeff.y
    + (crx*cry*srz*pointOri.x + crx * cry*crz*pointOri.y - cry * srx*pointOri.z) * coeff.z;

2.2 r y , r z ry,rz ry,rz略过,对 T T T的求导部分为:
在这里插入图片描述
这一块的雅可比为:
在这里插入图片描述
对应代码中的:

matA(i, 3) = coeff.x;
matA(i, 4) = coeff.y;
matA(i, 5) = coeff.z;

3、最后把3个旋转,和T(x,y,z)的雅可比拼凑起来,就得到该特征点对应到1X6的雅可比矩阵:
在这里插入图片描述
附录:
参考1 2 3;
和参考123有些差异的地方,其中的 R R R如下,为啥差着负号,还没搞懂,没看过loam源码,估计是优化的R和计算误差时用的R是逆的关系,优化用 R t − 1 t R_{t-1}^t Rt1t,误差函数是当前帧点变到上一帧 R t t − 1 R_{t}^{t-1} Rtt1,但最后优化结果直接加在 R t − 1 w o r l d R_{t-1}^{world} Rt1world上了(也就是 R t t − 1 R_{t}^{t-1} Rtt1)?不知道不知道…
在这里插入图片描述

这里附上wiki中的公式:
在这里插入图片描述
在这里插入图片描述
标量对列向量求导(参考1中有误):
在这里插入图片描述

代码:

// 求roll、pitch、yaw对应的sin和cos
float srx = _transformTobeMapped.rot_x.sin();
float crx = _transformTobeMapped.rot_x.cos();
float sry = _transformTobeMapped.rot_y.sin();
float cry = _transformTobeMapped.rot_y.cos();
float srz = _transformTobeMapped.rot_z.sin();
float crz = _transformTobeMapped.rot_z.cos();

Eigen::Matrix<float, Eigen::Dynamic, 6> matA(laserCloudSelNum, 6);	// J
Eigen::Matrix<float, 6, Eigen::Dynamic> matAt(6, laserCloudSelNum);	// JT
Eigen::Matrix<float, 6, 6> matAtA;									// JT*J
Eigen::VectorXf matB(laserCloudSelNum);								// f
Eigen::VectorXf matAtB;												// JT*f
Eigen::VectorXf matX;													// delta

// 对每个点依次构建雅克比
for (int i = 0; i < laserCloudSelNum; i++)
{
    // scan中每个特征点的坐标(当前雷达系下)
    pointOri = _laserCloudOri.points[i];
    // scan中特征点到map中对应点的距离的方向单位向量(即误差在xyz方向上的单位分量)
    coeff = _coeffSel.points[i];
    // 雅克比中对roll求导部分
    float arx = (crx*sry*srz*pointOri.x + crx * crz*sry*pointOri.y - srx * sry*pointOri.z) * coeff.x
    + (-srx * srz*pointOri.x - crz * srx*pointOri.y - crx * pointOri.z) * coeff.y
    + (crx*cry*srz*pointOri.x + crx * cry*crz*pointOri.y - cry * srx*pointOri.z) * coeff.z;
    // 雅克比中对pitch求导部分
    float ary = ((cry*srx*srz - crz * sry)*pointOri.x
                + (sry*srz + cry * crz*srx)*pointOri.y + crx * cry*pointOri.z) * coeff.x
    + ((-cry * crz - srx * sry*srz)*pointOri.x
        + (cry*srz - crz * srx*sry)*pointOri.y - crx * sry*pointOri.z) * coeff.z;
    // 雅克比中对yaw求导部分
    float arz = ((crz*srx*sry - cry * srz)*pointOri.x + (-cry * crz - srx * sry*srz)*pointOri.y)*coeff.x
    + (crx*crz*pointOri.x - crx * srz*pointOri.y) * coeff.y
    + ((sry*srz + cry * crz*srx)*pointOri.x + (crz*sry - cry * srx*srz)*pointOri.y)*coeff.z;

    // 雅克比中对roll,pitch,yaw求导部分值
    matA(i, 0) = arx;
    matA(i, 1) = ary;
    matA(i, 2) = arz;
    //雅克比中对x,y,z求导部分值
    matA(i, 3) = coeff.x;
    matA(i, 4) = coeff.y;
    matA(i, 5) = coeff.z;
    // 残差f
    matB(i, 0) = -coeff.intensity;
}

matAt = matA.transpose();
matAtA = matAt * matA;
matAtB = matAt * matB;

// LM求解:JT*J*x=-JT*f (solve: A * X = B, 求解X)
matX = matAtA.colPivHouseholderQr().solve(matAtB);

// 更新状态量: X = X + δX
_transformTobeMapped.rot_x += matX(0, 0);
_transformTobeMapped.rot_y += matX(1, 0);
_transformTobeMapped.rot_z += matX(2, 0);
_transformTobeMapped.pos.x() += matX(3, 0);
_transformTobeMapped.pos.y() += matX(4, 0);
_transformTobeMapped.pos.z() += matX(5, 0);

// 更新小于阈值,则判断收敛;或达到指定次数,则退出
float deltaR = sqrt(pow(rad2deg(matX(0, 0)), 2) +
                    pow(rad2deg(matX(1, 0)), 2) +
                    pow(rad2deg(matX(2, 0)), 2));
float deltaT = sqrt(pow(matX(3, 0) * 100, 2) +
                    pow(matX(4, 0) * 100, 2) +
                    pow(matX(5, 0) * 100, 2));

if (deltaR < _deltaRAbort && deltaT < _deltaTAbort)
    break;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

lio-sam中雅克比推导 的相关文章

随机推荐

  • 匿名飞控openmv寻色块解读

    作者 xff1a 不会写代码的菜鸟 时间 xff1a 2019 7 26 源码 xff1a 匿名TI板飞控源码 43 openmvH4 说明 xff1a 限于本人水平有限 xff0c 并不能写的很详细 xff0c 还望各位能够补充
  • 校验和的计算方法

    实验要求 编写一个计算机程序用来计算一个文件的16位效验和 最快速的方法是用一个32位的整数来存放这个和 记住要处理进位 xff08 例如 xff0c 超过16位的那些位 xff09 xff0c 把它们加到效验和中 要求 xff1a 1 x
  • MT7621路由器芯片/处理器参数介绍

    MT7621路由器芯片包括一个880 MHz MIPS 1004Kc CPU双核 xff0c 一个5端口10 100 1000交换机 PHY和一个RGMII 嵌入式高性能cpu可以很容易地处理高级应用程序 如路由 安全和VoIP等 MT76
  • 谈谈你对事件的传递链和响应链的理解

    一 xff1a 响应者链 UIResponser包括了各种Touch message 的处理 xff0c 比如开始 xff0c 移动 xff0c 停止等等 常见的 UIResponser 有 UIView及子类 xff0c UIViCont
  • CMake 引入第三方库

    CMake 引入第三方库 在 CMake 中 xff0c 如何引入第三方库是一个常见的问题 在本文中 xff0c 我们将介绍 CMake 中引入第三方库的不同方法 xff0c 以及它们的优缺点 1 使用 find package 命令 在
  • u-boot的启动模式(面试常考)

    交互模式 uboot启动之后 xff0c 在倒计时减到0之前按任意键 xff0c uboot会进入到交互模式 xff0c 此时可以输入各种uboot命令 和uboot进行交互 自启动模式 uboot启动之后 xff0c 在倒计时减到0之前不
  • vins-fusion代码理解

    代码通读了一遍做些总结 xff0c 肯定有很多理解错了的地方 xff0c 清晰起见详细程序都放到引用链接里 从rosNodeTest cpp开始 main函数 ros span class token operator span span
  • vins博客的一部分1

    文章目录 imu callbackimg callback imu callback 从话题中读入各个数据的t x y z g y r xff0c 存放到acc和gry中 span class token comment 从话题读入 spa
  • vins博客的一部分2

    sync process 对两个imgBuf里的图像进行双目时间匹配 xff08 通过判断双目图像时间之差 lt 3ms xff09 xff0c 扔掉匹配不到的老帧 span class token keyword double span
  • vins博客的一部分3

    FeatureTracker trackImage 包含了 xff1a 帧间光流法 区域mask 检测特征点 左右目光流法匹配 计算像素速度 画图 跟踪上一帧的特征点 如果已经有特征点 xff0c 就直接进行LK追踪 xff0c 新的特征点
  • vins博客的一部分4

    processMeasurements 取出数据 将 featureBuf中 xff0c 最早帧的feature取出 xff1a feature 61 featureBuf front 节点的接收IMU的消息再imu callback中被放
  • vins博客的一部分5

    目录 initFirstIMUPose xff08 xff09 processIMU propagate initFirstIMUPose xff08 xff09 得到IUM的Z与重力对齐的旋转矩阵 xff1a IMU开始很大可能不是水平放
  • vins博客的一部分6

    processImage 输入是本帧的特征点 id cam id xyz uv vxvy 包含了检测关键帧 估计外部参数 初始化 状态估计 划窗等等 检测关键帧 选择margin帧 addFeatureCheckParallax 检测和上一
  • vins博客的一部分7

    目录 initFramePoseByPnP frame count Ps Rs tic ric triangulate frame count Ps Rs tic ric initFramePoseByPnP frame count Ps
  • vins博客的一部分8

    目录 optimization slideWindow optimization 优化先验残差 重投影残差 预积分残差 xff08 即要拟合的目标是 xff0c 之前边缘化后的先验值 xff0c 前后帧之间的IMU的预积分值 xff0c 每
  • 以下为WindowsNT下32位 C++程序,请计算sizeof的值

    转帖地址 xff1a http hi baidu com hikeba blog item 68ad9f10a7dd8003213f2ecf html char str 61 34 hello 34 char p1 61 str int n
  • vins博客的一部分9

    目录 IMUFactor xff08 imu约束 xff09 ProjectionTwoFrameOneCamFactor xff08 视觉约束 xff09 marginalize 边缘化约束 IMUFactor xff08 imu约束 x
  • fiesta论文翻译和代码

    论文 体素信息结构 VIS Namemean符号position体素坐标posoccupancy占用概率occESDF到最近障碍物的欧几里得距离disClosest Obstacle Coordinate最近障碍物的体素坐标cocobser
  • ROS中四元数、欧拉角、旋转矩阵等格式转换

    未完 ROS接收到odometry格式消息 xff1a nav msgs span class token operator span Odometry pos msg 具有 xff1a pos msg span class token p
  • lio-sam中雅克比推导

    lio sam做的是scan map xff0c 点变到世界系下 xff0c 优化本帧在是世界系下的位姿 xff0c 和loam有所不同 符号 xff1a 本帧特征点云 xff08 相对机体系 xff09 P s c a