Fixed missing 3D keypoints when RGBD/LoopClosureReextractFeatures=true and Reg/Strategy=1 (https://github.com/introlab/rtabmap_ros/issues/668). DBViewer: fixed wrong poses optimization with GTSAM when showing scans of loop closures by proximity by space (multiscan) while there are GPS priors.

This commit is contained in:
matlabbe
2021-12-05 17:38:53 -05:00
parent 12906f4490
commit 580e35afb1
6 changed files with 43 additions and 43 deletions
+2 -1
View File
@@ -133,7 +133,8 @@ std::multimap<int, Link>::iterator RTABMAP_EXP findLink(
std::multimap<int, Link> & links, std::multimap<int, Link> & links,
int from, int from,
int to, int to,
bool checkBothWays = true); bool checkBothWays = true,
Link::Type type = Link::kUndef);
std::multimap<int, int>::iterator RTABMAP_EXP findLink( std::multimap<int, int>::iterator RTABMAP_EXP findLink(
std::multimap<int, int> & links, std::multimap<int, int> & links,
int from, int from,
+4 -3
View File
@@ -989,12 +989,13 @@ std::multimap<int, Link>::iterator findLink(
std::multimap<int, Link> & links, std::multimap<int, Link> & links,
int from, int from,
int to, int to,
bool checkBothWays) bool checkBothWays,
Link::Type type)
{ {
std::multimap<int, Link>::iterator iter = links.find(from); std::multimap<int, Link>::iterator iter = links.find(from);
while(iter != links.end() && iter->first == from) while(iter != links.end() && iter->first == from)
{ {
if(iter->second.to() == to) if(iter->second.to() == to && (type==Link::kUndef || type == iter->second.type()))
{ {
return iter; return iter;
} }
@@ -1007,7 +1008,7 @@ std::multimap<int, Link>::iterator findLink(
iter = links.find(to); iter = links.find(to);
while(iter != links.end() && iter->first == to) while(iter != links.end() && iter->first == to)
{ {
if(iter->second.to() == from) if(iter->second.to() == from && (type==Link::kUndef || type == iter->second.type()))
{ {
return iter; return iter;
} }
+8 -8
View File
@@ -2732,13 +2732,13 @@ Transform Memory::computeTransform(
// make sure we have all data needed // make sure we have all data needed
// load binary data from database if not in RAM (if image is already here, scan and userData should be or they are null) // load binary data from database if not in RAM (if image is already here, scan and userData should be or they are null)
if(((_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired()) && fromS.sensorData().imageCompressed().empty()) || if(((_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull())) && fromS.sensorData().imageCompressed().empty()) ||
(_registrationPipeline->isScanRequired() && fromS.sensorData().imageCompressed().empty() && fromS.sensorData().laserScanCompressed().isEmpty()) || (_registrationPipeline->isScanRequired() && fromS.sensorData().imageCompressed().empty() && fromS.sensorData().laserScanCompressed().isEmpty()) ||
(_registrationPipeline->isUserDataRequired() && fromS.sensorData().imageCompressed().empty() && fromS.sensorData().userDataCompressed().empty())) (_registrationPipeline->isUserDataRequired() && fromS.sensorData().imageCompressed().empty() && fromS.sensorData().userDataCompressed().empty()))
{ {
fromS.sensorData() = getNodeData(fromS.id(), true, true, true, true); fromS.sensorData() = getNodeData(fromS.id(), true, true, true, true);
} }
if(((_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired()) && toS.sensorData().imageCompressed().empty()) || if(((_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull())) && toS.sensorData().imageCompressed().empty()) ||
(_registrationPipeline->isScanRequired() && toS.sensorData().imageCompressed().empty() && toS.sensorData().laserScanCompressed().isEmpty()) || (_registrationPipeline->isScanRequired() && toS.sensorData().imageCompressed().empty() && toS.sensorData().laserScanCompressed().isEmpty()) ||
(_registrationPipeline->isUserDataRequired() && toS.sensorData().imageCompressed().empty() && toS.sensorData().userDataCompressed().empty())) (_registrationPipeline->isUserDataRequired() && toS.sensorData().imageCompressed().empty() && toS.sensorData().userDataCompressed().empty()))
{ {
@@ -2748,27 +2748,27 @@ Transform Memory::computeTransform(
cv::Mat imgBuf, depthBuf, userBuf; cv::Mat imgBuf, depthBuf, userBuf;
LaserScan laserBuf; LaserScan laserBuf;
fromS.sensorData().uncompressData( fromS.sensorData().uncompressData(
(_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired())?&imgBuf:0, (_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull()))?&imgBuf:0,
(_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired())?&depthBuf:0, (_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull()))?&depthBuf:0,
_registrationPipeline->isScanRequired()?&laserBuf:0, _registrationPipeline->isScanRequired()?&laserBuf:0,
_registrationPipeline->isUserDataRequired()?&userBuf:0); _registrationPipeline->isUserDataRequired()?&userBuf:0);
toS.sensorData().uncompressData( toS.sensorData().uncompressData(
(_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired())?&imgBuf:0, (_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull()))?&imgBuf:0,
(_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired())?&depthBuf:0, (_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull()))?&depthBuf:0,
_registrationPipeline->isScanRequired()?&laserBuf:0, _registrationPipeline->isScanRequired()?&laserBuf:0,
_registrationPipeline->isUserDataRequired()?&userBuf:0); _registrationPipeline->isUserDataRequired()?&userBuf:0);
// compute transform fromId -> toId // compute transform fromId -> toId
std::vector<int> inliersV; std::vector<int> inliersV;
if((_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired()) || if((_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull())) ||
(fromS.getWords().size() && toS.getWords().size()) || (fromS.getWords().size() && toS.getWords().size()) ||
(!guess.isNull() && !_registrationPipeline->isImageRequired())) (!guess.isNull() && !_registrationPipeline->isImageRequired()))
{ {
Signature tmpFrom = fromS; Signature tmpFrom = fromS;
Signature tmpTo = toS; Signature tmpTo = toS;
if(_reextractLoopClosureFeatures && _registrationPipeline->isImageRequired()) if(_reextractLoopClosureFeatures && (_registrationPipeline->isImageRequired() || guess.isNull()))
{ {
UDEBUG(""); UDEBUG("");
tmpFrom.removeAllWords(); tmpFrom.removeAllWords();
+1 -1
View File
@@ -118,7 +118,7 @@ void Signature::addLinks(const std::map<int, Link> & links)
} }
void Signature::addLink(const Link & link) void Signature::addLink(const Link & link)
{ {
UDEBUG("Add link %d to %d (type=%d var=%f,%f)", link.to(), this->id(), (int)link.type(), link.transVariance(), link.rotVariance()); UDEBUG("Add link %d to %d (type=%d/%s var=%f,%f)", link.to(), this->id(), (int)link.type(), link.typeName().c_str(), link.transVariance(), link.rotVariance());
UASSERT_MSG(link.from() == this->id(), uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str()); UASSERT_MSG(link.from() == this->id(), uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str());
UASSERT_MSG((link.to() != this->id()) || link.type()==Link::kPosePrior || link.type()==Link::kGravity, uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str()); UASSERT_MSG((link.to() != this->id()) || link.type()==Link::kPosePrior || link.type()==Link::kGravity, uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str());
UASSERT_MSG(link.to() == this->id() || _links.find(link.to()) == _links.end(), uFormat("Link %d (type=%d) already added to signature %d!", link.to(), link.type(), this->id()).c_str()); UASSERT_MSG(link.to() == this->id() || _links.find(link.to()) == _links.end(), uFormat("Link %d (type=%d) already added to signature %d!", link.to(), link.type(), this->id()).c_str());
+10 -15
View File
@@ -106,29 +106,24 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
gtsam::NonlinearFactorGraph graph; gtsam::NonlinearFactorGraph graph;
// detect if there is a global pose prior set, if so remove rootId // detect if there is a global pose prior set, if so remove rootId
bool gpsPriorOnly = false; bool hasGPSPrior = false;
bool hasPriorPoses = false;
if(!priorsIgnored()) if(!priorsIgnored())
{ {
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter) for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
{ {
if(iter->second.from() == iter->second.to() && iter->second.type() == Link::kPosePrior) if(iter->second.from() == iter->second.to() && iter->second.type() == Link::kPosePrior)
{ {
hasPriorPoses = true; hasGPSPrior = true;
if ((isSlam2d() && 1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) < 9999) || if ((isSlam2d() && 1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) < 9999) ||
(1 / static_cast<double>(iter->second.infMatrix().at<double>(3,3)) < 9999.0 && (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 &&
1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) < 9999.0)) 1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) < 9999.0))
{ {
// orientation is set, don't set root prior // orientation is set, don't set root prior (it is no GPS)
gpsPriorOnly = false;
rootId = 0; rootId = 0;
hasGPSPrior = false;
break; break;
} }
else if(gravitySigma()<=0)
{
gpsPriorOnly = true;
}
} }
} }
} }
@@ -138,25 +133,25 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
{ {
UASSERT(uContains(poses, rootId)); UASSERT(uContains(poses, rootId));
const Transform & initialPose = poses.at(rootId); const Transform & initialPose = poses.at(rootId);
UDEBUG("hasPriorPoses=%s, gpsPriorOnly=%s", hasPriorPoses?"true":"false", gpsPriorOnly?"true":"false"); UDEBUG("hasGPSPrior=%s", hasGPSPrior?"true":"false");
if(isSlam2d()) if(isSlam2d())
{ {
gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(gtsam::Vector3(0.01, 0.01, hasPriorPoses?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));
} }
else else
{ {
gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances( gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(
(gtsam::Vector(6) << (gtsam::Vector(6) <<
1e-2, 1e-2, hasPriorPoses?1e-2:std::numeric_limits<double>::min(), // roll, pitch, fixed yaw if there are no priors 1e-2, 1e-2, hasGPSPrior?1e-2:std::numeric_limits<double>::min(), // roll, pitch, fixed yaw if there are no priors
(gpsPriorOnly?2:1e-2), gpsPriorOnly?2:1e-2, gpsPriorOnly?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));
} }
} }
UDEBUG("fill poses to gtsam... rootId=%d (priorsIgnored=%d gpsPriorOnly=%d landmarksIgnored=%d)", UDEBUG("fill poses to gtsam... rootId=%d (priorsIgnored=%d landmarksIgnored=%d)",
rootId, priorsIgnored()?1:0, gpsPriorOnly?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 = poses.begin(); iter!=poses.end(); ++iter)
+18 -15
View File
@@ -1831,11 +1831,11 @@ void DatabaseViewer::updateIds()
infoReducedGraph_ = true; infoReducedGraph_ = true;
} }
std::multimap<int, Link>::iterator invertedLinkIter = graph::findLink(links, jter->second.to(), jter->second.from(), false); std::multimap<int, Link>::iterator invertedLinkIter = graph::findLink(links, jter->second.to(), jter->second.from(), false, jter->second.type());
if( jter->second.isValid() && // null transform means a rehearsed location if( jter->second.isValid() && // null transform means a rehearsed location
ids.find(jter->second.from()) != ids.end() && ids.find(jter->second.from()) != ids.end() &&
(ids.find(jter->second.to()) != ids.end() || jter->second.to()<0) && // to add landmark links (ids.find(jter->second.to()) != ids.end() || jter->second.to()<0) && // to add landmark links
graph::findLink(links_, jter->second.from(), jter->second.to()) == links_.end() && graph::findLink(links_, jter->second.from(), jter->second.to(), false, jter->second.type()) == links_.end() &&
invertedLinkIter != links.end() && invertedLinkIter != links.end() &&
w != -9) w != -9)
{ {
@@ -6218,25 +6218,28 @@ void DatabaseViewer::updateConstraintView(
if(poses.size() != posesOut.size()) if(poses.size() != posesOut.size())
{ {
UWARN("Scan poses input and output are different! %d vs %d", (int)poses.size(), (int)posesOut.size()); UWARN("Scan poses input and output are different! %d vs %d", (int)poses.size(), (int)posesOut.size());
UWARN("Input poses: ");
for(std::map<int, Transform>::iterator iter=poses.begin(); iter!=poses.end(); ++iter)
{
UWARN(" %d", iter->first);
}
UWARN("Input links: ");
std::multimap<int, Link> modifiedLinks = updateLinksWithModifications(links_);
for(std::multimap<int, Link>::iterator iter=modifiedLinks.begin(); iter!=modifiedLinks.end(); ++iter)
{
UWARN(" %d->%d", iter->second.from(), iter->second.to());
}
} }
UDEBUG("Input poses: ");
for(std::map<int, Transform>::iterator iter=posesOut.begin(); iter!=posesOut.end(); ++iter)
{
UDEBUG(" %d=%s", iter->first, iter->second.prettyPrint().c_str());
}
UDEBUG("Input links: ");
for(std::multimap<int, Link>::iterator iter=linksOut.begin(); iter!=linksOut.end(); ++iter)
{
UDEBUG(" %d->%d (type=%s) %s", iter->second.from(), iter->second.to(), iter->second.typeName().c_str(), iter->second.transform().prettyPrint().c_str());
}
QTime time;
time.start();
std::map<int, rtabmap::Transform> finalPoses = optimizer->optimize(link.to(), posesOut, linksOut); std::map<int, rtabmap::Transform> finalPoses = optimizer->optimize(link.to(), posesOut, linksOut);
delete optimizer; delete optimizer;
UDEBUG("Output poses: ");
for(std::map<int, Transform>::iterator iter=finalPoses.begin(); iter!=finalPoses.end(); ++iter)
{
UDEBUG(" %d=%s", iter->first, iter->second.prettyPrint().c_str());
}
// transform local poses in loop referential // transform local poses in loop referential
Transform u = t * finalPoses.at(link.to()).inverse(); Transform u = t * finalPoses.at(link.to()).inverse();
pcl::PointCloud<pcl::PointXYZ>::Ptr assembledScans(new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr assembledScans(new pcl::PointCloud<pcl::PointXYZ>);