| 
 | 
 
在阅读LIO-SAM源码的时候,发现点线残差和点面残差和雅克比构建采用了LOAM的表示方法。由于采用了欧拉角构建旋转矩阵,使得雅克比推导和优化方程构建比较复杂,代码也比较难阅读。A-LOAM中使用四元数表示旋转,使用了ceres自动求导,省去了雅可比的推导过程,代码大大简化。LIO-SAM作为一项非常优秀的3D激光SLAM框架,在进行当前帧点云位姿优化的时候使用了Eigen库构造优化过程,后续加入GPS和回环因子又用了gtsam因子图优化,作为对比,gtsam的代码封装性极强,作者把两种优化方式融合到了一个框架中,达到了很好的效果,因此有必要对代码中对应的公式进行推导。 
scan2MapOptimization() 
点线面残差和雅克比构建主要的代码在LIO-SAM/src/mapOptmization.cpp的scan2MapOptimization()函数中,流程如下: 
// 进行角点和平面匹配并构建残差 
cornerOptimization(); 
surfOptimization(); 
 
// 将两中特征点匹配的残差组合 
combineOptimizationCoeffs(); 
 
// 对残差进行一次优化,如果收敛则提前结束 
if (LMOptimization(iterCount) == true) 
    break;       
点线残差构建 
点线残差的定义为点到线的距离,在该直线上取两个点 A=(x1,y1,z1), B=(x2,y2,z2),设当前点为 P=(x0,y0,z0),如下 
 
  
计算 △PAB的面积,除以AB即可,即 
 \mathrm{d}=\frac{|\mathbf{PA} \times \mathbf{PB}|}{|\mathbf{AB}|}  
\begin{aligned} S_{\triangle A B P} \propto|\overrightarrow{P A} \times \overrightarrow{P B}| \\ \overrightarrow{P A} \times \overrightarrow{P B}=&\left|\begin{array}{ccc} i & j & k \\ x_{1}-x_{0} & y_{1}-y_{0} & z_{1}-z_{0} \\ x_{2}-x_{0} & y_{2}-y_{0} & z_{2}-z_{0} \end{array}\right| \\ =& {\left[\begin{array}{c} \left(y_{1}-y_{0}\right)\left(z_{2}-z_{0}\right)-\left(z_{1}-z_{0}\right)\left(y_{2}-y_{0}\right) \\ \left(z_{1}-z_{0}\right)\left(x_{2}-x_{0}\right)-\left(x_{1}-x_{0}\right)\left(z_{2}-z_{0}\right) \\ \left(x_{1}-x_{0}\right)\left(y_{2}-y_{0}\right)-\left(y_{1}-y_{0}\right)\left(x_{2}-x_{0}\right) \end{array}\right] } \\ \overrightarrow{P A} \times \overrightarrow{P B} \mid &=(\overrightarrow{P A} \times \overrightarrow{P B})^{T}(\overrightarrow{P A} \times \overrightarrow{P B}) \\ &=\left(\left(y_{1}-y_{0}\right)\left(z_{2}-z_{0}\right)-\left(z_{1}-z_{0}\right)\left(y_{2}-y_{0}\right)\right)^{2} \\ &+\left(\left(z_{1}-z_{0}\right)\left(x_{2}-x_{0}\right)-\left(x_{1}-x_{0}\right)\left(z_{2}-z_{0}\right)\right)^{2} \\ &+\left(\left(x_{1}-x_{0}\right)\left(y_{2}-y_{0}\right)-\left(y_{1}-y_{0}\right)\left(x_{2}-x_{0}\right)\right)^{2} \end{aligned}  
对应代码中 
// 计算 PA x PB 的模长 
float a012 = sqrt(((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) * ((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1))      
            + ((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1)) * ((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1))     
            + ((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1)) * ((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1)));  
 
// 计算 AB 模长 
float l12 = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2) + (z1 - z2)*(z1 - z2)); 
 
// P 到 AB 的距离 
float ld2 = a012 / l12; 
点线雅可比计算 
分为两步,先对点求导,再对位姿求导 
J=\frac{\partial d}{\partial T_{w b}}=\frac{\partial d}{\partial \boldsymbol{p}^{w}} \frac{\partial \boldsymbol{p}^{w}}{\partial \boldsymbol{T}_{w b}}  
设 \boldsymbol{p}_{m}=\overrightarrow{P M}=\overrightarrow{P A} \times \overrightarrow{P B} ,残差为: 
\begin{aligned} d \propto \sqrt{{ }\left(\left(y_{1}-y_{0}\right)\left(z_{2}-z_{0}\right)-\left(z_{1}-z_{0}\right)\left(y_{2}-y_{0}\right)\right)^{2} \\ +\left(\left(z_{1}-z_{0}\right)\left(x_{2}-x_{0}\right)-\left(x_{1}-x_{0}\right)\left(z_{2}-z_{0}\right)\right)^{2} \\ +\left(\left(x_{1}-x_{0}\right)\left(y_{2}-y_{0}\right)-\left(y_{1}-y_{0}\right)\left(x_{2}-x_{0}\right)\right)^{2}} \\ = \sqrt{\boldsymbol{p}_{m}^{T} \boldsymbol{p}_{m}}  \\ = \sqrt{\left(p_{m x}^{2}+p_{m y}^{2}+p_{m z}^{2}\right)} \end{aligned}  
分别对(x0,y0,z0)求导 
\begin{aligned} \frac{\partial d}{\partial \boldsymbol{p}^{w}} &=\left[\begin{array}{c} \frac{\partial d}{\partial x_{0}} \\ \frac{\partial d}{\partial y_{0}} \\ \frac{\partial d}{\partial z_{0}} \end{array}\right]^{T} \\ & \propto \frac{1}{2}\left[\begin{array}{c} 2 p_{m y}\left(\left(z_{2}-z_{0}\right)-\left(z_{1}-z_{0}\right)\right)+2 p_{m z}\left(\left(y_{1}-y_{0}\right)-\left(y_{2}-y_{0}\right)\right) \\ 2 p_{m z}\left(\left(x_{2}-x_{0}\right)-\left(x_{1}-x_{0}\right)\right)+2 p_{m x}\left(\left(z_{1}-z_{0}\right)-\left(z_{2}-z_{0}\right)\right) \\ 2 p_{m x}\left(\left(y_{2}-y_{0}\right)-\left(y_{1}-y_{0}\right)\right)+2 p_{m y}\left(\left(x_{1}-x_{0}\right)-\left(x_{2}-x_{0}\right)\right) \end{array}\right]^{T} \\ &=\left[\begin{array}{l} p_{m y}\left(z_{2}-z_{1}\right)+p_{m z}\left(y_{1}-y_{2}\right) \\ p_{m z}\left(x_{2}-x_{1}\right)+p_{m x}\left(z_{1}-z_{2}\right) \\ p_{m x}\left(y_{2}-y_{1}\right)+p_{m y}\left(x_{1}-x_{2}\right) \end{array}\right]^{T} \end{aligned}  
对应代码中,代码中对向量|AB||PM|除是为了进行单位化 
// 计算残差对点 P 的雅可比且进行单位化 
float la = ((y1 - y2)*((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1))  
      + (z1 - z2)*((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1))) / a012 / l12; 
 
float lb = -((x1 - x2)*((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1))  
       - (z1 - z2)*((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1))) / a012 / l12; 
 
float lc = -((x1 - x2)*((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1))  
       + (y1 - y2)*((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1))) / a012 / l12; 
点面残差计算 
点到的平面的距离作为残差,对系数进行归一化处理,则计算方式为: 
d_{h}=\frac{\left|pa  \cdot x_{0}+ pb  \cdot y_{0}+ pc  \cdot z_{0}+pd\right|}{\sqrt{pa^{2}+pb^{2}+pc^{2}}}=\left|pa  \cdot x_{0}+pb  \cdot y_{0}+ pc  \cdot z_{0}+pd\right|  
对应代码为: 
// 构建超定方程 
for (int j = 0; j < 5; j++) { 
    matA0(j, 0) = laserCloudSurfFromMapDS->points[pointSearchInd[j]].x; 
    matA0(j, 1) = laserCloudSurfFromMapDS->points[pointSearchInd[j]].y; 
    matA0(j, 2) = laserCloudSurfFromMapDS->points[pointSearchInd[j]].z; 
} 
 
// 求解平面法向量,并单位化,单位化后的平面方程为 Ax + By + Cz + D = 0, A^2 + B^2 + C^2 = 1 
matX0 = matA0.colPivHouseholderQr().solve(matB0); 
 
float pa = matX0(0, 0); 
float pb = matX0(1, 0); 
float pc = matX0(2, 0); 
float pd = 1; 
 
float ps = sqrt(pa * pa + pb * pb + pc * pc); 
pa /= ps; pb /= ps; pc /= ps; pd /= ps; 
 
// 对每个点计算点到平面的距离,只有都在 0.2m 范围内才认为这个平面匹配是准的 
bool planeValid = true; 
for (int j = 0; j < 5; j++) { 
    if (fabs(pa * laserCloudSurfFromMapDS->points[pointSearchInd[j]].x + 
             pb * laserCloudSurfFromMapDS->points[pointSearchInd[j]].y + 
             pc * laserCloudSurfFromMapDS->points[pointSearchInd[j]].z + pd) > 0.2) { 
        planeValid = false; 
        break; 
    } 
} 
 
if (planeValid) { 
    // 用点到的平面的距离作为残差(带符号) 
    float pd2 = pa * pointSel.x + pb * pointSel.y + pc * pointSel.z + pd; 
点面雅可比计算 
用法向量作为雅可比,方向由 pd2 决定 
// 用法向量作为雅可比,方向由 pd2 间接决定 
coeff.x = s * pa; 
coeff.y = s * pb; 
coeff.z = s * pc; 
coeff.intensity = s * pd2; 
点线面残差对位姿的雅可比 
\text { mat } A=\left[\begin{array}{c} \frac{\partial d}{\partial \text { roll }} \\ \frac{\partial d}{\partial p i t c h} \\ \frac{\partial d}{\partial y a w} \\ \frac{\partial d}{\partial t_{x}} \\ \frac{\partial d}{\partial t_{y}} \\ \frac{\partial d}{\partial t_{z}} \end{array}\right]  
对平移的雅可比 
雅可比推导分为两步,残差对点的雅可比和点对位姿的雅可比,下面推导点对位姿的雅可比 
设 Lidar 相对于世界坐标系旋转矩阵为R ,平移为t ,将 Lidar 坐标系下的点转为世界坐标系的过程为: 
\boldsymbol{p}^{w}=\boldsymbol{R} \boldsymbol{p}+\boldsymbol{t}  
对平移的推导很简单,即 
\frac{\partial \boldsymbol{p}^{w}}{\partial \boldsymbol{t}}=\frac{\partial \boldsymbol{R} \boldsymbol{p}+\boldsymbol{t}}{\partial \boldsymbol{t}}=\boldsymbol{I}  
对应代码中 
matA.at<float>(i, 3) = coeff.z; 
matA.at<float>(i, 4) = coeff.x; 
matA.at<float>(i, 5) = coeff.y; 
对旋转的雅可比 
LOAM 原作者选择的是 ZXY 外旋(沿固定 Z 轴旋转-> 沿固定 X 轴旋转 -> 沿固定 Y 轴旋转)表示,由于外旋的旋转矩阵顺序为作乘,因此 ZXY 内旋的旋转矩阵的计算方式为:R=RyRxRz,即: 
\begin{aligned} \boldsymbol{R} &=\boldsymbol{R}_{y} \boldsymbol{R}_{x} \boldsymbol{R}_{z} \\ &=\left[\begin{array}{ccc} \cos (ry) & 0 & \sin (ry) \\ 0 & 1 & 0 \\ -\sin (ry) & 0 & \cos (ry) \end{array}\right]\left[\begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos (rx) & -\sin (rx) \\ 0 & \sin (rx) &  \cos (rx) \end{array}\right]\left[\begin{array}{ccc} \cos (rz) & -\sin (rz) & 0 \\ \sin (rz) & \cos (rz) & 0 \\ 0 & 0 & 1 \end{array}\right] \\ &=\left[\begin{array}{ccc} \cos (ry) \cos (rz)+\sin (ry) \sin (rx) \sin (rz) & \cos (rz) \sin (ry) \sin (rx)-\cos (ry) \sin (rz) & \cos (rx) \sin (ry) \\ \cos (rx) \sin (rz) & \cos (rx) \cos (rz) & -\sin (rx) \\ \cos (ry) \sin (rx) \sin (rz)-\cos (rz) \sin (ry) & \cos (ry) \cos (rz) \sin (rx)+\sin (ry) \sin (rz) & \cos (ry) \cos (rx) \end{array}\right] \end{aligned}  
平移对旋转求导为0,忽略 
点对rx求雅可比有: 
\frac{\partial(\boldsymbol{R} \boldsymbol{p})}{\partial rx}=\left[\begin{array}{c} (\sin (ry) \cos (rx) \sin (rz)) p_{x}+(\cos (rz) \sin (ry) \cos (rx)) p_{y}-(\sin (rx) \sin (ry)) p_{z} \\ -(\sin (rx) \sin (rz)) p_{x}-(\sin (rx) \cos (rz)) p_{y}-(\cos (rx)) p_{z} \\ (\cos (ry) \cos (rx) \sin (rz)) p_{x}+(\cos (ry) \cos (rz) \cos (rx)) p_{y}-(\cos (ry) \sin (rx)) p_{z} \end{array}\right]  
上式为点对位姿的雅可比,乘以残差对点的雅可比(coeff.x,coeff.y,coeff.z),得到对应代码中: 
// 进行 ZXY 外旋定义坐标系下的雅可比计算 
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; 
点对ry求雅可比有: 
\frac{\partial(\boldsymbol{R} \boldsymbol{p})}{\partial ry}=\left[\begin{array}{c} (-\sin (ry) \cos (rz)+\cos (ry) \sin (rx) \sin (rz)) p_{x}+(\cos (rz) \cos (ry) \sin (rx)+\sin (ry) \sin (rz)) p_{y}+(\cos (rx)  \cos (ry)) p_{z} \\ 0 \\ (-\sin (ry) \sin (rx) \sin (rz)-\cos (rz) \cos (ry)) p_{x}+(-\sin (ry) \cos (rz) \sin (rx)+\cos (ry) \sin (rz)) p_{y}-(\sin (ry) \cos (rx)) p_{z} \end{array}\right]  
对应代码中: 
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; 
点对rz求雅可比有: 
\frac{\partial(\boldsymbol{R} \boldsymbol{p})}{\partial rz}=\left[\begin{array}{c} (-\cos (ry) \sin (rz)+\sin (ry) \sin (rx) \cos (rz)) p_{x}+(-\sin (rz) \sin (ry) \sin (rx)-\cos (ry) \cos (rz)) p_{y} \\ (\cos (rx) \cos (rz)) p_{x}-(\cos (rx) \sin (rz)) p_{y} \\ (\cos (ry) \sin (rx) \cos (rz)+\sin (rz) \sin (ry)) p_{x}+(-\cos (ry) \sin (rz) \sin (rx)+\sin (ry) \cos (rz)) p_{y} \end{array}\right]  
对应代码中: 
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; 
至此雅可比推导完毕,接下来构建方程进行求解 
J^{T} J \Delta x=-J \cdot f(x)  
对应代码中: 
matA.at<float>(i, 0) = arz; 
matA.at<float>(i, 1) = arx; 
matA.at<float>(i, 2) = ary; 
matA.at<float>(i, 3) = coeff.z; 
matA.at<float>(i, 4) = coeff.x; 
matA.at<float>(i, 5) = coeff.y; 
matB.at<float>(i, 0) = -coeff.intensity; 
 
 
// QR 分解求解 Hx=b 
cv::transpose(matA, matAt); 
matAtA = matAt * matA; 
matAtB = matAt * matB; 
cv::solve(matAtA, matAtB, matX, cv::DECOMP_QR); 
更新位姿优化增量: 
/// 更新当前关键帧全局位姿估计 
transformTobeMapped[0] += matX.at<float>(0, 0); 
transformTobeMapped[1] += matX.at<float>(1, 0); 
transformTobeMapped[2] += matX.at<float>(2, 0); 
transformTobeMapped[3] += matX.at<float>(3, 0); 
transformTobeMapped[4] += matX.at<float>(4, 0); 
transformTobeMapped[5] += matX.at<float>(5, 0); 
参考: 
1、https://blog.csdn.net/weixin_37835423/article/details/111587379 
2、https://zhuanlan.zhihu.com/p/57351961 
3、基于 LOAM 的方法中平面点和边缘线的残差构建以及雅可比推导 
4、wykxwyc的博客 | wykxwyc&#39;s Blog |   
 
 
 
 |