mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 01:20:25 +08:00
Rtabmap::detectorMoreLoopClosures() added intraSession and interSession parameters
This commit is contained in:
@@ -57,6 +57,8 @@ public:
|
||||
double clusterRadius() const;
|
||||
double clusterAngle() const;
|
||||
int iterations() const;
|
||||
bool intraSession() const;
|
||||
bool interSession() const;
|
||||
bool isRefineNeighborLinks() const;
|
||||
bool isRefineLoopClosureLinks() const;
|
||||
bool isSBA() const;
|
||||
@@ -70,6 +72,8 @@ public:
|
||||
void setClusterRadius(double radius);
|
||||
void setClusterAngle(double angle);
|
||||
void setIterations(int iterations);
|
||||
void setIntraSession(bool enabled);
|
||||
void setInterSession(bool enabled);
|
||||
void setRefineNeighborLinks(bool on);
|
||||
void setRefineLoopClosureLinks(bool on);
|
||||
void setSBA(bool on);
|
||||
@@ -82,12 +86,15 @@ Q_SIGNALS:
|
||||
void configChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
void closeDialog ( QAbstractButton * button );
|
||||
void restoreDefaults();
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateVisibility();
|
||||
void updateButtonBox();
|
||||
|
||||
private:
|
||||
bool validateForm();
|
||||
|
||||
private:
|
||||
Ui_PostProcessingDialog * _ui;
|
||||
|
||||
@@ -391,6 +391,8 @@ DatabaseViewer::DatabaseViewer(const QString & ini, QWidget * parent) :
|
||||
connect(ui_->doubleSpinBox_detectMore_radius, SIGNAL(valueChanged(double)), this, SLOT(configModified()));
|
||||
connect(ui_->doubleSpinBox_detectMore_angle, SIGNAL(valueChanged(double)), this, SLOT(configModified()));
|
||||
connect(ui_->spinBox_detectMore_iterations, SIGNAL(valueChanged(int)), this, SLOT(configModified()));
|
||||
connect(ui_->checkBox_detectMore_intraSession, SIGNAL(stateChanged(int)), this, SLOT(configModified()));
|
||||
connect(ui_->checkBox_detectMore_interSession, SIGNAL(stateChanged(int)), this, SLOT(configModified()));
|
||||
|
||||
connect(ui_->lineEdit_obstacleColor, SIGNAL(textChanged(const QString &)), this, SLOT(configModified()));
|
||||
connect(ui_->lineEdit_groundColor, SIGNAL(textChanged(const QString &)), this, SLOT(configModified()));
|
||||
@@ -544,6 +546,8 @@ void DatabaseViewer::readSettings()
|
||||
ui_->doubleSpinBox_detectMore_radius->setValue(settings.value("cluster_radius", ui_->doubleSpinBox_detectMore_radius->value()).toDouble());
|
||||
ui_->doubleSpinBox_detectMore_angle->setValue(settings.value("cluster_angle", ui_->doubleSpinBox_detectMore_angle->value()).toDouble());
|
||||
ui_->spinBox_detectMore_iterations->setValue(settings.value("iterations", ui_->spinBox_detectMore_iterations->value()).toInt());
|
||||
ui_->checkBox_detectMore_intraSession->setChecked(settings.value("intra_session", ui_->checkBox_detectMore_intraSession->isChecked()).toBool());
|
||||
ui_->checkBox_detectMore_interSession->setChecked(settings.value("inter_session", ui_->checkBox_detectMore_interSession->isChecked()).toBool());
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
|
||||
@@ -626,6 +630,8 @@ void DatabaseViewer::writeSettings()
|
||||
settings.setValue("cluster_radius", ui_->doubleSpinBox_detectMore_radius->value());
|
||||
settings.setValue("cluster_angle", ui_->doubleSpinBox_detectMore_angle->value());
|
||||
settings.setValue("iterations", ui_->spinBox_detectMore_iterations->value());
|
||||
settings.setValue("intra_session", ui_->checkBox_detectMore_intraSession->isChecked());
|
||||
settings.setValue("inter_session", ui_->checkBox_detectMore_interSession->isChecked());
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
|
||||
@@ -693,6 +699,8 @@ void DatabaseViewer::restoreDefaultSettings()
|
||||
ui_->doubleSpinBox_detectMore_radius->setValue(1.0);
|
||||
ui_->doubleSpinBox_detectMore_angle->setValue(30.0);
|
||||
ui_->spinBox_detectMore_iterations->setValue(5);
|
||||
ui_->checkBox_detectMore_intraSession->setChecked(true);
|
||||
ui_->checkBox_detectMore_interSession->setChecked(true);
|
||||
}
|
||||
|
||||
void DatabaseViewer::openDatabase()
|
||||
@@ -3234,6 +3242,13 @@ void DatabaseViewer::detectMoreLoopClosures()
|
||||
int added = 0;
|
||||
std::multimap<int, int> checkedLoopClosures;
|
||||
std::pair<int, int> lastAdded(0,0);
|
||||
bool intraSession = ui_->checkBox_detectMore_intraSession->isChecked();
|
||||
bool interSession = ui_->checkBox_detectMore_interSession->isChecked();
|
||||
if(!interSession && !intraSession)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Cannot detect more loop closures"), tr("Intra and inter session parameters are disabled! Enable one or both."));
|
||||
return;
|
||||
}
|
||||
for(int n=0; n<iterations; ++n)
|
||||
{
|
||||
UINFO("iteration %d/%d", n+1, iterations);
|
||||
@@ -3262,24 +3277,32 @@ void DatabaseViewer::detectMoreLoopClosures()
|
||||
to = iter->first;
|
||||
}
|
||||
|
||||
// only add new links and one per cluster per iteration
|
||||
if(rtabmap::graph::findLink(checkedLoopClosures, from, to) == checkedLoopClosures.end())
|
||||
{
|
||||
if(!findActiveLink(from, to).isValid() && !containsLink(linksRemoved_, from, to) &&
|
||||
addedLinks.find(from) == addedLinks.end() && addedLinks.find(to) == addedLinks.end())
|
||||
{
|
||||
checkedLoopClosures.insert(std::make_pair(from, to));
|
||||
if(addConstraint(from, to, true))
|
||||
{
|
||||
UINFO("Added new loop closure between %d and %d.", from, to);
|
||||
++added;
|
||||
addedLinks.insert(from);
|
||||
addedLinks.insert(to);
|
||||
lastAdded.first = from;
|
||||
lastAdded.second = to;
|
||||
int mapIdFrom = uValue(mapIds_, from, 0);
|
||||
int mapIdTo = uValue(mapIds_, to, 0);
|
||||
|
||||
progressDialog->appendText(tr("Detected loop closure %1->%2! (%3/%4)").arg(from).arg(to).arg(i+1).arg(clusters.size()));
|
||||
QApplication::processEvents();
|
||||
if((interSession && mapIdFrom != mapIdTo) ||
|
||||
(intraSession && mapIdFrom == mapIdTo))
|
||||
{
|
||||
// only add new links and one per cluster per iteration
|
||||
if(rtabmap::graph::findLink(checkedLoopClosures, from, to) == checkedLoopClosures.end())
|
||||
{
|
||||
if(!findActiveLink(from, to).isValid() && !containsLink(linksRemoved_, from, to) &&
|
||||
addedLinks.find(from) == addedLinks.end() &&
|
||||
addedLinks.find(to) == addedLinks.end())
|
||||
{
|
||||
checkedLoopClosures.insert(std::make_pair(from, to));
|
||||
if(addConstraint(from, to, true))
|
||||
{
|
||||
UINFO("Added new loop closure between %d and %d.", from, to);
|
||||
++added;
|
||||
addedLinks.insert(from);
|
||||
addedLinks.insert(to);
|
||||
lastAdded.first = from;
|
||||
lastAdded.second = to;
|
||||
|
||||
progressDialog->appendText(tr("Detected loop closure %1->%2! (%3/%4)").arg(from).arg(to).arg(i+1).arg(clusters.size()));
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5490,6 +5490,8 @@ void MainWindow::postProcessing()
|
||||
}
|
||||
|
||||
UASSERT(detectLoopClosureIterations>0);
|
||||
bool interSession = _postProcessingDialog->interSession();
|
||||
bool intraSession = _postProcessingDialog->intraSession();
|
||||
for(int n=0; n<detectLoopClosureIterations && !_progressCanceled; ++n)
|
||||
{
|
||||
_progressDialog->appendText(tr("Looking for more loop closures, clustering poses... (iteration=%1/%2, radius=%3 m angle=%4 degrees)")
|
||||
@@ -5516,207 +5518,215 @@ void MainWindow::postProcessing()
|
||||
to = iter->first;
|
||||
}
|
||||
|
||||
bool alreadyChecked = false;
|
||||
for(std::multimap<int, int>::iterator jter = checkedLoopClosures.lower_bound(from);
|
||||
!alreadyChecked && jter!=checkedLoopClosures.end() && jter->first == from;
|
||||
++jter)
|
||||
int mapIdFrom = uValue(_currentMapIds, from, 0);
|
||||
int mapIdTo = uValue(_currentMapIds, to, 0);
|
||||
|
||||
if((interSession && mapIdFrom != mapIdTo) ||
|
||||
(intraSession && mapIdFrom == mapIdTo))
|
||||
{
|
||||
if(to == jter->second)
|
||||
bool alreadyChecked = false;
|
||||
for(std::multimap<int, int>::iterator jter = checkedLoopClosures.lower_bound(from);
|
||||
!alreadyChecked && jter!=checkedLoopClosures.end() && jter->first == from;
|
||||
++jter)
|
||||
{
|
||||
alreadyChecked = true;
|
||||
if(to == jter->second)
|
||||
{
|
||||
alreadyChecked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!alreadyChecked)
|
||||
{
|
||||
// only add new links and one per cluster per iteration
|
||||
if(addedLinks.find(from) == addedLinks.end() &&
|
||||
rtabmap::graph::findLink(_currentLinksMap, from, to) == _currentLinksMap.end())
|
||||
if(!alreadyChecked)
|
||||
{
|
||||
checkedLoopClosures.insert(std::make_pair(from, to));
|
||||
// only add new links and one per cluster per iteration
|
||||
if(addedLinks.find(from) == addedLinks.end() &&
|
||||
addedLinks.find(to) == addedLinks.end() &&
|
||||
rtabmap::graph::findLink(_currentLinksMap, from, to) == _currentLinksMap.end())
|
||||
{
|
||||
checkedLoopClosures.insert(std::make_pair(from, to));
|
||||
|
||||
if(!_cachedSignatures.contains(from))
|
||||
{
|
||||
UERROR("Didn't find signature %d", from);
|
||||
}
|
||||
else if(!_cachedSignatures.contains(to))
|
||||
{
|
||||
UERROR("Didn't find signature %d", to);
|
||||
}
|
||||
else
|
||||
{
|
||||
Signature signatureFrom = _cachedSignatures[from];
|
||||
Signature signatureTo = _cachedSignatures[to];
|
||||
|
||||
if(signatureFrom.getWeight() >= 0 &&
|
||||
signatureTo.getWeight() >= 0) // ignore intermediate nodes
|
||||
if(!_cachedSignatures.contains(from))
|
||||
{
|
||||
Transform transform;
|
||||
RegistrationInfo info;
|
||||
if(parameters.find(Parameters::kRegStrategy()) != parameters.end() &&
|
||||
parameters.at(Parameters::kRegStrategy()).compare("1") == 0)
|
||||
{
|
||||
uInsert(parameters, ParametersPair(Parameters::kRegStrategy(), "2"));
|
||||
}
|
||||
Registration * registration = Registration::create(parameters);
|
||||
UERROR("Didn't find signature %d", from);
|
||||
}
|
||||
else if(!_cachedSignatures.contains(to))
|
||||
{
|
||||
UERROR("Didn't find signature %d", to);
|
||||
}
|
||||
else
|
||||
{
|
||||
Signature signatureFrom = _cachedSignatures[from];
|
||||
Signature signatureTo = _cachedSignatures[to];
|
||||
|
||||
if(reextractFeatures)
|
||||
if(signatureFrom.getWeight() >= 0 &&
|
||||
signatureTo.getWeight() >= 0) // ignore intermediate nodes
|
||||
{
|
||||
signatureFrom.sensorData().uncompressData();
|
||||
signatureTo.sensorData().uncompressData();
|
||||
Transform transform;
|
||||
RegistrationInfo info;
|
||||
if(parameters.find(Parameters::kRegStrategy()) != parameters.end() &&
|
||||
parameters.at(Parameters::kRegStrategy()).compare("1") == 0)
|
||||
{
|
||||
uInsert(parameters, ParametersPair(Parameters::kRegStrategy(), "2"));
|
||||
}
|
||||
Registration * registration = Registration::create(parameters);
|
||||
|
||||
if(signatureFrom.sensorData().imageRaw().empty() &&
|
||||
signatureTo.sensorData().imageRaw().empty())
|
||||
if(reextractFeatures)
|
||||
{
|
||||
UWARN("\"%s\" is false and signatures (%d and %d) don't have raw "
|
||||
"images. Update the cache.",
|
||||
Parameters::kRGBDLoopClosureReextractFeatures().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
signatureFrom.setWords(std::multimap<int, cv::KeyPoint>());
|
||||
signatureFrom.setWords3(std::multimap<int, cv::Point3f>());
|
||||
signatureFrom.setWordsDescriptors(std::multimap<int, cv::Mat>());
|
||||
signatureFrom.sensorData().setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
|
||||
signatureTo.setWords(std::multimap<int, cv::KeyPoint>());
|
||||
signatureTo.setWords3(std::multimap<int, cv::Point3f>());
|
||||
signatureTo.setWordsDescriptors(std::multimap<int, cv::Mat>());
|
||||
signatureTo.sensorData().setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
|
||||
}
|
||||
}
|
||||
else if(!reextractFeatures && signatureFrom.getWords().empty() && signatureTo.getWords().empty())
|
||||
{
|
||||
UWARN("\"%s\" is false and signatures (%d and %d) don't have words, "
|
||||
"registration will not be possible. Set \"%s\" to true.",
|
||||
Parameters::kRGBDLoopClosureReextractFeatures().c_str(),
|
||||
signatureFrom.id(),
|
||||
signatureTo.id(),
|
||||
Parameters::kRGBDLoopClosureReextractFeatures().c_str());
|
||||
}
|
||||
transform = registration->computeTransformation(signatureFrom, signatureTo, Transform(), &info);
|
||||
delete registration;
|
||||
if(!transform.isNull())
|
||||
{
|
||||
//optimize the graph to see if the new constraint is globally valid
|
||||
bool updateConstraint = true;
|
||||
cv::Mat information = info.covariance.inv();
|
||||
if(odomMaxInf.size() == 6 && information.cols==6 && information.rows==6)
|
||||
{
|
||||
for(int i=0; i<6; ++i)
|
||||
signatureFrom.sensorData().uncompressData();
|
||||
signatureTo.sensorData().uncompressData();
|
||||
|
||||
if(signatureFrom.sensorData().imageRaw().empty() &&
|
||||
signatureTo.sensorData().imageRaw().empty())
|
||||
{
|
||||
if(information.at<double>(i,i) > odomMaxInf[i])
|
||||
{
|
||||
information.at<double>(i,i) = odomMaxInf[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(optimizeMaxError > 0.0f && optimizeIterations > 0)
|
||||
{
|
||||
int fromId = from;
|
||||
int mapId = _currentMapIds.at(from);
|
||||
// use first node of the map containing from
|
||||
for(std::map<int, int>::iterator iter=_currentMapIds.begin(); iter!=_currentMapIds.end(); ++iter)
|
||||
{
|
||||
if(iter->second == mapId && odomPoses.find(iter->first)!=odomPoses.end())
|
||||
{
|
||||
fromId = iter->first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::multimap<int, Link> linksIn = _currentLinksMap;
|
||||
linksIn.insert(std::make_pair(from, Link(from, to, Link::kUserClosure, transform, information)));
|
||||
const Link * maxLinearLink = 0;
|
||||
const Link * maxAngularLink = 0;
|
||||
float maxLinearError = 0.0f;
|
||||
float maxAngularError = 0.0f;
|
||||
std::map<int, Transform> poses;
|
||||
std::multimap<int, Link> links;
|
||||
UASSERT(odomPoses.find(fromId) != odomPoses.end());
|
||||
UASSERT_MSG(odomPoses.find(from) != odomPoses.end(), uFormat("id=%d poses=%d links=%d", from, (int)poses.size(), (int)links.size()).c_str());
|
||||
UASSERT_MSG(odomPoses.find(to) != odomPoses.end(), uFormat("id=%d poses=%d links=%d", to, (int)poses.size(), (int)links.size()).c_str());
|
||||
optimizer->getConnectedGraph(fromId, odomPoses, linksIn, poses, links);
|
||||
UASSERT(poses.find(fromId) != poses.end());
|
||||
UASSERT_MSG(poses.find(from) != poses.end(), uFormat("id=%d poses=%d links=%d", from, (int)poses.size(), (int)links.size()).c_str());
|
||||
UASSERT_MSG(poses.find(to) != poses.end(), uFormat("id=%d poses=%d links=%d", to, (int)poses.size(), (int)links.size()).c_str());
|
||||
UASSERT(graph::findLink(links, from, to) != links.end());
|
||||
poses = optimizer->optimize(fromId, poses, links);
|
||||
std::string msg;
|
||||
if(poses.size())
|
||||
{
|
||||
float maxLinearErrorRatio = 0.0f;
|
||||
float maxAngularErrorRatio = 0.0f;
|
||||
graph::computeMaxGraphErrors(
|
||||
poses,
|
||||
links,
|
||||
maxLinearErrorRatio,
|
||||
maxAngularErrorRatio,
|
||||
maxLinearError,
|
||||
maxAngularError,
|
||||
&maxLinearLink,
|
||||
&maxAngularLink);
|
||||
if(maxLinearLink)
|
||||
{
|
||||
UINFO("Max optimization linear error = %f m (link %d->%d)", maxLinearError, maxLinearLink->from(), maxLinearLink->to());
|
||||
if(maxLinearErrorRatio > optimizeMaxError)
|
||||
{
|
||||
msg = uFormat("Rejecting edge %d->%d because "
|
||||
"graph error is too large after optimization (%f m for edge %d->%d with ratio %f > std=%f m). "
|
||||
"\"%s\" is %f.",
|
||||
from,
|
||||
to,
|
||||
maxLinearError,
|
||||
maxLinearLink->from(),
|
||||
maxLinearLink->to(),
|
||||
maxLinearErrorRatio,
|
||||
sqrt(maxLinearLink->transVariance()),
|
||||
Parameters::kRGBDOptimizeMaxError().c_str(),
|
||||
optimizeMaxError);
|
||||
}
|
||||
}
|
||||
else if(maxAngularLink)
|
||||
{
|
||||
UINFO("Max optimization angular error = %f deg (link %d->%d)", maxAngularError*180.0f/M_PI, maxAngularLink->from(), maxAngularLink->to());
|
||||
if(maxAngularErrorRatio > optimizeMaxError)
|
||||
{
|
||||
msg = uFormat("Rejecting edge %d->%d because "
|
||||
"graph error is too large after optimization (%f deg for edge %d->%d with ratio %f > std=%f deg). "
|
||||
"\"%s\" is %f m.",
|
||||
from,
|
||||
to,
|
||||
maxAngularError*180.0f/M_PI,
|
||||
maxAngularLink->from(),
|
||||
maxAngularLink->to(),
|
||||
maxAngularErrorRatio,
|
||||
sqrt(maxAngularLink->rotVariance()),
|
||||
Parameters::kRGBDOptimizeMaxError().c_str(),
|
||||
optimizeMaxError);
|
||||
}
|
||||
}
|
||||
UWARN("\"%s\" is false and signatures (%d and %d) don't have raw "
|
||||
"images. Update the cache.",
|
||||
Parameters::kRGBDLoopClosureReextractFeatures().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = uFormat("Rejecting edge %d->%d because graph optimization has failed!",
|
||||
from,
|
||||
to);
|
||||
}
|
||||
if(!msg.empty())
|
||||
{
|
||||
UWARN("%s", msg.c_str());
|
||||
_progressDialog->appendText(tr("%1").arg(msg.c_str()));
|
||||
QApplication::processEvents();
|
||||
updateConstraint = false;
|
||||
signatureFrom.setWords(std::multimap<int, cv::KeyPoint>());
|
||||
signatureFrom.setWords3(std::multimap<int, cv::Point3f>());
|
||||
signatureFrom.setWordsDescriptors(std::multimap<int, cv::Mat>());
|
||||
signatureFrom.sensorData().setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
|
||||
signatureTo.setWords(std::multimap<int, cv::KeyPoint>());
|
||||
signatureTo.setWords3(std::multimap<int, cv::Point3f>());
|
||||
signatureTo.setWordsDescriptors(std::multimap<int, cv::Mat>());
|
||||
signatureTo.sensorData().setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
|
||||
}
|
||||
}
|
||||
|
||||
if(updateConstraint)
|
||||
else if(!reextractFeatures && signatureFrom.getWords().empty() && signatureTo.getWords().empty())
|
||||
{
|
||||
UINFO("Added new loop closure between %d and %d.", from, to);
|
||||
addedLinks.insert(from);
|
||||
addedLinks.insert(to);
|
||||
UWARN("\"%s\" is false and signatures (%d and %d) don't have words, "
|
||||
"registration will not be possible. Set \"%s\" to true.",
|
||||
Parameters::kRGBDLoopClosureReextractFeatures().c_str(),
|
||||
signatureFrom.id(),
|
||||
signatureTo.id(),
|
||||
Parameters::kRGBDLoopClosureReextractFeatures().c_str());
|
||||
}
|
||||
transform = registration->computeTransformation(signatureFrom, signatureTo, Transform(), &info);
|
||||
delete registration;
|
||||
if(!transform.isNull())
|
||||
{
|
||||
//optimize the graph to see if the new constraint is globally valid
|
||||
bool updateConstraint = true;
|
||||
cv::Mat information = info.covariance.inv();
|
||||
if(odomMaxInf.size() == 6 && information.cols==6 && information.rows==6)
|
||||
{
|
||||
for(int i=0; i<6; ++i)
|
||||
{
|
||||
if(information.at<double>(i,i) > odomMaxInf[i])
|
||||
{
|
||||
information.at<double>(i,i) = odomMaxInf[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(optimizeMaxError > 0.0f && optimizeIterations > 0)
|
||||
{
|
||||
int fromId = from;
|
||||
int mapId = _currentMapIds.at(from);
|
||||
// use first node of the map containing from
|
||||
for(std::map<int, int>::iterator iter=_currentMapIds.begin(); iter!=_currentMapIds.end(); ++iter)
|
||||
{
|
||||
if(iter->second == mapId && odomPoses.find(iter->first)!=odomPoses.end())
|
||||
{
|
||||
fromId = iter->first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::multimap<int, Link> linksIn = _currentLinksMap;
|
||||
linksIn.insert(std::make_pair(from, Link(from, to, Link::kUserClosure, transform, information)));
|
||||
const Link * maxLinearLink = 0;
|
||||
const Link * maxAngularLink = 0;
|
||||
float maxLinearError = 0.0f;
|
||||
float maxAngularError = 0.0f;
|
||||
std::map<int, Transform> poses;
|
||||
std::multimap<int, Link> links;
|
||||
UASSERT(odomPoses.find(fromId) != odomPoses.end());
|
||||
UASSERT_MSG(odomPoses.find(from) != odomPoses.end(), uFormat("id=%d poses=%d links=%d", from, (int)poses.size(), (int)links.size()).c_str());
|
||||
UASSERT_MSG(odomPoses.find(to) != odomPoses.end(), uFormat("id=%d poses=%d links=%d", to, (int)poses.size(), (int)links.size()).c_str());
|
||||
optimizer->getConnectedGraph(fromId, odomPoses, linksIn, poses, links);
|
||||
UASSERT(poses.find(fromId) != poses.end());
|
||||
UASSERT_MSG(poses.find(from) != poses.end(), uFormat("id=%d poses=%d links=%d", from, (int)poses.size(), (int)links.size()).c_str());
|
||||
UASSERT_MSG(poses.find(to) != poses.end(), uFormat("id=%d poses=%d links=%d", to, (int)poses.size(), (int)links.size()).c_str());
|
||||
UASSERT(graph::findLink(links, from, to) != links.end());
|
||||
poses = optimizer->optimize(fromId, poses, links);
|
||||
std::string msg;
|
||||
if(poses.size())
|
||||
{
|
||||
float maxLinearErrorRatio = 0.0f;
|
||||
float maxAngularErrorRatio = 0.0f;
|
||||
graph::computeMaxGraphErrors(
|
||||
poses,
|
||||
links,
|
||||
maxLinearErrorRatio,
|
||||
maxAngularErrorRatio,
|
||||
maxLinearError,
|
||||
maxAngularError,
|
||||
&maxLinearLink,
|
||||
&maxAngularLink);
|
||||
if(maxLinearLink)
|
||||
{
|
||||
UINFO("Max optimization linear error = %f m (link %d->%d)", maxLinearError, maxLinearLink->from(), maxLinearLink->to());
|
||||
if(maxLinearErrorRatio > optimizeMaxError)
|
||||
{
|
||||
msg = uFormat("Rejecting edge %d->%d because "
|
||||
"graph error is too large after optimization (%f m for edge %d->%d with ratio %f > std=%f m). "
|
||||
"\"%s\" is %f.",
|
||||
from,
|
||||
to,
|
||||
maxLinearError,
|
||||
maxLinearLink->from(),
|
||||
maxLinearLink->to(),
|
||||
maxLinearErrorRatio,
|
||||
sqrt(maxLinearLink->transVariance()),
|
||||
Parameters::kRGBDOptimizeMaxError().c_str(),
|
||||
optimizeMaxError);
|
||||
}
|
||||
}
|
||||
else if(maxAngularLink)
|
||||
{
|
||||
UINFO("Max optimization angular error = %f deg (link %d->%d)", maxAngularError*180.0f/M_PI, maxAngularLink->from(), maxAngularLink->to());
|
||||
if(maxAngularErrorRatio > optimizeMaxError)
|
||||
{
|
||||
msg = uFormat("Rejecting edge %d->%d because "
|
||||
"graph error is too large after optimization (%f deg for edge %d->%d with ratio %f > std=%f deg). "
|
||||
"\"%s\" is %f m.",
|
||||
from,
|
||||
to,
|
||||
maxAngularError*180.0f/M_PI,
|
||||
maxAngularLink->from(),
|
||||
maxAngularLink->to(),
|
||||
maxAngularErrorRatio,
|
||||
sqrt(maxAngularLink->rotVariance()),
|
||||
Parameters::kRGBDOptimizeMaxError().c_str(),
|
||||
optimizeMaxError);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = uFormat("Rejecting edge %d->%d because graph optimization has failed!",
|
||||
from,
|
||||
to);
|
||||
}
|
||||
if(!msg.empty())
|
||||
{
|
||||
UWARN("%s", msg.c_str());
|
||||
_progressDialog->appendText(tr("%1").arg(msg.c_str()));
|
||||
QApplication::processEvents();
|
||||
updateConstraint = false;
|
||||
}
|
||||
}
|
||||
|
||||
_currentLinksMap.insert(std::make_pair(from, Link(from, to, Link::kUserClosure, transform, information)));
|
||||
++loopClosuresAdded;
|
||||
_progressDialog->appendText(tr("Detected loop closure %1->%2! (%3/%4)").arg(from).arg(to).arg(i+1).arg(clusters.size()));
|
||||
if(updateConstraint)
|
||||
{
|
||||
UINFO("Added new loop closure between %d and %d.", from, to);
|
||||
addedLinks.insert(from);
|
||||
addedLinks.insert(to);
|
||||
|
||||
_currentLinksMap.insert(std::make_pair(from, Link(from, to, Link::kUserClosure, transform, information)));
|
||||
++loopClosuresAdded;
|
||||
_progressDialog->appendText(tr("Detected loop closure %1->%2! (%3/%4)").arg(from).arg(to).arg(i+1).arg(clusters.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "ui_postProcessingDialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <rtabmap/core/Optimizer.h>
|
||||
|
||||
namespace rtabmap {
|
||||
@@ -57,6 +58,8 @@ PostProcessingDialog::PostProcessingDialog(QWidget * parent) :
|
||||
|
||||
restoreDefaults();
|
||||
|
||||
connect(_ui->buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(closeDialog(QAbstractButton *)));
|
||||
|
||||
connect(_ui->detectMoreLoopClosures, SIGNAL(clicked(bool)), this, SLOT(updateButtonBox()));
|
||||
connect(_ui->refineNeighborLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox()));
|
||||
connect(_ui->refineLoopClosureLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox()));
|
||||
@@ -67,6 +70,8 @@ PostProcessingDialog::PostProcessingDialog(QWidget * parent) :
|
||||
connect(_ui->clusterRadius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->clusterAngle, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->iterations, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->intraSession, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->interSession, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->refineNeighborLinks, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->refineLoopClosureLinks, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
|
||||
@@ -84,6 +89,43 @@ PostProcessingDialog::~PostProcessingDialog()
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void PostProcessingDialog::closeDialog ( QAbstractButton * button )
|
||||
{
|
||||
UDEBUG("");
|
||||
|
||||
QDialogButtonBox::ButtonRole role = _ui->buttonBox->buttonRole(button);
|
||||
switch(role)
|
||||
{
|
||||
case QDialogButtonBox::RejectRole:
|
||||
this->reject();
|
||||
break;
|
||||
|
||||
case QDialogButtonBox::AcceptRole:
|
||||
if(validateForm())
|
||||
{
|
||||
this->accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->reject();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool PostProcessingDialog::validateForm()
|
||||
{
|
||||
if(!this->intraSession() && !this->interSession())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Configuration error"), tr("Intra-session and inter-session parameters cannot be both disabled at the same time. Please select one (or both)."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PostProcessingDialog::updateVisibility()
|
||||
{
|
||||
_ui->sba_variance->setVisible(_ui->comboBox_sbaType->currentIndex() == 0);
|
||||
@@ -100,6 +142,8 @@ void PostProcessingDialog::saveSettings(QSettings & settings, const QString & gr
|
||||
settings.setValue("cluster_radius", this->clusterRadius());
|
||||
settings.setValue("cluster_angle", this->clusterAngle());
|
||||
settings.setValue("iterations", this->iterations());
|
||||
settings.setValue("intra_session", this->intraSession());
|
||||
settings.setValue("inter_session", this->interSession());
|
||||
settings.setValue("refine_neigbors", this->isRefineNeighborLinks());
|
||||
settings.setValue("refine_lc", this->isRefineLoopClosureLinks());
|
||||
settings.setValue("sba", this->isSBA());
|
||||
@@ -123,6 +167,8 @@ void PostProcessingDialog::loadSettings(QSettings & settings, const QString & gr
|
||||
this->setClusterRadius(settings.value("cluster_radius", this->clusterRadius()).toDouble());
|
||||
this->setClusterAngle(settings.value("cluster_angle", this->clusterAngle()).toDouble());
|
||||
this->setIterations(settings.value("iterations", this->iterations()).toInt());
|
||||
this->setIntraSession(settings.value("intra_session", this->intraSession()).toBool());
|
||||
this->setInterSession(settings.value("inter_session", this->interSession()).toBool());
|
||||
this->setRefineNeighborLinks(settings.value("refine_neigbors", this->isRefineNeighborLinks()).toBool());
|
||||
this->setRefineLoopClosureLinks(settings.value("refine_lc", this->isRefineLoopClosureLinks()).toBool());
|
||||
this->setSBA(settings.value("sba", this->isSBA()).toBool());
|
||||
@@ -143,6 +189,8 @@ void PostProcessingDialog::restoreDefaults()
|
||||
setClusterRadius(1);
|
||||
setClusterAngle(30);
|
||||
setIterations(5);
|
||||
setIntraSession(true);
|
||||
setInterSession(true);
|
||||
setRefineNeighborLinks(false);
|
||||
setRefineLoopClosureLinks(false);
|
||||
setSBA(false);
|
||||
@@ -178,6 +226,16 @@ int PostProcessingDialog::iterations() const
|
||||
return _ui->iterations->value();
|
||||
}
|
||||
|
||||
bool PostProcessingDialog::intraSession() const
|
||||
{
|
||||
return _ui->intraSession->isChecked();
|
||||
}
|
||||
|
||||
bool PostProcessingDialog::interSession() const
|
||||
{
|
||||
return _ui->interSession->isChecked();
|
||||
}
|
||||
|
||||
bool PostProcessingDialog::isRefineNeighborLinks() const
|
||||
{
|
||||
return _ui->refineNeighborLinks->isChecked();
|
||||
@@ -227,6 +285,14 @@ void PostProcessingDialog::setIterations(int iterations)
|
||||
{
|
||||
_ui->iterations->setValue(iterations);
|
||||
}
|
||||
void PostProcessingDialog::setIntraSession(bool enabled)
|
||||
{
|
||||
_ui->intraSession->setChecked(enabled);
|
||||
}
|
||||
void PostProcessingDialog::setInterSession(bool enabled)
|
||||
{
|
||||
_ui->interSession->setChecked(enabled);
|
||||
}
|
||||
void PostProcessingDialog::setRefineNeighborLinks(bool on)
|
||||
{
|
||||
_ui->refineNeighborLinks->setChecked(on);
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>287</width>
|
||||
<width>413</width>
|
||||
<height>288</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -287,7 +287,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>287</width>
|
||||
<width>412</width>
|
||||
<height>288</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -1291,7 +1291,7 @@
|
||||
<item>
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page_3">
|
||||
<property name="geometry">
|
||||
@@ -1459,7 +1459,7 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<y>-151</y>
|
||||
<width>519</width>
|
||||
<height>791</height>
|
||||
</rect>
|
||||
@@ -1994,14 +1994,21 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>205</width>
|
||||
<height>114</height>
|
||||
<width>310</width>
|
||||
<height>243</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Detect more loop closures</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_10" columnstretch="0,1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_32">
|
||||
<property name="text">
|
||||
<string>Intra-session</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_detectMore_radius">
|
||||
<property name="suffix">
|
||||
@@ -2074,7 +2081,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="5" column="0">
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@@ -2087,6 +2094,27 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label_34">
|
||||
<property name="text">
|
||||
<string>Inter-session</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_detectMore_intraSession">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_detectMore_interSession">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>552</width>
|
||||
<height>562</height>
|
||||
<height>633</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -110,6 +110,40 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Intra-session</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Inter-session</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="intraSession">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="interSession">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
Reference in New Issue
Block a user