36 #include <rtabmap/core/Version.h> 43 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM) 44 #include "g2o/core/sparse_optimizer.h" 45 #include "g2o/core/block_solver.h" 46 #include "g2o/core/factory.h" 47 #include "g2o/core/optimization_algorithm_factory.h" 48 #include "g2o/core/optimization_algorithm_gauss_newton.h" 49 #include "g2o/core/optimization_algorithm_levenberg.h" 50 #include "g2o/core/robust_kernel_impl.h" 53 typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> MatrixXD;
57 #include "g2o/types/sba/types_sba.h" 58 #include "g2o/solvers/eigen/linear_solver_eigen.h" 59 #include "g2o/config.h" 60 #include "g2o/types/slam2d/types_slam2d.h" 61 #include "g2o/types/slam3d/types_slam3d.h" 67 #ifdef G2O_HAVE_CSPARSE 68 #include "g2o/solvers/csparse/linear_solver_csparse.h" 70 #include "g2o/solvers/pcg/linear_solver_pcg.h" 71 #ifdef G2O_HAVE_CHOLMOD 72 #include "g2o/solvers/cholmod/linear_solver_cholmod.h" 76 #ifdef RTABMAP_ORB_SLAM 77 #include "g2o/types/types_sba.h" 78 #include "g2o/types/types_six_dof_expmap.h" 79 #include "g2o/solvers/linear_solver_eigen.h" 82 typedef g2o::BlockSolver< g2o::BlockSolverTraits<-1, -1> > SlamBlockSolver;
83 typedef g2o::LinearSolverEigen<SlamBlockSolver::PoseMatrixType> SlamLinearEigenSolver;
85 typedef g2o::LinearSolverPCG<SlamBlockSolver::PoseMatrixType> SlamLinearPCGSolver;
86 #ifdef G2O_HAVE_CSPARSE 87 typedef g2o::LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearCSparseSolver;
89 #ifdef G2O_HAVE_CHOLMOD 90 typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver;
95 typedef VertexPointXYZ VertexSBAPointXYZ;
99 #if defined(RTABMAP_VERTIGO) 107 #endif // end defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM) 113 #define MULTICAM_OFFSET 10 // 10 means max 10 cameras per pose 117 bool OptimizerG2O::available()
119 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM) 126 bool OptimizerG2O::isCSparseAvailable()
128 #ifdef G2O_HAVE_CSPARSE 135 bool OptimizerG2O::isCholmodAvailable()
137 #ifdef G2O_HAVE_CHOLMOD 147 optimizer_(
Parameters::defaultg2oOptimizer()),
148 pixelVariance_(
Parameters::defaultg2oPixelVariance()),
149 robustKernelDelta_(
Parameters::defaultg2oRobustKernelDelta()),
154 if(!g2o::Factory::instance()->knowsTag(
"CACHE_SE3_OFFSET"))
156 #if defined(RTABMAP_G2O_CPP11) and RTABMAP_G2O_CPP11 == 1 157 g2o::Factory::instance()->registerType(
"CACHE_SE3_OFFSET", g2o::make_unique<g2o::HyperGraphElementCreator<g2o::CacheSE3Offset> >());
159 g2o::Factory::instance()->registerType(
"CACHE_SE3_OFFSET",
new g2o::HyperGraphElementCreator<g2o::CacheSE3Offset>);
178 #ifdef RTABMAP_ORB_SLAM 181 UWARN(
"g2o built with ORB_SLAM has only Eigen solver available, using Eigen=3 instead of %d.",
solver_);
185 #ifndef G2O_HAVE_CHOLMOD 188 UWARN(
"g2o is not built with chmold, so it cannot be used as solver. Using CSparse instead.");
193 #ifndef G2O_HAVE_CSPARSE 196 UWARN(
"g2o is not built with csparse, so it cannot be used as solver. Using PCG instead.");
206 const std::map<int, Transform> & poses,
207 const std::multimap<int, Link> & edgeConstraints,
208 cv::Mat & outputCovariance,
209 std::list<std::map<int, Transform> > * intermediateGraphes,
211 int * iterationsDone)
213 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
214 std::map<int, Transform> optimizedPoses;
216 UDEBUG(
"Optimizing graph...");
218 #ifndef RTABMAP_VERTIGO 221 UWARN(
"Vertigo robust optimization is not available! Robust optimization is now disabled.");
226 optimizedPoses.clear();
227 if(edgeConstraints.size()>=1 && poses.size()>=2 &&
iterations() > 0 && poses.rbegin()->first > 0)
231 g2o::SparseOptimizer optimizer;
235 g2o::ParameterSE2Offset* odomOffset =
new g2o::ParameterSE2Offset();
237 optimizer.addParameter(odomOffset);
241 g2o::ParameterSE3Offset* odomOffset =
new g2o::ParameterSE3Offset();
243 optimizer.addParameter(odomOffset);
246 #ifdef RTABMAP_G2O_CPP11 248 std::unique_ptr<SlamBlockSolver> blockSolver;
253 auto linearSolver = g2o::make_unique<SlamLinearEigenSolver>();
254 linearSolver->setBlockOrdering(
false);
255 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
257 #ifdef G2O_HAVE_CHOLMOD 261 auto linearSolver = g2o::make_unique<SlamLinearCholmodSolver>();
262 linearSolver->setBlockOrdering(
false);
263 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
266 #ifdef G2O_HAVE_CSPARSE 271 auto linearSolver = g2o::make_unique<SlamLinearCSparseSolver>();
272 linearSolver->setBlockOrdering(
false);
273 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
279 auto linearSolver = g2o::make_unique<SlamLinearPCGSolver>();
280 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
286 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(std::move(blockSolver)));
290 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(std::move(blockSolver)));
295 SlamBlockSolver * blockSolver = 0;
300 SlamLinearEigenSolver * linearSolver =
new SlamLinearEigenSolver();
301 linearSolver->setBlockOrdering(
false);
302 blockSolver =
new SlamBlockSolver(linearSolver);
304 #ifdef G2O_HAVE_CHOLMOD 308 SlamLinearCholmodSolver * linearSolver =
new SlamLinearCholmodSolver();
309 linearSolver->setBlockOrdering(
false);
310 blockSolver =
new SlamBlockSolver(linearSolver);
313 #ifdef G2O_HAVE_CSPARSE 317 SlamLinearCSparseSolver* linearSolver =
new SlamLinearCSparseSolver();
318 linearSolver->setBlockOrdering(
false);
319 blockSolver =
new SlamBlockSolver(linearSolver);
325 SlamLinearPCGSolver * linearSolver =
new SlamLinearPCGSolver();
326 blockSolver =
new SlamBlockSolver(linearSolver);
331 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(blockSolver));
335 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(blockSolver));
339 bool hasGravityConstraints =
false;
342 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
344 if(iter->second.from() == iter->second.to())
355 hasGravityConstraints =
true;
365 int landmarkVertexOffset = poses.rbegin()->first+1;
366 std::map<int, bool> isLandmarkWithRotation;
368 UDEBUG(
"fill poses to g2o... (rootId=%d hasGravityConstraints=%d isSlam2d=%d)", rootId, hasGravityConstraints?1:0,
isSlam2d()?1:0);
369 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
371 UASSERT(!iter->second.isNull());
372 g2o::HyperGraph::Vertex * vertex = 0;
373 int id = iter->first;
378 g2o::VertexSE2 * v2 =
new g2o::VertexSE2();
379 v2->setEstimate(g2o::SE2(iter->second.x(), iter->second.y(), iter->second.theta()));
382 UDEBUG(
"Set %d fixed",
id);
390 std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(
id);
391 UASSERT(jter != edgeConstraints.end());
393 if (1 / static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
395 g2o::VertexPointXY * v2 =
new g2o::VertexPointXY();
396 v2->setEstimate(Eigen::Vector2d(iter->second.x(), iter->second.y()));
398 isLandmarkWithRotation.insert(std::make_pair(
id,
false));
399 id = landmarkVertexOffset - id;
403 g2o::VertexSE2 * v2 =
new g2o::VertexSE2();
404 v2->setEstimate(g2o::SE2(iter->second.x(), iter->second.y(), iter->second.theta()));
407 UDEBUG(
"Set %d fixed",
id);
411 isLandmarkWithRotation.insert(std::make_pair(
id,
true));
412 id = landmarkVertexOffset - id;
424 g2o::VertexSE3 * v3 =
new g2o::VertexSE3();
426 Eigen::Affine3d a = iter->second.toEigen3d();
427 Eigen::Isometry3d pose;
429 pose.translation() = a.translation();
430 v3->setEstimate(pose);
431 if(
id == rootId && !hasGravityConstraints)
433 UDEBUG(
"Set %d fixed",
id);
441 std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(
id);
442 UASSERT(jter != edgeConstraints.end());
444 if (1 / static_cast<double>(jter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
445 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
446 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
448 g2o::VertexPointXYZ * v3 =
new g2o::VertexPointXYZ();
449 v3->setEstimate(Eigen::Vector3d(iter->second.x(), iter->second.y(), iter->second.z()));
451 isLandmarkWithRotation.insert(std::make_pair(
id,
false));
452 id = landmarkVertexOffset - id;
456 g2o::VertexSE3 * v3 =
new g2o::VertexSE3();
457 Eigen::Affine3d a = iter->second.toEigen3d();
458 Eigen::Isometry3d pose;
460 pose.translation() = a.translation();
461 v3->setEstimate(pose);
462 if(
id == rootId && !hasGravityConstraints)
464 UDEBUG(
"Set %d fixed",
id);
468 isLandmarkWithRotation.insert(std::make_pair(
id,
true));
469 id = landmarkVertexOffset - id;
479 UERROR(
"Could not create vertex for node %d",
id);
484 UASSERT_MSG(optimizer.addVertex(vertex),
uFormat(
"cannot insert vertex %d!?", iter->first).c_str());
489 if(!
isSlam2d() && rootId !=0 && hasGravityConstraints)
491 g2o::VertexSE3* v1 =
dynamic_cast<g2o::VertexSE3*
>(optimizer.vertex(rootId));
494 g2o::EdgeSE3Prior *
e =
new g2o::EdgeSE3Prior();
496 Eigen::Affine3d a = poses.at(rootId).toEigen3d();
497 Eigen::Isometry3d pose;
499 pose.translation() = a.translation();
500 e->setMeasurement(pose);
502 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity()*10e6;
504 information(3,3) = information(4,4) = 1;
505 e->setInformation(information);
506 if (!optimizer.addEdge(e))
509 UERROR(
"Map: Failed adding fixed constraint of rootid %d, set as fixed instead", rootId);
514 UDEBUG(
"Set %d fixed with prior (have gravity constraints)", rootId);
519 UERROR(
"Map: Failed adding fixed constraint of rootid %d (not found in added vertices)", rootId);
523 UDEBUG(
"fill edges to g2o...");
524 #if defined(RTABMAP_VERTIGO) 525 int vertigoVertexId = landmarkVertexOffset - (poses.begin()->first<0?poses.begin()->first-1:0);
527 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
529 int id1 = iter->second.from();
530 int id2 = iter->second.to();
532 UASSERT(!iter->second.transform().isNull());
534 g2o::HyperGraph::Edge * edge = 0;
545 id1 = landmarkVertexOffset - id1;
546 id2 = landmarkVertexOffset - id2;
551 if(idTag < 0 && !isLandmarkWithRotation.at(idTag))
554 g2o::VertexPointXY* v1 = (g2o::VertexPointXY*)optimizer.vertex(id1);
555 priorEdge->setVertex(0, v1);
556 priorEdge->
setMeasurement(Eigen::Vector2d(iter->second.transform().x(), iter->second.transform().y()));
557 Eigen::Matrix<double, 2, 2> information = Eigen::Matrix<double, 2, 2>::Identity();
560 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
561 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
562 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
563 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
565 priorEdge->setInformation(information);
568 else if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
570 g2o::EdgeSE2XYPrior * priorEdge =
new g2o::EdgeSE2XYPrior();
571 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
572 priorEdge->setVertex(0, v1);
573 priorEdge->setMeasurement(Eigen::Vector2d(iter->second.transform().x(), iter->second.transform().y()));
574 Eigen::Matrix<double, 2, 2> information = Eigen::Matrix<double, 2, 2>::Identity();
577 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
578 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
579 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
580 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
582 priorEdge->setInformation(information);
587 g2o::EdgeSE2Prior * priorEdge =
new g2o::EdgeSE2Prior();
588 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
589 priorEdge->setVertex(0, v1);
590 priorEdge->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
592 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
595 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
596 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
597 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
598 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
599 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
600 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
601 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
602 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
603 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
605 priorEdge->setInformation(information);
611 if(idTag < 0 && !isLandmarkWithRotation.at(idTag))
615 g2o::VertexPointXYZ* v1 = (g2o::VertexPointXYZ*)optimizer.vertex(id1);
616 priorEdge->setVertex(0, v1);
617 priorEdge->
setMeasurement(Eigen::Vector3d(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()));
619 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
622 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
623 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
624 information(0,2) = iter->second.infMatrix().at<
double>(0,2);
625 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
626 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
627 information(1,2) = iter->second.infMatrix().at<
double>(1,2);
628 information(2,0) = iter->second.infMatrix().at<
double>(2,0);
629 information(2,1) = iter->second.infMatrix().at<
double>(2,1);
630 information(2,2) = iter->second.infMatrix().at<
double>(2,2);
632 priorEdge->setInformation(information);
635 else if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
636 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
637 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
641 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
642 priorEdge->setVertex(0, v1);
643 priorEdge->
setMeasurement(Eigen::Vector3d(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()));
645 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
648 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
649 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
650 information(0,2) = iter->second.infMatrix().at<
double>(0,2);
651 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
652 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
653 information(1,2) = iter->second.infMatrix().at<
double>(1,2);
654 information(2,0) = iter->second.infMatrix().at<
double>(2,0);
655 information(2,1) = iter->second.infMatrix().at<
double>(2,1);
656 information(2,2) = iter->second.infMatrix().at<
double>(2,2);
658 priorEdge->setInformation(information);
664 g2o::EdgeSE3Prior * priorEdge =
new g2o::EdgeSE3Prior();
665 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
666 priorEdge->setVertex(0, v1);
667 Eigen::Affine3d a = iter->second.transform().toEigen3d();
668 Eigen::Isometry3d pose;
670 pose.translation() = a.translation();
671 priorEdge->setMeasurement(pose);
673 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
676 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
678 priorEdge->setInformation(information);
685 Eigen::Matrix<double, 6, 1> m;
687 m.head<3>() = Eigen::Vector3d::UnitZ();
690 iter->second.transform().getEulerAngles(roll, pitch, yaw);
695 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
698 priorEdge->setInformation(information);
699 priorEdge->vertices()[0] = v1;
703 else if(id1<0 || id2 < 0)
708 UASSERT((id1 < 0 && id2 > 0) || (id1 > 0 && id2 < 0));
713 t = iter->second.transform();
717 t = iter->second.transform().
inverse();
721 id2 = landmarkVertexOffset - id2;
725 if(isLandmarkWithRotation.at(idTag))
727 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
730 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
731 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
732 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
733 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
734 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
735 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
736 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
737 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
738 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
740 g2o::EdgeSE2 *
e =
new g2o::EdgeSE2();
741 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
742 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
747 e->setMeasurement(g2o::SE2(t.
x(), t.
y(), t.
theta()));
748 e->setInformation(information);
753 Eigen::Matrix<double, 2, 2> information = Eigen::Matrix<double, 2, 2>::Identity();
756 cv::Mat linearCov = cv::Mat(iter->second.infMatrix(), cv::Range(0,2), cv::Range(0,2)).clone();
757 memcpy(information.data(), linearCov.data, linearCov.total()*
sizeof(double));
759 g2o::EdgeSE2PointXY*
e =
new g2o::EdgeSE2PointXY;
760 e->vertices()[0] = optimizer.vertex(id1);
761 e->vertices()[1] = optimizer.vertex(id2);
762 e->setMeasurement(Eigen::Vector2d(t.
x(), t.
y()));
763 e->setInformation(information);
770 if(isLandmarkWithRotation.at(idTag))
772 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
775 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
779 Eigen::Isometry3d constraint;
780 constraint = a.linear();
781 constraint.translation() = a.translation();
783 g2o::EdgeSE3 *
e =
new g2o::EdgeSE3();
784 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
785 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
790 e->setMeasurement(constraint);
791 e->setInformation(information);
796 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
799 cv::Mat linearCov = cv::Mat(iter->second.infMatrix(), cv::Range(0,3), cv::Range(0,3)).clone();
800 memcpy(information.data(), linearCov.data, linearCov.total()*
sizeof(double));
803 g2o::EdgeSE3PointXYZ*
e =
new g2o::EdgeSE3PointXYZ;
804 e->vertices()[0] = optimizer.vertex(id1);
805 e->vertices()[1] = optimizer.vertex(id2);
806 e->setMeasurement(Eigen::Vector3d(t.
x(), t.
y(), t.
z()));
807 e->setInformation(information);
816 #if defined(RTABMAP_VERTIGO) 831 v->setId(vertigoVertexId++);
832 UASSERT_MSG(optimizer.addVertex(v),
uFormat(
"cannot insert switchable vertex %d!?", v->id()).c_str());
840 prior->setMeasurement(1.0);
841 prior->setVertex(0, v);
842 UASSERT_MSG(optimizer.addEdge(prior),
uFormat(
"cannot insert switchable prior edge %d!?", v->id()).c_str());
848 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
851 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
852 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
853 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
854 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
855 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
856 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
857 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
858 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
859 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
862 #if defined(RTABMAP_VERTIGO) 868 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
869 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
875 e->
setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
876 e->setInformation(information);
882 g2o::EdgeSE2 *
e =
new g2o::EdgeSE2();
883 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
884 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
889 e->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
890 e->setInformation(information);
896 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
899 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
902 Eigen::Affine3d a = iter->second.transform().toEigen3d();
903 Eigen::Isometry3d constraint;
904 constraint = a.linear();
905 constraint.translation() = a.translation();
907 #if defined(RTABMAP_VERTIGO) 913 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
914 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
921 e->setInformation(information);
927 g2o::EdgeSE3 *
e =
new g2o::EdgeSE3();
928 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
929 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
934 e->setMeasurement(constraint);
935 e->setInformation(information);
941 if (edge && !optimizer.addEdge(edge))
944 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
950 UDEBUG(
"Initial optimization...");
951 optimizer.initializeOptimization();
953 if(!optimizer.verifyInformationMatrices(
true))
955 UERROR(
"This error can be caused by (1) bad covariance matrix " 956 "set in odometry messages " 957 "(see requirements in g2o::OptimizableGraph::verifyInformationMatrices() function) " 958 "or that (2) PCL and g2o hadn't " 959 "been built both with or without \"-march=native\" compilation " 960 "flag (if one library is built with this flag and not the other, " 961 "this is causing Eigen to not work properly, resulting in segmentation faults).");
962 return optimizedPoses;
968 double lastError = 0.0;
969 if(intermediateGraphes || this->
epsilon() > 0.0)
973 if(intermediateGraphes)
977 std::map<int, Transform> tmpPoses;
980 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
982 int id = iter->first;
985 const g2o::VertexSE2* v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
989 iter->second.getEulerAngles(roll, pitch, yaw);
990 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(),
roll,
pitch, v->estimate().rotation().angle());
991 tmpPoses.insert(std::pair<int, Transform>(
id, t));
996 UERROR(
"Vertex %d not found!?",
id);
1001 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1004 if(isLandmarkWithRotation.at(
id))
1006 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)v;
1008 iter->second.getEulerAngles(roll, pitch, yaw);
1009 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1], iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
1010 tmpPoses.insert(std::pair<int, Transform>(
id, t));
1015 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)v;
1017 iter->second.getEulerAngles(roll, pitch, yaw);
1019 tmpPoses.insert(std::pair<int, Transform>(
id, t));
1025 UERROR(
"Vertex %d not found!?",
id);
1032 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1034 int id = iter->first;
1037 const g2o::VertexSE3* v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
1041 tmpPoses.insert(std::pair<int, Transform>(
id, t));
1046 UERROR(
"Vertex %d not found!?",
id);
1051 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1054 if(isLandmarkWithRotation.at(
id))
1056 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)v;
1058 tmpPoses.insert(std::pair<int, Transform>(
id, t));
1063 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)v;
1065 iter->second.getEulerAngles(roll, pitch, yaw);
1067 tmpPoses.insert(std::pair<int, Transform>(
id, t));
1073 UERROR(
"Vertex %d not found!?",
id);
1078 intermediateGraphes->push_back(tmpPoses);
1082 it += optimizer.optimize(1);
1085 optimizer.computeActiveErrors();
1086 double chi2 = optimizer.activeRobustChi2();
1087 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f", i, (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
1089 if(i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
1091 UERROR(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
1092 return optimizedPoses;
1095 double errorDelta = lastError - chi2;
1096 if(i>0 && errorDelta < this->
epsilon())
1100 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
1104 UINFO(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
1108 else if(i==0 && chi2 < this->
epsilon())
1110 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)", chi2, this->
epsilon());
1119 optimizer.computeActiveErrors();
1120 UDEBUG(
"%d nodes, %d edges, chi2: %f", (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), optimizer.activeRobustChi2());
1124 *finalError = lastError;
1128 *iterationsDone = it;
1130 UINFO(
"g2o optimizing end (%d iterations done, error=%f, time = %f s)", it, optimizer.activeRobustChi2(), timer.
ticks());
1132 if(optimizer.activeRobustChi2() > 1000000000000.0)
1134 UERROR(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
1135 return optimizedPoses;
1140 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1142 int id = iter->first;
1145 const g2o::VertexSE2* v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
1149 iter->second.getEulerAngles(roll, pitch, yaw);
1150 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(),
roll,
pitch, v->estimate().rotation().angle());
1151 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1156 UERROR(
"Vertex %d not found!?",
id);
1161 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1165 if(isLandmarkWithRotation.at(
id))
1167 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)v;
1169 iter->second.getEulerAngles(roll, pitch, yaw);
1170 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1], iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
1171 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1176 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)v;
1178 iter->second.getEulerAngles(roll, pitch, yaw);
1180 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1186 UERROR(
"Vertex %d not found!?",
id);
1191 g2o::VertexSE2* v = (g2o::VertexSE2*)optimizer.vertex(poses.rbegin()->first);
1195 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
1196 optimizer.computeMarginals(spinv, v);
1197 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)", t.
ticks(), spinv.cols(), spinv.rows(), v->hessianIndex(), poses.rbegin()->first);
1198 if(v->hessianIndex() >= 0 && v->hessianIndex() < (int)spinv.blockCols().size())
1200 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock * block = spinv.blockCols()[v->hessianIndex()].begin()->second;
1201 UASSERT(block && block->cols() == 3 && block->cols() == 3);
1202 outputCovariance.at<
double>(0,0) = (*block)(0,0);
1203 outputCovariance.at<
double>(0,1) = (*block)(0,1);
1204 outputCovariance.at<
double>(0,5) = (*block)(0,2);
1205 outputCovariance.at<
double>(1,0) = (*block)(1,0);
1206 outputCovariance.at<
double>(1,1) = (*block)(1,1);
1207 outputCovariance.at<
double>(1,5) = (*block)(1,2);
1208 outputCovariance.at<
double>(5,0) = (*block)(2,0);
1209 outputCovariance.at<
double>(5,1) = (*block)(2,1);
1210 outputCovariance.at<
double>(5,5) = (*block)(2,2);
1212 else if(v->hessianIndex() < 0)
1214 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex());
1218 UWARN(
"Computing marginals: vertex %d has hessian not valid (%d > block size=%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex(), (int)spinv.blockCols().size());
1223 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1228 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1230 int id = iter->first;
1233 const g2o::VertexSE3* v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
1237 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1242 UERROR(
"Vertex %d not found!?",
id);
1247 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1251 if(isLandmarkWithRotation.at(
id))
1253 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)v;
1255 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1260 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)v;
1262 iter->second.getEulerAngles(roll, pitch, yaw);
1264 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1270 UERROR(
"Vertex %d not found!?",
id);
1275 g2o::VertexSE3* v = (g2o::VertexSE3*)optimizer.vertex(poses.rbegin()->first);
1279 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
1280 optimizer.computeMarginals(spinv, v);
1281 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)", t.
ticks(), spinv.cols(), spinv.rows(), v->hessianIndex(), poses.rbegin()->first);
1282 if(v->hessianIndex() >= 0 && v->hessianIndex() < (int)spinv.blockCols().size())
1284 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock * block = spinv.blockCols()[v->hessianIndex()].begin()->second;
1285 UASSERT(block && block->cols() == 6 && block->cols() == 6);
1286 memcpy(outputCovariance.data, block->data(), outputCovariance.total()*
sizeof(double));
1288 else if(v->hessianIndex() < 0)
1290 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex());
1292 #ifdef RTABMAP_G2O_CPP11 1295 UWARN(
"Computing marginals: vertex %d has hessian not valid (%d > block size=%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex(), (int)spinv.blockCols().size());
1301 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1305 else if(poses.size() == 1 ||
iterations() <= 0)
1307 optimizedPoses = poses;
1311 UWARN(
"This method should be called at least with 1 pose!");
1313 UDEBUG(
"Optimizing graph...end!");
1315 #ifdef RTABMAP_ORB_SLAM 1316 UERROR(
"G2O graph optimization cannot be used with g2o built from ORB_SLAM, only SBA is available.");
1318 UERROR(
"Not built with G2O support!");
1321 return optimizedPoses;
1324 #ifdef RTABMAP_ORB_SLAM 1328 class EdgeSE3Expmap :
public g2o::BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>
1331 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
1332 EdgeSE3Expmap(): BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>(){}
1333 bool read(std::istream& is)
1338 bool write(std::ostream& os)
const 1345 const g2o::VertexSE3Expmap* v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1346 const g2o::VertexSE3Expmap* v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1347 g2o::SE3Quat delta = _inverseMeasurement * (v1->estimate().inverse()*v2->estimate());
1348 _error[0]=delta.translation().x();
1349 _error[1]=delta.translation().y();
1350 _error[2]=delta.translation().z();
1351 _error[3]=delta.rotation().x();
1352 _error[4]=delta.rotation().y();
1353 _error[5]=delta.rotation().z();
1356 virtual void setMeasurement(
const g2o::SE3Quat& meas){
1358 _inverseMeasurement=meas.inverse();
1361 virtual double initialEstimatePossible(
const g2o::OptimizableGraph::VertexSet& , g2o::OptimizableGraph::Vertex* ) {
return 1.;}
1362 virtual void initialEstimate(
const g2o::OptimizableGraph::VertexSet& from_, g2o::OptimizableGraph::Vertex* ){
1363 g2o::VertexSE3Expmap* from =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[0]);
1364 g2o::VertexSE3Expmap* to =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[1]);
1365 if (from_.count(from) > 0)
1366 to->setEstimate((g2o::SE3Quat) from->estimate() * _measurement);
1368 from->setEstimate((g2o::SE3Quat) to->estimate() * _inverseMeasurement);
1371 virtual bool setMeasurementData(
const double*
d){
1372 Eigen::Map<const g2o::Vector7d> v(d);
1373 _measurement.fromVector(v);
1374 _inverseMeasurement = _measurement.inverse();
1378 virtual bool getMeasurementData(
double* d)
const{
1379 Eigen::Map<g2o::Vector7d> v(d);
1380 v = _measurement.toVector();
1384 virtual int measurementDimension()
const {
return 7;}
1386 virtual bool setMeasurementFromState() {
1387 const g2o::VertexSE3Expmap* v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1388 const g2o::VertexSE3Expmap* v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1389 _measurement = (v1->estimate().inverse()*v2->estimate());
1390 _inverseMeasurement = _measurement.inverse();
1395 g2o::SE3Quat _inverseMeasurement;
1401 const std::map<int, Transform> & poses,
1402 const std::multimap<int, Link> & links,
1403 const std::map<
int, std::vector<CameraModel> > & models,
1404 std::map<int, cv::Point3f> & points3DMap,
1405 const std::map<
int, std::map<int, FeatureBA> > & wordReferences,
1406 std::set<int> * outliers)
1408 std::map<int, Transform> optimizedPoses;
1409 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM) 1410 UDEBUG(
"Optimizing graph...");
1412 optimizedPoses.clear();
1413 if(poses.size()>=2 &&
iterations() > 0 && (models.size() == poses.size() || poses.begin()->first < 0))
1415 g2o::SparseOptimizer optimizer;
1417 #if defined(RTABMAP_G2O_CPP11) and not defined(RTABMAP_ORB_SLAM) 1418 std::unique_ptr<g2o::BlockSolver_6_3::LinearSolverType> linearSolver;
1420 g2o::BlockSolver_6_3::LinearSolverType * linearSolver = 0;
1423 #ifdef RTABMAP_ORB_SLAM 1424 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1429 #ifdef RTABMAP_G2O_CPP11 1430 linearSolver = g2o::make_unique<g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType> >();
1432 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1435 #ifdef G2O_HAVE_CHOLMOD 1439 #ifdef RTABMAP_G2O_CPP11 1440 linearSolver = g2o::make_unique<g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType> >();
1442 linearSolver =
new g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType>();
1446 #ifdef G2O_HAVE_CSPARSE 1450 #ifdef RTABMAP_G2O_CPP11 1451 linearSolver = g2o::make_unique<g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType> >();
1453 linearSolver =
new g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType>();
1460 #ifdef RTABMAP_G2O_CPP11 1461 linearSolver = g2o::make_unique<g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType> >();
1463 linearSolver =
new g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType>();
1466 #endif // RTABMAP_ORB_SLAM 1468 #ifndef RTABMAP_ORB_SLAM 1471 #ifdef RTABMAP_G2O_CPP11 1472 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
1473 g2o::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1475 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
new g2o::BlockSolver_6_3(linearSolver)));
1481 #if defined(RTABMAP_G2O_CPP11) and not defined(RTABMAP_ORB_SLAM) 1482 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
1483 g2o::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1485 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
new g2o::BlockSolver_6_3(linearSolver)));
1490 UDEBUG(
"fill poses to g2o...");
1491 for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
1496 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(iter->first);
1497 UASSERT(iterModel != models.end() && !iterModel->second.empty());
1498 for(
size_t i=0; i<iterModel->second.size(); ++i)
1500 UASSERT(iterModel->second[i].isValidForProjection());
1502 Transform camPose = iter->second * iterModel->second[i].localTransform();
1506 #ifdef RTABMAP_ORB_SLAM 1507 g2o::VertexSE3Expmap * vCam =
new g2o::VertexSE3Expmap();
1509 g2o::VertexCam * vCam =
new g2o::VertexCam();
1512 Eigen::Affine3d a = camPose.toEigen3d();
1513 #ifdef RTABMAP_ORB_SLAM 1515 vCam->setEstimate(g2o::SE3Quat(a.linear(), a.translation()));
1517 g2o::SBACam
cam(Eigen::Quaterniond(a.linear()), a.translation());
1519 iterModel->second[i].fx(),
1520 iterModel->second[i].fy(),
1521 iterModel->second[i].cx(),
1522 iterModel->second[i].cy(),
1523 iterModel->second[i].Tx()<0.0?-iterModel->second[i].Tx()/iterModel->second[i].fx():
baseline_);
1524 vCam->setEstimate(
cam);
1529 vCam->setFixed((rootId >= 0 && iter->first == rootId) || (rootId < 0 && iter->first != -rootId));
1543 UASSERT_MSG(optimizer.addVertex(vCam),
uFormat(
"cannot insert cam vertex %d (pose=%d)!?", vCam->id(), iter->first).c_str());
1548 UDEBUG(
"fill edges to g2o...");
1549 for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
1551 if(iter->second.from() > 0 &&
1552 iter->second.to() > 0 &&
1553 uContains(poses, iter->second.from()) &&
1557 int id1 = iter->second.from();
1558 int id2 = iter->second.to();
1562 #ifndef RTABMAP_ORB_SLAM 1563 g2o::HyperGraph::Edge * edge = 0;
1566 Eigen::Matrix<double, 6, 1> m;
1568 m.head<3>() = Eigen::Vector3d::UnitZ();
1571 iter->second.transform().getEulerAngles(roll, pitch, yaw);
1576 g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1*
MULTICAM_OFFSET);
1578 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(iter->first);
1580 UASSERT(iterModel != models.end() && !iterModel->second.empty() && !iterModel->second[0].localTransform().isNull());
1583 priorEdge->setInformation(information);
1584 priorEdge->vertices()[0] = v1;
1587 if (edge && !optimizer.addEdge(edge))
1590 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1591 return optimizedPoses;
1595 else if(id1>0 && id2>0)
1597 UASSERT(!iter->second.transform().isNull());
1599 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
1602 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
1606 Transform camLink = models.at(id1)[0].localTransform().
inverse()*iter->second.transform()*models.at(id2)[0].localTransform();
1613 #ifdef RTABMAP_ORB_SLAM 1614 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1615 g2o::VertexSE3Expmap* v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id1*
MULTICAM_OFFSET);
1616 g2o::VertexSE3Expmap* v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id2*MULTICAM_OFFSET);
1621 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1623 g2o::EdgeSBACam * e =
new g2o::EdgeSBACam();
1624 g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1*MULTICAM_OFFSET);
1625 g2o::VertexCam* v2 = (g2o::VertexCam*)optimizer.vertex(id2*MULTICAM_OFFSET);
1629 e->setVertex(0, v1);
1630 e->setVertex(1, v2);
1631 Eigen::Affine3d a = camLink.toEigen3d();
1632 e->setMeasurement(g2o::SE3Quat(a.linear(), a.translation()));
1633 e->setInformation(information);
1635 if (!optimizer.addEdge(e))
1638 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1639 return optimizedPoses;
1645 UDEBUG(
"fill hard edges between camera 0 and other cameras (multicam)...");
1646 for(std::map<
int, std::vector<CameraModel> >::const_iterator iter=models.begin(); iter!=models.end(); ++iter)
1648 int id = iter->first;
1651 for(
size_t i=1; i<iter->second.size(); ++i)
1655 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity()*9999999;
1658 Transform camLink = iter->second[0].localTransform().
inverse()*iter->second[i].localTransform();
1659 #ifdef RTABMAP_ORB_SLAM 1660 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1661 g2o::VertexSE3Expmap* v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(
id*
MULTICAM_OFFSET);
1662 g2o::VertexSE3Expmap* v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(
id*MULTICAM_OFFSET+i);
1667 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1669 g2o::EdgeSBACam * e =
new g2o::EdgeSBACam();
1670 g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(
id*MULTICAM_OFFSET);
1671 g2o::VertexCam* v2 = (g2o::VertexCam*)optimizer.vertex(
id*MULTICAM_OFFSET+i);
1681 e->setVertex(0, v1);
1682 e->setVertex(1, v2);
1683 Eigen::Affine3d a = camLink.toEigen3d();
1684 e->setMeasurement(g2o::SE3Quat(a.linear(), a.translation()));
1685 e->setInformation(information);
1687 if (!optimizer.addEdge(e))
1690 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", v1->id(), v2->id());
1691 return optimizedPoses;
1697 UDEBUG(
"fill 3D points to g2o...");
1699 int negVertexOffset = stepVertexId;
1700 if(wordReferences.size() && wordReferences.rbegin()->first>0)
1702 negVertexOffset += wordReferences.rbegin()->first;
1704 UDEBUG(
"stepVertexId=%d, negVertexOffset=%d", stepVertexId, negVertexOffset);
1705 std::list<g2o::OptimizableGraph::Edge*> edges;
1706 for(std::map<
int, std::map<int, FeatureBA> >::const_iterator iter = wordReferences.begin(); iter!=wordReferences.end(); ++iter)
1708 int id = iter->first;
1709 if(points3DMap.find(
id) != points3DMap.end())
1711 cv::Point3f pt3d = points3DMap.at(
id);
1714 UWARN(
"Ignoring 3D point %d because it has nan value(s)!",
id);
1717 g2o::VertexSBAPointXYZ* vpt3d =
new g2o::VertexSBAPointXYZ();
1719 vpt3d->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1722 vpt3d->setId(negVertexOffset +
id*-1);
1726 vpt3d->setId(stepVertexId +
id);
1729 vpt3d->setMarginalized(
true);
1730 optimizer.addVertex(vpt3d);
1735 for(std::map<int, FeatureBA>::const_iterator jter=iter->second.begin(); jter!=iter->second.end(); ++jter)
1737 int poseId = jter->first;
1738 int camIndex = jter->second.cameraIndex;
1739 int camId = poseId*MULTICAM_OFFSET+camIndex;
1740 if(poses.find(poseId) != poses.end() && optimizer.vertex(camId) != 0)
1743 double depth = pt.
depth;
1747 g2o::OptimizableGraph::Edge *
e;
1748 double baseline = 0.0;
1749 #ifdef RTABMAP_ORB_SLAM 1750 g2o::VertexSE3Expmap* vcam =
dynamic_cast<g2o::VertexSE3Expmap*
>(optimizer.vertex(camId));
1751 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(poseId);
1753 UASSERT(iterModel != models.end() && camIndex<iterModel->second.size() && iterModel->second[camIndex].isValidForProjection());
1754 baseline = iterModel->second[camIndex].Tx()<0.0?-iterModel->second[camIndex].Tx()/iterModel->second[camIndex].fx():
baseline_;
1756 g2o::VertexCam* vcam =
dynamic_cast<g2o::VertexCam*
>(optimizer.vertex(camId));
1757 baseline = vcam->estimate().baseline;
1760 if(
uIsFinite(depth) && depth > 0.0 && baseline > 0.0)
1763 #ifdef RTABMAP_ORB_SLAM 1764 g2o::EdgeStereoSE3ProjectXYZ* es =
new g2o::EdgeStereoSE3ProjectXYZ();
1765 float disparity = baseline * iterModel->second[camIndex].fx() / depth;
1766 Eigen::Vector3d obs( pt.
kpt.pt.x, pt.
kpt.pt.y, pt.
kpt.pt.x-disparity);
1767 es->setMeasurement(obs);
1769 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1770 es->fx = iterModel->second[camIndex].fx();
1771 es->fy = iterModel->second[camIndex].fy();
1772 es->cx = iterModel->second[camIndex].cx();
1773 es->cy = iterModel->second[camIndex].cy();
1774 es->bf = baseline*es->fx;
1777 g2o::EdgeProjectP2SC* es =
new g2o::EdgeProjectP2SC();
1778 float disparity = baseline * vcam->estimate().Kcam(0,0) / depth;
1779 Eigen::Vector3d obs( pt.
kpt.pt.x, pt.
kpt.pt.y, pt.
kpt.pt.x-disparity);
1780 es->setMeasurement(obs);
1782 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1790 UDEBUG(
"Stereo camera model detected but current " 1791 "observation (pt=%d to cam=%d, kpt=[%d,%d]) has null depth (%f m), adding " 1792 "mono observation instead.",
1793 vpt3d->id()-stepVertexId, camId, (int)pt.
kpt.pt.x, (
int)pt.
kpt.pt.y, depth);
1796 #ifdef RTABMAP_ORB_SLAM 1797 g2o::EdgeSE3ProjectXYZ* em =
new g2o::EdgeSE3ProjectXYZ();
1798 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1799 em->setMeasurement(obs);
1800 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1801 em->fx = iterModel->second[camIndex].fx();
1802 em->fy = iterModel->second[camIndex].fy();
1803 em->cx = iterModel->second[camIndex].cx();
1804 em->cy = iterModel->second[camIndex].cy();
1808 g2o::EdgeProjectP2MC* em =
new g2o::EdgeProjectP2MC();
1809 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1810 em->setMeasurement(obs);
1811 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1815 e->setVertex(0, vpt3d);
1816 e->setVertex(1, vcam);
1820 g2o::RobustKernelHuber* kernel =
new g2o::RobustKernelHuber;
1822 e->setRobustKernel(kernel);
1825 optimizer.addEdge(e);
1832 UDEBUG(
"Initial optimization...");
1833 optimizer.initializeOptimization();
1835 UASSERT(optimizer.verifyInformationMatrices());
1841 int outliersCount = 0;
1842 int outliersCountFar = 0;
1844 for(
int i=0; i<(robustKernelDelta_>0.0?2:1); ++i)
1849 optimizer.computeActiveErrors();
1850 double chi2 = optimizer.activeRobustChi2();
1854 UERROR(
"Optimization generated NANs, aborting optimization! Try another g2o's optimizer (current=%d).",
optimizer_);
1855 return optimizedPoses;
1857 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f", i, (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
1859 if(i>0 && (optimizer.activeRobustChi2() > 1000000000000.0 || !
uIsFinite(optimizer.activeRobustChi2())))
1861 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1862 return optimizedPoses;
1867 for(std::list<g2o::OptimizableGraph::Edge*>::iterator iter=edges.begin(); iter!=edges.end();++iter)
1869 if((*iter)->level() == 0 && (*iter)->chi2() > (*iter)->robustKernel()->delta())
1871 (*iter)->setLevel(1);
1874 #ifdef RTABMAP_ORB_SLAM 1875 if(dynamic_cast<g2o::EdgeStereoSE3ProjectXYZ*>(*iter) != 0)
1877 d = ((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[0]-((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[2];
1881 if(dynamic_cast<g2o::EdgeProjectP2SC*>(*iter) != 0)
1883 d = ((g2o::EdgeProjectP2SC*)(*iter))->measurement()[0]-((g2o::EdgeProjectP2SC*)(*iter))->measurement()[2];
1889 if((*iter)->vertex(0)->id() > negVertexOffset)
1891 pt3d = points3DMap.at(negVertexOffset - (*iter)->vertex(0)->id());
1895 pt3d = points3DMap.at((*iter)->vertex(0)->id()-stepVertexId);
1897 ((g2o::VertexSBAPointXYZ*)(*iter)->vertex(0))->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1901 outliers->insert((*iter)->vertex(0)->id()-stepVertexId);
1911 optimizer.initializeOptimization(0);
1912 UDEBUG(
"outliers=%d outliersCountFar=%d", outliersCount, outliersCountFar);
1915 UDEBUG(
"g2o optimizing end (%d iterations done, error=%f, outliers=%d/%d (delta=%f) time = %f s)", it, optimizer.activeRobustChi2(), outliersCount, (int)edges.size(),
robustKernelDelta_, timer.
ticks());
1917 if(optimizer.activeRobustChi2() > 1000000000000.0)
1919 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1920 return optimizedPoses;
1924 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1929 #ifdef RTABMAP_ORB_SLAM 1930 const g2o::VertexSE3Expmap* v = (
const g2o::VertexSE3Expmap*)optimizer.vertex(camId);
1932 const g2o::VertexCam* v = (
const g2o::VertexCam*)optimizer.vertex(camId);
1938 #ifdef RTABMAP_ORB_SLAM 1943 t *= models.at(iter->first)[0].localTransform().
inverse();
1948 UERROR(
"Optimized pose %d is null!?!?", iter->first);
1949 optimizedPoses.clear();
1950 return optimizedPoses;
1957 t = iter->second.inverse() * t;
1958 optimizedPoses.insert(std::pair<int, Transform>(iter->first, iter->second * t.to3DoF()));
1962 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
1967 UERROR(
"Vertex (pose) %d (cam=%d) not found!?", iter->first, camId);
1974 for(std::map<int, cv::Point3f>::iterator iter = points3DMap.begin(); iter!=points3DMap.end(); ++iter)
1976 const g2o::VertexSBAPointXYZ* v;
1977 int id = iter->first;
1980 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(negVertexOffset +
id*-1);
1984 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(stepVertexId +
id);
1989 cv::Point3f p(v->estimate()[0], v->estimate()[1], v->estimate()[2]);
1995 iter->second.x = iter->second.y = iter->second.z = std::numeric_limits<float>::quiet_NaN();
1999 else if(poses.size() > 1 && (poses.size() != models.size() && poses.begin()->first > 0))
2001 UERROR(
"This method should be called with size of poses = size camera models!");
2003 else if(poses.size() == 1 ||
iterations() <= 0)
2005 optimizedPoses = poses;
2009 UWARN(
"This method should be called at least with 1 pose!");
2011 UDEBUG(
"Optimizing graph...end!");
2013 UERROR(
"Not built with G2O support!");
2015 return optimizedPoses;
2019 const std::string & fileName,
2020 const std::map<int, Transform> & poses,
2021 const std::multimap<int, Link> & edgeConstraints)
2026 fopen_s(&file, fileName.c_str(),
"w");
2028 file = fopen(fileName.c_str(),
"w");
2034 setlocale(LC_ALL,
"en_US.UTF-8");
2039 fprintf(file,
"PARAMS_SE2OFFSET %d 0 0 0\n",
PARAM_OFFSET);
2044 Eigen::Vector3f v = Eigen::Vector3f::Zero();
2045 Eigen::Quaternionf
q = Eigen::Quaternionf::Identity();
2046 fprintf(file,
"PARAMS_SE3OFFSET %d %f %f %f %f %f %f %f\n",
2057 int landmarkOffset = poses.size()&&poses.rbegin()->first>0?poses.rbegin()->first+1:0;
2058 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
2065 fprintf(file,
"VERTEX_SE2 %d %f %f %f\n",
2066 landmarkOffset-iter->first,
2069 iter->second.theta());
2074 fprintf(file,
"VERTEX_XY %d %f %f\n",
2085 Eigen::Quaternionf
q = iter->second.getQuaternionf();
2086 fprintf(file,
"VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
2099 fprintf(file,
"VERTEX_TRACKXYZ %d %f %f %f\n",
2100 landmarkOffset-iter->first,
2108 int virtualVertexId = landmarkOffset - (poses.size()&&poses.rbegin()->first<0?poses.rbegin()->first:0);
2109 for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
2120 fprintf(file,
"EDGE_SE2_XY %d %d %f %f %f %f %f\n",
2121 iter->second.from()<0?landmarkOffset-iter->second.from():iter->second.from(),
2122 iter->second.to()<0?landmarkOffset-iter->second.to():iter->second.to(),
2123 iter->second.transform().x(),
2124 iter->second.transform().y(),
2125 iter->second.infMatrix().at<
double>(0, 0),
2126 iter->second.infMatrix().at<
double>(0, 1),
2127 iter->second.infMatrix().at<
double>(1, 1));
2132 fprintf(file,
"EDGE_SE3_TRACKXYZ %d %d %d %f %f %f %f %f %f %f %f %f\n",
2133 iter->second.from()<0?landmarkOffset-iter->second.from():iter->second.from(),
2134 iter->second.to()<0?landmarkOffset-iter->second.to():iter->second.to(),
2136 iter->second.transform().x(),
2137 iter->second.transform().y(),
2138 iter->second.transform().z(),
2139 iter->second.infMatrix().at<
double>(0, 0),
2140 iter->second.infMatrix().at<
double>(0, 1),
2141 iter->second.infMatrix().at<
double>(0, 2),
2142 iter->second.infMatrix().at<
double>(1, 1),
2143 iter->second.infMatrix().at<
double>(1, 2),
2144 iter->second.infMatrix().at<
double>(2, 2));
2149 std::string prefix =
isSlam2d()?
"EDGE_SE2" :
"EDGE_SE3:QUAT";
2150 std::string suffix =
"";
2151 std::string to =
uFormat(
" %d", iter->second.to());
2168 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2170 prefix =
"EDGE_PRIOR_SE2_XY";
2175 prefix =
"EDGE_PRIOR_SE2";
2184 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
2185 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
2186 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2189 prefix =
"EDGE_POINTXYZ_PRIOR";
2195 prefix =
"EDGE_SE3_PRIOR";
2203 fprintf(file,
"VERTEX_SWITCH %d 1\n", virtualVertexId);
2204 fprintf(file,
"EDGE_SWITCH_PRIOR %d 1 1.0\n", virtualVertexId);
2205 prefix =
isSlam2d() ?
"EDGE_SE2_SWITCHABLE" :
"EDGE_SE3_SWITCHABLE";
2206 suffix =
uFormat(
" %d", virtualVertexId++);
2215 fprintf(file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2217 iter->second.from(),
2220 iter->second.transform().x(),
2221 iter->second.transform().y(),
2222 iter->second.transform().theta(),
2223 iter->second.infMatrix().at<
double>(0, 0),
2224 iter->second.infMatrix().at<
double>(0, 1),
2225 iter->second.infMatrix().at<
double>(0, 5),
2226 iter->second.infMatrix().at<
double>(1, 1),
2227 iter->second.infMatrix().at<
double>(1, 5),
2228 iter->second.infMatrix().at<
double>(5, 5));
2234 fprintf(file,
"%s %d%s%s %f %f %f %f %f\n",
2236 iter->second.from(),
2239 iter->second.transform().x(),
2240 iter->second.transform().y(),
2241 iter->second.infMatrix().at<
double>(0, 0),
2242 iter->second.infMatrix().at<
double>(0, 1),
2243 iter->second.infMatrix().at<
double>(1, 1));
2252 Eigen::Quaternionf
q = iter->second.transform().getQuaternionf();
2253 fprintf(file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n",
2255 iter->second.from(),
2258 iter->second.transform().x(),
2259 iter->second.transform().y(),
2260 iter->second.transform().z(),
2265 iter->second.infMatrix().at<
double>(0, 0),
2266 iter->second.infMatrix().at<
double>(0, 1),
2267 iter->second.infMatrix().at<
double>(0, 2),
2268 iter->second.infMatrix().at<
double>(0, 3),
2269 iter->second.infMatrix().at<
double>(0, 4),
2270 iter->second.infMatrix().at<
double>(0, 5),
2271 iter->second.infMatrix().at<
double>(1, 1),
2272 iter->second.infMatrix().at<
double>(1, 2),
2273 iter->second.infMatrix().at<
double>(1, 3),
2274 iter->second.infMatrix().at<
double>(1, 4),
2275 iter->second.infMatrix().at<
double>(1, 5),
2276 iter->second.infMatrix().at<
double>(2, 2),
2277 iter->second.infMatrix().at<
double>(2, 3),
2278 iter->second.infMatrix().at<
double>(2, 4),
2279 iter->second.infMatrix().at<
double>(2, 5),
2280 iter->second.infMatrix().at<
double>(3, 3),
2281 iter->second.infMatrix().at<
double>(3, 4),
2282 iter->second.infMatrix().at<
double>(3, 5),
2283 iter->second.infMatrix().at<
double>(4, 4),
2284 iter->second.infMatrix().at<
double>(4, 5),
2285 iter->second.infMatrix().at<
double>(5, 5));
2291 fprintf(file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2293 iter->second.from(),
2296 iter->second.transform().x(),
2297 iter->second.transform().y(),
2298 iter->second.transform().z(),
2299 iter->second.infMatrix().at<
double>(0, 0),
2300 iter->second.infMatrix().at<
double>(0, 1),
2301 iter->second.infMatrix().at<
double>(0, 2),
2302 iter->second.infMatrix().at<
double>(1, 1),
2303 iter->second.infMatrix().at<
double>(1, 2),
2304 iter->second.infMatrix().at<
double>(2, 2));
2308 UINFO(
"Graph saved to %s", fileName.c_str());
2314 UERROR(
"Cannot save to file %s", fileName.c_str());
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
bool RTABMAP_EXP isFinite(const cv::Point3f &pt)
static bool parse(const ParametersMap ¶meters, const std::string &key, bool &value)
rtabmap::CameraThread * cam
virtual void setMeasurement(const Vector3d &m)
bool saveGraph(const std::string &fileName, const std::map< int, Transform > &poses, const std::multimap< int, Link > &edgeConstraints)
prior for an XYZ vertex (VertexPointXYZ)
g2o edge with gravity constraint
GLM_FUNC_DECL genType e()
double robustKernelDelta_
std::map< std::string, std::string > ParametersMap
void swap(linb::any &lhs, linb::any &rhs) noexcept
Basic mathematics functions.
Some conversion functions.
virtual void setMeasurement(const Vector2d &m)
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
virtual std::map< int, Transform > optimizeBA(int rootId, const std::map< int, Transform > &poses, const std::multimap< int, Link > &links, const std::map< int, std::vector< CameraModel > > &models, std::map< int, cv::Point3f > &points3DMap, const std::map< int, std::map< int, FeatureBA > > &wordReferences, std::set< int > *outliers=0)
float gravitySigma() const
virtual void setMeasurement(const Eigen::Isometry3d &m)
bool uIsFinite(const T &value)
#define UASSERT(condition)
Wrappers of STL for convenient functions.
virtual std::map< int, Transform > optimize(int rootId, const std::map< int, Transform > &poses, const std::multimap< int, Link > &edgeConstraints, cv::Mat &outputCovariance, std::list< std::map< int, Transform > > *intermediateGraphes=0, double *finalError=0, int *iterationsDone=0)
virtual void parseParameters(const ParametersMap ¶meters)
bool landmarksIgnored() const
#define UASSERT_MSG(condition, msg_str)
virtual void setMeasurement(const Vector3d &m)
virtual void setMeasurement(const Eigen::Matrix< double, 6, 1 > &m)
bool uContains(const std::list< V > &list, const V &value)
virtual void setMeasurement(const g2o::SE2 &m)
void setRobust(bool enabled)
ULogger class and convenient macros.
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
bool uIsNan(const T &value)
Prior for a 3D pose with constraints only in xyz direction.
virtual void setEstimate(const double &et)
virtual void setMeasurement(const Eigen::Matrix< double, 6, 1 > &m)
bool isCovarianceIgnored() const
virtual void parseParameters(const ParametersMap ¶meters)
bool priorsIgnored() const
std::string UTILITE_EXP uFormat(const char *fmt,...)
void setCameraInvLocalTransform(const Eigen::Matrix3d &t)
GLM_FUNC_DECL matType< T, P > inverse(matType< T, P > const &m)