42 #include <gtsam/inference/Symbol.h>
43 #include <gtsam/slam/PriorFactor.h>
45 #include <gtsam/sam/BearingFactor.h>
46 #include <gtsam/sam/BearingRangeFactor.h>
59 #ifdef RTABMAP_VERTIGO
63 #endif // end RTABMAP_GTSAM
69 internalOptimizerType_(
Parameters::defaultGTSAMOptimizer()),
71 lastSwitchId_(1000000000)
100 double threshold = Parameters::defaultGTSAMIncRelinearizeThreshold();
101 int skip = Parameters::defaultGTSAMIncRelinearizeSkip();
103 Parameters::parse(parameters, Parameters::kGTSAMIncRelinearizeThreshold(), threshold);
107 UDEBUG(
"GTSAM %s=%f", Parameters::kGTSAMIncRelinearizeThreshold().
c_str(), threshold);
108 UDEBUG(
"GTSAM %s=%d", Parameters::kGTSAMIncRelinearizeSkip().
c_str(), skip);
121 params.relinearizeThreshold = threshold;
122 params.relinearizeSkip = skip;
123 params.evaluateNonlinearError =
true;
141 const std::map<int, Transform> & poses,
142 const std::multimap<int, Link> & edgeConstraints,
143 cv::Mat & outputCovariance,
144 std::list<std::map<int, Transform> > * intermediateGraphes,
146 int * iterationsDone)
148 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
149 std::map<int, Transform> optimizedPoses;
152 #ifndef RTABMAP_VERTIGO
155 UWARN(
"Vertigo robust optimization is not available! Robust optimization is now disabled.");
160 UDEBUG(
"Optimizing graph...");
161 if(edgeConstraints.size()>=1 && poses.size()>=2 &&
iterations() > 0)
166 bool hasGPSPrior =
false;
167 bool hasGravityConstraints =
false;
170 for(std::multimap<int, Link>::const_iterator
iter=edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
172 if(
iter->second.from() ==
iter->second.to())
177 if ((
isSlam2d() && 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) < 9999) ||
178 (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) < 9999.0 &&
179 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) < 9999.0 &&
180 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) < 9999.0))
190 hasGravityConstraints =
true;
200 std::vector<ConstraintToFactor> addedPrior;
206 UDEBUG(
"Setting prior for rootId=%d", rootId);
208 const Transform & initialPose = poses.at(rootId);
209 UDEBUG(
"hasGPSPrior=%s", hasGPSPrior?
"true":
"false");
221 (hasGPSPrior?2:1
e-2), hasGPSPrior?2:1
e-2, hasGPSPrior?2:1
e-2
235 UDEBUG(
"isam2: reset iSAM2, disjoint mapping sessions between previous root %d and new root %d",
lastRootFactorIndex_.first, rootId);
249 std::map<int, Transform> newPoses;
250 std::multimap<int, Link> newEdgeConstraints;
254 UDEBUG(
"Add new poses...");
256 for(std::map<int, Transform>::const_iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
260 newPoses.insert(*
iter);
261 UDEBUG(
"Adding pose %d to factor graph",
iter->first);
264 UDEBUG(
"Add new links...");
266 for(std::multimap<int, Link>::const_iterator
iter=edgeConstraints.begin();
iter!=edgeConstraints.end(); ++
iter)
271 newEdgeConstraints.insert(*
iter);
272 UDEBUG(
"Adding constraint %d (%d->%d) to factor graph",
iter->first,
iter->second.from(),
iter->second.to());
278 UDEBUG(
"Remove links...");
287 UDEBUG(
"Removing constraint %d->%d (factor indice=%ld)",
294 else if(poses.rbegin()->first >= 1000000000)
296 UERROR(
"Lastest pose id (%d) is too huge for robust optimization (over switch offset of 1000000000)", poses.rbegin()->first);
297 return optimizedPoses;
305 newEdgeConstraints = edgeConstraints;
308 UDEBUG(
"fill poses to gtsam... rootId=%d (priorsIgnored=%d landmarksIgnored=%d)",
311 std::map<int, bool> isLandmarkWithRotation;
312 for(std::map<int, Transform>::const_iterator
iter = newPoses.begin();
iter!=newPoses.end(); ++
iter)
325 std::multimap<int, Link>::const_iterator jter=newEdgeConstraints.find(
iter->first);
326 UASSERT_MSG(jter != newEdgeConstraints.end(),
uFormat(
"Not found landmark %d in edges!",
iter->first).c_str());
328 if (1 /
static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
331 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
false));
336 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
true));
352 std::multimap<int, Link>::const_iterator jter=newEdgeConstraints.find(
iter->first);
353 UASSERT_MSG(jter != newEdgeConstraints.end(),
uFormat(
"Not found landmark %d in edges!",
iter->first).c_str());
355 if (1 /
static_cast<double>(jter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
356 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
357 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
360 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
false));
365 isLandmarkWithRotation.insert(std::make_pair(
iter->first,
true));
372 UDEBUG(
"fill edges to gtsam...");
377 for(std::multimap<int, Link>::const_iterator
iter=newEdgeConstraints.begin();
iter!=newEdgeConstraints.end(); ++
iter)
379 int id1 =
iter->second.from();
380 int id2 =
iter->second.to();
393 if(id1 < 0 && !isLandmarkWithRotation.at(id1))
395 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Variances(
Vector2(
396 1/
iter->second.infMatrix().at<
double>(0,0),
397 1/
iter->second.infMatrix().at<
double>(1,1)));
401 else if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
403 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Variances(
Vector2(
404 1/
iter->second.infMatrix().at<
double>(0,0),
405 1/
iter->second.infMatrix().at<
double>(1,1)));
414 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
415 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
416 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
417 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
418 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
419 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
420 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
421 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
422 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
432 if(id1 < 0 && !isLandmarkWithRotation.at(id1))
434 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Precisions(
Vector3(
435 iter->second.infMatrix().at<
double>(0,0),
436 iter->second.infMatrix().at<
double>(1,1),
437 iter->second.infMatrix().at<
double>(2,2)));
441 else if (1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
442 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
443 1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
445 noiseModel::Diagonal::shared_ptr
model = noiseModel::Diagonal::Precisions(
Vector3(
446 iter->second.infMatrix().at<
double>(0,0),
447 iter->second.infMatrix().at<
double>(1,1),
448 iter->second.infMatrix().at<
double>(2,2)));
457 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
461 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
462 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
463 mgtsam.block(0,3,3,3) = information.block(3,0,3,3);
464 mgtsam.block(3,0,3,3) = information.block(0,3,3,3);
481 else if(id1<0 || id2 < 0)
486 UASSERT((id1 < 0 && id2 > 0) || (id1 > 0 && id2 < 0));
490 t =
iter->second.transform();
494 t =
iter->second.transform().inverse();
498 #ifdef RTABMAP_VERTIGO
499 if(this->
isRobust() && isLandmarkWithRotation.at(id2))
517 else if(this->
isRobust() && !isLandmarkWithRotation.at(id2))
519 UWARN(
"%s cannot be used for landmark constraints without orientation.", Parameters::kOptimizerRobust().
c_str());
525 if(isLandmarkWithRotation.at(id2))
530 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
531 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
532 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
533 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
534 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
535 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
536 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
537 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
538 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
542 #ifdef RTABMAP_VERTIGO
555 else if(1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(1,1)) < 9999)
560 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,2), cv::Range(0,2)).clone();;
561 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
575 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,1), cv::Range(0,1)).clone();;
576 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
588 if(isLandmarkWithRotation.at(id2))
593 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
597 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
598 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
599 mgtsam.block(0,3,3,3) = information.block(3,0,3,3);
600 mgtsam.block(3,0,3,3) = information.block(0,3,3,3);
603 #ifdef RTABMAP_VERTIGO
618 else if(1 /
static_cast<double>(
iter->second.infMatrix().at<
double>(2,2)) < 9999)
623 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,3), cv::Range(0,3)).clone();
624 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
639 cv::Mat linearCov = cv::Mat(
iter->second.infMatrix(), cv::Range(0,2), cv::Range(0,2)).clone();
640 memcpy(information.
data(), linearCov.data, linearCov.total()*
sizeof(
double));
655 #ifdef RTABMAP_VERTIGO
682 information(0,0) =
iter->second.infMatrix().at<
double>(0,0);
683 information(0,1) =
iter->second.infMatrix().at<
double>(0,1);
684 information(0,2) =
iter->second.infMatrix().at<
double>(0,5);
685 information(1,0) =
iter->second.infMatrix().at<
double>(1,0);
686 information(1,1) =
iter->second.infMatrix().at<
double>(1,1);
687 information(1,2) =
iter->second.infMatrix().at<
double>(1,5);
688 information(2,0) =
iter->second.infMatrix().at<
double>(5,0);
689 information(2,1) =
iter->second.infMatrix().at<
double>(5,1);
690 information(2,2) =
iter->second.infMatrix().at<
double>(5,5);
694 #ifdef RTABMAP_VERTIGO
714 memcpy(information.
data(),
iter->second.infMatrix().data,
iter->second.infMatrix().total()*
sizeof(
double));
718 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
719 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
720 mgtsam.block(0,3,3,3) = information.block(3,0,3,3);
721 mgtsam.block(3,0,3,3) = information.block(0,3,3,3);
724 #ifdef RTABMAP_VERTIGO
742 UDEBUG(
"create optimizer");
747 UDEBUG(
"Batch optimization...");
772 UDEBUG(
"iSAM2 optimization...");
778 double initialError = optimizer?
graph.error(initialEstimate):0;
779 double lastError = optimizer?optimizer->
error():0;
782 if(intermediateGraphes &&
i > 0)
785 std::map<int, Transform> tmpPoses;
787 #if GTSAM_VERSION_NUMERIC >= 40200
801 tmpPoses.insert(std::make_pair(
key,
Transform(
p.x(),
p.y(),
p.theta())));
803 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
805 if(isLandmarkWithRotation.at(
key))
826 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
828 if(isLandmarkWithRotation.at(
key))
843 intermediateGraphes->push_back(tmpPoses);
857 UDEBUG(
"Update iSAM with the new factors");
859 #if BOOST_VERSION >= 106800
866 UDEBUG(
"error before = %f after=%f",
result.errorBefore.value(),
result.errorAfter.value());
867 initialError = lastError =
result.errorBefore.value();
873 for(
size_t j=0;
j<
result.newFactorsIndices.size(); ++
j)
875 UDEBUG(
"New factor indice: %ld",
result.newFactorsIndices[
j]);
889 #if BOOST_VERSION >= 106800
896 UDEBUG(
"error before = %f after=%f",
result.errorBefore.value(),
result.errorAfter.value());
898 lastError =
result.errorBefore.value();
905 UWARN(
"GTSAM exception caught: %s\n Graph has %d edges and %d vertices",
e.what(),
906 (
int)newEdgeConstraints.size(),
907 (
int)newPoses.size());
921 return optimizedPoses;
926 double errorDelta = lastError -
error;
931 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
935 UDEBUG(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
939 else if(
i==0 && error < this->
epsilon())
941 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)",
error, this->
epsilon());
948 *finalError = lastError;
952 *iterationsDone = it;
954 UDEBUG(
"GTSAM optimizing end (%d iterations done (error initial=%f final=%f), time=%f s)",
955 it, initialError, lastError,
timer.ticks());
959 #if GTSAM_VERSION_NUMERIC >= 40200
976 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
978 if(isLandmarkWithRotation.at(
key))
999 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(
key)!=isLandmarkWithRotation.end())
1001 if(isLandmarkWithRotation.at(
key))
1019 UDEBUG(
"Computing marginals for node %d...", poses.rbegin()->first);
1031 UDEBUG(
"Computed marginals = %fs (key=%d)",
t.ticks(), poses.rbegin()->first);
1034 outputCovariance.at<
double>(0,0) =
info(0,0);
1035 outputCovariance.at<
double>(0,1) =
info(0,1);
1036 outputCovariance.at<
double>(0,5) =
info(0,2);
1037 outputCovariance.at<
double>(1,0) =
info(1,0);
1038 outputCovariance.at<
double>(1,1) =
info(1,1);
1039 outputCovariance.at<
double>(1,5) =
info(1,2);
1040 outputCovariance.at<
double>(5,0) =
info(2,0);
1041 outputCovariance.at<
double>(5,1) =
info(2,1);
1042 outputCovariance.at<
double>(5,5) =
info(2,2);
1047 mgtsam.block(3,3,3,3) =
info.block(0,0,3,3);
1048 mgtsam.block(0,0,3,3) =
info.block(3,3,3,3);
1049 mgtsam.block(0,3,3,3) =
info.block(3,0,3,3);
1050 mgtsam.block(3,0,3,3) =
info.block(0,3,3,3);
1051 memcpy(outputCovariance.data, mgtsam.
data(), outputCovariance.total()*
sizeof(
double));
1055 UERROR(
"GTSAM: Could not compute marginal covariance!");
1056 optimizedPoses.clear();
1061 UERROR(
"GTSAM exception caught: %s",
e.what());
1062 optimizedPoses.clear();
1064 catch(std::exception&
e)
1066 UERROR(
"GTSAM exception caught: %s",
e.what());
1067 optimizedPoses.clear();
1072 else if(poses.size() == 1 ||
iterations() <= 0)
1074 optimizedPoses = poses;
1078 UWARN(
"This method should be called at least with 1 pose!");
1080 UDEBUG(
"Optimizing graph...end!");
1082 UERROR(
"Not built with GTSAM support!");
1084 return optimizedPoses;