object_detection_action_server.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015, Jorge Santos
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Author: Jorge Santos
30  */
31 
32 #include <cmath>
33 #include <algorithm>
34 
35 #include <ros/ros.h>
36 #include <tf/transform_listener.h>
37 
38 // auxiliary libraries
41 
42 // action client: ORK's tabletop object recognition
45 #include <object_recognition_msgs/TableArray.h>
46 #include <object_recognition_msgs/RecognizedObjectArray.h>
47 #include <object_recognition_msgs/GetObjectInformation.h>
48 #include <object_recognition_msgs/ObjectRecognitionAction.h>
49 
50 // action server: make things easier for interactive manipulation
52 #include <turtlebot_arm_object_manipulation/ObjectDetectionAction.h>
53 
54 // MoveIt!
55 #include <moveit_msgs/ObjectColor.h>
56 #include <moveit_msgs/PlanningScene.h>
58 
59 
61 {
62 
64 {
65  class DetectionBin; // forward declaration of private class DetectionBin
66 
67 private:
68 
69  // ROS interface
72 
73  // Action client for the ORK object recognition and server
77 
78  // Action server to handle it conveniently for our object manipulation demo
80  turtlebot_arm_object_manipulation::ObjectDetectionFeedback feedback_;
81  turtlebot_arm_object_manipulation::ObjectDetectionResult result_;
82  turtlebot_arm_object_manipulation::ObjectDetectionGoalConstPtr goal_;
83  std::string action_name_;
84 
85  // Get object information from database service and keep in a map
87  std::map<std::string, object_recognition_msgs::ObjectInformation> objs_info_;
88 
89  // Publishers and subscribers
91 
94 
95  typedef struct
96  {
97  double size_x;
98  double size_y;
99  double center_x;
100  double center_y;
101  double yaw;
102  } TableDescriptor;
103 
104  std::vector<geometry_msgs::Pose> table_poses_;
105  std::vector<TableDescriptor> table_params_;
106 
107  // We use the planning_scene_interface::PlanningSceneInterface to manipulate the world
109 
111 
112  // Parameters from goal
113  std::string arm_link_;
114 
115  // Object detection and classification constants
116  const double CONFIDENCE_THRESHOLD = 0.85; // minimum confidence required to accept an object
117  const double CLUSTERING_THRESHOLD = 0.05; // maximum acceptable distance to assign an object to a bin
118  const unsigned CALLS_TO_ORK_TABLETOP = 10;
119 
120 public:
121  ObjectDetectionServer(const std::string name) :
122  pnh_("~"), ork_ac_("tabletop/recognize_objects", true), od_as_(name, false), action_name_(name),
123  ork_execute_timeout_(5.0), ork_preempt_timeout_(1.0)
124  {
125  // Create the action client; spin its own thread
126 
127  // Wait for the tabletop/recognize_objects action server to start before we provide our own service
128  ROS_INFO("[object detection] Waiting for tabletop/recognize_objects action server to start...");
129  if (! ork_ac_.waitForServer(ros::Duration(60.0)))
130  {
131  ROS_ERROR("[object detection] tabletop/recognize_objects action server not available after 1 minute");
132  ROS_ERROR("[object detection] Shutting down node...");
133  throw;
134  }
135 
136  ROS_INFO("[object detection] tabletop/recognize_objects action server started; ready for sending goals.");
137 
138  // Wait for the get object information service (mandatory, as we need to know objects' mesh)
139  obj_info_srv_ = nh_.serviceClient<object_recognition_msgs::GetObjectInformation>("get_object_info");
140  if (! obj_info_srv_.waitForExistence(ros::Duration(60.0)))
141  {
142  ROS_ERROR("[object detection] Get object information service not available after 1 minute");
143  ROS_ERROR("[object detection] Shutting down node...");
144  throw;
145  }
146 
147  // Register the goal and feedback callbacks.
148  od_as_.registerGoalCallback(boost::bind(&ObjectDetectionServer::goalCB, this));
149  od_as_.registerPreemptCallback(boost::bind(&ObjectDetectionServer::preemptCB, this));
150 
151  od_as_.start();
152 
153  // Subscribe to detected tables array
154  table_sub_ = nh_.subscribe("tabletop/table_array", 1, &ObjectDetectionServer::tableCb, this);
155 
156  // Publish empty objects and table to clear ORK RViz visualizations
157  clear_objs_pub_ =
158  nh_.advertise<object_recognition_msgs::RecognizedObjectArray>("/tabletop/recognized_object_array", 1, true);
159  clear_table_pub_ =
160  nh_.advertise<object_recognition_msgs::TableArray>("/tabletop/table_array", 1, true);
161  }
162 
163  void goalCB()
164  {
165  ROS_INFO("[object detection] Received goal!");
166 
167  // Accept the new goal
168  goal_ = od_as_.acceptNewGoal();
169 
170  arm_link_ = goal_->frame;
171 
172  // Clear results from previous goals
173  table_poses_.clear();
174  table_params_.clear();
175  result_.obj_names.clear();
176 
177  planning_scene_interface_.removeCollisionObjects(planning_scene_interface_.getKnownObjectNames());
178 
179 
180  // Call ORK tabletop action server and wait for the action to return
181  // We do it CALLS_TO_TABLETOP times and accumulate the results on bins to provide more reliable results
182  std::vector<DetectionBin> detection_bins;
183 
184  ROS_INFO("[object detection] Sending %d goals to tabletop/recognize_objects action server...",
185  CALLS_TO_ORK_TABLETOP);
186  object_recognition_msgs::ObjectRecognitionGoal goal;
187  for (int i = 0; i < CALLS_TO_ORK_TABLETOP; ++i)
188  {
190  ork_ac_.sendGoalAndWait(goal, ork_execute_timeout_, ork_preempt_timeout_);
191 
193  {
194  ROS_INFO("[object detection] Action successfully finished");
195  }
196  else
197  {
198  ROS_WARN("[object detection] Tabletop/recognize_objects action did not finish before the time out: %s",
199  state.toString().c_str());
200  od_as_.setAborted(result_, "Tabletop/recognize_objects action did not finish before the time out");
201  continue;
202  }
203 
204  object_recognition_msgs::ObjectRecognitionResultConstPtr result = ork_ac_.getResult();
205 
206  // Classify objects detected in each call to tabletop into bins based on distance to bin's centroid
207  for (const object_recognition_msgs::RecognizedObject& obj: result->recognized_objects.objects)
208  {
209  if (obj.confidence < CONFIDENCE_THRESHOLD)
210  continue;
211 
212  bool assigned = false;
213  for (DetectionBin& bin: detection_bins)
214  {
215  if (mtk::distance3D(bin.getCentroid().pose, obj.pose.pose.pose) <= CLUSTERING_THRESHOLD)
216  {
217  ROS_DEBUG("Object with pose [%s] added to bin %d with centroid [%s] with distance [%f]",
218  mtk::pose2str3D(obj.pose.pose.pose).c_str(), bin.id, mtk::pose2str3D(bin.getCentroid()).c_str(),
219  mtk::distance3D(bin.getCentroid().pose, obj.pose.pose.pose));
220  bin.addObject(obj);
221  assigned = true;
222  break;
223  }
224  }
225 
226  if (! assigned)
227  {
228  // No matching bin; create a new one for this object
229  ROS_DEBUG("Object with pose [%s] added to a new bin", mtk::pose2str3D(obj.pose.pose.pose).c_str());
230  DetectionBin new_bin;
231  new_bin.id = detection_bins.size();
232  new_bin.addObject(obj);
233  detection_bins.push_back(new_bin);
234  }
235  }
236 
237  ros::spinOnce(); // keep spinning so table messages callbacks are processed
238  } // loop up to CALLS_TO_ORK_TABLETOP
239 
240  // Add a detected object per bin to the goal result, if the bin is consistent enough
241  int added_objects = addObjects(detection_bins);
242  if (added_objects > 0)
243  {
244  ROS_INFO("[object detection] Succeeded! %d objects detected", added_objects);
245 
246  // Add also the table as a collision object, so it gets filtered out from MoveIt! octomap
247  if (table_poses_.size() > 0)
248  addTable(table_poses_, table_params_);
249  else
250  ROS_WARN("[object detection] No near-horizontal table detected!");
251 
252  // Clear recognized objects and tables from RViz by publishing empty messages, so they don't
253  // mangle with interactive markers; shoddy... ORK visualizations should have expiration time
254  object_recognition_msgs::RecognizedObjectArray roa;
255  object_recognition_msgs::TableArray ta;
256 
257  roa.header.frame_id = arm_link_;
258  ta.header.frame_id = arm_link_;
259 
260  clear_objs_pub_.publish(roa);
261  clear_table_pub_.publish(ta);
262  }
263  else
264  {
265  ROS_INFO("[object detection] Succeeded, but couldn't find any object");
266  }
267 
268  od_as_.setSucceeded(result_);
269  }
270 
271  void preemptCB()
272  {
273  ROS_WARN("[object detection] %s: Preempted", action_name_.c_str());
274  // set the action state to preempted
275  od_as_.setPreempted();
276  }
277 
278  void tableCb(const object_recognition_msgs::TableArray& msg)
279  {
280  if (msg.tables.size() == 0)
281  {
282  ROS_WARN("[object detection] Table array message is empty");
283  return;
284  }
285 
286  // Accumulate table poses while detecting objects so the resulting pose (centroid) is more accurate
287  // We only take the first table in the message, assuming it is always the best match and so the same
288  // table... very risky, to say the least. TODO: assure we always accumulate data over the same table
289  object_recognition_msgs::Table table = msg.tables[0];
290  geometry_msgs::Pose table_pose = table.pose;
291  // Tables often have orientations with all-nan values, but assertQuaternionValid lets them go!
292  if (std::isnan(table.pose.orientation.x) ||
293  std::isnan(table.pose.orientation.y) ||
294  std::isnan(table.pose.orientation.z) ||
295  std::isnan(table.pose.orientation.w))
296  {
297  ROS_WARN("[object detection] Table discarded as its orientation has nan values");
298  return;
299  }
300 
301  if (! transformPose(msg.header.frame_id, arm_link_, table.pose, table_pose))
302  {
303  return;
304  }
305 
306  if ((std::abs(mtk::roll(table_pose)) < M_PI/10.0) && (std::abs(mtk::pitch(table_pose)) < M_PI/10.0))
307  {
308  // Only consider tables within +/-18 degrees away from the horizontal plane
309  table_poses_.push_back(table_pose);
310  table_params_.push_back(getTableParams(table.convex_hull));
311  }
312  else
313  {
314  ROS_WARN("[object detection] Table with pose [%s] discarded: %.2f radians away from the horizontal",
315  mtk::pose2str3D(table_pose).c_str(),
316  std::max(std::abs(mtk::roll(table_pose)), std::abs(mtk::pitch(table_pose))));
317  }
318  }
319 
320 
321 private:
322 
323  int addObjects(const std::vector<DetectionBin>& detection_bins)
324  {
325  std::map<std::string, unsigned int> obj_name_occurences;
326  std::vector<moveit_msgs::CollisionObject> collision_objects;
327  moveit_msgs::PlanningScene ps;
328  ps.is_diff = true;
329 
330  // Add a detected object per bin to the goal result and to the planning scene as a collision object
331  // Only bins receiving detections on most of the ORK tabletop calls are considered consistent enough
332  for (const DetectionBin& bin: detection_bins)
333  {
334  if (bin.countObjects() < CALLS_TO_ORK_TABLETOP/1.5)
335  {
336  ROS_DEBUG("Bin %d with centroid [%s] discarded as it received %d objects out of %d attempts",
337  bin.id, mtk::pose2str3D(bin.getCentroid()).c_str(), bin.countObjects(), CALLS_TO_ORK_TABLETOP);
338  continue;
339  }
340 
341  try
342  {
343  // Compose object name with the name provided by the database plus an index, starting with [1]
344  object_recognition_msgs::ObjectInformation obj_info = getObjInfo(bin.getType());
345  obj_name_occurences[obj_info.name]++;
346  std::stringstream sstream;
347  sstream << obj_info.name << " [" << obj_name_occurences[obj_info.name] << "]";
348  std::string obj_name = sstream.str();
349 
350  ROS_DEBUG("Bin %d with centroid [%s] and %d objects added as object '%s'",
351  bin.id, mtk::pose2str3D(bin.getCentroid()).c_str(), bin.countObjects(), obj_name.c_str());
352 
353  geometry_msgs::Pose out_pose;
354  transformPose(bin.getCentroid().header.frame_id, arm_link_, bin.getCentroid().pose, out_pose);
355  ROS_INFO("[object detection] Adding '%s' object at %s",
356  obj_name.c_str(), mtk::point2str3D(out_pose.position).c_str());
357 
358  result_.obj_names.push_back(obj_name);
359 
360  moveit_msgs::CollisionObject co;
361  co.id = obj_name;
362  co.header.frame_id = arm_link_;
363  co.operation = moveit_msgs::CollisionObject::ADD;
364 
365  if (obj_name.find("cube 2.5") != std::string::npos)
366  {
367  // XXX, TODO : temporal hack because bloody meshes don't clean the octomap! (anyway, as I support both mechanism, it's also useful for debug)
368  // Problem discussed in this moveit-users group thread: https://groups.google.com/forum/?fromgroups#!topic/moveit-users/-GuBSnvCYbM
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] = 0.025;
373  co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_Y] = 0.025;
374  co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_Z] = 0.025;
375  out_pose.position.z += 0.0125;
376  co.primitive_poses.push_back(out_pose);
377  }
378  else
379  {
380  co.meshes.resize(1, obj_info.ground_truth_mesh);
381  co.mesh_poses.push_back(out_pose);
382  }
383 
384  collision_objects.push_back(co);
385 
386  // Provide a random color to the collision object
387  moveit_msgs::ObjectColor oc;
388  oc.id = co.id;
389  oc.color = getRandColor(1.0);
390  ps.object_colors.push_back(oc);
391  }
392  catch (...)
393  {
394  continue;
395  }
396  }
397 
398  if (collision_objects.size() > 0)
399  {
400  planning_scene_interface_.addCollisionObjects(collision_objects, ps.object_colors);
401  }
402 
403  return collision_objects.size();
404  }
405 
406  void addTable(const std::vector<geometry_msgs::Pose>& table_poses,
407  const std::vector<TableDescriptor>& table_params)
408  {
409  // Add the table as a collision object into the world, so it gets excluded from the collision map
410 
411  // We calculate table size, centroid and orientation as the median of the accumulated convex hulls
412  std::vector<double> table_center_x;
413  std::vector<double> table_center_y;
414  std::vector<double> table_size_x;
415  std::vector<double> table_size_y;
416  std::vector<double> table_yaw;
417 
418  for (const TableDescriptor& table: table_params)
419  {
420  table_center_x.push_back(table.center_x);
421  table_center_y.push_back(table.center_y);
422  table_size_x.push_back(table.size_x);
423  table_size_y.push_back(table.size_y);
424  table_yaw.push_back(table.yaw);
425  }
426 
427  moveit_msgs::CollisionObject co;
428  co.header.stamp = ros::Time::now();
429  co.header.frame_id = arm_link_;
430  co.id = "table";
431 
432  co.operation = moveit_msgs::CollisionObject::ADD;
433  co.primitives.resize(1);
434  co.primitives[0].type = shape_msgs::SolidPrimitive::BOX;
435  co.primitives[0].dimensions.resize(3);
436  co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_X] = median(table_size_x);
437  co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_Y] = median(table_size_y);
438  co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_Z] = 0.01; // arbitrarily set to 1 cm
439  co.primitive_poses.resize(1);
440 
441  // Calculate table's pose as the centroid of all accumulated poses;
442  // as yaw we use the one estimated for the convex hull
443  double roll_acc = 0.0, pitch_acc = 0.0;//, yaw_acc = 0.0;
444  for (const geometry_msgs::Pose& pose: table_poses)
445  {
446  co.primitive_poses[0].position.x += pose.position.x;
447  co.primitive_poses[0].position.y += pose.position.y;
448  co.primitive_poses[0].position.z += pose.position.z;
449  roll_acc += mtk::roll(pose);
450  pitch_acc += mtk::pitch (pose);
451  // yaw_acc += mtk::yaw(pose);
452  }
453 
454  co.primitive_poses[0].position.x = co.primitive_poses[0].position.x / (double)table_poses.size();
455  co.primitive_poses[0].position.y = co.primitive_poses[0].position.y / (double)table_poses.size();
456  co.primitive_poses[0].position.z = co.primitive_poses[0].position.z / (double)table_poses.size();
457  co.primitive_poses[0].orientation = tf::createQuaternionMsgFromRollPitchYaw(roll_acc/(double)table_poses.size(),
458  pitch_acc/(double)table_poses.size(),
459  median(table_yaw));
460  // Displace the table center according to the centroid of its convex hull
461  co.primitive_poses[0].position.x += median(table_center_x);
462  co.primitive_poses[0].position.y += median(table_center_y);
463  co.primitive_poses[0].position.z -= co.primitives[0].dimensions[shape_msgs::SolidPrimitive::BOX_Z]/2.0;
464 
465  ROS_INFO("[object detection] Adding a table at %s as a collision object, based on %u observations",
466  mtk::point2str3D(co.primitive_poses[0].position).c_str(), table_poses.size());
467  planning_scene_interface_.addCollisionObjects(std::vector<moveit_msgs::CollisionObject>(1, co));
468  }
469 
470  TableDescriptor getTableParams(std::vector<geometry_msgs::Point> convex_hull)
471  {
472  // Calculate centroid, size and orientation for the given 2D convex hull
473  // Algorithm adapted from here: http://www.geometrictools.com/Documentation/MinimumAreaRectangle.pdf
474  TableDescriptor table;
475  double min_area = std::numeric_limits<double>::max();
476 
477  for (size_t i0 = convex_hull.size() - 1, i1 = 0; i1 < convex_hull.size(); i0 = i1++)
478  {
479  convex_hull[i0].z = convex_hull[i1].z = 0; // z-coordinate is ignored
480 
481  tf::Point origin, U0, U1, D;
482  tf::pointMsgToTF(convex_hull[i0], origin);
483  tf::pointMsgToTF(convex_hull[i1], U0); U0 -= origin;
484  U0.normalize(); // length of U0 is 1
485  U1 = tf::Point(-U0.y(), U0.x(), 0.0); // - perpendicular vector of U0
486  U1.normalize(); // length of U1 is 1
487  double min0 = 0, max0 = 0; // projection onto U0 − axis is [min0, max0]
488  double min1 = 0, max1 = 0; // projection onto U1 − axis is [min1, max1], min1 = 0 is guaranteed TODO si?????
489  for (size_t j = 0; j < convex_hull.size(); ++j)
490  {
491  convex_hull[j].z = 0; // z-coordinate is ignored
492 
493  tf::pointMsgToTF(convex_hull[j], D); D -= origin;
494  double dot = U0.dot(D);
495  if (dot < min0)
496  min0 = dot;
497  else if (dot > max0)
498  max0 = dot;
499  dot = U1.dot(D);
500  if (dot < min1)
501  min1 = dot;
502  else if (dot > max1)
503  max1 = dot;
504  }
505  double area = (max0 - min0) * (max1 - min1);
506 
507  if (area < min_area)
508  {
509  tf::Point center(origin + ((min0 + max0)/2.0) * U0 + ((min1 + max1)/2.0) * U1);
510  table.center_x = center.y();
511  table.center_y = - center.x();
512  table.size_x = max0 - min0;
513  table.size_y = max1 - min1;
514  table.yaw = std::atan2(U1.y(), U1.x());
515  if (table.yaw > 0.0)
516  table.yaw -= M_PI;
517  min_area = area;
518  }
519  }
520  ROS_DEBUG("Table parameters: pose [%f, %f, %f %f], size [%f, %f]", table.center_x, table.center_y, table.yaw, table.size_x, table.size_y);
521 
522  return table;
523  }
524 
525  double median(std::vector<double>& values)
526  {
527  std::vector<double>::iterator first = values.begin();
528  std::vector<double>::iterator last = values.end();
529  std::vector<double>::iterator middle = first + (last - first) / 2;
530  std::nth_element(first, middle, last); // short values till middle one
531  return *middle;
532  }
533 
534  bool transformPose(const std::string& in_frame, const std::string& out_frame,
535  const geometry_msgs::Pose& in_pose, geometry_msgs::Pose& out_pose)
536  {
537  geometry_msgs::PoseStamped in_stamped;
538  geometry_msgs::PoseStamped out_stamped;
539 
540  in_stamped.header.frame_id = in_frame;
541  in_stamped.pose = in_pose;
542  try
543  {
544  tf_listener_.waitForTransform(in_frame, out_frame, ros::Time(0.0), ros::Duration(1.0));
545  tf_listener_.transformPose(out_frame, in_stamped, out_stamped);
546  out_pose = out_stamped.pose;
547 
548 // // Some verifications...
549 // // tables sometimes have orientations with all-nan values, but assertQuaternionValid lets them go!
550 // tf::assertQuaternionValid(out_stamped.pose.orientation);
551 // if (std::isnan(out_pose.orientation.x) ||
552 // std::isnan(out_pose.orientation.y) ||
553 // std::isnan(out_pose.orientation.z) ||
554 // std::isnan(out_pose.orientation.w))
555 // throw tf::InvalidArgument("Quaternion contains nan values");
556 
557  return true;
558  }
559  catch (tf::InvalidArgument& e)
560  {
561  ROS_ERROR("[object detection] Transformed pose has invalid orientation: %s", e.what());
562  return false;
563  }
564  catch (tf::TransformException& e)
565  {
566  ROS_ERROR("[object detection] Could not get sensor to arm transform: %s", e.what());
567  return false;
568  }
569  }
570 
571  const object_recognition_msgs::ObjectInformation& getObjInfo(const object_recognition_msgs::ObjectType& obj_type)
572  {
573  if (objs_info_.find(obj_type.key) == objs_info_.end() )
574  {
575  // Get object information from db using object's type
576  object_recognition_msgs::GetObjectInformation srv;
577  srv.request.type = obj_type;
578 
579  if (obj_info_srv_.call(srv))
580  {
581  ROS_DEBUG("Database information retrieved for object '%s'", obj_type.key.c_str());
582  objs_info_[obj_type.key] = srv.response.information;
583  }
584  else
585  {
586  ROS_ERROR("Call to object information service with key '%s' failed", obj_type.key.c_str());
587  throw;
588  }
589  }
590 
591  return objs_info_[obj_type.key];
592  }
593 
594  std_msgs::ColorRGBA getRandColor(float alpha = 1.0)
595  {
596  std_msgs::ColorRGBA color;
597  color.r = float(rand())/RAND_MAX;
598  color.g = float(rand())/RAND_MAX;
599  color.b = float(rand())/RAND_MAX;
600  color.a = alpha;
601  return color;
602  }
603 
609  {
610  public:
611  unsigned int id;
612  unsigned int countObjects() const { return objects.size(); }
613  const geometry_msgs::PoseStamped& getCentroid() const { return centroid; }
614 
615  void addObject(object_recognition_msgs::RecognizedObject object)
616  {
617  objects.push_back(object);
618 
619  // recalculate centroid
620  centroid = geometry_msgs::PoseStamped();
621  double confidence_acc = 0.0, roll_acc = 0.0, pitch_acc = 0.0, yaw_acc = 0.0;
622  for (const object_recognition_msgs::RecognizedObject& obj: objects)
623  {
624  centroid.header.stamp = ros::Time::now();
625  centroid.header.frame_id = obj.pose.header.frame_id; // TODO could do some checking
626 
627  centroid.pose.position.x += obj.pose.pose.pose.position.x * obj.confidence;
628  centroid.pose.position.y += obj.pose.pose.pose.position.y * obj.confidence;
629  centroid.pose.position.z += obj.pose.pose.pose.position.z * obj.confidence;
630  roll_acc += mtk::roll(obj.pose.pose.pose) * obj.confidence;
631  pitch_acc += mtk::pitch (obj.pose.pose.pose) * obj.confidence;
632  yaw_acc += mtk::yaw(obj.pose.pose.pose) * obj.confidence;
633 
634  confidence_acc += obj.confidence;
635  }
636 
637  centroid.pose.position.x /= confidence_acc;
638  centroid.pose.position.y /= confidence_acc;
639  centroid.pose.position.z /= confidence_acc;
640  centroid.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll_acc/confidence_acc,
641  pitch_acc/confidence_acc,
642  yaw_acc/confidence_acc);
643  confidence = confidence_acc/(double)objects.size();
644  }
645 
646  std::string getName() const
647  {
648  std::map<std::string, unsigned int> key_occurences;
649  for (const object_recognition_msgs::RecognizedObject& obj: objects)
650  key_occurences[obj.type.key]++;
651 
652  std::string commonest_key;
653  std::map<std::string, unsigned int>::iterator it;
654  for(it = key_occurences.begin(); it != key_occurences.end(); it++)
655  {
656  if (it->second > key_occurences[commonest_key])
657  commonest_key = it->first;
658  }
659  return commonest_key;
660  }
661 
662  object_recognition_msgs::ObjectType getType() const
663  {
664  std::string commonest_key = getName();
665  for (const object_recognition_msgs::RecognizedObject& obj: objects)
666  if (obj.type.key == commonest_key)
667  return obj.type;
668 
669  return object_recognition_msgs::ObjectType();
670  }
671 
672  private:
673  double confidence;
674  geometry_msgs::PoseStamped centroid;
675  std::vector<object_recognition_msgs::RecognizedObject> objects;
676  };
677 
678 };
679 
680 }; // namespace turtlebot_arm_object_manipulation
681 
682 int main(int argc, char** argv)
683 {
684  ros::init(argc, argv, "object_detection_action_server");
685 
687  ros::spin();
688 
689  return 0;
690 }
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
turtlebot_arm_object_manipulation::ObjectDetectionFeedback feedback_
boost::shared_ptr< const Goal > acceptNewGoal()
actionlib::SimpleActionServer< turtlebot_arm_object_manipulation::ObjectDetectionAction > od_as_
bool waitForServer(const ros::Duration &timeout=ros::Duration(0, 0)) const
static void pointMsgToTF(const geometry_msgs::Point &msg_v, Point &bt_v)
const object_recognition_msgs::ObjectInformation & getObjInfo(const object_recognition_msgs::ObjectType &obj_type)
void publish(const boost::shared_ptr< M > &message) const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void tableCb(const object_recognition_msgs::TableArray &msg)
TableDescriptor getTableParams(std::vector< geometry_msgs::Point > convex_hull)
int main(int argc, char **argv)
turtlebot_arm_object_manipulation::ObjectDetectionResult result_
void addTable(const std::vector< geometry_msgs::Pose > &table_poses, const std::vector< TableDescriptor > &table_params)
double pitch(const tf::Transform &tf)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
boost::shared_ptr< interactive_markers::InteractiveMarkerServer > server
bool call(MReq &req, MRes &res)
double yaw(const tf::Transform &tf)
void addCollisionObjects(const std::vector< moveit_msgs::CollisionObject > &collision_objects, const std::vector< moveit_msgs::ObjectColor > &object_colors=std::vector< moveit_msgs::ObjectColor >()) const
#define M_PI
std::string pose2str3D(const geometry_msgs::Pose &pose)
tf::Vector3 Point
turtlebot_arm_object_manipulation::ObjectDetectionGoalConstPtr goal_
double roll(const tf::Transform &tf)
#define ROS_WARN(...)
void setSucceeded(const Result &result=Result(), const std::string &text=std::string(""))
ROSCPP_DECL void spin(Spinner &spinner)
int addObjects(const std::vector< DetectionBin > &detection_bins)
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
std::string point2str3D(const geometry_msgs::Point &point)
std::string toString() const
TFSIMD_FORCE_INLINE const tfScalar & x() const
TFSIMD_FORCE_INLINE tfScalar dot(const Vector3 &v) const
void setPreempted(const Result &result=Result(), const std::string &text=std::string(""))
TFSIMD_FORCE_INLINE const tfScalar & z() const
bool waitForExistence(ros::Duration timeout=ros::Duration(-1))
#define ROS_INFO(...)
SimpleClientGoalState sendGoalAndWait(const Goal &goal, const ros::Duration &execute_timeout=ros::Duration(0, 0), const ros::Duration &preempt_timeout=ros::Duration(0, 0))
void registerPreemptCallback(boost::function< void()> cb)
TFSIMD_FORCE_INLINE const tfScalar & y() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
TFSIMD_FORCE_INLINE Vector3 & normalize()
void setAborted(const Result &result=Result(), const std::string &text=std::string(""))
bool transformPose(const std::string &in_frame, const std::string &out_frame, const geometry_msgs::Pose &in_pose, geometry_msgs::Pose &out_pose)
void transformPose(const std::string &target_frame, const geometry_msgs::PoseStamped &stamped_in, geometry_msgs::PoseStamped &stamped_out) const
std::vector< std::string > getKnownObjectNames(bool with_type=false)
void removeCollisionObjects(const std::vector< std::string > &object_ids) const
static geometry_msgs::Quaternion createQuaternionMsgFromRollPitchYaw(double roll, double pitch, double yaw)
static Time now()
ResultConstPtr getResult() const
void registerGoalCallback(boost::function< void()> cb)
ROSCPP_DECL void spinOnce()
std::map< std::string, object_recognition_msgs::ObjectInformation > objs_info_
#define ROS_ERROR(...)
moveit::planning_interface::PlanningSceneInterface planning_scene_interface_
actionlib::SimpleActionClient< object_recognition_msgs::ObjectRecognitionAction > ork_ac_
double distance3D(double ax, double ay, double az, double bx, double by, double bz)
#define ROS_DEBUG(...)


turtlebot_arm_object_manipulation
Author(s): Jorge Santos
autogenerated on Fri Feb 7 2020 03:56:40