35 #include <rtabmap/core/Version.h> 41 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 42 #include "g2o/core/sparse_optimizer.h" 43 #include "g2o/core/block_solver.h" 44 #include "g2o/core/factory.h" 45 #include "g2o/core/optimization_algorithm_factory.h" 46 #include "g2o/core/optimization_algorithm_gauss_newton.h" 47 #include "g2o/core/optimization_algorithm_levenberg.h" 48 #include "g2o/core/robust_kernel_impl.h" 51 typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> MatrixXD;
55 #include "g2o/types/sba/types_sba.h" 56 #include "g2o/solvers/eigen/linear_solver_eigen.h" 57 #include "g2o/config.h" 58 #include "g2o/types/slam2d/types_slam2d.h" 59 #include "g2o/types/slam3d/types_slam3d.h" 60 #ifdef G2O_HAVE_CSPARSE 61 #include "g2o/solvers/csparse/linear_solver_csparse.h" 63 #include "g2o/solvers/pcg/linear_solver_pcg.h" 64 #ifdef G2O_HAVE_CHOLMOD 65 #include "g2o/solvers/cholmod/linear_solver_cholmod.h" 72 #ifdef RTABMAP_ORB_SLAM2 73 #include "g2o/types/types_sba.h" 74 #include "g2o/types/types_six_dof_expmap.h" 75 #include "g2o/solvers/linear_solver_eigen.h" 78 typedef g2o::BlockSolver< g2o::BlockSolverTraits<-1, -1> > SlamBlockSolver;
79 typedef g2o::LinearSolverEigen<SlamBlockSolver::PoseMatrixType> SlamLinearEigenSolver;
81 typedef g2o::LinearSolverPCG<SlamBlockSolver::PoseMatrixType> SlamLinearPCGSolver;
82 #ifdef G2O_HAVE_CSPARSE 83 typedef g2o::LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearCSparseSolver;
85 #ifdef G2O_HAVE_CHOLMOD 86 typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver;
89 #if defined(RTABMAP_VERTIGO) 97 #endif // end defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 101 bool OptimizerG2O::available()
103 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 110 bool OptimizerG2O::isCSparseAvailable()
112 #ifdef G2O_HAVE_CSPARSE 119 bool OptimizerG2O::isCholmodAvailable()
121 #ifdef G2O_HAVE_CHOLMOD 130 Optimizer::parseParameters(parameters);
132 Parameters::parse(parameters, Parameters::kg2oSolver(), solver_);
133 Parameters::parse(parameters, Parameters::kg2oOptimizer(), optimizer_);
134 Parameters::parse(parameters, Parameters::kg2oPixelVariance(), pixelVariance_);
135 Parameters::parse(parameters, Parameters::kg2oRobustKernelDelta(), robustKernelDelta_);
136 Parameters::parse(parameters, Parameters::kg2oBaseline(), baseline_);
140 #ifdef RTABMAP_ORB_SLAM2 143 UWARN(
"g2o built with ORB_SLAM2 has only Eigen solver available, using Eigen=3 instead of %d.", solver_);
147 #ifndef G2O_HAVE_CHOLMOD 150 UWARN(
"g2o is not built with chmold, so it cannot be used as solver. Using CSparse instead.");
155 #ifndef G2O_HAVE_CSPARSE 158 UWARN(
"g2o is not built with csparse, so it cannot be used as solver. Using PCG instead.");
166 std::map<int, Transform> OptimizerG2O::optimize(
168 const std::map<int, Transform> & poses,
169 const std::multimap<int, Link> & edgeConstraints,
170 cv::Mat & outputCovariance,
171 std::list<std::map<int, Transform> > * intermediateGraphes,
173 int * iterationsDone)
175 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
176 std::map<int, Transform> optimizedPoses;
178 UDEBUG(
"Optimizing graph...");
180 #ifndef RTABMAP_VERTIGO 183 UWARN(
"Vertigo robust optimization is not available! Robust optimization is now disabled.");
188 optimizedPoses.clear();
189 if(edgeConstraints.size()>=1 && poses.size()>=2 && iterations() > 0)
193 g2o::SparseOptimizer optimizer;
195 g2o::ParameterSE3Offset* odomOffset =
new g2o::ParameterSE3Offset();
196 odomOffset->setId(PARAM_OFFSET);
197 optimizer.addParameter(odomOffset);
199 #ifdef RTABMAP_G2O_CPP11 201 std::unique_ptr<SlamBlockSolver> blockSolver;
206 auto linearSolver = g2o::make_unique<SlamLinearEigenSolver>();
207 linearSolver->setBlockOrdering(
false);
208 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
210 #ifdef G2O_HAVE_CHOLMOD 211 else if(solver_ == 2)
214 auto linearSolver = g2o::make_unique<SlamLinearCholmodSolver>();
215 linearSolver->setBlockOrdering(
false);
216 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
219 #ifdef G2O_HAVE_CSPARSE 220 else if(solver_ == 0)
224 auto linearSolver = g2o::make_unique<SlamLinearCSparseSolver>();
225 linearSolver->setBlockOrdering(
false);
226 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
232 auto linearSolver = g2o::make_unique<SlamLinearPCGSolver>();
233 blockSolver = g2o::make_unique<SlamBlockSolver>(std::move(linearSolver));
239 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(std::move(blockSolver)));
243 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(std::move(blockSolver)));
248 SlamBlockSolver * blockSolver = 0;
253 SlamLinearEigenSolver * linearSolver =
new SlamLinearEigenSolver();
254 linearSolver->setBlockOrdering(
false);
255 blockSolver =
new SlamBlockSolver(linearSolver);
257 #ifdef G2O_HAVE_CHOLMOD 258 else if(solver_ == 2)
261 SlamLinearCholmodSolver * linearSolver =
new SlamLinearCholmodSolver();
262 linearSolver->setBlockOrdering(
false);
263 blockSolver =
new SlamBlockSolver(linearSolver);
266 #ifdef G2O_HAVE_CSPARSE 267 else if(solver_ == 0)
270 SlamLinearCSparseSolver* linearSolver =
new SlamLinearCSparseSolver();
271 linearSolver->setBlockOrdering(
false);
272 blockSolver =
new SlamBlockSolver(linearSolver);
278 SlamLinearPCGSolver * linearSolver =
new SlamLinearPCGSolver();
279 blockSolver =
new SlamBlockSolver(linearSolver);
284 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(blockSolver));
288 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(blockSolver));
294 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
296 if(iter->second.from() == iter->second.to())
304 UDEBUG(
"fill poses to g2o...");
305 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
307 UASSERT(!iter->second.isNull());
308 g2o::HyperGraph::Vertex * vertex = 0;
311 g2o::VertexSE2 * v2 =
new g2o::VertexSE2();
312 v2->setEstimate(g2o::SE2(iter->second.x(), iter->second.y(), iter->second.theta()));
313 if(iter->first == rootId)
321 g2o::VertexSE3 * v3 =
new g2o::VertexSE3();
323 Eigen::Affine3d a = iter->second.toEigen3d();
324 Eigen::Isometry3d pose;
326 pose.translation() = a.translation();
327 v3->setEstimate(pose);
328 if(iter->first == rootId)
334 vertex->setId(iter->first);
335 UASSERT_MSG(optimizer.addVertex(vertex),
uFormat(
"cannot insert vertex %d!?", iter->first).c_str());
338 UDEBUG(
"fill edges to g2o...");
339 #if defined(RTABMAP_VERTIGO) 340 int vertigoVertexId = poses.rbegin()->first+1;
342 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
344 int id1 = iter->second.from();
345 int id2 = iter->second.to();
347 UASSERT(!iter->second.transform().isNull());
349 g2o::HyperGraph::Edge * edge = 0;
357 g2o::EdgeSE2Prior * priorEdge =
new g2o::EdgeSE2Prior();
358 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
359 priorEdge->setVertex(0, v1);
360 priorEdge->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
361 priorEdge->setParameterId(0, PARAM_OFFSET);
362 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
363 if(!isCovarianceIgnored())
365 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
366 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
367 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
368 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
369 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
370 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
371 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
372 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
373 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
375 priorEdge->setInformation(information);
380 g2o::EdgeSE3Prior * priorEdge =
new g2o::EdgeSE3Prior();
381 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
382 priorEdge->setVertex(0, v1);
383 Eigen::Affine3d a = iter->second.transform().toEigen3d();
384 Eigen::Isometry3d pose;
386 pose.translation() = a.translation();
387 priorEdge->setMeasurement(pose);
388 priorEdge->setParameterId(0, PARAM_OFFSET);
389 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
390 if(!isCovarianceIgnored())
392 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
394 priorEdge->setInformation(information);
401 #if defined(RTABMAP_VERTIGO) 403 if(this->isRobust() &&
404 iter->second.type() != Link::kNeighbor &&
405 iter->second.type() != Link::kNeighborMerged)
416 v->setId(vertigoVertexId++);
417 UASSERT_MSG(optimizer.addVertex(v),
uFormat(
"cannot insert switchable vertex %d!?", v->id()).c_str());
425 prior->setMeasurement(1.0);
426 prior->setVertex(0, v);
427 UASSERT_MSG(optimizer.addEdge(prior),
uFormat(
"cannot insert switchable prior edge %d!?", v->id()).c_str());
433 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
434 if(!isCovarianceIgnored())
436 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
437 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
438 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
439 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
440 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
441 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
442 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
443 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
444 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
447 #if defined(RTABMAP_VERTIGO) 448 if(this->isRobust() &&
449 iter->second.type() != Link::kNeighbor &&
450 iter->second.type() != Link::kNeighborMerged)
453 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
454 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
460 e->
setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
461 e->setInformation(information);
467 g2o::EdgeSE2 *
e =
new g2o::EdgeSE2();
468 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
469 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
474 e->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
475 e->setInformation(information);
481 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
482 if(!isCovarianceIgnored())
484 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
487 Eigen::Affine3d a = iter->second.transform().toEigen3d();
488 Eigen::Isometry3d constraint;
489 constraint = a.linear();
490 constraint.translation() = a.translation();
492 #if defined(RTABMAP_VERTIGO) 493 if(this->isRobust() &&
494 iter->second.type() != Link::kNeighbor &&
495 iter->second.type() != Link::kNeighborMerged)
498 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
499 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
506 e->setInformation(information);
512 g2o::EdgeSE3 *
e =
new g2o::EdgeSE3();
513 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
514 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
519 e->setMeasurement(constraint);
520 e->setInformation(information);
526 if (edge && !optimizer.addEdge(edge))
529 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
535 UDEBUG(
"Initial optimization...");
536 optimizer.initializeOptimization();
538 UASSERT_MSG(optimizer.verifyInformationMatrices(
true),
539 "This error can be caused by (1) bad covariance matrix " 540 "set in odometry messages " 541 "(see requirements in g2o::OptimizableGraph::verifyInformationMatrices() function) " 542 "or that (2) PCL and g2o hadn't " 543 "been built both with or without \"-march=native\" compilation " 544 "flag (if one library is built with this flag and not the other, " 545 "this is causing Eigen to not work properly, resulting in segmentation faults).");
547 UINFO(
"g2o optimizing begin (max iterations=%d, robust=%d)", iterations(), isRobust()?1:0);
550 double lastError = 0.0;
551 if(intermediateGraphes || this->
epsilon() > 0.0)
553 for(
int i=0; i<iterations(); ++i)
555 if(intermediateGraphes)
559 std::map<int, Transform> tmpPoses;
562 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
564 const g2o::VertexSE2* v = (
const g2o::VertexSE2*)optimizer.vertex(iter->first);
568 iter->second.getEulerAngles(roll, pitch, yaw);
569 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(),
roll,
pitch, v->estimate().rotation().angle());
570 tmpPoses.insert(std::pair<int, Transform>(iter->first, t));
571 UASSERT_MSG(!t.isNull(),
uFormat(
"Optimized pose %d is null!?!?", iter->first).c_str());
575 UERROR(
"Vertex %d not found!?", iter->first);
581 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
583 const g2o::VertexSE3* v = (
const g2o::VertexSE3*)optimizer.vertex(iter->first);
586 Transform t = Transform::fromEigen3d(v->estimate());
587 tmpPoses.insert(std::pair<int, Transform>(iter->first, t));
592 UERROR(
"Vertex %d not found!?", iter->first);
596 intermediateGraphes->push_back(tmpPoses);
600 it += optimizer.optimize(1);
603 optimizer.computeActiveErrors();
604 double chi2 = optimizer.activeRobustChi2();
605 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f", i, (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
607 if(i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
609 UWARN(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
610 return optimizedPoses;
613 double errorDelta = lastError - chi2;
614 if(i>0 && errorDelta < this->
epsilon())
618 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
622 UINFO(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
626 else if(i==0 && chi2 < this->
epsilon())
628 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)", chi2, this->
epsilon());
636 it = optimizer.optimize(iterations());
637 optimizer.computeActiveErrors();
638 UDEBUG(
"%d nodes, %d edges, chi2: %f", (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), optimizer.activeRobustChi2());
642 *finalError = lastError;
646 *iterationsDone = it;
648 UINFO(
"g2o optimizing end (%d iterations done, error=%f, time = %f s)", it, optimizer.activeRobustChi2(), timer.
ticks());
650 if(optimizer.activeRobustChi2() > 1000000000000.0)
652 UWARN(
"g2o: Large optimimzation error detected (%f), aborting optimization!");
653 return optimizedPoses;
658 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
660 const g2o::VertexSE2* v = (
const g2o::VertexSE2*)optimizer.vertex(iter->first);
664 iter->second.getEulerAngles(roll, pitch, yaw);
665 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(),
roll,
pitch, v->estimate().rotation().angle());
666 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
667 UASSERT_MSG(!t.isNull(),
uFormat(
"Optimized pose %d is null!?!?", iter->first).c_str());
671 UERROR(
"Vertex %d not found!?", iter->first);
675 g2o::VertexSE2* v = (g2o::VertexSE2*)optimizer.vertex(poses.rbegin()->first);
679 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
680 optimizer.computeMarginals(spinv, v);
681 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)", t.
ticks(), spinv.cols(), spinv.rows(), v->hessianIndex(), poses.rbegin()->first);
682 if(v->hessianIndex() >= 0 && v->hessianIndex() < (int)spinv.blockCols().size())
684 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock * block = spinv.blockCols()[v->hessianIndex()].begin()->second;
685 UASSERT(block && block->cols() == 3 && block->cols() == 3);
686 outputCovariance.at<
double>(0,0) = (*block)(0,0);
687 outputCovariance.at<
double>(0,1) = (*block)(0,1);
688 outputCovariance.at<
double>(0,5) = (*block)(0,2);
689 outputCovariance.at<
double>(1,0) = (*block)(1,0);
690 outputCovariance.at<
double>(1,1) = (*block)(1,1);
691 outputCovariance.at<
double>(1,5) = (*block)(1,2);
692 outputCovariance.at<
double>(5,0) = (*block)(2,0);
693 outputCovariance.at<
double>(5,1) = (*block)(2,1);
694 outputCovariance.at<
double>(5,5) = (*block)(2,2);
696 else if(v->hessianIndex() < 0)
698 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex());
702 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());
707 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
712 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
714 const g2o::VertexSE3* v = (
const g2o::VertexSE3*)optimizer.vertex(iter->first);
717 Transform t = Transform::fromEigen3d(v->estimate());
718 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
723 UERROR(
"Vertex %d not found!?", iter->first);
727 g2o::VertexSE3* v = (g2o::VertexSE3*)optimizer.vertex(poses.rbegin()->first);
731 g2o::SparseBlockMatrix<g2o::MatrixXD> spinv;
732 optimizer.computeMarginals(spinv, v);
733 UINFO(
"Computed marginals = %fs (cols=%d rows=%d, v=%d id=%d)", t.
ticks(), spinv.cols(), spinv.rows(), v->hessianIndex(), poses.rbegin()->first);
734 if(v->hessianIndex() >= 0 && v->hessianIndex() < (int)spinv.blockCols().size())
736 g2o::SparseBlockMatrix<g2o::MatrixXD>::SparseMatrixBlock * block = spinv.blockCols()[v->hessianIndex()].begin()->second;
737 UASSERT(block && block->cols() == 6 && block->cols() == 6);
738 memcpy(outputCovariance.data, block->data(), outputCovariance.total()*
sizeof(double));
740 else if(v->hessianIndex() < 0)
742 UWARN(
"Computing marginals: vertex %d has negative hessian index (%d). Cannot compute last pose covariance.", poses.rbegin()->first, v->hessianIndex());
744 #ifdef RTABMAP_G2O_CPP11 747 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());
753 UERROR(
"Vertex %d not found!? Cannot compute marginals...", poses.rbegin()->first);
757 else if(poses.size() == 1 || iterations() <= 0)
759 optimizedPoses = poses;
763 UWARN(
"This method should be called at least with 1 pose!");
765 UDEBUG(
"Optimizing graph...end!");
767 #ifdef RTABMAP_ORB_SLAM2 768 UERROR(
"G2O graph optimization cannot be used with g2o built from ORB_SLAM2, only SBA is available.");
770 UERROR(
"Not built with G2O support!");
773 return optimizedPoses;
776 #ifdef RTABMAP_ORB_SLAM2 780 class EdgeSE3Expmap :
public g2o::BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>
783 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
784 EdgeSE3Expmap(): BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>(){}
785 bool read(std::istream& is)
790 bool write(std::ostream& os)
const 797 const g2o::VertexSE3Expmap* v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
798 const g2o::VertexSE3Expmap* v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
799 g2o::SE3Quat delta = _inverseMeasurement * (v1->estimate().inverse()*v2->estimate());
800 _error[0]=delta.translation().x();
801 _error[1]=delta.translation().y();
802 _error[2]=delta.translation().z();
803 _error[3]=delta.rotation().x();
804 _error[4]=delta.rotation().y();
805 _error[5]=delta.rotation().z();
808 virtual void setMeasurement(
const g2o::SE3Quat& meas){
810 _inverseMeasurement=meas.inverse();
813 virtual double initialEstimatePossible(
const g2o::OptimizableGraph::VertexSet& , g2o::OptimizableGraph::Vertex* ) {
return 1.;}
814 virtual void initialEstimate(
const g2o::OptimizableGraph::VertexSet& from_, g2o::OptimizableGraph::Vertex* ){
815 g2o::VertexSE3Expmap* from =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[0]);
816 g2o::VertexSE3Expmap* to =
static_cast<g2o::VertexSE3Expmap*
>(_vertices[1]);
817 if (from_.count(from) > 0)
818 to->setEstimate((g2o::SE3Quat) from->estimate() * _measurement);
820 from->setEstimate((g2o::SE3Quat) to->estimate() * _inverseMeasurement);
823 virtual bool setMeasurementData(
const double*
d){
824 Eigen::Map<const g2o::Vector7d> v(d);
825 _measurement.fromVector(v);
826 _inverseMeasurement = _measurement.inverse();
830 virtual bool getMeasurementData(
double* d)
const{
831 Eigen::Map<g2o::Vector7d> v(d);
832 v = _measurement.toVector();
836 virtual int measurementDimension()
const {
return 7;}
838 virtual bool setMeasurementFromState() {
839 const g2o::VertexSE3Expmap* v1 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[0]);
840 const g2o::VertexSE3Expmap* v2 =
dynamic_cast<const g2o::VertexSE3Expmap*
>(_vertices[1]);
841 _measurement = (v1->estimate().inverse()*v2->estimate());
842 _inverseMeasurement = _measurement.inverse();
847 g2o::SE3Quat _inverseMeasurement;
851 std::map<int, Transform> OptimizerG2O::optimizeBA(
853 const std::map<int, Transform> & poses,
854 const std::multimap<int, Link> & links,
855 const std::map<int, CameraModel> & models,
856 std::map<int, cv::Point3f> & points3DMap,
857 const std::map<
int, std::map<int, cv::Point3f> > & wordReferences,
858 std::set<int> * outliers)
860 std::map<int, Transform> optimizedPoses;
861 #if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM2) 862 UDEBUG(
"Optimizing graph...");
864 optimizedPoses.clear();
865 if(poses.size()>=2 && iterations() > 0 && models.size() == poses.size())
867 g2o::SparseOptimizer optimizer;
869 #if defined(RTABMAP_G2O_CPP11) and not defined(RTABMAP_ORB_SLAM2) 870 std::unique_ptr<g2o::BlockSolver_6_3::LinearSolverType> linearSolver;
872 g2o::BlockSolver_6_3::LinearSolverType * linearSolver = 0;
875 #ifdef RTABMAP_ORB_SLAM2 876 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
881 #ifdef RTABMAP_G2O_CPP11 882 linearSolver = g2o::make_unique<g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType> >();
884 linearSolver =
new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>();
887 #ifdef G2O_HAVE_CHOLMOD 888 else if(solver_ == 2)
891 #ifdef RTABMAP_G2O_CPP11 892 linearSolver = g2o::make_unique<g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType> >();
894 linearSolver =
new g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType>();
898 #ifdef G2O_HAVE_CSPARSE 899 else if(solver_ == 0)
902 #ifdef RTABMAP_G2O_CPP11 903 linearSolver = g2o::make_unique<g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType> >();
905 linearSolver =
new g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType>();
912 #ifdef RTABMAP_G2O_CPP11 913 linearSolver = g2o::make_unique<g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType> >();
915 linearSolver =
new g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType>();
918 #endif // RTABMAP_ORB_SLAM2 920 #ifndef RTABMAP_ORB_SLAM2 923 #ifdef RTABMAP_G2O_CPP11 924 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
925 g2o::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
927 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmGaussNewton(
new g2o::BlockSolver_6_3(linearSolver)));
933 #if defined(RTABMAP_G2O_CPP11) and not defined(RTABMAP_ORB_SLAM2) 934 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
935 g2o::make_unique<g2o::BlockSolver_6_3>(std::move(linearSolver))));
937 optimizer.setAlgorithm(
new g2o::OptimizationAlgorithmLevenberg(
new g2o::BlockSolver_6_3(linearSolver)));
942 UDEBUG(
"fill poses to g2o...");
943 for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); )
946 std::map<int, CameraModel>::const_iterator iterModel = models.find(iter->first);
947 UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
949 Transform camPose = iter->second * iterModel->second.localTransform();
953 #ifdef RTABMAP_ORB_SLAM2 954 g2o::VertexSE3Expmap * vCam =
new g2o::VertexSE3Expmap();
956 g2o::VertexCam * vCam =
new g2o::VertexCam();
959 Eigen::Affine3d a = camPose.toEigen3d();
960 #ifdef RTABMAP_ORB_SLAM2 962 vCam->setEstimate(g2o::SE3Quat(a.linear(), a.translation()));
964 g2o::SBACam
cam(Eigen::Quaterniond(a.linear()), a.translation());
966 iterModel->second.fx(),
967 iterModel->second.fy(),
968 iterModel->second.cx(),
969 iterModel->second.cy(),
970 iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_);
971 vCam->setEstimate(
cam);
973 vCam->setId(iter->first);
976 vCam->setFixed((rootId >= 0 && iter->first == rootId) || (rootId < 0 && iter->first != -rootId));
978 UDEBUG(
"cam %d (fixed=%d) fx=%f fy=%f cx=%f cy=%f Tx=%f baseline=%f t=%s",
981 iterModel->second.fx(),
982 iterModel->second.fy(),
983 iterModel->second.cx(),
984 iterModel->second.cy(),
985 iterModel->second.Tx(),
986 iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_,
987 camPose.prettyPrint().c_str());
989 UASSERT_MSG(optimizer.addVertex(vCam),
uFormat(
"cannot insert vertex %d!?", iter->first).c_str());
994 UDEBUG(
"fill edges to g2o...");
995 for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
997 if(
uContains(poses, iter->second.from()) &&
1001 int id1 = iter->second.from();
1002 int id2 = iter->second.to();
1006 UASSERT(!iter->second.transform().isNull());
1008 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
1009 if(!isCovarianceIgnored())
1011 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
1015 Transform camLink = models.at(id1).localTransform().
inverse()*iter->second.transform()*models.at(id2).localTransform();
1020 #ifdef RTABMAP_ORB_SLAM2 1021 EdgeSE3Expmap *
e =
new EdgeSE3Expmap();
1022 g2o::VertexSE3Expmap* v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id1);
1023 g2o::VertexSE3Expmap* v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id2);
1026 Transform camPose2Inv = Transform::fromEigen3d(v2->estimate());
1028 camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
1030 g2o::EdgeSBACam * e =
new g2o::EdgeSBACam();
1031 g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1);
1032 g2o::VertexCam* v2 = (g2o::VertexCam*)optimizer.vertex(id2);
1036 e->setVertex(0, v1);
1037 e->setVertex(1, v2);
1038 Eigen::Affine3d a = camLink.toEigen3d();
1039 e->setMeasurement(g2o::SE3Quat(a.linear(), a.translation()));
1040 e->setInformation(information);
1042 if (!optimizer.addEdge(e))
1045 UERROR(
"Map: Failed adding constraint between %d and %d, skipping", id1, id2);
1046 return optimizedPoses;
1052 UDEBUG(
"fill 3D points to g2o...");
1053 const int stepVertexId = poses.rbegin()->first+1;
1054 int negVertexOffset = stepVertexId;
1055 if(wordReferences.size() && wordReferences.rbegin()->first>0)
1057 negVertexOffset += wordReferences.rbegin()->first;
1059 UDEBUG(
"stepVertexId=%d, negVertexOffset=%d", stepVertexId, negVertexOffset);
1060 std::list<g2o::OptimizableGraph::Edge*> edges;
1061 for(std::map<
int, std::map<int, cv::Point3f> >::const_iterator iter = wordReferences.begin(); iter!=wordReferences.end(); ++iter)
1063 int id = iter->first;
1064 if(points3DMap.find(
id) != points3DMap.end())
1066 cv::Point3f pt3d = points3DMap.at(
id);
1067 g2o::VertexSBAPointXYZ* vpt3d =
new g2o::VertexSBAPointXYZ();
1069 vpt3d->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1072 vpt3d->setId(negVertexOffset +
id*-1);
1076 vpt3d->setId(stepVertexId +
id);
1079 vpt3d->setMarginalized(
true);
1080 optimizer.addVertex(vpt3d);
1085 for(std::map<int, cv::Point3f>::const_iterator jter=iter->second.begin(); jter!=iter->second.end(); ++jter)
1087 int camId = jter->first;
1088 if(poses.find(camId) != poses.end() && optimizer.vertex(camId) != 0)
1090 const cv::Point3f & pt = jter->second;
1091 double depth = pt.z;
1095 g2o::OptimizableGraph::Edge *
e;
1096 double baseline = 0.0;
1097 #ifdef RTABMAP_ORB_SLAM2 1098 g2o::VertexSE3Expmap* vcam =
dynamic_cast<g2o::VertexSE3Expmap*
>(optimizer.vertex(camId));
1099 std::map<int, CameraModel>::const_iterator iterModel = models.find(camId);
1101 UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
1102 baseline = iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_;
1104 g2o::VertexCam* vcam =
dynamic_cast<g2o::VertexCam*
>(optimizer.vertex(camId));
1105 baseline = vcam->estimate().baseline;
1107 double variance = pixelVariance_;
1108 if(
uIsFinite(depth) && depth > 0.0 && baseline > 0.0)
1111 #ifdef RTABMAP_ORB_SLAM2 1112 g2o::EdgeStereoSE3ProjectXYZ* es =
new g2o::EdgeStereoSE3ProjectXYZ();
1113 float disparity = baseline * iterModel->second.fx() / depth;
1114 Eigen::Vector3d obs( pt.x, pt.y, pt.x-disparity);
1115 es->setMeasurement(obs);
1117 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1118 es->fx = iterModel->second.fx();
1119 es->fy = iterModel->second.fy();
1120 es->cx = iterModel->second.cx();
1121 es->cy = iterModel->second.cy();
1122 es->bf = baseline*es->fx;
1125 g2o::EdgeProjectP2SC* es =
new g2o::EdgeProjectP2SC();
1126 float disparity = baseline * vcam->estimate().Kcam(0,0) / depth;
1127 Eigen::Vector3d obs( pt.x, pt.y, pt.x-disparity);
1128 es->setMeasurement(obs);
1130 es->setInformation(Eigen::Matrix3d::Identity() / variance);
1138 UDEBUG(
"Stereo camera model detected but current " 1139 "observation (pt=%d to cam=%d) has null depth (%f m), adding " 1140 "mono observation instead.",
1141 vpt3d->id()-stepVertexId, camId, depth);
1144 #ifdef RTABMAP_ORB_SLAM2 1145 g2o::EdgeSE3ProjectXYZ* em =
new g2o::EdgeSE3ProjectXYZ();
1146 Eigen::Vector2d obs( pt.x, pt.y);
1147 em->setMeasurement(obs);
1148 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1149 em->fx = iterModel->second.fx();
1150 em->fy = iterModel->second.fy();
1151 em->cx = iterModel->second.cx();
1152 em->cy = iterModel->second.cy();
1156 g2o::EdgeProjectP2MC* em =
new g2o::EdgeProjectP2MC();
1157 Eigen::Vector2d obs( pt.x, pt.y);
1158 em->setMeasurement(obs);
1159 em->setInformation(Eigen::Matrix2d::Identity() / variance);
1163 e->setVertex(0, vpt3d);
1164 e->setVertex(1, vcam);
1166 if(robustKernelDelta_ > 0.0)
1168 g2o::RobustKernelHuber* kernel =
new g2o::RobustKernelHuber;
1169 kernel->setDelta(robustKernelDelta_);
1170 e->setRobustKernel(kernel);
1173 optimizer.addEdge(e);
1180 UDEBUG(
"Initial optimization...");
1181 optimizer.initializeOptimization();
1183 UASSERT(optimizer.verifyInformationMatrices());
1185 UINFO(
"g2o optimizing begin (max iterations=%d, robustKernel=%f)", iterations(), robustKernelDelta_);
1189 int outliersCount = 0;
1190 int outliersCountFar = 0;
1192 for(
int i=0; i<(robustKernelDelta_>0.0?2:1); ++i)
1194 it += optimizer.optimize(i==0&&robustKernelDelta_>0.0?5:iterations());
1197 optimizer.computeActiveErrors();
1198 double chi2 = optimizer.activeRobustChi2();
1202 UERROR(
"Optimization generated NANs, aborting optimization! Try another g2o's optimizer (current=%d).", optimizer_);
1203 return optimizedPoses;
1205 UDEBUG(
"iteration %d: %d nodes, %d edges, chi2: %f", i, (
int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
1207 if(i>0 && (optimizer.activeRobustChi2() > 1000000000000.0 || !
uIsFinite(optimizer.activeRobustChi2())))
1209 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1210 return optimizedPoses;
1213 if(robustKernelDelta_>0.0)
1215 for(std::list<g2o::OptimizableGraph::Edge*>::iterator iter=edges.begin(); iter!=edges.end();++iter)
1217 if((*iter)->level() == 0 && (*iter)->chi2() > (*iter)->robustKernel()->delta())
1219 (*iter)->setLevel(1);
1222 #ifdef RTABMAP_ORB_SLAM2 1223 if(dynamic_cast<g2o::EdgeStereoSE3ProjectXYZ*>(*iter) != 0)
1225 d = ((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[0]-((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[2];
1227 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());
1229 if(dynamic_cast<g2o::EdgeProjectP2SC*>(*iter) != 0)
1231 d = ((g2o::EdgeProjectP2SC*)(*iter))->measurement()[0]-((g2o::EdgeProjectP2SC*)(*iter))->measurement()[2];
1233 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());
1237 if((*iter)->vertex(0)->id() > negVertexOffset)
1239 pt3d = points3DMap.at(negVertexOffset - (*iter)->vertex(0)->id());
1243 pt3d = points3DMap.at((*iter)->vertex(0)->id()-stepVertexId);
1245 ((g2o::VertexSBAPointXYZ*)(*iter)->vertex(0))->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
1249 outliers->insert((*iter)->vertex(0)->id()-stepVertexId);
1259 optimizer.initializeOptimization(0);
1260 UDEBUG(
"outliers=%d outliersCountFar=%d", outliersCount, outliersCountFar);
1263 UINFO(
"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());
1265 if(optimizer.activeRobustChi2() > 1000000000000.0)
1267 UWARN(
"g2o: Large optimization error detected (%f), aborting optimization!");
1268 return optimizedPoses;
1272 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1274 #ifdef RTABMAP_ORB_SLAM2 1275 const g2o::VertexSE3Expmap* v = (
const g2o::VertexSE3Expmap*)optimizer.vertex(iter->first);
1277 const g2o::VertexCam* v = (
const g2o::VertexCam*)optimizer.vertex(iter->first);
1281 Transform t = Transform::fromEigen3d(v->estimate());
1283 #ifdef RTABMAP_ORB_SLAM2 1288 t *= models.at(iter->first).localTransform().
inverse();
1290 UDEBUG(
"%d from=%s to=%s", iter->first, iter->second.prettyPrint().c_str(), t.prettyPrint().c_str());
1293 UERROR(
"Optimized pose %d is null!?!?", iter->first);
1294 optimizedPoses.clear();
1295 return optimizedPoses;
1299 if(this->isSlam2d())
1302 t = iter->second.inverse() * t;
1303 optimizedPoses.insert(std::pair<int, Transform>(iter->first, iter->second * t.to3DoF()));
1307 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
1312 UERROR(
"Vertex (pose) %d not found!?", iter->first);
1318 for(std::map<int, cv::Point3f>::iterator iter = points3DMap.begin(); iter!=points3DMap.end(); ++iter)
1320 const g2o::VertexSBAPointXYZ* v;
1321 int id = iter->first;
1324 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(negVertexOffset +
id*-1);
1328 v = (
const g2o::VertexSBAPointXYZ*)optimizer.vertex(stepVertexId +
id);
1333 cv::Point3f p(v->estimate()[0], v->estimate()[1], v->estimate()[2]);
1339 iter->second.x = iter->second.y = iter->second.z = std::numeric_limits<float>::quiet_NaN();
1343 else if(poses.size() > 1 && poses.size() != models.size())
1345 UERROR(
"This method should be called with size of poses = size camera models!");
1347 else if(poses.size() == 1 || iterations() <= 0)
1349 optimizedPoses = poses;
1353 UWARN(
"This method should be called at least with 1 pose!");
1355 UDEBUG(
"Optimizing graph...end!");
1357 UERROR(
"Not built with G2O support!");
1359 return optimizedPoses;
1362 bool OptimizerG2O::saveGraph(
1363 const std::string & fileName,
1364 const std::map<int, Transform> & poses,
1365 const std::multimap<int, Link> & edgeConstraints,
1366 bool useRobustConstraints)
1371 fopen_s(&file, fileName.c_str(),
"w");
1373 file = fopen(fileName.c_str(),
"w");
1379 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
1381 Eigen::Quaternionf q = iter->second.getQuaternionf();
1382 fprintf(file,
"VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
1394 int virtualVertexId = poses.size()?poses.rbegin()->first+1:0;
1395 for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
1397 std::string prefix =
"EDGE_SE3:QUAT";
1398 std::string suffix =
"";
1400 if(useRobustConstraints &&
1401 iter->second.type() != Link::kNeighbor &&
1402 iter->second.type() != Link::kNeighborMerged)
1404 prefix =
"EDGE_SE3_SWITCHABLE";
1405 fprintf(file,
"VERTEX_SWITCH %d 1\n", virtualVertexId);
1406 fprintf(file,
"EDGE_SWITCH_PRIOR %d 1 1.0\n", virtualVertexId);
1407 suffix =
uFormat(
" %d", virtualVertexId++);
1410 Eigen::Quaternionf q = iter->second.transform().getQuaternionf();
1411 fprintf(file,
"%s %d %d%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",
1413 iter->second.from(),
1416 iter->second.transform().x(),
1417 iter->second.transform().y(),
1418 iter->second.transform().z(),
1423 iter->second.infMatrix().at<
double>(0,0),
1424 iter->second.infMatrix().at<
double>(0,1),
1425 iter->second.infMatrix().at<
double>(0,2),
1426 iter->second.infMatrix().at<
double>(0,3),
1427 iter->second.infMatrix().at<
double>(0,4),
1428 iter->second.infMatrix().at<
double>(0,5),
1429 iter->second.infMatrix().at<
double>(1,1),
1430 iter->second.infMatrix().at<
double>(1,2),
1431 iter->second.infMatrix().at<
double>(1,3),
1432 iter->second.infMatrix().at<
double>(1,4),
1433 iter->second.infMatrix().at<
double>(1,5),
1434 iter->second.infMatrix().at<
double>(2,2),
1435 iter->second.infMatrix().at<
double>(2,3),
1436 iter->second.infMatrix().at<
double>(2,4),
1437 iter->second.infMatrix().at<
double>(2,5),
1438 iter->second.infMatrix().at<
double>(3,3),
1439 iter->second.infMatrix().at<
double>(3,4),
1440 iter->second.infMatrix().at<
double>(3,5),
1441 iter->second.infMatrix().at<
double>(4,4),
1442 iter->second.infMatrix().at<
double>(4,5),
1443 iter->second.infMatrix().at<
double>(5,5));
1445 UINFO(
"Graph saved to %s", fileName.c_str());
1450 UERROR(
"Cannot save to file %s", fileName.c_str());
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
rtabmap::CameraThread * cam
GLM_FUNC_DECL genType e()
std::map< std::string, std::string > ParametersMap
Basic mathematics functions.
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)
static ULogger::Level level()
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)
std::string UTILITE_EXP uFormat(const char *fmt,...)
GLM_FUNC_DECL matType< T, P > inverse(matType< T, P > const &m)