33 #include <sensor_msgs/PointCloud2.h> 35 #include <turtlebot_arm_block_manipulation/BlockDetectionAction.h> 39 #include <pcl/conversions.h> 40 #include <pcl/point_cloud.h> 41 #include <pcl/point_types.h> 42 #include <pcl/filters/passthrough.h> 43 #include <pcl/segmentation/extract_clusters.h> 44 #include <pcl/filters/extract_indices.h> 45 #include <pcl/kdtree/kdtree_flann.h> 46 #include <pcl/sample_consensus/method_types.h> 47 #include <pcl/sample_consensus/model_types.h> 48 #include <pcl/segmentation/sac_segmentation.h> 49 #include <pcl/segmentation/extract_clusters.h> 70 turtlebot_arm_block_manipulation::BlockDetectionFeedback
feedback_;
71 turtlebot_arm_block_manipulation::BlockDetectionResult
result_;
72 turtlebot_arm_block_manipulation::BlockDetectionGoalConstPtr
goal_;
100 nh_(
"~"), as_(name, false), action_name_(name)
103 if ((nh_.
getParam(
"table_pose", table_pose_) ==
true) && (table_pose_.size() != 3))
105 ROS_ERROR(
"Invalid table_pose vector size; must contain 3 values (x, y, z); ignoring");
119 pub_ = nh_.
advertise< pcl::PointCloud<pcl::PointXYZRGB> >(
"block_output", 1);
122 block_pub_ = nh_.
advertise<geometry_msgs::PoseArray>(
"/turtlebot_blocks", 1,
true);
127 ROS_INFO(
"[block detection] Received goal!");
129 result_.blocks.poses.clear();
133 block_size_ = goal_->block_size;
134 table_height_ = goal_->table_height;
135 arm_link_ = goal_->frame;
137 result_.blocks.header.frame_id =
arm_link_;
141 if (table_pose_.size() == 3)
143 if (std::abs(table_height_ - table_pose_[2]) > 0.05)
144 ROS_WARN(
"The table height (%f) passed with goal and table_pose[2] parameter (%f) " \
145 "should be very similar", table_height_, table_pose_[2]);
152 ROS_INFO(
"%s: Preempted", action_name_.c_str());
157 void cloudCb(
const sensor_msgs::PointCloud2ConstPtr& msg)
163 result_.blocks.header.stamp = msg->header.stamp;
166 pcl::PointCloud < pcl::PointXYZRGB > cloud;
170 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_transformed(
new pcl::PointCloud<pcl::PointXYZRGB>);
172 tf_listener_.
waitForTransform(std::string(arm_link_), cloud.header.frame_id,
176 ROS_ERROR(
"Error converting to desired frame");
181 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(
new pcl::PointCloud<pcl::PointXYZRGB>);
182 pcl::SACSegmentation<pcl::PointXYZRGB> seg;
183 pcl::PointIndices::Ptr inliers(
new pcl::PointIndices);
184 pcl::ModelCoefficients::Ptr coefficients(
new pcl::ModelCoefficients);
185 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_plane(
new pcl::PointCloud<pcl::PointXYZRGB>());
186 seg.setOptimizeCoefficients(
true);
187 seg.setModelType(pcl::SACMODEL_PLANE);
188 seg.setMethodType(pcl::SAC_RANSAC);
189 seg.setMaxIterations(200);
190 seg.setDistanceThreshold(0.005);
193 pcl::PassThrough<pcl::PointXYZRGB> pass;
194 pass.setInputCloud(cloud_transformed);
195 pass.setFilterFieldName(
"z");
197 pass.setFilterLimits(table_height_ - 0.05, table_height_ + block_size_ + 0.05);
198 pass.filter(*cloud_filtered);
199 if (cloud_filtered->points.size() == 0)
205 ROS_INFO(
"Filtered, %d points left", (
int ) cloud_filtered->points.size());
207 int nr_points = cloud_filtered->points.size ();
208 while (cloud_filtered->points.size() > 0.3 * nr_points)
211 seg.setInputCloud(cloud_filtered);
212 seg.segment(*inliers, *coefficients);
213 if (inliers->indices.size() == 0)
215 std::cout <<
"Could not estimate a planar model for the given dataset." << std::endl;
219 std::cout <<
"Inliers: " << (inliers->indices.size()) << std::endl;
222 pcl::ExtractIndices<pcl::PointXYZRGB> extract;
223 extract.setInputCloud(cloud_filtered);
224 extract.setIndices(inliers);
225 extract.setNegative(
false);
228 extract.filter(*cloud_plane);
229 std::cout <<
"PointCloud representing the planar component: " 230 << cloud_plane->points.size() <<
" data points." << std::endl;
233 extract.setNegative(
true);
234 extract.filter(*cloud_filtered);
238 pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree(
new pcl::search::KdTree<pcl::PointXYZRGB>);
239 tree->setInputCloud(cloud_filtered);
241 std::vector<pcl::PointIndices> cluster_indices;
242 pcl::EuclideanClusterExtraction<pcl::PointXYZRGB> ec;
243 ec.setClusterTolerance(0.005);
244 ec.setMinClusterSize(100);
245 ec.setMaxClusterSize(25000);
246 ec.setSearchMethod(tree);
247 ec.setInputCloud(cloud_filtered);
248 ec.extract(cluster_indices);
253 for (
size_t c = 0; c < cluster_indices.size (); ++c)
256 float xmin = 0;
float xmax = 0;
257 float ymin = 0;
float ymax = 0;
258 float zmin = 0;
float zmax = 0;
260 for (
size_t i = 0; i < cluster_indices[c].indices.size(); i++)
262 int j = cluster_indices[c].indices[i];
263 float x = cloud_filtered->points[j].x;
264 float y = cloud_filtered->points[j].y;
265 float z = cloud_filtered->points[j].z;
274 xmin = std::min(xmin, x);
275 xmax = std::max(xmax, x);
276 ymin = std::min(ymin, y);
277 ymax = std::max(ymax, y);
278 zmin = std::min(zmin, z);
279 zmax = std::max(zmax, z);
284 float xside = xmax-xmin;
285 float yside = ymax-ymin;
286 float zside = zmax-zmin;
288 const float tol = 0.01;
292 if (xside > block_size_ - tol && xside < block_size_*sqrt(2) + tol &&
293 yside > block_size_ - tol && yside < block_size_*sqrt(2) + tol &&
294 zside > tol && zside < block_size_ + tol)
296 float x = xmin + xside/2.0;
297 float y = ymin + yside/2.0;
298 float z = zmax - block_size_/2.0;
302 if ((table_pose_.size() == 3) &&
303 ((x < table_pose_[0]) || (x > table_pose_[0] +
TABLE_SIZE_X) ||
304 (y < table_pose_[1] - TABLE_SIZE_Y/2.0) || (y > table_pose_[1] + TABLE_SIZE_Y/2.0)))
306 ROS_DEBUG_STREAM(
"Block at [" << x <<
", " << y <<
"] outside table limits [" 307 << table_pose_[0] - TABLE_SIZE_X/2.0 <<
", " << table_pose_[0] + TABLE_SIZE_X/2.0 <<
", " 308 << table_pose_[1] - TABLE_SIZE_Y/2.0 <<
", " << table_pose_[1] + TABLE_SIZE_Y/2.0);
313 float angle = atan(block_size_/((xside + yside)/2));
315 if (yside < block_size_)
318 ROS_INFO_STREAM(
"xside: " << xside <<
" yside: " << yside <<
" zside " << zside <<
" angle: " << angle);
320 ROS_INFO_STREAM(
"Adding a new block at [" << x <<
", " << y <<
", " << z <<
", " << angle <<
"]");
325 if (result_.blocks.poses.size() > 0)
328 block_pub_.
publish(result_.blocks);
329 ROS_INFO(
"[block detection] Set as succeeded!");
332 ROS_INFO(
"[block detection] Couldn't find any blocks this iteration!");
339 void addBlock(
float x,
float y,
float z,
float angle)
341 geometry_msgs::Pose block_pose;
342 block_pose.position.x = x;
343 block_pose.position.y = y;
344 block_pose.position.z = z;
346 Eigen::Quaternionf
quat(Eigen::AngleAxis<float>(angle, Eigen::Vector3f(0,0,1)));
348 block_pose.orientation.x = quat.x();
349 block_pose.orientation.y = quat.y();
350 block_pose.orientation.z = quat.z();
351 block_pose.orientation.w = quat.w();
353 result_.blocks.poses.push_back(block_pose);
361 moveit_msgs::CollisionObject co;
368 co.operation = moveit_msgs::CollisionObject::ADD;
369 co.primitives.resize(1);
370 co.primitives[0].type = shape_msgs::SolidPrimitive::BOX;
371 co.primitives[0].dimensions.resize(3);
372 co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_X] =
TABLE_SIZE_X;
373 co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_Y] =
TABLE_SIZE_Y;
374 co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_Z] =
TABLE_SIZE_Z;
375 co.primitive_poses.resize(1);
376 co.primitive_poses[0].position.x = table_pose_[0] + TABLE_SIZE_X/2.0;
377 co.primitive_poses[0].position.y = table_pose_[1];
378 co.primitive_poses[0].position.z = table_pose_[2] - TABLE_SIZE_Z/2.0;
380 ROS_INFO(
"Add the table as a collision object into the world");
381 std::vector<moveit_msgs::CollisionObject> collision_objects(1, co);
389 int main(
int argc,
char** argv)
391 ros::init(argc, argv,
"block_detection_action_server");
turtlebot_arm_block_manipulation::BlockDetectionGoalConstPtr goal_
boost::shared_ptr< const Goal > acceptNewGoal()
void publish(const boost::shared_ptr< M > &message) const
turtlebot_arm_block_manipulation::BlockDetectionFeedback feedback_
moveit::planning_interface::PlanningSceneInterface planning_scene_interface_
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ros::Publisher c_obj_pub_
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
boost::shared_ptr< interactive_markers::InteractiveMarkerServer > server
actionlib::SimpleActionServer< turtlebot_arm_block_manipulation::BlockDetectionAction > as_
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
void addCollisionObjects(const std::vector< moveit_msgs::CollisionObject > &collision_objects, const std::vector< moveit_msgs::ObjectColor > &object_colors=std::vector< moveit_msgs::ObjectColor >()) const
void setSucceeded(const Result &result=Result(), const std::string &text=std::string(""))
ROSCPP_DECL void spin(Spinner &spinner)
void addBlock(float x, float y, float z, float angle)
turtlebot_arm_block_manipulation::BlockDetectionResult result_
void setPreempted(const Result &result=Result(), const std::string &text=std::string(""))
BlockDetectionServer(const std::string name)
int main(int argc, char **argv)
void registerPreemptCallback(boost::function< void()> cb)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
#define ROS_DEBUG_STREAM(args)
const double TABLE_SIZE_Y
ros::Publisher block_pub_
const double TABLE_SIZE_Z
#define ROS_INFO_STREAM(args)
tf::TransformListener tf_listener_
void transformPointCloud(const pcl::PointCloud< PointT > &cloud_in, pcl::PointCloud< PointT > &cloud_out, const tf::Transform &transform)
bool getParam(const std::string &key, std::string &s) const
void removeCollisionObjects(const std::vector< std::string > &object_ids) const
std::vector< double > table_pose_
const double TABLE_SIZE_X
void registerGoalCallback(boost::function< void()> cb)
void cloudCb(const sensor_msgs::PointCloud2ConstPtr &msg)