mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-03 01:50:24 +08:00
iSAM2 integration (#1249)
* isam2 integration * fonctional iSAM2, added related parameters * updated default params after testing large-scale enviroment * proximity search optimization * boost<1.68 fix * boost version * Fixed map not showing in graphview * iSAM2: supporting MM/localization/multi-session modes
This commit is contained in:
@@ -436,6 +436,9 @@ class RTABMAP_CORE_EXPORT Parameters
|
|||||||
RTABMAP_PARAM(g2o, Baseline, double, 0.075, "When doing bundle adjustment with RGB-D data, we can set a fake baseline (m) to do stereo bundle adjustment (if 0, mono bundle adjustment is done). For stereo data, the baseline in the calibration is used directly.");
|
RTABMAP_PARAM(g2o, Baseline, double, 0.075, "When doing bundle adjustment with RGB-D data, we can set a fake baseline (m) to do stereo bundle adjustment (if 0, mono bundle adjustment is done). For stereo data, the baseline in the calibration is used directly.");
|
||||||
|
|
||||||
RTABMAP_PARAM(GTSAM, Optimizer, int, 1, "0=Levenberg 1=GaussNewton 2=Dogleg");
|
RTABMAP_PARAM(GTSAM, Optimizer, int, 1, "0=Levenberg 1=GaussNewton 2=Dogleg");
|
||||||
|
RTABMAP_PARAM(GTSAM, Incremental, bool, false, uFormat("Do graph optimization incrementally (iSAM2) to increase optimization speed on loop closures. Note that only GaussNewton and Dogleg optimization algorithms are supported (%s) in this mode.", kGTSAMOptimizer().c_str()));
|
||||||
|
RTABMAP_PARAM(GTSAM, IncRelinearizeThreshold, double, 0.01, "Only relinearize variables whose linear delta magnitude is greater than this threshold. See GTSAM::ISAM2 doc for more info.");
|
||||||
|
RTABMAP_PARAM(GTSAM, IncRelinearizeSkip, int, 1, "Only relinearize any variables every X calls to ISAM2::update(). See GTSAM::ISAM2 doc for more info.");
|
||||||
|
|
||||||
// Odometry
|
// Odometry
|
||||||
RTABMAP_PARAM(Odom, Strategy, int, 0, "0=Frame-to-Map (F2M) 1=Frame-to-Frame (F2F) 2=Fovis 3=viso2 4=DVO-SLAM 5=ORB_SLAM2 6=OKVIS 7=LOAM 8=MSCKF_VIO 9=VINS-Fusion 10=OpenVINS 11=FLOAM 12=Open3D");
|
RTABMAP_PARAM(Odom, Strategy, int, 0, "0=Frame-to-Map (F2M) 1=Frame-to-Frame (F2F) 2=Fovis 3=viso2 4=DVO-SLAM 5=ORB_SLAM2 6=OKVIS 7=LOAM 8=MSCKF_VIO 9=VINS-Fusion 10=OpenVINS 11=FLOAM 12=Open3D");
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ class RTABMAP_CORE_EXPORT Statistics
|
|||||||
RTABMAP_STATS(Timing, Memory_update, ms);
|
RTABMAP_STATS(Timing, Memory_update, ms);
|
||||||
RTABMAP_STATS(Timing, Neighbor_link_refining, ms);
|
RTABMAP_STATS(Timing, Neighbor_link_refining, ms);
|
||||||
RTABMAP_STATS(Timing, Proximity_by_time, ms);
|
RTABMAP_STATS(Timing, Proximity_by_time, ms);
|
||||||
|
RTABMAP_STATS(Timing, Proximity_by_space_search, ms);
|
||||||
RTABMAP_STATS(Timing, Proximity_by_space_visual, ms);
|
RTABMAP_STATS(Timing, Proximity_by_space_visual, ms);
|
||||||
RTABMAP_STATS(Timing, Proximity_by_space, ms);
|
RTABMAP_STATS(Timing, Proximity_by_space, ms);
|
||||||
RTABMAP_STATS(Timing, Cleaning_neighbors, ms);
|
RTABMAP_STATS(Timing, Cleaning_neighbors, ms);
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
#include <rtabmap/core/Optimizer.h>
|
#include <rtabmap/core/Optimizer.h>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
class ISAM2;
|
||||||
|
}
|
||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
|
|
||||||
class RTABMAP_CORE_EXPORT OptimizerGTSAM : public Optimizer
|
class RTABMAP_CORE_EXPORT OptimizerGTSAM : public Optimizer
|
||||||
@@ -38,13 +42,8 @@ public:
|
|||||||
static bool available();
|
static bool available();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OptimizerGTSAM(const ParametersMap & parameters = ParametersMap()) :
|
OptimizerGTSAM(const ParametersMap & parameters = ParametersMap());
|
||||||
Optimizer(parameters),
|
virtual ~OptimizerGTSAM();
|
||||||
optimizer_(Parameters::defaultGTSAMOptimizer())
|
|
||||||
{
|
|
||||||
parseParameters(parameters);
|
|
||||||
}
|
|
||||||
virtual ~OptimizerGTSAM() {}
|
|
||||||
|
|
||||||
virtual Type type() const {return kTypeGTSAM;}
|
virtual Type type() const {return kTypeGTSAM;}
|
||||||
|
|
||||||
@@ -60,7 +59,25 @@ public:
|
|||||||
int * iterationsDone = 0);
|
int * iterationsDone = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int optimizer_;
|
int internalOptimizerType_;
|
||||||
|
|
||||||
|
gtsam::ISAM2 * isam2_;
|
||||||
|
struct ConstraintToFactor {
|
||||||
|
ConstraintToFactor(int _from, int _to, std::uint64_t _factorIndice)
|
||||||
|
{
|
||||||
|
from = _from;
|
||||||
|
to = _to;
|
||||||
|
factorIndice = _factorIndice;
|
||||||
|
}
|
||||||
|
int from;
|
||||||
|
int to;
|
||||||
|
std::uint64_t factorIndice;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ConstraintToFactor> lastAddedConstraints_;
|
||||||
|
int lastSwitchId_;
|
||||||
|
std::set<int> addedPoses_;
|
||||||
|
std::pair<int, std::uint64_t> lastRootFactorIndex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace rtabmap */
|
} /* namespace rtabmap */
|
||||||
|
|||||||
@@ -292,6 +292,11 @@ bool CameraThread::odomProvided() const
|
|||||||
void CameraThread::mainLoopBegin()
|
void CameraThread::mainLoopBegin()
|
||||||
{
|
{
|
||||||
ULogger::registerCurrentThread("Camera");
|
ULogger::registerCurrentThread("Camera");
|
||||||
|
if(_imuFilter)
|
||||||
|
{
|
||||||
|
// In case we paused the camera and moved somewhere else, restart filtering.
|
||||||
|
_imuFilter->reset();
|
||||||
|
}
|
||||||
_camera->resetTimer();
|
_camera->resetTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ bool GlobalMap::update(const std::map<int, Transform> & poses)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UDEBUG("Updated pose for node %d is not found, some points may not be copied. Use negative ids to just update cell values without adding new ones.", jter->first);
|
UDEBUG("Updated pose for node %d is not found, some points may not be copied. Use negative ids to just update cell values without adding new ones.", iter->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1094,6 +1094,13 @@ void Rtabmap::resetMemory()
|
|||||||
{
|
{
|
||||||
UERROR("RTAB-Map is not initialized. No memory to reset...");
|
UERROR("RTAB-Map is not initialized. No memory to reset...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_graphOptimizer)
|
||||||
|
{
|
||||||
|
delete _graphOptimizer;
|
||||||
|
_graphOptimizer = Optimizer::create(_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
this->setupLogFiles(true);
|
this->setupLogFiles(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1177,6 +1184,7 @@ bool Rtabmap::process(
|
|||||||
double timeMemoryUpdate = 0;
|
double timeMemoryUpdate = 0;
|
||||||
double timeNeighborLinkRefining = 0;
|
double timeNeighborLinkRefining = 0;
|
||||||
double timeProximityByTimeDetection = 0;
|
double timeProximityByTimeDetection = 0;
|
||||||
|
double timeProximityBySpaceSearch = 0;
|
||||||
double timeProximityBySpaceVisualDetection = 0;
|
double timeProximityBySpaceVisualDetection = 0;
|
||||||
double timeProximityBySpaceDetection = 0;
|
double timeProximityBySpaceDetection = 0;
|
||||||
double timeCleaningNeighbors = 0;
|
double timeCleaningNeighbors = 0;
|
||||||
@@ -2598,22 +2606,39 @@ bool Rtabmap::process(
|
|||||||
// 1) compare visually with nearest locations
|
// 1) compare visually with nearest locations
|
||||||
//
|
//
|
||||||
UDEBUG("Proximity detection (local loop closure in SPACE using matching images, local radius=%fm)", _localRadius);
|
UDEBUG("Proximity detection (local loop closure in SPACE using matching images, local radius=%fm)", _localRadius);
|
||||||
std::map<int, float> nearestIds;
|
std::map<int, float> nearestIds = graph::findNearestNodes(signature->id(), _optimizedPoses, _localRadius);
|
||||||
if(_memory->isIncremental() && _proximityMaxGraphDepth > 0)
|
|
||||||
{
|
|
||||||
nearestIds = _memory->getNeighborsIdRadius(signature->id(), _localRadius, _optimizedPoses, _proximityMaxGraphDepth);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nearestIds = graph::findNearestNodes(signature->id(), _optimizedPoses, _localRadius);
|
|
||||||
}
|
|
||||||
UDEBUG("nearestIds=%d/%d", (int)nearestIds.size(), (int)_optimizedPoses.size());
|
UDEBUG("nearestIds=%d/%d", (int)nearestIds.size(), (int)_optimizedPoses.size());
|
||||||
std::map<int, Transform> nearestPoses;
|
std::map<int, Transform> nearestPoses;
|
||||||
|
std::multimap<int, int> links;
|
||||||
|
if(_memory->isIncremental() && _proximityMaxGraphDepth>0)
|
||||||
|
{
|
||||||
|
// get bidirectional links
|
||||||
|
for(std::multimap<int, Link>::iterator iter=_constraints.begin(); iter!=_constraints.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(uContains(_optimizedPoses, iter->second.from()) && uContains(_optimizedPoses, iter->second.to()))
|
||||||
|
{
|
||||||
|
links.insert(std::make_pair(iter->second.from(), iter->second.to()));
|
||||||
|
links.insert(std::make_pair(iter->second.to(), iter->second.from())); // <->
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for(std::map<int, float>::iterator iter=nearestIds.lower_bound(1); iter!=nearestIds.end(); ++iter)
|
for(std::map<int, float>::iterator iter=nearestIds.lower_bound(1); iter!=nearestIds.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(_memory->getStMem().find(iter->first) == _memory->getStMem().end())
|
if(_memory->getStMem().find(iter->first) == _memory->getStMem().end())
|
||||||
{
|
{
|
||||||
nearestPoses.insert(std::make_pair(iter->first, _optimizedPoses.at(iter->first)));
|
if(_memory->isIncremental() && _proximityMaxGraphDepth > 0)
|
||||||
|
{
|
||||||
|
std::list<std::pair<int, Transform> > path = graph::computePath(_optimizedPoses, links, signature->id(), iter->first);
|
||||||
|
UDEBUG("Graph depth to %d = %ld", iter->first, path.size());
|
||||||
|
if(!path.empty() && (int)path.size() <= _proximityMaxGraphDepth)
|
||||||
|
{
|
||||||
|
nearestPoses.insert(std::make_pair(iter->first, _optimizedPoses.at(iter->first)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nearestPoses.insert(std::make_pair(iter->first, _optimizedPoses.at(iter->first)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UDEBUG("nearestPoses=%d", (int)nearestPoses.size());
|
UDEBUG("nearestPoses=%d", (int)nearestPoses.size());
|
||||||
@@ -2645,6 +2670,9 @@ bool Rtabmap::process(
|
|||||||
}
|
}
|
||||||
UDEBUG("nearestPaths=%d proximityMaxPaths=%d", (int)nearestPaths.size(), _proximityMaxPaths);
|
UDEBUG("nearestPaths=%d proximityMaxPaths=%d", (int)nearestPaths.size(), _proximityMaxPaths);
|
||||||
|
|
||||||
|
timeProximityBySpaceSearch = timer.ticks();
|
||||||
|
ULOGGER_INFO("timeProximityBySpaceSearch=%fs", timeProximityBySpaceSearch);
|
||||||
|
|
||||||
float proximityFilteringRadius = _proximityFilteringRadius;
|
float proximityFilteringRadius = _proximityFilteringRadius;
|
||||||
if(_maxLoopClosureDistance>0.0f && (proximityFilteringRadius <= 0.0f || _maxLoopClosureDistance<proximityFilteringRadius))
|
if(_maxLoopClosureDistance>0.0f && (proximityFilteringRadius <= 0.0f || _maxLoopClosureDistance<proximityFilteringRadius))
|
||||||
{
|
{
|
||||||
@@ -4072,6 +4100,7 @@ bool Rtabmap::process(
|
|||||||
statistics_.addStatistic(Statistics::kTimingMemory_update(), timeMemoryUpdate*1000);
|
statistics_.addStatistic(Statistics::kTimingMemory_update(), timeMemoryUpdate*1000);
|
||||||
statistics_.addStatistic(Statistics::kTimingNeighbor_link_refining(), timeNeighborLinkRefining*1000);
|
statistics_.addStatistic(Statistics::kTimingNeighbor_link_refining(), timeNeighborLinkRefining*1000);
|
||||||
statistics_.addStatistic(Statistics::kTimingProximity_by_time(), timeProximityByTimeDetection*1000);
|
statistics_.addStatistic(Statistics::kTimingProximity_by_time(), timeProximityByTimeDetection*1000);
|
||||||
|
statistics_.addStatistic(Statistics::kTimingProximity_by_space_search(), timeProximityBySpaceSearch*1000);
|
||||||
statistics_.addStatistic(Statistics::kTimingProximity_by_space_visual(), timeProximityBySpaceVisualDetection*1000);
|
statistics_.addStatistic(Statistics::kTimingProximity_by_space_visual(), timeProximityBySpaceVisualDetection*1000);
|
||||||
statistics_.addStatistic(Statistics::kTimingProximity_by_space(), timeProximityBySpaceDetection*1000);
|
statistics_.addStatistic(Statistics::kTimingProximity_by_space(), timeProximityBySpaceDetection*1000);
|
||||||
statistics_.addStatistic(Statistics::kTimingReactivation(), timeReactivations*1000);
|
statistics_.addStatistic(Statistics::kTimingReactivation(), timeReactivations*1000);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include "gtsam/GravityFactor.h"
|
#include "gtsam/GravityFactor.h"
|
||||||
#include <optimizer/gtsam/XYFactor.h>
|
#include <optimizer/gtsam/XYFactor.h>
|
||||||
#include <optimizer/gtsam/XYZFactor.h>
|
#include <optimizer/gtsam/XYZFactor.h>
|
||||||
|
#include <gtsam/nonlinear/ISAM2.h>
|
||||||
|
|
||||||
#ifdef RTABMAP_VERTIGO
|
#ifdef RTABMAP_VERTIGO
|
||||||
#include "vertigo/gtsam/betweenFactorSwitchable.h"
|
#include "vertigo/gtsam/betweenFactorSwitchable.h"
|
||||||
@@ -62,6 +63,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
|
|
||||||
|
OptimizerGTSAM::OptimizerGTSAM(const ParametersMap & parameters) :
|
||||||
|
Optimizer(parameters),
|
||||||
|
internalOptimizerType_(Parameters::defaultGTSAMOptimizer()),
|
||||||
|
isam2_(0),
|
||||||
|
lastSwitchId_(1000000000)
|
||||||
|
{
|
||||||
|
lastRootFactorIndex_.first = 0;
|
||||||
|
parseParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptimizerGTSAM::~OptimizerGTSAM()
|
||||||
|
{
|
||||||
|
#ifdef RTABMAP_GTSAM
|
||||||
|
delete isam2_;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool OptimizerGTSAM::available()
|
bool OptimizerGTSAM::available()
|
||||||
{
|
{
|
||||||
#ifdef RTABMAP_GTSAM
|
#ifdef RTABMAP_GTSAM
|
||||||
@@ -74,7 +92,47 @@ bool OptimizerGTSAM::available()
|
|||||||
void OptimizerGTSAM::parseParameters(const ParametersMap & parameters)
|
void OptimizerGTSAM::parseParameters(const ParametersMap & parameters)
|
||||||
{
|
{
|
||||||
Optimizer::parseParameters(parameters);
|
Optimizer::parseParameters(parameters);
|
||||||
Parameters::parse(parameters, Parameters::kGTSAMOptimizer(), optimizer_);
|
#ifdef RTABMAP_GTSAM
|
||||||
|
Parameters::parse(parameters, Parameters::kGTSAMOptimizer(), internalOptimizerType_);
|
||||||
|
|
||||||
|
bool incremental = isam2_;
|
||||||
|
double threshold = Parameters::defaultGTSAMIncRelinearizeThreshold();
|
||||||
|
int skip = Parameters::defaultGTSAMIncRelinearizeSkip();
|
||||||
|
Parameters::parse(parameters, Parameters::kGTSAMIncremental(), incremental);
|
||||||
|
Parameters::parse(parameters, Parameters::kGTSAMIncRelinearizeThreshold(), threshold);
|
||||||
|
Parameters::parse(parameters, Parameters::kGTSAMIncRelinearizeSkip(), skip);
|
||||||
|
UDEBUG("GTSAM %s=%d", Parameters::kGTSAMOptimizer().c_str(), internalOptimizerType_);
|
||||||
|
UDEBUG("GTSAM %s=%d", Parameters::kGTSAMIncremental().c_str(), incremental?1:0);
|
||||||
|
UDEBUG("GTSAM %s=%f", Parameters::kGTSAMIncRelinearizeThreshold().c_str(), threshold);
|
||||||
|
UDEBUG("GTSAM %s=%d", Parameters::kGTSAMIncRelinearizeSkip().c_str(), skip);
|
||||||
|
if(incremental && !isam2_)
|
||||||
|
{
|
||||||
|
gtsam::ISAM2Params::OptimizationParams optParams;
|
||||||
|
if(internalOptimizerType_==2)
|
||||||
|
{
|
||||||
|
optParams = gtsam::ISAM2DoglegParams();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
optParams = gtsam::ISAM2GaussNewtonParams();
|
||||||
|
}
|
||||||
|
gtsam::ISAM2Params params(optParams);
|
||||||
|
params.relinearizeThreshold = threshold;
|
||||||
|
params.relinearizeSkip = skip;
|
||||||
|
params.evaluateNonlinearError = true;
|
||||||
|
isam2_ = new ISAM2(params);
|
||||||
|
|
||||||
|
addedPoses_.clear();
|
||||||
|
lastAddedConstraints_.clear();
|
||||||
|
lastRootFactorIndex_.first = 0;
|
||||||
|
lastSwitchId_ = 1000000000;
|
||||||
|
}
|
||||||
|
else if(!incremental && isam2_)
|
||||||
|
{
|
||||||
|
delete isam2_;
|
||||||
|
isam2_ = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<int, Transform> OptimizerGTSAM::optimize(
|
std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||||
@@ -138,9 +196,13 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ConstraintToFactor> addedPrior;
|
||||||
|
gtsam::FactorIndices removeFactorIndices;
|
||||||
|
|
||||||
//prior first pose
|
//prior first pose
|
||||||
if(rootId != 0)
|
if(rootId != 0 && (!isam2_ || lastRootFactorIndex_.first != rootId))
|
||||||
{
|
{
|
||||||
|
UDEBUG("Setting prior for rootId=%d", rootId);
|
||||||
UASSERT(uContains(poses, rootId));
|
UASSERT(uContains(poses, rootId));
|
||||||
const Transform & initialPose = poses.at(rootId);
|
const Transform & initialPose = poses.at(rootId);
|
||||||
UDEBUG("hasGPSPrior=%s", hasGPSPrior?"true":"false");
|
UDEBUG("hasGPSPrior=%s", hasGPSPrior?"true":"false");
|
||||||
@@ -148,6 +210,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
{
|
{
|
||||||
gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(gtsam::Vector3(0.01, 0.01, hasGPSPrior?1e-2:std::numeric_limits<double>::min()));
|
gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(gtsam::Vector3(0.01, 0.01, hasGPSPrior?1e-2:std::numeric_limits<double>::min()));
|
||||||
graph.add(gtsam::PriorFactor<gtsam::Pose2>(rootId, gtsam::Pose2(initialPose.x(), initialPose.y(), initialPose.theta()), priorNoise));
|
graph.add(gtsam::PriorFactor<gtsam::Pose2>(rootId, gtsam::Pose2(initialPose.x(), initialPose.y(), initialPose.theta()), priorNoise));
|
||||||
|
addedPrior.push_back(ConstraintToFactor(rootId, rootId, -1));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -157,14 +220,95 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
(hasGPSPrior?2:1e-2), hasGPSPrior?2:1e-2, hasGPSPrior?2:1e-2 // xyz
|
(hasGPSPrior?2:1e-2), hasGPSPrior?2:1e-2, hasGPSPrior?2:1e-2 // xyz
|
||||||
).finished());
|
).finished());
|
||||||
graph.add(gtsam::PriorFactor<gtsam::Pose3>(rootId, gtsam::Pose3(initialPose.toEigen4d()), priorNoise));
|
graph.add(gtsam::PriorFactor<gtsam::Pose3>(rootId, gtsam::Pose3(initialPose.toEigen4d()), priorNoise));
|
||||||
|
addedPrior.push_back(ConstraintToFactor(rootId, rootId, -1));
|
||||||
}
|
}
|
||||||
|
if(isam2_ && lastRootFactorIndex_.first!=0)
|
||||||
|
{
|
||||||
|
if(uContains(poses, lastRootFactorIndex_.first))
|
||||||
|
{
|
||||||
|
UDEBUG("isam2: switching rootid from %d to %d", lastRootFactorIndex_.first, rootId);
|
||||||
|
removeFactorIndices.push_back(lastRootFactorIndex_.second);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UDEBUG("isam2: reset iSAM2, disjoint mapping sessions between previous root %d and new root %d", lastRootFactorIndex_.first, rootId);
|
||||||
|
// reset iSAM2, disjoint mapping session
|
||||||
|
gtsam::ISAM2Params params = isam2_->params();
|
||||||
|
delete isam2_;
|
||||||
|
isam2_ = new gtsam::ISAM2(params);
|
||||||
|
addedPoses_.clear();
|
||||||
|
lastAddedConstraints_.clear();
|
||||||
|
lastRootFactorIndex_.first = 0;
|
||||||
|
lastSwitchId_ = 1000000000;
|
||||||
|
}
|
||||||
|
lastRootFactorIndex_.first = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, Transform> newPoses;
|
||||||
|
std::multimap<int, Link> newEdgeConstraints;
|
||||||
|
|
||||||
|
if(isam2_)
|
||||||
|
{
|
||||||
|
UDEBUG("Add new poses...");
|
||||||
|
// new poses?
|
||||||
|
for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(addedPoses_.find(iter->first) == addedPoses_.end())
|
||||||
|
{
|
||||||
|
newPoses.insert(*iter);
|
||||||
|
UDEBUG("Adding pose %d to factor graph", iter->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UDEBUG("Add new links...");
|
||||||
|
// new links?
|
||||||
|
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(addedPoses_.find(iter->second.from()) == addedPoses_.end() ||
|
||||||
|
addedPoses_.find(iter->second.to()) == addedPoses_.end())
|
||||||
|
{
|
||||||
|
newEdgeConstraints.insert(*iter);
|
||||||
|
UDEBUG("Adding constraint %d (%d->%d) to factor graph", iter->first, iter->second.from(), iter->second.to());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this->isRobust())
|
||||||
|
{
|
||||||
|
UDEBUG("Remove links...");
|
||||||
|
// Remove constraints not there anymore in case the last loop closures were rejected.
|
||||||
|
// As we don't track "switch" constraints, we don't support this if vertigo is used.
|
||||||
|
for(size_t i=0; i<lastAddedConstraints_.size(); ++i)
|
||||||
|
{
|
||||||
|
if(lastAddedConstraints_[i].from != lastAddedConstraints_[i].to &&
|
||||||
|
graph::findLink(edgeConstraints, lastAddedConstraints_[i].from, lastAddedConstraints_[i].to) == edgeConstraints.end())
|
||||||
|
{
|
||||||
|
removeFactorIndices.push_back(lastAddedConstraints_[i].factorIndice);
|
||||||
|
UDEBUG("Removing constraint %d->%d (factor indice=%ld)",
|
||||||
|
lastAddedConstraints_[i].from,
|
||||||
|
lastAddedConstraints_[i].to,
|
||||||
|
lastAddedConstraints_[i].factorIndice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(poses.rbegin()->first >= 1000000000)
|
||||||
|
{
|
||||||
|
UERROR("Lastest pose id (%d) is too huge for robust optimization (over switch offset of 1000000000)", poses.rbegin()->first);
|
||||||
|
return optimizedPoses;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastAddedConstraints_ = addedPrior;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newPoses = poses;
|
||||||
|
newEdgeConstraints = edgeConstraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
UDEBUG("fill poses to gtsam... rootId=%d (priorsIgnored=%d landmarksIgnored=%d)",
|
UDEBUG("fill poses to gtsam... rootId=%d (priorsIgnored=%d landmarksIgnored=%d)",
|
||||||
rootId, priorsIgnored()?1:0, landmarksIgnored()?1:0);
|
rootId, priorsIgnored()?1:0, landmarksIgnored()?1:0);
|
||||||
gtsam::Values initialEstimate;
|
gtsam::Values initialEstimate;
|
||||||
std::map<int, bool> isLandmarkWithRotation;
|
std::map<int, bool> isLandmarkWithRotation;
|
||||||
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
for(std::map<int, Transform>::const_iterator iter = newPoses.begin(); iter!=newPoses.end(); ++iter)
|
||||||
{
|
{
|
||||||
UASSERT(!iter->second.isNull());
|
UASSERT(!iter->second.isNull());
|
||||||
if(isSlam2d())
|
if(isSlam2d())
|
||||||
@@ -172,12 +316,13 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
if(iter->first > 0)
|
if(iter->first > 0)
|
||||||
{
|
{
|
||||||
initialEstimate.insert(iter->first, gtsam::Pose2(iter->second.x(), iter->second.y(), iter->second.theta()));
|
initialEstimate.insert(iter->first, gtsam::Pose2(iter->second.x(), iter->second.y(), iter->second.theta()));
|
||||||
|
addedPoses_.insert(iter->first);
|
||||||
}
|
}
|
||||||
else if(!landmarksIgnored())
|
else if(!landmarksIgnored())
|
||||||
{
|
{
|
||||||
// check if it is SE2 or only PointXY
|
// check if it is SE2 or only PointXY
|
||||||
std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(iter->first);
|
std::multimap<int, Link>::const_iterator jter=newEdgeConstraints.find(iter->first);
|
||||||
UASSERT_MSG(jter != edgeConstraints.end(), uFormat("Not found landmark %d in edges!", iter->first).c_str());
|
UASSERT_MSG(jter != newEdgeConstraints.end(), uFormat("Not found landmark %d in edges!", iter->first).c_str());
|
||||||
|
|
||||||
if (1 / static_cast<double>(jter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
if (1 / static_cast<double>(jter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||||
{
|
{
|
||||||
@@ -189,6 +334,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
initialEstimate.insert(iter->first, gtsam::Pose2(iter->second.x(), iter->second.y(), iter->second.theta()));
|
initialEstimate.insert(iter->first, gtsam::Pose2(iter->second.x(), iter->second.y(), iter->second.theta()));
|
||||||
isLandmarkWithRotation.insert(std::make_pair(iter->first, true));
|
isLandmarkWithRotation.insert(std::make_pair(iter->first, true));
|
||||||
}
|
}
|
||||||
|
addedPoses_.insert(iter->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -197,12 +343,13 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
if(iter->first > 0)
|
if(iter->first > 0)
|
||||||
{
|
{
|
||||||
initialEstimate.insert(iter->first, gtsam::Pose3(iter->second.toEigen4d()));
|
initialEstimate.insert(iter->first, gtsam::Pose3(iter->second.toEigen4d()));
|
||||||
|
addedPoses_.insert(iter->first);
|
||||||
}
|
}
|
||||||
else if(!landmarksIgnored())
|
else if(!landmarksIgnored())
|
||||||
{
|
{
|
||||||
// check if it is SE3 or only PointXYZ
|
// check if it is SE3 or only PointXYZ
|
||||||
std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(iter->first);
|
std::multimap<int, Link>::const_iterator jter=newEdgeConstraints.find(iter->first);
|
||||||
UASSERT_MSG(jter != edgeConstraints.end(), uFormat("Not found landmark %d in edges!", iter->first).c_str());
|
UASSERT_MSG(jter != newEdgeConstraints.end(), uFormat("Not found landmark %d in edges!", iter->first).c_str());
|
||||||
|
|
||||||
if (1 / static_cast<double>(jter->second.infMatrix().at<double>(3,3)) >= 9999.0 ||
|
if (1 / static_cast<double>(jter->second.infMatrix().at<double>(3,3)) >= 9999.0 ||
|
||||||
1 / static_cast<double>(jter->second.infMatrix().at<double>(4,4)) >= 9999.0 ||
|
1 / static_cast<double>(jter->second.infMatrix().at<double>(4,4)) >= 9999.0 ||
|
||||||
@@ -216,19 +363,23 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
initialEstimate.insert(iter->first, gtsam::Pose3(iter->second.toEigen4d()));
|
initialEstimate.insert(iter->first, gtsam::Pose3(iter->second.toEigen4d()));
|
||||||
isLandmarkWithRotation.insert(std::make_pair(iter->first, true));
|
isLandmarkWithRotation.insert(std::make_pair(iter->first, true));
|
||||||
}
|
}
|
||||||
|
addedPoses_.insert(iter->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UDEBUG("fill edges to gtsam...");
|
UDEBUG("fill edges to gtsam...");
|
||||||
int switchCounter = poses.rbegin()->first+1;
|
if(!isam2_)
|
||||||
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
|
{
|
||||||
|
lastSwitchId_ = newPoses.rbegin()->first+1;
|
||||||
|
}
|
||||||
|
for(std::multimap<int, Link>::const_iterator iter=newEdgeConstraints.begin(); iter!=newEdgeConstraints.end(); ++iter)
|
||||||
{
|
{
|
||||||
int id1 = iter->second.from();
|
int id1 = iter->second.from();
|
||||||
int id2 = iter->second.to();
|
int id2 = iter->second.to();
|
||||||
|
|
||||||
UASSERT_MSG(initialEstimate.find(id1)!=initialEstimate.end(), uFormat("id1=%d", id1).c_str());
|
UASSERT_MSG(poses.find(id1)!=poses.end(), uFormat("id1=%d", id1).c_str());
|
||||||
UASSERT_MSG(initialEstimate.find(id2)!=initialEstimate.end(), uFormat("id2=%d", id2).c_str());
|
UASSERT_MSG(poses.find(id2)!=poses.end(), uFormat("id2=%d", id2).c_str());
|
||||||
|
|
||||||
UASSERT(!iter->second.transform().isNull());
|
UASSERT(!iter->second.transform().isNull());
|
||||||
if(id1 == id2)
|
if(id1 == id2)
|
||||||
@@ -244,6 +395,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
1/iter->second.infMatrix().at<double>(0,0),
|
1/iter->second.infMatrix().at<double>(0,0),
|
||||||
1/iter->second.infMatrix().at<double>(1,1)));
|
1/iter->second.infMatrix().at<double>(1,1)));
|
||||||
graph.add(XYFactor<gtsam::Point2>(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
|
graph.add(XYFactor<gtsam::Point2>(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||||
}
|
}
|
||||||
else if (1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
else if (1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||||
{
|
{
|
||||||
@@ -251,6 +403,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
1/iter->second.infMatrix().at<double>(0,0),
|
1/iter->second.infMatrix().at<double>(0,0),
|
||||||
1/iter->second.infMatrix().at<double>(1,1)));
|
1/iter->second.infMatrix().at<double>(1,1)));
|
||||||
graph.add(XYFactor<gtsam::Pose2>(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
|
graph.add(XYFactor<gtsam::Pose2>(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -270,6 +423,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
|
|
||||||
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
||||||
graph.add(gtsam::PriorFactor<gtsam::Pose2>(id1, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
graph.add(gtsam::PriorFactor<gtsam::Pose2>(id1, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -281,6 +435,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
iter->second.infMatrix().at<double>(1,1),
|
iter->second.infMatrix().at<double>(1,1),
|
||||||
iter->second.infMatrix().at<double>(2,2)));
|
iter->second.infMatrix().at<double>(2,2)));
|
||||||
graph.add(XYZFactor<gtsam::Point3>(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
|
graph.add(XYZFactor<gtsam::Point3>(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||||
}
|
}
|
||||||
else if (1 / static_cast<double>(iter->second.infMatrix().at<double>(3,3)) >= 9999.0 ||
|
else if (1 / static_cast<double>(iter->second.infMatrix().at<double>(3,3)) >= 9999.0 ||
|
||||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(4,4)) >= 9999.0 ||
|
1 / static_cast<double>(iter->second.infMatrix().at<double>(4,4)) >= 9999.0 ||
|
||||||
@@ -291,6 +446,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
iter->second.infMatrix().at<double>(1,1),
|
iter->second.infMatrix().at<double>(1,1),
|
||||||
iter->second.infMatrix().at<double>(2,2)));
|
iter->second.infMatrix().at<double>(2,2)));
|
||||||
graph.add(XYZFactor<gtsam::Pose3>(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
|
graph.add(XYZFactor<gtsam::Pose3>(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -308,15 +464,17 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
|
gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
|
||||||
|
|
||||||
graph.add(gtsam::PriorFactor<gtsam::Pose3>(id1, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
graph.add(gtsam::PriorFactor<gtsam::Pose3>(id1, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!isSlam2d() && gravitySigma() > 0 && iter->second.type() == Link::kGravity && poses.find(iter->first) != poses.end())
|
else if(!isSlam2d() && gravitySigma() > 0 && iter->second.type() == Link::kGravity && newPoses.find(iter->first) != newPoses.end())
|
||||||
{
|
{
|
||||||
Vector3 r = gtsam::Pose3(iter->second.transform().toEigen4d()).rotation().xyz();
|
Vector3 r = gtsam::Pose3(iter->second.transform().toEigen4d()).rotation().xyz();
|
||||||
gtsam::Unit3 nG = gtsam::Rot3::RzRyRx(r.x(), r.y(), 0).rotate(gtsam::Unit3(0,0,-1));
|
gtsam::Unit3 nG = gtsam::Rot3::RzRyRx(r.x(), r.y(), 0).rotate(gtsam::Unit3(0,0,-1));
|
||||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Isotropic::Sigmas(gtsam::Vector2(gravitySigma(), 10));
|
gtsam::SharedNoiseModel model = gtsam::noiseModel::Isotropic::Sigmas(gtsam::Vector2(gravitySigma(), 10));
|
||||||
graph.add(Pose3GravityFactor(iter->first, nG, model, Unit3(0,0,1)));
|
graph.add(Pose3GravityFactor(iter->first, nG, model, Unit3(0,0,1)));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(iter->first, iter->first, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(id1<0 || id2 < 0)
|
else if(id1<0 || id2 < 0)
|
||||||
@@ -354,6 +512,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
}
|
}
|
||||||
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
||||||
graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(t.x(), t.y(), t.theta()), model));
|
graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(t.x(), t.y(), t.theta()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -368,6 +527,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
gtsam::Point2 landmark(t.x(), t.y());
|
gtsam::Point2 landmark(t.x(), t.y());
|
||||||
gtsam::Pose2 p;
|
gtsam::Pose2 p;
|
||||||
graph.add(gtsam::BearingRangeFactor<gtsam::Pose2, gtsam::Point2>(id1, id2, p.bearing(landmark), p.range(landmark), model));
|
graph.add(gtsam::BearingRangeFactor<gtsam::Pose2, gtsam::Point2>(id1, id2, p.bearing(landmark), p.range(landmark), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -387,6 +547,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
mgtsam.block(3,0,3,3) = information.block(0,3,3,3); // off diagonal
|
mgtsam.block(3,0,3,3) = information.block(0,3,3,3); // off diagonal
|
||||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
|
gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
|
||||||
graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(t.toEigen4d()), model));
|
graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(t.toEigen4d()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -401,6 +562,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
gtsam::Point3 landmark(t.x(), t.y(), t.z());
|
gtsam::Point3 landmark(t.x(), t.y(), t.z());
|
||||||
gtsam::Pose3 p;
|
gtsam::Pose3 p;
|
||||||
graph.add(gtsam::BearingRangeFactor<gtsam::Pose3, gtsam::Point3>(id1, id2, p.bearing(landmark), p.range(landmark), model));
|
graph.add(gtsam::BearingRangeFactor<gtsam::Pose3, gtsam::Point3>(id1, id2, p.bearing(landmark), p.range(landmark), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,7 +580,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
// a proper and convenient initial value for all switch variables would be
|
// a proper and convenient initial value for all switch variables would be
|
||||||
// sij = 1 when using the linear switch function"
|
// sij = 1 when using the linear switch function"
|
||||||
double prior = 1.0;
|
double prior = 1.0;
|
||||||
initialEstimate.insert(gtsam::Symbol('s',switchCounter), vertigo::SwitchVariableLinear(prior));
|
initialEstimate.insert(gtsam::Symbol('s',lastSwitchId_), vertigo::SwitchVariableLinear(prior));
|
||||||
|
|
||||||
// create switch prior factor
|
// create switch prior factor
|
||||||
// "If the front-end is not able to assign sound individual values
|
// "If the front-end is not able to assign sound individual values
|
||||||
@@ -426,7 +588,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
// to the individual optimal choice of Ξij for a large range of
|
// to the individual optimal choice of Ξij for a large range of
|
||||||
// outliers."
|
// outliers."
|
||||||
gtsam::noiseModel::Diagonal::shared_ptr switchPriorModel = gtsam::noiseModel::Diagonal::Sigmas(gtsam::Vector1(1.0));
|
gtsam::noiseModel::Diagonal::shared_ptr switchPriorModel = gtsam::noiseModel::Diagonal::Sigmas(gtsam::Vector1(1.0));
|
||||||
graph.add(gtsam::PriorFactor<vertigo::SwitchVariableLinear> (gtsam::Symbol('s',switchCounter), vertigo::SwitchVariableLinear(prior), switchPriorModel));
|
graph.add(gtsam::PriorFactor<vertigo::SwitchVariableLinear> (gtsam::Symbol('s',lastSwitchId_), vertigo::SwitchVariableLinear(prior), switchPriorModel));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(isSlam2d())
|
if(isSlam2d())
|
||||||
@@ -452,12 +614,13 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
iter->second.type() != Link::kNeighborMerged)
|
iter->second.type() != Link::kNeighborMerged)
|
||||||
{
|
{
|
||||||
// create switchable edge factor
|
// create switchable edge factor
|
||||||
graph.add(vertigo::BetweenFactorSwitchableLinear<gtsam::Pose2>(id1, id2, gtsam::Symbol('s', switchCounter++), gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
graph.add(vertigo::BetweenFactorSwitchableLinear<gtsam::Pose2>(id1, id2, gtsam::Symbol('s', lastSwitchId_++), gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -481,61 +644,68 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
iter->second.type() != Link::kNeighborMerged)
|
iter->second.type() != Link::kNeighborMerged)
|
||||||
{
|
{
|
||||||
// create switchable edge factor
|
// create switchable edge factor
|
||||||
graph.add(vertigo::BetweenFactorSwitchableLinear<gtsam::Pose3>(id1, id2, gtsam::Symbol('s', switchCounter++), gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
graph.add(vertigo::BetweenFactorSwitchableLinear<gtsam::Pose3>(id1, id2, gtsam::Symbol('s', lastSwitchId_++), gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||||
|
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UDEBUG("create optimizer");
|
UDEBUG("create optimizer");
|
||||||
gtsam::NonlinearOptimizer * optimizer;
|
gtsam::NonlinearOptimizer * optimizer = 0;
|
||||||
|
|
||||||
if(optimizer_ == 2)
|
if(!isam2_) // Batch optimization
|
||||||
{
|
{
|
||||||
gtsam::DoglegParams parameters;
|
UDEBUG("Batch optimization...");
|
||||||
parameters.relativeErrorTol = epsilon();
|
if(internalOptimizerType_ == 2)
|
||||||
parameters.maxIterations = iterations();
|
{
|
||||||
optimizer = new gtsam::DoglegOptimizer(graph, initialEstimate, parameters);
|
gtsam::DoglegParams parameters;
|
||||||
}
|
parameters.relativeErrorTol = epsilon();
|
||||||
else if(optimizer_ == 1)
|
parameters.maxIterations = iterations();
|
||||||
{
|
optimizer = new gtsam::DoglegOptimizer(graph, initialEstimate, parameters);
|
||||||
gtsam::GaussNewtonParams parameters;
|
}
|
||||||
parameters.relativeErrorTol = epsilon();
|
else if(internalOptimizerType_ == 1)
|
||||||
parameters.maxIterations = iterations();
|
{
|
||||||
optimizer = new gtsam::GaussNewtonOptimizer(graph, initialEstimate, parameters);
|
gtsam::GaussNewtonParams parameters;
|
||||||
|
parameters.relativeErrorTol = epsilon();
|
||||||
|
parameters.maxIterations = iterations();
|
||||||
|
optimizer = new gtsam::GaussNewtonOptimizer(graph, initialEstimate, parameters);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gtsam::LevenbergMarquardtParams parameters;
|
||||||
|
parameters.relativeErrorTol = epsilon();
|
||||||
|
parameters.maxIterations = iterations();
|
||||||
|
optimizer = new gtsam::LevenbergMarquardtOptimizer(graph, initialEstimate, parameters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gtsam::LevenbergMarquardtParams parameters;
|
UDEBUG("iSAM2 optimization...");
|
||||||
parameters.relativeErrorTol = epsilon();
|
|
||||||
parameters.maxIterations = iterations();
|
|
||||||
optimizer = new gtsam::LevenbergMarquardtOptimizer(graph, initialEstimate, parameters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UDEBUG("GTSAM optimizing begin (max iterations=%d, robust=%d)", iterations(), isRobust()?1:0);
|
UDEBUG("GTSAM optimizing begin (max iterations=%d, robust=%d)", iterations(), isRobust()?1:0);
|
||||||
UTimer timer;
|
UTimer timer;
|
||||||
int it = 0;
|
int it = 0;
|
||||||
double lastError = optimizer->error();
|
double initialError = optimizer?graph.error(initialEstimate):0;
|
||||||
|
double lastError = optimizer?optimizer->error():0;
|
||||||
for(int i=0; i<iterations(); ++i)
|
for(int i=0; i<iterations(); ++i)
|
||||||
{
|
{
|
||||||
if(intermediateGraphes && i > 0)
|
if(intermediateGraphes && i > 0)
|
||||||
{
|
{
|
||||||
float x,y,z,roll,pitch,yaw;
|
float x,y,z,roll,pitch,yaw;
|
||||||
std::map<int, Transform> tmpPoses;
|
std::map<int, Transform> tmpPoses;
|
||||||
#if GTSAM_VERSION_NUMERIC >= 40200
|
Values values = isam2_?isam2_->calculateEstimate():optimizer->values();
|
||||||
for(gtsam::Values::deref_iterator iter=optimizer->values().begin(); iter!=optimizer->values().end(); ++iter)
|
for(gtsam::Values::iterator iter=values.begin(); iter!=values.end(); ++iter)
|
||||||
#else
|
|
||||||
for(gtsam::Values::const_iterator iter=optimizer->values().begin(); iter!=optimizer->values().end(); ++iter)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
if(iter->value.dim() > 1)
|
int key = (int)iter->key;
|
||||||
|
if(iter->value.dim() > 1 && uContains(newPoses, key))
|
||||||
{
|
{
|
||||||
int key = (int)iter->key;
|
|
||||||
if(isSlam2d())
|
if(isSlam2d())
|
||||||
{
|
{
|
||||||
if(key > 0)
|
if(key > 0)
|
||||||
@@ -547,13 +717,13 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
{
|
{
|
||||||
if(isLandmarkWithRotation.at(key))
|
if(isLandmarkWithRotation.at(key))
|
||||||
{
|
{
|
||||||
poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
newPoses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
||||||
gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
|
gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
|
||||||
tmpPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), z, roll, pitch, p.theta())));
|
tmpPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), z, roll, pitch, p.theta())));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
newPoses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
||||||
gtsam::Point2 p = iter->value.cast<gtsam::Point2>();
|
gtsam::Point2 p = iter->value.cast<gtsam::Point2>();
|
||||||
tmpPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), z, roll,pitch,yaw)));
|
tmpPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), z, roll,pitch,yaw)));
|
||||||
}
|
}
|
||||||
@@ -575,7 +745,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
newPoses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
||||||
gtsam::Point3 p = iter->value.cast<gtsam::Point3>();
|
gtsam::Point3 p = iter->value.cast<gtsam::Point3>();
|
||||||
tmpPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), p.z(), roll,pitch,yaw)));
|
tmpPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), p.z(), roll,pitch,yaw)));
|
||||||
}
|
}
|
||||||
@@ -585,25 +755,89 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
}
|
}
|
||||||
intermediateGraphes->push_back(tmpPoses);
|
intermediateGraphes->push_back(tmpPoses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gtsam::ISAM2Result result;
|
||||||
|
double error = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
optimizer->iterate();
|
if(optimizer) // Batch optimization
|
||||||
|
{
|
||||||
|
optimizer->iterate();
|
||||||
|
error = optimizer->error();
|
||||||
|
}
|
||||||
|
else if(i==0) // iSAM2 (add factors)
|
||||||
|
{
|
||||||
|
UDEBUG("Update iSAM with the new factors");
|
||||||
|
result = isam2_->update(graph, initialEstimate, removeFactorIndices);
|
||||||
|
#if BOOST_VERSION >= 106800
|
||||||
|
UASSERT(result.errorBefore.has_value());
|
||||||
|
UASSERT(result.errorAfter.has_value());
|
||||||
|
#else
|
||||||
|
UASSERT(result.errorBefore.is_initialized());
|
||||||
|
UASSERT(result.errorAfter.is_initialized());
|
||||||
|
#endif
|
||||||
|
UDEBUG("error before = %f after=%f", result.errorBefore.value(), result.errorAfter.value());
|
||||||
|
initialError = lastError = result.errorBefore.value();
|
||||||
|
error = result.errorAfter.value();
|
||||||
|
if(!this->isRobust())
|
||||||
|
{
|
||||||
|
UASSERT_MSG(lastAddedConstraints_.size() == result.newFactorsIndices.size(),
|
||||||
|
uFormat("%ld versus %ld", lastAddedConstraints_.size(), result.newFactorsIndices.size()).c_str());
|
||||||
|
for(size_t j=0; j<result.newFactorsIndices.size(); ++j)
|
||||||
|
{
|
||||||
|
UDEBUG("New factor indice: %ld", result.newFactorsIndices[j]);
|
||||||
|
lastAddedConstraints_[j].factorIndice = result.newFactorsIndices[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rootId != 0 && lastRootFactorIndex_.first == 0)
|
||||||
|
{
|
||||||
|
UASSERT(result.newFactorsIndices.size()>=1);
|
||||||
|
lastRootFactorIndex_.first = rootId;
|
||||||
|
lastRootFactorIndex_.second = result.newFactorsIndices[0]; // first one should be always the root prior
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // iSAM2 (more iterations)
|
||||||
|
{
|
||||||
|
result = isam2_->update();
|
||||||
|
#if BOOST_VERSION >= 106800
|
||||||
|
UASSERT(result.errorBefore.has_value());
|
||||||
|
UASSERT(result.errorAfter.has_value());
|
||||||
|
#else
|
||||||
|
UASSERT(result.errorBefore.is_initialized());
|
||||||
|
UASSERT(result.errorAfter.is_initialized());
|
||||||
|
#endif
|
||||||
|
UDEBUG("error before = %f after=%f", result.errorBefore.value(), result.errorAfter.value());
|
||||||
|
|
||||||
|
lastError = result.errorBefore.value();
|
||||||
|
error = result.errorAfter.value();
|
||||||
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
catch(gtsam::IndeterminantLinearSystemException & e)
|
catch(gtsam::IndeterminantLinearSystemException & e)
|
||||||
{
|
{
|
||||||
UWARN("GTSAM exception caught: %s\n Graph has %d edges and %d vertices", e.what(),
|
UWARN("GTSAM exception caught: %s\n Graph has %d edges and %d vertices", e.what(),
|
||||||
(int)edgeConstraints.size(),
|
(int)newEdgeConstraints.size(),
|
||||||
(int)poses.size());
|
(int)newPoses.size());
|
||||||
delete optimizer;
|
delete optimizer;
|
||||||
|
if(isam2_)
|
||||||
|
{
|
||||||
|
// We are in bad state, cleanup
|
||||||
|
UDEBUG("Reset iSAM2!");
|
||||||
|
gtsam::ISAM2Params params = isam2_->params();
|
||||||
|
delete isam2_;
|
||||||
|
isam2_ = new gtsam::ISAM2(params);
|
||||||
|
addedPoses_.clear();
|
||||||
|
lastAddedConstraints_.clear();
|
||||||
|
lastRootFactorIndex_.first = 0;
|
||||||
|
lastSwitchId_ = 1000000000;
|
||||||
|
}
|
||||||
return optimizedPoses;
|
return optimizedPoses;
|
||||||
}
|
}
|
||||||
|
|
||||||
// early stop condition
|
// early stop condition
|
||||||
double error = optimizer->error();
|
|
||||||
UDEBUG("iteration %d error =%f", i+1, error);
|
UDEBUG("iteration %d error =%f", i+1, error);
|
||||||
double errorDelta = lastError - error;
|
double errorDelta = lastError - error;
|
||||||
if(i>0 && errorDelta < this->epsilon())
|
if((isam2_ || i>0) && errorDelta < this->epsilon())
|
||||||
{
|
{
|
||||||
if(errorDelta < 0)
|
if(errorDelta < 0)
|
||||||
{
|
{
|
||||||
@@ -630,19 +864,16 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
{
|
{
|
||||||
*iterationsDone = it;
|
*iterationsDone = it;
|
||||||
}
|
}
|
||||||
UDEBUG("GTSAM optimizing end (%d iterations done, error=%f (initial=%f final=%f), time=%f s)",
|
UDEBUG("GTSAM optimizing end (%d iterations done (error initial=%f final=%f), time=%f s)",
|
||||||
optimizer->iterations(), optimizer->error(), graph.error(initialEstimate), graph.error(optimizer->values()), timer.ticks());
|
it, initialError, lastError, timer.ticks());
|
||||||
|
|
||||||
float x,y,z,roll,pitch,yaw;
|
float x,y,z,roll,pitch,yaw;
|
||||||
#if GTSAM_VERSION_NUMERIC >= 40200
|
gtsam::Values values = isam2_?isam2_->calculateEstimate():optimizer->values();
|
||||||
for(gtsam::Values::deref_iterator iter=optimizer->values().begin(); iter!=optimizer->values().end(); ++iter)
|
for(gtsam::Values::iterator iter=values.begin(); iter!=values.end(); ++iter)
|
||||||
#else
|
|
||||||
for(gtsam::Values::const_iterator iter=optimizer->values().begin(); iter!=optimizer->values().end(); ++iter)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
if(iter->value.dim() > 1)
|
int key = (int)iter->key;
|
||||||
|
if(iter->value.dim() > 1 && uContains(poses, key))
|
||||||
{
|
{
|
||||||
int key = (int)iter->key;
|
|
||||||
if(isSlam2d())
|
if(isSlam2d())
|
||||||
{
|
{
|
||||||
if(key > 0)
|
if(key > 0)
|
||||||
@@ -694,10 +925,18 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
|||||||
|
|
||||||
// compute marginals
|
// compute marginals
|
||||||
try {
|
try {
|
||||||
UDEBUG("Computing marginals...");
|
UDEBUG("Computing marginals for node %d...", poses.rbegin()->first);
|
||||||
UTimer t;
|
UTimer t;
|
||||||
gtsam::Marginals marginals(graph, optimizer->values());
|
gtsam::Matrix info;
|
||||||
gtsam::Matrix info = marginals.marginalCovariance(poses.rbegin()->first);
|
if(optimizer)
|
||||||
|
{
|
||||||
|
gtsam::Marginals marginals(graph, optimizer->values());
|
||||||
|
info = marginals.marginalCovariance(poses.rbegin()->first);
|
||||||
|
}
|
||||||
|
else //iSAM2
|
||||||
|
{
|
||||||
|
info = isam2_->marginalCovariance(poses.rbegin()->first);
|
||||||
|
}
|
||||||
UDEBUG("Computed marginals = %fs (key=%d)", t.ticks(), poses.rbegin()->first);
|
UDEBUG("Computed marginals = %fs (key=%d)", t.ticks(), poses.rbegin()->first);
|
||||||
if(isSlam2d() && info.cols() == 3 && info.cols() == 3)
|
if(isSlam2d() && info.cols() == 3 && info.cols() == 3)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3429,7 +3429,8 @@ void MainWindow::updateMapCloud(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cv::Mat map8U;
|
cv::Mat map8U;
|
||||||
if((_ui->graphicsView_graphView->isVisible() || _preferencesDialog->getGridMapShown()))
|
if((_ui->graphicsView_graphView->isVisible() && _ui->graphicsView_graphView->isGridMapVisible()) ||
|
||||||
|
(_cloudViewer->isVisible() && _preferencesDialog->getGridMapShown()))
|
||||||
{
|
{
|
||||||
float xMin, yMin;
|
float xMin, yMin;
|
||||||
float resolution = _occupancyGrid->getCellSize();
|
float resolution = _occupancyGrid->getCellSize();
|
||||||
@@ -3438,7 +3439,6 @@ void MainWindow::updateMapCloud(
|
|||||||
if(_preferencesDialog->isOctomap2dGrid())
|
if(_preferencesDialog->isOctomap2dGrid())
|
||||||
{
|
{
|
||||||
map8S = _octomap->createProjectionMap(xMin, yMin, resolution, 0, _preferencesDialog->getOctomapTreeDepth());
|
map8S = _octomap->createProjectionMap(xMin, yMin, resolution, 0, _preferencesDialog->getOctomapTreeDepth());
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@@ -3458,12 +3458,12 @@ void MainWindow::updateMapCloud(
|
|||||||
//convert to gray scaled map
|
//convert to gray scaled map
|
||||||
map8U = util3d::convertMap2Image8U(map8S);
|
map8U = util3d::convertMap2Image8U(map8S);
|
||||||
|
|
||||||
if(_preferencesDialog->getGridMapShown())
|
if(_cloudViewer->isVisible() && _preferencesDialog->getGridMapShown())
|
||||||
{
|
{
|
||||||
float opacity = _preferencesDialog->getGridMapOpacity();
|
float opacity = _preferencesDialog->getGridMapOpacity();
|
||||||
_cloudViewer->addOccupancyGridMap(map8U, resolution, xMin, yMin, opacity);
|
_cloudViewer->addOccupancyGridMap(map8U, resolution, xMin, yMin, opacity);
|
||||||
}
|
}
|
||||||
if(_ui->graphicsView_graphView->isVisible())
|
if(_ui->graphicsView_graphView->isVisible() && _ui->graphicsView_graphView->isGridMapVisible())
|
||||||
{
|
{
|
||||||
_ui->graphicsView_graphView->updateMap(map8U, resolution, xMin, yMin);
|
_ui->graphicsView_graphView->updateMap(map8U, resolution, xMin, yMin);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1137,6 +1137,9 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
|
|||||||
_ui->doubleSpinBox_g2o_baseline->setObjectName(Parameters::kg2oBaseline().c_str());
|
_ui->doubleSpinBox_g2o_baseline->setObjectName(Parameters::kg2oBaseline().c_str());
|
||||||
|
|
||||||
_ui->comboBox_gtsam_optimizer->setObjectName(Parameters::kGTSAMOptimizer().c_str());
|
_ui->comboBox_gtsam_optimizer->setObjectName(Parameters::kGTSAMOptimizer().c_str());
|
||||||
|
_ui->gtsam_incremental->setObjectName(Parameters::kGTSAMIncremental().c_str());
|
||||||
|
_ui->gtsam_incremental_threshold->setObjectName(Parameters::kGTSAMIncRelinearizeThreshold().c_str());
|
||||||
|
_ui->gtsam_incremental_skip->setObjectName(Parameters::kGTSAMIncRelinearizeSkip().c_str());
|
||||||
|
|
||||||
_ui->graphPlan_goalReachedRadius->setObjectName(Parameters::kRGBDGoalReachedRadius().c_str());
|
_ui->graphPlan_goalReachedRadius->setObjectName(Parameters::kRGBDGoalReachedRadius().c_str());
|
||||||
_ui->graphPlan_goalsSavedInUserData->setObjectName(Parameters::kRGBDGoalsSavedInUserData().c_str());
|
_ui->graphPlan_goalsSavedInUserData->setObjectName(Parameters::kRGBDGoalsSavedInUserData().c_str());
|
||||||
|
|||||||
@@ -23285,6 +23285,16 @@ Lower the ratio -> higher the precision.</string>
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="label_742">
|
||||||
|
<property name="text">
|
||||||
|
<string>iSAM2: Do graph optimization incrementally to increase optimization speed on loop closures. Note that only GaussNewton and Dogleg optimization algorithms are supported in this mode.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLabel" name="label_364">
|
<widget class="QLabel" name="label_364">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -23295,6 +23305,65 @@ Lower the ratio -> higher the precision.</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QDoubleSpinBox" name="gtsam_incremental_threshold">
|
||||||
|
<property name="decimals">
|
||||||
|
<number>4</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>0.000100000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>999.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="label_743">
|
||||||
|
<property name="text">
|
||||||
|
<string>Only relinearize variables whose linear delta magnitude is greater than this threshold. See GTSAM::ISAM2 doc for more info.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="gtsam_incremental">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QSpinBox" name="gtsam_incremental_skip">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="label_744">
|
||||||
|
<property name="text">
|
||||||
|
<string>Only relinearize any variables every X calls to ISAM2::update(). See GTSAM::ISAM2 doc for more info.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
Reference in New Issue
Block a user