mirror of
https://github.com/introlab/rtabmap_ros.git
synced 2026-09-12 22:30:19 +08:00
Refactored obstacles_detection nodelet to use OccupancyGrid segmentation approach (so using exactly the same parameter names as rtabmap node for the map)
This commit is contained in:
+1
-1
@@ -160,7 +160,7 @@ SET(rtabmap_ros_lib_src
|
||||
src/nodelets/point_cloud_xyz.cpp
|
||||
src/nodelets/disparity_to_depth.cpp
|
||||
src/nodelets/obstacles_detection.cpp
|
||||
src/nodelets/obstacles_detection_indoor.cpp
|
||||
src/nodelets/obstacles_detection_old.cpp
|
||||
src/nodelets/point_cloud_aggregator.cpp
|
||||
src/OdometryROS.cpp
|
||||
src/MsgConversion.cpp
|
||||
|
||||
@@ -283,7 +283,6 @@ private:
|
||||
double genScanMinDepth_;
|
||||
int scanCloudMaxPoints_;
|
||||
int scanCloudNormalK_;
|
||||
bool flipScan_;
|
||||
|
||||
rtabmap::Transform mapToOdom_;
|
||||
boost::mutex mapToOdomMutex_;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<launch>
|
||||
|
||||
<!-- Use stereo_outdoorA.bag for testing -->
|
||||
<arg name="optimize_for_close_objects" default="false" />
|
||||
|
||||
<include file="$(find rtabmap_ros)/launch/demo/demo_stereo_outdoor.launch"/>
|
||||
|
||||
<group ns="/stereo_camera" >
|
||||
@@ -23,7 +21,6 @@
|
||||
<param name="wait_for_transform" type="bool" value="true"/>
|
||||
<param name="min_cluster_size" type="int" value="20"/>
|
||||
<param name="max_obstacles_height" type="double" value="0.0"/>
|
||||
<param name="optimize_for_close_objects" type="bool" value="$(arg optimize_for_close_objects)"/>
|
||||
</node>
|
||||
</group>
|
||||
|
||||
|
||||
+2
-2
@@ -88,8 +88,8 @@
|
||||
</description>
|
||||
</class>
|
||||
|
||||
<class name="rtabmap_ros/obstacles_detection_indoor"
|
||||
type="rtabmap_ros::ObstaclesDetectionIndoor"
|
||||
<class name="rtabmap_ros/obstacles_detection_old"
|
||||
type="rtabmap_ros::ObstaclesDetectionOld"
|
||||
base_class_type="nodelet::Nodelet">
|
||||
<description>
|
||||
This is my nodelet.
|
||||
|
||||
@@ -36,28 +36,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <tf/transform_listener.h>
|
||||
|
||||
#include <sensor_msgs/PointCloud2.h>
|
||||
#include <sensor_msgs/Image.h>
|
||||
#include <sensor_msgs/image_encodings.h>
|
||||
#include <sensor_msgs/CameraInfo.h>
|
||||
#include <stereo_msgs/DisparityImage.h>
|
||||
|
||||
#include <image_transport/image_transport.h>
|
||||
#include <image_transport/subscriber_filter.h>
|
||||
|
||||
#include <image_geometry/pinhole_camera_model.h>
|
||||
|
||||
#include <message_filters/sync_policies/approximate_time.h>
|
||||
#include <message_filters/subscriber.h>
|
||||
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
|
||||
#include <rtabmap_ros/MsgConversion.h>
|
||||
|
||||
#include "rtabmap/core/util3d.h"
|
||||
#include "rtabmap/core/util3d_filtering.h"
|
||||
#include "rtabmap/core/util3d_mapping.h"
|
||||
#include "rtabmap/core/util3d_transforms.h"
|
||||
#include "rtabmap/core/OccupancyGrid.h"
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
|
||||
namespace rtabmap_ros
|
||||
{
|
||||
@@ -67,51 +50,154 @@ class ObstaclesDetection : public nodelet::Nodelet
|
||||
public:
|
||||
ObstaclesDetection() :
|
||||
frameId_("base_link"),
|
||||
normalKSearch_(20),
|
||||
groundNormalAngle_(M_PI_4),
|
||||
clusterRadius_(0.05),
|
||||
minClusterSize_(20),
|
||||
maxObstaclesHeight_(0.0), // if<=0.0 -> disabled
|
||||
maxGroundHeight_(0.0), // if<=0.0 -> disabled, used only if detect_flat_obstacles is true
|
||||
segmentFlatObstacles_(false),
|
||||
waitForTransform_(false),
|
||||
optimizeForCloseObjects_(false),
|
||||
projVoxelSize_(0.01)
|
||||
waitForTransform_(false)
|
||||
{}
|
||||
|
||||
virtual ~ObstaclesDetection()
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
void parameterMoved(
|
||||
ros::NodeHandle & nh,
|
||||
const std::string & rosName,
|
||||
const std::string & parameterName,
|
||||
rtabmap::ParametersMap & parameters)
|
||||
{
|
||||
if(nh.hasParam(rosName))
|
||||
{
|
||||
rtabmap::ParametersMap gridParameters = rtabmap::Parameters::getDefaultParameters("Grid");
|
||||
rtabmap::ParametersMap::const_iterator iter =gridParameters.find(parameterName);
|
||||
if(iter != gridParameters.end())
|
||||
{
|
||||
NODELET_ERROR("obstacles_detection: Parameter \"%s\" has moved from "
|
||||
"rtabmap_ros to rtabmap library. Use "
|
||||
"parameter \"%s\" instead. The value is still "
|
||||
"copied to new parameter name.",
|
||||
rosName.c_str(),
|
||||
parameterName.c_str());
|
||||
std::string type = rtabmap::Parameters::getType(parameterName);
|
||||
if(type.compare("float") || type.compare("double"))
|
||||
{
|
||||
double v = uStr2Double(iter->second);
|
||||
nh.getParam(rosName, v);
|
||||
parameters.insert(rtabmap::ParametersPair(parameterName, uNumber2Str(v)));
|
||||
}
|
||||
else if(type.compare("int") || type.compare("unsigned int"))
|
||||
{
|
||||
int v = uStr2Int(iter->second);
|
||||
nh.getParam(rosName, v);
|
||||
parameters.insert(rtabmap::ParametersPair(parameterName, uNumber2Str(v)));
|
||||
}
|
||||
else
|
||||
{
|
||||
NODELET_ERROR("Not handled type \"%s\" for parameter \"%s\"", type.c_str(), parameterName.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NODELET_ERROR("Parameter \"%s\" not found in default parameters.", parameterName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void onInit()
|
||||
{
|
||||
ROS_DEBUG("_"); // not sure why, but all NODELET_*** log are not shown if a normal ROS_*** is not called!?
|
||||
ros::NodeHandle & nh = getNodeHandle();
|
||||
ros::NodeHandle & pnh = getPrivateNodeHandle();
|
||||
|
||||
int queueSize = 10;
|
||||
pnh.param("queue_size", queueSize, queueSize);
|
||||
pnh.param("frame_id", frameId_, frameId_);
|
||||
pnh.param("normal_k", normalKSearch_, normalKSearch_);
|
||||
pnh.param("ground_normal_angle", groundNormalAngle_, groundNormalAngle_);
|
||||
if(pnh.hasParam("normal_estimation_radius") && !pnh.hasParam("cluster_radius"))
|
||||
{
|
||||
NODELET_WARN("Parameter \"normal_estimation_radius\" has been renamed "
|
||||
"to \"cluster_radius\"! Your value is still copied to "
|
||||
"corresponding parameter. Instead of normal radius, nearest neighbors count "
|
||||
"\"normal_k\" is used instead (default 20).");
|
||||
pnh.param("normal_estimation_radius", clusterRadius_, clusterRadius_);
|
||||
}
|
||||
else
|
||||
{
|
||||
pnh.param("cluster_radius", clusterRadius_, clusterRadius_);
|
||||
}
|
||||
pnh.param("min_cluster_size", minClusterSize_, minClusterSize_);
|
||||
pnh.param("max_obstacles_height", maxObstaclesHeight_, maxObstaclesHeight_);
|
||||
pnh.param("max_ground_height", maxGroundHeight_, maxGroundHeight_);
|
||||
pnh.param("detect_flat_obstacles", segmentFlatObstacles_, segmentFlatObstacles_);
|
||||
pnh.param("map_frame_id", mapFrameId_, mapFrameId_);
|
||||
pnh.param("wait_for_transform", waitForTransform_, waitForTransform_);
|
||||
pnh.param("optimize_for_close_objects", optimizeForCloseObjects_, optimizeForCloseObjects_);
|
||||
pnh.param("proj_voxel_size", projVoxelSize_, projVoxelSize_);
|
||||
|
||||
if(pnh.hasParam("optimize_for_close_objects"))
|
||||
{
|
||||
NODELET_ERROR("\"optimize_for_close_objects\" parameter doesn't exist "
|
||||
"anymore. Use rtabmap_ros/obstacles_detection_old nodelet to use "
|
||||
"the old interface.");
|
||||
}
|
||||
|
||||
rtabmap::ParametersMap parameters;
|
||||
|
||||
// Backward compatibility
|
||||
for(std::map<std::string, std::pair<bool, std::string> >::const_iterator iter=rtabmap::Parameters::getRemovedParameters().begin();
|
||||
iter!=rtabmap::Parameters::getRemovedParameters().end();
|
||||
++iter)
|
||||
{
|
||||
std::string vStr;
|
||||
if(pnh.getParam(iter->first, vStr))
|
||||
{
|
||||
if(iter->second.first)
|
||||
{
|
||||
// can be migrated
|
||||
uInsert(parameters, rtabmap::ParametersPair(iter->second.second, vStr));
|
||||
NODELET_ERROR("obstacles_detection: Parameter name changed: \"%s\" -> \"%s\". Please update your launch file accordingly. Value \"%s\" is still set to the new parameter name.",
|
||||
iter->first.c_str(), iter->second.second.c_str(), vStr.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(iter->second.second.empty())
|
||||
{
|
||||
NODELET_ERROR("obstacles_detection: Parameter \"%s\" doesn't exist anymore!",
|
||||
iter->first.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
NODELET_ERROR("obstacles_detection: Parameter \"%s\" doesn't exist anymore! You may look at this similar parameter: \"%s\"",
|
||||
iter->first.c_str(), iter->second.second.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtabmap::ParametersMap gridParameters2 = rtabmap::Parameters::getDefaultParameters();
|
||||
rtabmap::ParametersMap gridParameters = rtabmap::Parameters::getDefaultParameters("Grid");
|
||||
for(rtabmap::ParametersMap::iterator iter=gridParameters.begin(); iter!=gridParameters.end(); ++iter)
|
||||
{
|
||||
std::string vStr;
|
||||
bool vBool;
|
||||
int vInt;
|
||||
double vDouble;
|
||||
if(pnh.getParam(iter->first, vStr))
|
||||
{
|
||||
NODELET_INFO("obstacles_detection: Setting parameter \"%s\"=\"%s\"", iter->first.c_str(), vStr.c_str());
|
||||
iter->second = vStr;
|
||||
}
|
||||
else if(pnh.getParam(iter->first, vBool))
|
||||
{
|
||||
NODELET_INFO("obstacles_detection: Setting parameter \"%s\"=\"%s\"", iter->first.c_str(), uBool2Str(vBool).c_str());
|
||||
iter->second = uBool2Str(vBool);
|
||||
}
|
||||
else if(pnh.getParam(iter->first, vDouble))
|
||||
{
|
||||
NODELET_INFO("obstacles_detection: Setting parameter \"%s\"=\"%s\"", iter->first.c_str(), uNumber2Str(vDouble).c_str());
|
||||
iter->second = uNumber2Str(vDouble);
|
||||
}
|
||||
else if(pnh.getParam(iter->first, vInt))
|
||||
{
|
||||
NODELET_INFO("obstacles_detection: Setting parameter \"%s\"=\"%s\"", iter->first.c_str(), uNumber2Str(vInt).c_str());
|
||||
iter->second = uNumber2Str(vInt);
|
||||
}
|
||||
}
|
||||
uInsert(parameters, gridParameters);
|
||||
parameterMoved(pnh, "proj_voxel_size", rtabmap::Parameters::kGridCellSize(), parameters);
|
||||
parameterMoved(pnh, "ground_normal_angle", rtabmap::Parameters::kGridMaxGroundAngle(), parameters);
|
||||
parameterMoved(pnh, "min_cluster_size", rtabmap::Parameters::kGridMinClusterSize(), parameters);
|
||||
parameterMoved(pnh, "normal_estimation_radius", rtabmap::Parameters::kGridClusterRadius(), parameters);
|
||||
parameterMoved(pnh, "cluster_radius", rtabmap::Parameters::kGridClusterRadius(), parameters);
|
||||
parameterMoved(pnh, "max_obstacles_height", rtabmap::Parameters::kGridMaxObstacleHeight(), parameters);
|
||||
parameterMoved(pnh, "max_ground_height", rtabmap::Parameters::kGridMaxGroundHeight(), parameters);
|
||||
parameterMoved(pnh, "detect_flat_obstacles", rtabmap::Parameters::kGridFlatObstacleDetected(), parameters);
|
||||
parameterMoved(pnh, "normal_k", rtabmap::Parameters::kGridNormalK(), parameters);
|
||||
|
||||
UASSERT(uContains(parameters, rtabmap::Parameters::kGridMapFrameProjection()));
|
||||
if(uStr2Bool(parameters.at(rtabmap::Parameters::kGridMapFrameProjection())) && mapFrameId_.empty())
|
||||
{
|
||||
NODELET_ERROR("obstacles_detection: Parameter \"%s\" is true but map_frame_id is not set!", rtabmap::Parameters::kGridMapFrameProjection().c_str());
|
||||
}
|
||||
|
||||
cloudSub_ = nh.subscribe("cloud", 1, &ObstaclesDetection::callback, this);
|
||||
|
||||
@@ -153,8 +239,32 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromROSMsg(*cloudMsg, *originalCloud);
|
||||
rtabmap::Transform pose = rtabmap::Transform::getIdentity();
|
||||
if(!mapFrameId_.empty())
|
||||
{
|
||||
try
|
||||
{
|
||||
if(waitForTransform_)
|
||||
{
|
||||
if(!tfListener_.waitForTransform(mapFrameId_, frameId_, cloudMsg->header.stamp, ros::Duration(1)))
|
||||
{
|
||||
NODELET_ERROR("Could not get transform from %s to %s after 1 second!", mapFrameId_.c_str(), frameId_.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
tf::StampedTransform tmp;
|
||||
tfListener_.lookupTransform(mapFrameId_, frameId_, cloudMsg->header.stamp, tmp);
|
||||
pose = rtabmap_ros::transformFromTF(tmp);
|
||||
}
|
||||
catch(tf::TransformException & ex)
|
||||
{
|
||||
NODELET_ERROR("%s",ex.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr inputCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromROSMsg(*cloudMsg, *inputCloud);
|
||||
|
||||
//Common variables for all strategies
|
||||
pcl::IndicesPtr ground, obstacles;
|
||||
@@ -162,136 +272,56 @@ private:
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloudWithoutFlatSurfaces(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
|
||||
if(originalCloud->size())
|
||||
if(inputCloud->size())
|
||||
{
|
||||
originalCloud = rtabmap::util3d::transformPointCloud(originalCloud, localTransform);
|
||||
if(maxObstaclesHeight_ > 0)
|
||||
{
|
||||
// std::numeric_limits<float>::lowest() exists only for c++11
|
||||
originalCloud = rtabmap::util3d::passThrough(originalCloud, "z", std::numeric_limits<int>::min(), maxObstaclesHeight_);
|
||||
}
|
||||
inputCloud = rtabmap::util3d::transformPointCloud(inputCloud, localTransform);
|
||||
|
||||
if(originalCloud->size())
|
||||
pcl::IndicesPtr flatObstacles(new std::vector<int>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = grid_.segmentCloud<pcl::PointXYZ>(
|
||||
inputCloud,
|
||||
pcl::IndicesPtr(new std::vector<int>),
|
||||
pose,
|
||||
cv::Point3f(localTransform.x(), localTransform.y(), localTransform.z()),
|
||||
ground,
|
||||
obstacles,
|
||||
&flatObstacles);
|
||||
|
||||
if(cloud->size() && (ground->size() || obstacles->size()))
|
||||
{
|
||||
if(!optimizeForCloseObjects_)
|
||||
if(groundPub_.getNumSubscribers() &&
|
||||
ground.get() && ground->size())
|
||||
{
|
||||
// This is the default strategy
|
||||
pcl::IndicesPtr flatObstacles(new std::vector<int>);
|
||||
rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(
|
||||
originalCloud,
|
||||
ground,
|
||||
obstacles,
|
||||
normalKSearch_,
|
||||
groundNormalAngle_,
|
||||
clusterRadius_,
|
||||
minClusterSize_,
|
||||
segmentFlatObstacles_,
|
||||
maxGroundHeight_,
|
||||
&flatObstacles);
|
||||
|
||||
if(groundPub_.getNumSubscribers() &&
|
||||
ground.get() && ground->size())
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud, *ground, *groundCloud);
|
||||
}
|
||||
|
||||
if((obstaclesPub_.getNumSubscribers() || projObstaclesPub_.getNumSubscribers()) &&
|
||||
obstacles.get() && obstacles->size())
|
||||
{
|
||||
// remove flat obstacles from obstacles
|
||||
std::set<int> flatObstaclesSet;
|
||||
if(projObstaclesPub_.getNumSubscribers())
|
||||
{
|
||||
flatObstaclesSet.insert(flatObstacles->begin(), flatObstacles->end());
|
||||
}
|
||||
|
||||
obstaclesCloud->resize(obstacles->size());
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(obstacles->size());
|
||||
|
||||
int oi=0;
|
||||
for(unsigned int i=0; i<obstacles->size(); ++i)
|
||||
{
|
||||
obstaclesCloud->points[i] = originalCloud->at(obstacles->at(i));
|
||||
if(flatObstaclesSet.size() == 0 ||
|
||||
flatObstaclesSet.find(obstacles->at(i))==flatObstaclesSet.end())
|
||||
{
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi] = obstaclesCloud->points[i];
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi].z = 0;
|
||||
++oi;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(oi);
|
||||
if(obstaclesCloudWithoutFlatSurfaces->size() && projVoxelSize_ > 0.0)
|
||||
{
|
||||
obstaclesCloudWithoutFlatSurfaces = rtabmap::util3d::voxelize(obstaclesCloudWithoutFlatSurfaces, projVoxelSize_);
|
||||
}
|
||||
}
|
||||
pcl::copyPointCloud(*cloud, *ground, *groundCloud);
|
||||
}
|
||||
else
|
||||
|
||||
if((obstaclesPub_.getNumSubscribers() || projObstaclesPub_.getNumSubscribers()) &&
|
||||
obstacles.get() && obstacles->size())
|
||||
{
|
||||
// in this case optimizeForCloseObject_ is true:
|
||||
// we divide the floor point cloud into two subsections, one for all potential floor points up to 1m
|
||||
// one for potential floor points further away than 1m.
|
||||
// For the points at closer range, we use a smaller normal estimation radius and ground normal angle,
|
||||
// which allows to detect smaller objects, without increasing the number of false positive.
|
||||
// For all other points, we use a bigger normal estimation radius (* 3.) and tolerance for the
|
||||
// grond normal angle (* 2.).
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud_near = rtabmap::util3d::passThrough(originalCloud, "x", std::numeric_limits<int>::min(), 1.);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud_far = rtabmap::util3d::passThrough(originalCloud, "x", 1., std::numeric_limits<int>::max());
|
||||
|
||||
// Part 1: segment floor and obstacles near the robot
|
||||
rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(
|
||||
originalCloud_near,
|
||||
ground,
|
||||
obstacles,
|
||||
normalKSearch_,
|
||||
groundNormalAngle_,
|
||||
clusterRadius_,
|
||||
minClusterSize_,
|
||||
segmentFlatObstacles_,
|
||||
maxGroundHeight_);
|
||||
|
||||
if(groundPub_.getNumSubscribers() && ground.get() && ground->size())
|
||||
// remove flat obstacles from obstacles
|
||||
std::set<int> flatObstaclesSet;
|
||||
if(projObstaclesPub_.getNumSubscribers())
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud_near, *ground, *groundCloud);
|
||||
ground->clear();
|
||||
flatObstaclesSet.insert(flatObstacles->begin(), flatObstacles->end());
|
||||
}
|
||||
|
||||
if((obstaclesPub_.getNumSubscribers() || projObstaclesPub_.getNumSubscribers()) && obstacles.get() && obstacles->size())
|
||||
obstaclesCloud->resize(obstacles->size());
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(obstacles->size());
|
||||
|
||||
int oi=0;
|
||||
for(unsigned int i=0; i<obstacles->size(); ++i)
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud_near, *obstacles, *obstaclesCloud);
|
||||
obstacles->clear();
|
||||
obstaclesCloud->points[i] = cloud->at(obstacles->at(i));
|
||||
if(flatObstaclesSet.size() == 0 ||
|
||||
flatObstaclesSet.find(obstacles->at(i))==flatObstaclesSet.end())
|
||||
{
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi] = obstaclesCloud->points[i];
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi].z = 0;
|
||||
++oi;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Part 2: segment floor and obstacles far from the robot
|
||||
rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(
|
||||
originalCloud_far,
|
||||
ground,
|
||||
obstacles,
|
||||
normalKSearch_,
|
||||
2.*groundNormalAngle_,
|
||||
3.*clusterRadius_,
|
||||
minClusterSize_,
|
||||
segmentFlatObstacles_,
|
||||
maxGroundHeight_);
|
||||
|
||||
if(groundPub_.getNumSubscribers() && ground.get() && ground->size())
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud2 (new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::copyPointCloud(*originalCloud_far, *ground, *groundCloud2);
|
||||
*groundCloud += *groundCloud2;
|
||||
}
|
||||
|
||||
|
||||
if((obstaclesPub_.getNumSubscribers() || projObstaclesPub_.getNumSubscribers()) && obstacles.get() && obstacles->size())
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstacles2(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::copyPointCloud(*originalCloud_far, *obstacles, *obstacles2);
|
||||
*obstaclesCloud += *obstacles2;
|
||||
}
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(oi);
|
||||
}
|
||||
|
||||
if(!localTransform.isIdentity())
|
||||
@@ -346,16 +376,10 @@ private:
|
||||
|
||||
private:
|
||||
std::string frameId_;
|
||||
int normalKSearch_;
|
||||
double groundNormalAngle_;
|
||||
double clusterRadius_;
|
||||
int minClusterSize_;
|
||||
double maxObstaclesHeight_;
|
||||
double maxGroundHeight_;
|
||||
bool segmentFlatObstacles_;
|
||||
std::string mapFrameId_;
|
||||
bool waitForTransform_;
|
||||
bool optimizeForCloseObjects_;
|
||||
double projVoxelSize_;
|
||||
|
||||
rtabmap::OccupancyGrid grid_;
|
||||
|
||||
tf::TransformListener tfListener_;
|
||||
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ros/ros.h>
|
||||
#include <pluginlib/class_list_macros.h>
|
||||
#include <nodelet/nodelet.h>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl/common/transforms.h>
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
|
||||
#include <tf/transform_listener.h>
|
||||
|
||||
#include <sensor_msgs/PointCloud2.h>
|
||||
|
||||
#include <rtabmap_ros/MsgConversion.h>
|
||||
|
||||
#include "rtabmap/core/util3d_filtering.h"
|
||||
#include "rtabmap/core/util3d_transforms.h"
|
||||
|
||||
namespace rtabmap_ros
|
||||
{
|
||||
|
||||
class ObstaclesDetectionIndoor : public nodelet::Nodelet
|
||||
{
|
||||
public:
|
||||
ObstaclesDetectionIndoor() :
|
||||
frameId_(""),
|
||||
minObstaclesHeight_(0.0), // if >0.0 -> disabled
|
||||
maxObstaclesHeight_(0.0), // if <=0.0 -> disabled
|
||||
minGroundHeight_(-0.05),
|
||||
maxGroundHeight_(0.05),
|
||||
waitForTransform_(false),
|
||||
projVoxelSize_(0.01),
|
||||
noiseFilterRadius_(0.0), // if<=0.0 -> disabled
|
||||
noiseFilterMinNeighbors_(5)
|
||||
{}
|
||||
|
||||
virtual ~ObstaclesDetectionIndoor()
|
||||
{}
|
||||
|
||||
private:
|
||||
virtual void onInit()
|
||||
{
|
||||
ros::NodeHandle & nh = getNodeHandle();
|
||||
ros::NodeHandle & pnh = getPrivateNodeHandle();
|
||||
|
||||
int queueSize = 10;
|
||||
pnh.param("queue_size", queueSize, queueSize);
|
||||
pnh.param("frame_id", frameId_, frameId_);
|
||||
|
||||
pnh.param("min_obstacles_height", minObstaclesHeight_, minObstaclesHeight_);
|
||||
pnh.param("max_obstacles_height", maxObstaclesHeight_, maxObstaclesHeight_);
|
||||
pnh.param("min_ground_height", minGroundHeight_, minGroundHeight_);
|
||||
pnh.param("max_ground_height", maxGroundHeight_, maxGroundHeight_);
|
||||
pnh.param("noise_filter_radius", noiseFilterRadius_, noiseFilterRadius_);
|
||||
pnh.param("noise_filter_min_neighbors", noiseFilterMinNeighbors_, noiseFilterMinNeighbors_);
|
||||
pnh.param("wait_for_transform", waitForTransform_, waitForTransform_);
|
||||
pnh.param("proj_voxel_size", projVoxelSize_, projVoxelSize_);
|
||||
|
||||
cloudSub_ = nh.subscribe("cloud", 1, &ObstaclesDetectionIndoor::callback, this);
|
||||
|
||||
groundPub_ = nh.advertise<sensor_msgs::PointCloud2>("ground", 1);
|
||||
obstaclesPub_ = nh.advertise<sensor_msgs::PointCloud2>("obstacles", 1);
|
||||
projObstaclesPub_ = nh.advertise<sensor_msgs::PointCloud2>("proj_obstacles", 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void callback(const sensor_msgs::PointCloud2ConstPtr & cloudMsg)
|
||||
{
|
||||
ros::WallTime time = ros::WallTime::now();
|
||||
|
||||
if (groundPub_.getNumSubscribers() == 0 && obstaclesPub_.getNumSubscribers() == 0 && projObstaclesPub_.getNumSubscribers() == 0)
|
||||
{
|
||||
// no one wants the results
|
||||
return;
|
||||
}
|
||||
|
||||
rtabmap::Transform localTransform;
|
||||
try
|
||||
{
|
||||
if(waitForTransform_)
|
||||
{
|
||||
if(!tfListener_.waitForTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, ros::Duration(1)))
|
||||
{
|
||||
NODELET_ERROR("Could not get transform from %s to %s after 1 second!", frameId_.c_str(), cloudMsg->header.frame_id.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
tf::StampedTransform tmp;
|
||||
tfListener_.lookupTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, tmp);
|
||||
localTransform = rtabmap_ros::transformFromTF(tmp);
|
||||
}
|
||||
catch(tf::TransformException & ex)
|
||||
{
|
||||
NODELET_ERROR("%s",ex.what());
|
||||
return;
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromROSMsg(*cloudMsg, *originalCloud);
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr projectedObstaclesCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
|
||||
if(originalCloud->size())
|
||||
{
|
||||
originalCloud = rtabmap::util3d::transformPointCloud(originalCloud, localTransform);
|
||||
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
indices->resize(originalCloud->size());
|
||||
for(unsigned int i=0; i<indices->size(); ++i)
|
||||
{
|
||||
indices->at(i) = i;
|
||||
}
|
||||
|
||||
// segmentation
|
||||
pcl::IndicesPtr groundIndices = rtabmap::util3d::passThrough(originalCloud, indices, "z", minGroundHeight_, maxGroundHeight_);
|
||||
pcl::IndicesPtr obstacleIndices = rtabmap::util3d::extractIndices(originalCloud, groundIndices, true);
|
||||
|
||||
if(minObstaclesHeight_ <= 0.0 || maxObstaclesHeight_ > 0.0)
|
||||
{
|
||||
// std::numeric_limits<float>::lowest() exists only for c++11
|
||||
obstacleIndices = rtabmap::util3d::passThrough(originalCloud, obstacleIndices, "z",
|
||||
minObstaclesHeight_>0.0?std::numeric_limits<int>::min():minObstaclesHeight_,
|
||||
maxObstaclesHeight_<=0.0?std::numeric_limits<int>::max():maxObstaclesHeight_);
|
||||
}
|
||||
|
||||
// Do optional radius filtering to remove some noise
|
||||
if(noiseFilterRadius_ > 0.0 && noiseFilterMinNeighbors_ > 0)
|
||||
{
|
||||
if(groundIndices->size())
|
||||
{
|
||||
groundIndices = rtabmap::util3d::radiusFiltering(originalCloud, groundIndices, noiseFilterRadius_, noiseFilterMinNeighbors_);
|
||||
}
|
||||
if(obstacleIndices->size())
|
||||
{
|
||||
obstacleIndices = rtabmap::util3d::radiusFiltering(originalCloud, obstacleIndices, noiseFilterRadius_, noiseFilterMinNeighbors_);
|
||||
}
|
||||
}
|
||||
|
||||
if(projObstaclesPub_.getNumSubscribers() && obstacleIndices->size())
|
||||
{
|
||||
projectedObstaclesCloud->resize(obstacleIndices->size());
|
||||
for(unsigned int i=0; i<obstacleIndices->size(); ++i)
|
||||
{
|
||||
projectedObstaclesCloud->points[i] = originalCloud->at(obstacleIndices->at(i));
|
||||
projectedObstaclesCloud->points[i].z = 0;
|
||||
}
|
||||
|
||||
if(projVoxelSize_ > 0.0)
|
||||
{
|
||||
projectedObstaclesCloud = rtabmap::util3d::voxelize(projectedObstaclesCloud, projVoxelSize_);
|
||||
}
|
||||
}
|
||||
|
||||
if(!localTransform.isIdentity())
|
||||
{
|
||||
//transform back in topic frame
|
||||
rtabmap::Transform localTransformInv = localTransform.inverse();
|
||||
if(groundIndices->size())
|
||||
{
|
||||
pcl::transformPointCloud(*originalCloud, *groundIndices, *groundCloud, localTransformInv.toEigen3f());
|
||||
}
|
||||
if(obstacleIndices->size())
|
||||
{
|
||||
pcl::transformPointCloud(*originalCloud, *obstacleIndices, *obstaclesCloud, localTransformInv.toEigen3f());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(groundIndices->size())
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud, *groundIndices, *groundCloud);
|
||||
}
|
||||
if(obstacleIndices->size())
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud, *obstacleIndices, *obstaclesCloud);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(groundPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::PointCloud2 rosCloud;
|
||||
pcl::toROSMsg(*groundCloud, rosCloud);
|
||||
rosCloud.header = cloudMsg->header;
|
||||
|
||||
//publish the message
|
||||
groundPub_.publish(rosCloud);
|
||||
}
|
||||
|
||||
if(obstaclesPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::PointCloud2 rosCloud;
|
||||
pcl::toROSMsg(*obstaclesCloud, rosCloud);
|
||||
rosCloud.header = cloudMsg->header;
|
||||
|
||||
//publish the message
|
||||
obstaclesPub_.publish(rosCloud);
|
||||
}
|
||||
|
||||
if(projObstaclesPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::PointCloud2 rosCloud;
|
||||
pcl::toROSMsg(*projectedObstaclesCloud, rosCloud);
|
||||
rosCloud.header.stamp = cloudMsg->header.stamp;
|
||||
rosCloud.header.frame_id = frameId_;
|
||||
|
||||
//publish the message
|
||||
projObstaclesPub_.publish(rosCloud);
|
||||
}
|
||||
|
||||
NODELET_DEBUG("Obstacle segmentation time = %f s", (ros::WallTime::now() - time).toSec());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string frameId_;
|
||||
double minObstaclesHeight_;
|
||||
double maxObstaclesHeight_;
|
||||
double minGroundHeight_;
|
||||
double maxGroundHeight_;
|
||||
bool waitForTransform_;
|
||||
double projVoxelSize_;
|
||||
double noiseFilterRadius_;
|
||||
int noiseFilterMinNeighbors_;
|
||||
|
||||
tf::TransformListener tfListener_;
|
||||
|
||||
ros::Publisher groundPub_;
|
||||
ros::Publisher obstaclesPub_;
|
||||
ros::Publisher projObstaclesPub_;
|
||||
|
||||
ros::Subscriber cloudSub_;
|
||||
};
|
||||
|
||||
PLUGINLIB_EXPORT_CLASS(rtabmap_ros::ObstaclesDetectionIndoor, nodelet::Nodelet);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ros/ros.h>
|
||||
#include <pluginlib/class_list_macros.h>
|
||||
#include <nodelet/nodelet.h>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
|
||||
#include <tf/transform_listener.h>
|
||||
|
||||
#include <sensor_msgs/PointCloud2.h>
|
||||
|
||||
#include <rtabmap_ros/MsgConversion.h>
|
||||
|
||||
#include "rtabmap/core/util3d.h"
|
||||
#include "rtabmap/core/util3d_filtering.h"
|
||||
#include "rtabmap/core/util3d_mapping.h"
|
||||
#include "rtabmap/core/util3d_transforms.h"
|
||||
|
||||
namespace rtabmap_ros
|
||||
{
|
||||
|
||||
class ObstaclesDetectionOld : public nodelet::Nodelet
|
||||
{
|
||||
public:
|
||||
ObstaclesDetectionOld() :
|
||||
frameId_("base_link"),
|
||||
normalKSearch_(20),
|
||||
groundNormalAngle_(M_PI_4),
|
||||
clusterRadius_(0.05),
|
||||
minClusterSize_(20),
|
||||
maxObstaclesHeight_(0.0), // if<=0.0 -> disabled
|
||||
maxGroundHeight_(0.0), // if<=0.0 -> disabled, used only if detect_flat_obstacles is true
|
||||
segmentFlatObstacles_(false),
|
||||
waitForTransform_(false),
|
||||
optimizeForCloseObjects_(false),
|
||||
projVoxelSize_(0.01)
|
||||
{}
|
||||
|
||||
virtual ~ObstaclesDetectionOld()
|
||||
{}
|
||||
|
||||
private:
|
||||
virtual void onInit()
|
||||
{
|
||||
ros::NodeHandle & nh = getNodeHandle();
|
||||
ros::NodeHandle & pnh = getPrivateNodeHandle();
|
||||
|
||||
int queueSize = 10;
|
||||
pnh.param("queue_size", queueSize, queueSize);
|
||||
pnh.param("frame_id", frameId_, frameId_);
|
||||
pnh.param("normal_k", normalKSearch_, normalKSearch_);
|
||||
pnh.param("ground_normal_angle", groundNormalAngle_, groundNormalAngle_);
|
||||
if(pnh.hasParam("normal_estimation_radius") && !pnh.hasParam("cluster_radius"))
|
||||
{
|
||||
NODELET_WARN("Parameter \"normal_estimation_radius\" has been renamed "
|
||||
"to \"cluster_radius\"! Your value is still copied to "
|
||||
"corresponding parameter. Instead of normal radius, nearest neighbors count "
|
||||
"\"normal_k\" is used instead (default 20).");
|
||||
pnh.param("normal_estimation_radius", clusterRadius_, clusterRadius_);
|
||||
}
|
||||
else
|
||||
{
|
||||
pnh.param("cluster_radius", clusterRadius_, clusterRadius_);
|
||||
}
|
||||
pnh.param("min_cluster_size", minClusterSize_, minClusterSize_);
|
||||
pnh.param("max_obstacles_height", maxObstaclesHeight_, maxObstaclesHeight_);
|
||||
pnh.param("max_ground_height", maxGroundHeight_, maxGroundHeight_);
|
||||
pnh.param("detect_flat_obstacles", segmentFlatObstacles_, segmentFlatObstacles_);
|
||||
pnh.param("wait_for_transform", waitForTransform_, waitForTransform_);
|
||||
pnh.param("optimize_for_close_objects", optimizeForCloseObjects_, optimizeForCloseObjects_);
|
||||
pnh.param("proj_voxel_size", projVoxelSize_, projVoxelSize_);
|
||||
|
||||
cloudSub_ = nh.subscribe("cloud", 1, &ObstaclesDetectionOld::callback, this);
|
||||
|
||||
groundPub_ = nh.advertise<sensor_msgs::PointCloud2>("ground", 1);
|
||||
obstaclesPub_ = nh.advertise<sensor_msgs::PointCloud2>("obstacles", 1);
|
||||
projObstaclesPub_ = nh.advertise<sensor_msgs::PointCloud2>("proj_obstacles", 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void callback(const sensor_msgs::PointCloud2ConstPtr & cloudMsg)
|
||||
{
|
||||
ros::WallTime time = ros::WallTime::now();
|
||||
|
||||
if (groundPub_.getNumSubscribers() == 0 && obstaclesPub_.getNumSubscribers() == 0 && projObstaclesPub_.getNumSubscribers() == 0)
|
||||
{
|
||||
// no one wants the results
|
||||
return;
|
||||
}
|
||||
|
||||
rtabmap::Transform localTransform;
|
||||
try
|
||||
{
|
||||
if(waitForTransform_)
|
||||
{
|
||||
if(!tfListener_.waitForTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, ros::Duration(1)))
|
||||
{
|
||||
NODELET_ERROR("Could not get transform from %s to %s after 1 second!", frameId_.c_str(), cloudMsg->header.frame_id.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
tf::StampedTransform tmp;
|
||||
tfListener_.lookupTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, tmp);
|
||||
localTransform = rtabmap_ros::transformFromTF(tmp);
|
||||
}
|
||||
catch(tf::TransformException & ex)
|
||||
{
|
||||
NODELET_ERROR("%s",ex.what());
|
||||
return;
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromROSMsg(*cloudMsg, *originalCloud);
|
||||
|
||||
//Common variables for all strategies
|
||||
pcl::IndicesPtr ground, obstacles;
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloudWithoutFlatSurfaces(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
|
||||
if(originalCloud->size())
|
||||
{
|
||||
originalCloud = rtabmap::util3d::transformPointCloud(originalCloud, localTransform);
|
||||
if(maxObstaclesHeight_ > 0)
|
||||
{
|
||||
// std::numeric_limits<float>::lowest() exists only for c++11
|
||||
originalCloud = rtabmap::util3d::passThrough(originalCloud, "z", std::numeric_limits<int>::min(), maxObstaclesHeight_);
|
||||
}
|
||||
|
||||
if(originalCloud->size())
|
||||
{
|
||||
if(!optimizeForCloseObjects_)
|
||||
{
|
||||
// This is the default strategy
|
||||
pcl::IndicesPtr flatObstacles(new std::vector<int>);
|
||||
rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(
|
||||
originalCloud,
|
||||
ground,
|
||||
obstacles,
|
||||
normalKSearch_,
|
||||
groundNormalAngle_,
|
||||
clusterRadius_,
|
||||
minClusterSize_,
|
||||
segmentFlatObstacles_,
|
||||
maxGroundHeight_,
|
||||
&flatObstacles);
|
||||
|
||||
if(groundPub_.getNumSubscribers() &&
|
||||
ground.get() && ground->size())
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud, *ground, *groundCloud);
|
||||
}
|
||||
|
||||
if((obstaclesPub_.getNumSubscribers() || projObstaclesPub_.getNumSubscribers()) &&
|
||||
obstacles.get() && obstacles->size())
|
||||
{
|
||||
// remove flat obstacles from obstacles
|
||||
std::set<int> flatObstaclesSet;
|
||||
if(projObstaclesPub_.getNumSubscribers())
|
||||
{
|
||||
flatObstaclesSet.insert(flatObstacles->begin(), flatObstacles->end());
|
||||
}
|
||||
|
||||
obstaclesCloud->resize(obstacles->size());
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(obstacles->size());
|
||||
|
||||
int oi=0;
|
||||
for(unsigned int i=0; i<obstacles->size(); ++i)
|
||||
{
|
||||
obstaclesCloud->points[i] = originalCloud->at(obstacles->at(i));
|
||||
if(flatObstaclesSet.size() == 0 ||
|
||||
flatObstaclesSet.find(obstacles->at(i))==flatObstaclesSet.end())
|
||||
{
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi] = obstaclesCloud->points[i];
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi].z = 0;
|
||||
++oi;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(oi);
|
||||
if(obstaclesCloudWithoutFlatSurfaces->size() && projVoxelSize_ > 0.0)
|
||||
{
|
||||
obstaclesCloudWithoutFlatSurfaces = rtabmap::util3d::voxelize(obstaclesCloudWithoutFlatSurfaces, projVoxelSize_);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// in this case optimizeForCloseObject_ is true:
|
||||
// we divide the floor point cloud into two subsections, one for all potential floor points up to 1m
|
||||
// one for potential floor points further away than 1m.
|
||||
// For the points at closer range, we use a smaller normal estimation radius and ground normal angle,
|
||||
// which allows to detect smaller objects, without increasing the number of false positive.
|
||||
// For all other points, we use a bigger normal estimation radius (* 3.) and tolerance for the
|
||||
// grond normal angle (* 2.).
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud_near = rtabmap::util3d::passThrough(originalCloud, "x", std::numeric_limits<int>::min(), 1.);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud_far = rtabmap::util3d::passThrough(originalCloud, "x", 1., std::numeric_limits<int>::max());
|
||||
|
||||
// Part 1: segment floor and obstacles near the robot
|
||||
rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(
|
||||
originalCloud_near,
|
||||
ground,
|
||||
obstacles,
|
||||
normalKSearch_,
|
||||
groundNormalAngle_,
|
||||
clusterRadius_,
|
||||
minClusterSize_,
|
||||
segmentFlatObstacles_,
|
||||
maxGroundHeight_);
|
||||
|
||||
if(groundPub_.getNumSubscribers() && ground.get() && ground->size())
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud_near, *ground, *groundCloud);
|
||||
ground->clear();
|
||||
}
|
||||
|
||||
if((obstaclesPub_.getNumSubscribers() || projObstaclesPub_.getNumSubscribers()) && obstacles.get() && obstacles->size())
|
||||
{
|
||||
pcl::copyPointCloud(*originalCloud_near, *obstacles, *obstaclesCloud);
|
||||
obstacles->clear();
|
||||
}
|
||||
|
||||
// Part 2: segment floor and obstacles far from the robot
|
||||
rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(
|
||||
originalCloud_far,
|
||||
ground,
|
||||
obstacles,
|
||||
normalKSearch_,
|
||||
2.*groundNormalAngle_,
|
||||
3.*clusterRadius_,
|
||||
minClusterSize_,
|
||||
segmentFlatObstacles_,
|
||||
maxGroundHeight_);
|
||||
|
||||
if(groundPub_.getNumSubscribers() && ground.get() && ground->size())
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud2 (new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::copyPointCloud(*originalCloud_far, *ground, *groundCloud2);
|
||||
*groundCloud += *groundCloud2;
|
||||
}
|
||||
|
||||
|
||||
if((obstaclesPub_.getNumSubscribers() || projObstaclesPub_.getNumSubscribers()) && obstacles.get() && obstacles->size())
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstacles2(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::copyPointCloud(*originalCloud_far, *obstacles, *obstacles2);
|
||||
*obstaclesCloud += *obstacles2;
|
||||
}
|
||||
}
|
||||
|
||||
if(!localTransform.isIdentity())
|
||||
{
|
||||
//transform back in topic frame
|
||||
rtabmap::Transform localTransformInv = localTransform.inverse();
|
||||
if(groundCloud->size())
|
||||
{
|
||||
groundCloud = rtabmap::util3d::transformPointCloud(groundCloud, localTransformInv);
|
||||
}
|
||||
if(obstaclesCloud->size())
|
||||
{
|
||||
obstaclesCloud = rtabmap::util3d::transformPointCloud(obstaclesCloud, localTransformInv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(groundPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::PointCloud2 rosCloud;
|
||||
pcl::toROSMsg(*groundCloud, rosCloud);
|
||||
rosCloud.header = cloudMsg->header;
|
||||
|
||||
//publish the message
|
||||
groundPub_.publish(rosCloud);
|
||||
}
|
||||
|
||||
if(obstaclesPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::PointCloud2 rosCloud;
|
||||
pcl::toROSMsg(*obstaclesCloud, rosCloud);
|
||||
rosCloud.header = cloudMsg->header;
|
||||
|
||||
//publish the message
|
||||
obstaclesPub_.publish(rosCloud);
|
||||
}
|
||||
|
||||
if(projObstaclesPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::PointCloud2 rosCloud;
|
||||
pcl::toROSMsg(*obstaclesCloudWithoutFlatSurfaces, rosCloud);
|
||||
rosCloud.header.stamp = cloudMsg->header.stamp;
|
||||
rosCloud.header.frame_id = frameId_;
|
||||
|
||||
//publish the message
|
||||
projObstaclesPub_.publish(rosCloud);
|
||||
}
|
||||
|
||||
NODELET_DEBUG("Obstacles segmentation time = %f s", (ros::WallTime::now() - time).toSec());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string frameId_;
|
||||
int normalKSearch_;
|
||||
double groundNormalAngle_;
|
||||
double clusterRadius_;
|
||||
int minClusterSize_;
|
||||
double maxObstaclesHeight_;
|
||||
double maxGroundHeight_;
|
||||
bool segmentFlatObstacles_;
|
||||
bool waitForTransform_;
|
||||
bool optimizeForCloseObjects_;
|
||||
double projVoxelSize_;
|
||||
|
||||
tf::TransformListener tfListener_;
|
||||
|
||||
ros::Publisher groundPub_;
|
||||
ros::Publisher obstaclesPub_;
|
||||
ros::Publisher projObstaclesPub_;
|
||||
|
||||
ros::Subscriber cloudSub_;
|
||||
};
|
||||
|
||||
PLUGINLIB_EXPORT_CLASS(rtabmap_ros::ObstaclesDetectionOld, nodelet::Nodelet);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user