36 #include <rtabmap/core/Version.h> 43 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 44 #include "g2o/core/sparse_optimizer.h" 45 #include "g2o/core/block_solver.h" 46 #include "g2o/core/factory.h" 47 #include "g2o/core/optimization_algorithm_factory.h" 48 #include "g2o/core/optimization_algorithm_gauss_newton.h" 49 #include "g2o/core/optimization_algorithm_levenberg.h" 50 #include "g2o/core/robust_kernel_impl.h" 53 typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> MatrixXD;
57 #include "g2o/types/sba/types_sba.h" 58 #include "g2o/solvers/eigen/linear_solver_eigen.h" 59 #include "g2o/config.h" 60 #include "g2o/types/slam2d/types_slam2d.h" 61 #include "g2o/types/slam3d/types_slam3d.h" 65 #ifdef G2O_HAVE_CSPARSE 66 #include "g2o/solvers/csparse/linear_solver_csparse.h" 68 #include "g2o/solvers/pcg/linear_solver_pcg.h" 69 #ifdef G2O_HAVE_CHOLMOD 70 #include "g2o/solvers/cholmod/linear_solver_cholmod.h" 74 #ifdef RTABMAP_ORB_SLAM2 75 #include "g2o/types/types_sba.h" 76 #include "g2o/types/types_six_dof_expmap.h" 77 #include "g2o/solvers/linear_solver_eigen.h" 80 typedef g2o::BlockSolver< g2o::BlockSolverTraits<-1, -1> > SlamBlockSolver;
81 typedef g2o::LinearSolverEigen<SlamBlockSolver::PoseMatrixType> SlamLinearEigenSolver;
83 typedef g2o::LinearSolverPCG<SlamBlockSolver::PoseMatrixType> SlamLinearPCGSolver;
84 #ifdef G2O_HAVE_CSPARSE 85 typedef g2o::LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearCSparseSolver;
87 #ifdef G2O_HAVE_CHOLMOD 88 typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver;
91 #if defined(RTABMAP_VERTIGO) 99 #endif // end defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 107 bool OptimizerG2O::available()
109 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 116 bool OptimizerG2O::isCSparseAvailable()
118 #ifdef G2O_HAVE_CSPARSE 125 bool OptimizerG2O::isCholmodAvailable()
127 #ifdef G2O_HAVE_CHOLMOD 136 Optimizer::parseParameters(parameters);
138 Parameters::parse(parameters, Parameters::kg2oSolver(), solver_);
139 Parameters::parse(parameters, Parameters::kg2oOptimizer(), optimizer_);
140 Parameters::parse(parameters, Parameters::kg2oPixelVariance(), pixelVariance_);
141 Parameters::parse(parameters, Parameters::kg2oRobustKernelDelta(), robustKernelDelta_);
142 Parameters::parse(parameters, Parameters::kg2oBaseline(), baseline_);
146 #ifdef RTABMAP_ORB_SLAM2 149 UWARN(
"g2o built with ORB_SLAM2 has only Eigen solver available, using Eigen=3 instead of %d.", solver_);
153 #ifndef G2O_HAVE_CHOLMOD 156 UWARN(
"g2o is not built with chmold, so it cannot be used as solver. Using CSparse instead.");
161 #ifndef G2O_HAVE_CSPARSE 164 UWARN(
"g2o is not built with csparse, so it cannot be used as solver. Using PCG instead.");
172 std::map<int, Transform> OptimizerG2O::optimize(
174 const std::map<int, Transform> & poses,
175 const std::multimap<int, Link> & edgeConstraints,
176 cv::Mat & outputCovariance,
177 std::list<std::map<int, Transform> > * intermediateGraphes,
179 int * iterationsDone)
181 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
182 std::map<int, Transform> optimizedPoses;
184 UDEBUG(
"Optimizing graph...");
186 #ifndef RTABMAP_VERTIGO 189 UWARN(
"Vertigo robust optimization is not available! Robust optimization is now disabled.");
194 optimizedPoses.clear();
195 if(edgeConstraints.size()>=1 && poses.size()>=2 && iterations() > 0 && poses.rbegin()->first > 0)
199 g2o::SparseOptimizer optimizer;
203 g2o::ParameterSE2Offset* odomOffset =
new g2o::ParameterSE2Offset();
205 optimizer.addParameter(odomOffset);
209 g2o::ParameterSE3Offset* odomOffset =
new g2o::ParameterSE3Offset();
211 optimizer.addParameter(odomOffset);
214 #ifdef RTABMAP_G2O_CPP11 216 std::unique_ptr<SlamBlockSolver> blockSolver;
221 auto linearSolver = g2o::make_unique<SlamLinearEigenSolver>();
222 linearSolver->setBlockOrdering(
false);
223 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
225 #ifdef G2O_HAVE_CHOLMOD 226 else if(solver_ == 2)
229 auto linearSolver = g2o::make_unique<SlamLinearCholmodSolver>();
230 linearSolver->setBlockOrdering(
false);
231 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
234 #ifdef G2O_HAVE_CSPARSE 235 else if(solver_ == 0)
239 auto linearSolver = g2o::make_unique<SlamLinearCSparseSolver>();
240 linearSolver->setBlockOrdering(
false);
241 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
247 auto linearSolver = g2o::make_unique<SlamLinearPCGSolver>();
248 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
254 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(std::move(blockSolver)));
258 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(std::move(blockSolver)));
263 SlamBlockSolver * blockSolver = 0;
268 SlamLinearEigenSolver * linearSolver =
new SlamLinearEigenSolver();
269 linearSolver->setBlockOrdering(
false);
270 blockSolver =
new SlamBlockSolver(linearSolver);
272 #ifdef G2O_HAVE_CHOLMOD 273 else if(solver_ == 2)
276 SlamLinearCholmodSolver * linearSolver =
new SlamLinearCholmodSolver();
277 linearSolver->setBlockOrdering(
false);
278 blockSolver =
new SlamBlockSolver(linearSolver);
281 #ifdef G2O_HAVE_CSPARSE 282 else if(solver_ == 0)
285 SlamLinearCSparseSolver* linearSolver =
new SlamLinearCSparseSolver();
286 linearSolver->setBlockOrdering(
false);
287 blockSolver =
new SlamBlockSolver(linearSolver);
293 SlamLinearPCGSolver * linearSolver =
new SlamLinearPCGSolver();
294 blockSolver =
new SlamBlockSolver(linearSolver);
299 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(blockSolver));
303 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(blockSolver));
309 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
311 if(iter->second.from() == iter->second.to() && iter->second.type() == Link::kPosePrior)
319 int landmarkVertexOffset = poses.rbegin()->first+1;
320 std::map<int, bool> isLandmarkWithRotation;
322 UDEBUG(
"fill poses to g2o...");
323 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
325 UASSERT(!iter->second.isNull());
326 g2o::HyperGraph::Vertex * vertex = 0;
327 int id = iter->first;
332 g2o::VertexSE2 * v2 =
new g2o::VertexSE2();
333 v2->setEstimate(g2o::SE2(iter->second.x(), iter->second.y(), iter->second.theta()));
340 else if(!landmarksIgnored())
343 std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(
id);
344 UASSERT(jter != edgeConstraints.end());
346 if (1 / static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
348 g2o::VertexPointXY * v2 =
new g2o::VertexPointXY();
349 v2->setEstimate(Eigen::Vector2d(iter->second.x(), iter->second.y()));
351 isLandmarkWithRotation.insert(std::make_pair(
id,
false));
352 id = landmarkVertexOffset - id;
356 g2o::VertexSE2 * v2 =
new g2o::VertexSE2();
357 v2->setEstimate(g2o::SE2(iter->second.x(), iter->second.y(), iter->second.theta()));
359 isLandmarkWithRotation.insert(std::make_pair(
id,
true));
360 id = landmarkVertexOffset - id;
372 g2o::VertexSE3 * v3 =
new g2o::VertexSE3();
374 Eigen::Affine3d a = iter->second.toEigen3d();
375 Eigen::Isometry3d pose;
377 pose.translation() = a.translation();
378 v3->setEstimate(pose);
385 else if(!landmarksIgnored())
388 std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(
id);
389 UASSERT(jter != edgeConstraints.end());
391 if (1 / static_cast<double>(jter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
392 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
393 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
395 g2o::VertexPointXYZ * v3 =
new g2o::VertexPointXYZ();
396 v3->setEstimate(Eigen::Vector3d(iter->second.x(), iter->second.y(), iter->second.z()));
398 isLandmarkWithRotation.insert(std::make_pair(
id,
false));
399 id = landmarkVertexOffset - id;
403 g2o::VertexSE3 * v3 =
new g2o::VertexSE3();
404 Eigen::Affine3d a = iter->second.toEigen3d();
405 Eigen::Isometry3d pose;
407 pose.translation() = a.translation();
408 v3->setEstimate(pose);
410 isLandmarkWithRotation.insert(std::make_pair(
id,
true));
411 id = landmarkVertexOffset - id;
420 UASSERT_MSG(optimizer.addVertex(vertex),
uFormat(
"cannot insert vertex %d!?", iter->first).c_str());
423 UDEBUG(
"fill edges to g2o...");
424 #if defined(RTABMAP_VERTIGO) 425 int vertigoVertexId = landmarkVertexOffset - (poses.begin()->first<0?poses.begin()->first-1:0);
427 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
429 int id1 = iter->second.from();
430 int id2 = iter->second.to();
432 UASSERT(!iter->second.transform().isNull());
434 g2o::HyperGraph::Edge * edge = 0;
438 if(iter->second.type() == Link::kPosePrior && !priorsIgnored())
442 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
444 g2o::EdgeSE2XYPrior * priorEdge =
new g2o::EdgeSE2XYPrior();
445 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
446 priorEdge->setVertex(0, v1);
447 priorEdge->setMeasurement(Eigen::Vector2d(iter->second.transform().x(), iter->second.transform().y()));
448 Eigen::Matrix<double, 2, 2> information = Eigen::Matrix<double, 2, 2>::Identity();
449 if(!isCovarianceIgnored())
451 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
452 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
453 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
454 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
456 priorEdge->setInformation(information);
461 g2o::EdgeSE2Prior * priorEdge =
new g2o::EdgeSE2Prior();
462 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
463 priorEdge->setVertex(0, v1);
464 priorEdge->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
466 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
467 if(!isCovarianceIgnored())
469 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
470 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
471 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
472 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
473 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
474 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
475 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
476 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
477 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
479 priorEdge->setInformation(information);
485 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
486 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
487 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
491 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
492 priorEdge->setVertex(0, v1);
493 priorEdge->
setMeasurement(Eigen::Vector3d(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()));
495 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
496 if(!isCovarianceIgnored())
498 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
499 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
500 information(0,2) = iter->second.infMatrix().at<
double>(0,2);
501 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
502 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
503 information(1,2) = iter->second.infMatrix().at<
double>(1,2);
504 information(2,0) = iter->second.infMatrix().at<
double>(2,0);
505 information(2,1) = iter->second.infMatrix().at<
double>(2,1);
506 information(2,2) = iter->second.infMatrix().at<
double>(2,2);
508 priorEdge->setInformation(information);
514 g2o::EdgeSE3Prior * priorEdge =
new g2o::EdgeSE3Prior();
515 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
516 priorEdge->setVertex(0, v1);
517 Eigen::Affine3d a = iter->second.transform().toEigen3d();
518 Eigen::Isometry3d pose;
520 pose.translation() = a.translation();
521 priorEdge->setMeasurement(pose);
523 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
524 if(!isCovarianceIgnored())
526 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
528 priorEdge->setInformation(information);
533 else if(!isSlam2d() && gravitySigma() > 0 && iter->second.type() == Link::kGravity && poses.find(iter->first) != poses.end())
535 Eigen::Matrix<double, 6, 1> m;
537 m.head<3>() = Eigen::Vector3d::UnitZ();
540 iter->second.transform().getEulerAngles(roll, pitch, yaw);
543 Eigen::MatrixXd information = Eigen::MatrixXd::Identity(3, 3) * 1.0/(gravitySigma()*gravitySigma());
545 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
548 priorEdge->setInformation(information);
549 priorEdge->vertices()[0] = v1;
553 else if(id1<0 || id2 < 0)
555 if(!landmarksIgnored())
558 UASSERT((id1 < 0 && id2 > 0) || (id1 > 0 && id2 < 0));
563 t = iter->second.transform();
567 t = iter->second.transform().
inverse();
571 id2 = landmarkVertexOffset - id2;
575 if(isLandmarkWithRotation.at(idTag))
577 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
578 if(!isCovarianceIgnored())
580 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
581 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
582 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
583 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
584 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
585 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
586 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
587 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
588 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
590 g2o::EdgeSE2 *
e =
new g2o::EdgeSE2();
591 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
592 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
597 e->setMeasurement(g2o::SE2(t.
x(), t.
y(), t.
theta()));
598 e->setInformation(information);
603 Eigen::Matrix<double, 2, 2> information = Eigen::Matrix<double, 2, 2>::Identity();
604 if(!isCovarianceIgnored())
606 cv::Mat linearCov = cv::Mat(iter->second.infMatrix(), cv::Range(0,2), cv::Range(0,2)).clone();
607 memcpy(information.data(), linearCov.data, linearCov.total()*
sizeof(double));
609 g2o::EdgeSE2PointXY*
e =
new g2o::EdgeSE2PointXY;
610 e->vertices()[0] = optimizer.vertex(id1);
611 e->vertices()[1] = optimizer.vertex(id2);
612 e->setMeasurement(Eigen::Vector2d(t.
x(), t.
y()));
613 e->setInformation(information);
620 if(isLandmarkWithRotation.at(idTag))
622 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
623 if(!isCovarianceIgnored())
625 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
629 Eigen::Isometry3d constraint;
630 constraint = a.linear();
631 constraint.translation() = a.translation();
633 g2o::EdgeSE3 *
e =
new g2o::EdgeSE3();
634 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
635 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
640 e->setMeasurement(constraint);
641 e->setInformation(information);
646 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
647 if(!isCovarianceIgnored())
649 cv::Mat linearCov = cv::Mat(iter->second.infMatrix(), cv::Range(0,3), cv::Range(0,3)).clone();
650 memcpy(information.data(), linearCov.data, linearCov.total()*
sizeof(double));
653 g2o::EdgeSE3PointXYZ*
e =
new g2o::EdgeSE3PointXYZ;
654 e->vertices()[0] = optimizer.vertex(id1);
655 e->vertices()[1] = optimizer.vertex(id2);
656 e->setMeasurement(Eigen::Vector3d(t.
x(), t.
y(), t.
z()));
657 e->setInformation(information);
666 #if defined(RTABMAP_VERTIGO) 668 if(this->isRobust() &&
669 iter->second.type() != Link::kNeighbor &&
670 iter->second.type() != Link::kNeighborMerged)
681 v->setId(vertigoVertexId++);
682 UASSERT_MSG(optimizer.addVertex(v),
uFormat(
"cannot insert switchable vertex %d!?", v->id()).c_str());
690 prior->setMeasurement(1.0);
691 prior->setVertex(0, v);
692 UASSERT_MSG(optimizer.addEdge(prior),
uFormat(
"cannot insert switchable prior edge %d!?", v->id()).c_str());
698 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
699 if(!isCovarianceIgnored())
701 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
702 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
703 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
704 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
705 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
706 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
707 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
708 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
709 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
712 #if defined(RTABMAP_VERTIGO) 713 if(this->isRobust() &&
714 iter->second.type() != Link::kNeighbor &&
715 iter->second.type() != Link::kNeighborMerged)
718 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
719 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
725 e->
setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
726 e->setInformation(information);
732 g2o::EdgeSE2 *
e =
new g2o::EdgeSE2();
733 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
734 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
739 e->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
740 e->setInformation(information);
746 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
747 if(!isCovarianceIgnored())
749 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
752 Eigen::Affine3d a = iter->second.transform().toEigen3d();
753 Eigen::Isometry3d constraint;
754 constraint = a.linear();
755 constraint.translation() = a.translation();
757 #if defined(RTABMAP_VERTIGO) 758 if(this->isRobust() &&
759 iter->second.type() != Link::kNeighbor &&
760 iter->second.type() != Link::kNeighborMerged)
763 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
764 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
771 e->setInformation(information);
777 g2o::EdgeSE3 *
e =
new g2o::EdgeSE3();
778 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
779 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
784 e->setMeasurement(constraint);
785 e->setInformation(information);
791 if (edge && !optimizer.addEdge(edge))
794 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
800 UDEBUG(
"Initial optimization...");
801 optimizer.initializeOptimization();
803 UASSERT_MSG(optimizer.verifyInformationMatrices(
true),
804 "This error can be caused by (1) bad covariance matrix " 805 "set in odometry messages " 806 "(see requirements in g2o::OptimizableGraph::verifyInformationMatrices() function) " 807 "or that (2) PCL and g2o hadn't " 808 "been built both with or without \"-march=native\" compilation " 809 "flag (if one library is built with this flag and not the other, " 810 "this is causing Eigen to not work properly, resulting in segmentation faults).");
812 UINFO(
"g2o optimizing begin (max iterations=%d, robust=%d)", iterations(), isRobust()?1:0);
815 double lastError = 0.0;
816 if(intermediateGraphes || this->
epsilon() > 0.0)
818 for(
int i=0; i<iterations(); ++i)
820 if(intermediateGraphes)
824 std::map<int, Transform> tmpPoses;
827 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
829 int id = iter->first;
832 const g2o::VertexSE2* v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
836 iter->second.getEulerAngles(roll, pitch, yaw);
837 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(),
roll,
pitch, v->estimate().rotation().angle());
838 tmpPoses.insert(std::pair<int, Transform>(
id, t));
843 UERROR(
"Vertex %d not found!?",
id);
846 else if(!landmarksIgnored())
848 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
851 if(isLandmarkWithRotation.at(
id))
853 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)v;
855 iter->second.getEulerAngles(roll, pitch, yaw);
856 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1], iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
857 tmpPoses.insert(std::pair<int, Transform>(
id, t));
862 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)v;
864 iter->second.getEulerAngles(roll, pitch, yaw);
866 tmpPoses.insert(std::pair<int, Transform>(
id, t));
872 UERROR(
"Vertex %d not found!?",
id);
879 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
881 int id = iter->first;
884 const g2o::VertexSE3* v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
887 Transform t = Transform::fromEigen3d(v->estimate());
888 tmpPoses.insert(std::pair<int, Transform>(
id, t));
893 UERROR(
"Vertex %d not found!?",
id);
896 else if(!landmarksIgnored())
898 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
901 if(isLandmarkWithRotation.at(
id))
903 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)v;
904 Transform t = Transform::fromEigen3d(vSE3->estimate());
905 tmpPoses.insert(std::pair<int, Transform>(
id, t));
910 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)v;
912 iter->second.getEulerAngles(roll, pitch, yaw);
914 tmpPoses.insert(std::pair<int, Transform>(
id, t));
920 UERROR(
"Vertex %d not found!?",
id);
925 intermediateGraphes->push_back(tmpPoses);
929 it += optimizer.optimize(1);
932 optimizer.computeActiveErrors();
933 double chi2 = optimizer.activeRobustChi2();
934 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f", i, (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
936 if(i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
938 UWARN(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
939 return optimizedPoses;
942 double errorDelta = lastError - chi2;
943 if(i>0 && errorDelta < this->
epsilon())
947 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
951 UINFO(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
955 else if(i==0 && chi2 < this->
epsilon())
957 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)", chi2, this->
epsilon());
965 it = optimizer.optimize(iterations());
966 optimizer.computeActiveErrors();
967 UDEBUG(
"%d nodes, %d edges, chi2: %f", (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), optimizer.activeRobustChi2());
971 *finalError = lastError;
975 *iterationsDone = it;
977 UINFO(
"g2o optimizing end (%d iterations done, error=%f, time = %f s)", it, optimizer.activeRobustChi2(), timer.
ticks());
979 if(optimizer.activeRobustChi2() > 1000000000000.0)
981 UWARN(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
982 return optimizedPoses;
987 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
989 int id = iter->first;
992 const g2o::VertexSE2* v = (
const g2o::VertexSE2*)optimizer.vertex(
id);
996 iter->second.getEulerAngles(roll, pitch, yaw);
997 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(),
roll,
pitch, v->estimate().rotation().angle());
998 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1003 UERROR(
"Vertex %d not found!?",
id);
1006 else if(!landmarksIgnored())
1008 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1012 if(isLandmarkWithRotation.at(
id))
1014 const g2o::VertexSE2* vSE2 = (
const g2o::VertexSE2*)v;
1016 iter->second.getEulerAngles(roll, pitch, yaw);
1017 Transform t(vSE2->estimate().translation()[0], vSE2->estimate().translation()[1], iter->second.z(),
roll,
pitch, vSE2->estimate().rotation().angle());
1018 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1023 const g2o::VertexPointXY* vP = (
const g2o::VertexPointXY*)v;
1025 iter->second.getEulerAngles(roll, pitch, yaw);
1027 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1033 UERROR(
"Vertex %d not found!?",
id);
1038 g2o::VertexSE2* v = (g2o::VertexSE2*)optimizer.vertex(poses.rbegin()->first);
1042 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
1043 optimizer.computeMarginals(spinv, v);
1044 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)", t.
ticks(), spinv.cols(), spinv.rows(), v->hessianIndex(), poses.rbegin()->first);
1045 if(v->hessianIndex() >= 0 && v->hessianIndex() < (int)spinv.blockCols().size())
1047 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock * block = spinv.blockCols()[v->hessianIndex()].begin()->second;
1048 UASSERT(block && block->cols() == 3 && block->cols() == 3);
1049 outputCovariance.at<
double>(0,0) = (*block)(0,0);
1050 outputCovariance.at<
double>(0,1) = (*block)(0,1);
1051 outputCovariance.at<
double>(0,5) = (*block)(0,2);
1052 outputCovariance.at<
double>(1,0) = (*block)(1,0);
1053 outputCovariance.at<
double>(1,1) = (*block)(1,1);
1054 outputCovariance.at<
double>(1,5) = (*block)(1,2);
1055 outputCovariance.at<
double>(5,0) = (*block)(2,0);
1056 outputCovariance.at<
double>(5,1) = (*block)(2,1);
1057 outputCovariance.at<
double>(5,5) = (*block)(2,2);
1059 else if(v->hessianIndex() < 0)
1061 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex());
1065 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());
1070 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1075 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1077 int id = iter->first;
1080 const g2o::VertexSE3* v = (
const g2o::VertexSE3*)optimizer.vertex(
id);
1083 Transform t = Transform::fromEigen3d(v->estimate());
1084 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1089 UERROR(
"Vertex %d not found!?",
id);
1092 else if(!landmarksIgnored())
1094 const g2o::OptimizableGraph::Vertex* v = (
const g2o::OptimizableGraph::Vertex*)optimizer.vertex(landmarkVertexOffset -
id);
1098 if(isLandmarkWithRotation.at(
id))
1100 const g2o::VertexSE3* vSE3 = (
const g2o::VertexSE3*)v;
1101 Transform t = Transform::fromEigen3d(vSE3->estimate());
1102 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1107 const g2o::VertexPointXYZ* vP = (
const g2o::VertexPointXYZ*)v;
1109 iter->second.getEulerAngles(roll, pitch, yaw);
1111 optimizedPoses.insert(std::pair<int, Transform>(
id, t));
1117 UERROR(
"Vertex %d not found!?",
id);
1122 g2o::VertexSE3* v = (g2o::VertexSE3*)optimizer.vertex(poses.rbegin()->first);
1126 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
1127 optimizer.computeMarginals(spinv, v);
1128 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)", t.
ticks(), spinv.cols(), spinv.rows(), v->hessianIndex(), poses.rbegin()->first);
1129 if(v->hessianIndex() >= 0 && v->hessianIndex() < (int)spinv.blockCols().size())
1131 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock * block = spinv.blockCols()[v->hessianIndex()].begin()->second;
1132 UASSERT(block && block->cols() == 6 && block->cols() == 6);
1133 memcpy(outputCovariance.data, block->data(), outputCovariance.total()*
sizeof(double));
1135 else if(v->hessianIndex() < 0)
1137 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex());
1139 #ifdef RTABMAP_G2O_CPP11 1142 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());
1148 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
1152 else if(poses.size() == 1 || iterations() <= 0)
1154 optimizedPoses = poses;
1158 UWARN(
"This method should be called at least with 1 pose!");
1160 UDEBUG(
"Optimizing graph...end!");
1162 #ifdef RTABMAP_ORB_SLAM2 1163 UERROR(
"G2O graph optimization cannot be used with g2o built from ORB_SLAM2, only SBA is available.");
1165 UERROR(
"Not built with G2O support!");
1168 return optimizedPoses;
1171 #ifdef RTABMAP_ORB_SLAM2 1175 class EdgeSE3Expmap :
public g2o::BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>
1178 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
1179 EdgeSE3Expmap(): BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>(){}
1180 bool read(std::istream& is)
1185 bool write(std::ostream& os)
const 1192 const g2o::VertexSE3Expmap* v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1193 const g2o::VertexSE3Expmap* v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1194 g2o::SE3Quat delta = _inverseMeasurement * (v1->estimate().inverse()*v2->estimate());
1195 _error[0]=delta.translation().x();
1196 _error[1]=delta.translation().y();
1197 _error[2]=delta.translation().z();
1198 _error[3]=delta.rotation().x();
1199 _error[4]=delta.rotation().y();
1200 _error[5]=delta.rotation().z();
1203 virtual void setMeasurement(
const g2o::SE3Quat& meas){
1205 _inverseMeasurement=meas.inverse();
1208 virtual double initialEstimatePossible(
const g2o::OptimizableGraph::VertexSet& , g2o::OptimizableGraph::Vertex* ) {
return 1.;}
1209 virtual void initialEstimate(
const g2o::OptimizableGraph::VertexSet& from_, g2o::OptimizableGraph::Vertex* ){
1210 g2o::VertexSE3Expmap* from =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[0]);
1211 g2o::VertexSE3Expmap* to =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[1]);
1212 if (from_.count(from) > 0)
1213 to->setEstimate((g2o::SE3Quat) from->estimate() * _measurement);
1215 from->setEstimate((g2o::SE3Quat) to->estimate() * _inverseMeasurement);
1218 virtual bool setMeasurementData(
const double*
d){
1219 Eigen::Map<const g2o::Vector7d> v(d);
1220 _measurement.fromVector(v);
1221 _inverseMeasurement = _measurement.inverse();
1225 virtual bool getMeasurementData(
double* d)
const{
1226 Eigen::Map<g2o::Vector7d> v(d);
1227 v = _measurement.toVector();
1231 virtual int measurementDimension()
const {
return 7;}
1233 virtual bool setMeasurementFromState() {
1234 const g2o::VertexSE3Expmap* v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
1235 const g2o::VertexSE3Expmap* v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
1236 _measurement = (v1->estimate().inverse()*v2->estimate());
1237 _inverseMeasurement = _measurement.inverse();
1242 g2o::SE3Quat _inverseMeasurement;
1246 std::map<int, Transform> OptimizerG2O::optimizeBA(
1248 const std::map<int, Transform> & poses,
1249 const std::multimap<int, Link> & links,
1250 const std::map<int, CameraModel> & models,
1251 std::map<int, cv::Point3f> & points3DMap,
1252 const std::map<
int, std::map<int, FeatureBA> > & wordReferences,
1253 std::set<int> * outliers)
1255 std::map<int, Transform> optimizedPoses;
1256 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 1257 UDEBUG(
"Optimizing graph...");
1259 optimizedPoses.clear();
1260 if(poses.size()>=2 && iterations() > 0 && (models.size() == poses.size() || poses.begin()->first < 0))
1262 g2o::SparseOptimizer optimizer;
1264 #if defined(RTABMAP_G2O_CPP11) and not defined(RTABMAP_ORB_SLAM2) 1265 std::unique_ptr<g2o::BlockSolver_6_3::LinearSolverType> linearSolver;
1267 g2o::BlockSolver_6_3::LinearSolverType * linearSolver = 0;
1270 #ifdef RTABMAP_ORB_SLAM2 1271 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1276 #ifdef RTABMAP_G2O_CPP11 1277 linearSolver = g2o::make_unique<g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType> >();
1279 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
1282 #ifdef G2O_HAVE_CHOLMOD 1283 else if(solver_ == 2)
1286 #ifdef RTABMAP_G2O_CPP11 1287 linearSolver = g2o::make_unique<g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType> >();
1289 linearSolver =
new g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType>();
1293 #ifdef G2O_HAVE_CSPARSE 1294 else if(solver_ == 0)
1297 #ifdef RTABMAP_G2O_CPP11 1298 linearSolver = g2o::make_unique<g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType> >();
1300 linearSolver =
new g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType>();
1307 #ifdef RTABMAP_G2O_CPP11 1308 linearSolver = g2o::make_unique<g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType> >();
1310 linearSolver =
new g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType>();
1313 #endif // RTABMAP_ORB_SLAM2 1315 #ifndef RTABMAP_ORB_SLAM2 1318 #ifdef RTABMAP_G2O_CPP11 1319 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
1320 g2o::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1322 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
new g2o::BlockSolver_6_3(linearSolver)));
1328 #if defined(RTABMAP_G2O_CPP11) and not defined(RTABMAP_ORB_SLAM2) 1329 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
1330 g2o::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
1332 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
new g2o::BlockSolver_6_3(linearSolver)));
1337 UDEBUG(
"fill poses to g2o...");
1338 for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
1343 std::map<int, CameraModel>::const_iterator iterModel = models.find(iter->first);
1344 UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
1346 Transform camPose = iter->second * iterModel->second.localTransform();
1350 #ifdef RTABMAP_ORB_SLAM2 1351 g2o::VertexSE3Expmap * vCam =
new g2o::VertexSE3Expmap();
1353 g2o::VertexCam * vCam =
new g2o::VertexCam();
1356 Eigen::Affine3d a = camPose.toEigen3d();
1357 #ifdef RTABMAP_ORB_SLAM2 1359 vCam->setEstimate(g2o::SE3Quat(a.linear(), a.translation()));
1361 g2o::SBACam
cam(Eigen::Quaterniond(a.linear()), a.translation());
1363 iterModel->second.fx(),
1364 iterModel->second.fy(),
1365 iterModel->second.cx(),
1366 iterModel->second.cy(),
1367 iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_);
1368 vCam->setEstimate(
cam);
1370 vCam->setId(iter->first);
1373 vCam->setFixed((rootId >= 0 && iter->first == rootId) || (rootId < 0 && iter->first != -rootId));
1386 UASSERT_MSG(optimizer.addVertex(vCam),
uFormat(
"cannot insert vertex %d!?", iter->first).c_str());
1390 UDEBUG(
"fill edges to g2o...");
1391 for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
1393 if(iter->second.from() > 0 &&
1394 iter->second.to() > 0 &&
1395 uContains(poses, iter->second.from()) &&
1399 int id1 = iter->second.from();
1400 int id2 = iter->second.to();
1404 #ifndef RTABMAP_ORB_SLAM2 1405 g2o::HyperGraph::Edge * edge = 0;
1406 if(gravitySigma() > 0 && iter->second.type() == Link::kGravity && poses.find(iter->first) != poses.end())
1408 Eigen::Matrix<double, 6, 1> m;
1410 m.head<3>() = Eigen::Vector3d::UnitZ();
1413 iter->second.transform().getEulerAngles(roll, pitch, yaw);
1416 Eigen::MatrixXd information = Eigen::MatrixXd::Identity(3, 3) * 1.0/(gravitySigma()*gravitySigma());
1418 g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1);
1420 std::map<int, CameraModel>::const_iterator iterModel = models.find(iter->first);
1421 UASSERT(iterModel != models.end() && !iterModel->second.localTransform().isNull());
1424 priorEdge->setInformation(information);
1425 priorEdge->vertices()[0] = v1;
1428 if (edge && !optimizer.addEdge(edge))
1431 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1432 return optimizedPoses;
1436 else if(id1>0 && id2>0)
1438 UASSERT(!iter->second.transform().isNull());
1440 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
1441 if(!isCovarianceIgnored())
1443 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
1447 Transform camLink = models.at(id1).localTransform().
inverse()*iter->second.transform()*models.at(id2).localTransform();
1452 #ifdef RTABMAP_ORB_SLAM2 1453 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1454 g2o::VertexSE3Expmap* v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id1);
1455 g2o::VertexSE3Expmap* v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id2);
1458 Transform camPose2Inv = Transform::fromEigen3d(v2->estimate());
1460 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1462 g2o::EdgeSBACam * e =
new g2o::EdgeSBACam();
1463 g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1);
1464 g2o::VertexCam* v2 = (g2o::VertexCam*)optimizer.vertex(id2);
1468 e->setVertex(0, v1);
1469 e->setVertex(1, v2);
1470 Eigen::Affine3d a = camLink.toEigen3d();
1471 e->setMeasurement(g2o::SE3Quat(a.linear(), a.translation()));
1472 e->setInformation(information);
1474 if (!optimizer.addEdge(e))
1477 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1478 return optimizedPoses;
1484 UDEBUG(
"fill 3D points to g2o...");
1485 const int stepVertexId = poses.rbegin()->first+1;
1486 int negVertexOffset = stepVertexId;
1487 if(wordReferences.size() && wordReferences.rbegin()->first>0)
1489 negVertexOffset += wordReferences.rbegin()->first;
1491 UDEBUG(
"stepVertexId=%d, negVertexOffset=%d", stepVertexId, negVertexOffset);
1492 std::list<g2o::OptimizableGraph::Edge*> edges;
1493 for(std::map<
int, std::map<int, FeatureBA> >::const_iterator iter = wordReferences.begin(); iter!=wordReferences.end(); ++iter)
1495 int id = iter->first;
1496 if(points3DMap.find(
id) != points3DMap.end())
1498 cv::Point3f pt3d = points3DMap.at(
id);
1501 UWARN(
"Ignoring 3D point %d because it has nan value(s)!",
id);
1504 g2o::VertexSBAPointXYZ* vpt3d =
new g2o::VertexSBAPointXYZ();
1506 vpt3d->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1509 vpt3d->setId(negVertexOffset +
id*-1);
1513 vpt3d->setId(stepVertexId +
id);
1516 vpt3d->setMarginalized(
true);
1517 optimizer.addVertex(vpt3d);
1522 for(std::map<int, FeatureBA>::const_iterator jter=iter->second.begin(); jter!=iter->second.end(); ++jter)
1524 int camId = jter->first;
1525 if(poses.find(camId) != poses.end() && optimizer.vertex(camId) != 0)
1528 double depth = pt.
depth;
1532 g2o::OptimizableGraph::Edge *
e;
1533 double baseline = 0.0;
1534 #ifdef RTABMAP_ORB_SLAM2 1535 g2o::VertexSE3Expmap* vcam =
dynamic_cast<g2o::VertexSE3Expmap*
>(optimizer.vertex(camId));
1536 std::map<int, CameraModel>::const_iterator iterModel = models.find(camId);
1538 UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
1539 baseline = iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_;
1541 g2o::VertexCam* vcam =
dynamic_cast<g2o::VertexCam*
>(optimizer.vertex(camId));
1542 baseline = vcam->estimate().baseline;
1544 double variance = pixelVariance_;
1545 if(
uIsFinite(depth) && depth > 0.0 && baseline > 0.0)
1548 #ifdef RTABMAP_ORB_SLAM2 1549 g2o::EdgeStereoSE3ProjectXYZ* es =
new g2o::EdgeStereoSE3ProjectXYZ();
1550 float disparity = baseline * iterModel->second.fx() / depth;
1551 Eigen::Vector3d obs( pt.
kpt.pt.x, pt.
kpt.pt.y, pt.
kpt.pt.x-disparity);
1552 es->setMeasurement(obs);
1554 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1555 es->fx = iterModel->second.fx();
1556 es->fy = iterModel->second.fy();
1557 es->cx = iterModel->second.cx();
1558 es->cy = iterModel->second.cy();
1559 es->bf = baseline*es->fx;
1562 g2o::EdgeProjectP2SC* es =
new g2o::EdgeProjectP2SC();
1563 float disparity = baseline * vcam->estimate().Kcam(0,0) / depth;
1564 Eigen::Vector3d obs( pt.
kpt.pt.x, pt.
kpt.pt.y, pt.
kpt.pt.x-disparity);
1565 es->setMeasurement(obs);
1567 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1575 UDEBUG(
"Stereo camera model detected but current " 1576 "observation (pt=%d to cam=%d, kpt=[%d,%d]) has null depth (%f m), adding " 1577 "mono observation instead.",
1578 vpt3d->id()-stepVertexId, camId, (int)pt.
kpt.pt.x, (
int)pt.
kpt.pt.y, depth);
1581 #ifdef RTABMAP_ORB_SLAM2 1582 g2o::EdgeSE3ProjectXYZ* em =
new g2o::EdgeSE3ProjectXYZ();
1583 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1584 em->setMeasurement(obs);
1585 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1586 em->fx = iterModel->second.fx();
1587 em->fy = iterModel->second.fy();
1588 em->cx = iterModel->second.cx();
1589 em->cy = iterModel->second.cy();
1593 g2o::EdgeProjectP2MC* em =
new g2o::EdgeProjectP2MC();
1594 Eigen::Vector2d obs( pt.
kpt.pt.x, pt.
kpt.pt.y);
1595 em->setMeasurement(obs);
1596 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1600 e->setVertex(0, vpt3d);
1601 e->setVertex(1, vcam);
1603 if(robustKernelDelta_ > 0.0)
1605 g2o::RobustKernelHuber* kernel =
new g2o::RobustKernelHuber;
1606 kernel->setDelta(robustKernelDelta_);
1607 e->setRobustKernel(kernel);
1610 optimizer.addEdge(e);
1617 UDEBUG(
"Initial optimization...");
1618 optimizer.initializeOptimization();
1620 UASSERT(optimizer.verifyInformationMatrices());
1622 UDEBUG(
"g2o optimizing begin (max iterations=%d, robustKernel=%f)", iterations(), robustKernelDelta_);
1626 int outliersCount = 0;
1627 int outliersCountFar = 0;
1629 for(
int i=0; i<(robustKernelDelta_>0.0?2:1); ++i)
1631 it += optimizer.optimize(i==0&&robustKernelDelta_>0.0?5:iterations());
1634 optimizer.computeActiveErrors();
1635 double chi2 = optimizer.activeRobustChi2();
1639 UERROR(
"Optimization generated NANs, aborting optimization! Try another g2o's optimizer (current=%d).", optimizer_);
1640 return optimizedPoses;
1642 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f", i, (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
1644 if(i>0 && (optimizer.activeRobustChi2() > 1000000000000.0 || !
uIsFinite(optimizer.activeRobustChi2())))
1646 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1647 return optimizedPoses;
1650 if(robustKernelDelta_>0.0)
1652 for(std::list<g2o::OptimizableGraph::Edge*>::iterator iter=edges.begin(); iter!=edges.end();++iter)
1654 if((*iter)->level() == 0 && (*iter)->chi2() > (*iter)->robustKernel()->delta())
1656 (*iter)->setLevel(1);
1659 #ifdef RTABMAP_ORB_SLAM2 1660 if(dynamic_cast<g2o::EdgeStereoSE3ProjectXYZ*>(*iter) != 0)
1662 d = ((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[0]-((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[2];
1664 UDEBUG(
"Ignoring edge (%d<->%d) d=%f var=%f kernel=%f chi2=%f", (*iter)->vertex(0)->id()-stepVertexId, (*iter)->vertex(1)->id(), d, 1.0/((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->information()(0,0), (*iter)->robustKernel()->delta(), (*iter)->chi2());
1666 if(dynamic_cast<g2o::EdgeProjectP2SC*>(*iter) != 0)
1668 d = ((g2o::EdgeProjectP2SC*)(*iter))->measurement()[0]-((g2o::EdgeProjectP2SC*)(*iter))->measurement()[2];
1670 UDEBUG(
"Ignoring edge (%d<->%d) d=%f var=%f kernel=%f chi2=%f", (*iter)->vertex(0)->id()-stepVertexId, (*iter)->vertex(1)->id(), d, 1.0/((g2o::EdgeProjectP2SC*)(*iter))->information()(0,0), (*iter)->robustKernel()->delta(), (*iter)->chi2());
1674 if((*iter)->vertex(0)->id() > negVertexOffset)
1676 pt3d = points3DMap.at(negVertexOffset - (*iter)->vertex(0)->id());
1680 pt3d = points3DMap.at((*iter)->vertex(0)->id()-stepVertexId);
1682 ((g2o::VertexSBAPointXYZ*)(*iter)->vertex(0))->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1686 outliers->insert((*iter)->vertex(0)->id()-stepVertexId);
1696 optimizer.initializeOptimization(0);
1697 UDEBUG(
"outliers=%d outliersCountFar=%d", outliersCount, outliersCountFar);
1700 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());
1702 if(optimizer.activeRobustChi2() > 1000000000000.0)
1704 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1705 return optimizedPoses;
1709 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1713 #ifdef RTABMAP_ORB_SLAM2 1714 const g2o::VertexSE3Expmap* v = (
const g2o::VertexSE3Expmap*)optimizer.vertex(iter->first);
1716 const g2o::VertexCam* v = (
const g2o::VertexCam*)optimizer.vertex(iter->first);
1720 Transform t = Transform::fromEigen3d(v->estimate());
1722 #ifdef RTABMAP_ORB_SLAM2 1727 t *= models.at(iter->first).localTransform().
inverse();
1732 UERROR(
"Optimized pose %d is null!?!?", iter->first);
1733 optimizedPoses.clear();
1734 return optimizedPoses;
1738 if(this->isSlam2d())
1741 t = iter->second.inverse() * t;
1742 optimizedPoses.insert(std::pair<int, Transform>(iter->first, iter->second * t.to3DoF()));
1746 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
1751 UERROR(
"Vertex (pose) %d not found!?", iter->first);
1758 for(std::map<int, cv::Point3f>::iterator iter = points3DMap.begin(); iter!=points3DMap.end(); ++iter)
1760 const g2o::VertexSBAPointXYZ* v;
1761 int id = iter->first;
1764 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(negVertexOffset +
id*-1);
1768 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(stepVertexId +
id);
1773 cv::Point3f p(v->estimate()[0], v->estimate()[1], v->estimate()[2]);
1779 iter->second.x = iter->second.y = iter->second.z = std::numeric_limits<float>::quiet_NaN();
1783 else if(poses.size() > 1 && (poses.size() != models.size() && poses.begin()->first > 0))
1785 UERROR(
"This method should be called with size of poses = size camera models!");
1787 else if(poses.size() == 1 || iterations() <= 0)
1789 optimizedPoses = poses;
1793 UWARN(
"This method should be called at least with 1 pose!");
1795 UDEBUG(
"Optimizing graph...end!");
1797 UERROR(
"Not built with G2O support!");
1799 return optimizedPoses;
1802 bool OptimizerG2O::saveGraph(
1803 const std::string & fileName,
1804 const std::map<int, Transform> & poses,
1805 const std::multimap<int, Link> & edgeConstraints)
1810 fopen_s(&file, fileName.c_str(),
"w");
1812 file = fopen(fileName.c_str(),
"w");
1818 setlocale(LC_ALL,
"en_US.UTF-8");
1823 fprintf(file,
"PARAMS_SE2OFFSET %d 0 0 0\n",
PARAM_OFFSET);
1828 Eigen::Vector3f v = Eigen::Vector3f::Zero();
1829 Eigen::Quaternionf q = Eigen::Quaternionf::Identity();
1830 fprintf(file,
"PARAMS_SE3OFFSET %d %f %f %f %f %f %f %f\n",
1841 int landmarkOffset = poses.size()&&poses.rbegin()->first>0?poses.rbegin()->first+1:0;
1842 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1849 fprintf(file,
"VERTEX_SE2 %d %f %f %f\n",
1850 landmarkOffset-iter->first,
1853 iter->second.theta());
1855 else if(!landmarksIgnored())
1858 fprintf(file,
"VERTEX_XY %d %f %f\n",
1869 Eigen::Quaternionf q = iter->second.getQuaternionf();
1870 fprintf(file,
"VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
1880 else if(!landmarksIgnored())
1883 fprintf(file,
"VERTEX_TRACKXYZ %d %f %f %f\n",
1884 landmarkOffset-iter->first,
1892 int virtualVertexId = landmarkOffset - (poses.size()&&poses.rbegin()->first<0?poses.rbegin()->first:0);
1893 for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
1895 if (iter->second.type() == Link::kLandmark)
1897 if (this->landmarksIgnored())
1904 fprintf(file,
"EDGE_SE2_XY %d %d %f %f %f %f %f\n",
1905 iter->second.from()<0?landmarkOffset-iter->second.from():iter->second.from(),
1906 iter->second.to()<0?landmarkOffset-iter->second.to():iter->second.to(),
1907 iter->second.transform().x(),
1908 iter->second.transform().y(),
1909 iter->second.infMatrix().at<
double>(0, 0),
1910 iter->second.infMatrix().at<
double>(0, 1),
1911 iter->second.infMatrix().at<
double>(1, 1));
1916 fprintf(file,
"EDGE_SE3_TRACKXYZ %d %d %d %f %f %f %f %f %f %f %f %f\n",
1917 iter->second.from()<0?landmarkOffset-iter->second.from():iter->second.from(),
1918 iter->second.to()<0?landmarkOffset-iter->second.to():iter->second.to(),
1920 iter->second.transform().x(),
1921 iter->second.transform().y(),
1922 iter->second.transform().z(),
1923 iter->second.infMatrix().at<
double>(0, 0),
1924 iter->second.infMatrix().at<
double>(0, 1),
1925 iter->second.infMatrix().at<
double>(0, 2),
1926 iter->second.infMatrix().at<
double>(1, 1),
1927 iter->second.infMatrix().at<
double>(1, 2),
1928 iter->second.infMatrix().at<
double>(2, 2));
1933 std::string prefix = isSlam2d()?
"EDGE_SE2" :
"EDGE_SE3:QUAT";
1934 std::string suffix =
"";
1935 std::string to =
uFormat(
" %d", iter->second.to());
1940 if (iter->second.type() == Link::kGravity)
1944 else if (iter->second.type() == Link::kPosePrior)
1946 if (this->priorsIgnored())
1952 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
1954 prefix =
"EDGE_PRIOR_SE2_XY";
1959 prefix =
"EDGE_PRIOR_SE2";
1968 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
1969 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
1970 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
1973 prefix =
"EDGE_POINTXYZ_PRIOR";
1979 prefix =
"EDGE_SE3_PRIOR";
1983 else if(this->isRobust() &&
1984 iter->second.type() != Link::kNeighbor &&
1985 iter->second.type() != Link::kNeighborMerged)
1987 fprintf(file,
"VERTEX_SWITCH %d 1\n", virtualVertexId);
1988 fprintf(file,
"EDGE_SWITCH_PRIOR %d 1 1.0\n", virtualVertexId);
1989 prefix = isSlam2d() ?
"EDGE_SE2_SWITCHABLE" :
"EDGE_SE3_SWITCHABLE";
1990 suffix =
uFormat(
" %d", virtualVertexId++);
1999 fprintf(file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2001 iter->second.from(),
2004 iter->second.transform().x(),
2005 iter->second.transform().y(),
2006 iter->second.transform().theta(),
2007 iter->second.infMatrix().at<
double>(0, 0),
2008 iter->second.infMatrix().at<
double>(0, 1),
2009 iter->second.infMatrix().at<
double>(0, 5),
2010 iter->second.infMatrix().at<
double>(1, 1),
2011 iter->second.infMatrix().at<
double>(1, 5),
2012 iter->second.infMatrix().at<
double>(5, 5));
2018 fprintf(file,
"%s %d%s%s %f %f %f %f %f\n",
2020 iter->second.from(),
2023 iter->second.transform().x(),
2024 iter->second.transform().y(),
2025 iter->second.infMatrix().at<
double>(0, 0),
2026 iter->second.infMatrix().at<
double>(0, 1),
2027 iter->second.infMatrix().at<
double>(1, 1));
2036 Eigen::Quaternionf q = iter->second.transform().getQuaternionf();
2037 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",
2039 iter->second.from(),
2042 iter->second.transform().x(),
2043 iter->second.transform().y(),
2044 iter->second.transform().z(),
2049 iter->second.infMatrix().at<
double>(0, 0),
2050 iter->second.infMatrix().at<
double>(0, 1),
2051 iter->second.infMatrix().at<
double>(0, 2),
2052 iter->second.infMatrix().at<
double>(0, 3),
2053 iter->second.infMatrix().at<
double>(0, 4),
2054 iter->second.infMatrix().at<
double>(0, 5),
2055 iter->second.infMatrix().at<
double>(1, 1),
2056 iter->second.infMatrix().at<
double>(1, 2),
2057 iter->second.infMatrix().at<
double>(1, 3),
2058 iter->second.infMatrix().at<
double>(1, 4),
2059 iter->second.infMatrix().at<
double>(1, 5),
2060 iter->second.infMatrix().at<
double>(2, 2),
2061 iter->second.infMatrix().at<
double>(2, 3),
2062 iter->second.infMatrix().at<
double>(2, 4),
2063 iter->second.infMatrix().at<
double>(2, 5),
2064 iter->second.infMatrix().at<
double>(3, 3),
2065 iter->second.infMatrix().at<
double>(3, 4),
2066 iter->second.infMatrix().at<
double>(3, 5),
2067 iter->second.infMatrix().at<
double>(4, 4),
2068 iter->second.infMatrix().at<
double>(4, 5),
2069 iter->second.infMatrix().at<
double>(5, 5));
2075 fprintf(file,
"%s %d%s%s %f %f %f %f %f %f %f %f %f\n",
2077 iter->second.from(),
2080 iter->second.transform().x(),
2081 iter->second.transform().y(),
2082 iter->second.transform().z(),
2083 iter->second.infMatrix().at<
double>(0, 0),
2084 iter->second.infMatrix().at<
double>(0, 1),
2085 iter->second.infMatrix().at<
double>(0, 2),
2086 iter->second.infMatrix().at<
double>(1, 1),
2087 iter->second.infMatrix().at<
double>(1, 2),
2088 iter->second.infMatrix().at<
double>(2, 2));
2092 UINFO(
"Graph saved to %s", fileName.c_str());
2098 UERROR(
"Cannot save to file %s", fileName.c_str());
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
bool RTABMAP_EXP isFinite(const cv::Point3f &pt)
rtabmap::CameraThread * cam
g2o edge with gravity constraint
virtual void setMeasurement(const Eigen::Vector3d &m)
GLM_FUNC_DECL genType e()
std::map< std::string, std::string > ParametersMap
Basic mathematics functions.
Prior for a 3D pose with constraints only in xyz direction.
Some conversion functions.
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
virtual void setMeasurement(const Eigen::Isometry3d &m)
bool uIsFinite(const T &value)
#define UASSERT(condition)
Wrappers of STL for convenient functions.
#define UASSERT_MSG(condition, msg_str)
virtual void setMeasurement(const Eigen::Matrix< double, 6, 1 > &m)
bool uContains(const std::list< V > &list, const V &value)
virtual void setMeasurement(const g2o::SE2 &m)
ULogger class and convenient macros.
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
bool uIsNan(const T &value)
virtual void setEstimate(const double &et)
virtual void setMeasurement(const Eigen::Matrix< double, 6, 1 > &m)
std::string UTILITE_EXP uFormat(const char *fmt,...)
void setCameraInvLocalTransform(const Eigen::Matrix3d &t)
GLM_FUNC_DECL matType< T, P > inverse(matType< T, P > const &m)