mirror of
https://github.com/introlab/rtabmap_ros.git
synced 2026-10-03 16:27:46 +08:00
Update version 0.10.11:
-Added parameter "Mem/ReduceGraph" with new link type (NeighborMerged) -DatabaseViewer: add Info panel, fixed empty extracted database when "RGB images" is not checked -ConsoleWidget: added buffer size and refresh rate options -DBDriver: new database info getters -Graph: added reduceGraph() method, added Dijsktra computePath() with links only -Updated default parameters: RGBD/LocalLoopDetectionSpace=true, RGBD/LinearUpdate=0.1 and RGBD/AngularUpdate=0.1 -Goals are saved with label instead of ID if the target node has a label -DataRecorder: fixed saving incrementally the database (instead of only at the end) -MainWindow: When waypoints are used, resend the current goal (1 sec delay) if it has failed setting it
This commit is contained in:
+1
-1
@@ -20,7 +20,7 @@ SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
|
||||
#######################
|
||||
SET(RTABMAP_MAJOR_VERSION 0)
|
||||
SET(RTABMAP_MINOR_VERSION 10)
|
||||
SET(RTABMAP_PATCH_VERSION 10)
|
||||
SET(RTABMAP_PATCH_VERSION 11)
|
||||
SET(RTABMAP_VERSION
|
||||
${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION})
|
||||
|
||||
|
||||
@@ -98,6 +98,15 @@ public:
|
||||
bool isConnected() const;
|
||||
long getMemoryUsed() const; // In bytes
|
||||
std::string getDatabaseVersion() const;
|
||||
long getImagesMemoryUsed() const;
|
||||
long getDepthImagesMemoryUsed() const;
|
||||
long getLaserScansMemoryUsed() const;
|
||||
long getUserDataMemoryUsed() const;
|
||||
long getWordsMemoryUsed() const;
|
||||
int getLastNodesSize() const; // working memory
|
||||
int getLastDictionarySize() const; // working memory
|
||||
int getTotalNodesSize() const;
|
||||
int getTotalDictionarySize() const;
|
||||
|
||||
void executeNoResult(const std::string & sql) const;
|
||||
|
||||
@@ -130,6 +139,15 @@ private:
|
||||
virtual bool isConnectedQuery() const = 0;
|
||||
virtual long getMemoryUsedQuery() const = 0; // In bytes
|
||||
virtual bool getDatabaseVersionQuery(std::string & version) const = 0;
|
||||
virtual long getImagesMemoryUsedQuery() const = 0;
|
||||
virtual long getDepthImagesMemoryUsedQuery() const = 0;
|
||||
virtual long getLaserScansMemoryUsedQuery() const = 0;
|
||||
virtual long getUserDataMemoryUsedQuery() const = 0;
|
||||
virtual long getWordsMemoryUsedQuery() const = 0;
|
||||
virtual int getLastNodesSizeQuery() const = 0;
|
||||
virtual int getLastDictionarySizeQuery() const = 0;
|
||||
virtual int getTotalNodesSizeQuery() const = 0;
|
||||
virtual int getTotalDictionarySizeQuery() const = 0;
|
||||
|
||||
virtual void executeNoResultQuery(const std::string & sql) const = 0;
|
||||
|
||||
|
||||
@@ -244,19 +244,23 @@ bool RTABMAP_EXP exportPoses(
|
||||
std::multimap<int, Link>::iterator RTABMAP_EXP findLink(
|
||||
std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to);
|
||||
int to,
|
||||
bool checkBothWays = true);
|
||||
std::multimap<int, int>::iterator RTABMAP_EXP findLink(
|
||||
std::multimap<int, int> & links,
|
||||
int from,
|
||||
int to);
|
||||
int to,
|
||||
bool checkBothWays = true);
|
||||
std::multimap<int, Link>::const_iterator RTABMAP_EXP findLink(
|
||||
const std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to);
|
||||
int to,
|
||||
bool checkBothWays = true);
|
||||
std::multimap<int, int>::const_iterator RTABMAP_EXP findLink(
|
||||
const std::multimap<int, int> & links,
|
||||
int from,
|
||||
int to);
|
||||
int to,
|
||||
bool checkBothWays = true);
|
||||
|
||||
/**
|
||||
* Get only the the most recent or older poses in the defined radius.
|
||||
@@ -284,6 +288,12 @@ std::multimap<int, int> RTABMAP_EXP radiusPosesClustering(
|
||||
float radius,
|
||||
float angle);
|
||||
|
||||
void reduceGraph(
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
std::multimap<int, int> & hyperNodes, //<parent ID, child ID>
|
||||
std::multimap<int, Link> & hyperLinks);
|
||||
|
||||
/**
|
||||
* Perform A* path planning in the graph.
|
||||
* @param poses The graph's poses
|
||||
@@ -300,6 +310,23 @@ std::list<std::pair<int, Transform> > RTABMAP_EXP computePath(
|
||||
int to,
|
||||
bool updateNewCosts = false);
|
||||
|
||||
/**
|
||||
* Perform Dijkstra path planning in the graph.
|
||||
* @param poses The graph's poses
|
||||
* @param links The graph's links (from node id -> to node id)
|
||||
* @param from initial node
|
||||
* @param to final node
|
||||
* @param updateNewCosts Keep up-to-date costs while traversing the graph.
|
||||
* @param useSameCostForAllLinks Ignore distance between nodes
|
||||
* @return the path ids from id "from" to id "to" including initial and final nodes.
|
||||
*/
|
||||
std::list<int> RTABMAP_EXP computePath(
|
||||
const std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to,
|
||||
bool updateNewCosts = false,
|
||||
bool useSameCostForAllLinks = false);
|
||||
|
||||
/**
|
||||
* Perform Dijkstra path planning in the graph.
|
||||
* @param fromId initial node
|
||||
|
||||
@@ -38,7 +38,15 @@ namespace rtabmap {
|
||||
class RTABMAP_EXP Link
|
||||
{
|
||||
public:
|
||||
enum Type {kNeighbor, kGlobalClosure, kLocalSpaceClosure, kLocalTimeClosure, kUserClosure, kVirtualClosure, kUndef};
|
||||
enum Type {
|
||||
kNeighbor,
|
||||
kGlobalClosure,
|
||||
kLocalSpaceClosure,
|
||||
kLocalTimeClosure,
|
||||
kUserClosure,
|
||||
kVirtualClosure,
|
||||
kNeighborMerged,
|
||||
kUndef};
|
||||
Link();
|
||||
Link(int from,
|
||||
int to,
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
bool postInitClosingEvents = false);
|
||||
std::map<int, float> computeLikelihood(const Signature * signature,
|
||||
const std::list<int> & ids);
|
||||
int incrementMapId();
|
||||
int incrementMapId(std::map<int, int> * reducedIds = 0);
|
||||
void updateAge(int signatureId);
|
||||
|
||||
std::list<int> forget(const std::set<int> & ignoredIds = std::set<int>());
|
||||
@@ -155,6 +155,7 @@ public:
|
||||
bool isIDsGenerated() const {return _generateIds;}
|
||||
int getLastGlobalLoopClosureId() const {return _lastGlobalLoopClosureId;}
|
||||
const Feature2D * getFeature2D() const {return _feature2D;}
|
||||
bool isGraphReduced() const {return _reduceGraph;}
|
||||
|
||||
void setRoi(const std::string & roi);
|
||||
|
||||
@@ -197,7 +198,8 @@ private:
|
||||
void clear();
|
||||
void moveToTrash(Signature * s, bool keepLinkedToGraph = true, std::list<int> * deletedWords = 0);
|
||||
|
||||
void addSignatureToWm(Signature * signature);
|
||||
void moveSignatureToWMFromSTM(int id, int * reducedTo = 0);
|
||||
void addSignatureToWmFromLTM(Signature * signature);
|
||||
Signature * _getSignature(int id) const;
|
||||
std::list<Signature *> getRemovableSignatures(int count,
|
||||
const std::set<int> & ignoredIds = std::set<int>());
|
||||
@@ -231,6 +233,7 @@ private:
|
||||
bool _saveDepth16Format;
|
||||
bool _notLinkedNodesKeptInDb;
|
||||
bool _incrementalMemory;
|
||||
bool _reduceGraph;
|
||||
int _maxStMemSize;
|
||||
float _recentWmRatio;
|
||||
bool _transferSortingByWeightId;
|
||||
|
||||
@@ -189,7 +189,8 @@ class RTABMAP_EXP Parameters
|
||||
RTABMAP_PARAM(Mem, SaveDepth16Format, bool, true, "Save depth image into 16 bits format to reduce memory used. Warning: values over ~65 meters are ignored (maximum 65535 millimeters).");
|
||||
RTABMAP_PARAM(Mem, NotLinkedNodesKept, bool, true, "Keep not linked nodes in db (rehearsed nodes and deleted nodes).");
|
||||
RTABMAP_PARAM(Mem, STMSize, unsigned int, 10, "Short-term memory size.");
|
||||
RTABMAP_PARAM(Mem, IncrementalMemory, bool, true, "SLAM mode, otherwise it is Localization mode.");
|
||||
RTABMAP_PARAM(Mem, IncrementalMemory, bool, true, "SLAM mode, otherwise it is Localization mode.");
|
||||
RTABMAP_PARAM(Mem, ReduceGraph, bool, false, "Reduce graph. Merge nodes when loop closures are added (ignoring those with user data set).");
|
||||
RTABMAP_PARAM(Mem, RecentWmRatio, float, 0.2, "Ratio of locations after the last loop closure in WM that cannot be transferred.");
|
||||
RTABMAP_PARAM(Mem, TransferSortingByWeightId, bool, false, "On transfer, signatures are sorted by weight->ID only (i.e. the oldest of the lowest weighted signatures are transferred first). If false, the signatures are sorted by weight->Age->ID (i.e. the oldest inserted in WM of the lowest weighted signatures are transferred first). Note that retrieval updates the age, not the ID.");
|
||||
RTABMAP_PARAM(Mem, RehearsalIdUpdatedToNewOne, bool, false, "On merge, update to new id. When false, no copy.");
|
||||
@@ -286,8 +287,8 @@ class RTABMAP_EXP Parameters
|
||||
// RGB-D SLAM
|
||||
RTABMAP_PARAM(RGBD, Enabled, bool, true, "");
|
||||
RTABMAP_PARAM(RGBD, PoseScanMatching, bool, false, "Laser scan matching for odometry pose correction (laser scans are required).");
|
||||
RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.0, "Minimum linear displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||
RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.0, "Minimum angular displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||
RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.1, "Minimum linear displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||
RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.1, "Minimum angular displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||
RTABMAP_PARAM(RGBD, NewMapOdomChangeDistance, float, 0, "A new map is created if a change of odometry translation greater than X m is detected (0 m = disabled).");
|
||||
RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, false, "Optimize graph from the newest node. If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest mode of the current graph, but it can be useful to preserve the map referential from the oldest node). Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation).");
|
||||
RTABMAP_PARAM(RGBD, OptimizeMaxError, float, 1.0, "Reject loop closures if optimization error is greater than this value (0=disabled). This will help to detect when a wrong loop closure is added to the graph.");
|
||||
@@ -303,7 +304,7 @@ class RTABMAP_EXP Parameters
|
||||
|
||||
// Local loop closure detection
|
||||
RTABMAP_PARAM(RGBD, LocalLoopDetectionTime, bool, false, "Detection over all locations in STM.");
|
||||
RTABMAP_PARAM(RGBD, LocalLoopDetectionSpace, bool, false, "Detection over locations (in Working Memory or STM) near in space.");
|
||||
RTABMAP_PARAM(RGBD, LocalLoopDetectionSpace, bool, true, "Detection over locations (in Working Memory or STM) near in space.");
|
||||
RTABMAP_PARAM(RGBD, LocalLoopDetectionMaxGraphDepth, int, 50, "Maximum depth from the current/last loop closure location and the local loop closure hypotheses. Set 0 to ignore.");
|
||||
RTABMAP_PARAM(RGBD, LocalLoopDetectionPathFilteringRadius, float, 0.5, "Path filtering radius.");
|
||||
RTABMAP_PARAM(RGBD, LocalLoopDetectionPathOdomPosesUsed, bool, true, "When comparing to a local path, merge the scan using the odometry poses instead of the ones in the optimized local graph.");
|
||||
|
||||
@@ -154,6 +154,7 @@ public:
|
||||
void setRawLikelihood(const std::map<int, float> & rawLikelihood) {_rawLikelihood = rawLikelihood;}
|
||||
void setLocalPath(const std::vector<int> & localPath) {_localPath=localPath;}
|
||||
void setCurrentGoalId(int goal) {_currentGoalId=goal;}
|
||||
void setReducedIds(const std::map<int, int> & reducedIds) {_reducedIds = reducedIds;}
|
||||
|
||||
// getters
|
||||
bool extended() const {return _extended;}
|
||||
@@ -173,6 +174,7 @@ public:
|
||||
const std::map<int, float> & rawLikelihood() const {return _rawLikelihood;}
|
||||
const std::vector<int> & localPath() const {return _localPath;}
|
||||
int currentGoalId() const {return _currentGoalId;}
|
||||
const std::map<int, int> & reducedIds() const {return _reducedIds;}
|
||||
|
||||
const std::map<std::string, float> & data() const {return _data;}
|
||||
|
||||
@@ -198,6 +200,8 @@ private:
|
||||
std::vector<int> _localPath;
|
||||
int _currentGoalId;
|
||||
|
||||
std::map<int, int> _reducedIds;
|
||||
|
||||
// Format for statistics (Plottable statistics must go in that map) :
|
||||
// {"Group/Name/Unit", value}
|
||||
// Example : {"Timing/Total time/ms", 500.0f}
|
||||
|
||||
@@ -46,7 +46,13 @@ class FlannIndex;
|
||||
class RTABMAP_EXP VWDictionary
|
||||
{
|
||||
public:
|
||||
enum NNStrategy{kNNFlannNaive, kNNFlannKdTree, kNNFlannLSH, kNNBruteForce, kNNBruteForceGPU, kNNUndef};
|
||||
enum NNStrategy{
|
||||
kNNFlannNaive,
|
||||
kNNFlannKdTree,
|
||||
kNNFlannLSH,
|
||||
kNNBruteForce,
|
||||
kNNBruteForceGPU,
|
||||
kNNUndef};
|
||||
static const int ID_START;
|
||||
static const int ID_INVALID;
|
||||
|
||||
@@ -79,6 +85,7 @@ public:
|
||||
unsigned int getIndexMemoryUsed() const;
|
||||
void setNNStrategy(NNStrategy strategy);
|
||||
bool isIncremental() const {return _incrementalDictionary;}
|
||||
bool isIncrementalFlann() const {return _incrementalFlann;}
|
||||
void setIncrementalDictionary();
|
||||
void setFixedDictionary(const std::string & dictionaryPath);
|
||||
|
||||
|
||||
@@ -106,6 +106,79 @@ long DBDriver::getMemoryUsed() const
|
||||
return bytes;
|
||||
}
|
||||
|
||||
long DBDriver::getImagesMemoryUsed() const
|
||||
{
|
||||
long bytes;
|
||||
_dbSafeAccessMutex.lock();
|
||||
bytes = getImagesMemoryUsedQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return bytes;
|
||||
}
|
||||
long DBDriver::getDepthImagesMemoryUsed() const
|
||||
{
|
||||
long bytes;
|
||||
_dbSafeAccessMutex.lock();
|
||||
bytes = getDepthImagesMemoryUsedQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return bytes;
|
||||
}
|
||||
long DBDriver::getLaserScansMemoryUsed() const
|
||||
{
|
||||
long bytes;
|
||||
_dbSafeAccessMutex.lock();
|
||||
bytes = getLaserScansMemoryUsedQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return bytes;
|
||||
}
|
||||
long DBDriver::getUserDataMemoryUsed() const
|
||||
{
|
||||
long bytes;
|
||||
_dbSafeAccessMutex.lock();
|
||||
bytes = getUserDataMemoryUsedQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return bytes;
|
||||
}
|
||||
long DBDriver::getWordsMemoryUsed() const
|
||||
{
|
||||
long bytes;
|
||||
_dbSafeAccessMutex.lock();
|
||||
bytes = getWordsMemoryUsedQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return bytes;
|
||||
}
|
||||
int DBDriver::getLastNodesSize() const
|
||||
{
|
||||
int nodes;
|
||||
_dbSafeAccessMutex.lock();
|
||||
nodes = getLastNodesSizeQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return nodes;
|
||||
}
|
||||
int DBDriver::getLastDictionarySize() const
|
||||
{
|
||||
int words;
|
||||
_dbSafeAccessMutex.lock();
|
||||
words = getLastDictionarySizeQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return words;
|
||||
}
|
||||
int DBDriver::getTotalNodesSize() const
|
||||
{
|
||||
int words;
|
||||
_dbSafeAccessMutex.lock();
|
||||
words = getTotalNodesSizeQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return words;
|
||||
}
|
||||
int DBDriver::getTotalDictionarySize() const
|
||||
{
|
||||
int words;
|
||||
_dbSafeAccessMutex.lock();
|
||||
words = getTotalDictionarySizeQuery();
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return words;
|
||||
}
|
||||
|
||||
std::string DBDriver::getDatabaseVersion() const
|
||||
{
|
||||
std::string version = "0.0.0";
|
||||
@@ -473,7 +546,8 @@ void DBDriver::getNodeData(
|
||||
}
|
||||
}
|
||||
|
||||
bool DBDriver::getNodeInfo(int signatureId,
|
||||
bool DBDriver::getNodeInfo(
|
||||
int signatureId,
|
||||
Transform & pose,
|
||||
int & mapId,
|
||||
int & weight,
|
||||
@@ -568,7 +642,8 @@ void DBDriver::getAllNodeIds(std::set<int> & ids, bool ignoreChildren) const
|
||||
nIter!=sIter->second->getLinks().end();
|
||||
++nIter)
|
||||
{
|
||||
if(nIter->second.type() == Link::kNeighbor)
|
||||
if(nIter->second.type() == Link::kNeighbor ||
|
||||
nIter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
hasNeighbors = true;
|
||||
break;
|
||||
@@ -778,7 +853,8 @@ void DBDriver::generateGraph(
|
||||
|
||||
const char * colorG = "green";
|
||||
const char * colorP = "pink";
|
||||
; UINFO("Generating map with %d locations", ids.size());
|
||||
const char * colorNM = "blue";
|
||||
UINFO("Generating map with %d locations", ids.size());
|
||||
fprintf(fout, "digraph G {\n");
|
||||
for(std::set<int>::iterator i=ids.begin(); i!=ids.end(); ++i)
|
||||
{
|
||||
@@ -809,6 +885,16 @@ void DBDriver::generateGraph(
|
||||
iter->first,
|
||||
weightNeighbor);
|
||||
}
|
||||
else if(iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
//merged neighbor
|
||||
fprintf(fout, " \"%d\\n%d\" -> \"%d\\n%d\" [label=\"M\", fontcolor=%s, fontsize=8];\n",
|
||||
id,
|
||||
weight,
|
||||
iter->first,
|
||||
weightNeighbor,
|
||||
colorNM);
|
||||
}
|
||||
else if(iter->first > id)
|
||||
{
|
||||
//loop
|
||||
@@ -860,6 +946,15 @@ void DBDriver::generateGraph(
|
||||
iter->first,
|
||||
weightNeighbor);
|
||||
}
|
||||
else if(iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
fprintf(fout, " \"%d\\n%d\" -> \"%d\\n%d\" [label=\"M\", fontcolor=%s, fontsize=8];\n",
|
||||
id,
|
||||
weight,
|
||||
iter->first,
|
||||
weightNeighbor,
|
||||
colorNM);
|
||||
}
|
||||
else if(iter->first > id)
|
||||
{
|
||||
//loop
|
||||
|
||||
@@ -446,6 +446,259 @@ long DBDriverSqlite3::getMemoryUsedQuery() const
|
||||
}
|
||||
}
|
||||
|
||||
long DBDriverSqlite3::getImagesMemoryUsedQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
long size = 0L;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query;
|
||||
if(uStrNumCmp(_version, "0.10.1") >= 0)
|
||||
{
|
||||
query = "SELECT sum(length(image)) from Data;";
|
||||
}
|
||||
else
|
||||
{
|
||||
query = "SELECT sum(length(data)) from Image;";
|
||||
}
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int64(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
long DBDriverSqlite3::getDepthImagesMemoryUsedQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
long size = 0L;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query;
|
||||
if(uStrNumCmp(_version, "0.10.1") >= 0)
|
||||
{
|
||||
query = "SELECT sum(length(depth)) from Data;";
|
||||
}
|
||||
else
|
||||
{
|
||||
query = "SELECT sum(length(data)) from Depth;";
|
||||
}
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int64(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
long DBDriverSqlite3::getLaserScansMemoryUsedQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
long size = 0L;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query;
|
||||
if(uStrNumCmp(_version, "0.10.1") >= 0)
|
||||
{
|
||||
query = "SELECT sum(length(scan)) from Data;";
|
||||
}
|
||||
else
|
||||
{
|
||||
query = "SELECT sum(length(data2d)) from Depth;";
|
||||
}
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int64(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
long DBDriverSqlite3::getUserDataMemoryUsedQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
long size = 0L;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query;
|
||||
if(uStrNumCmp(_version, "0.10.1") >= 0)
|
||||
{
|
||||
query = "SELECT sum(length(user_data)) from Data;";
|
||||
}
|
||||
else if(uStrNumCmp(_version, "0.8.8") >= 0)
|
||||
{
|
||||
query = "SELECT sum(length(user_data)) from Node;";
|
||||
}
|
||||
else
|
||||
{
|
||||
return size; // no user_data
|
||||
}
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int64(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
long DBDriverSqlite3::getWordsMemoryUsedQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
long size = 0L;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query = "SELECT sum(length(descriptor)) from Word;";
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int64(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
int DBDriverSqlite3::getLastNodesSizeQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
int size = 0;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query = "SELECT count(id) from Node WHERE time_enter >= (SELECT MAX(time_enter) FROM Statistics);";
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
int DBDriverSqlite3::getLastDictionarySizeQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
int size = 0;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query = "SELECT count(id) from Word WHERE time_enter >= (SELECT MAX(time_enter) FROM Statistics);";
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
int DBDriverSqlite3::getTotalNodesSizeQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
int size = 0;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query = "SELECT count(id) from Node;";
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
int DBDriverSqlite3::getTotalDictionarySizeQuery() const
|
||||
{
|
||||
UDEBUG("");
|
||||
int size = 0;
|
||||
if(_ppDb)
|
||||
{
|
||||
std::string query = "SELECT count(id) from Word;";
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt * ppStmt = 0;
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_step(ppStmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
size = sqlite3_column_int(ppStmt, 0);
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
rc = sqlite3_finalize(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures) const
|
||||
{
|
||||
UDEBUG("load data for %d signatures", (int)signatures.size());
|
||||
@@ -830,7 +1083,7 @@ void DBDriverSqlite3::getAllNodeIdsQuery(std::set<int> & ids, bool ignoreChildre
|
||||
query << "SELECT id "
|
||||
<< "FROM Node "
|
||||
<< "INNER JOIN Link "
|
||||
<< "ON id = to_id " // use to_id tp ignore all children (which don't have link pointing on them)
|
||||
<< "ON id = to_id " // use to_id to ignore all children (which don't have link pointing on them)
|
||||
<< "ORDER BY id";
|
||||
}
|
||||
|
||||
@@ -2067,7 +2320,10 @@ void DBDriverSqlite3::saveQuery(const std::list<Signature *> & signatures) const
|
||||
|
||||
for(std::list<Signature *>::const_iterator i=signatures.begin(); i!=signatures.end(); ++i)
|
||||
{
|
||||
if(!(*i)->sensorData().imageCompressed().empty())
|
||||
if(!(*i)->sensorData().imageCompressed().empty() ||
|
||||
!(*i)->sensorData().depthOrRightCompressed().empty() ||
|
||||
!(*i)->sensorData().laserScanCompressed().empty() ||
|
||||
!(*i)->sensorData().userDataCompressed().empty())
|
||||
{
|
||||
UASSERT((*i)->id() == (*i)->sensorData().id());
|
||||
stepSensorData(ppStmt, (*i)->sensorData());
|
||||
@@ -2506,7 +2762,7 @@ void DBDriverSqlite3::stepSensorData(sqlite3_stmt * ppStmt,
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = sqlite3_bind_zeroblob(ppStmt, index++, 4);
|
||||
rc = sqlite3_bind_null(ppStmt, index++);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
|
||||
@@ -2517,7 +2773,7 @@ void DBDriverSqlite3::stepSensorData(sqlite3_stmt * ppStmt,
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = sqlite3_bind_zeroblob(ppStmt, index++, 4);
|
||||
rc = sqlite3_bind_null(ppStmt, index++);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
|
||||
@@ -2578,7 +2834,7 @@ void DBDriverSqlite3::stepSensorData(sqlite3_stmt * ppStmt,
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = sqlite3_bind_zeroblob(ppStmt, index++, 4);
|
||||
rc = sqlite3_bind_null(ppStmt, index++);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
|
||||
@@ -2591,7 +2847,7 @@ void DBDriverSqlite3::stepSensorData(sqlite3_stmt * ppStmt,
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = sqlite3_bind_zeroblob(ppStmt, index++, 4);
|
||||
rc = sqlite3_bind_null(ppStmt, index++);
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
|
||||
}
|
||||
|
||||
@@ -54,6 +54,15 @@ private:
|
||||
virtual bool isConnectedQuery() const;
|
||||
virtual long getMemoryUsedQuery() const; // In bytes
|
||||
virtual bool getDatabaseVersionQuery(std::string & version) const;
|
||||
virtual long getImagesMemoryUsedQuery() const;
|
||||
virtual long getDepthImagesMemoryUsedQuery() const;
|
||||
virtual long getLaserScansMemoryUsedQuery() const;
|
||||
virtual long getUserDataMemoryUsedQuery() const;
|
||||
virtual long getWordsMemoryUsedQuery() const;
|
||||
virtual int getLastNodesSizeQuery() const;
|
||||
virtual int getLastDictionarySizeQuery() const;
|
||||
virtual int getTotalNodesSizeQuery() const;
|
||||
virtual int getTotalDictionarySizeQuery() const;
|
||||
|
||||
virtual void executeNoResultQuery(const std::string & sql) const;
|
||||
|
||||
|
||||
+26
-14
@@ -157,12 +157,13 @@ void DBReader::mainLoop()
|
||||
OdometryEvent odom = this->getNextData();
|
||||
if(odom.data().id())
|
||||
{
|
||||
int goalId = 0;
|
||||
std::string goalId;
|
||||
double previousStamp = odom.data().stamp();
|
||||
if(previousStamp == 0)
|
||||
{
|
||||
odom.data().setStamp(UTimer::now());
|
||||
}
|
||||
|
||||
if(!_goalsIgnored &&
|
||||
odom.data().userDataRaw().type() == CV_8SC1 &&
|
||||
odom.data().userDataRaw().cols >= 7 && // including null str ending
|
||||
@@ -176,7 +177,7 @@ void DBReader::mainLoop()
|
||||
std::list<std::string> strs = uSplit(goalStr, ':');
|
||||
if(strs.size() == 2)
|
||||
{
|
||||
goalId = atoi(strs.rbegin()->c_str());
|
||||
goalId = *strs.rbegin();
|
||||
odom.data().setUserData(cv::Mat());
|
||||
}
|
||||
}
|
||||
@@ -196,8 +197,9 @@ void DBReader::mainLoop()
|
||||
this->post(new CameraEvent(odom.data()));
|
||||
}
|
||||
|
||||
if(goalId > 0)
|
||||
if(!goalId.empty())
|
||||
{
|
||||
double delay = 0.0;
|
||||
if(!_ignoreGoalDelay && _currentId != _ids.end())
|
||||
{
|
||||
// get stamp for the next signature to compute the delay
|
||||
@@ -210,23 +212,33 @@ void DBReader::mainLoop()
|
||||
_dbDriver->getNodeInfo(*_currentId, pose, mapId, weight, label, stamp);
|
||||
if(previousStamp && stamp && stamp > previousStamp)
|
||||
{
|
||||
double delay = stamp - previousStamp;
|
||||
UWARN("Goal %d detected, posting it! Waiting %f seconds before sending next data...",
|
||||
goalId, delay);
|
||||
this->post(new RtabmapEventCmd(RtabmapEventCmd::kCmdGoal, goalId));
|
||||
uSleep(delay*1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Goal %d detected, posting it!", goalId);
|
||||
this->post(new RtabmapEventCmd(RtabmapEventCmd::kCmdGoal, goalId));
|
||||
delay = stamp - previousStamp;
|
||||
}
|
||||
}
|
||||
|
||||
if(delay > 0.0)
|
||||
{
|
||||
UWARN("Goal \"%s\" detected, posting it! Waiting %f seconds before sending next data...",
|
||||
goalId.c_str(), delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Goal \"%s\" detected, posting it!", goalId.c_str());
|
||||
}
|
||||
|
||||
if(uIsInteger(goalId))
|
||||
{
|
||||
this->post(new RtabmapEventCmd(RtabmapEventCmd::kCmdGoal, atoi(goalId.c_str())));
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Goal %d detected, posting it!", goalId);
|
||||
this->post(new RtabmapEventCmd(RtabmapEventCmd::kCmdGoal, goalId));
|
||||
}
|
||||
|
||||
if(delay > 0.0)
|
||||
{
|
||||
uSleep(delay*1000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ void Feature2D::filterKeypointsByDepth(
|
||||
const cv::Mat & depth,
|
||||
float maxDepth)
|
||||
{
|
||||
if(!depth.empty() && maxDepth > 0.0f && (descriptors.empty() || descriptors.rows == (int)keypoints.size()))
|
||||
if(!depth.empty() && (descriptors.empty() || descriptors.rows == (int)keypoints.size()))
|
||||
{
|
||||
std::vector<cv::KeyPoint> output(keypoints.size());
|
||||
std::vector<int> indexes(keypoints.size(), 0);
|
||||
@@ -85,7 +85,7 @@ void Feature2D::filterKeypointsByDepth(
|
||||
if(u >=0 && u<depth.cols && v >=0 && v<depth.rows)
|
||||
{
|
||||
float d = isInMM?(float)depth.at<uint16_t>(v,u)*0.001f:depth.at<float>(v,u);
|
||||
if(d!=0.0f && uIsFinite(d) && d < maxDepth)
|
||||
if(uIsFinite(d) && d>0.0f && (maxDepth <= 0.0f || d < maxDepth))
|
||||
{
|
||||
output[oi++] = keypoints[i];
|
||||
indexes[i] = 1;
|
||||
|
||||
+363
-39
@@ -240,7 +240,9 @@ void Optimizer::getConnectedGraph(
|
||||
std::multimap<int, int> biLinks;
|
||||
for(std::multimap<int, Link>::const_iterator iter=linksIn.begin(); iter!=linksIn.end(); ++iter)
|
||||
{
|
||||
UASSERT_MSG(findLink(biLinks, iter->second.from(), iter->second.to()) == biLinks.end(), "Input links should be unique between two poses.");
|
||||
UASSERT_MSG(findLink(biLinks, iter->second.from(), iter->second.to()) == biLinks.end(),
|
||||
uFormat("Input links should be unique between two poses (%d->%d).",
|
||||
iter->second.from(), iter->second.to()).c_str());
|
||||
biLinks.insert(std::make_pair(iter->second.from(), iter->second.to()));
|
||||
biLinks.insert(std::make_pair(iter->second.to(), iter->second.from()));
|
||||
}
|
||||
@@ -396,12 +398,14 @@ std::map<int, Transform> TOROOptimizer::optimize(
|
||||
}
|
||||
}
|
||||
}
|
||||
UDEBUG("buildMST...");
|
||||
UDEBUG("buildMST... root=%d", rootId);
|
||||
UASSERT(uContains(poses, rootId));
|
||||
if(isSlam2d())
|
||||
{
|
||||
pg2.buildMST(rootId); // pg.buildSimpleTree();
|
||||
UDEBUG("initializeOnTree()");
|
||||
pg2.initializeOnTree();
|
||||
UDEBUG("initializeTreeParameters()");
|
||||
pg2.initializeTreeParameters();
|
||||
UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
|
||||
"TORO is not able to find the root of the graph!)");
|
||||
@@ -410,7 +414,9 @@ std::map<int, Transform> TOROOptimizer::optimize(
|
||||
else
|
||||
{
|
||||
pg3.buildMST(rootId); // pg.buildSimpleTree();
|
||||
UDEBUG("initializeOnTree()");
|
||||
pg3.initializeOnTree();
|
||||
UDEBUG("initializeTreeParameters()");
|
||||
pg3.initializeTreeParameters();
|
||||
UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
|
||||
"TORO is not able to find the root of the graph!)");
|
||||
@@ -799,7 +805,9 @@ std::map<int, Transform> G2OOptimizer::optimize(
|
||||
g2o::HyperGraph::Edge * edge = 0;
|
||||
|
||||
VertexSwitchLinear * v = 0;
|
||||
if(this->isRobust() && iter->second.type() != Link::kNeighbor)
|
||||
if(this->isRobust() &&
|
||||
iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
// For loop closure links, add switchable edges
|
||||
|
||||
@@ -840,7 +848,9 @@ std::map<int, Transform> G2OOptimizer::optimize(
|
||||
information(2,2) = iter->second.infMatrix().at<double>(5,5); // theta-theta
|
||||
}
|
||||
|
||||
if(this->isRobust() && iter->second.type() != Link::kNeighbor)
|
||||
if(this->isRobust() &&
|
||||
iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
EdgeSE2Switchable * e = new EdgeSE2Switchable();
|
||||
g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
|
||||
@@ -881,7 +891,9 @@ std::map<int, Transform> G2OOptimizer::optimize(
|
||||
constraint = a.rotation();
|
||||
constraint.translation() = a.translation();
|
||||
|
||||
if(this->isRobust() && iter->second.type() != Link::kNeighbor)
|
||||
if(this->isRobust() &&
|
||||
iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
EdgeSE3Switchable * e = new EdgeSE3Switchable();
|
||||
g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
|
||||
@@ -1079,7 +1091,9 @@ bool G2OOptimizer::saveGraph(
|
||||
std::string prefix = "EDGE_SE3:QUAT";
|
||||
std::string suffix = "";
|
||||
|
||||
if(useRobustConstraints && iter->second.type() != Link::kNeighbor)
|
||||
if(useRobustConstraints &&
|
||||
iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
prefix = "EDGE_SE3_SWITCHABLE";
|
||||
fprintf(file, "VERTEX_SWITCH %d 1\n", virtualVertexId);
|
||||
@@ -1196,7 +1210,9 @@ std::map<int, Transform> GTSAMOptimizer::optimize(
|
||||
|
||||
UASSERT(!iter->second.transform().isNull());
|
||||
|
||||
if(this->isRobust() && iter->second.type()!=Link::kNeighbor)
|
||||
if(this->isRobust() &&
|
||||
iter->second.type()!=Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
// create new switch variable
|
||||
// Sunderhauf IROS 2012:
|
||||
@@ -1233,7 +1249,9 @@ std::map<int, Transform> GTSAMOptimizer::optimize(
|
||||
}
|
||||
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
||||
|
||||
if(this->isRobust() && iter->second.type()!=Link::kNeighbor)
|
||||
if(this->isRobust() &&
|
||||
iter->second.type()!=Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
// 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));
|
||||
@@ -1255,7 +1273,9 @@ std::map<int, Transform> GTSAMOptimizer::optimize(
|
||||
|
||||
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
||||
|
||||
if(this->isRobust() && iter->second.type()!=Link::kNeighbor)
|
||||
if(this->isRobust() &&
|
||||
iter->second.type()!=Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
// create switchable edge factor
|
||||
graph.add(vertigo::BetweenFactorSwitchableLinear<gtsam::Pose3>(id1, id2, gtsam::Symbol('s', switchCounter++), gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||
@@ -1683,7 +1703,8 @@ bool exportPoses(
|
||||
std::multimap<int, Link>::iterator findLink(
|
||||
std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to)
|
||||
int to,
|
||||
bool checkBothWays)
|
||||
{
|
||||
std::multimap<int, Link>::iterator iter = links.find(from);
|
||||
while(iter != links.end() && iter->first == from)
|
||||
@@ -1695,15 +1716,18 @@ std::multimap<int, Link>::iterator findLink(
|
||||
++iter;
|
||||
}
|
||||
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
if(checkBothWays)
|
||||
{
|
||||
if(iter->second.to() == from)
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
{
|
||||
return iter;
|
||||
if(iter->second.to() == from)
|
||||
{
|
||||
return iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
return links.end();
|
||||
}
|
||||
@@ -1711,7 +1735,8 @@ std::multimap<int, Link>::iterator findLink(
|
||||
std::multimap<int, int>::iterator findLink(
|
||||
std::multimap<int, int> & links,
|
||||
int from,
|
||||
int to)
|
||||
int to,
|
||||
bool checkBothWays)
|
||||
{
|
||||
std::multimap<int, int>::iterator iter = links.find(from);
|
||||
while(iter != links.end() && iter->first == from)
|
||||
@@ -1723,22 +1748,26 @@ std::multimap<int, int>::iterator findLink(
|
||||
++iter;
|
||||
}
|
||||
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
if(checkBothWays)
|
||||
{
|
||||
if(iter->second == from)
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
{
|
||||
return iter;
|
||||
if(iter->second == from)
|
||||
{
|
||||
return iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
return links.end();
|
||||
}
|
||||
std::multimap<int, Link>::const_iterator findLink(
|
||||
const std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to)
|
||||
int to,
|
||||
bool checkBothWays)
|
||||
{
|
||||
std::multimap<int, Link>::const_iterator iter = links.find(from);
|
||||
while(iter != links.end() && iter->first == from)
|
||||
@@ -1750,15 +1779,18 @@ std::multimap<int, Link>::const_iterator findLink(
|
||||
++iter;
|
||||
}
|
||||
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
if(checkBothWays)
|
||||
{
|
||||
if(iter->second.to() == from)
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
{
|
||||
return iter;
|
||||
if(iter->second.to() == from)
|
||||
{
|
||||
return iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
return links.end();
|
||||
}
|
||||
@@ -1766,7 +1798,8 @@ std::multimap<int, Link>::const_iterator findLink(
|
||||
std::multimap<int, int>::const_iterator findLink(
|
||||
const std::multimap<int, int> & links,
|
||||
int from,
|
||||
int to)
|
||||
int to,
|
||||
bool checkBothWays)
|
||||
{
|
||||
std::multimap<int, int>::const_iterator iter = links.find(from);
|
||||
while(iter != links.end() && iter->first == from)
|
||||
@@ -1778,15 +1811,18 @@ std::multimap<int, int>::const_iterator findLink(
|
||||
++iter;
|
||||
}
|
||||
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
if(checkBothWays)
|
||||
{
|
||||
if(iter->second == from)
|
||||
// let's try to -> from
|
||||
iter = links.find(to);
|
||||
while(iter != links.end() && iter->first == to)
|
||||
{
|
||||
return iter;
|
||||
if(iter->second == from)
|
||||
{
|
||||
return iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
return links.end();
|
||||
}
|
||||
@@ -1955,6 +1991,192 @@ std::multimap<int, int> radiusPosesClustering(const std::map<int, Transform> & p
|
||||
return clusters;
|
||||
}
|
||||
|
||||
void reduceGraph(
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
std::multimap<int, int> & hyperNodes, //<parent ID, child ID>
|
||||
std::multimap<int, Link> & hyperLinks)
|
||||
{
|
||||
UINFO("Input: poses=%d links=%d", (int)poses.size(), (int)links.size());
|
||||
UTimer timer;
|
||||
std::map<int, int> posesToHyperNodes;
|
||||
std::map<int, std::multimap<int, Link> > clusterloopClosureLinks;
|
||||
|
||||
{
|
||||
std::multimap<int, Link> bidirectionalLoopClosureLinks;
|
||||
for(std::multimap<int, Link>::const_iterator jter=links.begin(); jter!=links.end(); ++jter)
|
||||
{
|
||||
if(jter->second.type() != Link::kNeighbor &&
|
||||
jter->second.type() != Link::kNeighborMerged &&
|
||||
jter->second.userDataCompressed().empty())
|
||||
{
|
||||
if(uContains(poses, jter->second.from()) &&
|
||||
uContains(poses, jter->second.to()))
|
||||
{
|
||||
UASSERT_MSG(graph::findLink(links, jter->second.to(), jter->second.from(), false) == links.end(), "Input links should be unique!");
|
||||
bidirectionalLoopClosureLinks.insert(std::make_pair(jter->second.from(), jter->second));
|
||||
//bidirectionalLoopClosureLinks.insert(std::make_pair(jter->second.to(), jter->second.inverse()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UINFO("Clustering hyper nodes...");
|
||||
// largest ID to smallest ID
|
||||
for(std::map<int, Transform>::const_reverse_iterator iter=poses.rbegin(); iter!=poses.rend(); ++iter)
|
||||
{
|
||||
if(posesToHyperNodes.find(iter->first) == posesToHyperNodes.end())
|
||||
{
|
||||
int hyperNodeId = iter->first;
|
||||
std::list<int> loopClosures;
|
||||
std::set<int> loopClosuresAdded;
|
||||
loopClosures.push_back(iter->first);
|
||||
std::multimap<int, Link> clusterLinks;
|
||||
while(loopClosures.size())
|
||||
{
|
||||
int id = loopClosures.front();
|
||||
loopClosures.pop_front();
|
||||
|
||||
UASSERT(posesToHyperNodes.find(id) == posesToHyperNodes.end());
|
||||
|
||||
posesToHyperNodes.insert(std::make_pair(id, hyperNodeId));
|
||||
hyperNodes.insert(std::make_pair(hyperNodeId, id));
|
||||
|
||||
for(std::multimap<int, Link>::const_iterator jter=bidirectionalLoopClosureLinks.find(id); jter!=bidirectionalLoopClosureLinks.end() && jter->first==id; ++jter)
|
||||
{
|
||||
if(posesToHyperNodes.find(jter->second.to()) == posesToHyperNodes.end() &&
|
||||
loopClosuresAdded.find(jter->second.to()) == loopClosuresAdded.end())
|
||||
{
|
||||
loopClosures.push_back(jter->second.to());
|
||||
loopClosuresAdded.insert(jter->second.to());
|
||||
clusterLinks.insert(*jter);
|
||||
clusterLinks.insert(std::make_pair(jter->second.to(), jter->second.inverse()));
|
||||
if(jter->second.from() < jter->second.to())
|
||||
{
|
||||
UWARN("Child to Parent link? %d->%d (type=%d)",
|
||||
jter->second.from(),
|
||||
jter->second.to(),
|
||||
jter->second.type());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UASSERT(clusterloopClosureLinks.find(hyperNodeId) == clusterloopClosureLinks.end());
|
||||
clusterloopClosureLinks.insert(std::make_pair(hyperNodeId, clusterLinks));
|
||||
UDEBUG("Created hyper node %d with %d children (%f%%)",
|
||||
hyperNodeId, (int)loopClosuresAdded.size(), float(posesToHyperNodes.size())/float(poses.size())*100.0f);
|
||||
}
|
||||
}
|
||||
UINFO("Clustering hyper nodes... done! (%f s)", timer.ticks());
|
||||
}
|
||||
|
||||
UINFO("Creating hyper links...");
|
||||
int i=0;
|
||||
for(std::multimap<int, Link>::const_reverse_iterator jter=links.rbegin(); jter!=links.rend(); ++jter)
|
||||
{
|
||||
if((jter->second.type() == Link::kNeighbor ||
|
||||
jter->second.type() == Link::kNeighborMerged ||
|
||||
!jter->second.userDataCompressed().empty()) &&
|
||||
uContains(poses, jter->second.from()) &&
|
||||
uContains(poses, jter->second.to()))
|
||||
{
|
||||
UASSERT_MSG(uContains(posesToHyperNodes, jter->second.from()), uFormat("%d->%d (type=%d)", jter->second.from(), jter->second.to(), jter->second.type()).c_str());
|
||||
UASSERT_MSG(uContains(posesToHyperNodes, jter->second.to()), uFormat("%d->%d (type=%d)", jter->second.from(), jter->second.to(), jter->second.type()).c_str());
|
||||
int hyperNodeIDFrom = posesToHyperNodes.at(jter->second.from());
|
||||
int hyperNodeIDTo = posesToHyperNodes.at(jter->second.to());
|
||||
|
||||
// ignore links inside a hyper node
|
||||
if(hyperNodeIDFrom != hyperNodeIDTo)
|
||||
{
|
||||
std::multimap<int, Link>::iterator tmpIter = graph::findLink(hyperLinks, hyperNodeIDFrom, hyperNodeIDTo);
|
||||
if(tmpIter!=hyperLinks.end() &&
|
||||
hyperNodeIDFrom == jter->second.from() &&
|
||||
hyperNodeIDTo == jter->second.to() &&
|
||||
tmpIter->second.type() > Link::kNeighbor &&
|
||||
jter->second.type() == Link::kNeighbor)
|
||||
{
|
||||
// neighbor links have priority, so remove the previously added link
|
||||
hyperLinks.erase(tmpIter);
|
||||
}
|
||||
|
||||
// only add unique link between two hyper nodes (keeping only the more recent)
|
||||
if(graph::findLink(hyperLinks, hyperNodeIDFrom, hyperNodeIDTo) == hyperLinks.end())
|
||||
{
|
||||
UASSERT(clusterloopClosureLinks.find(hyperNodeIDFrom) != clusterloopClosureLinks.end());
|
||||
UASSERT(clusterloopClosureLinks.find(hyperNodeIDTo) != clusterloopClosureLinks.end());
|
||||
std::multimap<int, Link> tmpLinks = clusterloopClosureLinks.at(hyperNodeIDFrom);
|
||||
tmpLinks.insert(clusterloopClosureLinks.at(hyperNodeIDTo).begin(), clusterloopClosureLinks.at(hyperNodeIDTo).end());
|
||||
tmpLinks.insert(std::make_pair(jter->second.from(), jter->second));
|
||||
|
||||
std::list<int> path = computePath(tmpLinks, hyperNodeIDFrom, hyperNodeIDTo, false, true);
|
||||
UASSERT_MSG(path.size()>1,
|
||||
uFormat("path.size()=%d, hyperNodeIDFrom=%d, hyperNodeIDTo=%d",
|
||||
(int)path.size(),
|
||||
hyperNodeIDFrom,
|
||||
hyperNodeIDTo).c_str());
|
||||
|
||||
if(path.size() > 10)
|
||||
{
|
||||
UWARN("Large path! %d nodes", (int)path.size());
|
||||
std::stringstream stream;
|
||||
for(std::list<int>::const_iterator iter=path.begin(); iter!=path.end();++iter)
|
||||
{
|
||||
if(iter!=path.begin())
|
||||
{
|
||||
stream << ",";
|
||||
}
|
||||
|
||||
stream << *iter;
|
||||
}
|
||||
UWARN("Path = [%s]", stream.str().c_str());
|
||||
}
|
||||
|
||||
// create the hyperlink
|
||||
std::list<int>::iterator iter=path.begin();
|
||||
int from = *iter;
|
||||
++iter;
|
||||
int to = *iter;
|
||||
std::multimap<int, Link>::const_iterator foundIter = graph::findLink(tmpLinks, from, to, false);
|
||||
UASSERT(foundIter != tmpLinks.end());
|
||||
Link hyperLink = foundIter->second;
|
||||
++iter;
|
||||
from = to;
|
||||
for(; iter!=path.end(); ++iter)
|
||||
{
|
||||
to = *iter;
|
||||
std::multimap<int, Link>::const_iterator foundIter = graph::findLink(tmpLinks, from, to, false);
|
||||
UASSERT(foundIter != tmpLinks.end());
|
||||
hyperLink = hyperLink.merge(foundIter->second, jter->second.type());
|
||||
|
||||
from = to;
|
||||
}
|
||||
|
||||
UASSERT(hyperLink.from() == hyperNodeIDFrom);
|
||||
UASSERT(hyperLink.to() == hyperNodeIDTo);
|
||||
hyperLinks.insert(std::make_pair(hyperNodeIDFrom, hyperLink));
|
||||
|
||||
UDEBUG("Created hyper link %d->%d (%f%%)",
|
||||
hyperLink.from(), hyperLink.to(), float(i)/float(links.size())*100.0f);
|
||||
if(hyperLink.transform().getNorm() > jter->second.transform().getNorm()+1)
|
||||
{
|
||||
UWARN("Large hyper link %d->%d (%f m)! original %d->%d (%f m)",
|
||||
hyperLink.from(),
|
||||
hyperLink.to(),
|
||||
hyperLink.transform().getNorm(),
|
||||
jter->second.from(),
|
||||
jter->second.to(),
|
||||
jter->second.transform().getNorm());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
UINFO("Creating hyper links... done! (%f s)", timer.ticks());
|
||||
|
||||
UINFO("Output: poses=%d links=%d", (int)uUniqueKeys(hyperNodes).size(), (int)links.size());
|
||||
}
|
||||
|
||||
|
||||
class Node
|
||||
{
|
||||
@@ -2005,6 +2227,7 @@ struct Order
|
||||
}
|
||||
};
|
||||
|
||||
// A*
|
||||
std::list<std::pair<int, Transform> > computePath(
|
||||
const std::map<int, rtabmap::Transform> & poses,
|
||||
const std::multimap<int, int> & links,
|
||||
@@ -2014,7 +2237,6 @@ std::list<std::pair<int, Transform> > computePath(
|
||||
{
|
||||
std::list<std::pair<int, Transform> > path;
|
||||
|
||||
//A*
|
||||
int startNode = from;
|
||||
int endNode = to;
|
||||
rtabmap::Transform endPose = poses.at(endNode);
|
||||
@@ -2076,6 +2298,7 @@ std::list<std::pair<int, Transform> > computePath(
|
||||
Node n(iter->second, currentNode->id(), poseIter->second);
|
||||
n.setCostSoFar(currentNode->costSoFar() + currentNode->distFrom(poseIter->second));
|
||||
n.setDistToEnd(n.distFrom(endPose));
|
||||
|
||||
nodes.insert(std::make_pair(iter->second, n));
|
||||
if(updateNewCosts)
|
||||
{
|
||||
@@ -2110,6 +2333,107 @@ std::list<std::pair<int, Transform> > computePath(
|
||||
return path;
|
||||
}
|
||||
|
||||
// Dijksta
|
||||
std::list<int> RTABMAP_EXP computePath(
|
||||
const std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to,
|
||||
bool updateNewCosts,
|
||||
bool useSameCostForAllLinks)
|
||||
{
|
||||
std::list<int> path;
|
||||
|
||||
int startNode = from;
|
||||
int endNode = to;
|
||||
std::map<int, Node> nodes;
|
||||
nodes.insert(std::make_pair(startNode, Node(startNode, 0, Transform())));
|
||||
std::priority_queue<Pair, std::vector<Pair>, Order> pq;
|
||||
std::multimap<float, int> pqmap;
|
||||
if(updateNewCosts)
|
||||
{
|
||||
pqmap.insert(std::make_pair(0, startNode));
|
||||
}
|
||||
else
|
||||
{
|
||||
pq.push(Pair(startNode, 0));
|
||||
}
|
||||
|
||||
while((updateNewCosts && pqmap.size()) || (!updateNewCosts && pq.size()))
|
||||
{
|
||||
Node * currentNode;
|
||||
if(updateNewCosts)
|
||||
{
|
||||
currentNode = &nodes.find(pqmap.begin()->second)->second;
|
||||
pqmap.erase(pqmap.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
currentNode = &nodes.find(pq.top().first)->second;
|
||||
pq.pop();
|
||||
}
|
||||
|
||||
currentNode->setClosed(true);
|
||||
|
||||
if(currentNode->id() == endNode)
|
||||
{
|
||||
while(currentNode->id()!=startNode)
|
||||
{
|
||||
path.push_front(currentNode->id());
|
||||
currentNode = &nodes.find(currentNode->fromId())->second;
|
||||
}
|
||||
path.push_front(startNode);
|
||||
break;
|
||||
}
|
||||
|
||||
// lookup neighbors
|
||||
for(std::multimap<int, Link>::const_iterator iter = links.find(currentNode->id());
|
||||
iter!=links.end() && iter->first == currentNode->id();
|
||||
++iter)
|
||||
{
|
||||
std::map<int, Node>::iterator nodeIter = nodes.find(iter->second.to());
|
||||
float cost = 1;
|
||||
if(!useSameCostForAllLinks)
|
||||
{
|
||||
cost = iter->second.transform().getNorm();
|
||||
}
|
||||
if(nodeIter == nodes.end())
|
||||
{
|
||||
Node n(iter->second.to(), currentNode->id(), Transform());
|
||||
|
||||
n.setCostSoFar(currentNode->costSoFar() + cost);
|
||||
nodes.insert(std::make_pair(iter->second.to(), n));
|
||||
if(updateNewCosts)
|
||||
{
|
||||
pqmap.insert(std::make_pair(n.totalCost(), n.id()));
|
||||
}
|
||||
else
|
||||
{
|
||||
pq.push(Pair(n.id(), n.totalCost()));
|
||||
}
|
||||
}
|
||||
else if(!useSameCostForAllLinks && updateNewCosts && nodeIter->second.isOpened())
|
||||
{
|
||||
float newCostSoFar = currentNode->costSoFar() + cost;
|
||||
if(nodeIter->second.costSoFar() > newCostSoFar)
|
||||
{
|
||||
// update the cost in the priority queue
|
||||
for(std::multimap<float, int>::iterator mapIter=pqmap.begin(); mapIter!=pqmap.end(); ++mapIter)
|
||||
{
|
||||
if(mapIter->second == nodeIter->first)
|
||||
{
|
||||
pqmap.erase(mapIter);
|
||||
nodeIter->second.setCostSoFar(newCostSoFar);
|
||||
pqmap.insert(std::make_pair(nodeIter->second.totalCost(), nodeIter->first));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
// return path starting from "fromId" (Identity pose for the first node)
|
||||
std::list<std::pair<int, Transform> > computePath(
|
||||
@@ -2503,7 +2827,7 @@ std::list<std::map<int, Transform> > getPaths(
|
||||
for(std::map<int, Transform>::iterator iter=poses.begin(); iter!=poses.end();)
|
||||
{
|
||||
std::multimap<int, Link>::const_iterator jter = findLink(links, path.rbegin()->first, iter->first);
|
||||
if(path.size() == 0 || (jter != links.end() && jter->second.type() == Link::kNeighbor))
|
||||
if(path.size() == 0 || (jter != links.end() && (jter->second.type() == Link::kNeighbor || jter->second.type() == Link::kNeighborMerged)))
|
||||
{
|
||||
path.insert(*iter);
|
||||
poses.erase(iter++);
|
||||
|
||||
+268
-125
@@ -71,6 +71,7 @@ Memory::Memory(const ParametersMap & parameters) :
|
||||
_saveDepth16Format(Parameters::defaultMemSaveDepth16Format()),
|
||||
_notLinkedNodesKeptInDb(Parameters::defaultMemNotLinkedNodesKept()),
|
||||
_incrementalMemory(Parameters::defaultMemIncrementalMemory()),
|
||||
_reduceGraph(Parameters::defaultMemReduceGraph()),
|
||||
_maxStMemSize(Parameters::defaultMemSTMSize()),
|
||||
_recentWmRatio(Parameters::defaultMemRecentWmRatio()),
|
||||
_transferSortingByWeightId(Parameters::defaultMemTransferSortingByWeightId()),
|
||||
@@ -402,6 +403,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
|
||||
Parameters::parse(parameters, Parameters::kMemImageKept(), _rawDataKept);
|
||||
Parameters::parse(parameters, Parameters::kMemBinDataKept(), _binDataKept);
|
||||
Parameters::parse(parameters, Parameters::kMemSaveDepth16Format(), _saveDepth16Format);
|
||||
Parameters::parse(parameters, Parameters::kMemReduceGraph(), _reduceGraph);
|
||||
Parameters::parse(parameters, Parameters::kMemNotLinkedNodesKept(), _notLinkedNodesKeptInDb);
|
||||
Parameters::parse(parameters, Parameters::kMemRehearsalIdUpdatedToNewOne(), _idUpdatedToNewOneRehearsal);
|
||||
Parameters::parse(parameters, Parameters::kMemGenerateIds(), _generateIds);
|
||||
@@ -626,49 +628,36 @@ bool Memory::update(
|
||||
//============================================================
|
||||
// Transfer the oldest signature of the short-term memory to the working memory
|
||||
//============================================================
|
||||
int validSignaturesCount = 0;
|
||||
int notIntermediateNodesCount = 0;
|
||||
for(std::set<int>::iterator iter=_stMem.begin(); iter!=_stMem.end(); ++iter)
|
||||
{
|
||||
const Signature * s = this->getSignature(*iter);
|
||||
UASSERT(s != 0);
|
||||
if(!s->isBadSignature())
|
||||
if(s->getWeight() >= 0)
|
||||
{
|
||||
++validSignaturesCount;
|
||||
++notIntermediateNodesCount;
|
||||
}
|
||||
}
|
||||
while(_stMem.size() && _maxStMemSize>0 && validSignaturesCount > _maxStMemSize)
|
||||
std::map<int, int> reducedIds;
|
||||
while(_stMem.size() && _maxStMemSize>0 && notIntermediateNodesCount > _maxStMemSize)
|
||||
{
|
||||
UDEBUG("Inserting node %d from STM in WM...", *_stMem.begin());
|
||||
Signature * s = this->_getSignature(*_stMem.begin());
|
||||
if(!_localSpaceLinksKeptInWM)
|
||||
int id = *_stMem.begin();
|
||||
Signature * s = this->_getSignature(id);
|
||||
UASSERT(s != 0);
|
||||
if(s->getWeight() >= 0)
|
||||
{
|
||||
// remove local space links outside STM
|
||||
UASSERT(s!=0);
|
||||
std::map<int, Link> links = s->getLinks(); // get a copy because we will remove some links in "s"
|
||||
for(std::map<int, Link>::iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() == Link::kLocalSpaceClosure)
|
||||
{
|
||||
Signature * sTo = this->_getSignature(iter->first);
|
||||
if(sTo)
|
||||
{
|
||||
sTo->removeLink(s->id());
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Link %d of %d not in WM/STM?!?", iter->first, s->id());
|
||||
}
|
||||
s->removeLink(iter->first);
|
||||
}
|
||||
}
|
||||
--notIntermediateNodesCount;
|
||||
}
|
||||
if(!s->isBadSignature())
|
||||
|
||||
int reducedTo = 0;
|
||||
moveSignatureToWMFromSTM(id, &reducedTo);
|
||||
|
||||
if(reducedTo > 0)
|
||||
{
|
||||
--validSignaturesCount;
|
||||
reducedIds.insert(std::make_pair(id, reducedTo));
|
||||
}
|
||||
_workingMem.insert(_workingMem.end(), std::make_pair(*_stMem.begin(), UTimer::now()));
|
||||
_stMem.erase(*_stMem.begin());
|
||||
}
|
||||
if(stats) stats->setReducedIds(reducedIds);
|
||||
|
||||
if(!_memoryChanged && _incrementalMemory)
|
||||
{
|
||||
@@ -784,7 +773,7 @@ void Memory::addSignatureToStm(Signature * signature, const cv::Mat & covariance
|
||||
UDEBUG("time = %fs", timer.ticks());
|
||||
}
|
||||
|
||||
void Memory::addSignatureToWm(Signature * signature)
|
||||
void Memory::addSignatureToWmFromLTM(Signature * signature)
|
||||
{
|
||||
if(signature)
|
||||
{
|
||||
@@ -799,6 +788,127 @@ void Memory::addSignatureToWm(Signature * signature)
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::moveSignatureToWMFromSTM(int id, int * reducedTo)
|
||||
{
|
||||
UDEBUG("Inserting node %d from STM in WM...", id);
|
||||
UASSERT(_stMem.find(id) != _stMem.end());
|
||||
Signature * s = this->_getSignature(id);
|
||||
UASSERT(s!=0);
|
||||
if(!_localSpaceLinksKeptInWM)
|
||||
{
|
||||
// remove local space links outside STM
|
||||
std::map<int, Link> links = s->getLinks(); // get a copy because we will remove some links in "s"
|
||||
for(std::map<int, Link>::iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() == Link::kLocalSpaceClosure)
|
||||
{
|
||||
Signature * sTo = this->_getSignature(iter->first);
|
||||
if(sTo)
|
||||
{
|
||||
sTo->removeLink(s->id());
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Link %d of %d not in WM/STM?!?", iter->first, s->id());
|
||||
}
|
||||
s->removeLink(iter->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_reduceGraph)
|
||||
{
|
||||
bool merge = false;
|
||||
const std::map<int, Link> & links = s->getLinks();
|
||||
std::map<int, Link> neighbors;
|
||||
for(std::map<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
if(!merge)
|
||||
{
|
||||
merge = iter->second.to() < s->id() && // should be a parent->child link
|
||||
iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged &&
|
||||
iter->second.userDataCompressed().empty() &&
|
||||
iter->second.type() != Link::kUndef &&
|
||||
iter->second.type() != Link::kVirtualClosure;
|
||||
if(merge)
|
||||
{
|
||||
UDEBUG("Reduce %d to %d", s->id(), iter->second.to());
|
||||
if(reducedTo)
|
||||
{
|
||||
*reducedTo = iter->second.to();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
{
|
||||
neighbors.insert(*iter);
|
||||
}
|
||||
}
|
||||
if(merge)
|
||||
{
|
||||
if(s->getLabel().empty())
|
||||
{
|
||||
for(std::map<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
merge = true;
|
||||
Signature * sTo = this->_getSignature(iter->first);
|
||||
UASSERT(sTo!=0);
|
||||
sTo->removeLink(s->id());
|
||||
if(iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged &&
|
||||
iter->second.type() != Link::kUndef)
|
||||
{
|
||||
// link to all neighbors
|
||||
for(std::map<int, Link>::iterator jter=neighbors.begin(); jter!=neighbors.end(); ++jter)
|
||||
{
|
||||
if(!sTo->hasLink(jter->second.to()))
|
||||
{
|
||||
Link l = iter->second.inverse().merge(
|
||||
jter->second,
|
||||
iter->second.userDataCompressed().empty() && iter->second.type() != Link::kVirtualClosure?Link::kNeighborMerged:iter->second.type());
|
||||
sTo->addLink(l);
|
||||
Signature * sB = this->_getSignature(l.to());
|
||||
UASSERT(sB!=0);
|
||||
UASSERT(!sB->hasLink(l.to()));
|
||||
sB->addLink(l.inverse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove neighbor links
|
||||
std::map<int, Link> linksCopy = links;
|
||||
for(std::map<int, Link>::iterator iter=linksCopy.begin(); iter!=linksCopy.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() == Link::kNeighbor ||
|
||||
iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
s->removeLink(iter->first);
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
{
|
||||
if(_lastGlobalLoopClosureId == s->id())
|
||||
{
|
||||
_lastGlobalLoopClosureId = iter->first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->moveToTrash(s, _notLinkedNodesKeptInDb);
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(s != 0)
|
||||
{
|
||||
_workingMem.insert(_workingMem.end(), std::make_pair(*_stMem.begin(), UTimer::now()));
|
||||
_stMem.erase(*_stMem.begin());
|
||||
}
|
||||
// else already removed from STM/WM in moveToTrash()
|
||||
}
|
||||
|
||||
const Signature * Memory::getSignature(int id) const
|
||||
{
|
||||
return _getSignature(id);
|
||||
@@ -825,7 +935,8 @@ std::map<int, Link> Memory::getNeighborLinks(
|
||||
const std::map<int, Link> & allLinks = s->getLinks();
|
||||
for(std::map<int, Link>::const_iterator iter = allLinks.begin(); iter!=allLinks.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
if(iter->second.type() == Link::kNeighbor ||
|
||||
iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
links.insert(*iter);
|
||||
}
|
||||
@@ -834,8 +945,19 @@ std::map<int, Link> Memory::getNeighborLinks(
|
||||
else if(lookInDatabase && _dbDriver)
|
||||
{
|
||||
std::map<int, Link> neighbors;
|
||||
_dbDriver->loadLinks(signatureId, neighbors, Link::kNeighbor);
|
||||
links.insert(neighbors.begin(), neighbors.end());
|
||||
_dbDriver->loadLinks(signatureId, neighbors);
|
||||
for(std::map<int, Link>::iterator iter=neighbors.begin(); iter!=neighbors.end();)
|
||||
{
|
||||
if(iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
neighbors.erase(iter++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -855,7 +977,8 @@ std::map<int, Link> Memory::getLoopClosureLinks(
|
||||
const std::map<int, Link> & allLinks = s->getLinks();
|
||||
for(std::map<int, Link>::const_iterator iter = allLinks.begin(); iter!=allLinks.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() > Link::kNeighbor &&
|
||||
if(iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged &&
|
||||
iter->second.type() != Link::kUndef)
|
||||
{
|
||||
loopClosures.insert(*iter);
|
||||
@@ -867,7 +990,9 @@ std::map<int, Link> Memory::getLoopClosureLinks(
|
||||
_dbDriver->loadLinks(signatureId, loopClosures);
|
||||
for(std::map<int, Link>::iterator iter=loopClosures.begin(); iter!=loopClosures.end();)
|
||||
{
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
if(iter->second.type() == Link::kNeighbor ||
|
||||
iter->second.type() == Link::kNeighborMerged ||
|
||||
iter->second.type() == Link::kUndef )
|
||||
{
|
||||
loopClosures.erase(iter++);
|
||||
}
|
||||
@@ -931,7 +1056,8 @@ std::multimap<int, Link> Memory::getAllLinks(bool lookInDatabase, bool ignoreNul
|
||||
// return map<Id,Margin>, including signatureId
|
||||
// maxCheckedInDatabase = -1 means no limit to check in database (default)
|
||||
// maxCheckedInDatabase = 0 means don't check in database
|
||||
std::map<int, int> Memory::getNeighborsId(int signatureId,
|
||||
std::map<int, int> Memory::getNeighborsId(
|
||||
int signatureId,
|
||||
int maxGraphDepth, // 0 means infinite margin
|
||||
int maxCheckedInDatabase, // default -1 (no limit)
|
||||
bool incrementMarginOnLoop, // default false
|
||||
@@ -992,7 +1118,7 @@ std::map<int, int> Memory::getNeighborsId(int signatureId,
|
||||
ids.insert(std::pair<int, int>(*jter, m));
|
||||
|
||||
UTimer timer;
|
||||
_dbDriver->loadLinks(*jter, tmpLinks, ignoreLoopIds?Link::kNeighbor:Link::kUndef);
|
||||
_dbDriver->loadLinks(*jter, tmpLinks);
|
||||
if(dbAccessTime)
|
||||
{
|
||||
*dbAccessTime += timer.getElapsedTime();
|
||||
@@ -1005,7 +1131,8 @@ std::map<int, int> Memory::getNeighborsId(int signatureId,
|
||||
if( !uContains(ids, iter->first) && ignoredIds.find(iter->first) == ignoredIds.end())
|
||||
{
|
||||
UASSERT(iter->second.type() != Link::kUndef);
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
if(iter->second.type() == Link::kNeighbor ||
|
||||
iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
if(ignoreIntermediateNodes && s->getWeight()==-1)
|
||||
{
|
||||
@@ -1116,7 +1243,7 @@ int Memory::getNextId()
|
||||
return ++_idCount;
|
||||
}
|
||||
|
||||
int Memory::incrementMapId()
|
||||
int Memory::incrementMapId(std::map<int, int> * reducedIds)
|
||||
{
|
||||
//don't increment if there is no location in the current map
|
||||
const Signature * s = getLastWorkingSignature();
|
||||
@@ -1125,32 +1252,13 @@ int Memory::incrementMapId()
|
||||
// New session! move all signatures from the STM to WM
|
||||
while(_stMem.size())
|
||||
{
|
||||
UDEBUG("Inserting node %d from STM in WM...", *_stMem.begin());
|
||||
if(!_localSpaceLinksKeptInWM)
|
||||
int reducedId = 0;
|
||||
int id = *_stMem.begin();
|
||||
moveSignatureToWMFromSTM(id, &reducedId);
|
||||
if(reducedIds && reducedId > 0)
|
||||
{
|
||||
// remove local space links outside STM
|
||||
Signature * s = this->_getSignature(*_stMem.begin());
|
||||
UASSERT(s!=0);
|
||||
std::map<int, Link> links = s->getLinks(); // get a copy because we will remove some links in "s"
|
||||
for(std::map<int, Link>::iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() == Link::kLocalSpaceClosure)
|
||||
{
|
||||
Signature * sTo = this->_getSignature(iter->first);
|
||||
if(sTo)
|
||||
{
|
||||
sTo->removeLink(s->id());
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Link %d of %d not in WM/STM?!?", iter->first, s->id());
|
||||
}
|
||||
s->removeLink(iter->first);
|
||||
}
|
||||
}
|
||||
reducedIds->insert(std::make_pair(id, reducedId));
|
||||
}
|
||||
_workingMem.insert(_workingMem.end(), std::make_pair(*_stMem.begin(), UTimer::now()));
|
||||
_stMem.erase(*_stMem.begin());
|
||||
}
|
||||
|
||||
return ++_idMapCount;
|
||||
@@ -1210,6 +1318,17 @@ void Memory::clear()
|
||||
{
|
||||
UDEBUG("");
|
||||
|
||||
// empty the STM
|
||||
while(_stMem.size())
|
||||
{
|
||||
moveSignatureToWMFromSTM(*_stMem.begin());
|
||||
}
|
||||
if(_stMem.size() != 0)
|
||||
{
|
||||
ULOGGER_ERROR("_stMem must be empty here, size=%d", _stMem.size());
|
||||
}
|
||||
_stMem.clear();
|
||||
|
||||
this->cleanUnusedWords();
|
||||
|
||||
if(_dbDriver)
|
||||
@@ -1217,6 +1336,12 @@ void Memory::clear()
|
||||
_dbDriver->emptyTrashes();
|
||||
_dbDriver->join();
|
||||
}
|
||||
if(_dbDriver)
|
||||
{
|
||||
// make sure time_enter in database is at least 1 second
|
||||
// after for the next stuf added to database
|
||||
uSleep(1500);
|
||||
}
|
||||
|
||||
// Save some stats to the db, save only when the mem is not empty
|
||||
if(_dbDriver && (_stMem.size() || _workingMem.size()))
|
||||
@@ -1261,11 +1386,6 @@ void Memory::clear()
|
||||
ULOGGER_ERROR("_workingMem must be empty here, size=%d", _workingMem.size());
|
||||
}
|
||||
_workingMem.clear();
|
||||
if(_stMem.size() != 0)
|
||||
{
|
||||
ULOGGER_ERROR("_stMem must be empty here, size=%d", _stMem.size());
|
||||
}
|
||||
_stMem.clear();
|
||||
if(_signatures.size()!=0)
|
||||
{
|
||||
ULOGGER_ERROR("_signatures must be empty here, size=%d", _signatures.size());
|
||||
@@ -1450,8 +1570,15 @@ std::list<int> Memory::forget(const std::set<int> & ignoredIds)
|
||||
{
|
||||
UDEBUG("");
|
||||
std::list<int> signaturesRemoved;
|
||||
if(this->isIncremental() && _vwd->isIncremental() && _vwd->getVisualWords().size())
|
||||
if(this->isIncremental() &&
|
||||
_vwd->isIncremental() &&
|
||||
_vwd->getVisualWords().size() &&
|
||||
!_vwd->isIncrementalFlann())
|
||||
{
|
||||
// Note that when using incremental FLANN, the number of words
|
||||
// is not the biggest issue, so use the number of signatures instead
|
||||
// of the number of words
|
||||
|
||||
int newWords = 0;
|
||||
int wordsRemoved = 0;
|
||||
|
||||
@@ -1719,32 +1846,33 @@ void Memory::moveToTrash(Signature * s, bool keepLinkedToGraph, std::list<int> *
|
||||
if(!keepLinkedToGraph || (!s->isSaved() && s->isBadSignature() && _badSignaturesIgnored))
|
||||
{
|
||||
UASSERT_MSG(this->isInSTM(s->id()),
|
||||
uFormat("Deleting location (%d) outside the STM is not implemented!", s->id()).c_str());
|
||||
uFormat("Deleting location (%d) outside the "
|
||||
"STM is not implemented!", s->id()).c_str());
|
||||
const std::map<int, Link> & links = s->getLinks();
|
||||
for(std::map<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
Signature * sTo = this->_getSignature(iter->first);
|
||||
// neighbor to s
|
||||
if(sTo)
|
||||
{
|
||||
if(iter->first > s->id() && (sTo->getLinks().size() == 1 || !sTo->hasLink(s->id())))
|
||||
{
|
||||
UWARN("Link %d of %d is newer, removing neighbor link may split the map!",
|
||||
iter->first, s->id());
|
||||
}
|
||||
UASSERT_MSG(sTo!=0,
|
||||
uFormat("A neighbor (%d) of the deleted location %d is "
|
||||
"not found in WM/STM! Are you deleting a location "
|
||||
"outside the STM?", iter->first, s->id()).c_str());
|
||||
|
||||
// child
|
||||
if(iter->second.type() == Link::kGlobalClosure && s->id() > sTo->id())
|
||||
{
|
||||
sTo->setWeight(sTo->getWeight() + s->getWeight()); // copy weight
|
||||
}
|
||||
|
||||
sTo->removeLink(s->id());
|
||||
}
|
||||
else
|
||||
if(iter->first > s->id() && links.size()>1 && sTo->hasLink(s->id()))
|
||||
{
|
||||
UERROR("Link %d of %d not in WM/STM?!?", iter->first, s->id());
|
||||
UWARN("Link %d of %d is newer, removing neighbor link "
|
||||
"may split the map!",
|
||||
iter->first, s->id());
|
||||
}
|
||||
|
||||
// child
|
||||
if(iter->second.type() == Link::kGlobalClosure && s->id() > sTo->id())
|
||||
{
|
||||
sTo->setWeight(sTo->getWeight() + s->getWeight()); // copy weight
|
||||
}
|
||||
|
||||
sTo->removeLink(s->id());
|
||||
|
||||
}
|
||||
s->removeLinks(); // remove all links
|
||||
s->setWeight(0);
|
||||
@@ -1801,6 +1929,11 @@ void Memory::moveToTrash(Signature * s, bool keepLinkedToGraph, std::list<int> *
|
||||
}
|
||||
}
|
||||
|
||||
if(_lastGlobalLoopClosureId == s->id())
|
||||
{
|
||||
_lastGlobalLoopClosureId = 0;
|
||||
}
|
||||
|
||||
if( (_notLinkedNodesKeptInDb || keepLinkedToGraph) &&
|
||||
_dbDriver &&
|
||||
s->id()>0 &&
|
||||
@@ -1959,7 +2092,9 @@ void Memory::removeLink(int oldId, int newId)
|
||||
bool noChildrenAnymore = true;
|
||||
for(std::map<int, Link>::const_iterator iter=newS->getLinks().begin(); iter!=newS->getLinks().end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() > Link::kNeighbor && iter->first < newS->id())
|
||||
if(iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged &&
|
||||
iter->first < newS->id())
|
||||
{
|
||||
noChildrenAnymore = false;
|
||||
break;
|
||||
@@ -2554,7 +2689,11 @@ Transform Memory::computeIcpTransform(
|
||||
fabs(ipitch) > _icpMaxRotation ||
|
||||
fabs(iyaw) > _icpMaxRotation)))
|
||||
{
|
||||
msg = uFormat("Cannot compute transform (ICP correction too large)");
|
||||
msg = uFormat("Cannot compute transform (ICP correction too large -> %f m %f rad, limits=%f m, %f rad)",
|
||||
uMax3(fabs(ix), fabs(iy), fabs(iz)),
|
||||
uMax3(fabs(iroll), fabs(ipitch), fabs(iyaw)),
|
||||
_icpMaxTranslation,
|
||||
_icpMaxRotation);
|
||||
UINFO(msg.c_str());
|
||||
}
|
||||
else
|
||||
@@ -2888,23 +3027,28 @@ bool Memory::addLink(const Link & link)
|
||||
if(link.type()!=Link::kVirtualClosure)
|
||||
{
|
||||
_linksChanged = true;
|
||||
}
|
||||
|
||||
if(link.type() == Link::kGlobalClosure)
|
||||
{
|
||||
_lastGlobalLoopClosureId = fromS->id()>toS->id()?fromS->id():toS->id();
|
||||
// update weight
|
||||
// ignore scan matching loop closures
|
||||
if(link.type() != Link::kLocalSpaceClosure ||
|
||||
link.userDataCompressed().empty())
|
||||
{
|
||||
_lastGlobalLoopClosureId = fromS->id()>toS->id()?fromS->id():toS->id();
|
||||
|
||||
// update weights only if the memory is incremental
|
||||
UASSERT(fromS->getWeight() >= 0 && toS->getWeight() >=0);
|
||||
if(fromS->id() > toS->id())
|
||||
{
|
||||
fromS->setWeight(fromS->getWeight() + toS->getWeight());
|
||||
toS->setWeight(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
toS->setWeight(toS->getWeight() + fromS->getWeight());
|
||||
fromS->setWeight(0);
|
||||
// update weights only if the memory is incremental
|
||||
// When reducing the graph, transfer weight to the oldest signature
|
||||
UASSERT(fromS->getWeight() >= 0 && toS->getWeight() >=0);
|
||||
if((_reduceGraph && fromS->id() < toS->id()) ||
|
||||
(!_reduceGraph && fromS->id() > toS->id()))
|
||||
{
|
||||
fromS->setWeight(fromS->getWeight() + toS->getWeight());
|
||||
toS->setWeight(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
toS->setWeight(toS->getWeight() + fromS->getWeight());
|
||||
fromS->setWeight(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3101,7 +3245,8 @@ void Memory::dumpMemoryTree(const char * fileNameTree) const
|
||||
iter!=i->second->getLinks().end();
|
||||
++iter)
|
||||
{
|
||||
if(iter->second.type() > Link::kNeighbor)
|
||||
if(iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
if(iter->first < i->first)
|
||||
{
|
||||
@@ -3199,7 +3344,9 @@ bool Memory::rehearsalMerge(int oldId, int newId)
|
||||
if(oldS && newS && _incrementalMemory)
|
||||
{
|
||||
std::map<int, Link>::const_iterator iter = oldS->getLinks().find(newS->id());
|
||||
if(iter != oldS->getLinks().end() && iter->second.type() > Link::kNeighbor)
|
||||
if(iter != oldS->getLinks().end() &&
|
||||
iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
// do nothing, already merged
|
||||
UWARN("already merged, old=%d, new=%d", oldId, newId);
|
||||
@@ -3608,8 +3755,7 @@ Signature * Memory::createSignature(const SensorData & data, const Transform & p
|
||||
data.imageRaw().type() == CV_8UC3);
|
||||
UASSERT_MSG(data.depthOrRightRaw().empty() ||
|
||||
( (data.depthOrRightRaw().type() == CV_16UC1 || data.depthOrRightRaw().type() == CV_32FC1 || data.depthOrRightRaw().type() == CV_8UC1) &&
|
||||
data.depthOrRightRaw().rows == data.imageRaw().rows &&
|
||||
data.depthOrRightRaw().cols == data.imageRaw().cols),
|
||||
((data.imageRaw().empty() && data.depthOrRightRaw().type() != CV_8UC1) || (data.depthOrRightRaw().rows == data.imageRaw().rows && data.depthOrRightRaw().cols == data.imageRaw().cols))),
|
||||
uFormat("image=(%d/%d) depth=(%d/%d, type=%d [accepted=%d,%d,%d])",
|
||||
data.imageRaw().cols,
|
||||
data.imageRaw().rows,
|
||||
@@ -3912,7 +4058,7 @@ Signature * Memory::createSignature(const SensorData & data, const Transform & p
|
||||
descriptors = data.descriptors().clone();
|
||||
|
||||
// filter by depth
|
||||
if(!data.depthOrRightRaw().empty() && data.stereoCameraModel().isValid())
|
||||
if(!data.depthOrRightRaw().empty() && !data.imageRaw().empty() && data.stereoCameraModel().isValid())
|
||||
{
|
||||
//stereo
|
||||
cv::Mat imageMono;
|
||||
@@ -4104,6 +4250,12 @@ Signature * Memory::createSignature(const SensorData & data, const Transform & p
|
||||
Signature * s;
|
||||
if(this->isBinDataKept())
|
||||
{
|
||||
UDEBUG("Bin data kept: rgb=%d, depth=%d, scan=%d, userData=%d",
|
||||
image.empty()?0:1,
|
||||
depthOrRightImage.empty()?0:1,
|
||||
laserScan.empty()?0:1,
|
||||
data.userDataRaw().empty()?0:1);
|
||||
|
||||
std::vector<unsigned char> imageBytes;
|
||||
std::vector<unsigned char> depthBytes;
|
||||
|
||||
@@ -4398,7 +4550,7 @@ std::set<int> Memory::reactivateSignatures(const std::list<int> & ids, unsigned
|
||||
{
|
||||
idsLoaded.push_back((*i)->id());
|
||||
//append to working memory
|
||||
this->addSignatureToWm(*i);
|
||||
this->addSignatureToWmFromLTM(*i);
|
||||
}
|
||||
this->enableWordsRef(idsLoaded);
|
||||
UDEBUG("time = %fs", timer.ticks());
|
||||
@@ -4427,14 +4579,16 @@ void Memory::getMetricConstraints(
|
||||
{
|
||||
if(uContains(poses, *iter))
|
||||
{
|
||||
std::map<int, Link> neighbors = this->getNeighborLinks(*iter, lookInDatabase); // only direct neighbors
|
||||
for(std::map<int, Link>::iterator jter=neighbors.begin(); jter!=neighbors.end(); ++jter)
|
||||
std::map<int, Link> tmpLinks = getLinks(*iter, lookInDatabase);
|
||||
for(std::map<int, Link>::iterator jter=tmpLinks.begin(); jter!=tmpLinks.end(); ++jter)
|
||||
{
|
||||
if( jter->second.isValid() &&
|
||||
uContains(poses, jter->first) &&
|
||||
graph::findLink(links, *iter, jter->first) == links.end())
|
||||
{
|
||||
if(!lookInDatabase)
|
||||
if(!lookInDatabase &&
|
||||
(jter->second.type() == Link::kNeighbor ||
|
||||
jter->second.type() == Link::kNeighborMerged))
|
||||
{
|
||||
Link link = jter->second;
|
||||
const Signature * s = this->getSignature(jter->first);
|
||||
@@ -4471,17 +4625,6 @@ void Memory::getMetricConstraints(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int, Link> loops = this->getLoopClosureLinks(*iter, lookInDatabase);
|
||||
for(std::map<int, Link>::iterator jter=loops.begin(); jter!=loops.end(); ++jter)
|
||||
{
|
||||
if( jter->second.isValid() && // null transform means a rehearsed location
|
||||
jter->first < *iter && // Loop parent to child
|
||||
uContains(poses, jter->first))
|
||||
{
|
||||
links.insert(std::make_pair(*iter, jter->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,6 +505,7 @@ Transform OdometryOpticalFlow::computeTransform(
|
||||
data.cameraModels()[0].fy(),
|
||||
true);
|
||||
if(pcl::isFinite(pt) &&
|
||||
pt.z > 0 &&
|
||||
(this->getMaxDepth() == 0.0f || pt.z < this->getMaxDepth()))
|
||||
{
|
||||
newCorners3D->at(oi) = util3d::transformPoint(pt, data.cameraModels()[0].localTransform());
|
||||
|
||||
+129
-19
@@ -658,12 +658,27 @@ int Rtabmap::triggerNewMap()
|
||||
int mapId = -1;
|
||||
if(_memory)
|
||||
{
|
||||
mapId = _memory->incrementMapId();
|
||||
std::map<int, int> reducedIds;
|
||||
mapId = _memory->incrementMapId(&reducedIds);
|
||||
UINFO("New map triggered, new map = %d", mapId);
|
||||
_optimizedPoses.clear();
|
||||
_constraints.clear();
|
||||
_lastLocalizationNodeId = 0;
|
||||
_distanceTravelled = 0.0f;
|
||||
|
||||
//Verify if there are nodes that were merged through graph reduction
|
||||
if(reducedIds.size() && _path.size())
|
||||
{
|
||||
for(unsigned int i=0; i<_path.size(); ++i)
|
||||
{
|
||||
std::map<int, int>::const_iterator iter = reducedIds.find(_path[i].first);
|
||||
if(iter!= reducedIds.end())
|
||||
{
|
||||
// change path ID to loop closure ID
|
||||
_path[i].first = iter->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mapId;
|
||||
}
|
||||
@@ -942,7 +957,6 @@ bool Rtabmap::process(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
signature = _memory->getLastWorkingSignature();
|
||||
if(!signature)
|
||||
{
|
||||
@@ -957,6 +971,7 @@ bool Rtabmap::process(
|
||||
// Metric
|
||||
//============================================================
|
||||
bool smallDisplacement = false;
|
||||
std::list<int> signaturesRemoved;
|
||||
if(_rgbdSlamMode)
|
||||
{
|
||||
//Verify if there was a rehearsal
|
||||
@@ -1080,9 +1095,10 @@ bool Rtabmap::process(
|
||||
{
|
||||
newPose = _mapCorrection * signature->getPose();
|
||||
}
|
||||
|
||||
// Update Poses and Constraints
|
||||
_optimizedPoses.insert(std::make_pair(signature->id(), newPose));
|
||||
_lastLocalizationPose = newPose; // used in localization mode only (path planning)
|
||||
|
||||
if(signature->getLinks().size() == 1)
|
||||
{
|
||||
// link should be old to new
|
||||
@@ -1108,6 +1124,43 @@ bool Rtabmap::process(
|
||||
}
|
||||
_constraints.insert(std::make_pair(tmp.from(), tmp));
|
||||
}
|
||||
//============================================================
|
||||
// Reduced graph
|
||||
//============================================================
|
||||
//Verify if there are nodes that were merged through graph reduction
|
||||
if(statistics_.reducedIds().size())
|
||||
{
|
||||
for(unsigned int i=0; i<_path.size(); ++i)
|
||||
{
|
||||
std::map<int, int>::const_iterator iter = statistics_.reducedIds().find(_path[i].first);
|
||||
if(iter!= statistics_.reducedIds().end())
|
||||
{
|
||||
// change path ID to loop closure ID
|
||||
_path[i].first = iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
for(std::map<int, int>::const_iterator iter=statistics_.reducedIds().begin();
|
||||
iter!=statistics_.reducedIds().end();
|
||||
++iter)
|
||||
{
|
||||
int erased = _optimizedPoses.erase(iter->first);
|
||||
if(erased)
|
||||
{
|
||||
for(std::multimap<int, Link>::iterator jter = _constraints.begin(); jter!=_constraints.end();)
|
||||
{
|
||||
if(jter->second.from() == iter->first || jter->second.to() == iter->first)
|
||||
{
|
||||
_constraints.erase(jter++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++jter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// Local loop closure in TIME
|
||||
@@ -1132,7 +1185,6 @@ bool Rtabmap::process(
|
||||
if(!transform.isNull() && _globalLoopClosureIcpType > 0)
|
||||
{
|
||||
transform = _memory->computeIcpTransform(*iter, signature->id(), transform, _globalLoopClosureIcpType==1, &rejectedMsg, 0, &variance);
|
||||
variance = 1.0f; // ICP, set variance to 1 // FIXME why? all other links based on visual keep the variance
|
||||
}
|
||||
if(!transform.isNull())
|
||||
{
|
||||
@@ -2097,9 +2149,10 @@ bool Rtabmap::process(
|
||||
if(_rgbdSlamMode &&
|
||||
(_loopClosureHypothesis.first>0 ||
|
||||
lastLocalSpaceClosureId>0 || // can be different map of the current one
|
||||
statistics_.reducedIds().size() ||
|
||||
localLoopClosuresInTimeFound>0 ||
|
||||
((_memory->isIncremental() || signature->getLinks().size()) && // In localization mode, the new node should be linked
|
||||
(localLoopClosuresInTimeFound>0 || // only same map of the current one
|
||||
signaturesRetrieved.size())))) // can be different map of the current one
|
||||
signaturesRetrieved.size()))) // can be different map of the current one
|
||||
{
|
||||
UASSERT(uContains(_optimizedPoses, signature->id()));
|
||||
|
||||
@@ -2345,7 +2398,6 @@ bool Rtabmap::process(
|
||||
}
|
||||
|
||||
// remove last signature if the memory is not incremental or is a bad signature (if bad signatures are ignored)
|
||||
std::list<int> signaturesRemoved;
|
||||
int signatureRemoved = _memory->cleanup();
|
||||
if(signatureRemoved)
|
||||
{
|
||||
@@ -2898,11 +2950,46 @@ std::map<int, Transform> Rtabmap::optimizeGraph(
|
||||
{
|
||||
UTimer timer;
|
||||
std::map<int, Transform> optimizedPoses;
|
||||
std::map<int, Transform> poses;
|
||||
std::multimap<int, Link> edgeConstraints;
|
||||
std::map<int, Transform> poses, posesOut;
|
||||
std::multimap<int, Link> edgeConstraints, linksOut;
|
||||
UDEBUG("ids=%d", (int)ids.size());
|
||||
_memory->getMetricConstraints(ids, poses, edgeConstraints, lookInDatabase);
|
||||
UINFO("get constraints (%d poses, %d edges) time %f s", (int)poses.size(), (int)edgeConstraints.size(), timer.ticks());
|
||||
UINFO("get constraints (ids=%d, %d poses, %d edges) time %f s", (int)ids.size(), (int)poses.size(), (int)edgeConstraints.size(), timer.ticks());
|
||||
|
||||
// The constraints must be all already connected! Only check in debug
|
||||
if(ULogger::level() == ULogger::kDebug)
|
||||
{
|
||||
_graphOptimizer->getConnectedGraph(fromId, poses, edgeConstraints, posesOut, linksOut);
|
||||
if(poses.size() != posesOut.size())
|
||||
{
|
||||
for(std::map<int, Transform>::iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
||||
{
|
||||
if(posesOut.find(iter->first) == posesOut.end())
|
||||
{
|
||||
UERROR("Not found %d in posesOut", iter->first);
|
||||
for(std::multimap<int, Link>::iterator jter=edgeConstraints.begin(); jter!=edgeConstraints.end(); ++jter)
|
||||
{
|
||||
if(jter->second.from() == iter->first || jter->second.to()==iter->first)
|
||||
{
|
||||
UERROR("Found link %d->%d", jter->second.from(), jter->second.to());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(edgeConstraints.size() != linksOut.size())
|
||||
{
|
||||
for(std::multimap<int, Link>::iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
|
||||
{
|
||||
if(graph::findLink(linksOut, iter->second.from(), iter->second.to()) == linksOut.end())
|
||||
{
|
||||
UERROR("Not found link %d->%d in linksOut", iter->second.from(), iter->second.to());
|
||||
}
|
||||
}
|
||||
}
|
||||
UASSERT_MSG(poses.size() == posesOut.size() && edgeConstraints.size() == linksOut.size(),
|
||||
uFormat("nodes %d->%d, links %d->%d", poses.size(), posesOut.size(), edgeConstraints.size(), linksOut.size()).c_str());
|
||||
}
|
||||
|
||||
if(constraints)
|
||||
{
|
||||
@@ -3285,6 +3372,24 @@ bool Rtabmap::computePath(int targetNode, bool global)
|
||||
{
|
||||
// set goal to latest signature
|
||||
std::string goalStr = uFormat("GOAL:%d", targetNode);
|
||||
|
||||
// use label is exist
|
||||
if(_memory->getSignature(targetNode))
|
||||
{
|
||||
if(!_memory->getSignature(targetNode)->getLabel().empty())
|
||||
{
|
||||
goalStr = std::string("GOAL:")+_memory->getSignature(targetNode)->getLabel();
|
||||
}
|
||||
}
|
||||
else if(global)
|
||||
{
|
||||
std::map<int, std::string> labels = _memory->getAllLabels();
|
||||
std::map<int, std::string>::iterator iter = labels.find(targetNode);
|
||||
if(iter != labels.end() && !iter->second.empty())
|
||||
{
|
||||
goalStr = std::string("GOAL:")+labels.at(targetNode);
|
||||
}
|
||||
}
|
||||
setUserData(0, cv::Mat(1, int(goalStr.size()+1), CV_8SC1, (void *)goalStr.c_str()).clone());
|
||||
}
|
||||
updateGoalIndex();
|
||||
@@ -3474,17 +3579,19 @@ void Rtabmap::updateGoalIndex()
|
||||
// remove all previous virtual links
|
||||
for(unsigned int i=0; i<_pathCurrentIndex && i<_path.size(); ++i)
|
||||
{
|
||||
if(_memory->getSignature(_path[i].first))
|
||||
const Signature * s = _memory->getSignature(_path[i].first);
|
||||
if(s)
|
||||
{
|
||||
_memory->removeVirtualLinks(_path[i].first);
|
||||
_memory->removeVirtualLinks(s->id());
|
||||
}
|
||||
}
|
||||
|
||||
// for the current index, only keep the newest virtual link
|
||||
// This will make sure that the path is still connected even
|
||||
// if the new signature is removed (e.g., because of a small displacement)
|
||||
UASSERT(_pathCurrentIndex < _path.size());
|
||||
const Signature * currentIndexS = _memory->getSignature(_path[_pathCurrentIndex].first);
|
||||
UASSERT(currentIndexS != 0);
|
||||
UASSERT_MSG(currentIndexS != 0, uFormat("_path[%d].first=%d", _pathCurrentIndex, _path[_pathCurrentIndex].first).c_str());
|
||||
std::map<int, Link> links = currentIndexS->getLinks(); // make a copy
|
||||
bool latestVirtualLinkFound = false;
|
||||
for(std::map<int, Link>::reverse_iterator iter=links.rbegin(); iter!=links.rend(); ++iter)
|
||||
@@ -3516,14 +3623,17 @@ void Rtabmap::updateGoalIndex()
|
||||
}
|
||||
if(distanceSoFar <= _localRadius)
|
||||
{
|
||||
const Signature * s = _memory->getSignature(_path[i].first);
|
||||
if(s)
|
||||
if(_path[i].first != _path[i-1].first)
|
||||
{
|
||||
if(!s->hasLink(_path[i-1].first) && _memory->getSignature(_path[i-1].first) != 0)
|
||||
const Signature * s = _memory->getSignature(_path[i].first);
|
||||
if(s)
|
||||
{
|
||||
Transform virtualLoop = _path[i].second.inverse() * _path[i-1].second;
|
||||
_memory->addLink(Link(_path[i].first, _path[i-1].first, Link::kVirtualClosure, virtualLoop, 100, 100)); // on the optimized path
|
||||
UINFO("Added Virtual link between %d and %d", _path[i-1].first, _path[i].first);
|
||||
if(!s->hasLink(_path[i-1].first) && _memory->getSignature(_path[i-1].first) != 0)
|
||||
{
|
||||
Transform virtualLoop = _path[i].second.inverse() * _path[i-1].second;
|
||||
_memory->addLink(Link(_path[i].first, _path[i-1].first, Link::kVirtualClosure, virtualLoop, 100, 100)); // on the optimized path
|
||||
UINFO("Added Virtual link between %d and %d", _path[i-1].first, _path[i].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,10 +257,18 @@ void RtabmapThread::mainLoop()
|
||||
if(id == 0 && !parameters.at("label").empty() && _rtabmap->getMemory())
|
||||
{
|
||||
id = _rtabmap->getMemory()->getSignatureIdByLabel(parameters.at("label"));
|
||||
if(id <= 0)
|
||||
{
|
||||
UERROR("Failed to find a node with label \"%s\".", parameters.at("label").c_str());
|
||||
}
|
||||
}
|
||||
if(id <= 0 || !_rtabmap->computePath(id, true))
|
||||
else if(id < 0)
|
||||
{
|
||||
UERROR("Failed to set a goal to location=%d.", id);
|
||||
UERROR("Failed to set a goal. ID (%d) should be positive > 0", id);
|
||||
}
|
||||
if(id > 0 && !_rtabmap->computePath(id, true))
|
||||
{
|
||||
UERROR("Failed to compute a path to goal %d.", id);
|
||||
}
|
||||
this->post(new RtabmapGlobalPathEvent(id, parameters.at("label"), _rtabmap->getPath()));
|
||||
break;
|
||||
|
||||
@@ -97,7 +97,8 @@ void Signature::addLinks(const std::map<int, Link> & links)
|
||||
void Signature::addLink(const Link & link)
|
||||
{
|
||||
UDEBUG("Add link %d to %d (type=%d)", link.to(), this->id(), (int)link.type());
|
||||
UASSERT(link.from() == this->id());
|
||||
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(), uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str());
|
||||
std::pair<std::map<int, Link>::iterator, bool> pair = _links.insert(std::make_pair(link.to(), link));
|
||||
UASSERT_MSG(pair.second, uFormat("Link %d (type=%d) already added to signature %d!", link.to(), link.type(), this->id()).c_str());
|
||||
_linksModified = true;
|
||||
@@ -134,6 +135,7 @@ void Signature::removeLink(int idTo)
|
||||
int count = (int)_links.erase(idTo);
|
||||
if(count)
|
||||
{
|
||||
UDEBUG("Removed link %d from %d", idTo, this->id());
|
||||
_linksModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ signals:
|
||||
|
||||
private slots:
|
||||
void flushConsole();
|
||||
void updateTextEditBufferSize();
|
||||
|
||||
protected:
|
||||
virtual void handleEvent(UEvent * anEvent);
|
||||
|
||||
@@ -83,6 +83,7 @@ public:
|
||||
const QColor & getLocalLoopClosureColor() const {return _loopClosureLocalColor;}
|
||||
const QColor & getUserLoopClosureColor() const {return _loopClosureUserColor;}
|
||||
const QColor & getVirtualLoopClosureColor() const {return _loopClosureVirtualColor;}
|
||||
const QColor & getNeighborMergedColor() const {return _neighborMergedColor;}
|
||||
const QColor & getRejectedLoopClosureColor() const {return _loopClosureRejectedColor;}
|
||||
const QColor & getLocalPathColor() const {return _localPathColor;}
|
||||
const QColor & getGlobalPathColor() const {return _globalPathColor;}
|
||||
@@ -106,6 +107,7 @@ public:
|
||||
void setLocalLoopClosureColor(const QColor & color);
|
||||
void setUserLoopClosureColor(const QColor & color);
|
||||
void setVirtualLoopClosureColor(const QColor & color);
|
||||
void setNeighborMergedColor(const QColor & color);
|
||||
void setRejectedLoopClosureColor(const QColor & color);
|
||||
void setLocalPathColor(const QColor & color);
|
||||
void setGlobalPathColor(const QColor & color);
|
||||
@@ -137,6 +139,7 @@ private:
|
||||
QColor _loopClosureLocalColor;
|
||||
QColor _loopClosureUserColor;
|
||||
QColor _loopClosureVirtualColor;
|
||||
QColor _neighborMergedColor;
|
||||
QColor _loopClosureRejectedColor;
|
||||
QColor _localPathColor;
|
||||
QColor _globalPathColor;
|
||||
|
||||
@@ -33,9 +33,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <QtGui/QTextCursor>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#define OLD_TIME 1000 //ms
|
||||
#define MAXIMUM_ITEMS 100
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
ConsoleWidget::ConsoleWidget(QWidget * parent) :
|
||||
@@ -44,7 +41,7 @@ ConsoleWidget::ConsoleWidget(QWidget * parent) :
|
||||
_ui = new Ui_consoleWidget();
|
||||
_ui->setupUi(this);
|
||||
UEventsManager::addHandler(this);
|
||||
_ui->textEdit->document()->setMaximumBlockCount(MAXIMUM_ITEMS);
|
||||
_ui->textEdit->document()->setMaximumBlockCount(_ui->spinBox_lines->value());
|
||||
_textCursor = new QTextCursor(_ui->textEdit->document());
|
||||
_ui->textEdit->setFontPointSize(10);
|
||||
QPalette p(_ui->textEdit->palette());
|
||||
@@ -55,6 +52,7 @@ ConsoleWidget::ConsoleWidget(QWidget * parent) :
|
||||
_time.start();
|
||||
_timer.setSingleShot(true);
|
||||
connect(_ui->pushButton_clear, SIGNAL(clicked()), _ui->textEdit, SLOT(clear()));
|
||||
connect(_ui->spinBox_lines, SIGNAL(valueChanged(int)), this, SLOT(updateTextEditBufferSize()));
|
||||
connect(this, SIGNAL(msgReceived(const QString &, int)), this, SLOT(appendMsg(const QString &, int)));
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(flushConsole()));
|
||||
}
|
||||
@@ -72,13 +70,13 @@ void ConsoleWidget::handleEvent(UEvent * anEvent)
|
||||
ULogEvent * logEvent = (ULogEvent*)anEvent;
|
||||
_msgListMutex.lock();
|
||||
_msgList.append(QPair<QString, int>(logEvent->getMsg().c_str(), logEvent->getCode()));
|
||||
if(_msgList.size()>MAXIMUM_ITEMS)
|
||||
while(_ui->spinBox_lines->value()>0 && _msgList.size()>_ui->spinBox_lines->value())
|
||||
{
|
||||
_msgList.pop_front();
|
||||
}
|
||||
_msgListMutex.unlock();
|
||||
|
||||
if(_time.restart() < OLD_TIME)
|
||||
if(_ui->spinBox_time->value()>0 && _time.restart() < _ui->spinBox_time->value())
|
||||
{
|
||||
if(logEvent->getCode() == ULogger::kFatal)
|
||||
{
|
||||
@@ -86,7 +84,7 @@ void ConsoleWidget::handleEvent(UEvent * anEvent)
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(&_timer, "start", Q_ARG(int, OLD_TIME));
|
||||
QMetaObject::invokeMethod(&_timer, "start", Q_ARG(int, _ui->spinBox_time->value()));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -153,4 +151,9 @@ void ConsoleWidget::flushConsole()
|
||||
_ui->textEdit->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
void ConsoleWidget::updateTextEditBufferSize()
|
||||
{
|
||||
_ui->textEdit->document()->setMaximumBlockCount(_ui->spinBox_lines->value());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ bool DataRecorder::init(const QString & path, bool recordInRAM)
|
||||
customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal
|
||||
customParameters.insert(ParametersPair(Parameters::kKpWordsPerImage(), "-1")); // desactivate keypoints extraction
|
||||
customParameters.insert(ParametersPair(Parameters::kMemBinDataKept(), "true")); // to keep images
|
||||
customParameters.insert(ParametersPair(Parameters::kMemBadSignaturesIgnored(), "true")); // make usre memory cleanup is done
|
||||
if(!recordInRAM)
|
||||
{
|
||||
customParameters.insert(ParametersPair(Parameters::kDbSqlite3InMemory(), "false"));
|
||||
@@ -138,6 +139,7 @@ void DataRecorder::addData(const rtabmap::SensorData & data, const Transform & p
|
||||
const Signature * s = memory_->getLastWorkingSignature();
|
||||
totalSizeKB_ += (int)s->sensorData().imageCompressed().total()/1000;
|
||||
totalSizeKB_ += (int)s->sensorData().depthOrRightCompressed().total()/1000;
|
||||
totalSizeKB_ += (int)s->sensorData().laserScanCompressed().total()/1000;
|
||||
memory_->cleanup();
|
||||
|
||||
if(++count_ % 30)
|
||||
|
||||
+173
-50
@@ -96,6 +96,7 @@ DatabaseViewer::DatabaseViewer(QWidget * parent) :
|
||||
ui_->dockWidget_constraints->setVisible(false);
|
||||
ui_->dockWidget_graphView->setVisible(false);
|
||||
ui_->dockWidget_parameters->setVisible(false);
|
||||
ui_->dockWidget_info->setVisible(false);
|
||||
ui_->dockWidget_stereoView->setVisible(false);
|
||||
ui_->dockWidget_view3d->setVisible(false);
|
||||
|
||||
@@ -146,6 +147,7 @@ DatabaseViewer::DatabaseViewer(QWidget * parent) :
|
||||
ui_->menuView->addAction(ui_->dockWidget_stereoView->toggleViewAction());
|
||||
ui_->menuView->addAction(ui_->dockWidget_view3d->toggleViewAction());
|
||||
ui_->menuView->addAction(ui_->dockWidget_parameters->toggleViewAction());
|
||||
ui_->menuView->addAction(ui_->dockWidget_info->toggleViewAction());
|
||||
connect(ui_->dockWidget_graphView->toggleViewAction(), SIGNAL(triggered()), this, SLOT(updateGraphView()));
|
||||
|
||||
connect(ui_->actionQuit, SIGNAL(triggered()), this, SLOT(close()));
|
||||
@@ -328,6 +330,7 @@ DatabaseViewer::DatabaseViewer(QWidget * parent) :
|
||||
ui_->dockWidget_stereoView->installEventFilter(this);
|
||||
ui_->dockWidget_view3d->installEventFilter(this);
|
||||
ui_->dockWidget_parameters->installEventFilter(this);
|
||||
ui_->dockWidget_info->installEventFilter(this);
|
||||
}
|
||||
|
||||
DatabaseViewer::~DatabaseViewer()
|
||||
@@ -828,6 +831,11 @@ void DatabaseViewer::exportDatabase()
|
||||
progressDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
progressDialog->setMaximumSteps(ids.size());
|
||||
progressDialog->show();
|
||||
UINFO("Decompress: rgb=%d depth=%d scan=%d userData=%d",
|
||||
dialog.isRgbExported()?1:0,
|
||||
dialog.isDepthExported()?1:0,
|
||||
dialog.isDepth2dExported()?1:0,
|
||||
dialog.isUserDataExported()?1:0);
|
||||
|
||||
for(int i=0; i<ids.size(); ++i)
|
||||
{
|
||||
@@ -835,7 +843,12 @@ void DatabaseViewer::exportDatabase()
|
||||
|
||||
SensorData data;
|
||||
dbDriver_->getNodeData(id, data);
|
||||
data.uncompressData();
|
||||
cv::Mat depth, rgb, scan, userData;
|
||||
data.uncompressDataConst(
|
||||
!dialog.isRgbExported()?0:&rgb,
|
||||
!dialog.isDepthExported()?0:&depth,
|
||||
!dialog.isDepth2dExported()?0:&scan,
|
||||
!dialog.isUserDataExported()?0:&userData);
|
||||
cv::Mat covariance = cv::Mat::eye(6,6,CV_64FC1);
|
||||
if(dialog.isOdomExported())
|
||||
{
|
||||
@@ -851,28 +864,28 @@ void DatabaseViewer::exportDatabase()
|
||||
if(data.cameraModels().size())
|
||||
{
|
||||
sensorData = rtabmap::SensorData(
|
||||
dialog.isDepth2dExported()?data.laserScanRaw():cv::Mat(),
|
||||
scan,
|
||||
dialog.isDepth2dExported()?data.laserScanMaxPts():0,
|
||||
dialog.isDepth2dExported()?data.laserScanMaxRange():0,
|
||||
dialog.isRgbExported()?data.imageRaw():cv::Mat(),
|
||||
dialog.isDepthExported()?data.depthOrRightRaw():cv::Mat(),
|
||||
rgb,
|
||||
depth,
|
||||
data.cameraModels(),
|
||||
data.id(),
|
||||
data.stamp(),
|
||||
dialog.isUserDataExported()?data.userDataRaw():cv::Mat());
|
||||
userData);
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorData = rtabmap::SensorData(
|
||||
dialog.isDepth2dExported()?data.laserScanRaw():cv::Mat(),
|
||||
scan,
|
||||
dialog.isDepth2dExported()?data.laserScanMaxPts():0,
|
||||
dialog.isDepth2dExported()?data.laserScanMaxRange():0,
|
||||
dialog.isRgbExported()?data.imageRaw():cv::Mat(),
|
||||
dialog.isDepthExported()?data.depthOrRightRaw():cv::Mat(),
|
||||
rgb,
|
||||
depth,
|
||||
data.stereoCameraModel(),
|
||||
data.id(),
|
||||
data.stamp(),
|
||||
dialog.isUserDataExported()?data.userDataRaw():cv::Mat());
|
||||
userData);
|
||||
}
|
||||
|
||||
recorder.addData(sensorData, dialog.isOdomExported()?poses.at(id):Transform(), covariance);
|
||||
@@ -1029,6 +1042,7 @@ void DatabaseViewer::updateIds()
|
||||
return;
|
||||
}
|
||||
|
||||
UINFO("Loading all IDs...");
|
||||
std::set<int> ids;
|
||||
dbDriver_->getAllNodeIds(ids);
|
||||
ids_ = QList<int>::fromStdList(std::list<int>(ids.begin(), ids.end()));
|
||||
@@ -1040,6 +1054,12 @@ void DatabaseViewer::updateIds()
|
||||
linksRefined_.clear();
|
||||
linksRemoved_.clear();
|
||||
ui_->label_optimizeFrom->setText(tr("Optimize from"));
|
||||
std::multimap<int, Link> links;
|
||||
dbDriver_->getAllLinks(links, true);
|
||||
UDEBUG("%d total links loaded", (int)links.size());
|
||||
double totalOdom = 0.0;
|
||||
Transform previousPose;
|
||||
int sessions = ids_.size()?1:0;
|
||||
for(int i=0; i<ids_.size(); ++i)
|
||||
{
|
||||
idToIndex_.insert(ids_[i], i);
|
||||
@@ -1051,22 +1071,60 @@ void DatabaseViewer::updateIds()
|
||||
int mapId;
|
||||
dbDriver_->getNodeInfo(ids_[i], p, mapId, w, l, s);
|
||||
mapIds_.insert(std::make_pair(ids_[i], mapId));
|
||||
poses_.insert(std::make_pair(ids_[i], p));
|
||||
|
||||
//links
|
||||
std::map<int, Link> neighbors;
|
||||
dbDriver_->loadLinks(ids_[i], neighbors); // including loop closures
|
||||
for(std::map<int, Link>::iterator jter=neighbors.begin(); jter!=neighbors.end(); ++jter)
|
||||
if(i>0)
|
||||
{
|
||||
if( jter->second.isValid() && // null transform means a rehearsed location
|
||||
uContains(poses_, jter->first) &&
|
||||
graph::findLink(links_, ids_[i], jter->first) == links_.end() &&
|
||||
(jter->second.type() == Link::kNeighbor || jter->first < ids_[i])) //Loop parent to child
|
||||
if(mapIds_.at(ids_[i-1]) == mapId)
|
||||
{
|
||||
links_.insert(std::make_pair(ids_[i], jter->second));
|
||||
if(!p.isNull() && !previousPose.isNull())
|
||||
{
|
||||
totalOdom += p.getDistance(previousPose);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++sessions;
|
||||
}
|
||||
}
|
||||
previousPose=p;
|
||||
|
||||
//links
|
||||
bool linksInserted = false;
|
||||
for(std::map<int, Link>::iterator jter=links.find(ids_[i]); jter!=links.end() && jter->first == ids_[i]; ++jter)
|
||||
{
|
||||
if( jter->second.isValid() && // null transform means a rehearsed location
|
||||
ids.find(jter->second.from()) != ids.end() &&
|
||||
ids.find(jter->second.to()) != ids.end() &&
|
||||
graph::findLink(links_, jter->second.from(), jter->second.to()) == links_.end() &&
|
||||
graph::findLink(links, jter->second.from(), jter->second.to(), false) != links.end() &&
|
||||
graph::findLink(links, jter->second.to(), jter->second.from(), false) != links.end())
|
||||
{
|
||||
links_.insert(std::make_pair(ids_[i], jter->second));
|
||||
linksInserted = true;
|
||||
}
|
||||
}
|
||||
if(linksInserted)
|
||||
{
|
||||
poses_.insert(std::make_pair(ids_[i], p));
|
||||
}
|
||||
}
|
||||
UDEBUG("Update database info...");
|
||||
ui_->label_odom->setText(tr("%1 m").arg(totalOdom));
|
||||
ui_->label_sessions->setNum(sessions);
|
||||
ui_->label_nodes->setText(tr("%1 nodes").arg(ids.size()));
|
||||
ui_->label_version->setText(dbDriver_->getDatabaseVersion().c_str());
|
||||
ui_->label_db_size->setText(tr("%1 MB").arg(dbDriver_->getMemoryUsed()/1000000));
|
||||
ui_->label_words->setText(tr("%1 words").arg(dbDriver_->getTotalDictionarySize()));
|
||||
ui_->label_global_map->setText(tr("%1 nodes").arg(poses_.size()));
|
||||
ui_->label_rgb_size->setText(tr("%1 MB").arg(dbDriver_->getImagesMemoryUsed()/1000000));
|
||||
ui_->label_depth_size->setText(tr("%1 MB").arg(dbDriver_->getDepthImagesMemoryUsed()/1000000));
|
||||
ui_->label_scan_size->setText(tr("%1 MB").arg(dbDriver_->getLaserScansMemoryUsed()/1000000));
|
||||
ui_->label_userData_size->setText(tr("%1 bytes").arg(dbDriver_->getUserDataMemoryUsed()));
|
||||
ui_->label_words_size->setText(tr("%1 bytes").arg(dbDriver_->getWordsMemoryUsed()));
|
||||
ui_->label_wm->setText(tr("%1 nodes").arg(dbDriver_->getLastNodesSize()));
|
||||
ui_->label_dictionary->setText(tr("%1 words").arg(dbDriver_->getLastDictionarySize()));
|
||||
|
||||
UINFO("Loaded %d ids, %d poses and %d links", (int)ids_.size(), (int)poses_.size(), (int)links_.size());
|
||||
|
||||
if(ids.size())
|
||||
{
|
||||
@@ -1094,9 +1152,12 @@ void DatabaseViewer::updateIds()
|
||||
links_.clear();
|
||||
}
|
||||
|
||||
ui_->spinBox_optimizationsFrom->setRange(ids_.first(), ids_.last());
|
||||
ui_->spinBox_optimizationsFrom->setValue(ids_.first());
|
||||
ui_->label_optimizeFrom->setText(tr("Optimize from [%1, %2]").arg(ids_.first()).arg(ids_.last()));
|
||||
if(poses_.size())
|
||||
{
|
||||
ui_->spinBox_optimizationsFrom->setRange(poses_.begin()->first, poses_.rbegin()->first);
|
||||
ui_->spinBox_optimizationsFrom->setValue(poses_.begin()->first);
|
||||
ui_->label_optimizeFrom->setText(tr("Optimize from [%1, %2]").arg(poses_.begin()->first).arg(poses_.rbegin()->first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,7 +1171,8 @@ void DatabaseViewer::updateIds()
|
||||
{
|
||||
if(!iter->second.transform().isNull())
|
||||
{
|
||||
if(iter->second.type() == rtabmap::Link::kNeighbor)
|
||||
if(iter->second.type() == rtabmap::Link::kNeighbor ||
|
||||
iter->second.type() == rtabmap::Link::kNeighborMerged)
|
||||
{
|
||||
neighborLinks_.append(iter->second);
|
||||
}
|
||||
@@ -1227,7 +1289,8 @@ void DatabaseViewer::generateLocalGraph()
|
||||
if( !uContains(ids, iter->first))
|
||||
{
|
||||
UASSERT(iter->second.type() != Link::kUndef);
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
if(iter->second.type() == Link::kNeighbor ||
|
||||
iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
nextMargin.insert(iter->first);
|
||||
}
|
||||
@@ -1338,6 +1401,7 @@ void DatabaseViewer::view3DMap()
|
||||
QMessageBox::warning(this, tr("Cannot view 3D map"), tr("The database is empty..."));
|
||||
return;
|
||||
}
|
||||
|
||||
if(graphes_.empty())
|
||||
{
|
||||
this->updateGraphView();
|
||||
@@ -1798,13 +1862,30 @@ void DatabaseViewer::update(int value,
|
||||
}
|
||||
|
||||
// 3d view
|
||||
if(view3D->isVisible() && !data.depthOrRightRaw().empty())
|
||||
if(view3D->isVisible() &&
|
||||
(!data.depthOrRightRaw().empty() ||
|
||||
!data.laserScanRaw().empty()))
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
|
||||
cloud = util3d::cloudRGBFromSensorData(data);
|
||||
if(cloud->size())
|
||||
if(!data.depthOrRightRaw().empty())
|
||||
{
|
||||
view3D->addOrUpdateCloud("0", cloud);
|
||||
if(!data.imageRaw().empty())
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
|
||||
cloud = util3d::cloudRGBFromSensorData(data);
|
||||
if(cloud->size())
|
||||
{
|
||||
view3D->addOrUpdateCloud("0", cloud);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud;
|
||||
cloud = util3d::cloudFromSensorData(data);
|
||||
if(cloud->size())
|
||||
{
|
||||
view3D->addOrUpdateCloud("0", cloud);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//add scan
|
||||
@@ -1845,7 +1926,8 @@ void DatabaseViewer::update(int value,
|
||||
QString strParents, strChildren;
|
||||
for(std::map<int, rtabmap::Link>::iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() != Link::kNeighbor)
|
||||
if(iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
if(iter->first < id)
|
||||
{
|
||||
@@ -2271,7 +2353,8 @@ void DatabaseViewer::updateConstraintView()
|
||||
Link link = this->findActiveLink(ids_.at(ui_->horizontalSlider_A->value()), ids_.at(ui_->horizontalSlider_B->value()));
|
||||
if(link.isValid())
|
||||
{
|
||||
if(link.type() == Link::kNeighbor)
|
||||
if(link.type() == Link::kNeighbor ||
|
||||
link.type() == Link::kNeighborMerged)
|
||||
{
|
||||
this->updateConstraintView(neighborLinks_.at(ui_->horizontalSlider_neighbors->value()), false);
|
||||
}
|
||||
@@ -2304,17 +2387,25 @@ void DatabaseViewer::updateConstraintView(
|
||||
ui_->checkBox_showOptimized->setEnabled(false);
|
||||
UASSERT(!t.isNull() && dbDriver_);
|
||||
|
||||
ui_->label_type->setNum(link.type());
|
||||
ui_->label_type->setText(tr("%1 (%2)")
|
||||
.arg(link.type())
|
||||
.arg(link.type()==Link::kNeighbor?"Neigbor":
|
||||
link.type()==Link::kNeighbor?"Merged neighbor":
|
||||
link.type()==Link::kGlobalClosure?":Loop closure":
|
||||
link.type()==Link::kLocalSpaceClosure?"Space proximity link":
|
||||
link.type()==Link::kLocalTimeClosure?"Time proximity link":
|
||||
link.type()==Link::kUserClosure?"User link":
|
||||
link.type()==Link::kVirtualClosure?"Virtual link":"Undefined"));
|
||||
ui_->label_variance->setText(QString("%1, %2")
|
||||
.arg(sqrt(link.rotVariance()))
|
||||
.arg(sqrt(link.transVariance())));
|
||||
ui_->label_constraint->setText(QString("%1").arg(t.prettyPrint().c_str()).replace(" ", "\n"));
|
||||
if(link.type() == Link::kNeighbor &&
|
||||
if((link.type() == Link::kNeighbor || link.type() == Link::kNeighborMerged) &&
|
||||
graphes_.size() &&
|
||||
(int)graphes_.size()-1 == ui_->horizontalSlider_iterations->maximum())
|
||||
{
|
||||
std::map<int, rtabmap::Transform> & graph = uValueAt(graphes_, ui_->horizontalSlider_iterations->value());
|
||||
if(link.type() == Link::kNeighbor)
|
||||
if(link.type() == Link::kNeighbor || link.type() == Link::kNeighborMerged)
|
||||
{
|
||||
std::map<int, rtabmap::Transform>::iterator iterFrom = graph.find(link.from());
|
||||
std::map<int, rtabmap::Transform>::iterator iterTo = graph.find(link.to());
|
||||
@@ -2395,14 +2486,20 @@ void DatabaseViewer::updateConstraintView(
|
||||
if(ui_->checkBox_show3Dclouds->isChecked())
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudFrom, cloudTo;
|
||||
cloudFrom=util3d::cloudRGBFromSensorData(dataFrom, 1);
|
||||
cloudTo=util3d::cloudRGBFromSensorData(dataTo, 1);
|
||||
if(!dataFrom.imageRaw().empty() && !dataFrom.depthOrRightRaw().empty())
|
||||
{
|
||||
cloudFrom=util3d::cloudRGBFromSensorData(dataFrom, 1);
|
||||
}
|
||||
if(!dataTo.imageRaw().empty() && !dataTo.depthOrRightRaw().empty())
|
||||
{
|
||||
cloudTo=util3d::cloudRGBFromSensorData(dataTo, 1);
|
||||
}
|
||||
|
||||
if(cloudFrom->size())
|
||||
if(cloudFrom.get() && cloudFrom->size())
|
||||
{
|
||||
ui_->constraintsViewer->addOrUpdateCloud("cloud0", cloudFrom, Transform::getIdentity(), Qt::red);
|
||||
}
|
||||
if(cloudTo->size())
|
||||
if(cloudTo.get() && cloudTo->size())
|
||||
{
|
||||
cloudTo = rtabmap::util3d::transformPointCloud(cloudTo, t);
|
||||
ui_->constraintsViewer->addOrUpdateCloud("cloud1", cloudTo, Transform::getIdentity(), Qt::cyan);
|
||||
@@ -2737,7 +2834,9 @@ void DatabaseViewer::updateConstraintButtons()
|
||||
{
|
||||
if(!containsLink(linksRemoved_, from ,to))
|
||||
{
|
||||
ui_->pushButton_reject->setEnabled(currentLink.type() != Link::kNeighbor);
|
||||
ui_->pushButton_reject->setEnabled(
|
||||
currentLink.type() != Link::kNeighbor &&
|
||||
currentLink.type() != Link::kNeighborMerged);
|
||||
}
|
||||
|
||||
//check for modified link
|
||||
@@ -2887,7 +2986,8 @@ void DatabaseViewer::sliderIterationsValueChanged(int value)
|
||||
{
|
||||
const rtabmap::Transform & poseA = jterA->second;
|
||||
const rtabmap::Transform & poseB = jterB->second;
|
||||
if(iter->second.type() == rtabmap::Link::kNeighbor)
|
||||
if(iter->second.type() == rtabmap::Link::kNeighbor ||
|
||||
iter->second.type() == rtabmap::Link::kNeighborMerged)
|
||||
{
|
||||
Eigen::Vector3f vA, vB;
|
||||
poseA.getTranslation(vA[0], vA[1], vA[2]);
|
||||
@@ -2901,8 +3001,13 @@ void DatabaseViewer::sliderIterationsValueChanged(int value)
|
||||
}
|
||||
void DatabaseViewer::updateGraphView()
|
||||
{
|
||||
if(!ui_->graphViewer->isVisible())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ui_->label_loopClosures->clear();
|
||||
ui_->label_nodes->clear();
|
||||
ui_->label_poses->clear();
|
||||
if(poses_.size())
|
||||
{
|
||||
int fromId = ui_->spinBox_optimizationsFrom->value();
|
||||
@@ -2969,7 +3074,8 @@ void DatabaseViewer::updateGraphView()
|
||||
std::multimap<int, Link> tmp = links;
|
||||
for(std::multimap<int, Link>::iterator iter=tmp.begin(); iter!=tmp.end(); ++iter)
|
||||
{
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
if(iter->second.type() == Link::kNeighbor ||
|
||||
iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
Transform poseFrom = uValue(poses, iter->second.from(), Transform());
|
||||
Transform poseTo = uValue(poses, iter->second.to(), Transform());
|
||||
@@ -2988,13 +3094,23 @@ void DatabaseViewer::updateGraphView()
|
||||
}
|
||||
|
||||
// filter links
|
||||
int totalNeighbor = 0;
|
||||
int totalNeighborMerged = 0;
|
||||
int totalGlobal = 0;
|
||||
int totalLocalTime = 0;
|
||||
int totalLocalSpace = 0;
|
||||
int totalUser = 0;
|
||||
for(std::multimap<int, rtabmap::Link>::iterator iter=links.begin(); iter!=links.end();)
|
||||
{
|
||||
if(iter->second.type() == Link::kGlobalClosure)
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
{
|
||||
++totalNeighbor;
|
||||
}
|
||||
else if(iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
++totalNeighborMerged;
|
||||
}
|
||||
else if(iter->second.type() == Link::kGlobalClosure)
|
||||
{
|
||||
if(ui_->checkBox_ignoreGlobalLoop->isChecked())
|
||||
{
|
||||
@@ -3032,7 +3148,13 @@ void DatabaseViewer::updateGraphView()
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
ui_->label_loopClosures->setText(tr("(%1, %2, %3, %4)").arg(totalGlobal).arg(totalLocalSpace).arg(totalLocalTime).arg(totalUser));
|
||||
ui_->label_loopClosures->setText(tr("(%1, %2, %3, %4, %5, %6)")
|
||||
.arg(totalNeighbor)
|
||||
.arg(totalNeighborMerged)
|
||||
.arg(totalGlobal)
|
||||
.arg(totalLocalSpace)
|
||||
.arg(totalLocalTime)
|
||||
.arg(totalUser));
|
||||
|
||||
graph::Optimizer * optimizer = 0;
|
||||
if(ui_->comboBox_graphOptimizer->currentIndex() == graph::Optimizer::kTypeGTSAM)
|
||||
@@ -3064,7 +3186,7 @@ void DatabaseViewer::updateGraphView()
|
||||
}
|
||||
std::map<int, rtabmap::Transform> posesOut;
|
||||
std::multimap<int, rtabmap::Link> linksOut;
|
||||
UINFO("Get connected graph (%d poses, %d links)", (int)poses.size(), (int)links.size());
|
||||
UINFO("Get connected graph from %d (%d poses, %d links)", fromId, (int)poses.size(), (int)links.size());
|
||||
optimizer->getConnectedGraph(
|
||||
fromId,
|
||||
poses,
|
||||
@@ -3079,7 +3201,7 @@ void DatabaseViewer::updateGraphView()
|
||||
ui_->label_timeOptimization->setNum(double(time.elapsed())/1000.0);
|
||||
graphes_.push_back(finalPoses);
|
||||
graphLinks_ = linksOut;
|
||||
ui_->label_nodes->setNum((int)finalPoses.size());
|
||||
ui_->label_poses->setNum((int)finalPoses.size());
|
||||
delete optimizer;
|
||||
if(posesOut.size() && finalPoses.empty())
|
||||
{
|
||||
@@ -3169,12 +3291,12 @@ void DatabaseViewer::refineConstraint(int from, int to, bool silent, bool update
|
||||
}
|
||||
Transform t = currentLink.transform();
|
||||
if(ui_->checkBox_showOptimized->isChecked() &&
|
||||
currentLink.type() == Link::kNeighbor &&
|
||||
(currentLink.type() == Link::kNeighbor || currentLink.type() == Link::kNeighborMerged) &&
|
||||
graphes_.size() &&
|
||||
(int)graphes_.size()-1 == ui_->horizontalSlider_iterations->maximum())
|
||||
{
|
||||
std::map<int, rtabmap::Transform> & graph = uValueAt(graphes_, ui_->horizontalSlider_iterations->value());
|
||||
if(currentLink.type() == Link::kNeighbor)
|
||||
if(currentLink.type() == Link::kNeighbor || currentLink.type() == Link::kNeighborMerged)
|
||||
{
|
||||
std::map<int, rtabmap::Transform>::iterator iterFrom = graph.find(currentLink.from());
|
||||
std::map<int, rtabmap::Transform>::iterator iterTo = graph.find(currentLink.to());
|
||||
@@ -3713,7 +3835,7 @@ void DatabaseViewer::rejectConstraint()
|
||||
iter = rtabmap::graph::findLink(links_, from, to);
|
||||
if(iter != links_.end())
|
||||
{
|
||||
if(iter->second.type() == Link::kNeighbor)
|
||||
if(iter->second.type() == Link::kNeighbor || iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
UWARN("Cannot reject neighbor links (%d->%d)", from, to);
|
||||
return;
|
||||
@@ -3814,7 +3936,8 @@ void DatabaseViewer::updateLoopClosuresSlider(int from, int to)
|
||||
{
|
||||
if(!iter->second.transform().isNull())
|
||||
{
|
||||
if(iter->second.type() != rtabmap::Link::kNeighbor)
|
||||
if(iter->second.type() != rtabmap::Link::kNeighbor &&
|
||||
iter->second.type() != rtabmap::Link::kNeighborMerged)
|
||||
{
|
||||
if((iter->second.from() == from && iter->second.to() == to) ||
|
||||
(iter->second.to() == from && iter->second.from() == to))
|
||||
|
||||
@@ -179,6 +179,7 @@ GraphViewer::GraphViewer(QWidget * parent) :
|
||||
_loopClosureLocalColor(Qt::yellow),
|
||||
_loopClosureUserColor(Qt::red),
|
||||
_loopClosureVirtualColor(Qt::magenta),
|
||||
_neighborMergedColor(QColor(255,170,0)),
|
||||
_loopClosureRejectedColor(Qt::black),
|
||||
_localPathColor(Qt::cyan),
|
||||
_globalPathColor(Qt::darkMagenta),
|
||||
@@ -344,6 +345,10 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
|
||||
{
|
||||
linkItem->setColor(_loopClosureVirtualColor);
|
||||
}
|
||||
else if(iter->second.type() == Link::kNeighborMerged)
|
||||
{
|
||||
linkItem->setColor(_neighborMergedColor);
|
||||
}
|
||||
else if(iter->second.type() == Link::kUserClosure)
|
||||
{
|
||||
linkItem->setColor(_loopClosureUserColor);
|
||||
@@ -381,7 +386,8 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
|
||||
{
|
||||
t = t.inverse();
|
||||
}
|
||||
if(iter->second.type() != Link::kNeighbor)
|
||||
if(iter->second.type() != Link::kNeighbor &&
|
||||
iter->second.type() != Link::kNeighborMerged)
|
||||
{
|
||||
float linearError = uMax3(
|
||||
fabs(iter->second.transform().x() - t.x()),
|
||||
@@ -668,6 +674,7 @@ void GraphViewer::saveSettings(QSettings & settings, const QString & group) cons
|
||||
settings.setValue("local_color", this->getLocalLoopClosureColor());
|
||||
settings.setValue("user_color", this->getUserLoopClosureColor());
|
||||
settings.setValue("virtual_color", this->getVirtualLoopClosureColor());
|
||||
settings.setValue("neighbor_merged_color", this->getNeighborMergedColor());
|
||||
settings.setValue("rejected_color", this->getRejectedLoopClosureColor());
|
||||
settings.setValue("local_path_color", this->getLocalPathColor());
|
||||
settings.setValue("global_path_color", this->getGlobalPathColor());
|
||||
@@ -700,6 +707,7 @@ void GraphViewer::loadSettings(QSettings & settings, const QString & group)
|
||||
this->setLocalLoopClosureColor(settings.value("local_color", this->getLocalLoopClosureColor()).value<QColor>());
|
||||
this->setUserLoopClosureColor(settings.value("user_color", this->getUserLoopClosureColor()).value<QColor>());
|
||||
this->setVirtualLoopClosureColor(settings.value("virtual_color", this->getVirtualLoopClosureColor()).value<QColor>());
|
||||
this->setNeighborMergedColor(settings.value("neighbor_merged_color", this->getNeighborMergedColor()).value<QColor>());
|
||||
this->setRejectedLoopClosureColor(settings.value("rejected_color", this->getRejectedLoopClosureColor()).value<QColor>());
|
||||
this->setLocalPathColor(settings.value("local_path_color", this->getLocalPathColor()).value<QColor>());
|
||||
this->setGlobalPathColor(settings.value("global_path_color", this->getGlobalPathColor()).value<QColor>());
|
||||
@@ -841,6 +849,17 @@ void GraphViewer::setVirtualLoopClosureColor(const QColor & color)
|
||||
}
|
||||
}
|
||||
}
|
||||
void GraphViewer::setNeighborMergedColor(const QColor & color)
|
||||
{
|
||||
_neighborMergedColor = color;
|
||||
for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
|
||||
{
|
||||
if(iter.value()->linkType() == Link::kNeighborMerged)
|
||||
{
|
||||
iter.value()->setColor(_neighborMergedColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
void GraphViewer::setRejectedLoopClosureColor(const QColor & color)
|
||||
{
|
||||
_loopClosureRejectedColor = color;
|
||||
@@ -936,6 +955,7 @@ void GraphViewer::restoreDefaults()
|
||||
setLocalLoopClosureColor(Qt::yellow);
|
||||
setUserLoopClosureColor(Qt::red);
|
||||
setVirtualLoopClosureColor(Qt::magenta);
|
||||
setNeighborMergedColor(QColor(255,170,0));
|
||||
setGridMapVisible(true);
|
||||
}
|
||||
|
||||
@@ -978,6 +998,7 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
QAction * aChangeLocalLoopColor = menuLink->addAction(tr("Local loop closure"));
|
||||
QAction * aChangeUserLoopColor = menuLink->addAction(tr("User loop closure"));
|
||||
QAction * aChangeVirtualLoopColor = menuLink->addAction(tr("Virtual loop closure"));
|
||||
QAction * aChangeNeighborMergedColor = menuLink->addAction(tr("Neighbor merged"));
|
||||
QAction * aChangeRejectedLoopColor = menuLink->addAction(tr("Outlier loop closure"));
|
||||
QAction * aChangeRejectedLoopThr = menuLink->addAction(tr("Set outlier threshold..."));
|
||||
QAction * aChangeLocalPathColor = menuLink->addAction(tr("Local path"));
|
||||
@@ -991,6 +1012,7 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
aChangeLocalLoopColor->setIcon(createIcon(_loopClosureLocalColor));
|
||||
aChangeUserLoopColor->setIcon(createIcon(_loopClosureUserColor));
|
||||
aChangeVirtualLoopColor->setIcon(createIcon(_loopClosureVirtualColor));
|
||||
aChangeNeighborMergedColor->setIcon(createIcon(_neighborMergedColor));
|
||||
aChangeRejectedLoopColor->setIcon(createIcon(_loopClosureRejectedColor));
|
||||
aChangeLocalPathColor->setIcon(createIcon(_localPathColor));
|
||||
aChangeGlobalPathColor->setIcon(createIcon(_globalPathColor));
|
||||
@@ -1001,6 +1023,7 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
aChangeLocalLoopColor->setIconVisibleInMenu(true);
|
||||
aChangeUserLoopColor->setIconVisibleInMenu(true);
|
||||
aChangeVirtualLoopColor->setIconVisibleInMenu(true);
|
||||
aChangeNeighborMergedColor->setIconVisibleInMenu(true);
|
||||
aChangeRejectedLoopColor->setIconVisibleInMenu(true);
|
||||
aChangeLocalPathColor->setIconVisibleInMenu(true);
|
||||
aChangeGlobalPathColor->setIconVisibleInMenu(true);
|
||||
@@ -1149,6 +1172,7 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
r == aChangeLocalLoopColor ||
|
||||
r == aChangeUserLoopColor ||
|
||||
r == aChangeVirtualLoopColor ||
|
||||
r == aChangeNeighborMergedColor ||
|
||||
r == aChangeRejectedLoopColor ||
|
||||
r == aChangeLocalPathColor ||
|
||||
r == aChangeGlobalPathColor ||
|
||||
@@ -1180,6 +1204,10 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
{
|
||||
color = _loopClosureVirtualColor;
|
||||
}
|
||||
else if(r == aChangeNeighborMergedColor)
|
||||
{
|
||||
color = _neighborMergedColor;
|
||||
}
|
||||
else if(r == aChangeRejectedLoopColor)
|
||||
{
|
||||
color = _loopClosureRejectedColor;
|
||||
@@ -1232,6 +1260,10 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
{
|
||||
this->setVirtualLoopClosureColor(color);
|
||||
}
|
||||
else if(r == aChangeNeighborMergedColor)
|
||||
{
|
||||
this->setNeighborMergedColor(color);
|
||||
}
|
||||
else if(r == aChangeRejectedLoopColor)
|
||||
{
|
||||
this->setRejectedLoopClosureColor(color);
|
||||
|
||||
@@ -2229,6 +2229,12 @@ void MainWindow::processRtabmapGlobalPathEvent(const rtabmap::RtabmapGlobalPathE
|
||||
info->show();
|
||||
}
|
||||
}
|
||||
else if(event.getPoses().empty() && _waypoints.size())
|
||||
{
|
||||
// resend the same goal
|
||||
uSleep(1000);
|
||||
this->postGoal(_waypoints.at(_waypointsIndex % _waypoints.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::processRtabmapLabelErrorEvent(int id, const QString & label)
|
||||
|
||||
@@ -457,6 +457,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
|
||||
_ui->general_checkBox_keepRawData->setObjectName(Parameters::kMemImageKept().c_str());
|
||||
_ui->general_checkBox_keepBinaryData->setObjectName(Parameters::kMemBinDataKept().c_str());
|
||||
_ui->general_checkBox_saveDepth16bits->setObjectName(Parameters::kMemSaveDepth16Format().c_str());
|
||||
_ui->general_checkBox_reduceGraph->setObjectName(Parameters::kMemReduceGraph().c_str());
|
||||
_ui->general_checkBox_keepNotLinkedNodes->setObjectName(Parameters::kMemNotLinkedNodesKept().c_str());
|
||||
_ui->general_spinBox_maxStMemSize->setObjectName(Parameters::kMemSTMSize().c_str());
|
||||
_ui->doubleSpinBox_similarityThreshold->setObjectName(Parameters::kMemRehearsalSimilarity().c_str());
|
||||
|
||||
+249
-13
@@ -50,7 +50,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>197</width>
|
||||
<width>166</width>
|
||||
<height>173</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -236,7 +236,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>196</width>
|
||||
<width>165</width>
|
||||
<height>173</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -739,7 +739,7 @@
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_52">
|
||||
<property name="text">
|
||||
<string>Nodes</string>
|
||||
<string>Poses</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -753,14 +753,14 @@
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label_41">
|
||||
<property name="text">
|
||||
<string>Loop closures (G, LS, LT, U)</string>
|
||||
<string>Links (N, NM, G, LS, LT, U)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Total path length (m)</string>
|
||||
<string>Path length (m)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -800,7 +800,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_nodes">
|
||||
<widget class="QLabel" name="label_poses">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
@@ -836,7 +836,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>314</width>
|
||||
<width>338</width>
|
||||
<height>303</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -1055,7 +1055,7 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>-129</y>
|
||||
<y>0</y>
|
||||
<width>351</width>
|
||||
<height>407</height>
|
||||
</rect>
|
||||
@@ -1389,7 +1389,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>333</width>
|
||||
<width>338</width>
|
||||
<height>333</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -1628,7 +1628,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>276</width>
|
||||
<width>338</width>
|
||||
<height>377</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -1885,8 +1885,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>201</width>
|
||||
<height>117</height>
|
||||
<width>351</width>
|
||||
<height>176</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -1985,7 +1985,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>285</width>
|
||||
<width>338</width>
|
||||
<height>309</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -2330,6 +2330,242 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QDockWidget" name="dockWidget_info">
|
||||
<property name="windowTitle">
|
||||
<string>Info</string>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_4">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_15" columnstretch="0,1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_70">
|
||||
<property name="text">
|
||||
<string>Database version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_version">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_69">
|
||||
<property name="text">
|
||||
<string>Sessions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_sessions">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Nodes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_nodes">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_80">
|
||||
<property name="text">
|
||||
<string>Dictionary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_words">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_78">
|
||||
<property name="text">
|
||||
<string>Global map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label_global_map">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_79">
|
||||
<property name="text">
|
||||
<string>Working memory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_wm">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_77">
|
||||
<property name="text">
|
||||
<string>Last dictionary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label_dictionary">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_74">
|
||||
<property name="text">
|
||||
<string>Odometry</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="label_odom">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_14" columnstretch="0,1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_68">
|
||||
<property name="text">
|
||||
<string>Database size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_db_size">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_72">
|
||||
<property name="text">
|
||||
<string>RGB images</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_rgb_size">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_73">
|
||||
<property name="text">
|
||||
<string>Depth images</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_depth_size">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_71">
|
||||
<property name="text">
|
||||
<string>Laser scans</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_scan_size">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_75">
|
||||
<property name="text">
|
||||
<string>User data </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label_userData_size">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_76">
|
||||
<property name="text">
|
||||
<string>Dictionary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_words_size">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<action name="actionOpen_database">
|
||||
<property name="text">
|
||||
<string>Open database</string>
|
||||
|
||||
@@ -25,11 +25,44 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_clear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,1">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBox_lines">
|
||||
<property name="suffix">
|
||||
<string> lines</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>99999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBox_time">
|
||||
<property name="suffix">
|
||||
<string> ms</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_clear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>-390</y>
|
||||
<y>0</y>
|
||||
<width>760</width>
|
||||
<height>1705</height>
|
||||
</rect>
|
||||
@@ -86,7 +86,7 @@
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>19</number>
|
||||
<number>7</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page_22">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_29">
|
||||
@@ -3950,7 +3950,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QDoubleSpinBox" name="general_doubleSpinBox_laserScanVoxel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
@@ -3975,7 +3975,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<item row="10" column="0">
|
||||
<widget class="QSpinBox" name="spinBox_imageDecimation">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
@@ -4017,7 +4017,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="label_retrieved_4">
|
||||
<property name="text">
|
||||
<string>Initialize the Woking Memory with all nodes from Long-Term memory, instead of only nodes of the last session. This may be useful in localization mode, where less processing time is required than in SLAM mode, so more nodes can be kept in Working Memory.</string>
|
||||
@@ -4115,7 +4115,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="general_checkBox_initWMWithAllNodes">
|
||||
<property name="text">
|
||||
<string/>
|
||||
@@ -4125,7 +4125,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="QLabel" name="label_retrieved_5">
|
||||
<property name="text">
|
||||
<string>Keep raw sensor data. Only useful to save loop closure computation time when features re-extraction is enabled. Disable to save RAM memory.</string>
|
||||
@@ -4138,7 +4138,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="general_checkBox_keepRawData">
|
||||
<property name="text">
|
||||
<string/>
|
||||
@@ -4148,7 +4148,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<item row="10" column="1">
|
||||
<widget class="QLabel" name="label_retrieved_6">
|
||||
<property name="text">
|
||||
<string>Image decimation. This feature can be used to save images in lower resolution (size/decimation).</string>
|
||||
@@ -4184,7 +4184,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="QLabel" name="label_retrieved_8">
|
||||
<property name="text">
|
||||
<string>If > 0.0, voxelize laser scans when creating a location. This feature can be used to save laser scans already voxelized.</string>
|
||||
@@ -4197,7 +4197,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QLabel" name="label_retrieved_9">
|
||||
<property name="text">
|
||||
<string>Save depth image into 16 bits format to reduce memory used. Warning: values over ~65 meters are ignored (maximum 65535 millimeters).</string>
|
||||
@@ -4210,7 +4210,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="general_checkBox_saveDepth16bits">
|
||||
<property name="text">
|
||||
<string/>
|
||||
@@ -4220,6 +4220,29 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label_retrieved_10">
|
||||
<property name="text">
|
||||
<string>Reduce graph. Merge nodes when loop closures are added (ignoring those with user data set).</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="general_checkBox_reduceGraph">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<name>rtabmap</name>
|
||||
<version>0.10.10</version>
|
||||
<version>0.10.11</version>
|
||||
<description>RTAB-Map's standalone library. RTAB-Map is a RGB-D SLAM approach with real-time constraints.</description>
|
||||
<maintainer email="[email protected]">Mathieu Labbe</maintainer>
|
||||
<author>Mathieu Labbe</author>
|
||||
|
||||
@@ -600,13 +600,28 @@ inline std::string uJoin(const std::list<std::string> & strings, const std::stri
|
||||
/**
|
||||
* Check if a character is a digit.
|
||||
* @param c the character
|
||||
* @return if the character is a digit (if c >= '0' && c <= '9')
|
||||
* @return true if the character is a digit (if c >= '0' && c <= '9')
|
||||
*/
|
||||
inline bool uIsDigit(const char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a interger number.
|
||||
* @param str the string
|
||||
* @return true if the string is a integer number
|
||||
*/
|
||||
inline bool uIsInteger(const std::string & str)
|
||||
{
|
||||
bool isInteger = str.size();
|
||||
for(unsigned int i=0; i<str.size() && isInteger; ++i)
|
||||
{
|
||||
isInteger = isdigit(str[i]);
|
||||
}
|
||||
return isInteger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a string into number and character strings.
|
||||
* Example:
|
||||
|
||||
Reference in New Issue
Block a user