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"
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
119 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
128 #ifdef G2O_HAVE_CSPARSE
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) && RTABMAP_G2O_CPP11 == 1
157 g2o::Factory::instance()->registerType(
"CACHE_SE3_OFFSET", std::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 = std::make_unique<SlamLinearEigenSolver>();
254 linearSolver->setBlockOrdering(
false);
255 blockSolver = std::make_unique<SlamBlockSolver>(std::move(linearSolver));
257 #ifdef G2O_HAVE_CHOLMOD
261 auto linearSolver = std::make_unique<SlamLinearCholmodSolver>();
262 linearSolver->setBlockOrdering(
false);
263 blockSolver = std::make_unique<SlamBlockSolver>(std::move(linearSolver));
266 #ifdef G2O_HAVE_CSPARSE
271 auto linearSolver = std::make_unique<SlamLinearCSparseSolver>();
272 linearSolver->setBlockOrdering(
false);
273 blockSolver = std::make_unique<SlamBlockSolver>(std::move(linearSolver));
279 auto linearSolver = std::make_unique<SlamLinearPCGSolver>();
280 blockSolver = std::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)
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();
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();
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);
489 if(!
isSlam2d() && rootId !=0 && hasGravityConstraints)
491 g2o::VertexSE3*
v1 =
dynamic_cast<g2o::VertexSE3*
>(optimizer.vertex(rootId));
494 g2o::EdgeSE3Prior *
e =
new g2o::EdgeSE3Prior();
500 e->setMeasurement(pose);
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();
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);
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()));
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()));
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()));
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()));
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);
671 priorEdge->setMeasurement(pose);
676 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
678 priorEdge->setInformation(information);
687 m.head<3>() = Eigen::Vector3d::UnitZ();
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;
723 #if defined(RTABMAP_VERTIGO)
725 if(this->
isRobust() && isLandmarkWithRotation.at(idTag))
737 v->setId(vertigoVertexId++);
738 UASSERT_MSG(optimizer.addVertex(
v),
uFormat(
"cannot insert switchable vertex %d!?",
v->id()).c_str());
746 prior->setMeasurement(1.0);
750 else if(this->
isRobust() && !isLandmarkWithRotation.at(idTag))
752 UWARN(
"%s cannot be used for landmark constraints without orientation.", Parameters::kOptimizerRobust().
c_str());
758 if(isLandmarkWithRotation.at(idTag))
763 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
764 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
765 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
766 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
767 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
768 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
769 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
770 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
771 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
773 #if defined(RTABMAP_VERTIGO)
777 g2o::VertexSE2*
v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
778 g2o::VertexSE2*
v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
784 e->setMeasurement(g2o::SE2(
t.
x(),
t.
y(),
t.theta()));
785 e->setInformation(information);
791 g2o::EdgeSE2 *
e =
new g2o::EdgeSE2();
792 g2o::VertexSE2*
v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
793 g2o::VertexSE2*
v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
798 e->setMeasurement(g2o::SE2(
t.
x(),
t.
y(),
t.theta()));
799 e->setInformation(information);
808 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,2), cv::Range(0,2)).clone();
809 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
811 g2o::EdgeSE2PointXY*
e =
new g2o::EdgeSE2PointXY;
812 e->vertices()[0] = optimizer.vertex(id1);
813 e->vertices()[1] = optimizer.vertex(id2);
814 e->setMeasurement(Eigen::Vector2d(
t.
x(),
t.
y()));
815 e->setInformation(information);
822 if(isLandmarkWithRotation.at(idTag))
827 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
832 constraint =
a.linear();
835 #if defined(RTABMAP_VERTIGO)
839 g2o::VertexSE3*
v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
840 g2o::VertexSE3*
v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
846 e->setMeasurement(constraint);
847 e->setInformation(information);
853 g2o::EdgeSE3 *
e =
new g2o::EdgeSE3();
854 g2o::VertexSE3*
v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
855 g2o::VertexSE3*
v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
860 e->setMeasurement(constraint);
861 e->setInformation(information);
870 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,3), cv::Range(0,3)).clone();
871 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
874 g2o::EdgeSE3PointXYZ*
e =
new g2o::EdgeSE3PointXYZ;
875 e->vertices()[0] = optimizer.vertex(id1);
876 e->vertices()[1] = optimizer.vertex(id2);
877 e->setMeasurement(Eigen::Vector3d(
t.
x(),
t.
y(),
t.z()));
878 e->setInformation(information);
887 #if defined(RTABMAP_VERTIGO)
902 v->setId(vertigoVertexId++);
903 UASSERT_MSG(optimizer.addVertex(
v),
uFormat(
"cannot insert switchable vertex %d!?",
v->id()).c_str());
911 prior->setMeasurement(1.0);
922 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
923 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
924 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
925 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
926 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
927 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
928 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
929 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
930 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
933 #if defined(RTABMAP_VERTIGO)
939 g2o::VertexSE2*
v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
940 g2o::VertexSE2*
v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
946 e->setMeasurement(g2o::SE2(
iter->second.transform().x(),
iter->second.transform().y(),
iter->second.transform().theta()));
947 e->setInformation(information);
953 g2o::EdgeSE2 *
e =
new g2o::EdgeSE2();
954 g2o::VertexSE2*
v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
955 g2o::VertexSE2*
v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
960 e->setMeasurement(g2o::SE2(
iter->second.transform().x(),
iter->second.transform().y(),
iter->second.transform().theta()));
961 e->setInformation(information);
970 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
975 constraint =
a.linear();
978 #if defined(RTABMAP_VERTIGO)
984 g2o::VertexSE3*
v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
985 g2o::VertexSE3*
v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
991 e->setMeasurement(constraint);
992 e->setInformation(information);
998 g2o::EdgeSE3 *
e =
new g2o::EdgeSE3();
999 g2o::VertexSE3*
v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
1000 g2o::VertexSE3*
v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
1003 e->setVertex(0,
v1);
1004 e->setVertex(1,
v2);
1005 e->setMeasurement(constraint);
1006 e->setInformation(information);
1012 if (edge && !optimizer.addEdge(edge))
1015 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1021 UDEBUG(
"Initial optimization...");
1022 optimizer.initializeOptimization();
1024 if(!optimizer.verifyInformationMatrices(
true))
1026 UERROR(
"This error can be caused by (1) bad covariance matrix "
1027 "set in odometry messages "
1028 "(see requirements in g2o::OptimizableGraph::verifyInformationMatrices() function) "
1029 "or that (2) PCL and g2o hadn't "
1030 "been built both with or without \"-march=native\" compilation "
1031 "flag (if one library is built with this flag and not the other, "
1032 "this is causing Eigen to not work properly, resulting in segmentation faults).");
1033 return optimizedPoses;
1039 double lastError = 0.0;
1040 if(intermediateGraphes || this->
epsilon() > 0.0)
1044 if(intermediateGraphes)
1048 std::map<int, Transform> tmpPoses;
1051 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1053 int id =
iter->first;
1056 const g2o::VertexSE2*
v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
1061 Transform t(
v->estimate().translation()[0],
v->estimate().translation()[1],
iter->second.z(),
roll,
pitch,
v->estimate().rotation().angle());
1062 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1067 UERROR(
"Vertex %d not found!?",
id);
1072 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1075 if(isLandmarkWithRotation.at(
id))
1077 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)
v;
1080 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1],
iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
1081 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1086 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)
v;
1090 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1096 UERROR(
"Vertex %d not found!?",
id);
1103 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1105 int id =
iter->first;
1108 const g2o::VertexSE3*
v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
1112 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1117 UERROR(
"Vertex %d not found!?",
id);
1122 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1125 if(isLandmarkWithRotation.at(
id))
1127 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)
v;
1129 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1134 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)
v;
1138 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1144 UERROR(
"Vertex %d not found!?",
id);
1149 intermediateGraphes->push_back(tmpPoses);
1153 it += optimizer.optimize(1);
1156 optimizer.computeActiveErrors();
1157 double chi2 = optimizer.activeRobustChi2();
1158 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f",
i, (
int)optimizer.vertices().size(), (
int)optimizer.edges().size(), chi2);
1160 if(
i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
1162 UERROR(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
1163 return optimizedPoses;
1166 double errorDelta = lastError - chi2;
1167 if(
i>0 && errorDelta < this->
epsilon())
1171 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
1175 UINFO(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
1179 else if(
i==0 && chi2 < this->
epsilon())
1181 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)", chi2, this->
epsilon());
1190 optimizer.computeActiveErrors();
1191 UDEBUG(
"%d nodes, %d edges, chi2: %f", (
int)optimizer.vertices().size(), (
int)optimizer.edges().size(), optimizer.activeRobustChi2());
1195 *finalError = lastError;
1199 *iterationsDone = it;
1201 UINFO(
"g2o optimizing end (%d iterations done, error=%f, time = %f s)", it, optimizer.activeRobustChi2(),
timer.ticks());
1203 if(optimizer.activeRobustChi2() > 1000000000000.0)
1205 UERROR(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
1206 return optimizedPoses;
1211 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1213 int id =
iter->first;
1216 const g2o::VertexSE2*
v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
1221 Transform t(
v->estimate().translation()[0],
v->estimate().translation()[1],
iter->second.z(),
roll,
pitch,
v->estimate().rotation().angle());
1222 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1227 UERROR(
"Vertex %d not found!?",
id);
1232 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1236 if(isLandmarkWithRotation.at(
id))
1238 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)
v;
1241 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1],
iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
1242 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1247 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)
v;
1251 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1257 UERROR(
"Vertex %d not found!?",
id);
1262 g2o::VertexSE2*
v = (g2o::VertexSE2*)optimizer.vertex(poses.rbegin()->first);
1266 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
1267 optimizer.computeMarginals(spinv,
v);
1268 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)",
t.ticks(), spinv.cols(), spinv.rows(),
v->hessianIndex(), poses.rbegin()->first);
1269 if(
v->hessianIndex() >= 0 &&
v->hessianIndex() < (
int)spinv.blockCols().size())
1271 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock *
block = spinv.blockCols()[
v->hessianIndex()].begin()->second;
1273 outputCovariance.at<
double>(0,0) = (*
block)(0,0);
1274 outputCovariance.at<
double>(0,1) = (*
block)(0,1);
1275 outputCovariance.at<
double>(0,5) = (*
block)(0,2);
1276 outputCovariance.at<
double>(1,0) = (*
block)(1,0);
1277 outputCovariance.at<
double>(1,1) = (*
block)(1,1);
1278 outputCovariance.at<
double>(1,5) = (*
block)(1,2);
1279 outputCovariance.at<
double>(5,0) = (*
block)(2,0);
1280 outputCovariance.at<
double>(5,1) = (*
block)(2,1);
1281 outputCovariance.at<
double>(5,5) = (*
block)(2,2);
1283 else if(
v->hessianIndex() < 0)
1285 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first,
v->hessianIndex());
1289 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());
1294 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1299 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1301 int id =
iter->first;
1304 const g2o::VertexSE3*
v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
1308 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1313 UERROR(
"Vertex %d not found!?",
id);
1318 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1322 if(isLandmarkWithRotation.at(
id))
1324 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)
v;
1326 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1331 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)
v;
1335 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1341 UERROR(
"Vertex %d not found!?",
id);
1346 g2o::VertexSE3*
v = (g2o::VertexSE3*)optimizer.vertex(poses.rbegin()->first);
1350 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
1351 optimizer.computeMarginals(spinv,
v);
1352 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)",
t.ticks(), spinv.cols(), spinv.rows(),
v->hessianIndex(), poses.rbegin()->first);
1353 if(
v->hessianIndex() >= 0 &&
v->hessianIndex() < (
int)spinv.blockCols().size())
1355 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock *
block = spinv.blockCols()[
v->hessianIndex()].begin()->second;
1357 memcpy(outputCovariance.data,
block->data(), outputCovariance.total()*
sizeof(
double));
1359 else if(
v->hessianIndex() < 0)
1361 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first,
v->hessianIndex());
1363 #ifdef RTABMAP_G2O_CPP11
1366 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());
1372 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1376 else if(poses.size() == 1 ||
iterations() <= 0)
1378 optimizedPoses = poses;
1382 UWARN(
"This method should be called at least with 1 pose!");
1384 UDEBUG(
"Optimizing graph...end!");
1386 #ifdef RTABMAP_ORB_SLAM
1387 UERROR(
"G2O graph optimization cannot be used with g2o built from ORB_SLAM, only SBA is available.");
1389 UERROR(
"Not built with G2O support!");
1392 return optimizedPoses;
1395 #ifdef RTABMAP_ORB_SLAM
1399 class EdgeSE3Expmap :
public g2o::BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>
1403 EdgeSE3Expmap(): BaseBinaryEdge<6,
g2o::SE3Quat,
g2o::VertexSE3Expmap,
g2o::VertexSE3Expmap>(){}
1404 bool read(std::istream& is)
1409 bool write(std::ostream& os)
const
1416 const g2o::VertexSE3Expmap*
v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1417 const g2o::VertexSE3Expmap*
v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1418 g2o::SE3Quat
delta = _inverseMeasurement * (
v1->estimate().inverse()*
v2->estimate());
1419 _error[0]=
delta.translation().x();
1420 _error[1]=
delta.translation().y();
1421 _error[2]=
delta.translation().z();
1422 _error[3]=
delta.rotation().x();
1423 _error[4]=
delta.rotation().y();
1424 _error[5]=
delta.rotation().z();
1427 virtual void setMeasurement(
const g2o::SE3Quat& meas){
1429 _inverseMeasurement=meas.inverse();
1432 virtual double initialEstimatePossible(
const g2o::OptimizableGraph::VertexSet& , g2o::OptimizableGraph::Vertex* ) {
return 1.;}
1433 virtual void initialEstimate(
const g2o::OptimizableGraph::VertexSet& from_, g2o::OptimizableGraph::Vertex* ){
1434 g2o::VertexSE3Expmap* from =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[0]);
1435 g2o::VertexSE3Expmap* to =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[1]);
1436 if (from_.count(from) > 0)
1437 to->setEstimate((g2o::SE3Quat) from->estimate() * _measurement);
1439 from->setEstimate((g2o::SE3Quat) to->estimate() * _inverseMeasurement);
1442 virtual bool setMeasurementData(
const double* d){
1444 _measurement.fromVector(v);
1445 _inverseMeasurement = _measurement.inverse();
1449 virtual bool getMeasurementData(
double* d)
const{
1451 v = _measurement.toVector();
1455 virtual int measurementDimension()
const {
return 7;}
1457 virtual bool setMeasurementFromState() {
1458 const g2o::VertexSE3Expmap*
v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1459 const g2o::VertexSE3Expmap*
v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1460 _measurement = (
v1->estimate().inverse()*
v2->estimate());
1461 _inverseMeasurement = _measurement.inverse();
1466 g2o::SE3Quat _inverseMeasurement;
1472 const std::map<int, Transform> & poses,
1473 const std::multimap<int, Link> & links,
1474 const std::map<
int, std::vector<CameraModel> > & models,
1475 std::map<int, cv::Point3f> & points3DMap,
1476 const std::map<
int, std::map<int, FeatureBA> > & wordReferences,
1477 std::set<int> * outliers)
1479 std::map<int, Transform> optimizedPoses;
1480 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
1481 UDEBUG(
"Optimizing graph...");
1483 optimizedPoses.clear();
1484 if(poses.size()>=2 &&
iterations() > 0 && (models.size() == poses.size() || poses.begin()->first < 0))
1486 g2o::SparseOptimizer optimizer;
1488 #if defined(RTABMAP_G2O_CPP11) && !defined(RTABMAP_ORB_SLAM)
1489 std::unique_ptr<g2o::BlockSolver_6_3::LinearSolverType> linearSolver;
1491 g2o::BlockSolver_6_3::LinearSolverType * linearSolver = 0;
1494 #ifdef RTABMAP_ORB_SLAM
1495 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1500 #ifdef RTABMAP_G2O_CPP11
1501 linearSolver = std::make_unique<g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType> >();
1503 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1506 #ifdef G2O_HAVE_CHOLMOD
1510 #ifdef RTABMAP_G2O_CPP11
1511 linearSolver = std::make_unique<g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType> >();
1513 linearSolver =
new g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType>();
1517 #ifdef G2O_HAVE_CSPARSE
1521 #ifdef RTABMAP_G2O_CPP11
1522 linearSolver = std::make_unique<g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType> >();
1524 linearSolver =
new g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType>();
1531 #ifdef RTABMAP_G2O_CPP11
1532 linearSolver = std::make_unique<g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType> >();
1534 linearSolver =
new g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType>();
1537 #endif // RTABMAP_ORB_SLAM
1539 #ifndef RTABMAP_ORB_SLAM
1542 #ifdef RTABMAP_G2O_CPP11
1543 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
1544 std::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1546 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
new g2o::BlockSolver_6_3(linearSolver)));
1552 #if defined(RTABMAP_G2O_CPP11) && !defined(RTABMAP_ORB_SLAM)
1553 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
1554 std::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1556 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
new g2o::BlockSolver_6_3(linearSolver)));
1561 UDEBUG(
"fill poses to g2o...");
1562 for(std::map<int, Transform>::const_iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
1567 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(
iter->first);
1568 UASSERT(iterModel != models.end() && !iterModel->second.empty());
1569 for(
size_t i=0;
i<iterModel->second.size(); ++
i)
1571 UASSERT(iterModel->second[
i].isValidForProjection());
1573 Transform camPose =
iter->second * iterModel->second[
i].localTransform();
1577 #ifdef RTABMAP_ORB_SLAM
1578 g2o::VertexSE3Expmap * vCam =
new g2o::VertexSE3Expmap();
1580 g2o::VertexCam * vCam =
new g2o::VertexCam();
1584 #ifdef RTABMAP_ORB_SLAM
1586 vCam->setEstimate(g2o::SE3Quat(
a.linear(),
a.translation()));
1590 iterModel->second[
i].fx(),
1591 iterModel->second[
i].fy(),
1592 iterModel->second[
i].cx(),
1593 iterModel->second[
i].cy(),
1594 iterModel->second[
i].Tx()<0.0?-iterModel->second[
i].Tx()/iterModel->second[
i].fx():
baseline_);
1595 vCam->setEstimate(
cam);
1600 vCam->setFixed((rootId >= 0 &&
iter->first == rootId) || (rootId < 0 && iter->
first != -rootId));
1614 UASSERT_MSG(optimizer.addVertex(vCam),
uFormat(
"cannot insert cam vertex %d (pose=%d)!?", vCam->id(),
iter->first).c_str());
1619 UDEBUG(
"fill edges to g2o...");
1620 for(std::multimap<int, Link>::const_iterator
iter=links.begin();
iter!=links.end(); ++
iter)
1622 if(
iter->second.from() > 0 &&
1623 iter->second.to() > 0 &&
1628 int id1 =
iter->second.from();
1629 int id2 =
iter->second.to();
1633 #ifndef RTABMAP_ORB_SLAM
1634 g2o::HyperGraph::Edge * edge = 0;
1639 m.head<3>() = Eigen::Vector3d::UnitZ();
1649 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(
iter->first);
1651 UASSERT(iterModel != models.end() && !iterModel->second.empty() && !iterModel->second[0].localTransform().isNull());
1654 priorEdge->setInformation(information);
1655 priorEdge->vertices()[0] =
v1;
1658 if (edge && !optimizer.addEdge(edge))
1661 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1662 return optimizedPoses;
1666 else if(id1>0 && id2>0)
1673 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
1677 Transform camLink = models.at(id1)[0].localTransform().
inverse()*
iter->second.transform()*models.at(id2)[0].localTransform();
1684 #ifdef RTABMAP_ORB_SLAM
1685 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1686 g2o::VertexSE3Expmap*
v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id1*
MULTICAM_OFFSET);
1687 g2o::VertexSE3Expmap*
v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id2*
MULTICAM_OFFSET);
1692 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1694 g2o::EdgeSBACam *
e =
new g2o::EdgeSBACam();
1700 e->setVertex(0,
v1);
1701 e->setVertex(1,
v2);
1703 e->setMeasurement(g2o::SE3Quat(
a.linear(),
a.translation()));
1704 e->setInformation(information);
1706 if (!optimizer.addEdge(
e))
1709 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1710 return optimizedPoses;
1716 UDEBUG(
"fill hard edges between camera 0 and other cameras (multicam)...");
1717 for(std::map<
int, std::vector<CameraModel> >::const_iterator
iter=models.begin();
iter!=models.end(); ++
iter)
1719 int id =
iter->first;
1722 for(
size_t i=1;
i<
iter->second.size(); ++
i)
1729 Transform camLink =
iter->second[0].localTransform().inverse()*
iter->second[
i].localTransform();
1730 #ifdef RTABMAP_ORB_SLAM
1731 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1732 g2o::VertexSE3Expmap*
v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(
id*
MULTICAM_OFFSET);
1733 g2o::VertexSE3Expmap*
v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(
id*
MULTICAM_OFFSET+
i);
1738 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1740 g2o::EdgeSBACam *
e =
new g2o::EdgeSBACam();
1752 e->setVertex(0,
v1);
1753 e->setVertex(1,
v2);
1755 e->setMeasurement(g2o::SE3Quat(
a.linear(),
a.translation()));
1756 e->setInformation(information);
1758 if (!optimizer.addEdge(
e))
1761 UERROR(
"Map: Failed adding constraint between %d and %d, skipping",
v1->id(),
v2->id());
1762 return optimizedPoses;
1768 UDEBUG(
"fill 3D points to g2o...");
1770 int negVertexOffset = stepVertexId;
1771 if(wordReferences.size() && wordReferences.rbegin()->first>0)
1773 negVertexOffset += wordReferences.rbegin()->first;
1775 UDEBUG(
"stepVertexId=%d, negVertexOffset=%d", stepVertexId, negVertexOffset);
1776 std::list<g2o::OptimizableGraph::Edge*>
edges;
1777 for(std::map<
int, std::map<int, FeatureBA> >::const_iterator
iter = wordReferences.begin();
iter!=wordReferences.end(); ++
iter)
1779 int id =
iter->first;
1780 if(points3DMap.find(
id) != points3DMap.end())
1782 cv::Point3f pt3d = points3DMap.at(
id);
1785 UWARN(
"Ignoring 3D point %d because it has nan value(s)!",
id);
1788 g2o::VertexSBAPointXYZ* vpt3d =
new g2o::VertexSBAPointXYZ();
1790 vpt3d->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1793 vpt3d->setId(negVertexOffset +
id*-1);
1797 vpt3d->setId(stepVertexId +
id);
1800 vpt3d->setMarginalized(
true);
1801 optimizer.addVertex(vpt3d);
1806 for(std::map<int, FeatureBA>::const_iterator jter=
iter->second.begin(); jter!=
iter->second.end(); ++jter)
1808 int poseId = jter->first;
1809 int camIndex = jter->second.cameraIndex;
1811 if(poses.find(poseId) != poses.end() && optimizer.vertex(camId) != 0)
1814 double depth = pt.
depth;
1818 g2o::OptimizableGraph::Edge *
e;
1820 #ifdef RTABMAP_ORB_SLAM
1821 g2o::VertexSE3Expmap* vcam =
dynamic_cast<g2o::VertexSE3Expmap*
>(optimizer.vertex(camId));
1822 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(poseId);
1824 UASSERT(iterModel != models.end() && camIndex<iterModel->
second.size() && iterModel->second[camIndex].isValidForProjection());
1825 baseline = iterModel->second[camIndex].Tx()<0.0?-iterModel->second[camIndex].Tx()/iterModel->second[camIndex].fx():
baseline_;
1827 g2o::VertexCam* vcam =
dynamic_cast<g2o::VertexCam*
>(optimizer.vertex(camId));
1828 baseline = vcam->estimate().baseline;
1834 #ifdef RTABMAP_ORB_SLAM
1835 g2o::EdgeStereoSE3ProjectXYZ*
es =
new g2o::EdgeStereoSE3ProjectXYZ();
1836 float disparity =
baseline * iterModel->second[camIndex].fx() / depth;
1837 Eigen::Vector3d obs( pt.
kpt.pt.x, pt.
kpt.pt.y, pt.
kpt.pt.x-disparity);
1838 es->setMeasurement(obs);
1840 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1841 es->fx = iterModel->second[camIndex].fx();
1842 es->fy = iterModel->second[camIndex].fy();
1843 es->cx = iterModel->second[camIndex].cx();
1844 es->cy = iterModel->second[camIndex].cy();
1848 g2o::EdgeProjectP2SC*
es =
new g2o::EdgeProjectP2SC();
1849 float disparity =
baseline * vcam->estimate().Kcam(0,0) / depth;
1850 Eigen::Vector3d obs( pt.
kpt.pt.x, pt.
kpt.pt.y, pt.
kpt.pt.x-disparity);
1851 es->setMeasurement(obs);
1853 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1861 UDEBUG(
"Stereo camera model detected but current "
1862 "observation (pt=%d to cam=%d, kpt=[%d,%d]) has null depth (%f m), adding "
1863 "mono observation instead.",
1864 vpt3d->id()-stepVertexId, camId, (
int)pt.
kpt.pt.x, (
int)pt.
kpt.pt.y, depth);
1867 #ifdef RTABMAP_ORB_SLAM
1868 g2o::EdgeSE3ProjectXYZ* em =
new g2o::EdgeSE3ProjectXYZ();
1869 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1870 em->setMeasurement(obs);
1871 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1872 em->fx = iterModel->second[camIndex].fx();
1873 em->fy = iterModel->second[camIndex].fy();
1874 em->cx = iterModel->second[camIndex].cx();
1875 em->cy = iterModel->second[camIndex].cy();
1879 g2o::EdgeProjectP2MC* em =
new g2o::EdgeProjectP2MC();
1880 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1881 em->setMeasurement(obs);
1882 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1886 e->setVertex(0, vpt3d);
1887 e->setVertex(1, vcam);
1891 g2o::RobustKernelHuber* kernel =
new g2o::RobustKernelHuber;
1893 e->setRobustKernel(kernel);
1896 optimizer.addEdge(
e);
1903 UDEBUG(
"Initial optimization...");
1904 optimizer.initializeOptimization();
1906 UASSERT(optimizer.verifyInformationMatrices());
1912 int outliersCount = 0;
1913 int outliersCountFar = 0;
1920 optimizer.computeActiveErrors();
1921 double chi2 = optimizer.activeRobustChi2();
1925 UERROR(
"Optimization generated NANs, aborting optimization! Try another g2o's optimizer (current=%d).",
optimizer_);
1926 return optimizedPoses;
1928 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f",
i, (
int)optimizer.vertices().size(), (
int)optimizer.edges().size(), chi2);
1930 if(
i>0 && (optimizer.activeRobustChi2() > 1000000000000.0 || !
uIsFinite(optimizer.activeRobustChi2())))
1932 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1933 return optimizedPoses;
1940 if((*iter)->level() == 0 && (*iter)->chi2() > (*iter)->robustKernel()->delta())
1942 (*iter)->setLevel(1);
1945 #ifdef RTABMAP_ORB_SLAM
1946 if(
dynamic_cast<g2o::EdgeStereoSE3ProjectXYZ*
>(*
iter) != 0)
1948 d = ((g2o::EdgeStereoSE3ProjectXYZ*)(*
iter))->measurement()[0]-((g2o::EdgeStereoSE3ProjectXYZ*)(*
iter))->measurement()[2];
1952 if(
dynamic_cast<g2o::EdgeProjectP2SC*
>(*
iter) != 0)
1954 d = ((g2o::EdgeProjectP2SC*)(*
iter))->measurement()[0]-((g2o::EdgeProjectP2SC*)(*
iter))->measurement()[2];
1960 if((*iter)->vertex(0)->id() > negVertexOffset)
1962 pt3d = points3DMap.at(negVertexOffset - (*iter)->vertex(0)->id());
1966 pt3d = points3DMap.at((*iter)->vertex(0)->id()-stepVertexId);
1968 ((g2o::VertexSBAPointXYZ*)(*iter)->vertex(0))->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1972 outliers->insert((*iter)->vertex(0)->id()-stepVertexId);
1982 optimizer.initializeOptimization(0);
1983 UDEBUG(
"outliers=%d outliersCountFar=%d", outliersCount, outliersCountFar);
1986 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());
1988 if(optimizer.activeRobustChi2() > 1000000000000.0)
1990 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1991 return optimizedPoses;
1995 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
2000 #ifdef RTABMAP_ORB_SLAM
2001 const g2o::VertexSE3Expmap*
v = (
const g2o::VertexSE3Expmap*)optimizer.vertex(camId);
2003 const g2o::VertexCam*
v = (
const g2o::VertexCam*)optimizer.vertex(camId);
2009 #ifdef RTABMAP_ORB_SLAM
2014 t *= models.at(
iter->first)[0].localTransform().inverse();
2019 UERROR(
"Optimized pose %d is null!?!?",
iter->first);
2020 optimizedPoses.clear();
2021 return optimizedPoses;
2028 t =
iter->second.inverse() *
t;
2029 optimizedPoses.insert(std::pair<int, Transform>(
iter->first,
iter->second *
t.to3DoF()));
2033 optimizedPoses.insert(std::pair<int, Transform>(
iter->first,
t));
2038 UERROR(
"Vertex (pose) %d (cam=%d) not found!?",
iter->first, camId);
2045 for(std::map<int, cv::Point3f>::iterator
iter = points3DMap.begin();
iter!=points3DMap.end(); ++
iter)
2047 const g2o::VertexSBAPointXYZ*
v;
2048 int id =
iter->first;
2051 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(negVertexOffset +
id*-1);
2055 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(stepVertexId +
id);
2060 cv::Point3f
p(
v->estimate()[0],
v->estimate()[1],
v->estimate()[2]);
2066 iter->second.x =
iter->second.y =
iter->second.z = std::numeric_limits<float>::quiet_NaN();
2070 else if(poses.size() > 1 && (poses.size() != models.size() && poses.begin()->first > 0))
2072 UERROR(
"This method should be called with size of poses = size camera models!");
2074 else if(poses.size() == 1 ||
iterations() <= 0)
2076 optimizedPoses = poses;
2080 UWARN(
"This method should be called at least with 1 pose!");
2082 UDEBUG(
"Optimizing graph...end!");
2084 UERROR(
"Not built with G2O support!");
2086 return optimizedPoses;
2090 const std::string & fileName,
2091 const std::map<int, Transform> & poses,
2092 const std::multimap<int, Link> & edgeConstraints)
2097 fopen_s(&
file, fileName.c_str(),
"w");
2099 file = fopen(fileName.c_str(),
"w");
2105 setlocale(LC_ALL,
"en_US.UTF-8");
2115 Eigen::Vector3f
v = Eigen::Vector3f::Zero();
2117 fprintf(
file,
"PARAMS_SE3OFFSET %d %f %f %f %f %f %f %f\n",
2129 std::map<int, bool> isLandmarkWithRotation;
2130 for(std::multimap<int, Link>::const_iterator
iter = edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
2132 int landmarkId =
iter->second.from() < 0?
iter->second.from():
iter->second.to() < 0?
iter->second.to():0;
2133 if(landmarkId != 0 && isLandmarkWithRotation.find(landmarkId) == isLandmarkWithRotation.end())
2137 if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2139 isLandmarkWithRotation.insert(std::make_pair(landmarkId,
false));
2140 UDEBUG(
"Tag %d has no orientation", landmarkId);
2144 isLandmarkWithRotation.insert(std::make_pair(landmarkId,
true));
2145 UDEBUG(
"Tag %d has orientation", landmarkId);
2148 else if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
2149 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
2150 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2152 isLandmarkWithRotation.insert(std::make_pair(landmarkId,
false));
2153 UDEBUG(
"Tag %d has no orientation", landmarkId);
2157 isLandmarkWithRotation.insert(std::make_pair(landmarkId,
true));
2158 UDEBUG(
"Tag %d has orientation", landmarkId);
2164 int landmarkOffset = poses.size()&&poses.rbegin()->first>0?poses.rbegin()->first:0;
2165 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
2172 fprintf(
file,
"VERTEX_SE2 %d %f %f %f\n",
2176 iter->second.theta());
2180 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2183 fprintf(
file,
"VERTEX_SE2 %d %f %f %f\n",
2184 landmarkOffset-
iter->first,
2187 iter->second.theta());
2192 fprintf(
file,
"VERTEX_XY %d %f %f\n",
2193 landmarkOffset-
iter->first,
2205 fprintf(
file,
"VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
2217 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2221 fprintf(
file,
"VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
2222 landmarkOffset-
iter->first,
2234 fprintf(
file,
"VERTEX_TRACKXYZ %d %f %f %f\n",
2235 landmarkOffset-
iter->first,
2244 int virtualVertexId = landmarkOffset - (poses.size()&&poses.rbegin()->first<0?poses.rbegin()->first:0);
2245 for(std::multimap<int, Link>::const_iterator
iter = edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
2255 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2258 fprintf(
file,
"EDGE_SE2 %d %d %f %f %f %f %f %f %f %f %f\n",
2259 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2260 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2261 iter->second.transform().x(),
2262 iter->second.transform().y(),
2263 iter->second.transform().theta(),
2264 iter->second.infMatrix().at<
double>(0, 0),
2265 iter->second.infMatrix().at<
double>(0, 1),
2266 iter->second.infMatrix().at<
double>(0, 5),
2267 iter->second.infMatrix().at<
double>(1, 1),
2268 iter->second.infMatrix().at<
double>(1, 5),
2269 iter->second.infMatrix().at<
double>(5, 5));
2274 fprintf(
file,
"EDGE_SE2_XY %d %d %f %f %f %f %f\n",
2275 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2276 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2277 iter->second.transform().x(),
2278 iter->second.transform().y(),
2279 iter->second.infMatrix().at<
double>(0, 0),
2280 iter->second.infMatrix().at<
double>(0, 1),
2281 iter->second.infMatrix().at<
double>(1, 1));
2286 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2290 fprintf(
file,
"EDGE_SE3 %d %d %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",
2291 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2292 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2293 iter->second.transform().x(),
2294 iter->second.transform().y(),
2295 iter->second.transform().z(),
2300 iter->second.infMatrix().at<
double>(0, 0),
2301 iter->second.infMatrix().at<
double>(0, 1),
2302 iter->second.infMatrix().at<
double>(0, 2),
2303 iter->second.infMatrix().at<
double>(0, 3),
2304 iter->second.infMatrix().at<
double>(0, 4),
2305 iter->second.infMatrix().at<
double>(0, 5),
2306 iter->second.infMatrix().at<
double>(1, 1),
2307 iter->second.infMatrix().at<
double>(1, 2),
2308 iter->second.infMatrix().at<
double>(1, 3),
2309 iter->second.infMatrix().at<
double>(1, 4),
2310 iter->second.infMatrix().at<
double>(1, 5),
2311 iter->second.infMatrix().at<
double>(2, 2),
2312 iter->second.infMatrix().at<
double>(2, 3),
2313 iter->second.infMatrix().at<
double>(2, 4),
2314 iter->second.infMatrix().at<
double>(2, 5),
2315 iter->second.infMatrix().at<
double>(3, 3),
2316 iter->second.infMatrix().at<
double>(3, 4),
2317 iter->second.infMatrix().at<
double>(3, 5),
2318 iter->second.infMatrix().at<
double>(4, 4),
2319 iter->second.infMatrix().at<
double>(4, 5),
2320 iter->second.infMatrix().at<
double>(5, 5));
2325 fprintf(
file,
"EDGE_SE3_TRACKXYZ %d %d %d %f %f %f %f %f %f %f %f %f\n",
2326 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2327 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2329 iter->second.transform().x(),
2330 iter->second.transform().y(),
2331 iter->second.transform().z(),
2332 iter->second.infMatrix().at<
double>(0, 0),
2333 iter->second.infMatrix().at<
double>(0, 1),
2334 iter->second.infMatrix().at<
double>(0, 2),
2335 iter->second.infMatrix().at<
double>(1, 1),
2336 iter->second.infMatrix().at<
double>(1, 2),
2337 iter->second.infMatrix().at<
double>(2, 2));
2343 std::string prefix =
isSlam2d()?
"EDGE_SE2" :
"EDGE_SE3:QUAT";
2344 std::string suffix =
"";
2345 std::string to =
uFormat(
" %d",
iter->second.to());
2362 if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2364 prefix =
"EDGE_PRIOR_SE2_XY";
2369 prefix =
"EDGE_PRIOR_SE2";
2378 if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
2379 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
2380 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2383 prefix =
"EDGE_POINTXYZ_PRIOR";
2389 prefix =
"EDGE_SE3_PRIOR";
2397 fprintf(
file,
"VERTEX_SWITCH %d 1\n", virtualVertexId);
2398 fprintf(
file,
"EDGE_SWITCH_PRIOR %d 1 1.0\n", virtualVertexId);
2399 prefix =
isSlam2d() ?
"EDGE_SE2_SWITCHABLE" :
"EDGE_SE3_SWITCHABLE";
2400 suffix =
uFormat(
" %d", virtualVertexId++);
2409 fprintf(
file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2411 iter->second.from(),
2414 iter->second.transform().x(),
2415 iter->second.transform().y(),
2416 iter->second.transform().theta(),
2417 iter->second.infMatrix().at<
double>(0, 0),
2418 iter->second.infMatrix().at<
double>(0, 1),
2419 iter->second.infMatrix().at<
double>(0, 5),
2420 iter->second.infMatrix().at<
double>(1, 1),
2421 iter->second.infMatrix().at<
double>(1, 5),
2422 iter->second.infMatrix().at<
double>(5, 5));
2428 fprintf(
file,
"%s %d%s%s %f %f %f %f %f\n",
2430 iter->second.from(),
2433 iter->second.transform().x(),
2434 iter->second.transform().y(),
2435 iter->second.infMatrix().at<
double>(0, 0),
2436 iter->second.infMatrix().at<
double>(0, 1),
2437 iter->second.infMatrix().at<
double>(1, 1));
2447 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",
2449 iter->second.from(),
2452 iter->second.transform().x(),
2453 iter->second.transform().y(),
2454 iter->second.transform().z(),
2459 iter->second.infMatrix().at<
double>(0, 0),
2460 iter->second.infMatrix().at<
double>(0, 1),
2461 iter->second.infMatrix().at<
double>(0, 2),
2462 iter->second.infMatrix().at<
double>(0, 3),
2463 iter->second.infMatrix().at<
double>(0, 4),
2464 iter->second.infMatrix().at<
double>(0, 5),
2465 iter->second.infMatrix().at<
double>(1, 1),
2466 iter->second.infMatrix().at<
double>(1, 2),
2467 iter->second.infMatrix().at<
double>(1, 3),
2468 iter->second.infMatrix().at<
double>(1, 4),
2469 iter->second.infMatrix().at<
double>(1, 5),
2470 iter->second.infMatrix().at<
double>(2, 2),
2471 iter->second.infMatrix().at<
double>(2, 3),
2472 iter->second.infMatrix().at<
double>(2, 4),
2473 iter->second.infMatrix().at<
double>(2, 5),
2474 iter->second.infMatrix().at<
double>(3, 3),
2475 iter->second.infMatrix().at<
double>(3, 4),
2476 iter->second.infMatrix().at<
double>(3, 5),
2477 iter->second.infMatrix().at<
double>(4, 4),
2478 iter->second.infMatrix().at<
double>(4, 5),
2479 iter->second.infMatrix().at<
double>(5, 5));
2485 fprintf(
file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2487 iter->second.from(),
2490 iter->second.transform().x(),
2491 iter->second.transform().y(),
2492 iter->second.transform().z(),
2493 iter->second.infMatrix().at<
double>(0, 0),
2494 iter->second.infMatrix().at<
double>(0, 1),
2495 iter->second.infMatrix().at<
double>(0, 2),
2496 iter->second.infMatrix().at<
double>(1, 1),
2497 iter->second.infMatrix().at<
double>(1, 2),
2498 iter->second.infMatrix().at<
double>(2, 2));
2502 UINFO(
"Graph saved to %s", fileName.c_str());
2508 UERROR(
"Cannot save to file %s", fileName.c_str());