42 #include <gtsam/inference/Symbol.h>
43 #include <gtsam/slam/PriorFactor.h>
45 #include <gtsam/sam/BearingRangeFactor.h>
58 #ifdef RTABMAP_VERTIGO
62 #endif // end RTABMAP_GTSAM
68 internalOptimizerType_(
Parameters::defaultGTSAMOptimizer()),
70 lastSwitchId_(1000000000)
99 double threshold = Parameters::defaultGTSAMIncRelinearizeThreshold();
100 int skip = Parameters::defaultGTSAMIncRelinearizeSkip();
102 Parameters::parse(parameters, Parameters::kGTSAMIncRelinearizeThreshold(), threshold);
106 UDEBUG(
"GTSAM %s=%f", Parameters::kGTSAMIncRelinearizeThreshold().
c_str(), threshold);
107 UDEBUG(
"GTSAM %s=%d", Parameters::kGTSAMIncRelinearizeSkip().
c_str(), skip);
120 params.relinearizeThreshold = threshold;
121 params.relinearizeSkip = skip;
122 params.evaluateNonlinearError =
true;
140 const std::map<int, Transform> & poses,
141 const std::multimap<int, Link> & edgeConstraints,
142 cv::Mat & outputCovariance,
143 std::list<std::map<int, Transform> > * intermediateGraphes,
145 int * iterationsDone)
147 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
148 std::map<int, Transform> optimizedPoses;
151 #ifndef RTABMAP_VERTIGO
154 UWARN(
"Vertigo robust optimization is not available! Robust optimization is now disabled.");
159 UDEBUG(
"Optimizing graph...");
160 if(edgeConstraints.size()>=1 && poses.size()>=2 &&
iterations() > 0)
165 bool hasGPSPrior =
false;
166 bool hasGravityConstraints =
false;
169 for(std::multimap<int, Link>::const_iterator
iter=edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
171 if(
iter->second.from() ==
iter->second.to())
176 if ((
isSlam2d() && 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) < 9999) ||
177 (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) < 9999.0 &&
178 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) < 9999.0 &&
179 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) < 9999.0))
189 hasGravityConstraints =
true;
199 std::vector<ConstraintToFactor> addedPrior;
205 UDEBUG(
"Setting prior for rootId=%d", rootId);
207 const Transform & initialPose = poses.at(rootId);
208 UDEBUG(
"hasGPSPrior=%s", hasGPSPrior?
"true":
"false");
220 (hasGPSPrior?2:1
e-2), hasGPSPrior?2:1
e-2, hasGPSPrior?2:1
e-2
234 UDEBUG(
"isam2: reset iSAM2, disjoint mapping sessions between previous root %d and new root %d",
lastRootFactorIndex_.first, rootId);
248 std::map<int, Transform> newPoses;
249 std::multimap<int, Link> newEdgeConstraints;
253 UDEBUG(
"Add new poses...");
255 for(std::map<int, Transform>::const_iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
259 newPoses.insert(*
iter);
260 UDEBUG(
"Adding pose %d to factor graph",
iter->first);
263 UDEBUG(
"Add new links...");
265 for(std::multimap<int, Link>::const_iterator
iter=edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
270 newEdgeConstraints.insert(*
iter);
271 UDEBUG(
"Adding constraint %d (%d->%d) to factor graph",
iter->first,
iter->second.from(),
iter->second.to());
277 UDEBUG(
"Remove links...");
286 UDEBUG(
"Removing constraint %d->%d (factor indice=%ld)",
293 else if(poses.rbegin()->first >= 1000000000)
295 UERROR(
"Lastest pose id (%d) is too huge for robust optimization (over switch offset of 1000000000)", poses.rbegin()->first);
296 return optimizedPoses;
304 newEdgeConstraints = edgeConstraints;
307 UDEBUG(
"fill poses to gtsam... rootId=%d (priorsIgnored=%d landmarksIgnored=%d)",
310 std::map<int, bool> isLandmarkWithRotation;
311 for(std::map<int, Transform>::const_iterator
iter = newPoses.begin();
iter!=newPoses.end(); ++
iter)
324 std::multimap<int, Link>::const_iterator jter=newEdgeConstraints.find(
iter->first);
325 UASSERT_MSG(jter != newEdgeConstraints.end(),
uFormat(
"Not found landmark %d in edges!",
iter->first).c_str());
327 if (1 /
static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
330 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
false));
335 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
true));
351 std::multimap<int, Link>::const_iterator jter=newEdgeConstraints.find(
iter->first);
352 UASSERT_MSG(jter != newEdgeConstraints.end(),
uFormat(
"Not found landmark %d in edges!",
iter->first).c_str());
354 if (1 /
static_cast<double>(jter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
355 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
356 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
359 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
false));
364 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
true));
371 UDEBUG(
"fill edges to gtsam...");
376 for(std::multimap<int, Link>::const_iterator
iter=newEdgeConstraints.begin();
iter!=newEdgeConstraints.end(); ++
iter)
378 int id1 =
iter->second.from();
379 int id2 =
iter->second.to();
392 if(id1 < 0 && !isLandmarkWithRotation.at(id1))
394 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Variances(
Vector2(
395 1/
iter->second.infMatrix().at<
double>(0,0),
396 1/
iter->second.infMatrix().at<
double>(1,1)));
400 else if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
402 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Variances(
Vector2(
403 1/
iter->second.infMatrix().at<
double>(0,0),
404 1/
iter->second.infMatrix().at<
double>(1,1)));
413 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
414 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
415 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
416 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
417 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
418 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
419 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
420 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
421 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
431 if(id1 < 0 && !isLandmarkWithRotation.at(id1))
433 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Precisions(
Vector3(
434 iter->second.infMatrix().at<
double>(0,0),
435 iter->second.infMatrix().at<
double>(1,1),
436 iter->second.infMatrix().at<
double>(2,2)));
440 else if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
441 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
442 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
444 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Precisions(
Vector3(
445 iter->second.infMatrix().at<
double>(0,0),
446 iter->second.infMatrix().at<
double>(1,1),
447 iter->second.infMatrix().at<
double>(2,2)));
456 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
460 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
461 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
462 mgtsam.block(0,3,3,3) = information.block(3,0,3,3);
463 mgtsam.block(3,0,3,3) = information.block(0,3,3,3);
480 else if(id1<0 || id2 < 0)
485 UASSERT((id1 < 0 && id2 > 0) || (id1 > 0 && id2 < 0));
489 t =
iter->second.transform();
493 t =
iter->second.transform().inverse();
497 #ifdef RTABMAP_VERTIGO
498 if(this->
isRobust() && isLandmarkWithRotation.at(id2))
516 else if(this->
isRobust() && !isLandmarkWithRotation.at(id2))
518 UWARN(
"%s cannot be used for landmark constraints without orientation.", Parameters::kOptimizerRobust().
c_str());
524 if(isLandmarkWithRotation.at(id2))
529 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
530 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
531 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
532 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
533 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
534 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
535 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
536 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
537 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
541 #ifdef RTABMAP_VERTIGO
559 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,2), cv::Range(0,2)).clone();;
560 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
572 if(isLandmarkWithRotation.at(id2))
577 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
581 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
582 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
583 mgtsam.block(0,3,3,3) = information.block(3,0,3,3);
584 mgtsam.block(3,0,3,3) = information.block(0,3,3,3);
587 #ifdef RTABMAP_VERTIGO
607 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,3), cv::Range(0,3)).clone();;
608 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
622 #ifdef RTABMAP_VERTIGO
649 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
650 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
651 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
652 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
653 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
654 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
655 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
656 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
657 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
661 #ifdef RTABMAP_VERTIGO
681 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
685 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
686 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
687 mgtsam.block(0,3,3,3) = information.block(3,0,3,3);
688 mgtsam.block(3,0,3,3) = information.block(0,3,3,3);
691 #ifdef RTABMAP_VERTIGO
709 UDEBUG(
"create optimizer");
714 UDEBUG(
"Batch optimization...");
739 UDEBUG(
"iSAM2 optimization...");
745 double initialError = optimizer?
graph.error(initialEstimate):0;
746 double lastError = optimizer?optimizer->
error():0;
749 if(intermediateGraphes &&
i > 0)
752 std::map<int, Transform> tmpPoses;
754 #if GTSAM_VERSION_NUMERIC >= 40200
768 tmpPoses.insert(std::make_pair(
key,
Transform(
p.x(),
p.y(),
p.theta())));
770 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
772 if(isLandmarkWithRotation.at(
key))
793 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
795 if(isLandmarkWithRotation.at(
key))
810 intermediateGraphes->push_back(tmpPoses);
824 UDEBUG(
"Update iSAM with the new factors");
826 #if BOOST_VERSION >= 106800
833 UDEBUG(
"error before = %f after=%f",
result.errorBefore.value(),
result.errorAfter.value());
834 initialError = lastError =
result.errorBefore.value();
840 for(
size_t j=0;
j<
result.newFactorsIndices.size(); ++
j)
842 UDEBUG(
"New factor indice: %ld",
result.newFactorsIndices[
j]);
856 #if BOOST_VERSION >= 106800
863 UDEBUG(
"error before = %f after=%f",
result.errorBefore.value(),
result.errorAfter.value());
865 lastError =
result.errorBefore.value();
872 UWARN(
"GTSAM exception caught: %s\n Graph has %d edges and %d vertices",
e.what(),
873 (
int)newEdgeConstraints.size(),
874 (
int)newPoses.size());
888 return optimizedPoses;
893 double errorDelta = lastError -
error;
898 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
902 UDEBUG(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
906 else if(
i==0 && error < this->
epsilon())
908 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)",
error, this->
epsilon());
915 *finalError = lastError;
919 *iterationsDone = it;
921 UDEBUG(
"GTSAM optimizing end (%d iterations done (error initial=%f final=%f), time=%f s)",
922 it, initialError, lastError,
timer.ticks());
926 #if GTSAM_VERSION_NUMERIC >= 40200
943 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
945 if(isLandmarkWithRotation.at(
key))
966 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
968 if(isLandmarkWithRotation.at(
key))
986 UDEBUG(
"Computing marginals for node %d...", poses.rbegin()->first);
998 UDEBUG(
"Computed marginals = %fs (key=%d)",
t.ticks(), poses.rbegin()->first);
1001 outputCovariance.at<
double>(0,0) =
info(0,0);
1002 outputCovariance.at<
double>(0,1) =
info(0,1);
1003 outputCovariance.at<
double>(0,5) =
info(0,2);
1004 outputCovariance.at<
double>(1,0) =
info(1,0);
1005 outputCovariance.at<
double>(1,1) =
info(1,1);
1006 outputCovariance.at<
double>(1,5) =
info(1,2);
1007 outputCovariance.at<
double>(5,0) =
info(2,0);
1008 outputCovariance.at<
double>(5,1) =
info(2,1);
1009 outputCovariance.at<
double>(5,5) =
info(2,2);
1014 mgtsam.block(3,3,3,3) =
info.block(0,0,3,3);
1015 mgtsam.block(0,0,3,3) =
info.block(3,3,3,3);
1016 mgtsam.block(0,3,3,3) =
info.block(3,0,3,3);
1017 mgtsam.block(3,0,3,3) =
info.block(0,3,3,3);
1018 memcpy(outputCovariance.data, mgtsam.
data(), outputCovariance.total()*
sizeof(
double));
1022 UERROR(
"GTSAM: Could not compute marginal covariance!");
1023 optimizedPoses.clear();
1028 UERROR(
"GTSAM exception caught: %s",
e.what());
1029 optimizedPoses.clear();
1031 catch(std::exception&
e)
1033 UERROR(
"GTSAM exception caught: %s",
e.what());
1034 optimizedPoses.clear();
1039 else if(poses.size() == 1 ||
iterations() <= 0)
1041 optimizedPoses = poses;
1045 UWARN(
"This method should be called at least with 1 pose!");
1047 UDEBUG(
"Optimizing graph...end!");
1049 UERROR(
"Not built with GTSAM support!");
1051 return optimizedPoses;