diff --git a/build.sh b/build.sh
index 9aadf59..8b00265 100644
--- a/build.sh
+++ b/build.sh
@@ -15,6 +15,15 @@ cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j
+cd ../../Sophus
+
+echo "Configuring and building Thirdparty/Sophus ..."
+
+mkdir build
+cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+make -j
+
cd ../../../
echo "Uncompress vocabulary ..."
@@ -28,4 +37,5 @@ echo "Configuring and building ORB_SLAM3 ..."
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
-make -j
+make -j4
+
diff --git a/src/MLPnPsolver.cpp b/src/MLPnPsolver.cpp
index 2f8702b..47361f6 100644
--- a/src/MLPnPsolver.cpp
+++ b/src/MLPnPsolver.cpp
@@ -1,1056 +1,1745 @@
/**
-* This file is part of ORB-SLAM3
-*
-* Copyright (C) 2017-2021 Carlos Campos, Richard Elvira, Juan J. Gómez Rodríguez, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
-* Copyright (C) 2014-2016 Raúl Mur-Artal, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
-*
-* ORB-SLAM3 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
-* License as published by the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* ORB-SLAM3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
-* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License along with ORB-SLAM3.
-* If not, see .
-*/
+ * This file is part of ORB-SLAM3
+ *
+ * Copyright (C) 2017-2021 Carlos Campos, Richard Elvira, Juan J. Gómez Rodríguez, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
+ * Copyright (C) 2014-2016 Raúl Mur-Artal, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
+ *
+ * ORB-SLAM3 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ORB-SLAM3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with ORB-SLAM3.
+ * If not, see .
+ */
/******************************************************************************
-* Author: Steffen Urban *
-* Contact: urbste@gmail.com *
-* License: Copyright (c) 2016 Steffen Urban, ANU. All rights reserved. *
-* *
-* Redistribution and use in source and binary forms, with or without *
-* modification, are permitted provided that the following conditions *
-* are met: *
-* * Redistributions of source code must retain the above copyright *
-* notice, this list of conditions and the following disclaimer. *
-* * Redistributions in binary form must reproduce the above copyright *
-* notice, this list of conditions and the following disclaimer in the *
-* documentation and/or other materials provided with the distribution. *
-* * Neither the name of ANU nor the names of its contributors may be *
-* used to endorse or promote products derived from this software without *
-* specific prior written permission. *
-* *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"*
-* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE *
-* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE *
-* ARE DISCLAIMED. IN NO EVENT SHALL ANU OR THE CONTRIBUTORS BE LIABLE *
-* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *
-* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
-* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *
-* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *
-* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
-* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
-* SUCH DAMAGE. *
-******************************************************************************/
-
+ * Author: Steffen Urban *
+ * Contact: urbste@gmail.com *
+ * License: Copyright (c) 2016 Steffen Urban, ANU. All rights reserved. *
+ * *
+ * Redistribution and use in source and binary forms, with or without *
+ * modification, are permitted provided that the following conditions *
+ * are met: *
+ * * Redistributions of source code must retain the above copyright *
+ * notice, this list of conditions and the following disclaimer. *
+ * * Redistributions in binary form must reproduce the above copyright *
+ * notice, this list of conditions and the following disclaimer in the *
+ * documentation and/or other materials provided with the distribution. *
+ * * Neither the name of ANU nor the names of its contributors may be *
+ * used to endorse or promote products derived from this software without *
+ * specific prior written permission. *
+ * *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"*
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE *
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE *
+ * ARE DISCLAIMED. IN NO EVENT SHALL ANU OR THE CONTRIBUTORS BE LIABLE *
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
+ * SUCH DAMAGE. *
+ ******************************************************************************/
#include "MLPnPsolver.h"
#include
+// MLPnP算法,极大似然PnP算法,解决PnP问题,用在重定位中,不用运动的先验知识来估计相机位姿
+// 基于论文《MLPNP - A REAL-TIME MAXIMUM LIKELIHOOD SOLUTION TO THE PERSPECTIVE-N-POINT PROBLEM》
-namespace ORB_SLAM3 {
- MLPnPsolver::MLPnPsolver(const Frame &F, const vector &vpMapPointMatches):
- mnInliersi(0), mnIterations(0), mnBestInliers(0), N(0), mpCamera(F.mpCamera){
- mvpMapPointMatches = vpMapPointMatches;
- mvBearingVecs.reserve(F.mvpMapPoints.size());
- mvP2D.reserve(F.mvpMapPoints.size());
- mvSigma2.reserve(F.mvpMapPoints.size());
- mvP3Dw.reserve(F.mvpMapPoints.size());
- mvKeyPointIndices.reserve(F.mvpMapPoints.size());
- mvAllIndices.reserve(F.mvpMapPoints.size());
+namespace ORB_SLAM3
+{
+/**
+ * @brief MLPnP 构造函数
+ *
+ * @param[in] F 输入帧的数据
+ * @param[in] vpMapPointMatches 待匹配的特征点
+ * @param[in] mnInliersi 内点的个数
+ * @param[in] mnIterations Ransac迭代次数
+ * @param[in] mnBestInliers 最佳内点数
+ * @param[in] N 所有2D点的个数
+ * @param[in] mpCamera 相机模型,利用该变量对3D点进行投影
+ */
+MLPnPsolver::MLPnPsolver(const Frame &F, // 输入帧的数据
+ const vector &vpMapPointMatches // 待匹配的特征点,是当前帧和候选关键帧用BoW进行快速匹配的结果
+ ) : mnInliersi(0), // 内点的个数
+ mnIterations(0), // Ransac迭代次数
+ mnBestInliers(0), // 最佳内点数
+ N(0), // 所有2D点的个数
+ mpCamera(F.mpCamera) // 相机模型,利用该变量对3D点进行投影
+{
+ mvpMapPointMatches = vpMapPointMatches; // 待匹配的特征点,是当前帧和候选关键帧用BoW进行快速匹配的结果
+ mvBearingVecs.reserve(F.mvpMapPoints.size()); // 初始化3D点的单位向量
+ mvP2D.reserve(F.mvpMapPoints.size()); // 初始化3D点的投影点
+ mvSigma2.reserve(F.mvpMapPoints.size()); // 初始化卡方检验中的sigma值
+ mvP3Dw.reserve(F.mvpMapPoints.size()); // 初始化3D点坐标
+ mvKeyPointIndices.reserve(F.mvpMapPoints.size()); // 初始化3D点的索引值
+ mvAllIndices.reserve(F.mvpMapPoints.size()); // 初始化所有索引值
- int idx = 0;
- for(size_t i = 0, iend = mvpMapPointMatches.size(); i < iend; i++){
- MapPoint* pMP = vpMapPointMatches[i];
+ // 一些必要的初始化操作
+ int idx = 0;
+ for (size_t i = 0, iend = mvpMapPointMatches.size(); i < iend; i++)
+ {
+ MapPoint *pMP = vpMapPointMatches[i];
- if(pMP){
- if(!pMP -> isBad()){
- if(i >= F.mvKeysUn.size()) continue;
- const cv::KeyPoint &kp = F.mvKeysUn[i];
-
- mvP2D.push_back(kp.pt);
- mvSigma2.push_back(F.mvLevelSigma2[kp.octave]);
-
- //Bearing vector should be normalized
- cv::Point3f cv_br = mpCamera->unproject(kp.pt);
- cv_br /= cv_br.z;
- bearingVector_t br(cv_br.x,cv_br.y,cv_br.z);
- mvBearingVecs.push_back(br);
-
- //3D coordinates
- Eigen::Matrix posEig = pMP -> GetWorldPos();
- point_t pos(posEig(0),posEig(1),posEig(2));
- mvP3Dw.push_back(pos);
-
- mvKeyPointIndices.push_back(i);
- mvAllIndices.push_back(idx);
-
- idx++;
- }
- }
- }
-
- SetRansacParameters();
- }
-
- //RANSAC methods
- bool MLPnPsolver::iterate(int nIterations, bool &bNoMore, vector &vbInliers, int &nInliers, Eigen::Matrix4f &Tout){
- Tout.setIdentity();
- bNoMore = false;
- vbInliers.clear();
- nInliers=0;
-
- if(N vAvailableIndices;
-
- int nCurrentIterations = 0;
- while(mnIterations indexes(mRansacMinSet);
-
- // Get min set of points
- for(short i = 0; i < mRansacMinSet; ++i)
- {
- int randi = DUtils::Random::RandomInt(0, vAvailableIndices.size()-1);
-
- int idx = vAvailableIndices[randi];
-
- bearingVecs[i] = mvBearingVecs[idx];
- p3DS[i] = mvP3Dw[idx];
- indexes[i] = i;
-
- vAvailableIndices[randi] = vAvailableIndices.back();
- vAvailableIndices.pop_back();
- }
-
- //By the moment, we are using MLPnP without covariance info
- cov3_mats_t covs(1);
-
- //Result
- transformation_t result;
-
- // Compute camera pose
- computePose(bearingVecs,p3DS,covs,indexes,result);
-
- //Save result
- mRi[0][0] = result(0,0);
- mRi[0][1] = result(0,1);
- mRi[0][2] = result(0,2);
-
- mRi[1][0] = result(1,0);
- mRi[1][1] = result(1,1);
- mRi[1][2] = result(1,2);
-
- mRi[2][0] = result(2,0);
- mRi[2][1] = result(2,1);
- mRi[2][2] = result(2,2);
-
- mti[0] = result(0,3);mti[1] = result(1,3);mti[2] = result(2,3);
-
- // Check inliers
- CheckInliers();
-
- if(mnInliersi>=mRansacMinInliers)
- {
- // If it is the best solution so far, save it
- if(mnInliersi>mnBestInliers)
- {
- mvbBestInliers = mvbInliersi;
- mnBestInliers = mnInliersi;
-
- cv::Mat Rcw(3,3,CV_64F,mRi);
- cv::Mat tcw(3,1,CV_64F,mti);
- Rcw.convertTo(Rcw,CV_32F);
- tcw.convertTo(tcw,CV_32F);
- mBestTcw.setIdentity();
- mBestTcw.block<3,3>(0,0) = Converter::toMatrix3f(Rcw);
- mBestTcw.block<3,1>(0,3) = Converter::toVector3f(tcw);
-
- Eigen::Matrix eigRcw(mRi[0]);
- Eigen::Vector3d eigtcw(mti);
- }
-
- if(Refine())
- {
- nInliers = mnRefinedInliers;
- vbInliers = vector(mvpMapPointMatches.size(),false);
- for(int i=0; i=mRansacMaxIts)
- {
- bNoMore=true;
- if(mnBestInliers>=mRansacMinInliers)
- {
- nInliers=mnBestInliers;
- vbInliers = vector(mvpMapPointMatches.size(),false);
- for(int i=0; iproject(P3Dc);
-
- float distX = P2D.x-uv.x;
- float distY = P2D.y-uv.y;
-
- float error2 = distX*distX+distY*distY;
-
- if(error2isBad())
{
- mvbInliersi[i]=true;
- mnInliersi++;
- }
- else
- {
- mvbInliersi[i]=false;
+ // 如果记录的点个数超过总数,则不做任何事情,否则继续记录
+ if (i >= F.mvKeysUn.size())
+ continue;
+ const cv::KeyPoint &kp = F.mvKeysUn[i];
+
+ // 保存3D点的投影点
+ mvP2D.push_back(kp.pt);
+
+ // 保存卡方检验中的sigma值
+ mvSigma2.push_back(F.mvLevelSigma2[kp.octave]);
+
+ // Bearing vector should be normalized
+ // 特征点投影,并计算单位向量
+ cv::Point3f cv_br = mpCamera->unproject(kp.pt);
+ cv_br /= cv_br.z;
+ bearingVector_t br(cv_br.x, cv_br.y, cv_br.z);
+ mvBearingVecs.push_back(br);
+
+ // 3D coordinates
+ // 获取当前特征点的3D坐标
+ Eigen::Matrix posEig = pMP->GetWorldPos();
+ point_t pos(posEig(0), posEig(1), posEig(2));
+ mvP3Dw.push_back(pos);
+
+ // 记录当前特征点的索引值,挑选后的
+ mvKeyPointIndices.push_back(i);
+
+ // 记录所有特征点的索引值
+ mvAllIndices.push_back(idx);
+
+ idx++;
}
}
}
- bool MLPnPsolver::Refine(){
- vector vIndices;
- vIndices.reserve(mvbBestInliers.size());
+ // 设置RANSAC参数
+ SetRansacParameters();
+}
- for(size_t i=0; i &vbInliers, int &nInliers, Eigen::Matrix4f &Tout)
+{
+ Tout.setIdentity();
+ bNoMore = false; // 已经达到最大迭代次数的标志
+ vbInliers.clear(); // 清除保存判断是否是内点的容器
+ nInliers = 0; // 当前次迭代时的内点数
+ // N为所有2D点的个数, mRansacMinInliers为正常退出RANSAC迭代过程中最少的inlier数
+ // Step 1: 判断,如果2D点个数不足以启动RANSAC迭代过程的最小下限,则退出
+ if (N < mRansacMinInliers)
+ {
+ bNoMore = true; // 已经达到最大迭代次数的标志
+ return false; // 函数退出
+ }
+
+ // mvAllIndices为所有参与PnP的2D点的索引
+ // vAvailableIndices为每次从mvAllIndices中随机挑选mRansacMinSet组3D-2D对应点进行一次RANSAC
+ vector vAvailableIndices;
+
+ // 当前的迭代次数id
+ int nCurrentIterations = 0;
+
+ // Step 2: 正常迭代计算进行相机位姿估计,如果满足效果上限,直接返回最佳估计结果,否则就继续利用最小集(6个点)估计位姿
+ // 进行迭代的条件:
+ // 条件1: 历史进行的迭代次数少于最大迭代值
+ // 条件2: 当前进行的迭代次数少于当前函数给定的最大迭代值
+ while (mnIterations < mRansacMaxIts || nCurrentIterations < nIterations)
+ {
+ // 迭代次数更新加1,直到达到最大迭代次数
+ nCurrentIterations++;
+ mnIterations++;
+
+ // 清空已有的匹配点的计数,为新的一次迭代作准备
+ vAvailableIndices = mvAllIndices;
+
+ // Bearing vectors and 3D points used for this ransac iteration
+ // 初始化单位向量和3D点,给当前ransac迭代使用
+ bearingVectors_t bearingVecs(mRansacMinSet);
+ points_t p3DS(mRansacMinSet);
+ vector indexes(mRansacMinSet);
+
+ // Get min set of points
+ // 选取最小集,从vAvailableIndices中选取mRansacMinSet个点进行操作,这里应该是6
+ for (short i = 0; i < mRansacMinSet; ++i)
{
- if(mvbBestInliers[i])
- {
- vIndices.push_back(i);
- }
- }
+ // 在所有备选点中随机抽取一个,通过随机抽取索引数组vAvailableIndices的索引[randi]来实现
+ int randi = DUtils::Random::RandomInt(0, vAvailableIndices.size() - 1);
- //Bearing vectors and 3D points used for this ransac iteration
- bearingVectors_t bearingVecs;
- points_t p3DS;
- vector indexes;
+ // vAvailableIndices[randi]才是备选点真正的索引值,randi是索引数组的索引值,不要搞混了
+ int idx = vAvailableIndices[randi];
- for(size_t i=0; imRansacMinInliers)
+ if (mnInliersi >= mRansacMinInliers)
{
- cv::Mat Rcw(3,3,CV_64F,mRi);
- cv::Mat tcw(3,1,CV_64F,mti);
- Rcw.convertTo(Rcw,CV_32F);
- tcw.convertTo(tcw,CV_32F);
- mRefinedTcw.setIdentity();
+ // If it is the best solution so far, save it
+ // 如果该结果是目前内点数最多的,说明该结果是目前最好的,保存起来
+ if (mnInliersi > mnBestInliers)
+ {
+ mvbBestInliers = mvbInliersi; // 每个点是否是内点的标记
+ mnBestInliers = mnInliersi; // 内点个数
- mRefinedTcw.block<3,3>(0,0) = Converter::toMatrix3f(Rcw);
- mRefinedTcw.block<3,1>(0,3) = Converter::toVector3f(tcw);
+ cv::Mat Rcw(3, 3, CV_64F, mRi);
+ cv::Mat tcw(3, 1, CV_64F, mti);
+ Rcw.convertTo(Rcw, CV_32F);
+ tcw.convertTo(tcw, CV_32F);
+ mBestTcw.setIdentity();
+ mBestTcw.block<3, 3>(0, 0) = Converter::toMatrix3f(Rcw);
+ mBestTcw.block<3, 1>(0, 3) = Converter::toVector3f(tcw);
- Eigen::Matrix eigRcw(mRi[0]);
- Eigen::Vector3d eigtcw(mti);
+ Eigen::Matrix eigRcw(mRi[0]);
+ Eigen::Vector3d eigtcw(mti);
+ }
+ // 用新的内点对相机位姿精求解,提高位姿估计精度,这里如果有足够内点的话,函数直接返回该值,不再继续计算
+ if (Refine())
+ {
+ nInliers = mnRefinedInliers;
+ vbInliers = vector(mvpMapPointMatches.size(), false);
+ for (int i = 0; i < N; i++)
+ {
+ if (mvbRefinedInliers[i])
+ vbInliers[mvKeyPointIndices[i]] = true;
+ }
+ Tout = mRefinedTcw;
+ return true;
+ }
+ }
+ } // 迭代
+
+ // Step 3: 选择最小集中效果最好的相机位姿估计结果,如果没有,只能用6个点去计算这个值了
+ // 程序运行到这里,说明Refine失败了,说明精求解过程中,内点的个数不满足最小阈值,那就只能在当前结果中选择内点数最多的那个最小集
+ // 但是也意味着这样子的结果最终是用6个点来求出来的,近似效果一般
+ if (mnIterations >= mRansacMaxIts)
+ {
+ bNoMore = true;
+ if (mnBestInliers >= mRansacMinInliers)
+ {
+ nInliers = mnBestInliers;
+ vbInliers = vector(mvpMapPointMatches.size(), false);
+ for (int i = 0; i < N; i++)
+ {
+ if (mvbBestInliers[i])
+ vbInliers[mvKeyPointIndices[i]] = true;
+ }
+ Tout = mBestTcw;
return true;
}
- return false;
}
- //MLPnP methods
- void MLPnPsolver::computePose(const bearingVectors_t &f, const points_t &p, const cov3_mats_t &covMats,
- const std::vector &indices, transformation_t &result) {
- size_t numberCorrespondences = indices.size();
- assert(numberCorrespondences > 5);
+ // step 4 相机位姿估计失败,返回零值
+ // 程序运行到这里,那说明没有满足条件的相机位姿估计结果,位姿估计失败了
+ return false;
+}
- bool planar = false;
- // compute the nullspace of all vectors
- std::vector nullspaces(numberCorrespondences);
- Eigen::MatrixXd points3(3, numberCorrespondences);
- points_t points3v(numberCorrespondences);
- points4_t points4v(numberCorrespondences);
- for (size_t i = 0; i < numberCorrespondences; i++) {
- bearingVector_t f_current = f[indices[i]];
- points3.col(i) = p[indices[i]];
- // nullspace of right vector
- Eigen::JacobiSVD
- svd_f(f_current.transpose(), Eigen::ComputeFullV);
- nullspaces[i] = svd_f.matrixV().block(0, 1, 3, 2);
- points3v[i] = p[indices[i]];
+/**
+ * @brief 设置RANSAC迭代的参数
+ *
+ * @param[in] probability 模型最大概率值,默认0.9
+ * @param[in] minInliers 内点的最小阈值,默认8
+ * @param[in] maxIterations 最大迭代次数,默认300
+ * @param[in] minSet 最小集,每次采样六个点,即最小集应该设置为6,论文里面写着I > 5
+ * @param[in] epsilon 理论最少内点个数,这里是按照总数的比例计算,所以epsilon是比例,默认是0.4
+ * @param[in] th2 卡方检验阈值
+ *
+ */
+void MLPnPsolver::SetRansacParameters(double probability, int minInliers, int maxIterations, int minSet, float epsilon, float th2)
+{
+ mRansacProb = probability; // 模型最大概率值,默认0.9
+ mRansacMinInliers = minInliers; // 内点的最小阈值,默认8
+ mRansacMaxIts = maxIterations; // 最大迭代次数,默认300
+ mRansacEpsilon = epsilon; // 理论最少内点个数,这里是按照总数的比例计算,所以epsilon是比例,默认是0.4
+ mRansacMinSet = minSet; // 每次采样六个点,即最小集应该设置为6,论文里面写着I > 5
+
+ N = mvP2D.size(); // number of correspondences
+
+ mvbInliersi.resize(N); // 是否是内点的标记位
+
+ // Adjust Parameters according to number of correspondences
+ // 计算最少个数点,选择(给定内点数, 最小集, 理论内点数)的最小值
+ int nMinInliers = N * mRansacEpsilon;
+ if (nMinInliers < mRansacMinInliers)
+ nMinInliers = mRansacMinInliers;
+ if (nMinInliers < minSet)
+ nMinInliers = minSet;
+ mRansacMinInliers = nMinInliers;
+
+ // 根据最终得到的"最小内点数"来调整 内点数/总体数 比例epsilon
+ if (mRansacEpsilon < (float)mRansacMinInliers / N)
+ mRansacEpsilon = (float)mRansacMinInliers / N;
+
+ // Set RANSAC iterations according to probability, epsilon, and max iterations
+ // 根据给出的各种参数计算RANSAC的理论迭代次数,并且敲定最终在迭代过程中使用的RANSAC最大迭代次数
+ int nIterations;
+
+ if (mRansacMinInliers == N)
+ nIterations = 1;
+ else
+ nIterations = ceil(log(1 - mRansacProb) / log(1 - pow(mRansacEpsilon, 3)));
+
+ mRansacMaxIts = max(1, min(nIterations, mRansacMaxIts));
+
+ // 计算不同图层上的特征点在进行内点检验的时候,所使用的不同判断误差阈值
+ mvMaxError.resize(mvSigma2.size()); // 层数
+ for (size_t i = 0; i < mvSigma2.size(); i++)
+ mvMaxError[i] = mvSigma2[i] * th2; // 不同的尺度,设置不同的最大偏差
+}
+
+/**
+ * @brief 通过之前求解的(R t)检查哪些3D-2D点对属于inliers
+ */
+void MLPnPsolver::CheckInliers()
+{
+ mnInliersi = 0;
+
+ // 遍历当前帧中所有的匹配点
+ for (int i = 0; i < N; i++)
+ {
+ // 取出对应的3D点和2D点
+ point_t p = mvP3Dw[i];
+ cv::Point3f P3Dw(p(0), p(1), p(2));
+ cv::Point2f P2D = mvP2D[i];
+
+ // 将3D点由世界坐标系旋转到相机坐标系
+ float xc = mRi[0][0] * P3Dw.x + mRi[0][1] * P3Dw.y + mRi[0][2] * P3Dw.z + mti[0];
+ float yc = mRi[1][0] * P3Dw.x + mRi[1][1] * P3Dw.y + mRi[1][2] * P3Dw.z + mti[1];
+ float zc = mRi[2][0] * P3Dw.x + mRi[2][1] * P3Dw.y + mRi[2][2] * P3Dw.z + mti[2];
+
+ // 将相机坐标系下的3D进行投影
+ cv::Point3f P3Dc(xc, yc, zc);
+ cv::Point2f uv = mpCamera->project(P3Dc);
+
+ // 计算残差
+ float distX = P2D.x - uv.x;
+ float distY = P2D.y - uv.y;
+
+ float error2 = distX * distX + distY * distY;
+
+ // 判定是不是内点
+ if (error2 < mvMaxError[i])
+ {
+ mvbInliersi[i] = true;
+ mnInliersi++;
}
-
- //////////////////////////////////////
- // 1. test if we have a planar scene
- //////////////////////////////////////
-
- Eigen::Matrix3d planarTest = points3 * points3.transpose();
- Eigen::FullPivHouseholderQR rankTest(planarTest);
- Eigen::Matrix3d eigenRot;
- eigenRot.setIdentity();
-
- // if yes -> transform points to new eigen frame
- //if (minEigenVal < 1e-3 || minEigenVal == 0.0)
- //rankTest.setThreshold(1e-10);
- if (rankTest.rank() == 2) {
- planar = true;
- // self adjoint is faster and more accurate than general eigen solvers
- // also has closed form solution for 3x3 self-adjoint matrices
- // in addition this solver sorts the eigenvalues in increasing order
- Eigen::SelfAdjointEigenSolver eigen_solver(planarTest);
- eigenRot = eigen_solver.eigenvectors().real();
- eigenRot.transposeInPlace();
- for (size_t i = 0; i < numberCorrespondences; i++)
- points3.col(i) = eigenRot * points3.col(i);
- }
- //////////////////////////////////////
- // 2. stochastic model
- //////////////////////////////////////
- Eigen::SparseMatrix P(2 * numberCorrespondences,
- 2 * numberCorrespondences);
- bool use_cov = false;
- P.setIdentity(); // standard
-
- // if we do have covariance information
- // -> fill covariance matrix
- if (covMats.size() == numberCorrespondences) {
- use_cov = true;
- int l = 0;
- for (size_t i = 0; i < numberCorrespondences; ++i) {
- // invert matrix
- cov2_mat_t temp = nullspaces[i].transpose() * covMats[i] * nullspaces[i];
- temp = temp.inverse().eval();
- P.coeffRef(l, l) = temp(0, 0);
- P.coeffRef(l, l + 1) = temp(0, 1);
- P.coeffRef(l + 1, l) = temp(1, 0);
- P.coeffRef(l + 1, l + 1) = temp(1, 1);
- l += 2;
- }
- }
-
- //////////////////////////////////////
- // 3. fill the design matrix A
- //////////////////////////////////////
- const int rowsA = 2 * numberCorrespondences;
- int colsA = 12;
- Eigen::MatrixXd A;
- if (planar) {
- colsA = 9;
- A = Eigen::MatrixXd(rowsA, 9);
- } else
- A = Eigen::MatrixXd(rowsA, 12);
- A.setZero();
-
- // fill design matrix
- if (planar) {
- for (size_t i = 0; i < numberCorrespondences; ++i) {
- point_t pt3_current = points3.col(i);
-
- // r12
- A(2 * i, 0) = nullspaces[i](0, 0) * pt3_current[1];
- A(2 * i + 1, 0) = nullspaces[i](0, 1) * pt3_current[1];
- // r13
- A(2 * i, 1) = nullspaces[i](0, 0) * pt3_current[2];
- A(2 * i + 1, 1) = nullspaces[i](0, 1) * pt3_current[2];
- // r22
- A(2 * i, 2) = nullspaces[i](1, 0) * pt3_current[1];
- A(2 * i + 1, 2) = nullspaces[i](1, 1) * pt3_current[1];
- // r23
- A(2 * i, 3) = nullspaces[i](1, 0) * pt3_current[2];
- A(2 * i + 1, 3) = nullspaces[i](1, 1) * pt3_current[2];
- // r32
- A(2 * i, 4) = nullspaces[i](2, 0) * pt3_current[1];
- A(2 * i + 1, 4) = nullspaces[i](2, 1) * pt3_current[1];
- // r33
- A(2 * i, 5) = nullspaces[i](2, 0) * pt3_current[2];
- A(2 * i + 1, 5) = nullspaces[i](2, 1) * pt3_current[2];
- // t1
- A(2 * i, 6) = nullspaces[i](0, 0);
- A(2 * i + 1, 6) = nullspaces[i](0, 1);
- // t2
- A(2 * i, 7) = nullspaces[i](1, 0);
- A(2 * i + 1, 7) = nullspaces[i](1, 1);
- // t3
- A(2 * i, 8) = nullspaces[i](2, 0);
- A(2 * i + 1, 8) = nullspaces[i](2, 1);
- }
- } else {
- for (size_t i = 0; i < numberCorrespondences; ++i) {
- point_t pt3_current = points3.col(i);
-
- // r11
- A(2 * i, 0) = nullspaces[i](0, 0) * pt3_current[0];
- A(2 * i + 1, 0) = nullspaces[i](0, 1) * pt3_current[0];
- // r12
- A(2 * i, 1) = nullspaces[i](0, 0) * pt3_current[1];
- A(2 * i + 1, 1) = nullspaces[i](0, 1) * pt3_current[1];
- // r13
- A(2 * i, 2) = nullspaces[i](0, 0) * pt3_current[2];
- A(2 * i + 1, 2) = nullspaces[i](0, 1) * pt3_current[2];
- // r21
- A(2 * i, 3) = nullspaces[i](1, 0) * pt3_current[0];
- A(2 * i + 1, 3) = nullspaces[i](1, 1) * pt3_current[0];
- // r22
- A(2 * i, 4) = nullspaces[i](1, 0) * pt3_current[1];
- A(2 * i + 1, 4) = nullspaces[i](1, 1) * pt3_current[1];
- // r23
- A(2 * i, 5) = nullspaces[i](1, 0) * pt3_current[2];
- A(2 * i + 1, 5) = nullspaces[i](1, 1) * pt3_current[2];
- // r31
- A(2 * i, 6) = nullspaces[i](2, 0) * pt3_current[0];
- A(2 * i + 1, 6) = nullspaces[i](2, 1) * pt3_current[0];
- // r32
- A(2 * i, 7) = nullspaces[i](2, 0) * pt3_current[1];
- A(2 * i + 1, 7) = nullspaces[i](2, 1) * pt3_current[1];
- // r33
- A(2 * i, 8) = nullspaces[i](2, 0) * pt3_current[2];
- A(2 * i + 1, 8) = nullspaces[i](2, 1) * pt3_current[2];
- // t1
- A(2 * i, 9) = nullspaces[i](0, 0);
- A(2 * i + 1, 9) = nullspaces[i](0, 1);
- // t2
- A(2 * i, 10) = nullspaces[i](1, 0);
- A(2 * i + 1, 10) = nullspaces[i](1, 1);
- // t3
- A(2 * i, 11) = nullspaces[i](2, 0);
- A(2 * i + 1, 11) = nullspaces[i](2, 1);
- }
- }
-
- //////////////////////////////////////
- // 4. solve least squares
- //////////////////////////////////////
- Eigen::MatrixXd AtPA;
- if (use_cov)
- AtPA = A.transpose() * P * A; // setting up the full normal equations seems to be unstable
else
- AtPA = A.transpose() * A;
-
- Eigen::JacobiSVD svd_A(AtPA, Eigen::ComputeFullV);
- Eigen::MatrixXd result1 = svd_A.matrixV().col(colsA - 1);
-
- ////////////////////////////////
- // now we treat the results differently,
- // depending on the scene (planar or not)
- ////////////////////////////////
- rotation_t Rout;
- translation_t tout;
- if (planar) // planar case
{
- rotation_t tmp;
- // until now, we only estimated
- // row one and two of the transposed rotation matrix
- tmp << 0.0, result1(0, 0), result1(1, 0),
- 0.0, result1(2, 0), result1(3, 0),
- 0.0, result1(4, 0), result1(5, 0);
- // row 3
- tmp.col(0) = tmp.col(1).cross(tmp.col(2));
- tmp.transposeInPlace();
-
- double scale = 1.0 / std::sqrt(std::abs(tmp.col(1).norm() * tmp.col(2).norm()));
- // find best rotation matrix in frobenius sense
- Eigen::JacobiSVD svd_R_frob(tmp, Eigen::ComputeFullU | Eigen::ComputeFullV);
- rotation_t Rout1 = svd_R_frob.matrixU() * svd_R_frob.matrixV().transpose();
- // test if we found a good rotation matrix
- if (Rout1.determinant() < 0)
- Rout1 *= -1.0;
- // rotate this matrix back using the eigen frame
- Rout1 = eigenRot.transpose() * Rout1;
-
- translation_t t = scale * translation_t(result1(6, 0), result1(7, 0), result1(8, 0));
- Rout1.transposeInPlace();
- Rout1 *= -1;
- if (Rout1.determinant() < 0.0)
- Rout1.col(2) *= -1;
- // now we have to find the best out of 4 combinations
- rotation_t R1, R2;
- R1.col(0) = Rout1.col(0);
- R1.col(1) = Rout1.col(1);
- R1.col(2) = Rout1.col(2);
- R2.col(0) = -Rout1.col(0);
- R2.col(1) = -Rout1.col(1);
- R2.col(2) = Rout1.col(2);
-
- vector> Ts(4);
- Ts[0].block<3, 3>(0, 0) = R1;
- Ts[0].block<3, 1>(0, 3) = t;
- Ts[1].block<3, 3>(0, 0) = R1;
- Ts[1].block<3, 1>(0, 3) = -t;
- Ts[2].block<3, 3>(0, 0) = R2;
- Ts[2].block<3, 1>(0, 3) = t;
- Ts[3].block<3, 3>(0, 0) = R2;
- Ts[3].block<3, 1>(0, 3) = -t;
-
- vector normVal(4);
- for (int i = 0; i < 4; ++i) {
- point_t reproPt;
- double norms = 0.0;
- for (int p = 0; p < 6; ++p) {
- reproPt = Ts[i].block<3, 3>(0, 0) * points3v[p] + Ts[i].block<3, 1>(0, 3);
- reproPt = reproPt / reproPt.norm();
- norms += (1.0 - reproPt.transpose() * f[indices[p]]);
- }
- normVal[i] = norms;
- }
- std::vector::iterator
- findMinRepro = std::min_element(std::begin(normVal), std::end(normVal));
- int idx = std::distance(std::begin(normVal), findMinRepro);
- Rout = Ts[idx].block<3, 3>(0, 0);
- tout = Ts[idx].block<3, 1>(0, 3);
- } else // non-planar
- {
- rotation_t tmp;
- tmp << result1(0, 0), result1(3, 0), result1(6, 0),
- result1(1, 0), result1(4, 0), result1(7, 0),
- result1(2, 0), result1(5, 0), result1(8, 0);
- // get the scale
- double scale = 1.0 /
- std::pow(std::abs(tmp.col(0).norm() * tmp.col(1).norm() * tmp.col(2).norm()), 1.0 / 3.0);
- //double scale = 1.0 / std::sqrt(std::abs(tmp.col(0).norm() * tmp.col(1).norm()));
- // find best rotation matrix in frobenius sense
- Eigen::JacobiSVD svd_R_frob(tmp, Eigen::ComputeFullU | Eigen::ComputeFullV);
- Rout = svd_R_frob.matrixU() * svd_R_frob.matrixV().transpose();
- // test if we found a good rotation matrix
- if (Rout.determinant() < 0)
- Rout *= -1.0;
- // scale translation
- tout = Rout * (scale * translation_t(result1(9, 0), result1(10, 0), result1(11, 0)));
-
- // find correct direction in terms of reprojection error, just take the first 6 correspondences
- vector error(2);
- vector> Ts(2);
- for (int s = 0; s < 2; ++s) {
- error[s] = 0.0;
- Ts[s] = Eigen::Matrix4d::Identity();
- Ts[s].block<3, 3>(0, 0) = Rout;
- if (s == 0)
- Ts[s].block<3, 1>(0, 3) = tout;
- else
- Ts[s].block<3, 1>(0, 3) = -tout;
- Ts[s] = Ts[s].inverse().eval();
- for (int p = 0; p < 6; ++p) {
- bearingVector_t v = Ts[s].block<3, 3>(0, 0) * points3v[p] + Ts[s].block<3, 1>(0, 3);
- v = v / v.norm();
- error[s] += (1.0 - v.transpose() * f[indices[p]]);
- }
- }
- if (error[0] < error[1])
- tout = Ts[0].block<3, 1>(0, 3);
- else
- tout = Ts[1].block<3, 1>(0, 3);
- Rout = Ts[0].block<3, 3>(0, 0);
-
+ mvbInliersi[i] = false;
}
-
- //////////////////////////////////////
- // 5. gauss newton
- //////////////////////////////////////
- rodrigues_t omega = rot2rodrigues(Rout);
- Eigen::VectorXd minx(6);
- minx[0] = omega[0];
- minx[1] = omega[1];
- minx[2] = omega[2];
- minx[3] = tout[0];
- minx[4] = tout[1];
- minx[5] = tout[2];
-
- mlpnp_gn(minx, points3v, nullspaces, P, use_cov);
-
- Rout = rodrigues2rot(rodrigues_t(minx[0], minx[1], minx[2]));
- tout = translation_t(minx[3], minx[4], minx[5]);
- // result inverse as opengv uses this convention
- result.block<3, 3>(0, 0) = Rout;
- result.block<3, 1>(0, 3) = tout;
}
+}
- Eigen::Matrix3d MLPnPsolver::rodrigues2rot(const Eigen::Vector3d &omega) {
- rotation_t R = Eigen::Matrix3d::Identity();
+/**
+ * @brief 使用新的内点来继续对位姿进行精求解
+ */
+bool MLPnPsolver::Refine()
+{
+ // 记录内点的索引值
+ vector vIndices;
+ vIndices.reserve(mvbBestInliers.size());
- Eigen::Matrix3d skewW;
- skewW << 0.0, -omega(2), omega(1),
- omega(2), 0.0, -omega(0),
- -omega(1), omega(0), 0.0;
-
- double omega_norm = omega.norm();
-
- if (omega_norm > std::numeric_limits::epsilon())
- R = R + sin(omega_norm) / omega_norm * skewW
- + (1 - cos(omega_norm)) / (omega_norm * omega_norm) * (skewW * skewW);
-
- return R;
- }
-
- Eigen::Vector3d MLPnPsolver::rot2rodrigues(const Eigen::Matrix3d &R) {
- rodrigues_t omega;
- omega << 0.0, 0.0, 0.0;
-
- double trace = R.trace() - 1.0;
- double wnorm = acos(trace / 2.0);
- if (wnorm > std::numeric_limits::epsilon())
+ for (size_t i = 0; i < mvbBestInliers.size(); i++)
+ {
+ if (mvbBestInliers[i])
{
- omega[0] = (R(2, 1) - R(1, 2));
- omega[1] = (R(0, 2) - R(2, 0));
- omega[2] = (R(1, 0) - R(0, 1));
- double sc = wnorm / (2.0*sin(wnorm));
- omega *= sc;
+ vIndices.push_back(i);
}
- return omega;
}
- void MLPnPsolver::mlpnp_gn(Eigen::VectorXd &x, const points_t &pts, const std::vector &nullspaces,
- const Eigen::SparseMatrix Kll, bool use_cov) {
- const int numObservations = pts.size();
- const int numUnknowns = 6;
- // check redundancy
- assert((2 * numObservations - numUnknowns) > 0);
+ // Bearing vectors and 3D points used for this ransac iteration
+ // 因为是重定义,所以要另外定义局部变量,和iterate里面一样
+ // 初始化单位向量和3D点,给当前重定义函数使用
+ bearingVectors_t bearingVecs;
+ points_t p3DS;
+ vector indexes;
- // =============
- // set all matrices up
- // =============
+ // 注意这里是用所有内点vIndices.size()来进行相机位姿估计
+ // 而iterate里面是用最小集mRansacMinSet(6个)来进行相机位姿估计
+ // TODO:有什么区别呢?答:肯定有啦,mRansacMinSet只是粗略解,
+ // 这里之所以要Refine就是要用所有满足模型的内点来更加精确地近似表达模型
+ // 这样求出来的解才会更加准确
+ for (size_t i = 0; i < vIndices.size(); i++)
+ {
+ int idx = vIndices[i];
- Eigen::VectorXd r(2 * numObservations);
- Eigen::VectorXd rd(2 * numObservations);
- Eigen::MatrixXd Jac(2 * numObservations, numUnknowns);
- Eigen::VectorXd g(numUnknowns, 1);
- Eigen::VectorXd dx(numUnknowns, 1); // result vector
+ bearingVecs.push_back(mvBearingVecs[idx]);
+ p3DS.push_back(mvP3Dw[idx]);
+ indexes.push_back(i);
+ }
+ // 后面操作和iterate类似,就不赘述了
- Jac.setZero();
- r.setZero();
- dx.setZero();
- g.setZero();
+ // By the moment, we are using MLPnP without covariance info
+ cov3_mats_t covs(1);
- int it_cnt = 0;
- bool stop = false;
- const int maxIt = 5;
- double epsP = 1e-5;
+ // Result
+ transformation_t result;
- Eigen::MatrixXd JacTSKll;
- Eigen::MatrixXd A;
- // solve simple gradient descent
- while (it_cnt < maxIt && !stop) {
- mlpnp_residuals_and_jacs(x, pts,
- nullspaces,
- r, Jac, true);
+ // Compute camera pose
+ computePose(bearingVecs, p3DS, covs, indexes, result);
- if (use_cov)
- JacTSKll = Jac.transpose() * Kll;
- else
- JacTSKll = Jac.transpose();
+ // Check inliers
+ CheckInliers();
- A = JacTSKll * Jac;
+ mnRefinedInliers = mnInliersi;
+ mvbRefinedInliers = mvbInliersi;
- // get system matrix
- g = JacTSKll * r;
+ if (mnInliersi > mRansacMinInliers)
+ {
+ cv::Mat Rcw(3, 3, CV_64F, mRi);
+ cv::Mat tcw(3, 1, CV_64F, mti);
+ Rcw.convertTo(Rcw, CV_32F);
+ tcw.convertTo(tcw, CV_32F);
+ mRefinedTcw.setIdentity();
- // solve
- Eigen::LDLT chol(A);
- dx = chol.solve(g);
- // this is to prevent the solution from falling into a wrong minimum
- // if the linear estimate is spurious
- if (dx.array().abs().maxCoeff() > 5.0 || dx.array().abs().minCoeff() > 1.0)
- break;
- // observation update
- Eigen::MatrixXd dl = Jac * dx;
- if (dl.array().abs().maxCoeff() < epsP) {
- stop = true;
- x = x - dx;
- break;
- } else
- x = x - dx;
+ mRefinedTcw.block<3, 3>(0, 0) = Converter::toMatrix3f(Rcw);
+ mRefinedTcw.block<3, 1>(0, 3) = Converter::toVector3f(tcw);
- ++it_cnt;
- }//while
- // result
+ Eigen::Matrix eigRcw(mRi[0]);
+ Eigen::Vector3d eigtcw(mti);
+ return true;
}
- void MLPnPsolver::mlpnp_residuals_and_jacs(const Eigen::VectorXd &x, const points_t &pts,
- const std::vector &nullspaces, Eigen::VectorXd &r,
- Eigen::MatrixXd &fjac, bool getJacs) {
- rodrigues_t w(x[0], x[1], x[2]);
- translation_t T(x[3], x[4], x[5]);
+ return false;
+}
- rotation_t R = rodrigues2rot(w);
- int ii = 0;
+// MLPnP methods
+/**
+ * @brief MLPnP相机位姿估计
+ *
+ * @param[in] f 单位向量
+ * @param[in] p 点的3D坐标
+ * @param[in] covMats 协方差矩阵
+ * @param[in] indices 对应点的索引值
+ * @param[in] result 相机位姿估计结果
+ *
+ */
+void MLPnPsolver::computePose(const bearingVectors_t &f, const points_t &p, const cov3_mats_t &covMats,
+ const std::vector &indices, transformation_t &result)
+{
+ // Step 1: 判断点的数量是否满足计算条件,否则直接报错
+ // 因为每个观测值会产生2个残差,所以至少需要6个点来计算公式12,所以要检验当前的点个数是否满足大于5的条件
+ size_t numberCorrespondences = indices.size();
+ // 当numberCorrespondences不满足>5的条件时会发生错误(如果小于6根本进不来)
+ assert(numberCorrespondences > 5);
- Eigen::MatrixXd jacs(2, 6);
+ // ? 用来标记是否满足平面条件,(平面情况下矩阵有相关性,秩为2,矩阵形式可以简化,但需要跟多的约束求解)
+ bool planar = false;
+ // compute the nullspace of all vectors
- for (int i = 0; i < pts.size(); ++i)
+ // compute the nullspace of all vectors
+ // step 2: 计算点的单位(方向向量)向量的零空间
+ // 利用公式7 Jvr(v) = null(v^T) = [r s]
+
+ // 给每个向量都开辟一个零空间,所以数量相等
+ std::vector nullspaces(numberCorrespondences);
+
+ // 存储世界坐标系下空间点的矩阵,3行N列,N是numberCorrespondences,即点的总个数
+ // |x1, x2, xn|
+ // points3 = |y1, y2, ..., yn|
+ // |z1, z2, zn|
+ Eigen::MatrixXd points3(3, numberCorrespondences);
+
+ // 空间点向量
+ // |xi|
+ // points3v = |yi|
+ // |zi|
+ points_t points3v(numberCorrespondences);
+
+ // 单个空间点的齐次坐标矩阵,TODO:没用到啊
+ // |xi|
+ // points4v = |yi|
+ // |zi|
+ // |1 |
+ points4_t points4v(numberCorrespondences);
+
+ // numberCorrespondences不等于所有点,而是提取出来的内点的数量,其作为连续索引值对indices进行索引
+ // 因为内点的索引并非连续,想要方便遍历,必须用连续的索引值,
+ // 所以就用了indices[i]嵌套形式,i表示内点数量numberCorrespondences范围内的连续形式
+ // indices里面保存的是不连续的内点的索引值
+ for (size_t i = 0; i < numberCorrespondences; i++)
+ {
+ // 当前空间点的单位向量,indices[i]是当前点坐标和向量的索引值,
+ bearingVector_t f_current = f[indices[i]];
+
+ // 取出当前点记录到 points3 空间点矩阵里
+ points3.col(i) = p[indices[i]];
+
+ // nullspace of right vector
+ // 求解方程 Jvr(v) = null(v^T) = [r s]
+ // A = U * S * V^T
+ // 这里只求解了V的完全解,没有求解U
+ Eigen::JacobiSVD
+ svd_f(f_current.transpose(), Eigen::ComputeFullV);
+
+ // 取特征值最小的那两个对应的2个特征向量
+ // |r1 s1|
+ // nullspaces = |r2 s2|
+ // |r3 s3|
+ nullspaces[i] = svd_f.matrixV().block(0, 1, 3, 2);
+ // 取出当前点记录到 points3v 空间点向量
+ points3v[i] = p[indices[i]];
+ }
+ // Step 3: 通过计算S的秩来判断是在平面情况还是在正常情况
+ // 令S = M * M^T,其中M = [p1,p2,...,pi],即 points3 空间点矩阵
+ //////////////////////////////////////
+ // 1. test if we have a planar scene
+ // 在平面条件下,会产生4个解,因此需要另外判断和解决平面条件下的问题
+ //////////////////////////////////////
+
+ // 令S = M * M^T,其中M = [p1,p2,...,pi],即 points3 空间点矩阵
+ // 如果矩阵S的秩为3且最小特征值不接近于0,则不属于平面条件,否则需要解决一下
+ Eigen::Matrix3d planarTest = points3 * points3.transpose();
+
+ // 为了计算矩阵S的秩,需要对矩阵S进行满秩的QR分解,通过其结果来判断矩阵S的秩,从而判断是否是平面条件
+ Eigen::FullPivHouseholderQR rankTest(planarTest);
+ // int r, c;
+ // double minEigenVal = abs(eigen_solver.eigenvalues().real().minCoeff(&r, &c));
+ // 特征旋转矩阵,用在平面条件下的计算
+ Eigen::Matrix3d eigenRot;
+ eigenRot.setIdentity();
+
+ // if yes -> transform points to new eigen frame
+ // if (minEigenVal < 1e-3 || minEigenVal == 0.0)
+ // rankTest.setThreshold(1e-10);
+ // 当矩阵S的秩为2时,属于平面条件,
+ if (rankTest.rank() == 2)
+ {
+ planar = true;
+ // self adjoint is faster and more accurate than general eigen solvers
+ // also has closed form solution for 3x3 self-adjoint matrices
+ // in addition this solver sorts the eigenvalues in increasing order
+ // 计算矩阵S的特征值和特征向量
+ Eigen::SelfAdjointEigenSolver eigen_solver(planarTest);
+
+ // 得到QR分解的结果
+ eigenRot = eigen_solver.eigenvectors().real();
+
+ // 把eigenRot变成其转置矩阵,即论文公式20的系数 R_S^T
+ eigenRot.transposeInPlace();
+
+ // 公式20: pi' = R_S^T * pi
+ for (size_t i = 0; i < numberCorrespondences; i++)
+ points3.col(i) = eigenRot * points3.col(i);
+ }
+ //////////////////////////////////////
+ // 2. stochastic model
+ //////////////////////////////////////
+ // Step 4: 计算随机模型中的协方差矩阵
+ // 但是作者并没有用到协方差信息
+ Eigen::SparseMatrix P(2 * numberCorrespondences,
+ 2 * numberCorrespondences);
+ bool use_cov = false;
+ P.setIdentity(); // standard
+
+ // if we do have covariance information
+ // -> fill covariance matrix
+ // 如果协方差矩阵的个数等于空间点的个数,说明前面已经计算好了,表示有协方差信息
+ // 目前版本是没有用到协方差信息的,所以调用本函数前就把协方差矩阵个数置为1了
+ if (covMats.size() == numberCorrespondences)
+ {
+ use_cov = true;
+ int l = 0;
+ for (size_t i = 0; i < numberCorrespondences; ++i)
{
- Eigen::Vector3d ptCam = R*pts[i] + T;
- ptCam /= ptCam.norm();
+ // invert matrix
+ cov2_mat_t temp = nullspaces[i].transpose() * covMats[i] * nullspaces[i];
+ temp = temp.inverse().eval();
+ P.coeffRef(l, l) = temp(0, 0);
+ P.coeffRef(l, l + 1) = temp(0, 1);
+ P.coeffRef(l + 1, l) = temp(1, 0);
+ P.coeffRef(l + 1, l + 1) = temp(1, 1);
+ l += 2;
+ }
+ }
- r[ii] = nullspaces[i].col(0).transpose()*ptCam;
- r[ii + 1] = nullspaces[i].col(1).transpose()*ptCam;
- if (getJacs)
+ // Step 5: 构造矩阵A来完成线性方程组的构建
+ //////////////////////////////////////
+ // 3. fill the design matrix A
+ //////////////////////////////////////
+ // 公式12,设矩阵A,则有 Au = 0
+ // u = [r11, r12, r13, r21, r22, r23, r31, r32, r33, t1, t2, t3]^T
+ // 对单位向量v的2个零空间向量做微分,所以有[dr, ds]^T,一个点有2行,N个点就有2*N行
+ const int rowsA = 2 * numberCorrespondences;
+
+ // 对应上面u的列数,因为旋转矩阵有9个元素,加上平移矩阵3个元素,总共12个元素
+ int colsA = 12;
+ Eigen::MatrixXd A;
+
+ // 如果世界点位于分别跨2个坐标轴的平面上,即所有世界点的一个元素是常数的时候,可简单地忽略矩阵A中对应的列
+ // 而且这不影响问题的结构本身,所以在计算公式20: pi' = R_S^T * pi的时候,忽略了r11,r21,r31,即第一列
+ // 对应的u只有9个元素 u = [r12, r13, r22, r23, r32, r33, t1, t2, t3]^T 所以A的列个数是9个
+ if (planar)
+ {
+ colsA = 9;
+ A = Eigen::MatrixXd(rowsA, 9);
+ }
+ else
+ A = Eigen::MatrixXd(rowsA, 12);
+ A.setZero();
+
+ // fill design matrix
+ // 构造矩阵A,分平面和非平面2种情况
+ if (planar)
+ {
+ for (size_t i = 0; i < numberCorrespondences; ++i)
+ {
+ // 列表示当前点的坐标
+ point_t pt3_current = points3.col(i);
+
+ // r12 r12 的系数 r1*py 和 s1*py
+ A(2 * i, 0) = nullspaces[i](0, 0) * pt3_current[1];
+ A(2 * i + 1, 0) = nullspaces[i](0, 1) * pt3_current[1];
+ // r13 r13 的系数 r1*pz 和 s1*pz
+ A(2 * i, 1) = nullspaces[i](0, 0) * pt3_current[2];
+ A(2 * i + 1, 1) = nullspaces[i](0, 1) * pt3_current[2];
+ // r22 r22 的系数 r2*py 和 s2*py
+ A(2 * i, 2) = nullspaces[i](1, 0) * pt3_current[1];
+ A(2 * i + 1, 2) = nullspaces[i](1, 1) * pt3_current[1];
+ // r23 r23 的系数 r2*pz 和 s2*pz
+ A(2 * i, 3) = nullspaces[i](1, 0) * pt3_current[2];
+ A(2 * i + 1, 3) = nullspaces[i](1, 1) * pt3_current[2];
+ // r32 r32 的系数 r3*py 和 s3*py
+ A(2 * i, 4) = nullspaces[i](2, 0) * pt3_current[1];
+ A(2 * i + 1, 4) = nullspaces[i](2, 1) * pt3_current[1];
+ // r33 r33 的系数 r3*pz 和 s3*pz
+ A(2 * i, 5) = nullspaces[i](2, 0) * pt3_current[2];
+ A(2 * i + 1, 5) = nullspaces[i](2, 1) * pt3_current[2];
+ // t1 t1 的系数 r1 和 s1
+ A(2 * i, 6) = nullspaces[i](0, 0);
+ A(2 * i + 1, 6) = nullspaces[i](0, 1);
+ // t2 t2 的系数 r2 和 s2
+ A(2 * i, 7) = nullspaces[i](1, 0);
+ A(2 * i + 1, 7) = nullspaces[i](1, 1);
+ // t3 t3 的系数 r3 和 s3
+ A(2 * i, 8) = nullspaces[i](2, 0);
+ A(2 * i + 1, 8) = nullspaces[i](2, 1);
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < numberCorrespondences; ++i)
+ {
+ point_t pt3_current = points3.col(i);
+
+ // 不是平面的情况下,三个列向量都保留求解
+ // r11
+ A(2 * i, 0) = nullspaces[i](0, 0) * pt3_current[0];
+ A(2 * i + 1, 0) = nullspaces[i](0, 1) * pt3_current[0];
+ // r12
+ A(2 * i, 1) = nullspaces[i](0, 0) * pt3_current[1];
+ A(2 * i + 1, 1) = nullspaces[i](0, 1) * pt3_current[1];
+ // r13
+ A(2 * i, 2) = nullspaces[i](0, 0) * pt3_current[2];
+ A(2 * i + 1, 2) = nullspaces[i](0, 1) * pt3_current[2];
+ // r21
+ A(2 * i, 3) = nullspaces[i](1, 0) * pt3_current[0];
+ A(2 * i + 1, 3) = nullspaces[i](1, 1) * pt3_current[0];
+ // r22
+ A(2 * i, 4) = nullspaces[i](1, 0) * pt3_current[1];
+ A(2 * i + 1, 4) = nullspaces[i](1, 1) * pt3_current[1];
+ // r23
+ A(2 * i, 5) = nullspaces[i](1, 0) * pt3_current[2];
+ A(2 * i + 1, 5) = nullspaces[i](1, 1) * pt3_current[2];
+ // r31
+ A(2 * i, 6) = nullspaces[i](2, 0) * pt3_current[0];
+ A(2 * i + 1, 6) = nullspaces[i](2, 1) * pt3_current[0];
+ // r32
+ A(2 * i, 7) = nullspaces[i](2, 0) * pt3_current[1];
+ A(2 * i + 1, 7) = nullspaces[i](2, 1) * pt3_current[1];
+ // r33
+ A(2 * i, 8) = nullspaces[i](2, 0) * pt3_current[2];
+ A(2 * i + 1, 8) = nullspaces[i](2, 1) * pt3_current[2];
+ // t1
+ A(2 * i, 9) = nullspaces[i](0, 0);
+ A(2 * i + 1, 9) = nullspaces[i](0, 1);
+ // t2
+ A(2 * i, 10) = nullspaces[i](1, 0);
+ A(2 * i + 1, 10) = nullspaces[i](1, 1);
+ // t3
+ A(2 * i, 11) = nullspaces[i](2, 0);
+ A(2 * i + 1, 11) = nullspaces[i](2, 1);
+ }
+ }
+
+ // Step 6: 计算线性方程组的最小二乘解
+ //////////////////////////////////////
+ // 4. solve least squares
+ //////////////////////////////////////
+ // 求解方程的最小二乘解
+ Eigen::MatrixXd AtPA;
+ if (use_cov)
+ // 有协方差信息的情况下,一般方程是 A^T*P*A*u = N*u = 0
+ AtPA = A.transpose() * P * A; // setting up the full normal equations seems to be unstable
+ else
+ // 无协方差信息的情况下,一般方程是 A^T*A*u = N*u = 0
+ AtPA = A.transpose() * A;
+
+ // SVD分解,满秩求解V
+ Eigen::JacobiSVD svd_A(AtPA, Eigen::ComputeFullV);
+
+ // 解就是对应奇异值最小的列向量,即最后一列
+ Eigen::MatrixXd result1 = svd_A.matrixV().col(colsA - 1);
+
+ // Step 7: 根据平面和非平面情况下选择最终位姿解
+ ////////////////////////////////
+ // now we treat the results differently,
+ // depending on the scene (planar or not)
+ ////////////////////////////////
+ // transformation_t T_final;
+ rotation_t Rout;
+ translation_t tout;
+ if (planar) // planar case
+ {
+ rotation_t tmp;
+ // until now, we only estimated
+ // row one and two of the transposed rotation matrix
+ // 暂时只估计了旋转矩阵的第1行和第2行,先记录到tmp中
+ tmp << 0.0, result1(0, 0), result1(1, 0),
+ 0.0, result1(2, 0), result1(3, 0),
+ 0.0, result1(4, 0), result1(5, 0);
+ // double scale = 1 / sqrt(tmp.col(1).norm() * tmp.col(2).norm());
+ // row 3
+ // 第3行等于第1行和第2行的叉积(这里应该是列,后面转置后成了行)
+ tmp.col(0) = tmp.col(1).cross(tmp.col(2));
+
+ // 原来是:
+ // |r11 r12 r13|
+ // tmp = |r21 r22 r23|
+ // |r31 r32 r33|
+ // 转置变成:
+ // |r11 r21 r31|
+ // tmp = |r12 r22 r32|
+ // |r13 r23 r33|
+ tmp.transposeInPlace();
+
+ // 平移部分 t 只表示了正确的方向,没有尺度,需要求解 scale, 先求系数
+ double scale = 1.0 / std::sqrt(std::abs(tmp.col(1).norm() * tmp.col(2).norm()));
+ // find best rotation matrix in frobenius sense
+ // 利用Frobenious范数计算最佳的旋转矩阵,利用公式(19), R = U_R*V_R^T
+ // 本质上,采用矩阵,将其元素平方,将它们加在一起并对结果平方根。计算得出的数字是矩阵的Frobenious范数
+ // 由于列向量是单列矩阵,行向量是单行矩阵,所以这些矩阵的Frobenius范数等于向量的长度(L)
+ Eigen::JacobiSVD svd_R_frob(tmp, Eigen::ComputeFullU | Eigen::ComputeFullV);
+
+ rotation_t Rout1 = svd_R_frob.matrixU() * svd_R_frob.matrixV().transpose();
+
+ // test if we found a good rotation matrix
+ // 如果估计出来的旋转矩阵的行列式小于0,则乘以-1(EPnP也是同样的操作)
+ if (Rout1.determinant() < 0)
+ Rout1 *= -1.0;
+
+ // rotate this matrix back using the eigen frame
+ // 因为是在平面情况下计算的,估计出来的旋转矩阵是要做一个转换的,根据公式(21),R = Rs*R
+ // 其中,Rs表示特征向量的旋转矩阵
+ // 注意eigenRot之前已经转置过了transposeInPlace(),所以这里Rout1在之前也转置了,即tmp.transposeInPlace()
+ Rout1 = eigenRot.transpose() * Rout1;
+
+ // 估计最终的平移矩阵,带尺度信息的,根据公式(17),t = t^ / three-party(||r1||*||r2||*||r3||)
+ // 这里是 t = t^ / sqrt(||r1||*||r2||)
+ translation_t t = scale * translation_t(result1(6, 0), result1(7, 0), result1(8, 0));
+
+ // 把之前转置过来的矩阵再转回去,变成公式里面的形态:
+ // |r11 r12 r13|
+ // Rout1 = |r21 r22 r23|
+ // |r31 r32 r33|
+ Rout1.transposeInPlace();
+
+ // 这里乘以-1是为了计算4种结果
+ Rout1 *= -1;
+ if (Rout1.determinant() < 0.0)
+ Rout1.col(2) *= -1;
+ // now we have to find the best out of 4 combinations
+ // |r11 r12 r13|
+ // R1 = |r21 r22 r23|
+ // |r31 r32 r33|
+ // |-r11 -r12 -r13|
+ // R2 = |-r21 -r22 -r23|
+ // |-r31 -r32 -r33|
+ rotation_t R1, R2;
+ R1.col(0) = Rout1.col(0);
+ R1.col(1) = Rout1.col(1);
+ R1.col(2) = Rout1.col(2);
+ R2.col(0) = -Rout1.col(0);
+ R2.col(1) = -Rout1.col(1);
+ R2.col(2) = Rout1.col(2);
+
+ // |R1 t|
+ // Ts = |R1 -t|
+ // |R2 t|
+ // |R2 -t|
+ vector> Ts(4);
+ Ts[0].block<3, 3>(0, 0) = R1;
+ Ts[0].block<3, 1>(0, 3) = t;
+ Ts[1].block<3, 3>(0, 0) = R1;
+ Ts[1].block<3, 1>(0, 3) = -t;
+ Ts[2].block<3, 3>(0, 0) = R2;
+ Ts[2].block<3, 1>(0, 3) = t;
+ Ts[3].block<3, 3>(0, 0) = R2;
+ Ts[3].block<3, 1>(0, 3) = -t;
+
+ // 遍历4种解
+ vector normVal(4);
+ for (int i = 0; i < 4; ++i)
+ {
+ point_t reproPt;
+ double norms = 0.0;
+ // 计算世界点p到切线空间v的投影的残差,对应最小的就是最好的解
+ // 用前6个点来验证4种解的残差
+ for (int p = 0; p < 6; ++p)
{
- // jacs
- mlpnpJacs(pts[i],
- nullspaces[i].col(0), nullspaces[i].col(1),
- w, T,
- jacs);
-
- // r
- fjac(ii, 0) = jacs(0, 0);
- fjac(ii, 1) = jacs(0, 1);
- fjac(ii, 2) = jacs(0, 2);
-
- fjac(ii, 3) = jacs(0, 3);
- fjac(ii, 4) = jacs(0, 4);
- fjac(ii, 5) = jacs(0, 5);
- // s
- fjac(ii + 1, 0) = jacs(1, 0);
- fjac(ii + 1, 1) = jacs(1, 1);
- fjac(ii + 1, 2) = jacs(1, 2);
-
- fjac(ii + 1, 3) = jacs(1, 3);
- fjac(ii + 1, 4) = jacs(1, 4);
- fjac(ii + 1, 5) = jacs(1, 5);
-
+ // 重投影的向量
+ reproPt = Ts[i].block<3, 3>(0, 0) * points3v[p] + Ts[i].block<3, 1>(0, 3);
+ // 变成单位向量
+ reproPt = reproPt / reproPt.norm();
+ // f[indices[p]] 是当前空间点的单位向量
+ // 利用欧氏距离来表示重投影向量(观测)和当前空间点向量(实际)的偏差
+ // 即两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的欧氏距离
+ norms += (1.0 - reproPt.transpose() * f[indices[p]]);
}
- ii += 2;
+ // 统计每种解的误差和,第i个解的误差和放入对应的变量normVal[i]
+ normVal[i] = norms;
}
+
+ // 搜索容器中的最小值,并返回该值对应的指针
+ std::vector::iterator
+ findMinRepro = std::min_element(std::begin(normVal), std::end(normVal));
+
+ // 计算容器头指针到最小值指针的距离,即可作为该最小值的索引值
+ int idx = std::distance(std::begin(normVal), findMinRepro);
+
+ // 得到最终相机位姿估计的结果
+ Rout = Ts[idx].block<3, 3>(0, 0);
+ tout = Ts[idx].block<3, 1>(0, 3);
+ }
+ else // non-planar
+ {
+ rotation_t tmp;
+ // 从AtPA的SVD分解中得到旋转矩阵,先存下来
+ // 注意这里的顺序是和公式16不同的
+ // |r11 r21 r31|
+ // tmp = |r12 r22 r32|
+ // |r13 r23 r33|
+ tmp << result1(0, 0), result1(3, 0), result1(6, 0),
+ result1(1, 0), result1(4, 0), result1(7, 0),
+ result1(2, 0), result1(5, 0), result1(8, 0);
+ // get the scale
+ // 计算尺度,根据公式(17),t = t^ / three-party(||r1||*||r2||*||r3||)
+ double scale = 1.0 /
+ std::pow(std::abs(tmp.col(0).norm() * tmp.col(1).norm() * tmp.col(2).norm()), 1.0 / 3.0);
+
+ // double scale = 1.0 / std::sqrt(std::abs(tmp.col(0).norm() * tmp.col(1).norm()));
+ // find best rotation matrix in frobenius sense
+ // 利用Frobenious范数计算最佳的旋转矩阵,利用公式(19), R = U_R*V_R^T
+ Eigen::JacobiSVD svd_R_frob(tmp, Eigen::ComputeFullU | Eigen::ComputeFullV);
+ Rout = svd_R_frob.matrixU() * svd_R_frob.matrixV().transpose();
+
+ // test if we found a good rotation matrix
+ // 如果估计出来的旋转矩阵的行列式小于0,则乘以-1
+ if (Rout.determinant() < 0)
+ Rout *= -1.0;
+
+ // scale translation
+ // 从相机坐标系到世界坐标系的转换关系是 lambda*v = R*pi+t
+ // 从世界坐标系到相机坐标系的转换关系是 pi = R^T*v-R^Tt
+ // 旋转矩阵的性质 R^-1 = R^T
+ // 所以,在下面的计算中,需要计算从世界坐标系到相机坐标系的转换,这里tout = -R^T*t,下面再计算前半部分R^T*v
+ // 先恢复平移部分的尺度再计算
+ tout = Rout * (scale * translation_t(result1(9, 0), result1(10, 0), result1(11, 0)));
+
+ // find correct direction in terms of reprojection error, just take the first 6 correspondences
+ // 非平面情况下,一共有2种解,R,t和R,-t
+ // 利用前6个点计算重投影误差,选择残差最小的一个解
+ vector error(2);
+ vector> Ts(2);
+ for (int s = 0; s < 2; ++s)
+ {
+ // 初始化error的值为0
+ error[s] = 0.0;
+
+ // |1 0 0 0|
+ // Ts[s] = |0 1 0 0|
+ // |0 0 1 0|
+ // |0 0 0 1|
+ Ts[s] = Eigen::Matrix4d::Identity();
+
+ // |. . . 0|
+ // Ts[s] = |. Rout . 0|
+ // |. . . 0|
+ // |0 0 0 1|
+ Ts[s].block<3, 3>(0, 0) = Rout;
+ if (s == 0)
+ // |. . . . |
+ // Ts[s] = |. Rout . tout|
+ // |. . . . |
+ // |0 0 0 1 |
+ Ts[s].block<3, 1>(0, 3) = tout;
+ else
+ // |. . . . |
+ // Ts[s] = |. Rout . -tout|
+ // |. . . . |
+ // |0 0 0 1 |
+ Ts[s].block<3, 1>(0, 3) = -tout;
+
+ // 为了避免Eigen中aliasing的问题,后面在计算矩阵的逆的时候,需要添加eval()条件
+ // a = a.transpose(); //error: aliasing
+ // a = a.transpose().eval(); //ok
+ // a.transposeInPlace(); //ok
+ // Eigen中aliasing指的是在赋值表达式的左右两边存在矩阵的重叠区域,这种情况下,有可能得到非预期的结果。
+ // 如mat = 2*mat或者mat = mat.transpose(),第一个例子中的alias是没有问题的,而第二的例子则会导致非预期的计算结果。
+ Ts[s] = Ts[s].inverse().eval();
+ for (int p = 0; p < 6; ++p)
+ {
+ // 从世界坐标系到相机坐标系的转换关系是 pi = R^T*v-R^Tt
+ // Ts[s].block<3, 3>(0, 0) * points3v[p] = Rout = R^T*v
+ // Ts[s].block<3, 1>(0, 3) = tout = -R^Tt
+ bearingVector_t v = Ts[s].block<3, 3>(0, 0) * points3v[p] + Ts[s].block<3, 1>(0, 3);
+ // 变成单位向量
+ v = v / v.norm();
+ // 计算重投影向量(观测)和当前空间点向量(实际)的偏差
+ error[s] += (1.0 - v.transpose() * f[indices[p]]);
+ }
+ }
+ // 选择残差最小的解作为最终解
+ if (error[0] < error[1])
+ tout = Ts[0].block<3, 1>(0, 3);
+ else
+ tout = Ts[1].block<3, 1>(0, 3);
+ Rout = Ts[0].block<3, 3>(0, 0);
}
- void MLPnPsolver::mlpnpJacs(const point_t& pt, const Eigen::Vector3d& nullspace_r,
- const Eigen::Vector3d& nullspace_s, const rodrigues_t& w,
- const translation_t& t, Eigen::MatrixXd& jacs){
- double r1 = nullspace_r[0];
- double r2 = nullspace_r[1];
- double r3 = nullspace_r[2];
+ // Step 8: 利用高斯牛顿法对位姿进行精确求解,提高位姿解的精度
+ //////////////////////////////////////
+ // 5. gauss newton
+ //////////////////////////////////////
+ // 求解非线性方程之前,需要得到罗德里格斯参数,来表达李群(SO3) -> 李代数(so3)的对数映射
+ rodrigues_t omega = rot2rodrigues(Rout);
+ // |r1|
+ // |r2|
+ // minx = |r3|
+ // |t1|
+ // |t2|
+ // |t3|
+ Eigen::VectorXd minx(6);
+ minx[0] = omega[0];
+ minx[1] = omega[1];
+ minx[2] = omega[2];
+ minx[3] = tout[0];
+ minx[4] = tout[1];
+ minx[5] = tout[2];
- double s1 = nullspace_s[0];
- double s2 = nullspace_s[1];
- double s3 = nullspace_s[2];
+ // 利用高斯牛顿迭代法来提炼相机位姿 pose
+ mlpnp_gn(minx, points3v, nullspaces, P, use_cov);
- double X1 = pt[0];
- double Y1 = pt[1];
- double Z1 = pt[2];
+ // 最终输出的结果
+ Rout = rodrigues2rot(rodrigues_t(minx[0], minx[1], minx[2]));
+ tout = translation_t(minx[3], minx[4], minx[5]);
- double w1 = w[0];
- double w2 = w[1];
- double w3 = w[2];
+ // result inverse as opengv uses this convention
+ // 这里是用来计算世界坐标系到相机坐标系的转换,所以是Pc=R^T*Pw-R^T*t,反变换
+ result.block<3, 3>(0, 0) = Rout; // Rout.transpose();
+ result.block<3, 1>(0, 3) = tout; //-result.block<3, 3>(0, 0) * tout;
+}
- double t1 = t[0];
- double t2 = t[1];
- double t3 = t[2];
+/**
+ * @brief 旋转向量转换成旋转矩阵,即李群->李代数
+ * @param[in] omega 旋转向量
+ * @param[out] R 旋转矩阵
+ */
+Eigen::Matrix3d MLPnPsolver::rodrigues2rot(const Eigen::Vector3d &omega)
+{
+ // 初始化旋转矩阵
+ rotation_t R = Eigen::Matrix3d::Identity();
- double t5 = w1*w1;
- double t6 = w2*w2;
- double t7 = w3*w3;
- double t8 = t5+t6+t7;
- double t9 = sqrt(t8);
- double t10 = sin(t9);
- double t11 = 1.0/sqrt(t8);
- double t12 = cos(t9);
- double t13 = t12-1.0;
- double t14 = 1.0/t8;
- double t16 = t10*t11*w3;
- double t17 = t13*t14*w1*w2;
- double t19 = t10*t11*w2;
- double t20 = t13*t14*w1*w3;
- double t24 = t6+t7;
- double t27 = t16+t17;
- double t28 = Y1*t27;
- double t29 = t19-t20;
- double t30 = Z1*t29;
- double t31 = t13*t14*t24;
- double t32 = t31+1.0;
- double t33 = X1*t32;
- double t15 = t1-t28+t30+t33;
- double t21 = t10*t11*w1;
- double t22 = t13*t14*w2*w3;
- double t45 = t5+t7;
- double t53 = t16-t17;
- double t54 = X1*t53;
- double t55 = t21+t22;
- double t56 = Z1*t55;
- double t57 = t13*t14*t45;
- double t58 = t57+1.0;
- double t59 = Y1*t58;
- double t18 = t2+t54-t56+t59;
- double t34 = t5+t6;
- double t38 = t19+t20;
- double t39 = X1*t38;
- double t40 = t21-t22;
- double t41 = Y1*t40;
- double t42 = t13*t14*t34;
- double t43 = t42+1.0;
- double t44 = Z1*t43;
- double t23 = t3-t39+t41+t44;
- double t25 = 1.0/pow(t8,3.0/2.0);
- double t26 = 1.0/(t8*t8);
- double t35 = t12*t14*w1*w2;
- double t36 = t5*t10*t25*w3;
- double t37 = t5*t13*t26*w3*2.0;
- double t46 = t10*t25*w1*w3;
- double t47 = t5*t10*t25*w2;
- double t48 = t5*t13*t26*w2*2.0;
- double t49 = t10*t11;
- double t50 = t5*t12*t14;
- double t51 = t13*t26*w1*w2*w3*2.0;
- double t52 = t10*t25*w1*w2*w3;
- double t60 = t15*t15;
- double t61 = t18*t18;
- double t62 = t23*t23;
- double t63 = t60+t61+t62;
- double t64 = t5*t10*t25;
- double t65 = 1.0/sqrt(t63);
- double t66 = Y1*r2*t6;
- double t67 = Z1*r3*t7;
- double t68 = r1*t1*t5;
- double t69 = r1*t1*t6;
- double t70 = r1*t1*t7;
- double t71 = r2*t2*t5;
- double t72 = r2*t2*t6;
- double t73 = r2*t2*t7;
- double t74 = r3*t3*t5;
- double t75 = r3*t3*t6;
- double t76 = r3*t3*t7;
- double t77 = X1*r1*t5;
- double t78 = X1*r2*w1*w2;
- double t79 = X1*r3*w1*w3;
- double t80 = Y1*r1*w1*w2;
- double t81 = Y1*r3*w2*w3;
- double t82 = Z1*r1*w1*w3;
- double t83 = Z1*r2*w2*w3;
- double t84 = X1*r1*t6*t12;
- double t85 = X1*r1*t7*t12;
- double t86 = Y1*r2*t5*t12;
- double t87 = Y1*r2*t7*t12;
- double t88 = Z1*r3*t5*t12;
- double t89 = Z1*r3*t6*t12;
- double t90 = X1*r2*t9*t10*w3;
- double t91 = Y1*r3*t9*t10*w1;
- double t92 = Z1*r1*t9*t10*w2;
- double t102 = X1*r3*t9*t10*w2;
- double t103 = Y1*r1*t9*t10*w3;
- double t104 = Z1*r2*t9*t10*w1;
- double t105 = X1*r2*t12*w1*w2;
- double t106 = X1*r3*t12*w1*w3;
- double t107 = Y1*r1*t12*w1*w2;
- double t108 = Y1*r3*t12*w2*w3;
- double t109 = Z1*r1*t12*w1*w3;
- double t110 = Z1*r2*t12*w2*w3;
- double t93 = t66+t67+t68+t69+t70+t71+t72+t73+t74+t75+t76+t77+t78+t79+t80+t81+t82+t83+t84+t85+t86+t87+t88+t89+t90+t91+t92-t102-t103-t104-t105-t106-t107-t108-t109-t110;
- double t94 = t10*t25*w1*w2;
- double t95 = t6*t10*t25*w3;
- double t96 = t6*t13*t26*w3*2.0;
- double t97 = t12*t14*w2*w3;
- double t98 = t6*t10*t25*w1;
- double t99 = t6*t13*t26*w1*2.0;
- double t100 = t6*t10*t25;
- double t101 = 1.0/pow(t63,3.0/2.0);
- double t111 = t6*t12*t14;
- double t112 = t10*t25*w2*w3;
- double t113 = t12*t14*w1*w3;
- double t114 = t7*t10*t25*w2;
- double t115 = t7*t13*t26*w2*2.0;
- double t116 = t7*t10*t25*w1;
- double t117 = t7*t13*t26*w1*2.0;
- double t118 = t7*t12*t14;
- double t119 = t13*t24*t26*w1*2.0;
- double t120 = t10*t24*t25*w1;
- double t121 = t119+t120;
- double t122 = t13*t26*t34*w1*2.0;
- double t123 = t10*t25*t34*w1;
- double t131 = t13*t14*w1*2.0;
- double t124 = t122+t123-t131;
- double t139 = t13*t14*w3;
- double t125 = -t35+t36+t37+t94-t139;
- double t126 = X1*t125;
- double t127 = t49+t50+t51+t52-t64;
- double t128 = Y1*t127;
- double t129 = t126+t128-Z1*t124;
- double t130 = t23*t129*2.0;
- double t132 = t13*t26*t45*w1*2.0;
- double t133 = t10*t25*t45*w1;
- double t138 = t13*t14*w2;
- double t134 = -t46+t47+t48+t113-t138;
- double t135 = X1*t134;
- double t136 = -t49-t50+t51+t52+t64;
- double t137 = Z1*t136;
- double t140 = X1*s1*t5;
- double t141 = Y1*s2*t6;
- double t142 = Z1*s3*t7;
- double t143 = s1*t1*t5;
- double t144 = s1*t1*t6;
- double t145 = s1*t1*t7;
- double t146 = s2*t2*t5;
- double t147 = s2*t2*t6;
- double t148 = s2*t2*t7;
- double t149 = s3*t3*t5;
- double t150 = s3*t3*t6;
- double t151 = s3*t3*t7;
- double t152 = X1*s2*w1*w2;
- double t153 = X1*s3*w1*w3;
- double t154 = Y1*s1*w1*w2;
- double t155 = Y1*s3*w2*w3;
- double t156 = Z1*s1*w1*w3;
- double t157 = Z1*s2*w2*w3;
- double t158 = X1*s1*t6*t12;
- double t159 = X1*s1*t7*t12;
- double t160 = Y1*s2*t5*t12;
- double t161 = Y1*s2*t7*t12;
- double t162 = Z1*s3*t5*t12;
- double t163 = Z1*s3*t6*t12;
- double t164 = X1*s2*t9*t10*w3;
- double t165 = Y1*s3*t9*t10*w1;
- double t166 = Z1*s1*t9*t10*w2;
- double t183 = X1*s3*t9*t10*w2;
- double t184 = Y1*s1*t9*t10*w3;
- double t185 = Z1*s2*t9*t10*w1;
- double t186 = X1*s2*t12*w1*w2;
- double t187 = X1*s3*t12*w1*w3;
- double t188 = Y1*s1*t12*w1*w2;
- double t189 = Y1*s3*t12*w2*w3;
- double t190 = Z1*s1*t12*w1*w3;
- double t191 = Z1*s2*t12*w2*w3;
- double t167 = t140+t141+t142+t143+t144+t145+t146+t147+t148+t149+t150+t151+t152+t153+t154+t155+t156+t157+t158+t159+t160+t161+t162+t163+t164+t165+t166-t183-t184-t185-t186-t187-t188-t189-t190-t191;
- double t168 = t13*t26*t45*w2*2.0;
- double t169 = t10*t25*t45*w2;
- double t170 = t168+t169;
- double t171 = t13*t26*t34*w2*2.0;
- double t172 = t10*t25*t34*w2;
- double t176 = t13*t14*w2*2.0;
- double t173 = t171+t172-t176;
- double t174 = -t49+t51+t52+t100-t111;
- double t175 = X1*t174;
- double t177 = t13*t24*t26*w2*2.0;
- double t178 = t10*t24*t25*w2;
- double t192 = t13*t14*w1;
- double t179 = -t97+t98+t99+t112-t192;
- double t180 = Y1*t179;
- double t181 = t49+t51+t52-t100+t111;
- double t182 = Z1*t181;
- double t193 = t13*t26*t34*w3*2.0;
- double t194 = t10*t25*t34*w3;
- double t195 = t193+t194;
- double t196 = t13*t26*t45*w3*2.0;
- double t197 = t10*t25*t45*w3;
- double t200 = t13*t14*w3*2.0;
- double t198 = t196+t197-t200;
- double t199 = t7*t10*t25;
- double t201 = t13*t24*t26*w3*2.0;
- double t202 = t10*t24*t25*w3;
- double t203 = -t49+t51+t52-t118+t199;
- double t204 = Y1*t203;
- double t205 = t1*2.0;
- double t206 = Z1*t29*2.0;
- double t207 = X1*t32*2.0;
- double t208 = t205+t206+t207-Y1*t27*2.0;
- double t209 = t2*2.0;
- double t210 = X1*t53*2.0;
- double t211 = Y1*t58*2.0;
- double t212 = t209+t210+t211-Z1*t55*2.0;
- double t213 = t3*2.0;
- double t214 = Y1*t40*2.0;
- double t215 = Z1*t43*2.0;
- double t216 = t213+t214+t215-X1*t38*2.0;
- jacs(0, 0) = t14*t65*(X1*r1*w1*2.0+X1*r2*w2+X1*r3*w3+Y1*r1*w2+Z1*r1*w3+r1*t1*w1*2.0+r2*t2*w1*2.0+r3*t3*w1*2.0+Y1*r3*t5*t12+Y1*r3*t9*t10-Z1*r2*t5*t12-Z1*r2*t9*t10-X1*r2*t12*w2-X1*r3*t12*w3-Y1*r1*t12*w2+Y1*r2*t12*w1*2.0-Z1*r1*t12*w3+Z1*r3*t12*w1*2.0+Y1*r3*t5*t10*t11-Z1*r2*t5*t10*t11+X1*r2*t12*w1*w3-X1*r3*t12*w1*w2-Y1*r1*t12*w1*w3+Z1*r1*t12*w1*w2-Y1*r1*t10*t11*w1*w3+Z1*r1*t10*t11*w1*w2-X1*r1*t6*t10*t11*w1-X1*r1*t7*t10*t11*w1+X1*r2*t5*t10*t11*w2+X1*r3*t5*t10*t11*w3+Y1*r1*t5*t10*t11*w2-Y1*r2*t5*t10*t11*w1-Y1*r2*t7*t10*t11*w1+Z1*r1*t5*t10*t11*w3-Z1*r3*t5*t10*t11*w1-Z1*r3*t6*t10*t11*w1+X1*r2*t10*t11*w1*w3-X1*r3*t10*t11*w1*w2+Y1*r3*t10*t11*w1*w2*w3+Z1*r2*t10*t11*w1*w2*w3)-t26*t65*t93*w1*2.0-t14*t93*t101*(t130+t15*(-X1*t121+Y1*(t46+t47+t48-t13*t14*w2-t12*t14*w1*w3)+Z1*(t35+t36+t37-t13*t14*w3-t10*t25*w1*w2))*2.0+t18*(t135+t137-Y1*(t132+t133-t13*t14*w1*2.0))*2.0)*(1.0/2.0);
- jacs(0, 1) = t14*t65*(X1*r2*w1+Y1*r1*w1+Y1*r2*w2*2.0+Y1*r3*w3+Z1*r2*w3+r1*t1*w2*2.0+r2*t2*w2*2.0+r3*t3*w2*2.0-X1*r3*t6*t12-X1*r3*t9*t10+Z1*r1*t6*t12+Z1*r1*t9*t10+X1*r1*t12*w2*2.0-X1*r2*t12*w1-Y1*r1*t12*w1-Y1*r3*t12*w3-Z1*r2*t12*w3+Z1*r3*t12*w2*2.0-X1*r3*t6*t10*t11+Z1*r1*t6*t10*t11+X1*r2*t12*w2*w3-Y1*r1*t12*w2*w3+Y1*r3*t12*w1*w2-Z1*r2*t12*w1*w2-Y1*r1*t10*t11*w2*w3+Y1*r3*t10*t11*w1*w2-Z1*r2*t10*t11*w1*w2-X1*r1*t6*t10*t11*w2+X1*r2*t6*t10*t11*w1-X1*r1*t7*t10*t11*w2+Y1*r1*t6*t10*t11*w1-Y1*r2*t5*t10*t11*w2-Y1*r2*t7*t10*t11*w2+Y1*r3*t6*t10*t11*w3-Z1*r3*t5*t10*t11*w2+Z1*r2*t6*t10*t11*w3-Z1*r3*t6*t10*t11*w2+X1*r2*t10*t11*w2*w3+X1*r3*t10*t11*w1*w2*w3+Z1*r1*t10*t11*w1*w2*w3)-t26*t65*t93*w2*2.0-t14*t93*t101*(t18*(Z1*(-t35+t94+t95+t96-t13*t14*w3)-Y1*t170+X1*(t97+t98+t99-t13*t14*w1-t10*t25*w2*w3))*2.0+t15*(t180+t182-X1*(t177+t178-t13*t14*w2*2.0))*2.0+t23*(t175+Y1*(t35-t94+t95+t96-t13*t14*w3)-Z1*t173)*2.0)*(1.0/2.0);
- jacs(0, 2) = t14*t65*(X1*r3*w1+Y1*r3*w2+Z1*r1*w1+Z1*r2*w2+Z1*r3*w3*2.0+r1*t1*w3*2.0+r2*t2*w3*2.0+r3*t3*w3*2.0+X1*r2*t7*t12+X1*r2*t9*t10-Y1*r1*t7*t12-Y1*r1*t9*t10+X1*r1*t12*w3*2.0-X1*r3*t12*w1+Y1*r2*t12*w3*2.0-Y1*r3*t12*w2-Z1*r1*t12*w1-Z1*r2*t12*w2+X1*r2*t7*t10*t11-Y1*r1*t7*t10*t11-X1*r3*t12*w2*w3+Y1*r3*t12*w1*w3+Z1*r1*t12*w2*w3-Z1*r2*t12*w1*w3+Y1*r3*t10*t11*w1*w3+Z1*r1*t10*t11*w2*w3-Z1*r2*t10*t11*w1*w3-X1*r1*t6*t10*t11*w3-X1*r1*t7*t10*t11*w3+X1*r3*t7*t10*t11*w1-Y1*r2*t5*t10*t11*w3-Y1*r2*t7*t10*t11*w3+Y1*r3*t7*t10*t11*w2+Z1*r1*t7*t10*t11*w1+Z1*r2*t7*t10*t11*w2-Z1*r3*t5*t10*t11*w3-Z1*r3*t6*t10*t11*w3-X1*r3*t10*t11*w2*w3+X1*r2*t10*t11*w1*w2*w3+Y1*r1*t10*t11*w1*w2*w3)-t26*t65*t93*w3*2.0-t14*t93*t101*(t18*(Z1*(t46-t113+t114+t115-t13*t14*w2)-Y1*t198+X1*(t49+t51+t52+t118-t7*t10*t25))*2.0+t23*(X1*(-t97+t112+t116+t117-t13*t14*w1)+Y1*(-t46+t113+t114+t115-t13*t14*w2)-Z1*t195)*2.0+t15*(t204+Z1*(t97-t112+t116+t117-t13*t14*w1)-X1*(t201+t202-t13*t14*w3*2.0))*2.0)*(1.0/2.0);
- jacs(0, 3) = r1*t65-t14*t93*t101*t208*(1.0/2.0);
- jacs(0, 4) = r2*t65-t14*t93*t101*t212*(1.0/2.0);
- jacs(0, 5) = r3*t65-t14*t93*t101*t216*(1.0/2.0);
- jacs(1, 0) = t14*t65*(X1*s1*w1*2.0+X1*s2*w2+X1*s3*w3+Y1*s1*w2+Z1*s1*w3+s1*t1*w1*2.0+s2*t2*w1*2.0+s3*t3*w1*2.0+Y1*s3*t5*t12+Y1*s3*t9*t10-Z1*s2*t5*t12-Z1*s2*t9*t10-X1*s2*t12*w2-X1*s3*t12*w3-Y1*s1*t12*w2+Y1*s2*t12*w1*2.0-Z1*s1*t12*w3+Z1*s3*t12*w1*2.0+Y1*s3*t5*t10*t11-Z1*s2*t5*t10*t11+X1*s2*t12*w1*w3-X1*s3*t12*w1*w2-Y1*s1*t12*w1*w3+Z1*s1*t12*w1*w2+X1*s2*t10*t11*w1*w3-X1*s3*t10*t11*w1*w2-Y1*s1*t10*t11*w1*w3+Z1*s1*t10*t11*w1*w2-X1*s1*t6*t10*t11*w1-X1*s1*t7*t10*t11*w1+X1*s2*t5*t10*t11*w2+X1*s3*t5*t10*t11*w3+Y1*s1*t5*t10*t11*w2-Y1*s2*t5*t10*t11*w1-Y1*s2*t7*t10*t11*w1+Z1*s1*t5*t10*t11*w3-Z1*s3*t5*t10*t11*w1-Z1*s3*t6*t10*t11*w1+Y1*s3*t10*t11*w1*w2*w3+Z1*s2*t10*t11*w1*w2*w3)-t14*t101*t167*(t130+t15*(Y1*(t46+t47+t48-t113-t138)+Z1*(t35+t36+t37-t94-t139)-X1*t121)*2.0+t18*(t135+t137-Y1*(-t131+t132+t133))*2.0)*(1.0/2.0)-t26*t65*t167*w1*2.0;
- jacs(1, 1) = t14*t65*(X1*s2*w1+Y1*s1*w1+Y1*s2*w2*2.0+Y1*s3*w3+Z1*s2*w3+s1*t1*w2*2.0+s2*t2*w2*2.0+s3*t3*w2*2.0-X1*s3*t6*t12-X1*s3*t9*t10+Z1*s1*t6*t12+Z1*s1*t9*t10+X1*s1*t12*w2*2.0-X1*s2*t12*w1-Y1*s1*t12*w1-Y1*s3*t12*w3-Z1*s2*t12*w3+Z1*s3*t12*w2*2.0-X1*s3*t6*t10*t11+Z1*s1*t6*t10*t11+X1*s2*t12*w2*w3-Y1*s1*t12*w2*w3+Y1*s3*t12*w1*w2-Z1*s2*t12*w1*w2+X1*s2*t10*t11*w2*w3-Y1*s1*t10*t11*w2*w3+Y1*s3*t10*t11*w1*w2-Z1*s2*t10*t11*w1*w2-X1*s1*t6*t10*t11*w2+X1*s2*t6*t10*t11*w1-X1*s1*t7*t10*t11*w2+Y1*s1*t6*t10*t11*w1-Y1*s2*t5*t10*t11*w2-Y1*s2*t7*t10*t11*w2+Y1*s3*t6*t10*t11*w3-Z1*s3*t5*t10*t11*w2+Z1*s2*t6*t10*t11*w3-Z1*s3*t6*t10*t11*w2+X1*s3*t10*t11*w1*w2*w3+Z1*s1*t10*t11*w1*w2*w3)-t26*t65*t167*w2*2.0-t14*t101*t167*(t18*(X1*(t97+t98+t99-t112-t192)+Z1*(-t35+t94+t95+t96-t139)-Y1*t170)*2.0+t15*(t180+t182-X1*(-t176+t177+t178))*2.0+t23*(t175+Y1*(t35-t94+t95+t96-t139)-Z1*t173)*2.0)*(1.0/2.0);
- jacs(1, 2) = t14*t65*(X1*s3*w1+Y1*s3*w2+Z1*s1*w1+Z1*s2*w2+Z1*s3*w3*2.0+s1*t1*w3*2.0+s2*t2*w3*2.0+s3*t3*w3*2.0+X1*s2*t7*t12+X1*s2*t9*t10-Y1*s1*t7*t12-Y1*s1*t9*t10+X1*s1*t12*w3*2.0-X1*s3*t12*w1+Y1*s2*t12*w3*2.0-Y1*s3*t12*w2-Z1*s1*t12*w1-Z1*s2*t12*w2+X1*s2*t7*t10*t11-Y1*s1*t7*t10*t11-X1*s3*t12*w2*w3+Y1*s3*t12*w1*w3+Z1*s1*t12*w2*w3-Z1*s2*t12*w1*w3-X1*s3*t10*t11*w2*w3+Y1*s3*t10*t11*w1*w3+Z1*s1*t10*t11*w2*w3-Z1*s2*t10*t11*w1*w3-X1*s1*t6*t10*t11*w3-X1*s1*t7*t10*t11*w3+X1*s3*t7*t10*t11*w1-Y1*s2*t5*t10*t11*w3-Y1*s2*t7*t10*t11*w3+Y1*s3*t7*t10*t11*w2+Z1*s1*t7*t10*t11*w1+Z1*s2*t7*t10*t11*w2-Z1*s3*t5*t10*t11*w3-Z1*s3*t6*t10*t11*w3+X1*s2*t10*t11*w1*w2*w3+Y1*s1*t10*t11*w1*w2*w3)-t26*t65*t167*w3*2.0-t14*t101*t167*(t18*(Z1*(t46-t113+t114+t115-t138)-Y1*t198+X1*(t49+t51+t52+t118-t199))*2.0+t23*(X1*(-t97+t112+t116+t117-t192)+Y1*(-t46+t113+t114+t115-t138)-Z1*t195)*2.0+t15*(t204+Z1*(t97-t112+t116+t117-t192)-X1*(-t200+t201+t202))*2.0)*(1.0/2.0);
- jacs(1, 3) = s1*t65-t14*t101*t167*t208*(1.0/2.0);
- jacs(1, 4) = s2*t65-t14*t101*t167*t212*(1.0/2.0);
- jacs(1, 5) = s3*t65-t14*t101*t167*t216*(1.0/2.0);
+ // 求旋转向量的反对称矩阵
+ Eigen::Matrix3d skewW;
+ skewW << 0.0, -omega(2), omega(1),
+ omega(2), 0.0, -omega(0),
+ -omega(1), omega(0), 0.0;
+
+ // 求旋转向量的角度
+ double omega_norm = omega.norm();
+
+ // 通过罗德里格斯公式把旋转向量转换成旋转矩阵
+ if (omega_norm > std::numeric_limits::epsilon())
+ R = R + sin(omega_norm) / omega_norm * skewW + (1 - cos(omega_norm)) / (omega_norm * omega_norm) * (skewW * skewW);
+
+ return R;
+}
+
+/**
+ * @brief 旋转矩阵转换成旋转向量,即李代数->李群
+ * @param[in] R 旋转矩阵
+ * @param[out] omega 旋转向量
+ 问题: 李群(SO3) -> 李代数(so3)的对数映射
+ 利用罗德里格斯公式,已知旋转矩阵的情况下,求解其对数映射ln(R)
+ 就像《视觉十四讲》里面说到的,使用李代数的一大动机是为了进行优化,而在优化过程中导数是非常必要的信息
+ 李群SO(3)中完成两个矩阵乘法,不能用李代数so(3)中的加法表示,问题描述为两个李代数对数映射乘积
+ 而两个李代数对数映射乘积的完整形式,可以用BCH公式表达,其中式子(左乘BCH近似雅可比J)可近似表达,该式也就是罗德里格斯公式
+ 计算完罗德里格斯参数之后,就可以用非线性优化方法了,里面就要用到导数,其形式就是李代数指数映射
+ 所以要在调用非线性MLPnP算法之前计算罗德里格斯参数
+ */
+Eigen::Vector3d MLPnPsolver::rot2rodrigues(const Eigen::Matrix3d &R)
+{
+ rodrigues_t omega;
+ omega << 0.0, 0.0, 0.0;
+
+ // R.trace() 矩阵的迹,即该矩阵的特征值总和
+ double trace = R.trace() - 1.0;
+
+ // 李群(SO3) -> 李代数(so3)的对数映射
+ // 对数映射ln(R)∨将一个旋转矩阵转换为一个李代数,但由于对数的泰勒展开不是很优雅所以用本式
+ // wnorm是求解出来的角度
+ double wnorm = acos(trace / 2.0);
+
+ // 如果wnorm大于运行编译程序的计算机所能识别的最小非零浮点数,则可以生成向量,否则为0
+ if (wnorm > std::numeric_limits::epsilon())
+ {
+ // |r11 r21 r31|
+ // R = |r12 r22 r32|
+ // |r13 r23 r33|
+ omega[0] = (R(2, 1) - R(1, 2));
+ omega[1] = (R(0, 2) - R(2, 0));
+ omega[2] = (R(1, 0) - R(0, 1));
+ // theta |r23 - r32|
+ // ln(R) = ------------ * |r31 - r13|
+ // 2*sin(theta) |r12 - r21|
+ double sc = wnorm / (2.0 * sin(wnorm));
+ omega *= sc;
}
-}//End namespace ORB_SLAM2
+ return omega;
+}
+
+/**
+ * @brief MLPnP的高斯牛顿解法
+ * @param[in] x 未知量矩阵
+ * @param[in] pts 3D点矩阵
+ * @param[in] nullspaces 零空间向量
+ * @param[in] Kll 协方差矩阵
+ * @param[in] use_cov 协方差方法使用标记位
+ */
+void MLPnPsolver::mlpnp_gn(Eigen::VectorXd &x, const points_t &pts, const std::vector &nullspaces,
+ const Eigen::SparseMatrix Kll, bool use_cov)
+{
+ // 计算观测值数量
+ const int numObservations = pts.size();
+
+ // 未知量是旋转向量和平移向量,即R和t,总共6个未知参数
+ const int numUnknowns = 6;
+
+ // check redundancy
+ // 检查观测数量是否满足计算条件,因为每个观测值都提供了2个约束,即r和s,所以这里乘以2
+ assert((2 * numObservations - numUnknowns) > 0);
+
+ // =============
+ // set all matrices up
+ // =============
+
+ Eigen::VectorXd r(2 * numObservations);
+ Eigen::VectorXd rd(2 * numObservations);
+ Eigen::MatrixXd Jac(2 * numObservations, numUnknowns);
+ Eigen::VectorXd g(numUnknowns, 1);
+ Eigen::VectorXd dx(numUnknowns, 1); // result vector
+
+ Jac.setZero();
+ r.setZero();
+ dx.setZero();
+ g.setZero();
+
+ int it_cnt = 0;
+ bool stop = false;
+ const int maxIt = 5;
+ double epsP = 1e-5;
+
+ Eigen::MatrixXd JacTSKll;
+ Eigen::MatrixXd A;
+ // solve simple gradient descent
+ while (it_cnt < maxIt && !stop)
+ {
+ mlpnp_residuals_and_jacs(x, pts,
+ nullspaces,
+ r, Jac, true);
+
+ if (use_cov)
+ JacTSKll = Jac.transpose() * Kll;
+ else
+ JacTSKll = Jac.transpose();
+
+ A = JacTSKll * Jac;
+
+ // get system matrix
+ g = JacTSKll * r;
+
+ // solve
+ Eigen::LDLT chol(A);
+ dx = chol.solve(g);
+ // dx = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(g);
+ // this is to prevent the solution from falling into a wrong minimum
+ // if the linear estimate is spurious
+ if (dx.array().abs().maxCoeff() > 5.0 || dx.array().abs().minCoeff() > 1.0)
+ break;
+ // observation update
+ Eigen::MatrixXd dl = Jac * dx;
+ if (dl.array().abs().maxCoeff() < epsP)
+ {
+ stop = true;
+ x = x - dx;
+ break;
+ }
+ else
+ x = x - dx;
+
+ ++it_cnt;
+ } // while
+ // result
+}
+
+/**
+ * @brief 计算Jacobian矩阵和残差
+ * @param[in] x 未知量矩阵
+ * @param[in] pts 3D点矩阵
+ * @param[in] nullspaces 零空间向量
+ * @param[in] r f(x)函数
+ * @param[out] fjac f(x)函数的Jacobian矩阵
+ * @param[in] getJacs 是否可以得到Jacobian矩阵标记位
+ */
+void MLPnPsolver::mlpnp_residuals_and_jacs(const Eigen::VectorXd &x, const points_t &pts,
+ const std::vector &nullspaces, Eigen::VectorXd &r,
+ Eigen::MatrixXd &fjac, bool getJacs)
+{
+ rodrigues_t w(x[0], x[1], x[2]);
+ translation_t T(x[3], x[4], x[5]);
+
+ // rotation_t R = math::cayley2rot(c);
+ rotation_t R = rodrigues2rot(w);
+ int ii = 0;
+
+ Eigen::MatrixXd jacs(2, 6);
+
+ for (int i = 0; i < pts.size(); ++i)
+ {
+ Eigen::Vector3d ptCam = R * pts[i] + T;
+ ptCam /= ptCam.norm();
+
+ r[ii] = nullspaces[i].col(0).transpose() * ptCam;
+ r[ii + 1] = nullspaces[i].col(1).transpose() * ptCam;
+ if (getJacs)
+ {
+ // jacs
+ mlpnpJacs(pts[i],
+ nullspaces[i].col(0), nullspaces[i].col(1),
+ w, T,
+ jacs);
+
+ // r
+ fjac(ii, 0) = jacs(0, 0);
+ fjac(ii, 1) = jacs(0, 1);
+ fjac(ii, 2) = jacs(0, 2);
+
+ fjac(ii, 3) = jacs(0, 3);
+ fjac(ii, 4) = jacs(0, 4);
+ fjac(ii, 5) = jacs(0, 5);
+ // s
+ fjac(ii + 1, 0) = jacs(1, 0);
+ fjac(ii + 1, 1) = jacs(1, 1);
+ fjac(ii + 1, 2) = jacs(1, 2);
+
+ fjac(ii + 1, 3) = jacs(1, 3);
+ fjac(ii + 1, 4) = jacs(1, 4);
+ fjac(ii + 1, 5) = jacs(1, 5);
+ }
+ ii += 2;
+ }
+}
+
+/**
+ * @brief 计算Jacobian矩阵
+ * @param[in] pt 3D点矩阵
+ * @param[in] nullspace_r 零空间向量r
+ * @param[in] nullspace_r 零空间向量s
+ * @param[in] w 旋转向量w
+ * @param[in] t 平移向量t
+ * @param[out] jacs Jacobian矩阵
+ */
+void MLPnPsolver::mlpnpJacs(const point_t &pt, const Eigen::Vector3d &nullspace_r,
+ const Eigen::Vector3d &nullspace_s, const rodrigues_t &w,
+ const translation_t &t, Eigen::MatrixXd &jacs)
+{
+ double r1 = nullspace_r[0];
+ double r2 = nullspace_r[1];
+ double r3 = nullspace_r[2];
+
+ double s1 = nullspace_s[0];
+ double s2 = nullspace_s[1];
+ double s3 = nullspace_s[2];
+
+ double X1 = pt[0];
+ double Y1 = pt[1];
+ double Z1 = pt[2];
+
+ double w1 = w[0];
+ double w2 = w[1];
+ double w3 = w[2];
+
+ double t1 = t[0];
+ double t2 = t[1];
+ double t3 = t[2];
+
+ double t5 = w1 * w1;
+ double t6 = w2 * w2;
+ double t7 = w3 * w3;
+
+ // t8 = theta^2 = w1^2+w2^2+w3^2
+ double t8 = t5 + t6 + t7;
+ // t9 = sqrt(t8) = sqrt(theta^2) = theta
+ double t9 = sqrt(t8);
+ // t10 = sin(theta)
+ double t10 = sin(t9);
+ // t11 = 1 / theta
+ double t11 = 1.0 / sqrt(t8);
+ // t12 = cos(theta)
+ double t12 = cos(t9);
+ // t13 = cos(theta) - 1
+ double t13 = t12 - 1.0;
+ // t14 = 1 / theta^2
+ double t14 = 1.0 / t8;
+ // t16 = sin(theta)/theta*w3
+ // 令 A = sin(theta)/theta = t10*t11
+ // t16 = A*w3
+ double t16 = t10 * t11 * w3;
+ // t17 = (cos(theta) - 1)/theta^2 * w1 * w2
+ // 令 B = (cos(theta) - 1)/theta^2 = t13*t14
+ // t17 = B*w1*w2
+ double t17 = t13 * t14 * w1 * w2;
+ // t19 = sin(theta)/theta*w2
+ // = A*w2
+ double t19 = t10 * t11 * w2;
+ // t20 = (cos(theta) - 1) / theta^2 * w1 * w3
+ // = B*w1*w3
+ double t20 = t13 * t14 * w1 * w3;
+ // t24 = w2^2+w3^2
+ double t24 = t6 + t7;
+ // t27 = A*w3 + B*w1*w2
+ // = -r12
+ double t27 = t16 + t17;
+ // t28 = (A*w3 + B*w1*w2)*Y1
+ // = -r12*Y1
+ double t28 = Y1 * t27;
+ // t29 = A*w2 - B*w1*w3
+ // = r13
+ double t29 = t19 - t20;
+ // t30 = (A*w2 - B*w1*w3) * Z1
+ // = r13 * Z1
+ double t30 = Z1 * t29;
+ // t31 = (cos(theta) - 1) /theta^2 * (w2^2+w3^2)
+ // = B * (w2^2+w3^2)
+ double t31 = t13 * t14 * t24;
+ // t32 = B * (w2^2+w3^2) + 1
+ // = r11
+ double t32 = t31 + 1.0;
+ // t33 = (B * (w2^2+w3^2) + 1) * X1
+ // = r11 * X1
+ double t33 = X1 * t32;
+ // t15 = t1 - (A*w3 + B*w1*w2)*Y1 + (A*w2 - B*w1*w3) * Z1 + (B * (w2^2+w3^2) + 1) * X1
+ // = t1 + r11*X1 + r12*Y1 + r13*Z1
+ double t15 = t1 - t28 + t30 + t33;
+ // t21 = t10 * t11 * w1 = sin(theta) / theta * w1
+ // = A*w1
+ double t21 = t10 * t11 * w1;
+ // t22 = t13 * t14 * w2 * w3 = (cos(theta) - 1) / theta^2 * w2 * w3
+ // = B*w2*w3
+ double t22 = t13 * t14 * w2 * w3;
+ // t45 = t5 + t7
+ // = w1^2 + w3^2
+ double t45 = t5 + t7;
+ // t53 = t16 - t17
+ // = A*w3 - B*w1*w2 = r21
+ double t53 = t16 - t17;
+ // t54 = r21*X1
+ double t54 = X1 * t53;
+ // t55 = A*w1 + B*w2*w3
+ // = -r23
+ double t55 = t21 + t22;
+ // t56 = -r23*Z1
+ double t56 = Z1 * t55;
+ // t57 = t13 * t14 * t45 = (cos(theta) - 1) / theta^2 * (w1^2 + w3^2)
+ // = B8(w1^2+w3^2)
+ double t57 = t13 * t14 * t45;
+ // t58 = B8(w1^2+w3^2) + 1.0
+ // = r22
+ double t58 = t57 + 1.0;
+ // t59 = r22*Y1
+ double t59 = Y1 * t58;
+ // t18 = t2 + t54 - t56 + t59
+ // = t2 + r21*X1 + r22*Y1 + r23*Z1
+ double t18 = t2 + t54 - t56 + t59;
+ // t34 = t5 + t6
+ // = w1^2+w2^2
+ double t34 = t5 + t6;
+ // t38 = t19 + t20 = A*w2+B*w1*w3
+ // = -r31
+ double t38 = t19 + t20;
+ // t39 = -r31*X1
+ double t39 = X1 * t38;
+ // t40 = A*w1 - B*w2*w3
+ // = r32
+ double t40 = t21 - t22;
+ // t41 = r32*Y1
+ double t41 = Y1 * t40;
+ // t42 = t13 * t14 * t34 = (cos(theta) - 1) / theta^2 * (w1^2+w2^2)
+ // = B*(w1^2+w2^2)
+ double t42 = t13 * t14 * t34;
+ // t43 = B*(w1^2+w2^2) + 1
+ // = r33
+ double t43 = t42 + 1.0;
+ // t44 = r33*Z1
+ double t44 = Z1 * t43;
+ // t23 = t3 - t39 + t41 + t44 = t3 + r31*X1 + r32*Y1 + r33*Z1
+ double t23 = t3 - t39 + t41 + t44;
+ // t25 = 1.0 / pow(theta^2, 3.0 / 2.0)
+ // = 1 / theta^3
+ double t25 = 1.0 / pow(t8, 3.0 / 2.0);
+ // t26 = 1.0 / (t8 * t8)
+ // = 1 / theta^4
+ double t26 = 1.0 / (t8 * t8);
+ // t35 = t12 * t14 * w1 * w2 = cos(theta) / theta^2 * w1 * w2
+ // 令 cos(theta) / theta^2 = E = t12*t14
+ // t35 = E*w1*w2
+ double t35 = t12 * t14 * w1 * w2;
+ // t36 = t5 * t10 * t25 * w3 = w1^2 * sin(theta) / theta^3 * w3
+ // 令 sin(theta) / theta^3 = F = t10*t25
+ // t36 = F*w1^2*w3
+ double t36 = t5 * t10 * t25 * w3;
+ // t37 = t5 * t13 * t26 * w3 * 2.0 = w1^2 * (cos(theta) - 1) / theta^4 * w3
+ // 令 (cos(theta) - 1) / theta^4 = G = t13*t26
+ // t37 = G*w1^2*w3
+ double t37 = t5 * t13 * t26 * w3 * 2.0;
+ // t46 = t10 * t25 * w1 * w3 = sin(theta) / theta^3 * w1 * w3 = F*w1*w3
+ double t46 = t10 * t25 * w1 * w3;
+ // t47 = t5 * t10 * t25 * w2 = w1^2 * sin(theta) / theta^3 * w2 = F*w1^2*w2
+ double t47 = t5 * t10 * t25 * w2;
+ // t48 = t5 * t13 * t26 * w2 * 2.0 = w1^2 * (cos(theta) - 1) / theta^4 * 2 * w2 = G*w1^2*w2
+ double t48 = t5 * t13 * t26 * w2 * 2.0;
+ // t49 = t10 * t11 = sin(theta) / theta = A
+ double t49 = t10 * t11;
+ // t50 = t5 * t12 * t14 = w1^2 * cos(theta) / theta^2 = E*w1^2
+ double t50 = t5 * t12 * t14;
+ // t51 = t13 * t26 * w1 * w2 * w3 * 2.0 = (cos(theta) - 1) / theta^4 * w1 * w2 * w3 * 2 = 2*G*w1*w2*w3
+ double t51 = t13 * t26 * w1 * w2 * w3 * 2.0;
+ // t52 = t10 * t25 * w1 * w2 * w3 = sin(theta) / theta^3 * w1 * w2 * w3 = F*w1*w2*w3
+ double t52 = t10 * t25 * w1 * w2 * w3;
+ // t60 = t15 * t15 = (t1 + r11*X1 + r12*Y1 + r13*Z1)^2
+ double t60 = t15 * t15;
+ // t61 = t18 * t18 = (t2 + r21*X1 + r22*Y1 + r23*Z1)^2
+ double t61 = t18 * t18;
+ // t62 = t23 * t23 = (t3 + r31*X1 + r32*Y1 + r33*Z1)^2
+ double t62 = t23 * t23;
+ // t63 = t60 + t61 + t62 = (t1 + r11*X1 + r12*Y1 + r13*Z1)^2 + (t2 + r21*X1 + r22*Y1 + r23*Z1)^2 + (t3 + r31*X1 + r32*Y1 + r33*Z1)^2
+ double t63 = t60 + t61 + t62;
+ // t64 = t5 * t10 * t25 = w1^2 * sin(theta) / theta^3 = F*w1^2
+ double t64 = t5 * t10 * t25;
+ // t65 = 1 / sqrt((t1 + r11*X1 + r12*Y1 + r13*Z1)^2 + (t2 + r21*X1 + r22*Y1 + r23*Z1)^2 + (t3 + r31*X1 + r32*Y1 + r33*Z1)^2)
+ double t65 = 1.0 / sqrt(t63);
+ // t66 = Y1 * r2 * t6 = Y1 * r2 * w2^2
+ double t66 = Y1 * r2 * t6;
+ // t67 = Z1 * r3 * t7 = Z1 * r3 * w3^2
+ double t67 = Z1 * r3 * t7;
+ // t68 = r1 * t1 * t5 = r1 * t1 * w1^2
+ double t68 = r1 * t1 * t5;
+ // t69 = r1 * t1 * t6 = r1 * t1 * w2^2
+ double t69 = r1 * t1 * t6;
+ // t70 = r1 * t1 * t7 = r1 * t1 * w3^2
+ double t70 = r1 * t1 * t7;
+ // t71 = r2 * t2 * t5 = r2 * t2 * w1^2
+ double t71 = r2 * t2 * t5;
+ // t72 = r2 * t2 * t6 = r2 * t2 * w2^2
+ double t72 = r2 * t2 * t6;
+ // t73 = r2 * t2 * t7 = r2 * t2 * w3^2
+ double t73 = r2 * t2 * t7;
+ // t74 = r3 * t3 * t5 = r3 * t3 * w1^2
+ double t74 = r3 * t3 * t5;
+ // t75 = r3 * t3 * t6 = r3 * t3 * w2^2
+ double t75 = r3 * t3 * t6;
+ // t76 = r3 * t3 * t7 = r3 * t3 * w3^2
+ double t76 = r3 * t3 * t7;
+ // t77 = X1 * r1 * t5 = X1 * r1 * w1^2
+ double t77 = X1 * r1 * t5;
+ double t78 = X1 * r2 * w1 * w2;
+ double t79 = X1 * r3 * w1 * w3;
+ double t80 = Y1 * r1 * w1 * w2;
+ double t81 = Y1 * r3 * w2 * w3;
+ double t82 = Z1 * r1 * w1 * w3;
+ double t83 = Z1 * r2 * w2 * w3;
+ // t84 = X1 * r1 * t6 * t12 = X1 * r1 * w2^2 * cos(theta)
+ double t84 = X1 * r1 * t6 * t12;
+ // t85 = X1 * r1 * t7 * t12 = X1 * r1 * w3^2 * cos(theta)
+ double t85 = X1 * r1 * t7 * t12;
+ // t86 = Y1 * r2 * t5 * t12 = Y1 * r2 * w1^2 * cos(theta)
+ double t86 = Y1 * r2 * t5 * t12;
+ // t87 = Y1 * r2 * t7 * t12 = Y1 * r2 * w3^2 * cos(theta)
+ double t87 = Y1 * r2 * t7 * t12;
+ // t88 = Z1 * r3 * t5 * t12 = Z1 * r3 * w1^2 * cos(theta)
+ double t88 = Z1 * r3 * t5 * t12;
+ // t89 = Z1 * r3 * t6 * t12 = Z1 * r3 * w2^2 * cos(theta)
+ double t89 = Z1 * r3 * t6 * t12;
+ // t90 = X1 * r2 * t9 * t10 * w3 = X1 * r2 * theta * sin(theta) * w3
+ double t90 = X1 * r2 * t9 * t10 * w3;
+ // t91 = Y1 * r3 * t9 * t10 * w1 = Y1 * r3 * theta * sin(theta) * w1
+ double t91 = Y1 * r3 * t9 * t10 * w1;
+ // t92 = Z1 * r1 * t9 * t10 * w2 = Z1 * r1 * theta * sin(theta) * w2
+ double t92 = Z1 * r1 * t9 * t10 * w2;
+ // t102 = X1 * r3 * t9 * t10 * w2 = X1 * r3 * theta * sin(theta) * w2
+ double t102 = X1 * r3 * t9 * t10 * w2;
+ // t103 = Y1 * r1 * t9 * t10 * w3 = Y1 * r1 * theta * sin(theta) * w3
+ double t103 = Y1 * r1 * t9 * t10 * w3;
+ // t104 = Z1 * r2 * t9 * t10 * w1 = Z1 * r2 * theta * sin(theta) * w1
+ double t104 = Z1 * r2 * t9 * t10 * w1;
+ // t105 = X1 * r2 * t12 * w1 * w2 = X1 * r2 * cos(theta) * w1 * w2
+ double t105 = X1 * r2 * t12 * w1 * w2;
+ // t106 = X1 * r3 * t12 * w1 * w3 = X1 * r3 * cos(theta) * w1 * w3
+ double t106 = X1 * r3 * t12 * w1 * w3;
+ // t107 = Y1 * r1 * t12 * w1 * w2 = Y1 * r1 * cos(theta) * w1 * w2
+ double t107 = Y1 * r1 * t12 * w1 * w2;
+ // t108 = Y1 * r3 * t12 * w2 * w3 = Y1 * r3 * cos(theta) * w2 * w3
+ double t108 = Y1 * r3 * t12 * w2 * w3;
+ // t109 = Z1 * r1 * t12 * w1 * w3 = Z1 * r1 * cos(theta) * w1 * w3
+ double t109 = Z1 * r1 * t12 * w1 * w3;
+ // t110 = Z1 * r2 * t12 * w2 * w3 = Z1 * r2 * cos(theta) * w2 * w3
+ double t110 = Z1 * r2 * t12 * w2 * w3;
+ // t93 = t66 + t67 + t68 + t69
+ // + t70 + t71 + t72 + t73 + t74
+ // + t75 + t76 + t77 + t78 + t79
+ // + t80 + t81 + t82 + t83 + t84
+ // + t85 + t86 + t87 + t88 + t89
+ // + t90 + t91 + t92 - t102 - t103
+ // - t104 - t105 - t106 - t107
+ // - t108 - t109 - t110
+ //
+ // = (Y1 * r2 * w2^2) + (Z1 * r3 * w3^2) + (r1 * t1 * w1^2) + (r1 * t1 * w2^2)
+ // + (r1 * t1 * w3^2) + (r2 * t2 * w1^2) + (r2 * t2 * w2^2) + (r2 * t2 * w3^2) + (r3 * t3 * w1^2)
+ // + (r3 * t3 * w2^2) + (r3 * t3 * w3^2) + (X1 * r1 * w1^2) + (X1 * r2 * w1 * w2) + (X1 * r3 * w1 * w3)
+ // + (Y1 * r1 * w1 * w2) + (Y1 * r3 * w2 * w3) + (Z1 * r1 * w1 * w3) + (Z1 * r2 * w2 * w3) + (X1 * r1 * w2^2 * cos(theta))
+ // + (X1 * r1 * w3^2 * cos(theta)) + (Y1 * r2 * w1^2 * cos(theta)) + (Y1 * r2 * w3^2 * cos(theta)) + (Z1 * r3 * w1^2 * cos(theta)) + (Z1 * r3 * w2^2 * cos(theta))
+ // + (X1 * r2 * theta * sin(theta) * w3) + (Y1 * r3 * theta * sin(theta) * w1) + (Z1 * r1 * theta * sin(theta) * w2) - (X1 * r3 * theta * sin(theta) * w2) - (Y1 * r1 * theta * sin(theta) * w3)
+ // - (Z1 * r2 * theta * sin(theta) * w1) - (X1 * r2 * cos(theta) * w1 * w2) - (X1 * r3 * cos(theta) * w1 * w3) - (Y1 * r1 * cos(theta) * w1 * w2) - ()
+ // - (Y1 * r3 * cos(theta) * w2 * w3) - (Z1 * r1 * cos(theta) * w1 * w3) - (Z1 * r2 * cos(theta) * w2 * w3)
+ double t93 =
+ t66 + t67 + t68 + t69 + t70 + t71 + t72 + t73 + t74 + t75 + t76 + t77 + t78 + t79 + t80 + t81 + t82 +
+ t83 + t84 + t85 + t86 + t87 + t88 + t89 + t90 + t91 + t92 - t102 - t103 - t104 - t105 - t106 - t107 -
+ t108 - t109 - t110;
+ // t94 = sin(theta)/theta^3* w1 * w2 = F*w1*w2
+ double t94 = t10 * t25 * w1 * w2;
+ // t95 = w2^2*sin(theta)/theta^3*w3 = F*w2^2*w3
+ double t95 = t6 * t10 * t25 * w3;
+ // t96 = 2 * w2^2 * (cos(theta) - 1) / theta^4 * w3 = 2*G*w2^2*w3
+ double t96 = t6 * t13 * t26 * w3 * 2.0;
+ // t97 = cos(theta) / theta^2 * w2 * w3 = E*w2*w3
+ double t97 = t12 * t14 * w2 * w3;
+ // t98 = w2^2 * sin(theta) / theta^3 * w1 = F*w2^2*w1
+ double t98 = t6 * t10 * t25 * w1;
+ // t99 = 2 * w2^2 * (cos(theta) - 1) / theta^4 * w1 = 2*G*w2^2*w1
+ double t99 = t6 * t13 * t26 * w1 * 2.0;
+ // t100 = w2^2 * sin(theta) / theta^3 = F*w2^2
+ double t100 = t6 * t10 * t25;
+ // t101 = 1.0 / pow(sqrt((t1 + r11*X1 + r12*Y1 + r13*Z1)^2 + (t2 + r21*X1 + r22*Y1 + r23*Z1)^2 + (t3 + r31*X1 + r32*Y1 + r33*Z1)^2), 3.0 / 2.0)
+ // = (t1 + r11*X1 + r12*Y1 + r13*Z1)^2 + (t2 + r21*X1 + r22*Y1 + r23*Z1)^2 + (t3 + r31*X1 + r32*Y1 + r33*Z1)^3
+ double t101 = 1.0 / pow(t63, 3.0 / 2.0);
+ // t111 = w2^2 * cos(theta) / theta^2 = E*w2^2
+ double t111 = t6 * t12 * t14;
+ // t112 = sin(theta) / theta^3 *w2 * w3 = F*w2*w3
+ double t112 = t10 * t25 * w2 * w3;
+ // t113 = cos(theta) / theta^2 * w1 * w3 = E*w1*w3
+ double t113 = t12 * t14 * w1 * w3;
+ // t114 = w3^2 * sin(theta) / theta^3 * w2 = F*w3^2*w2
+ double t114 = t7 * t10 * t25 * w2;
+ // t115 = t7 * t13 * t26 * w2 * 2.0 = 2*w3^2*(cos(theta) - 1) / theta^4*w2
+ double t115 = t7 * t13 * t26 * w2 * 2.0;
+ // t116 = w3^2 * sin(theta) / theta^3 * w1 = F*w3^2*w1
+ double t116 = t7 * t10 * t25 * w1;
+ // t117 = 2 * w3^2 * (cos(theta) - 1) / theta^4 * w1 = 2*G*w3^2*w1
+ double t117 = t7 * t13 * t26 * w1 * 2.0;
+ // t118 = E*w3^2
+ double t118 = t7 * t12 * t14;
+ // t119 = 2*w1*G*(w2^2+w3^2)
+ double t119 = t13 * t24 * t26 * w1 * 2.0;
+ // t120 = F*w1*(w2^2+w3^2)
+ double t120 = t10 * t24 * t25 * w1;
+ // t121 = (2*G+F)*w1*(w2^2+w3^2)
+ double t121 = t119 + t120;
+ // t122 = 2*G*w1*(w1^2+w2^2)
+ double t122 = t13 * t26 * t34 * w1 * 2.0;
+ // t123 = F*w1*(w1^2+w2^2)
+ double t123 = t10 * t25 * t34 * w1;
+ // t131 = 2*(cos(theta) - 1) / theta^2*w1 = 2*B*w1
+ double t131 = t13 * t14 * w1 * 2.0;
+ // t124 = t122 + t123 - t131 = 2*G*w1*(w1^2+w2^2)+F*w1*(w1^2+w2^2)-2*B*w1
+ // = (2*G+F)*w1*(w1^2+w2^2)-2*B*w1
+ double t124 = t122 + t123 - t131;
+ // t139 = t13 * t14 * w3 = B*w3
+ double t139 = t13 * t14 * w3;
+ // t125 = -t35 + t36 + t37 + t94 - t139 = -E*w1*w2+F*w1^2*w3+G*w1^2*w3+F*w1*w2-B*w3
+ double t125 = -t35 + t36 + t37 + t94 - t139;
+ // t126 = (-E*w1*w2+F*w1^2*w3+G*w1^2*w3+F*w1*w2-B*w3)*X1
+ double t126 = X1 * t125;
+ // t127 = t49 + t50 + t51 + t52 - t64 = A + E*w1^2 + (2*G+F)*w1*w2*w3 - F*w1^2
+ double t127 = t49 + t50 + t51 + t52 - t64;
+ // t128 = (A + E*w1^2 + (2*G+F)*w1*w2*w3 - F*w1^2)*Y1
+ double t128 = Y1 * t127;
+ // t129 = (-E*w1*w2+F*w1^2*w3+G*w1^2*w3+F*w1*w2-B*w3)*X1 + (A + E*w1^2 + (2*G+F)*w1*w2*w3 - F*w1^2)*Y1 - ((2*G+F)*w1*(w1^2+w2^2)-2*B*w1)Z1
+ double t129 = t126 + t128 - Z1 * t124;
+ // t130 = 2 * (t3 + r31*X1 + r32*Y1 + r33*Z1)
+ // * ((-E*w1*w2+F*w1^2*w3+G*w1^2*w3+F*w1*w2-B*w3)*X1 + (A + E*w1^2 + (2*G+F)*w1*w2*w3 - F*w1^2)*Y1 - ((2*G+F)*w1*(w1^2+w2^2)-2*B*w1)Z1)
+ double t130 = t23 * t129 * 2.0;
+ // t132 = t13 * t26 * t45 * w1 * 2.0 = 2*w1*G*(w1^2+w3^2)
+ double t132 = t13 * t26 * t45 * w1 * 2.0;
+ // t133 = t10 * t25 * t45 * w1 = F*w1*(w1^2+w3^2)
+ double t133 = t10 * t25 * t45 * w1;
+ // t138 = t13 * t14 * w2 = B*w2
+ double t138 = t13 * t14 * w2;
+ // t134 = -t46 + t47 + t48 + t113 - t138 = -F*w1*w3+F*w1^2*w2+G*w1^2*w2+E*w1*w3-B*w2
+ double t134 = -t46 + t47 + t48 + t113 - t138;
+ // t135 = (-F*w1*w3+F*w1^2*w2+G*w1^2*w2+E*w1*w3-B*w2)*X1
+ double t135 = X1 * t134;
+ // t136 = -t49 - t50 + t51 + t52 + t64 = -A-E*w1^2+2*G*w1*w2*w3+F*w1*w2*w3+F*w1^2
+ double t136 = -t49 - t50 + t51 + t52 + t64;
+ // t137 = (-A-E*w1^2+2*G*w1*w2*w3+F*w1*w2*w3+F*w1^2)*Z1
+ double t137 = Z1 * t136;
+
+ double t140 = X1 * s1 * t5;
+ double t141 = Y1 * s2 * t6;
+ double t142 = Z1 * s3 * t7;
+ double t143 = s1 * t1 * t5;
+ double t144 = s1 * t1 * t6;
+ double t145 = s1 * t1 * t7;
+ double t146 = s2 * t2 * t5;
+ double t147 = s2 * t2 * t6;
+ double t148 = s2 * t2 * t7;
+ double t149 = s3 * t3 * t5;
+ double t150 = s3 * t3 * t6;
+ double t151 = s3 * t3 * t7;
+ double t152 = X1 * s2 * w1 * w2;
+ double t153 = X1 * s3 * w1 * w3;
+ double t154 = Y1 * s1 * w1 * w2;
+ double t155 = Y1 * s3 * w2 * w3;
+ double t156 = Z1 * s1 * w1 * w3;
+ double t157 = Z1 * s2 * w2 * w3;
+ // t12 = cos(theta)
+ double t158 = X1 * s1 * t6 * t12;
+ double t159 = X1 * s1 * t7 * t12;
+ double t160 = Y1 * s2 * t5 * t12;
+ double t161 = Y1 * s2 * t7 * t12;
+ double t162 = Z1 * s3 * t5 * t12;
+ double t163 = Z1 * s3 * t6 * t12;
+ // t9 = theta, t10 = sin(theta)
+ double t164 = X1 * s2 * t9 * t10 * w3;
+ double t165 = Y1 * s3 * t9 * t10 * w1;
+ double t166 = Z1 * s1 * t9 * t10 * w2;
+ double t183 = X1 * s3 * t9 * t10 * w2;
+ double t184 = Y1 * s1 * t9 * t10 * w3;
+ double t185 = Z1 * s2 * t9 * t10 * w1;
+ // t12 = cos(theta)
+ double t186 = X1 * s2 * t12 * w1 * w2;
+ double t187 = X1 * s3 * t12 * w1 * w3;
+ double t188 = Y1 * s1 * t12 * w1 * w2;
+ double t189 = Y1 * s3 * t12 * w2 * w3;
+ double t190 = Z1 * s1 * t12 * w1 * w3;
+ double t191 = Z1 * s2 * t12 * w2 * w3;
+ // t167 = t140 + t141 + t142 + t143 + t144
+ // + t145 + t146 + t147 + t148 + t149
+ // + t150 + t151 + t152 + t153 + t154
+ // + t155 + t156 + t157 + t158 + t159
+ // + t160 + t161 + t162 + t163 + t164
+ // + t165 + t166 - t183 - t184 - t185
+ // - t186 - t187 - t188
+ // - t189 - t190 - t191
+ //
+ // = (X1 * s1 * t5) + (Y1 * s2 * t6) + (Z1 * s3 * t7) + (s1 * t1 * t5) + (s1 * t1 * t6)
+ // + (s1 * t1 * t7) + (s2 * t2 * t5) + (s2 * t2 * t6) + (s2 * t2 * t7) + (s3 * t3 * t5)
+ // + (s3 * t3 * t6) + (s3 * t3 * t7) + (X1 * s2 * w1 * w2) + (X1 * s3 * w1 * w3) + (Y1 * s1 * w1 * w2)
+ // + (Y1 * s3 * w2 * w3) + (Z1 * s1 * w1 * w3) + (Z1 * s2 * w2 * w3) + (X1 * s1 * t6 * t12) + (X1 * s1 * t7 * t12)
+ // + (Y1 * s2 * t5 * t12) + (Y1 * s2 * t7 * t12) + (Z1 * s3 * t5 * t12) + (Z1 * s3 * t6 * t12) + (X1 * s2 * t9 * t10 * w3)
+ // + (Y1 * s3 * t9 * t10 * w1) + (Z1 * s1 * t9 * t10 * w2) - (X1 * s3 * t9 * t10 * w2) - (Y1 * s1 * t9 * t10 * w3) - (Z1 * s2 * t9 * t10 * w1)
+ // - (X1 * s2 * t12 * w1 * w2) - (X1 * s3 * t12 * w1 * w3) - (Y1 * s1 * t12 * w1 * w2)
+ // - (Y1 * s3 * t12 * w2 * w3) - (Z1 * s1 * t12 * w1 * w3) - (Z1 * s2 * t12 * w2 * w3)
+ double t167 =
+ t140 + t141 + t142 + t143 + t144 + t145 + t146 + t147 + t148 + t149 + t150 + t151 + t152 + t153 + t154 +
+ t155 + t156 + t157 + t158 + t159 + t160 + t161 + t162 + t163 + t164 + t165 + t166 - t183 - t184 - t185 -
+ t186 - t187 - t188 - t189 - t190 - t191;
+ // t168 = t13 * t26 * t45 * w2 * 2.0 = 2*G*w2*(w1^2+w3^2)
+ double t168 = t13 * t26 * t45 * w2 * 2.0;
+ // t169 = t10 * t25 * t45 * w2 = F*w2*(w1^2+w3^2)
+ double t169 = t10 * t25 * t45 * w2;
+ // t170 = t168 + t169 = (2*G+F)*w2*(w1^2+w3^2)
+ double t170 = t168 + t169;
+ // t171 = t13 * t26 * t34 * w2 * 2.0 = 2*G*w2*(w1^2+w2^2)
+ double t171 = t13 * t26 * t34 * w2 * 2.0;
+ // t172 = t10 * t25 * t34 * w2 = F*w2*(w1^2+w2^2)
+ double t172 = t10 * t25 * t34 * w2;
+ // t176 = t13 * t14 * w2 * 2.0 = 2*B*w2
+ double t176 = t13 * t14 * w2 * 2.0;
+ // t173 = t171 + t172 - t176 = (2*G+F)*w2*(w1^2+w2^2) - 2*B*w2
+ double t173 = t171 + t172 - t176;
+ // t174 = -t49 + t51 + t52 + t100 - t111 = -A+2*G*w1*w2*w3+F*w1*w2*w3+F*w2^2-E*w2^2
+ double t174 = -t49 + t51 + t52 + t100 - t111;
+ // t175 = X1 * t174 = (-A+2*G*w1*w2*w3+F*w1*w2*w3+F*w2^2-E*w2^2)*X1
+ double t175 = X1 * t174;
+ // t177 = t13 * t24 * t26 * w2 * 2.0 = 2*w2*G*(w2^2+w3^2)
+ double t177 = t13 * t24 * t26 * w2 * 2.0;
+ // t178 = t10 * t24 * t25 * w2 = F*w2*(w2^2+w3^2)
+ double t178 = t10 * t24 * t25 * w2;
+ // t192 = t13 * t14 * w1 = B*w1
+ double t192 = t13 * t14 * w1;
+ // t179 = -t97 + t98 + t99 + t112 - t192 = -E*w2*w3+F*w2^2*w1+2*G*w2^2*w1+F*w2*w3-B*w1
+ double t179 = -t97 + t98 + t99 + t112 - t192;
+ // t180 = (-E*w2*w3+F*w2^2*w1+2*G*w2^2*w1+F*w2*w3-B*w1)*Y1
+ double t180 = Y1 * t179;
+ // t181 = A+2*G*w1*w2*w3+F*w1*w2*w3-F*w2^2+E*w2^2
+ double t181 = t49 + t51 + t52 - t100 + t111;
+ // t182 = (A+2*G*w1*w2*w3+F*w1*w2*w3-F*w2^2+E*w2^2)*Z1
+ double t182 = Z1 * t181;
+ // t193 = 2*G*w3*(w2^2+w3^2)
+ double t193 = t13 * t26 * t34 * w3 * 2.0;
+ // t194 = F*w3*(w2^2+w3^2)
+ double t194 = t10 * t25 * t34 * w3;
+ // t195 = (2*G+F)*w3*(w2^2+w3^2)
+ double t195 = t193 + t194;
+ // t196 = 2*G*w3*(w1^2+w3^2)
+ double t196 = t13 * t26 * t45 * w3 * 2.0;
+ // t197 = F*w3*(w1^2+w3^2)
+ double t197 = t10 * t25 * t45 * w3;
+ // t200 = 2*B*w3
+ double t200 = t13 * t14 * w3 * 2.0;
+ // t198 = (2*G+F)*w3*(w1^2+w3^2) - 2*B*w3
+ double t198 = t196 + t197 - t200;
+ // t199 = F*w3^2
+ double t199 = t7 * t10 * t25;
+ // t201 = 2*w3*G*(w2^2+w3^2)
+ double t201 = t13 * t24 * t26 * w3 * 2.0;
+ // t202 = F*w3*(w2^2+w3^2)
+ double t202 = t10 * t24 * t25 * w3;
+ // t203 = -t49 + t51 + t52 - t118 + t199 = -A+2*G*w1*w2*w3+F*w1*w2*w3-E*w3^2+F*w3^2
+ double t203 = -t49 + t51 + t52 - t118 + t199;
+ // t204 = (-t49 + t51 + t52 - t118 + t199 = -A+2*G*w1*w2*w3+F*w1*w2*w3-E*w3^2+F*w3^2)*Y1
+ double t204 = Y1 * t203;
+ // t205 = 2*t1
+ double t205 = t1 * 2.0;
+ // t206 = 2*r13*Z1
+ double t206 = Z1 * t29 * 2.0;
+ // t207 = 2*r11*X1
+ double t207 = X1 * t32 * 2.0;
+ // t208 = 2*t1 + 2*r13*Z1 + 2*r11*X1 + 2*r12*Y1
+ double t208 = t205 + t206 + t207 - Y1 * t27 * 2.0;
+ // t209 = 2*t2
+ double t209 = t2 * 2.0;
+ // t210 = 2*r21*X1
+ double t210 = X1 * t53 * 2.0;
+ // t211 = 2*r22*Y1
+ double t211 = Y1 * t58 * 2.0;
+ // t212 = 2*t2 + 2*r21*X1 + 2*r22*Y1 + 2*r23*Z1
+ double t212 = t209 + t210 + t211 - Z1 * t55 * 2.0;
+
+ double t213 = t3 * 2.0;
+ double t214 = Y1 * t40 * 2.0;
+ double t215 = Z1 * t43 * 2.0;
+ double t216 = t213 + t214 + t215 - X1 * t38 * 2.0;
+
+ //
+ jacs(0, 0) = t14 * t65 * (X1 * r1 * w1 * 2.0 + X1 * r2 * w2 + X1 * r3 * w3 + Y1 * r1 * w2 + Z1 * r1 * w3 + r1 * t1 * w1 * 2.0 + r2 * t2 * w1 * 2.0 + r3 * t3 * w1 * 2.0 + Y1 * r3 * t5 * t12 + Y1 * r3 * t9 * t10 - Z1 * r2 * t5 * t12 - Z1 * r2 * t9 * t10 - X1 * r2 * t12 * w2 - X1 * r3 * t12 * w3 - Y1 * r1 * t12 * w2 + Y1 * r2 * t12 * w1 * 2.0 - Z1 * r1 * t12 * w3 + Z1 * r3 * t12 * w1 * 2.0 + Y1 * r3 * t5 * t10 * t11 - Z1 * r2 * t5 * t10 * t11 + X1 * r2 * t12 * w1 * w3 - X1 * r3 * t12 * w1 * w2 - Y1 * r1 * t12 * w1 * w3 + Z1 * r1 * t12 * w1 * w2 - Y1 * r1 * t10 * t11 * w1 * w3 + Z1 * r1 * t10 * t11 * w1 * w2 - X1 * r1 * t6 * t10 * t11 * w1 - X1 * r1 * t7 * t10 * t11 * w1 + X1 * r2 * t5 * t10 * t11 * w2 + X1 * r3 * t5 * t10 * t11 * w3 + Y1 * r1 * t5 * t10 * t11 * w2 - Y1 * r2 * t5 * t10 * t11 * w1 - Y1 * r2 * t7 * t10 * t11 * w1 + Z1 * r1 * t5 * t10 * t11 * w3 - Z1 * r3 * t5 * t10 * t11 * w1 - Z1 * r3 * t6 * t10 * t11 * w1 + X1 * r2 * t10 * t11 * w1 * w3 - X1 * r3 * t10 * t11 * w1 * w2 + Y1 * r3 * t10 * t11 * w1 * w2 * w3 + Z1 * r2 * t10 * t11 * w1 * w2 * w3) - t26 * t65 * t93 * w1 * 2.0 - t14 * t93 * t101 * (t130 + t15 * (-X1 * t121 + Y1 * (t46 + t47 + t48 - t13 * t14 * w2 - t12 * t14 * w1 * w3) + Z1 * (t35 + t36 + t37 - t13 * t14 * w3 - t10 * t25 * w1 * w2)) * 2.0 + t18 * (t135 + t137 - Y1 * (t132 + t133 - t13 * t14 * w1 * 2.0)) * 2.0) * (1.0 / 2.0);
+
+ jacs(0, 1) = t14 * t65 * (X1 * r2 * w1 + Y1 * r1 * w1 + Y1 * r2 * w2 * 2.0 + Y1 * r3 * w3 + Z1 * r2 * w3 + r1 * t1 * w2 * 2.0 + r2 * t2 * w2 * 2.0 + r3 * t3 * w2 * 2.0 - X1 * r3 * t6 * t12 - X1 * r3 * t9 * t10 + Z1 * r1 * t6 * t12 + Z1 * r1 * t9 * t10 + X1 * r1 * t12 * w2 * 2.0 - X1 * r2 * t12 * w1 - Y1 * r1 * t12 * w1 - Y1 * r3 * t12 * w3 - Z1 * r2 * t12 * w3 + Z1 * r3 * t12 * w2 * 2.0 - X1 * r3 * t6 * t10 * t11 + Z1 * r1 * t6 * t10 * t11 + X1 * r2 * t12 * w2 * w3 - Y1 * r1 * t12 * w2 * w3 + Y1 * r3 * t12 * w1 * w2 - Z1 * r2 * t12 * w1 * w2 - Y1 * r1 * t10 * t11 * w2 * w3 + Y1 * r3 * t10 * t11 * w1 * w2 - Z1 * r2 * t10 * t11 * w1 * w2 - X1 * r1 * t6 * t10 * t11 * w2 + X1 * r2 * t6 * t10 * t11 * w1 - X1 * r1 * t7 * t10 * t11 * w2 + Y1 * r1 * t6 * t10 * t11 * w1 - Y1 * r2 * t5 * t10 * t11 * w2 - Y1 * r2 * t7 * t10 * t11 * w2 + Y1 * r3 * t6 * t10 * t11 * w3 - Z1 * r3 * t5 * t10 * t11 * w2 + Z1 * r2 * t6 * t10 * t11 * w3 - Z1 * r3 * t6 * t10 * t11 * w2 + X1 * r2 * t10 * t11 * w2 * w3 + X1 * r3 * t10 * t11 * w1 * w2 * w3 + Z1 * r1 * t10 * t11 * w1 * w2 * w3) -
+ t26 * t65 * t93 * w2 * 2.0 - t14 * t93 * t101 * (t18 * (Z1 * (-t35 + t94 + t95 + t96 - t13 * t14 * w3) - Y1 * t170 + X1 * (t97 + t98 + t99 - t13 * t14 * w1 - t10 * t25 * w2 * w3)) * 2.0 + t15 * (t180 + t182 - X1 * (t177 + t178 - t13 * t14 * w2 * 2.0)) * 2.0 + t23 * (t175 + Y1 * (t35 - t94 + t95 + t96 - t13 * t14 * w3) - Z1 * t173) * 2.0) * (1.0 / 2.0);
+ jacs(0, 2) = t14 * t65 * (X1 * r3 * w1 + Y1 * r3 * w2 + Z1 * r1 * w1 + Z1 * r2 * w2 + Z1 * r3 * w3 * 2.0 + r1 * t1 * w3 * 2.0 + r2 * t2 * w3 * 2.0 + r3 * t3 * w3 * 2.0 + X1 * r2 * t7 * t12 + X1 * r2 * t9 * t10 - Y1 * r1 * t7 * t12 - Y1 * r1 * t9 * t10 + X1 * r1 * t12 * w3 * 2.0 - X1 * r3 * t12 * w1 + Y1 * r2 * t12 * w3 * 2.0 - Y1 * r3 * t12 * w2 - Z1 * r1 * t12 * w1 - Z1 * r2 * t12 * w2 + X1 * r2 * t7 * t10 * t11 - Y1 * r1 * t7 * t10 * t11 - X1 * r3 * t12 * w2 * w3 + Y1 * r3 * t12 * w1 * w3 + Z1 * r1 * t12 * w2 * w3 - Z1 * r2 * t12 * w1 * w3 + Y1 * r3 * t10 * t11 * w1 * w3 + Z1 * r1 * t10 * t11 * w2 * w3 - Z1 * r2 * t10 * t11 * w1 * w3 - X1 * r1 * t6 * t10 * t11 * w3 - X1 * r1 * t7 * t10 * t11 * w3 + X1 * r3 * t7 * t10 * t11 * w1 - Y1 * r2 * t5 * t10 * t11 * w3 - Y1 * r2 * t7 * t10 * t11 * w3 + Y1 * r3 * t7 * t10 * t11 * w2 + Z1 * r1 * t7 * t10 * t11 * w1 + Z1 * r2 * t7 * t10 * t11 * w2 - Z1 * r3 * t5 * t10 * t11 * w3 - Z1 * r3 * t6 * t10 * t11 * w3 - X1 * r3 * t10 * t11 * w2 * w3 + X1 * r2 * t10 * t11 * w1 * w2 * w3 + Y1 * r1 * t10 * t11 * w1 * w2 * w3) -
+ t26 * t65 * t93 * w3 * 2.0 - t14 * t93 * t101 * (t18 * (Z1 * (t46 - t113 + t114 + t115 - t13 * t14 * w2) - Y1 * t198 + X1 * (t49 + t51 + t52 + t118 - t7 * t10 * t25)) * 2.0 + t23 * (X1 * (-t97 + t112 + t116 + t117 - t13 * t14 * w1) + Y1 * (-t46 + t113 + t114 + t115 - t13 * t14 * w2) - Z1 * t195) * 2.0 + t15 * (t204 + Z1 * (t97 - t112 + t116 + t117 - t13 * t14 * w1) - X1 * (t201 + t202 - t13 * t14 * w3 * 2.0)) * 2.0) * (1.0 / 2.0);
+
+ // = 1/2 * r1 * t65
+ // - t14 * t93
+ // * t101 * t208
+ // = 1/2 * r1 * 1 / sqrt((t1 + r11*X1 + r12*Y1 + r13*Z1)^2 + (t2 + r21*X1 + r22*Y1 + r23*Z1)^2 + (t3 + r31*X1 + r32*Y1 + r33*Z1)^2)
+ // - 1/theta^2 * ((Y1 * r2 * w2^2) + (Z1 * r3 * w3^2) + (r1 * t1 * w1^2) + (r1 * t1 * w2^2)
+ // + (r1 * t1 * w3^2) + (r2 * t2 * w1^2) + (r2 * t2 * w2^2) + (r2 * t2 * w3^2) + (r3 * t3 * w1^2)
+ // + (r3 * t3 * w2^2) + (r3 * t3 * w3^2) + (X1 * r1 * w1^2) + (X1 * r2 * w1 * w2) + (X1 * r3 * w1 * w3)
+ // + (Y1 * r1 * w1 * w2) + (Y1 * r3 * w2 * w3) + (Z1 * r1 * w1 * w3) + (Z1 * r2 * w2 * w3) + (X1 * r1 * w2^2 * cos(theta))
+ // + (X1 * r1 * w3^2 * cos(theta)) + (Y1 * r2 * w1^2 * cos(theta)) + (Y1 * r2 * w3^2 * cos(theta)) + (Z1 * r3 * w1^2 * cos(theta)) + (Z1 * r3 * w2^2 * cos(theta))
+ // + (X1 * r2 * theta * sin(theta) * w3) + (Y1 * r3 * theta * sin(theta) * w1) + (Z1 * r1 * theta * sin(theta) * w2) - (X1 * r3 * theta * sin(theta) * w2) - (Y1 * r1 * theta * sin(theta) * w3)
+ // - (Z1 * r2 * theta * sin(theta) * w1) - (X1 * r2 * cos(theta) * w1 * w2) - (X1 * r3 * cos(theta) * w1 * w3) - (Y1 * r1 * cos(theta) * w1 * w2) - ()
+ // - (Y1 * r3 * cos(theta) * w2 * w3) - (Z1 * r1 * cos(theta) * w1 * w3) - (Z1 * r2 * cos(theta) * w2 * w3))
+ // * (pow(((t1 + r11*X1 + r12*Y1 + r13*Z1)^2 + (t2 + r21*X1 + r22*Y1 + r23*Z1)^2 + (t3 + r31*X1 + r32*Y1 + r33*Z1)^2), 3/2))
+ // * (2*t1 + 2*r11*X1 + 2*r12*Y1 + 2*r13*Z1)
+ jacs(0, 3) = r1 * t65 - t14 * t93 * t101 * t208 * (1.0 / 2.0);
+ jacs(0, 4) = r2 * t65 - t14 * t93 * t101 * t212 * (1.0 / 2.0);
+ jacs(0, 5) = r3 * t65 - t14 * t93 * t101 * t216 * (1.0 / 2.0);
+ jacs(1, 0) = t14 * t65 * (X1 * s1 * w1 * 2.0 + X1 * s2 * w2 + X1 * s3 * w3 + Y1 * s1 * w2 + Z1 * s1 * w3 + s1 * t1 * w1 * 2.0 + s2 * t2 * w1 * 2.0 + s3 * t3 * w1 * 2.0 + Y1 * s3 * t5 * t12 + Y1 * s3 * t9 * t10 - Z1 * s2 * t5 * t12 - Z1 * s2 * t9 * t10 - X1 * s2 * t12 * w2 - X1 * s3 * t12 * w3 - Y1 * s1 * t12 * w2 + Y1 * s2 * t12 * w1 * 2.0 - Z1 * s1 * t12 * w3 + Z1 * s3 * t12 * w1 * 2.0 + Y1 * s3 * t5 * t10 * t11 - Z1 * s2 * t5 * t10 * t11 + X1 * s2 * t12 * w1 * w3 - X1 * s3 * t12 * w1 * w2 - Y1 * s1 * t12 * w1 * w3 + Z1 * s1 * t12 * w1 * w2 + X1 * s2 * t10 * t11 * w1 * w3 - X1 * s3 * t10 * t11 * w1 * w2 - Y1 * s1 * t10 * t11 * w1 * w3 + Z1 * s1 * t10 * t11 * w1 * w2 - X1 * s1 * t6 * t10 * t11 * w1 - X1 * s1 * t7 * t10 * t11 * w1 + X1 * s2 * t5 * t10 * t11 * w2 + X1 * s3 * t5 * t10 * t11 * w3 + Y1 * s1 * t5 * t10 * t11 * w2 - Y1 * s2 * t5 * t10 * t11 * w1 - Y1 * s2 * t7 * t10 * t11 * w1 + Z1 * s1 * t5 * t10 * t11 * w3 - Z1 * s3 * t5 * t10 * t11 * w1 - Z1 * s3 * t6 * t10 * t11 * w1 + Y1 * s3 * t10 * t11 * w1 * w2 * w3 + Z1 * s2 * t10 * t11 * w1 * w2 * w3) - t14 * t101 * t167 * (t130 + t15 * (Y1 * (t46 + t47 + t48 - t113 - t138) + Z1 * (t35 + t36 + t37 - t94 - t139) - X1 * t121) * 2.0 + t18 * (t135 + t137 - Y1 * (-t131 + t132 + t133)) * 2.0) * (1.0 / 2.0) - t26 * t65 * t167 * w1 * 2.0;
+ jacs(1, 1) = t14 * t65 * (X1 * s2 * w1 + Y1 * s1 * w1 + Y1 * s2 * w2 * 2.0 + Y1 * s3 * w3 + Z1 * s2 * w3 + s1 * t1 * w2 * 2.0 + s2 * t2 * w2 * 2.0 + s3 * t3 * w2 * 2.0 - X1 * s3 * t6 * t12 - X1 * s3 * t9 * t10 + Z1 * s1 * t6 * t12 + Z1 * s1 * t9 * t10 + X1 * s1 * t12 * w2 * 2.0 - X1 * s2 * t12 * w1 - Y1 * s1 * t12 * w1 - Y1 * s3 * t12 * w3 - Z1 * s2 * t12 * w3 + Z1 * s3 * t12 * w2 * 2.0 - X1 * s3 * t6 * t10 * t11 + Z1 * s1 * t6 * t10 * t11 + X1 * s2 * t12 * w2 * w3 - Y1 * s1 * t12 * w2 * w3 + Y1 * s3 * t12 * w1 * w2 - Z1 * s2 * t12 * w1 * w2 + X1 * s2 * t10 * t11 * w2 * w3 - Y1 * s1 * t10 * t11 * w2 * w3 + Y1 * s3 * t10 * t11 * w1 * w2 - Z1 * s2 * t10 * t11 * w1 * w2 - X1 * s1 * t6 * t10 * t11 * w2 + X1 * s2 * t6 * t10 * t11 * w1 - X1 * s1 * t7 * t10 * t11 * w2 + Y1 * s1 * t6 * t10 * t11 * w1 - Y1 * s2 * t5 * t10 * t11 * w2 - Y1 * s2 * t7 * t10 * t11 * w2 + Y1 * s3 * t6 * t10 * t11 * w3 - Z1 * s3 * t5 * t10 * t11 * w2 + Z1 * s2 * t6 * t10 * t11 * w3 - Z1 * s3 * t6 * t10 * t11 * w2 + X1 * s3 * t10 * t11 * w1 * w2 * w3 + Z1 * s1 * t10 * t11 * w1 * w2 * w3) -
+ t26 * t65 * t167 * w2 * 2.0 - t14 * t101 * t167 * (t18 * (X1 * (t97 + t98 + t99 - t112 - t192) + Z1 * (-t35 + t94 + t95 + t96 - t139) - Y1 * t170) * 2.0 + t15 * (t180 + t182 - X1 * (-t176 + t177 + t178)) * 2.0 + t23 * (t175 + Y1 * (t35 - t94 + t95 + t96 - t139) - Z1 * t173) * 2.0) * (1.0 / 2.0);
+ jacs(1, 2) = t14 * t65 * (X1 * s3 * w1 + Y1 * s3 * w2 + Z1 * s1 * w1 + Z1 * s2 * w2 + Z1 * s3 * w3 * 2.0 + s1 * t1 * w3 * 2.0 + s2 * t2 * w3 * 2.0 + s3 * t3 * w3 * 2.0 + X1 * s2 * t7 * t12 + X1 * s2 * t9 * t10 - Y1 * s1 * t7 * t12 - Y1 * s1 * t9 * t10 + X1 * s1 * t12 * w3 * 2.0 - X1 * s3 * t12 * w1 + Y1 * s2 * t12 * w3 * 2.0 - Y1 * s3 * t12 * w2 - Z1 * s1 * t12 * w1 - Z1 * s2 * t12 * w2 + X1 * s2 * t7 * t10 * t11 - Y1 * s1 * t7 * t10 * t11 - X1 * s3 * t12 * w2 * w3 + Y1 * s3 * t12 * w1 * w3 + Z1 * s1 * t12 * w2 * w3 - Z1 * s2 * t12 * w1 * w3 - X1 * s3 * t10 * t11 * w2 * w3 + Y1 * s3 * t10 * t11 * w1 * w3 + Z1 * s1 * t10 * t11 * w2 * w3 - Z1 * s2 * t10 * t11 * w1 * w3 - X1 * s1 * t6 * t10 * t11 * w3 - X1 * s1 * t7 * t10 * t11 * w3 + X1 * s3 * t7 * t10 * t11 * w1 - Y1 * s2 * t5 * t10 * t11 * w3 - Y1 * s2 * t7 * t10 * t11 * w3 + Y1 * s3 * t7 * t10 * t11 * w2 + Z1 * s1 * t7 * t10 * t11 * w1 + Z1 * s2 * t7 * t10 * t11 * w2 - Z1 * s3 * t5 * t10 * t11 * w3 - Z1 * s3 * t6 * t10 * t11 * w3 + X1 * s2 * t10 * t11 * w1 * w2 * w3 + Y1 * s1 * t10 * t11 * w1 * w2 * w3) -
+ t26 * t65 * t167 * w3 * 2.0 - t14 * t101 * t167 * (t18 * (Z1 * (t46 - t113 + t114 + t115 - t138) - Y1 * t198 + X1 * (t49 + t51 + t52 + t118 - t199)) * 2.0 + t23 * (X1 * (-t97 + t112 + t116 + t117 - t192) + Y1 * (-t46 + t113 + t114 + t115 - t138) - Z1 * t195) * 2.0 + t15 * (t204 + Z1 * (t97 - t112 + t116 + t117 - t192) - X1 * (-t200 + t201 + t202)) * 2.0) * (1.0 / 2.0);
+ jacs(1, 3) = s1 * t65 - t14 * t101 * t167 * t208 * (1.0 / 2.0);
+ jacs(1, 4) = s2 * t65 - t14 * t101 * t167 * t212 * (1.0 / 2.0);
+ jacs(1, 5) = s3 * t65 - t14 * t101 * t167 * t216 * (1.0 / 2.0);
+}
+} // End namespace ORB_SLAM3