mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-01 17:10:26 +08:00
updated util3d::create2dMap() with filling unknown space option (default true)
git-svn-id: http://rtabmap.googlecode.com/svn/trunk/rtabmap@1453 f169173b-cf89-36c8-b27e-44dbe73f0c83
This commit is contained in:
@@ -224,7 +224,7 @@ class RTABMAP_EXP Parameters
|
|||||||
RTABMAP_PARAM(RGBD, ScanMatchingSize, int, 0, "Laser scan matching history for odometry correction (laser scans are required). Set to 0 to disable odometry correction.");
|
RTABMAP_PARAM(RGBD, ScanMatchingSize, int, 0, "Laser scan matching history for odometry correction (laser scans are required). Set to 0 to disable odometry correction.");
|
||||||
RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.0, "Min linear displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.0, "Min linear displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||||
RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.0, "Min angular displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.0, "Min angular displacement to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||||
RTABMAP_PARAM(RGBD, NewMapOdomChangeDistance, float, 1, "A new map is created if a change of odometry translation greater than X m is detected (0 m = disabled).");
|
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, ToroIterations, int, 100, "TORO graph optimization iterations");
|
RTABMAP_PARAM(RGBD, ToroIterations, int, 100, "TORO graph optimization iterations");
|
||||||
RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, true, "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).");
|
RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, true, "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).");
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,20 @@ public:
|
|||||||
// x,y,z, roll,pitch,yaw
|
// x,y,z, roll,pitch,yaw
|
||||||
Transform(float x, float y, float z, float roll, float pitch, float yaw);
|
Transform(float x, float y, float z, float roll, float pitch, float yaw);
|
||||||
|
|
||||||
|
float r11() const {return data_[0];}
|
||||||
|
float r12() const {return data_[1];}
|
||||||
|
float r13() const {return data_[2];}
|
||||||
|
float r21() const {return data_[4];}
|
||||||
|
float r22() const {return data_[5];}
|
||||||
|
float r23() const {return data_[6];}
|
||||||
|
float r31() const {return data_[8];}
|
||||||
|
float r32() const {return data_[9];}
|
||||||
|
float r33() const {return data_[10];}
|
||||||
|
|
||||||
|
float o14() const {return data_[3];}
|
||||||
|
float o24() const {return data_[7];}
|
||||||
|
float o34() const {return data_[11];}
|
||||||
|
|
||||||
float & operator[](int index) {return data_[index];}
|
float & operator[](int index) {return data_[index];}
|
||||||
const float & operator[](int index) const {return data_[index];}
|
const float & operator[](int index) const {return data_[index];}
|
||||||
|
|
||||||
|
|||||||
@@ -390,15 +390,22 @@ bool RTABMAP_EXP loadTOROGraph(const std::string & fileName,
|
|||||||
std::map<int, Transform> & poses,
|
std::map<int, Transform> & poses,
|
||||||
std::multimap<int, std::pair<int, Transform> > & edgeConstraints);
|
std::multimap<int, std::pair<int, Transform> > & edgeConstraints);
|
||||||
|
|
||||||
|
std::map<int, Transform> RTABMAP_EXP radiusPosesFiltering(
|
||||||
|
const std::map<int, Transform> & poses,
|
||||||
|
float radius,
|
||||||
|
float angle);
|
||||||
|
|
||||||
cv::Mat RTABMAP_EXP create2DMap(const std::map<int, Transform> & poses,
|
cv::Mat RTABMAP_EXP create2DMap(const std::map<int, Transform> & poses,
|
||||||
const std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > & scans,
|
const std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > & scans,
|
||||||
float delta,
|
float cellSize,
|
||||||
|
bool unknownSpaceFilled,
|
||||||
float & xMin,
|
float & xMin,
|
||||||
float & yMin);
|
float & yMin);
|
||||||
|
|
||||||
void RTABMAP_EXP rayTrace(const cv::Point2i & start,
|
void RTABMAP_EXP rayTrace(const cv::Point2i & start,
|
||||||
const cv::Point2i & end,
|
const cv::Point2i & end,
|
||||||
cv::Mat & grid);
|
cv::Mat & grid,
|
||||||
|
bool stopOnObstacle);
|
||||||
|
|
||||||
} // namespace util3d
|
} // namespace util3d
|
||||||
} // namespace rtabmap
|
} // namespace rtabmap
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
#include "rtabmap/utilite/UConversion.h"
|
#include "rtabmap/utilite/UConversion.h"
|
||||||
|
#include "rtabmap/utilite/UTimer.h"
|
||||||
#include "rtabmap/core/util3d.h"
|
#include "rtabmap/core/util3d.h"
|
||||||
#include "rtabmap/core/Signature.h"
|
#include "rtabmap/core/Signature.h"
|
||||||
#include "toro3d/treeoptimizer3.hh"
|
#include "toro3d/treeoptimizer3.hh"
|
||||||
@@ -1791,9 +1792,104 @@ bool loadTOROGraph(const std::string & fileName,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::map<int, Transform> radiusPosesFiltering(const std::map<int, Transform> & poses, float radius, float angle)
|
||||||
|
{
|
||||||
|
if(poses.size() > 1 && radius > 0.0f && angle>0.0f)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||||
|
cloud->resize(poses.size());
|
||||||
|
int i=0;
|
||||||
|
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
||||||
|
{
|
||||||
|
(*cloud)[i++] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
// radius filtering
|
||||||
|
std::vector<int> names = uKeys(poses);
|
||||||
|
std::vector<Transform> transforms = uValues(poses);
|
||||||
|
|
||||||
|
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
|
||||||
|
tree->setInputCloud(cloud);
|
||||||
|
std::set<int> indicesChecked;
|
||||||
|
std::set<int> indicesKept;
|
||||||
|
|
||||||
|
for(unsigned int i=0; i<cloud->size(); ++i)
|
||||||
|
{
|
||||||
|
// ignore scans
|
||||||
|
if(indicesChecked.find(i) == indicesChecked.end())
|
||||||
|
{
|
||||||
|
std::vector<int> kIndices;
|
||||||
|
std::vector<float> kDistances;
|
||||||
|
tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
|
||||||
|
|
||||||
|
std::set<int> cloudIndices;
|
||||||
|
const Transform & currentT = transforms.at(i);
|
||||||
|
Eigen::Vector3f vA = util3d::transformToEigen3f(currentT).rotation()*Eigen::Vector3f(1,0,0);
|
||||||
|
for(unsigned int j=0; j<kIndices.size(); ++j)
|
||||||
|
{
|
||||||
|
if(indicesChecked.find(kIndices[j]) == indicesChecked.end())
|
||||||
|
{
|
||||||
|
const Transform & checkT = transforms.at(kIndices[j]);
|
||||||
|
// same orientation?
|
||||||
|
Eigen::Vector3f vB = util3d::transformToEigen3f(checkT).rotation()*Eigen::Vector3f(1,0,0);
|
||||||
|
double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
|
||||||
|
if(a <= angle)
|
||||||
|
{
|
||||||
|
cloudIndices.insert(kIndices[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool firstAdded = false;
|
||||||
|
for(std::set<int>::iterator iter = cloudIndices.begin(); iter!=cloudIndices.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(!firstAdded)
|
||||||
|
{
|
||||||
|
indicesKept.insert(*iter);
|
||||||
|
firstAdded = true;
|
||||||
|
}
|
||||||
|
indicesChecked.insert(*iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//pcl::IndicesPtr indicesOut(new std::vector<int>);
|
||||||
|
//indicesOut->insert(indicesOut->end(), indicesKept.begin(), indicesKept.end());
|
||||||
|
UINFO("Cloud filtered In = %d, Out = %d", cloud->size(), indicesKept.size());
|
||||||
|
//pcl::io::savePCDFile("duplicateIn.pcd", *cloud);
|
||||||
|
//pcl::io::savePCDFile("duplicateOut.pcd", *cloud, *indicesOut);
|
||||||
|
|
||||||
|
std::map<int, Transform> keptPoses;
|
||||||
|
for(std::set<int>::iterator iter = indicesKept.begin(); iter!=indicesKept.end(); ++iter)
|
||||||
|
{
|
||||||
|
keptPoses.insert(std::make_pair(names.at(*iter), transforms.at(*iter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return keptPoses;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return poses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create 2d Occupancy grid (CV_8S)
|
||||||
|
* -1 = unknown
|
||||||
|
* 0 = empty space
|
||||||
|
* 100 = obstacle
|
||||||
|
* @param poses
|
||||||
|
* @param scans
|
||||||
|
* @param cellSize m
|
||||||
|
* @param unknownSpaceFilled if false no fill, otherwise a virtual laser sweeps the unknown space from each pose (stopping on detected obstacle)
|
||||||
|
* @param xMin
|
||||||
|
* @param yMin
|
||||||
|
*/
|
||||||
cv::Mat create2DMap(const std::map<int, Transform> & poses,
|
cv::Mat create2DMap(const std::map<int, Transform> & poses,
|
||||||
const std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > & scans,
|
const std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > & scans,
|
||||||
float delta,
|
float cellSize,
|
||||||
|
bool unknownSpaceFilled,
|
||||||
float & xMin,
|
float & xMin,
|
||||||
float & yMin)
|
float & yMin)
|
||||||
{
|
{
|
||||||
@@ -1826,25 +1922,103 @@ cv::Mat create2DMap(const std::map<int, Transform> & poses,
|
|||||||
float xMax = max.x+1.0f;
|
float xMax = max.x+1.0f;
|
||||||
float yMax = max.y+1.0f;
|
float yMax = max.y+1.0f;
|
||||||
|
|
||||||
map = cv::Mat::ones((yMax - yMin) / delta, (xMax - xMin) / delta, CV_8S)*-1;
|
//UTimer timer;
|
||||||
|
|
||||||
|
map = cv::Mat::ones((yMax - yMin) / cellSize, (xMax - xMin) / cellSize, CV_8S)*-1;
|
||||||
|
std::vector<float> maxSquaredLength(localScans.size(), 0.0f);
|
||||||
|
int j=0;
|
||||||
for(std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr >::iterator iter = localScans.begin(); iter!=localScans.end(); ++iter)
|
for(std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr >::iterator iter = localScans.begin(); iter!=localScans.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
const Transform & pose = poses.at(iter->first);
|
||||||
|
cv::Point2i start((pose.x()-xMin)/cellSize + 0.5f, (pose.y()-yMin)/cellSize + 0.5f);
|
||||||
for(unsigned int i=0; i<iter->second->size(); ++i)
|
for(unsigned int i=0; i<iter->second->size(); ++i)
|
||||||
{
|
{
|
||||||
const Transform & pose = poses.at(iter->first);
|
cv::Point2i end((iter->second->points[i].x-xMin)/cellSize + 0.5f, (iter->second->points[i].y-yMin)/cellSize + 0.5f);
|
||||||
cv::Point2i start((pose.x()-xMin)/delta + 0.5f, (pose.y()-yMin)/delta + 0.5f);
|
|
||||||
cv::Point2i end((iter->second->points[i].x-xMin)/delta + 0.5f, (iter->second->points[i].y-yMin)/delta + 0.5f);
|
|
||||||
|
|
||||||
rayTrace(start, end, map); // trace free space
|
|
||||||
|
|
||||||
map.at<char>(end.y, end.x) = 100; // obstacle
|
map.at<char>(end.y, end.x) = 100; // obstacle
|
||||||
|
rayTrace(start, end, map, true); // trace free space
|
||||||
|
|
||||||
|
float dx = iter->second->points[i].x - pose.x();
|
||||||
|
float dy = iter->second->points[i].y - pose.y();
|
||||||
|
float l = dx*dx + dy*dy;
|
||||||
|
if(l > maxSquaredLength[j])
|
||||||
|
{
|
||||||
|
maxSquaredLength[j] = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
//UWARN("timer=%fs", timer.ticks());
|
||||||
|
|
||||||
|
// now fill unknown spaces
|
||||||
|
if(unknownSpaceFilled)
|
||||||
|
{
|
||||||
|
j=0;
|
||||||
|
for(std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr >::iterator iter = localScans.begin(); iter!=localScans.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(iter->second->size() > 1 && maxSquaredLength[j] > 0.0f)
|
||||||
|
{
|
||||||
|
float maxLength = sqrt(maxSquaredLength[j]);
|
||||||
|
if(maxLength > cellSize)
|
||||||
|
{
|
||||||
|
// compute angle
|
||||||
|
float a = (CV_PI/2.0f) / (maxLength / cellSize);
|
||||||
|
//UWARN("a=%f PI/256=%f", a, CV_PI/256.0f);
|
||||||
|
UASSERT_MSG(a >= 0 && a < 5.0f*CV_PI/8.0f, uFormat("a=%f length=%f cell=%f", a, maxLength, cellSize).c_str());
|
||||||
|
|
||||||
|
const Transform & pose = poses.at(iter->first);
|
||||||
|
cv::Point2i start((pose.x()-xMin)/cellSize + 0.5f, (pose.y()-yMin)/cellSize + 0.5f);
|
||||||
|
|
||||||
|
//UWARN("maxLength = %f", maxLength);
|
||||||
|
//rotate counterclockwise from the first point until we pass the last point
|
||||||
|
cv::Mat rotation = (cv::Mat_<float>(2,2) << cos(a), -sin(a),
|
||||||
|
sin(a), cos(a));
|
||||||
|
cv::Mat origin(2,1,CV_32F), endFirst(2,1,CV_32F), endLast(2,1,CV_32F);
|
||||||
|
origin.at<float>(0) = pose.x();
|
||||||
|
origin.at<float>(1) = pose.y();
|
||||||
|
endFirst.at<float>(0) = iter->second->points[0].x;
|
||||||
|
endFirst.at<float>(1) = iter->second->points[0].y;
|
||||||
|
endLast.at<float>(0) = iter->second->points[iter->second->points.size()-1].x;
|
||||||
|
endLast.at<float>(1) = iter->second->points[iter->second->points.size()-1].y;
|
||||||
|
//UWARN("origin = %f %f", origin.at<float>(0), origin.at<float>(1));
|
||||||
|
//UWARN("endFirst = %f %f", endFirst.at<float>(0), endFirst.at<float>(1));
|
||||||
|
//UWARN("endLast = %f %f", endLast.at<float>(0), endLast.at<float>(1));
|
||||||
|
cv::Mat tmp = (endFirst - origin);
|
||||||
|
cv::Mat endRotated = rotation*((tmp/cv::norm(tmp))*maxLength) + origin;
|
||||||
|
cv::Mat endLastVector(3,1,CV_32F), endRotatedVector(3,1,CV_32F);
|
||||||
|
endLastVector.at<float>(0) = endLast.at<float>(0) - origin.at<float>(0);
|
||||||
|
endLastVector.at<float>(1) = endLast.at<float>(1) - origin.at<float>(1);
|
||||||
|
endLastVector.at<float>(2) = 0.0f;
|
||||||
|
endRotatedVector.at<float>(0) = endRotated.at<float>(0) - origin.at<float>(0);
|
||||||
|
endRotatedVector.at<float>(1) = endRotated.at<float>(1) - origin.at<float>(1);
|
||||||
|
endRotatedVector.at<float>(2) = 0.0f;
|
||||||
|
//UWARN("endRotated = %f %f", endRotated.at<float>(0), endRotated.at<float>(1));
|
||||||
|
while(endRotatedVector.cross(endLastVector).at<float>(2) > 0.0f)
|
||||||
|
{
|
||||||
|
cv::Point2i end((endRotated.at<float>(0)-xMin)/cellSize + 0.5f, (endRotated.at<float>(1)-yMin)/cellSize + 0.5f);
|
||||||
|
//end must be inside the grid
|
||||||
|
end.x = end.x < 0?0:end.x;
|
||||||
|
end.x = end.x >= map.cols?map.cols-1:end.x;
|
||||||
|
end.y = end.y < 0?0:end.y;
|
||||||
|
end.y = end.y >= map.rows?map.rows-1:end.y;
|
||||||
|
rayTrace(start, end, map, true); // trace free space
|
||||||
|
|
||||||
|
// next point
|
||||||
|
endRotated = rotation*(endRotated - origin) + origin;
|
||||||
|
endRotatedVector.at<float>(0) = endRotated.at<float>(0) - origin.at<float>(0);
|
||||||
|
endRotatedVector.at<float>(1) = endRotated.at<float>(1) - origin.at<float>(1);
|
||||||
|
//UWARN("endRotated = %f %f", endRotated.at<float>(0), endRotated.at<float>(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++j;
|
||||||
|
//UWARN("timer=%fs", timer.ticks());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rayTrace(const cv::Point2i & start, const cv::Point2i & end, cv::Mat & grid)
|
void rayTrace(const cv::Point2i & start, const cv::Point2i & end, cv::Mat & grid, bool stopOnObstacle)
|
||||||
{
|
{
|
||||||
UASSERT_MSG(start.x >= 0 && start.x < grid.cols, uFormat("start.x=%d grid.cols=%d", start.x, grid.cols).c_str());
|
UASSERT_MSG(start.x >= 0 && start.x < grid.cols, uFormat("start.x=%d grid.cols=%d", start.x, grid.cols).c_str());
|
||||||
UASSERT_MSG(start.y >= 0 && start.y < grid.rows, uFormat("start.y=%d grid.rows=%d", start.y, grid.rows).c_str());
|
UASSERT_MSG(start.y >= 0 && start.y < grid.rows, uFormat("start.y=%d grid.rows=%d", start.y, grid.rows).c_str());
|
||||||
@@ -1852,46 +2026,50 @@ void rayTrace(const cv::Point2i & start, const cv::Point2i & end, cv::Mat & grid
|
|||||||
UASSERT_MSG(end.y >= 0 && end.y < grid.rows, uFormat("end.x=%d grid.cols=%d", end.y, grid.rows).c_str());
|
UASSERT_MSG(end.y >= 0 && end.y < grid.rows, uFormat("end.x=%d grid.cols=%d", end.y, grid.rows).c_str());
|
||||||
|
|
||||||
cv::Point2i ptA, ptB;
|
cv::Point2i ptA, ptB;
|
||||||
if(start.x > end.x)
|
ptA = start;
|
||||||
{
|
ptB = end;
|
||||||
ptA = end;
|
|
||||||
ptB = start;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ptA = start;
|
|
||||||
ptB = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
float slope = float(ptB.y - ptA.y)/float(ptB.x - ptA.x);
|
float slope = float(ptB.y - ptA.y)/float(ptB.x - ptA.x);
|
||||||
float b = ptA.y - slope*ptA.x;
|
float b = ptA.y - slope*ptA.x;
|
||||||
|
|
||||||
|
//UWARN("start=%d,%d end=%d,%d", ptA.x, ptA.y, ptB.x, ptB.y);
|
||||||
//ROS_WARN("start=%d,%d end=%d,%d", ptA.x, ptA.y, ptB.x, ptB.y);
|
|
||||||
|
|
||||||
//ROS_WARN("y = %f*x + %f", slope, b);
|
//ROS_WARN("y = %f*x + %f", slope, b);
|
||||||
|
for(int x=ptA.x; ptA.x<ptB.x?x<ptB.x:x>ptB.x; ptA.x<ptB.x?++x:--x)
|
||||||
for(int x=ptA.x; x<ptB.x; ++x)
|
|
||||||
{
|
{
|
||||||
float lowerbound = float(x)*slope + b;
|
int lowerbound = float(x)*slope + b;
|
||||||
float upperbound = float(x+1)*slope + b;
|
int upperbound = float(ptA.x<ptB.x?x+1:x-1)*slope + b;
|
||||||
|
|
||||||
if(lowerbound > upperbound)
|
if(lowerbound > upperbound)
|
||||||
{
|
{
|
||||||
float tmp = lowerbound;
|
int tmp = lowerbound;
|
||||||
lowerbound = upperbound;
|
lowerbound = upperbound;
|
||||||
upperbound = tmp;
|
upperbound = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ROS_WARN("lowerbound=%f upperbound=%f", lowerbound, upperbound);
|
//ROS_WARN("lowerbound=%f upperbound=%f", lowerbound, upperbound);
|
||||||
UASSERT_MSG(lowerbound >= 0 && lowerbound < grid.rows, uFormat("lowerbound=%f grid.cols=%d x=%d slope=%f b=%f", lowerbound, grid.cols, x, slope, b).c_str());
|
UASSERT_MSG(lowerbound >= 0 && lowerbound < grid.rows, uFormat("lowerbound=%f grid.rows=%d x=%d slope=%f b=%f x=%f", lowerbound, grid.rows, x, slope, b, x).c_str());
|
||||||
UASSERT_MSG(upperbound >= 0 && upperbound < grid.rows, uFormat("upperbound=%f grid.cols=%d x+1=%d slope=%f b=%f", upperbound, grid.cols, x+1, slope, b).c_str());
|
UASSERT_MSG(upperbound >= 0 && upperbound < grid.rows, uFormat("upperbound=%f grid.rows=%d x+1=%d slope=%f b=%f x=%f", upperbound, grid.rows, x+1, slope, b, x).c_str());
|
||||||
|
// verify if there is no obstacle
|
||||||
|
bool stopped = false;
|
||||||
|
if(stopOnObstacle)
|
||||||
|
{
|
||||||
|
for(int y = lowerbound; y<=(int)upperbound; ++y)
|
||||||
|
{
|
||||||
|
if(grid.at<char>(y, x) == 100)
|
||||||
|
{
|
||||||
|
stopped = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(stopped)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
for(int y = lowerbound; y<=(int)upperbound; ++y)
|
for(int y = lowerbound; y<=(int)upperbound; ++y)
|
||||||
{
|
{
|
||||||
//if(grid.at<char>(y, x) == -1)
|
grid.at<char>(y, x) = 0; // free space
|
||||||
{
|
|
||||||
grid.at<char>(y, x) = 0; // free space
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,6 @@ private:
|
|||||||
void updateMapCloud(const std::map<int, Transform> & poses, const Transform & pose);
|
void updateMapCloud(const std::map<int, Transform> & poses, const Transform & pose);
|
||||||
void createAndAddCloudToMap(int nodeId, const Transform & pose);
|
void createAndAddCloudToMap(int nodeId, const Transform & pose);
|
||||||
void createAndAddScanToMap(int nodeId, const Transform & pose);
|
void createAndAddScanToMap(int nodeId, const Transform & pose);
|
||||||
std::map<int, Transform> radiusPosesFiltering(const std::map<int, Transform> & poses) const;
|
|
||||||
void drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords, const std::multimap<int, cv::KeyPoint> & loopWords);
|
void drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords, const std::multimap<int, cv::KeyPoint> & loopWords);
|
||||||
void setupMainLayout(bool vertical);
|
void setupMainLayout(bool vertical);
|
||||||
void updateSelectSourceImageMenu(int type);
|
void updateSelectSourceImageMenu(int type);
|
||||||
|
|||||||
@@ -135,7 +135,9 @@ GraphViewer::GraphViewer(QWidget * parent) :
|
|||||||
_nodeRadius(0.01),
|
_nodeRadius(0.01),
|
||||||
_linkWidth(0),
|
_linkWidth(0),
|
||||||
_gridMap(0),
|
_gridMap(0),
|
||||||
_gridCellSize(0.05f)
|
_lastReferential(0),
|
||||||
|
_gridCellSize(0.05f),
|
||||||
|
_gridUnknownSpaceFilled(true)
|
||||||
{
|
{
|
||||||
Q_ASSERT(_gridCellSize > 0);
|
Q_ASSERT(_gridCellSize > 0);
|
||||||
|
|
||||||
@@ -154,6 +156,19 @@ GraphViewer::GraphViewer(QWidget * parent) :
|
|||||||
item->setZValue(100);
|
item->setZValue(100);
|
||||||
item->setParentItem(_root);
|
item->setParentItem(_root);
|
||||||
|
|
||||||
|
// current pose
|
||||||
|
_lastReferential = new QGraphicsItemGroup();
|
||||||
|
this->scene()->addItem(_lastReferential);
|
||||||
|
item = this->scene()->addLine(0,0,0,-0.5, QPen(QBrush(Qt::red), _linkWidth));
|
||||||
|
item->setZValue(100);
|
||||||
|
item->setParentItem(_root);
|
||||||
|
_lastReferential->addToGroup(item);
|
||||||
|
item = this->scene()->addLine(0,0,-0.5,0, QPen(QBrush(Qt::green), _linkWidth));
|
||||||
|
item->setZValue(100);
|
||||||
|
item->setParentItem(_root);
|
||||||
|
_lastReferential->addToGroup(item);
|
||||||
|
|
||||||
|
|
||||||
_gridMap = this->scene()->addPixmap(QPixmap());
|
_gridMap = this->scene()->addPixmap(QPixmap());
|
||||||
_gridMap->scale(_gridCellSize, -_gridCellSize);
|
_gridMap->scale(_gridCellSize, -_gridCellSize);
|
||||||
_gridMap->setRotation(90);
|
_gridMap->setRotation(90);
|
||||||
@@ -312,7 +327,7 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
|
|||||||
if(scanClouds.size())
|
if(scanClouds.size())
|
||||||
{
|
{
|
||||||
float xMin=0.0f, yMin=0.0f;
|
float xMin=0.0f, yMin=0.0f;
|
||||||
cv::Mat map8S = util3d::create2DMap(poses, scanClouds, _gridCellSize, xMin, yMin);
|
cv::Mat map8S = util3d::create2DMap(poses, scanClouds, _gridCellSize, _gridUnknownSpaceFilled, xMin, yMin);
|
||||||
cv::Mat map8U(map8S.rows, map8S.cols, CV_8U);
|
cv::Mat map8U(map8S.rows, map8S.cols, CV_8U);
|
||||||
//convert to gray scaled map
|
//convert to gray scaled map
|
||||||
for (int i = 0; i < map8S.rows; ++i)
|
for (int i = 0; i < map8S.rows; ++i)
|
||||||
@@ -346,7 +361,13 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
|
|||||||
|
|
||||||
if(_nodeItems.size())
|
if(_nodeItems.size())
|
||||||
{
|
{
|
||||||
(--_nodeItems.end()).value()->setColor(Qt::gray);
|
(--_nodeItems.end()).value()->setColor(Qt::green);
|
||||||
|
}
|
||||||
|
if(poses.size())
|
||||||
|
{
|
||||||
|
Transform t = poses.rbegin()->second;
|
||||||
|
QTransform qt(t.r11(), t.r12(), t.r21(), t.r22(), -t.o24(), -t.o14());
|
||||||
|
_lastReferential->setTransform(qt);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
||||||
@@ -362,6 +383,7 @@ void GraphViewer::clearGraph()
|
|||||||
qDeleteAll(_loopLinkItems);
|
qDeleteAll(_loopLinkItems);
|
||||||
_loopLinkItems.clear();
|
_loopLinkItems.clear();
|
||||||
_gridMap->setPixmap(QPixmap());
|
_gridMap->setPixmap(QPixmap());
|
||||||
|
_lastReferential->resetTransform();
|
||||||
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,6 +413,9 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
|||||||
QAction * aSetLinkSize = menu.addAction(tr("Set link width..."));
|
QAction * aSetLinkSize = menu.addAction(tr("Set link width..."));
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
QAction * aSetGridCellSize = menu.addAction(tr("Set grid cell size..."));
|
QAction * aSetGridCellSize = menu.addAction(tr("Set grid cell size..."));
|
||||||
|
QAction * aSetGridUnknownSpaceFilled = menu.addAction(tr("Unknown grid space filled"));
|
||||||
|
aSetGridUnknownSpaceFilled->setCheckable(true);
|
||||||
|
aSetGridUnknownSpaceFilled->setChecked(_gridUnknownSpaceFilled);
|
||||||
QAction * aShowHideGridMap;
|
QAction * aShowHideGridMap;
|
||||||
if(_gridMap->isVisible())
|
if(_gridMap->isVisible())
|
||||||
{
|
{
|
||||||
@@ -557,9 +582,12 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
|||||||
if(ok)
|
if(ok)
|
||||||
{
|
{
|
||||||
_gridCellSize = value;
|
_gridCellSize = value;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(r == aSetGridUnknownSpaceFilled)
|
||||||
|
{
|
||||||
|
_gridUnknownSpaceFilled = aSetGridUnknownSpaceFilled->isChecked();
|
||||||
|
}
|
||||||
else if(r == aShowHideGridMap)
|
else if(r == aShowHideGridMap)
|
||||||
{
|
{
|
||||||
_gridMap->setVisible(!_gridMap->isVisible());
|
_gridMap->setVisible(!_gridMap->isVisible());
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
class QGraphicsItem;
|
class QGraphicsItem;
|
||||||
class QGraphicsPixmapItem;
|
class QGraphicsPixmapItem;
|
||||||
|
class QGraphicsItemGroup;
|
||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
|
|
||||||
@@ -50,7 +51,9 @@ private:
|
|||||||
float _nodeRadius;
|
float _nodeRadius;
|
||||||
float _linkWidth;
|
float _linkWidth;
|
||||||
QGraphicsPixmapItem * _gridMap;
|
QGraphicsPixmapItem * _gridMap;
|
||||||
|
QGraphicsItemGroup * _lastReferential;
|
||||||
float _gridCellSize;
|
float _gridCellSize;
|
||||||
|
bool _gridUnknownSpaceFilled;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace rtabmap */
|
} /* namespace rtabmap */
|
||||||
|
|||||||
@@ -942,7 +942,21 @@ void MainWindow::processStats(const rtabmap::Statistics & stat)
|
|||||||
// update some widgets
|
// update some widgets
|
||||||
if(_ui->graphicsView_graphView->isVisible())
|
if(_ui->graphicsView_graphView->isVisible())
|
||||||
{
|
{
|
||||||
_ui->graphicsView_graphView->updateGraph(stat.poses(), stat.constraints(), _depths2DMap);
|
std::map<int, Transform> poses;
|
||||||
|
if(_preferencesDialog->isCloudFiltering() && stat.poses().size())
|
||||||
|
{
|
||||||
|
float radius = _preferencesDialog->getCloudFilteringRadius();
|
||||||
|
float angle = _preferencesDialog->getCloudFilteringAngle()*CV_PI/180.0; // convert to rad
|
||||||
|
poses = util3d::radiusPosesFiltering(stat.poses(), radius, angle);
|
||||||
|
// make sure the last is here
|
||||||
|
poses.insert(*stat.poses().rbegin());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
poses = stat.poses();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ui->graphicsView_graphView->updateGraph(poses, stat.constraints(), _depths2DMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
_odometryReceived = false;
|
_odometryReceived = false;
|
||||||
@@ -1036,9 +1050,13 @@ void MainWindow::updateMapCloud(const std::map<int, Transform> & posesIn, const
|
|||||||
|
|
||||||
// filter duplicated poses
|
// filter duplicated poses
|
||||||
std::map<int, Transform> poses;
|
std::map<int, Transform> poses;
|
||||||
if(_preferencesDialog->isCloudFiltering())
|
if(_preferencesDialog->isCloudFiltering() && posesIn.size())
|
||||||
{
|
{
|
||||||
poses = radiusPosesFiltering(posesIn);
|
float radius = _preferencesDialog->getCloudFilteringRadius();
|
||||||
|
float angle = _preferencesDialog->getCloudFilteringAngle()*CV_PI/180.0; // convert to rad
|
||||||
|
poses = util3d::radiusPosesFiltering(posesIn, radius, angle);
|
||||||
|
// make sure the last is here
|
||||||
|
poses.insert(*posesIn.rbegin());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1308,89 +1326,6 @@ void MainWindow::updateNodeVisibility(int nodeId, bool visible)
|
|||||||
_ui->widget_cloudViewer->render();
|
_ui->widget_cloudViewer->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<int, Transform> MainWindow::radiusPosesFiltering(const std::map<int, Transform> & poses) const
|
|
||||||
{
|
|
||||||
float radius = _preferencesDialog->getCloudFilteringRadius();
|
|
||||||
float angle = _preferencesDialog->getCloudFilteringAngle()*3.14159265359/180.0; // convert to rad
|
|
||||||
if(poses.size() > 1 && radius > 0.0f && angle>0.0f)
|
|
||||||
{
|
|
||||||
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
|
|
||||||
cloud->resize(poses.size());
|
|
||||||
int i=0;
|
|
||||||
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
|
||||||
{
|
|
||||||
(*cloud)[i++] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
// radius filtering
|
|
||||||
std::vector<int> names = uKeys(poses);
|
|
||||||
std::vector<Transform> transforms = uValues(poses);
|
|
||||||
|
|
||||||
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
|
|
||||||
tree->setInputCloud(cloud);
|
|
||||||
std::set<int> indicesChecked;
|
|
||||||
std::set<int> indicesKept;
|
|
||||||
|
|
||||||
for(unsigned int i=0; i<cloud->size(); ++i)
|
|
||||||
{
|
|
||||||
// ignore scans
|
|
||||||
if(indicesChecked.find(i) == indicesChecked.end())
|
|
||||||
{
|
|
||||||
std::vector<int> kIndices;
|
|
||||||
std::vector<float> kDistances;
|
|
||||||
tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
|
|
||||||
|
|
||||||
std::set<int> cloudIndices;
|
|
||||||
const Transform & currentT = transforms.at(i);
|
|
||||||
Eigen::Vector3f vA = util3d::transformToEigen3f(currentT).rotation()*Eigen::Vector3f(1,0,0);
|
|
||||||
for(unsigned int j=0; j<kIndices.size(); ++j)
|
|
||||||
{
|
|
||||||
if(indicesChecked.find(kIndices[j]) == indicesChecked.end())
|
|
||||||
{
|
|
||||||
const Transform & checkT = transforms.at(kIndices[j]);
|
|
||||||
// same orientation?
|
|
||||||
Eigen::Vector3f vB = util3d::transformToEigen3f(checkT).rotation()*Eigen::Vector3f(1,0,0);
|
|
||||||
double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
|
|
||||||
if(a <= angle)
|
|
||||||
{
|
|
||||||
cloudIndices.insert(kIndices[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool firstAdded = false;
|
|
||||||
for(std::set<int>::iterator iter = cloudIndices.begin(); iter!=cloudIndices.end(); ++iter)
|
|
||||||
{
|
|
||||||
if(!firstAdded)
|
|
||||||
{
|
|
||||||
indicesKept.insert(*iter);
|
|
||||||
firstAdded = true;
|
|
||||||
}
|
|
||||||
indicesChecked.insert(*iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//pcl::IndicesPtr indicesOut(new std::vector<int>);
|
|
||||||
//indicesOut->insert(indicesOut->end(), indicesKept.begin(), indicesKept.end());
|
|
||||||
UINFO("Cloud filtered In = %d, Out = %d", cloud->size(), indicesKept.size());
|
|
||||||
//pcl::io::savePCDFile("duplicateIn.pcd", *cloud);
|
|
||||||
//pcl::io::savePCDFile("duplicateOut.pcd", *cloud, *indicesOut);
|
|
||||||
|
|
||||||
std::map<int, Transform> keptPoses;
|
|
||||||
for(std::set<int>::iterator iter = indicesKept.begin(); iter!=indicesKept.end(); ++iter)
|
|
||||||
{
|
|
||||||
keptPoses.insert(std::make_pair(names.at(*iter), transforms.at(*iter)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return keptPoses;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return poses;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::processRtabmapEventInit(int status, const QString & info)
|
void MainWindow::processRtabmapEventInit(int status, const QString & info)
|
||||||
{
|
{
|
||||||
if((RtabmapEventInit::Status)status == RtabmapEventInit::kInitializing)
|
if((RtabmapEventInit::Status)status == RtabmapEventInit::kInitializing)
|
||||||
|
|||||||
Reference in New Issue
Block a user