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;
1041 if (!optimizer.solver()->init()) {
1042 UERROR(
"g2o: Error while initializing solver");
1043 return optimizedPoses;
1046 if(intermediateGraphes || this->
epsilon() > 0.0)
1050 if(intermediateGraphes)
1054 std::map<int, Transform> tmpPoses;
1057 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1059 int id =
iter->first;
1062 const g2o::VertexSE2*
v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
1067 Transform t(
v->estimate().translation()[0],
v->estimate().translation()[1],
iter->second.z(),
roll,
pitch,
v->estimate().rotation().angle());
1068 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1073 UERROR(
"Vertex %d not found!?",
id);
1078 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1081 if(isLandmarkWithRotation.at(
id))
1083 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)
v;
1086 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1],
iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
1087 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1092 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)
v;
1096 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1102 UERROR(
"Vertex %d not found!?",
id);
1109 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1111 int id =
iter->first;
1114 const g2o::VertexSE3*
v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
1118 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1123 UERROR(
"Vertex %d not found!?",
id);
1128 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1131 if(isLandmarkWithRotation.at(
id))
1133 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)
v;
1135 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1140 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)
v;
1144 tmpPoses.insert(std::pair<int, Transform>(
id,
t));
1150 UERROR(
"Vertex %d not found!?",
id);
1155 intermediateGraphes->push_back(tmpPoses);
1159 g2o::OptimizationAlgorithm::SolverResult
result = optimizer.solver()->solve(
i);
1163 optimizer.computeActiveErrors();
1164 double chi2 = optimizer.activeRobustChi2();
1165 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f",
i, (
int)optimizer.vertices().size(), (
int)optimizer.edges().size(), chi2);
1167 if(
i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
1169 UERROR(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
1170 return optimizedPoses;
1173 if(
result == g2o::OptimizationAlgorithm::Fail)
1175 UERROR(
"g2o: Solver failed, aborting optimization!");
1176 return optimizedPoses;
1179 double errorDelta = lastError - chi2;
1180 if(
i>0 && errorDelta < this->
epsilon())
1184 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
1188 UINFO(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
1192 else if(
i==0 && chi2 < this->
epsilon())
1194 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)", chi2, this->
epsilon());
1203 optimizer.computeActiveErrors();
1204 UDEBUG(
"%d nodes, %d edges, chi2: %f", (
int)optimizer.vertices().size(), (
int)optimizer.edges().size(), optimizer.activeRobustChi2());
1208 *finalError = lastError;
1212 *iterationsDone = it;
1214 UINFO(
"g2o optimizing end (%d iterations done, error=%f, time = %f s)", it, optimizer.activeRobustChi2(),
timer.ticks());
1216 if(optimizer.activeRobustChi2() > 1000000000000.0)
1218 UERROR(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
1219 return optimizedPoses;
1224 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1226 int id =
iter->first;
1229 const g2o::VertexSE2*
v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
1234 Transform t(
v->estimate().translation()[0],
v->estimate().translation()[1],
iter->second.z(),
roll,
pitch,
v->estimate().rotation().angle());
1235 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1240 UERROR(
"Vertex %d not found!?",
id);
1245 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1249 if(isLandmarkWithRotation.at(
id))
1251 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)
v;
1254 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1],
iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
1255 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1260 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)
v;
1264 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1270 UERROR(
"Vertex %d not found!?",
id);
1275 g2o::VertexSE2*
v = (g2o::VertexSE2*)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;
1286 outputCovariance.at<
double>(0,0) = (*
block)(0,0);
1287 outputCovariance.at<
double>(0,1) = (*
block)(0,1);
1288 outputCovariance.at<
double>(0,5) = (*
block)(0,2);
1289 outputCovariance.at<
double>(1,0) = (*
block)(1,0);
1290 outputCovariance.at<
double>(1,1) = (*
block)(1,1);
1291 outputCovariance.at<
double>(1,5) = (*
block)(1,2);
1292 outputCovariance.at<
double>(5,0) = (*
block)(2,0);
1293 outputCovariance.at<
double>(5,1) = (*
block)(2,1);
1294 outputCovariance.at<
double>(5,5) = (*
block)(2,2);
1296 else if(
v->hessianIndex() < 0)
1298 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first,
v->hessianIndex());
1302 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());
1307 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1312 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
1314 int id =
iter->first;
1317 const g2o::VertexSE3*
v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
1321 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1326 UERROR(
"Vertex %d not found!?",
id);
1331 const g2o::OptimizableGraph::Vertex*
v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1335 if(isLandmarkWithRotation.at(
id))
1337 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)
v;
1339 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1344 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)
v;
1348 optimizedPoses.insert(std::pair<int, Transform>(
id,
t));
1354 UERROR(
"Vertex %d not found!?",
id);
1359 g2o::VertexSE3*
v = (g2o::VertexSE3*)optimizer.vertex(poses.rbegin()->first);
1363 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
1364 optimizer.computeMarginals(spinv,
v);
1365 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)",
t.ticks(), spinv.cols(), spinv.rows(),
v->hessianIndex(), poses.rbegin()->first);
1366 if(
v->hessianIndex() >= 0 &&
v->hessianIndex() < (
int)spinv.blockCols().size())
1368 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock *
block = spinv.blockCols()[
v->hessianIndex()].begin()->second;
1370 memcpy(outputCovariance.data,
block->data(), outputCovariance.total()*
sizeof(
double));
1372 else if(
v->hessianIndex() < 0)
1374 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first,
v->hessianIndex());
1376 #ifdef RTABMAP_G2O_CPP11
1379 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());
1385 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1389 else if(poses.size() == 1 ||
iterations() <= 0)
1391 optimizedPoses = poses;
1395 UWARN(
"This method should be called at least with 1 pose!");
1397 UDEBUG(
"Optimizing graph...end!");
1399 #ifdef RTABMAP_ORB_SLAM
1400 UERROR(
"G2O graph optimization cannot be used with g2o built from ORB_SLAM, only SBA is available.");
1402 UERROR(
"Not built with G2O support!");
1405 return optimizedPoses;
1408 #ifdef RTABMAP_ORB_SLAM
1412 class EdgeSE3Expmap :
public g2o::BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>
1416 EdgeSE3Expmap(): BaseBinaryEdge<6,
g2o::SE3Quat,
g2o::VertexSE3Expmap,
g2o::VertexSE3Expmap>(){}
1417 bool read(std::istream& is)
1422 bool write(std::ostream& os)
const
1429 const g2o::VertexSE3Expmap*
v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1430 const g2o::VertexSE3Expmap*
v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1431 g2o::SE3Quat
delta = _inverseMeasurement * (
v1->estimate().inverse()*
v2->estimate());
1432 _error[0]=
delta.translation().x();
1433 _error[1]=
delta.translation().y();
1434 _error[2]=
delta.translation().z();
1435 _error[3]=
delta.rotation().x();
1436 _error[4]=
delta.rotation().y();
1437 _error[5]=
delta.rotation().z();
1440 virtual void setMeasurement(
const g2o::SE3Quat& meas){
1442 _inverseMeasurement=meas.inverse();
1445 virtual double initialEstimatePossible(
const g2o::OptimizableGraph::VertexSet& , g2o::OptimizableGraph::Vertex* ) {
return 1.;}
1446 virtual void initialEstimate(
const g2o::OptimizableGraph::VertexSet& from_, g2o::OptimizableGraph::Vertex* ){
1447 g2o::VertexSE3Expmap* from =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[0]);
1448 g2o::VertexSE3Expmap* to =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[1]);
1449 if (from_.count(from) > 0)
1450 to->setEstimate((g2o::SE3Quat) from->estimate() * _measurement);
1452 from->setEstimate((g2o::SE3Quat) to->estimate() * _inverseMeasurement);
1455 virtual bool setMeasurementData(
const double* d){
1457 _measurement.fromVector(v);
1458 _inverseMeasurement = _measurement.inverse();
1462 virtual bool getMeasurementData(
double* d)
const{
1464 v = _measurement.toVector();
1468 virtual int measurementDimension()
const {
return 7;}
1470 virtual bool setMeasurementFromState() {
1471 const g2o::VertexSE3Expmap*
v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1472 const g2o::VertexSE3Expmap*
v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1473 _measurement = (
v1->estimate().inverse()*
v2->estimate());
1474 _inverseMeasurement = _measurement.inverse();
1479 g2o::SE3Quat _inverseMeasurement;
1485 const std::map<int, Transform> & poses,
1486 const std::multimap<int, Link> & links,
1487 const std::map<
int, std::vector<CameraModel> > & models,
1488 std::map<int, cv::Point3f> & points3DMap,
1489 const std::map<
int, std::map<int, FeatureBA> > & wordReferences,
1490 std::set<int> * outliers)
1492 std::map<int, Transform> optimizedPoses;
1493 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
1494 UDEBUG(
"Optimizing graph...");
1496 optimizedPoses.clear();
1497 if(poses.size()>=2 &&
iterations() > 0 && (models.size() == poses.size() || poses.begin()->first < 0))
1499 g2o::SparseOptimizer optimizer;
1501 #if defined(RTABMAP_G2O_CPP11) && !defined(RTABMAP_ORB_SLAM)
1502 std::unique_ptr<g2o::BlockSolver_6_3::LinearSolverType> linearSolver;
1504 g2o::BlockSolver_6_3::LinearSolverType * linearSolver = 0;
1507 #ifdef RTABMAP_ORB_SLAM
1508 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1513 #ifdef RTABMAP_G2O_CPP11
1514 linearSolver = std::make_unique<g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType> >();
1516 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1519 #ifdef G2O_HAVE_CHOLMOD
1523 #ifdef RTABMAP_G2O_CPP11
1524 linearSolver = std::make_unique<g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType> >();
1526 linearSolver =
new g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType>();
1530 #ifdef G2O_HAVE_CSPARSE
1534 #ifdef RTABMAP_G2O_CPP11
1535 linearSolver = std::make_unique<g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType> >();
1537 linearSolver =
new g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType>();
1544 #ifdef RTABMAP_G2O_CPP11
1545 linearSolver = std::make_unique<g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType> >();
1547 linearSolver =
new g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType>();
1550 #endif // RTABMAP_ORB_SLAM
1552 #ifndef RTABMAP_ORB_SLAM
1555 #ifdef RTABMAP_G2O_CPP11
1556 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
1557 std::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1559 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
new g2o::BlockSolver_6_3(linearSolver)));
1565 #if defined(RTABMAP_G2O_CPP11) && !defined(RTABMAP_ORB_SLAM)
1566 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
1567 std::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1569 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
new g2o::BlockSolver_6_3(linearSolver)));
1574 UDEBUG(
"fill poses to g2o...");
1575 for(std::map<int, Transform>::const_iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
1580 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(
iter->first);
1581 UASSERT(iterModel != models.end() && !iterModel->second.empty());
1582 for(
size_t i=0;
i<iterModel->second.size(); ++
i)
1584 UASSERT(iterModel->second[
i].isValidForProjection());
1586 Transform camPose =
iter->second * iterModel->second[
i].localTransform();
1590 #ifdef RTABMAP_ORB_SLAM
1591 g2o::VertexSE3Expmap * vCam =
new g2o::VertexSE3Expmap();
1593 g2o::VertexCam * vCam =
new g2o::VertexCam();
1597 #ifdef RTABMAP_ORB_SLAM
1599 vCam->setEstimate(g2o::SE3Quat(
a.linear(),
a.translation()));
1603 iterModel->second[
i].fx(),
1604 iterModel->second[
i].fy(),
1605 iterModel->second[
i].cx(),
1606 iterModel->second[
i].cy(),
1607 iterModel->second[
i].Tx()<0.0?-iterModel->second[
i].Tx()/iterModel->second[
i].fx():
baseline_);
1608 vCam->setEstimate(
cam);
1613 vCam->setFixed((rootId >= 0 &&
iter->first == rootId) || (rootId < 0 && iter->
first != -rootId));
1627 UASSERT_MSG(optimizer.addVertex(vCam),
uFormat(
"cannot insert cam vertex %d (pose=%d)!?", vCam->id(),
iter->first).c_str());
1632 UDEBUG(
"fill edges to g2o...");
1633 for(std::multimap<int, Link>::const_iterator
iter=links.begin();
iter!=links.end(); ++
iter)
1635 if(
iter->second.from() > 0 &&
1636 iter->second.to() > 0 &&
1641 int id1 =
iter->second.from();
1642 int id2 =
iter->second.to();
1646 #ifndef RTABMAP_ORB_SLAM
1647 g2o::HyperGraph::Edge * edge = 0;
1652 m.head<3>() = Eigen::Vector3d::UnitZ();
1662 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(
iter->first);
1664 UASSERT(iterModel != models.end() && !iterModel->second.empty() && !iterModel->second[0].localTransform().isNull());
1667 priorEdge->setInformation(information);
1668 priorEdge->vertices()[0] =
v1;
1671 if (edge && !optimizer.addEdge(edge))
1674 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1675 return optimizedPoses;
1679 else if(id1>0 && id2>0)
1686 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
1690 Transform camLink = models.at(id1)[0].localTransform().
inverse()*
iter->second.transform()*models.at(id2)[0].localTransform();
1697 #ifdef RTABMAP_ORB_SLAM
1698 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1699 g2o::VertexSE3Expmap*
v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id1*
MULTICAM_OFFSET);
1700 g2o::VertexSE3Expmap*
v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id2*
MULTICAM_OFFSET);
1705 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1707 g2o::EdgeSBACam *
e =
new g2o::EdgeSBACam();
1713 e->setVertex(0,
v1);
1714 e->setVertex(1,
v2);
1716 e->setMeasurement(g2o::SE3Quat(
a.linear(),
a.translation()));
1717 e->setInformation(information);
1719 if (!optimizer.addEdge(
e))
1722 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1723 return optimizedPoses;
1729 UDEBUG(
"fill hard edges between camera 0 and other cameras (multicam)...");
1730 for(std::map<
int, std::vector<CameraModel> >::const_iterator
iter=models.begin();
iter!=models.end(); ++
iter)
1732 int id =
iter->first;
1735 for(
size_t i=1;
i<
iter->second.size(); ++
i)
1742 Transform camLink =
iter->second[0].localTransform().inverse()*
iter->second[
i].localTransform();
1743 #ifdef RTABMAP_ORB_SLAM
1744 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1745 g2o::VertexSE3Expmap*
v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(
id*
MULTICAM_OFFSET);
1746 g2o::VertexSE3Expmap*
v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(
id*
MULTICAM_OFFSET+
i);
1751 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1753 g2o::EdgeSBACam *
e =
new g2o::EdgeSBACam();
1765 e->setVertex(0,
v1);
1766 e->setVertex(1,
v2);
1768 e->setMeasurement(g2o::SE3Quat(
a.linear(),
a.translation()));
1769 e->setInformation(information);
1771 if (!optimizer.addEdge(
e))
1774 UERROR(
"Map: Failed adding constraint between %d and %d, skipping",
v1->id(),
v2->id());
1775 return optimizedPoses;
1781 UDEBUG(
"fill 3D points to g2o...");
1783 int negVertexOffset = stepVertexId;
1784 if(wordReferences.size() && wordReferences.rbegin()->first>0)
1786 negVertexOffset += wordReferences.rbegin()->first;
1788 UDEBUG(
"stepVertexId=%d, negVertexOffset=%d", stepVertexId, negVertexOffset);
1789 std::list<g2o::OptimizableGraph::Edge*>
edges;
1790 for(std::map<
int, std::map<int, FeatureBA> >::const_iterator
iter = wordReferences.begin();
iter!=wordReferences.end(); ++
iter)
1792 int id =
iter->first;
1793 if(points3DMap.find(
id) != points3DMap.end())
1795 cv::Point3f pt3d = points3DMap.at(
id);
1798 UWARN(
"Ignoring 3D point %d because it has nan value(s)!",
id);
1801 g2o::VertexSBAPointXYZ* vpt3d =
new g2o::VertexSBAPointXYZ();
1803 vpt3d->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1806 vpt3d->setId(negVertexOffset +
id*-1);
1810 vpt3d->setId(stepVertexId +
id);
1813 vpt3d->setMarginalized(
true);
1814 optimizer.addVertex(vpt3d);
1819 for(std::map<int, FeatureBA>::const_iterator jter=
iter->second.begin(); jter!=
iter->second.end(); ++jter)
1821 int poseId = jter->first;
1822 int camIndex = jter->second.cameraIndex;
1824 if(poses.find(poseId) != poses.end() && optimizer.vertex(camId) != 0)
1827 double depth = pt.
depth;
1831 g2o::OptimizableGraph::Edge *
e;
1833 #ifdef RTABMAP_ORB_SLAM
1834 g2o::VertexSE3Expmap* vcam =
dynamic_cast<g2o::VertexSE3Expmap*
>(optimizer.vertex(camId));
1835 std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(poseId);
1837 UASSERT(iterModel != models.end() && camIndex<iterModel->
second.size() && iterModel->second[camIndex].isValidForProjection());
1838 baseline = iterModel->second[camIndex].Tx()<0.0?-iterModel->second[camIndex].Tx()/iterModel->second[camIndex].fx():
baseline_;
1840 g2o::VertexCam* vcam =
dynamic_cast<g2o::VertexCam*
>(optimizer.vertex(camId));
1841 baseline = vcam->estimate().baseline;
1847 #ifdef RTABMAP_ORB_SLAM
1848 g2o::EdgeStereoSE3ProjectXYZ*
es =
new g2o::EdgeStereoSE3ProjectXYZ();
1849 float disparity =
baseline * iterModel->second[camIndex].fx() / 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);
1854 es->fx = iterModel->second[camIndex].fx();
1855 es->fy = iterModel->second[camIndex].fy();
1856 es->cx = iterModel->second[camIndex].cx();
1857 es->cy = iterModel->second[camIndex].cy();
1861 g2o::EdgeProjectP2SC*
es =
new g2o::EdgeProjectP2SC();
1862 float disparity =
baseline * vcam->estimate().Kcam(0,0) / depth;
1863 Eigen::Vector3d obs( pt.
kpt.pt.x, pt.
kpt.pt.y, pt.
kpt.pt.x-disparity);
1864 es->setMeasurement(obs);
1866 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1874 UDEBUG(
"Stereo camera model detected but current "
1875 "observation (pt=%d to cam=%d, kpt=[%d,%d]) has null depth (%f m), adding "
1876 "mono observation instead.",
1877 vpt3d->id()-stepVertexId, camId, (
int)pt.
kpt.pt.x, (
int)pt.
kpt.pt.y, depth);
1880 #ifdef RTABMAP_ORB_SLAM
1881 g2o::EdgeSE3ProjectXYZ* em =
new g2o::EdgeSE3ProjectXYZ();
1882 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1883 em->setMeasurement(obs);
1884 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1885 em->fx = iterModel->second[camIndex].fx();
1886 em->fy = iterModel->second[camIndex].fy();
1887 em->cx = iterModel->second[camIndex].cx();
1888 em->cy = iterModel->second[camIndex].cy();
1892 g2o::EdgeProjectP2MC* em =
new g2o::EdgeProjectP2MC();
1893 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1894 em->setMeasurement(obs);
1895 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1899 e->setVertex(0, vpt3d);
1900 e->setVertex(1, vcam);
1904 g2o::RobustKernelHuber* kernel =
new g2o::RobustKernelHuber;
1906 e->setRobustKernel(kernel);
1909 optimizer.addEdge(
e);
1916 UDEBUG(
"Initial optimization...");
1917 optimizer.initializeOptimization();
1919 UASSERT(optimizer.verifyInformationMatrices());
1925 int outliersCount = 0;
1926 int outliersCountFar = 0;
1933 optimizer.computeActiveErrors();
1934 double chi2 = optimizer.activeRobustChi2();
1938 UERROR(
"Optimization generated NANs, aborting optimization! Try another g2o's optimizer (current=%d).",
optimizer_);
1939 return optimizedPoses;
1941 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f",
i, (
int)optimizer.vertices().size(), (
int)optimizer.edges().size(), chi2);
1943 if(
i>0 && (optimizer.activeRobustChi2() > 1000000000000.0 || !
uIsFinite(optimizer.activeRobustChi2())))
1945 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1946 return optimizedPoses;
1953 if((*iter)->level() == 0 && (*iter)->chi2() > (*iter)->robustKernel()->delta())
1955 (*iter)->setLevel(1);
1958 #ifdef RTABMAP_ORB_SLAM
1959 if(
dynamic_cast<g2o::EdgeStereoSE3ProjectXYZ*
>(*
iter) != 0)
1961 d = ((g2o::EdgeStereoSE3ProjectXYZ*)(*
iter))->measurement()[0]-((g2o::EdgeStereoSE3ProjectXYZ*)(*
iter))->measurement()[2];
1965 if(
dynamic_cast<g2o::EdgeProjectP2SC*
>(*
iter) != 0)
1967 d = ((g2o::EdgeProjectP2SC*)(*
iter))->measurement()[0]-((g2o::EdgeProjectP2SC*)(*
iter))->measurement()[2];
1973 if((*iter)->vertex(0)->id() > negVertexOffset)
1975 pt3d = points3DMap.at(negVertexOffset - (*iter)->vertex(0)->id());
1979 pt3d = points3DMap.at((*iter)->vertex(0)->id()-stepVertexId);
1981 ((g2o::VertexSBAPointXYZ*)(*iter)->vertex(0))->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1985 outliers->insert((*iter)->vertex(0)->id()-stepVertexId);
1995 optimizer.initializeOptimization(0);
1996 UDEBUG(
"outliers=%d outliersCountFar=%d", outliersCount, outliersCountFar);
1999 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());
2001 if(optimizer.activeRobustChi2() > 1000000000000.0)
2003 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
2004 return optimizedPoses;
2008 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
2013 #ifdef RTABMAP_ORB_SLAM
2014 const g2o::VertexSE3Expmap*
v = (
const g2o::VertexSE3Expmap*)optimizer.vertex(camId);
2016 const g2o::VertexCam*
v = (
const g2o::VertexCam*)optimizer.vertex(camId);
2022 #ifdef RTABMAP_ORB_SLAM
2027 t *= models.at(
iter->first)[0].localTransform().inverse();
2032 UERROR(
"Optimized pose %d is null!?!?",
iter->first);
2033 optimizedPoses.clear();
2034 return optimizedPoses;
2041 t =
iter->second.inverse() *
t;
2042 optimizedPoses.insert(std::pair<int, Transform>(
iter->first,
iter->second *
t.to3DoF()));
2046 optimizedPoses.insert(std::pair<int, Transform>(
iter->first,
t));
2051 UERROR(
"Vertex (pose) %d (cam=%d) not found!?",
iter->first, camId);
2058 for(std::map<int, cv::Point3f>::iterator
iter = points3DMap.begin();
iter!=points3DMap.end(); ++
iter)
2060 const g2o::VertexSBAPointXYZ*
v;
2061 int id =
iter->first;
2064 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(negVertexOffset +
id*-1);
2068 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(stepVertexId +
id);
2073 cv::Point3f
p(
v->estimate()[0],
v->estimate()[1],
v->estimate()[2]);
2079 iter->second.x =
iter->second.y =
iter->second.z = std::numeric_limits<float>::quiet_NaN();
2083 else if(poses.size() > 1 && (poses.size() != models.size() && poses.begin()->first > 0))
2085 UERROR(
"This method should be called with size of poses = size camera models!");
2087 else if(poses.size() == 1 ||
iterations() <= 0)
2089 optimizedPoses = poses;
2093 UWARN(
"This method should be called at least with 1 pose!");
2095 UDEBUG(
"Optimizing graph...end!");
2097 UERROR(
"Not built with G2O support!");
2099 return optimizedPoses;
2103 const std::string & fileName,
2104 const std::map<int, Transform> & poses,
2105 const std::multimap<int, Link> & edgeConstraints)
2110 fopen_s(&
file, fileName.c_str(),
"w");
2112 file = fopen(fileName.c_str(),
"w");
2118 setlocale(LC_ALL,
"en_US.UTF-8");
2128 Eigen::Vector3f
v = Eigen::Vector3f::Zero();
2130 fprintf(
file,
"PARAMS_SE3OFFSET %d %f %f %f %f %f %f %f\n",
2142 std::map<int, bool> isLandmarkWithRotation;
2143 for(std::multimap<int, Link>::const_iterator
iter = edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
2145 int landmarkId =
iter->second.from() < 0?
iter->second.from():
iter->second.to() < 0?
iter->second.to():0;
2146 if(landmarkId != 0 && isLandmarkWithRotation.find(landmarkId) == isLandmarkWithRotation.end())
2150 if (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);
2161 else if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
2162 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
2163 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2165 isLandmarkWithRotation.insert(std::make_pair(landmarkId,
false));
2166 UDEBUG(
"Tag %d has no orientation", landmarkId);
2170 isLandmarkWithRotation.insert(std::make_pair(landmarkId,
true));
2171 UDEBUG(
"Tag %d has orientation", landmarkId);
2177 int landmarkOffset = poses.size()&&poses.rbegin()->first>0?poses.rbegin()->first:0;
2178 for(std::map<int, Transform>::const_iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
2185 fprintf(
file,
"VERTEX_SE2 %d %f %f %f\n",
2189 iter->second.theta());
2193 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2196 fprintf(
file,
"VERTEX_SE2 %d %f %f %f\n",
2197 landmarkOffset-
iter->first,
2200 iter->second.theta());
2205 fprintf(
file,
"VERTEX_XY %d %f %f\n",
2206 landmarkOffset-
iter->first,
2218 fprintf(
file,
"VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
2230 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2234 fprintf(
file,
"VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
2235 landmarkOffset-
iter->first,
2247 fprintf(
file,
"VERTEX_TRACKXYZ %d %f %f %f\n",
2248 landmarkOffset-
iter->first,
2257 int virtualVertexId = landmarkOffset - (poses.size()&&poses.rbegin()->first<0?poses.rbegin()->first:0);
2258 for(std::multimap<int, Link>::const_iterator
iter = edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
2268 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2271 fprintf(
file,
"EDGE_SE2 %d %d %f %f %f %f %f %f %f %f %f\n",
2272 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2273 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2274 iter->second.transform().x(),
2275 iter->second.transform().y(),
2276 iter->second.transform().theta(),
2277 iter->second.infMatrix().at<
double>(0, 0),
2278 iter->second.infMatrix().at<
double>(0, 1),
2279 iter->second.infMatrix().at<
double>(0, 5),
2280 iter->second.infMatrix().at<
double>(1, 1),
2281 iter->second.infMatrix().at<
double>(1, 5),
2282 iter->second.infMatrix().at<
double>(5, 5));
2287 fprintf(
file,
"EDGE_SE2_XY %d %d %f %f %f %f %f\n",
2288 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2289 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2290 iter->second.transform().x(),
2291 iter->second.transform().y(),
2292 iter->second.infMatrix().at<
double>(0, 0),
2293 iter->second.infMatrix().at<
double>(0, 1),
2294 iter->second.infMatrix().at<
double>(1, 1));
2299 if(
uValue(isLandmarkWithRotation,
iter->first,
false))
2303 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",
2304 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2305 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2306 iter->second.transform().x(),
2307 iter->second.transform().y(),
2308 iter->second.transform().z(),
2313 iter->second.infMatrix().at<
double>(0, 0),
2314 iter->second.infMatrix().at<
double>(0, 1),
2315 iter->second.infMatrix().at<
double>(0, 2),
2316 iter->second.infMatrix().at<
double>(0, 3),
2317 iter->second.infMatrix().at<
double>(0, 4),
2318 iter->second.infMatrix().at<
double>(0, 5),
2319 iter->second.infMatrix().at<
double>(1, 1),
2320 iter->second.infMatrix().at<
double>(1, 2),
2321 iter->second.infMatrix().at<
double>(1, 3),
2322 iter->second.infMatrix().at<
double>(1, 4),
2323 iter->second.infMatrix().at<
double>(1, 5),
2324 iter->second.infMatrix().at<
double>(2, 2),
2325 iter->second.infMatrix().at<
double>(2, 3),
2326 iter->second.infMatrix().at<
double>(2, 4),
2327 iter->second.infMatrix().at<
double>(2, 5),
2328 iter->second.infMatrix().at<
double>(3, 3),
2329 iter->second.infMatrix().at<
double>(3, 4),
2330 iter->second.infMatrix().at<
double>(3, 5),
2331 iter->second.infMatrix().at<
double>(4, 4),
2332 iter->second.infMatrix().at<
double>(4, 5),
2333 iter->second.infMatrix().at<
double>(5, 5));
2338 fprintf(
file,
"EDGE_SE3_TRACKXYZ %d %d %d %f %f %f %f %f %f %f %f %f\n",
2339 iter->second.from()<0?landmarkOffset-
iter->second.from():
iter->second.from(),
2340 iter->second.to()<0?landmarkOffset-
iter->second.to():
iter->second.to(),
2342 iter->second.transform().x(),
2343 iter->second.transform().y(),
2344 iter->second.transform().z(),
2345 iter->second.infMatrix().at<
double>(0, 0),
2346 iter->second.infMatrix().at<
double>(0, 1),
2347 iter->second.infMatrix().at<
double>(0, 2),
2348 iter->second.infMatrix().at<
double>(1, 1),
2349 iter->second.infMatrix().at<
double>(1, 2),
2350 iter->second.infMatrix().at<
double>(2, 2));
2356 std::string prefix =
isSlam2d()?
"EDGE_SE2" :
"EDGE_SE3:QUAT";
2357 std::string suffix =
"";
2358 std::string to =
uFormat(
" %d",
iter->second.to());
2375 if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2377 prefix =
"EDGE_PRIOR_SE2_XY";
2382 prefix =
"EDGE_PRIOR_SE2";
2391 if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
2392 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
2393 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
2396 prefix =
"EDGE_POINTXYZ_PRIOR";
2402 prefix =
"EDGE_SE3_PRIOR";
2410 fprintf(
file,
"VERTEX_SWITCH %d 1\n", virtualVertexId);
2411 fprintf(
file,
"EDGE_SWITCH_PRIOR %d 1 1.0\n", virtualVertexId);
2412 prefix =
isSlam2d() ?
"EDGE_SE2_SWITCHABLE" :
"EDGE_SE3_SWITCHABLE";
2413 suffix =
uFormat(
" %d", virtualVertexId++);
2422 fprintf(
file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2424 iter->second.from(),
2427 iter->second.transform().x(),
2428 iter->second.transform().y(),
2429 iter->second.transform().theta(),
2430 iter->second.infMatrix().at<
double>(0, 0),
2431 iter->second.infMatrix().at<
double>(0, 1),
2432 iter->second.infMatrix().at<
double>(0, 5),
2433 iter->second.infMatrix().at<
double>(1, 1),
2434 iter->second.infMatrix().at<
double>(1, 5),
2435 iter->second.infMatrix().at<
double>(5, 5));
2441 fprintf(
file,
"%s %d%s%s %f %f %f %f %f\n",
2443 iter->second.from(),
2446 iter->second.transform().x(),
2447 iter->second.transform().y(),
2448 iter->second.infMatrix().at<
double>(0, 0),
2449 iter->second.infMatrix().at<
double>(0, 1),
2450 iter->second.infMatrix().at<
double>(1, 1));
2460 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",
2462 iter->second.from(),
2465 iter->second.transform().x(),
2466 iter->second.transform().y(),
2467 iter->second.transform().z(),
2472 iter->second.infMatrix().at<
double>(0, 0),
2473 iter->second.infMatrix().at<
double>(0, 1),
2474 iter->second.infMatrix().at<
double>(0, 2),
2475 iter->second.infMatrix().at<
double>(0, 3),
2476 iter->second.infMatrix().at<
double>(0, 4),
2477 iter->second.infMatrix().at<
double>(0, 5),
2478 iter->second.infMatrix().at<
double>(1, 1),
2479 iter->second.infMatrix().at<
double>(1, 2),
2480 iter->second.infMatrix().at<
double>(1, 3),
2481 iter->second.infMatrix().at<
double>(1, 4),
2482 iter->second.infMatrix().at<
double>(1, 5),
2483 iter->second.infMatrix().at<
double>(2, 2),
2484 iter->second.infMatrix().at<
double>(2, 3),
2485 iter->second.infMatrix().at<
double>(2, 4),
2486 iter->second.infMatrix().at<
double>(2, 5),
2487 iter->second.infMatrix().at<
double>(3, 3),
2488 iter->second.infMatrix().at<
double>(3, 4),
2489 iter->second.infMatrix().at<
double>(3, 5),
2490 iter->second.infMatrix().at<
double>(4, 4),
2491 iter->second.infMatrix().at<
double>(4, 5),
2492 iter->second.infMatrix().at<
double>(5, 5));
2498 fprintf(
file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2500 iter->second.from(),
2503 iter->second.transform().x(),
2504 iter->second.transform().y(),
2505 iter->second.transform().z(),
2506 iter->second.infMatrix().at<
double>(0, 0),
2507 iter->second.infMatrix().at<
double>(0, 1),
2508 iter->second.infMatrix().at<
double>(0, 2),
2509 iter->second.infMatrix().at<
double>(1, 1),
2510 iter->second.infMatrix().at<
double>(1, 2),
2511 iter->second.infMatrix().at<
double>(2, 2));
2515 UINFO(
"Graph saved to %s", fileName.c_str());
2521 UERROR(
"Cannot save to file %s", fileName.c_str());