moveit2
The MoveIt Motion Planning Framework for ROS 2.
semantic_world.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Sachin Chitta */
36 
37 #include <visualization_msgs/msg/marker_array.hpp>
38 #include <geometry_msgs/msg/quaternion.hpp>
39 // MoveIt
41 #include <geometric_shapes/shape_operations.h>
42 #include <moveit_msgs/msg/planning_scene.hpp>
43 #include <moveit/utils/logger.hpp>
44 // OpenCV
45 #include <opencv2/imgproc/imgproc.hpp>
46 #include <rclcpp/experimental/buffers/intra_process_buffer.hpp>
47 #include <rclcpp/logger.hpp>
48 #include <rclcpp/logging.hpp>
49 #include <rclcpp/node.hpp>
50 #include <rclcpp/publisher.hpp>
51 #include <rclcpp/version.h>
52 #if RCLCPP_VERSION_GTE(20, 0, 0)
53 #include <rclcpp/event_handler.hpp>
54 #else
55 #include <rclcpp/qos_event.hpp>
56 #endif
57 #include <rclcpp/subscription.hpp>
58 
59 // Eigen
60 #include <tf2_eigen/tf2_eigen.hpp>
61 #include <Eigen/Geometry>
62 
63 namespace moveit
64 {
65 namespace semantic_world
66 {
67 
68 SemanticWorld::SemanticWorld(const rclcpp::Node::SharedPtr& node,
69  const planning_scene::PlanningSceneConstPtr& planning_scene)
70  : planning_scene_(planning_scene), node_handle_(node), logger_(moveit::getLogger("moveit.ros.semantic_world"))
71 
72 {
73  table_subscriber_ = node_handle_->create_subscription<object_recognition_msgs::msg::TableArray>(
74  "table_array", rclcpp::SystemDefaultsQoS(),
75  [this](const object_recognition_msgs::msg::TableArray::ConstSharedPtr& msg) { return tableCallback(msg); });
76  visualization_publisher_ =
77  node_handle_->create_publisher<visualization_msgs::msg::MarkerArray>("visualize_place", 20);
78  collision_object_publisher_ =
79  node_handle_->create_publisher<moveit_msgs::msg::CollisionObject>("/collision_object", 20);
80  planning_scene_diff_publisher_ = node_handle_->create_publisher<moveit_msgs::msg::PlanningScene>("planning_scene", 1);
81 }
82 
83 visualization_msgs::msg::MarkerArray
84 SemanticWorld::getPlaceLocationsMarker(const std::vector<geometry_msgs::msg::PoseStamped>& poses) const
85 {
86  RCLCPP_DEBUG(logger_, "Visualizing: %d place poses", static_cast<int>(poses.size()));
87  visualization_msgs::msg::MarkerArray marker;
88  for (std::size_t i = 0; i < poses.size(); ++i)
89  {
90  visualization_msgs::msg::Marker m;
91  m.action = m.ADD;
92  m.type = m.SPHERE;
93  m.ns = "place_locations";
94  m.id = i;
95  m.pose = poses[i].pose;
96  m.header = poses[i].header;
97 
98  m.scale.x = 0.02;
99  m.scale.y = 0.02;
100  m.scale.z = 0.02;
101 
102  m.color.r = 1.0;
103  m.color.g = 0.0;
104  m.color.b = 0.0;
105  m.color.a = 1.0;
106  marker.markers.push_back(m);
107  }
108  return marker;
109 }
110 
112 {
113  moveit_msgs::msg::PlanningScene planning_scene;
114  planning_scene.is_diff = true;
115 
116  // Remove the existing tables
117  std::map<std::string, object_recognition_msgs::msg::Table>::iterator it;
118  for (it = current_tables_in_collision_world_.begin(); it != current_tables_in_collision_world_.end(); ++it)
119  {
120  moveit_msgs::msg::CollisionObject co;
121  co.id = it->first;
122  co.operation = moveit_msgs::msg::CollisionObject::REMOVE;
123  planning_scene.world.collision_objects.push_back(co);
124  // collision_object_publisher_.publish(co);
125  }
126 
127  planning_scene_diff_publisher_->publish(planning_scene);
128  planning_scene.world.collision_objects.clear();
129  current_tables_in_collision_world_.clear();
130  // Add the new tables
131  for (std::size_t i = 0; i < table_array_.tables.size(); ++i)
132  {
133  moveit_msgs::msg::CollisionObject co;
134  std::stringstream ss;
135  ss << "table_" << i;
136  co.id = ss.str();
137  current_tables_in_collision_world_[co.id] = table_array_.tables[i];
138  co.operation = moveit_msgs::msg::CollisionObject::ADD;
139 
140  const std::vector<geometry_msgs::msg::Point>& convex_hull = table_array_.tables[i].convex_hull;
141 
142  EigenSTL::vector_Vector3d vertices(convex_hull.size());
143  std::vector<unsigned int> triangles((vertices.size() - 2) * 3);
144  for (unsigned int j = 0; j < convex_hull.size(); ++j)
145  vertices[j] = Eigen::Vector3d(convex_hull[j].x, convex_hull[j].y, convex_hull[j].z);
146  for (unsigned int j = 1; j < triangles.size() - 1; ++j)
147  {
148  unsigned int i3 = j * 3;
149  triangles[i3++] = 0;
150  triangles[i3++] = j;
151  triangles[i3] = j + 1;
152  }
153 
154  shapes::Shape* table_shape = shapes::createMeshFromVertices(vertices, triangles);
155  if (!table_shape)
156  continue;
157 
158  shapes::Mesh* table_mesh = static_cast<shapes::Mesh*>(table_shape);
159  shapes::Mesh* table_mesh_solid = orientPlanarPolygon(*table_mesh);
160  if (!table_mesh_solid)
161  {
162  delete table_shape;
163  continue;
164  }
165 
166  shapes::ShapeMsg table_shape_msg;
167  if (!shapes::constructMsgFromShape(table_mesh_solid, table_shape_msg))
168  {
169  delete table_shape;
170  delete table_mesh_solid;
171  continue;
172  }
173 
174  const shape_msgs::msg::Mesh& table_shape_msg_mesh = boost::get<shape_msgs::msg::Mesh>(table_shape_msg);
175 
176  co.meshes.push_back(table_shape_msg_mesh);
177  co.mesh_poses.push_back(table_array_.tables[i].pose);
178  co.header = table_array_.tables[i].header;
179  planning_scene.world.collision_objects.push_back(co);
180  // collision_object_publisher_.publish(co);
181  delete table_shape;
182  delete table_mesh_solid;
183  }
184  planning_scene_diff_publisher_->publish(planning_scene);
185  return true;
186 }
187 
188 object_recognition_msgs::msg::TableArray SemanticWorld::getTablesInROI(double minx, double miny, double minz,
189  double maxx, double maxy, double maxz) const
190 {
191  object_recognition_msgs::msg::TableArray tables_in_roi;
192  std::map<std::string, object_recognition_msgs::msg::Table>::const_iterator it;
193  for (it = current_tables_in_collision_world_.begin(); it != current_tables_in_collision_world_.end(); ++it)
194  {
195  if (it->second.pose.position.x >= minx && it->second.pose.position.x <= maxx &&
196  it->second.pose.position.y >= miny && it->second.pose.position.y <= maxy &&
197  it->second.pose.position.z >= minz && it->second.pose.position.z <= maxz)
198  {
199  tables_in_roi.tables.push_back(it->second);
200  }
201  }
202  return tables_in_roi;
203 }
204 
205 std::vector<std::string> SemanticWorld::getTableNamesInROI(double minx, double miny, double minz, double maxx,
206  double maxy, double maxz) const
207 {
208  std::vector<std::string> result;
209  std::map<std::string, object_recognition_msgs::msg::Table>::const_iterator it;
210  for (it = current_tables_in_collision_world_.begin(); it != current_tables_in_collision_world_.end(); ++it)
211  {
212  if (it->second.pose.position.x >= minx && it->second.pose.position.x <= maxx &&
213  it->second.pose.position.y >= miny && it->second.pose.position.y <= maxy &&
214  it->second.pose.position.z >= minz && it->second.pose.position.z <= maxz)
215  {
216  result.push_back(it->first);
217  }
218  }
219  return result;
220 }
221 
223 {
224  table_array_.tables.clear();
225  current_tables_in_collision_world_.clear();
226 }
227 
228 std::vector<geometry_msgs::msg::PoseStamped>
229 SemanticWorld::generatePlacePoses(const std::string& table_name, const shapes::ShapeConstPtr& object_shape,
230  const geometry_msgs::msg::Quaternion& object_orientation, double resolution,
231  double delta_height, unsigned int num_heights) const
232 {
233  object_recognition_msgs::msg::Table chosen_table;
234  std::map<std::string, object_recognition_msgs::msg::Table>::const_iterator it =
235  current_tables_in_collision_world_.find(table_name);
236 
237  if (it != current_tables_in_collision_world_.end())
238  {
239  chosen_table = it->second;
240  return generatePlacePoses(chosen_table, object_shape, object_orientation, resolution, delta_height, num_heights);
241  }
242 
243  std::vector<geometry_msgs::msg::PoseStamped> place_poses;
244  RCLCPP_ERROR(logger_, "Did not find table %s to place on", table_name.c_str());
245  return place_poses;
246 }
247 
248 std::vector<geometry_msgs::msg::PoseStamped>
249 SemanticWorld::generatePlacePoses(const object_recognition_msgs::msg::Table& chosen_table,
250  const shapes::ShapeConstPtr& object_shape,
251  const geometry_msgs::msg::Quaternion& object_orientation, double resolution,
252  double delta_height, unsigned int num_heights) const
253 {
254  std::vector<geometry_msgs::msg::PoseStamped> place_poses;
255  if (object_shape->type != shapes::MESH && object_shape->type != shapes::SPHERE && object_shape->type != shapes::BOX &&
256  object_shape->type != shapes::CYLINDER && object_shape->type != shapes::CONE)
257  {
258  return place_poses;
259  }
260 
261  double x_min(std::numeric_limits<double>::max()), x_max(-std::numeric_limits<double>::max());
262  double y_min(std::numeric_limits<double>::max()), y_max(-std::numeric_limits<double>::max());
263  double z_min(std::numeric_limits<double>::max()), z_max(-std::numeric_limits<double>::max());
264 
265  Eigen::Quaterniond rotation(object_orientation.x, object_orientation.y, object_orientation.z, object_orientation.w);
266  Eigen::Isometry3d object_pose(rotation);
267  double min_distance_from_edge = 0;
268  double height_above_table = 0;
269 
270  if (object_shape->type == shapes::MESH)
271  {
272  const shapes::Mesh* mesh = static_cast<const shapes::Mesh*>(object_shape.get());
273 
274  for (std::size_t i = 0; i < mesh->vertex_count; ++i)
275  {
276  Eigen::Vector3d position(mesh->vertices[3 * i], mesh->vertices[3 * i + 1], mesh->vertices[3 * i + 2]);
277  position = object_pose * position;
278 
279  if (x_min > position.x())
280  x_min = position.x();
281  if (x_max < position.x())
282  x_max = position.x();
283  if (y_min > position.y())
284  y_min = position.y();
285  if (y_max < position.y())
286  y_max = position.y();
287  if (z_min > position.z())
288  z_min = position.z();
289  if (z_max < position.z())
290  z_max = position.z();
291  }
292  min_distance_from_edge = 0.5 * std::max<double>(fabs(x_max - x_min), fabs(y_max - y_min));
293  height_above_table = -z_min;
294  }
295  else if (object_shape->type == shapes::BOX) // assuming box is being kept down upright
296  {
297  const shapes::Box* box = static_cast<const shapes::Box*>(object_shape.get());
298  min_distance_from_edge = std::max<double>(fabs(box->size[0]), fabs(box->size[1])) / 2.0;
299  height_above_table = fabs(box->size[2]) / 2.0;
300  }
301  else if (object_shape->type == shapes::SPHERE)
302  {
303  const shapes::Sphere* sphere = static_cast<const shapes::Sphere*>(object_shape.get());
304  min_distance_from_edge = sphere->radius;
305  height_above_table = -sphere->radius;
306  }
307  else if (object_shape->type == shapes::CYLINDER) // assuming cylinder is being kept down upright
308  {
309  const shapes::Cylinder* cylinder = static_cast<const shapes::Cylinder*>(object_shape.get());
310  min_distance_from_edge = cylinder->radius;
311  height_above_table = cylinder->length / 2.0;
312  }
313  else if (object_shape->type == shapes::CONE) // assuming cone is being kept down upright
314  {
315  const shapes::Cone* cone = static_cast<const shapes::Cone*>(object_shape.get());
316  min_distance_from_edge = cone->radius;
317  height_above_table = cone->length / 2.0;
318  }
319 
320  return generatePlacePoses(chosen_table, resolution, height_above_table, delta_height, num_heights,
321  min_distance_from_edge);
322 }
323 
324 std::vector<geometry_msgs::msg::PoseStamped>
325 SemanticWorld::generatePlacePoses(const object_recognition_msgs::msg::Table& table, double resolution,
326  double height_above_table, double delta_height, unsigned int num_heights,
327  double min_distance_from_edge) const
328 {
329  std::vector<geometry_msgs::msg::PoseStamped> place_poses;
330  // Assumption that the table's normal is along the Z axis
331  if (table.convex_hull.empty())
332  return place_poses;
333  const int scale_factor = 100;
334  std::vector<cv::Point2f> table_contour;
335  float x_min = table.convex_hull[0].x, x_max = x_min, y_min = table.convex_hull[0].y, y_max = y_min;
336  for (std::size_t j = 1; j < table.convex_hull.size(); ++j)
337  {
338  if (table.convex_hull[j].x < x_min)
339  {
340  x_min = table.convex_hull[j].x;
341  }
342  else if (table.convex_hull[j].x > x_max)
343  {
344  x_max = table.convex_hull[j].x;
345  }
346  if (table.convex_hull[j].y < y_min)
347  {
348  y_min = table.convex_hull[j].y;
349  }
350  else if (table.convex_hull[j].y > y_max)
351  {
352  y_max = table.convex_hull[j].y;
353  }
354  }
355  for (const geometry_msgs::msg::Point& vertex : table.convex_hull)
356  table_contour.push_back(cv::Point((vertex.x - x_min) * scale_factor, (vertex.y - y_min) * scale_factor));
357 
358  double x_range = fabs(x_max - x_min);
359  double y_range = fabs(y_max - y_min);
360  int max_range = static_cast<int>(x_range) + 1;
361  if (max_range < static_cast<int>(y_range) + 1)
362  max_range = static_cast<int>(y_range) + 1;
363 
364  int image_scale = std::max<int>(max_range, 4);
365  cv::Mat src = cv::Mat::zeros(image_scale * scale_factor, image_scale * scale_factor, CV_8UC1);
366 
367  for (std::size_t j = 0; j < table.convex_hull.size(); ++j)
368  {
369  cv::line(src, table_contour[j], table_contour[(j + 1) % table.convex_hull.size()], cv::Scalar(255), 3, 8);
370  }
371 
372  unsigned int num_x = fabs(x_max - x_min) / resolution + 1;
373  unsigned int num_y = fabs(y_max - y_min) / resolution + 1;
374 
375  RCLCPP_DEBUG(logger_, "Num points for possible place operations: %d %d", num_x, num_y);
376 
377  std::vector<std::vector<cv::Point> > contours;
378  std::vector<cv::Vec4i> hierarchy;
379  cv::findContours(src, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
380 
381  for (std::size_t j = 0; j < num_x; ++j)
382  {
383  int point_x = j * resolution * scale_factor;
384  for (std::size_t k = 0; k < num_y; ++k)
385  {
386  for (std::size_t mm = 0; mm < num_heights; ++mm)
387  {
388  int point_y = k * resolution * scale_factor;
389  cv::Point2f point2f(point_x, point_y);
390  double result = cv::pointPolygonTest(contours[0], point2f, true);
391  if (static_cast<int>(result) >= static_cast<int>(min_distance_from_edge * scale_factor))
392  {
393  Eigen::Vector3d point(static_cast<double>(point_x) / scale_factor + x_min,
394  static_cast<double>(point_y) / scale_factor + y_min,
395  height_above_table + mm * delta_height);
396  Eigen::Isometry3d pose;
397  tf2::fromMsg(table.pose, pose);
398  point = pose * point;
399  geometry_msgs::msg::PoseStamped place_pose;
400  place_pose.pose.orientation.w = 1.0;
401  place_pose.pose.position.x = point.x();
402  place_pose.pose.position.y = point.y();
403  place_pose.pose.position.z = point.z();
404  place_pose.header = table.header;
405  place_poses.push_back(place_pose);
406  }
407  }
408  }
409  }
410  return place_poses;
411 }
412 
413 bool SemanticWorld::isInsideTableContour(const geometry_msgs::msg::Pose& pose,
414  const object_recognition_msgs::msg::Table& table,
415  double min_distance_from_edge, double min_vertical_offset) const
416 {
417  // Assumption that the table's normal is along the Z axis
418  if (table.convex_hull.empty())
419  return false;
420  float x_min = table.convex_hull[0].x, x_max = x_min, y_min = table.convex_hull[0].y, y_max = y_min;
421  for (std::size_t j = 1; j < table.convex_hull.size(); ++j)
422  {
423  if (table.convex_hull[j].x < x_min)
424  {
425  x_min = table.convex_hull[j].x;
426  }
427  else if (table.convex_hull[j].x > x_max)
428  {
429  x_max = table.convex_hull[j].x;
430  }
431  if (table.convex_hull[j].y < y_min)
432  {
433  y_min = table.convex_hull[j].y;
434  }
435  else if (table.convex_hull[j].y > y_max)
436  {
437  y_max = table.convex_hull[j].y;
438  }
439  }
440  const int scale_factor = 100;
441  std::vector<cv::Point2f> table_contour;
442  for (const geometry_msgs::msg::Point& vertex : table.convex_hull)
443  table_contour.push_back(cv::Point((vertex.x - x_min) * scale_factor, (vertex.y - y_min) * scale_factor));
444 
445  double x_range = fabs(x_max - x_min);
446  double y_range = fabs(y_max - y_min);
447  int max_range = static_cast<int>(x_range) + 1;
448  if (max_range < static_cast<int>(y_range) + 1)
449  max_range = static_cast<int>(y_range) + 1;
450 
451  int image_scale = std::max<int>(max_range, 4);
452  cv::Mat src = cv::Mat::zeros(image_scale * scale_factor, image_scale * scale_factor, CV_8UC1);
453 
454  for (std::size_t j = 0; j < table.convex_hull.size(); ++j)
455  {
456  cv::line(src, table_contour[j], table_contour[(j + 1) % table.convex_hull.size()], cv::Scalar(255), 3, 8);
457  }
458 
459  std::vector<std::vector<cv::Point> > contours;
460  std::vector<cv::Vec4i> hierarchy;
461  cv::findContours(src, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
462 
463  Eigen::Vector3d point(pose.position.x, pose.position.y, pose.position.z);
464  Eigen::Isometry3d pose_table;
465  tf2::fromMsg(table.pose, pose_table);
466 
467  // Point in table frame
468  point = pose_table.inverse() * point;
469  // Assuming Z axis points upwards for the table
470  if (point.z() < -fabs(min_vertical_offset))
471  {
472  RCLCPP_ERROR(logger_, "Object is not above table");
473  return false;
474  }
475 
476  int point_x = (point.x() - x_min) * scale_factor;
477  int point_y = (point.y() - y_min) * scale_factor;
478  cv::Point2f point2f(point_x, point_y);
479  double result = cv::pointPolygonTest(contours[0], point2f, true);
480  RCLCPP_DEBUG(logger_, "table distance: %f", result);
481 
482  return static_cast<int>(result) >= static_cast<int>(min_distance_from_edge * scale_factor);
483 }
484 
485 std::string SemanticWorld::findObjectTable(const geometry_msgs::msg::Pose& pose, double min_distance_from_edge,
486  double min_vertical_offset) const
487 {
488  std::map<std::string, object_recognition_msgs::msg::Table>::const_iterator it;
489  for (it = current_tables_in_collision_world_.begin(); it != current_tables_in_collision_world_.end(); ++it)
490  {
491  RCLCPP_DEBUG_STREAM(logger_, "Testing table: " << it->first);
492  if (isInsideTableContour(pose, it->second, min_distance_from_edge, min_vertical_offset))
493  return it->first;
494  }
495  return std::string();
496 }
497 
498 void SemanticWorld::tableCallback(const object_recognition_msgs::msg::TableArray::ConstSharedPtr& msg)
499 {
500  table_array_ = *msg;
501  RCLCPP_INFO(logger_, "Table callback with %d tables", static_cast<int>(table_array_.tables.size()));
502  transformTableArray(table_array_);
503  // Callback on an update
504  if (table_callback_)
505  {
506  RCLCPP_INFO(logger_, "Calling table callback");
507  table_callback_();
508  }
509 }
510 
511 void SemanticWorld::transformTableArray(object_recognition_msgs::msg::TableArray& table_array) const
512 {
513  for (object_recognition_msgs::msg::Table& table : table_array.tables)
514  {
515  std::string original_frame = table.header.frame_id;
516  if (table.convex_hull.empty())
517  continue;
518  RCLCPP_INFO_STREAM(logger_, "Original pose: " << table.pose.position.x << ',' << table.pose.position.y << ','
519  << table.pose.position.z);
520  std::string error_text;
521  const Eigen::Isometry3d& original_transform = planning_scene_->getFrameTransform(original_frame);
522  Eigen::Isometry3d original_pose;
523  tf2::fromMsg(table.pose, original_pose);
524  original_pose = original_transform * original_pose;
525  table.pose = tf2::toMsg(original_pose);
526  table.header.frame_id = planning_scene_->getTransforms().getTargetFrame();
527  RCLCPP_INFO_STREAM(logger_, "Successfully transformed table array from " << original_frame << "to "
528  << table.header.frame_id);
529  RCLCPP_INFO_STREAM(logger_, "Transformed pose: " << table.pose.position.x << ',' << table.pose.position.y << ','
530  << table.pose.position.z);
531  }
532 }
533 
534 shapes::Mesh* SemanticWorld::orientPlanarPolygon(const shapes::Mesh& polygon) const
535 {
536  if (polygon.vertex_count < 3 || polygon.triangle_count < 1)
537  return nullptr;
538  // first get the normal of the first triangle of the input polygon
539  Eigen::Vector3d vec1, vec2, vec3, normal;
540 
541  int v_idx1 = polygon.triangles[0];
542  int v_idx2 = polygon.triangles[1];
543  int v_idx3 = polygon.triangles[2];
544  vec1 =
545  Eigen::Vector3d(polygon.vertices[v_idx1 * 3], polygon.vertices[v_idx1 * 3 + 1], polygon.vertices[v_idx1 * 3 + 2]);
546  vec2 =
547  Eigen::Vector3d(polygon.vertices[v_idx2 * 3], polygon.vertices[v_idx2 * 3 + 1], polygon.vertices[v_idx2 * 3 + 2]);
548  vec3 =
549  Eigen::Vector3d(polygon.vertices[v_idx3 * 3], polygon.vertices[v_idx3 * 3 + 1], polygon.vertices[v_idx3 * 3 + 2]);
550  vec2 -= vec1;
551  vec3 -= vec1;
552  normal = vec3.cross(vec2);
553 
554  if (normal[2] < 0.0)
555  normal *= -1.0;
556 
557  normal.normalize();
558 
559  shapes::Mesh* solid = new shapes::Mesh(polygon.vertex_count, polygon.triangle_count); // + polygon.vertex_count * 2);
560  solid->type = shapes::MESH;
561 
562  // copy the first set of vertices
563  memcpy(solid->vertices, polygon.vertices, polygon.vertex_count * 3 * sizeof(double));
564  // copy the first set of triangles
565  memcpy(solid->triangles, polygon.triangles, polygon.triangle_count * 3 * sizeof(unsigned int));
566 
567  for (unsigned t_idx = 0; t_idx < polygon.triangle_count; ++t_idx)
568  {
569  int v_idx1 = polygon.triangles[t_idx * 3];
570  int v_idx2 = polygon.triangles[t_idx * 3 + 1];
571  int v_idx3 = polygon.triangles[t_idx * 3 + 2];
572 
573  vec1 = Eigen::Vector3d(polygon.vertices[v_idx1 * 3], polygon.vertices[v_idx1 * 3 + 1],
574  polygon.vertices[v_idx1 * 3 + 2]);
575  vec2 = Eigen::Vector3d(polygon.vertices[v_idx2 * 3], polygon.vertices[v_idx2 * 3 + 1],
576  polygon.vertices[v_idx2 * 3 + 2]);
577  vec3 = Eigen::Vector3d(polygon.vertices[v_idx3 * 3], polygon.vertices[v_idx3 * 3 + 1],
578  polygon.vertices[v_idx3 * 3 + 2]);
579 
580  vec2 -= vec1;
581  vec3 -= vec1;
582 
583  Eigen::Vector3d triangle_normal = vec2.cross(vec1);
584 
585  if (triangle_normal.dot(normal) < 0.0)
586  std::swap(solid->triangles[t_idx * 3 + 1], solid->triangles[t_idx * 3 + 2]);
587  }
588  return solid;
589 }
590 
591 shapes::Mesh* SemanticWorld::createSolidMeshFromPlanarPolygon(const shapes::Mesh& polygon, double thickness) const
592 {
593  if (polygon.vertex_count < 3 || polygon.triangle_count < 1 || thickness <= 0)
594  return nullptr;
595  // first get the normal of the first triangle of the input polygon
596  Eigen::Vector3d vec1, vec2, vec3, normal;
597 
598  int v_idx1 = polygon.triangles[0];
599  int v_idx2 = polygon.triangles[1];
600  int v_idx3 = polygon.triangles[2];
601  vec1 =
602  Eigen::Vector3d(polygon.vertices[v_idx1 * 3], polygon.vertices[v_idx1 * 3 + 1], polygon.vertices[v_idx1 * 3 + 2]);
603  vec2 =
604  Eigen::Vector3d(polygon.vertices[v_idx2 * 3], polygon.vertices[v_idx2 * 3 + 1], polygon.vertices[v_idx2 * 3 + 2]);
605  vec3 =
606  Eigen::Vector3d(polygon.vertices[v_idx3 * 3], polygon.vertices[v_idx3 * 3 + 1], polygon.vertices[v_idx3 * 3 + 2]);
607  vec2 -= vec1;
608  vec3 -= vec1;
609  normal = vec3.cross(vec2);
610 
611  if (normal[2] < 0.0)
612  normal *= -1.0;
613 
614  normal.normalize();
615 
616  // shapes::Mesh* solid = new shapes::Mesh(polygon.vertex_count, polygon.triangle_count);// + polygon.vertex_count *
617  // 2);
618 
619  shapes::Mesh* solid =
620  new shapes::Mesh(polygon.vertex_count * 2, polygon.triangle_count * 2); // + polygon.vertex_count * 2);
621  solid->type = shapes::MESH;
622 
623  // copy the first set of vertices
624  memcpy(solid->vertices, polygon.vertices, polygon.vertex_count * 3 * sizeof(double));
625  // copy the first set of triangles
626  memcpy(solid->triangles, polygon.triangles, polygon.triangle_count * 3 * sizeof(unsigned int));
627 
628  for (unsigned t_idx = 0; t_idx < polygon.triangle_count; ++t_idx)
629  {
630  solid->triangles[(t_idx + polygon.triangle_count) * 3 + 0] = solid->triangles[t_idx * 3 + 0] + polygon.vertex_count;
631  solid->triangles[(t_idx + polygon.triangle_count) * 3 + 1] = solid->triangles[t_idx * 3 + 1] + polygon.vertex_count;
632  solid->triangles[(t_idx + polygon.triangle_count) * 3 + 2] = solid->triangles[t_idx * 3 + 2] + polygon.vertex_count;
633 
634  int v_idx1 = polygon.triangles[t_idx * 3];
635  int v_idx2 = polygon.triangles[t_idx * 3 + 1];
636  int v_idx3 = polygon.triangles[t_idx * 3 + 2];
637 
638  vec1 = Eigen::Vector3d(polygon.vertices[v_idx1 * 3], polygon.vertices[v_idx1 * 3 + 1],
639  polygon.vertices[v_idx1 * 3 + 2]);
640  vec2 = Eigen::Vector3d(polygon.vertices[v_idx2 * 3], polygon.vertices[v_idx2 * 3 + 1],
641  polygon.vertices[v_idx2 * 3 + 2]);
642  vec3 = Eigen::Vector3d(polygon.vertices[v_idx3 * 3], polygon.vertices[v_idx3 * 3 + 1],
643  polygon.vertices[v_idx3 * 3 + 2]);
644 
645  vec2 -= vec1;
646  vec3 -= vec1;
647 
648  Eigen::Vector3d triangle_normal = vec2.cross(vec1);
649 
650  if (triangle_normal.dot(normal) < 0.0)
651  {
652  std::swap(solid->triangles[t_idx * 3 + 1], solid->triangles[t_idx * 3 + 2]);
653  }
654  else
655  {
656  std::swap(solid->triangles[(t_idx + polygon.triangle_count) * 3 + 1],
657  solid->triangles[(t_idx + polygon.triangle_count) * 3 + 2]);
658  }
659  }
660 
661  for (unsigned v_idx = 0; v_idx < polygon.vertex_count; ++v_idx)
662  {
663  solid->vertices[(v_idx + polygon.vertex_count) * 3 + 0] = solid->vertices[v_idx * 3 + 0] - thickness * normal[0];
664  solid->vertices[(v_idx + polygon.vertex_count) * 3 + 1] = solid->vertices[v_idx * 3 + 1] - thickness * normal[1];
665  solid->vertices[(v_idx + polygon.vertex_count) * 3 + 2] = solid->vertices[v_idx * 3 + 2] - thickness * normal[2];
666  }
667 
668  return solid;
669 }
670 } // namespace semantic_world
671 } // namespace moveit
std::vector< std::string > getTableNamesInROI(double minx, double miny, double minz, double maxx, double maxy, double maxz) const
Get all the tables within a region of interest.
object_recognition_msgs::msg::TableArray getTablesInROI(double minx, double miny, double minz, double maxx, double maxy, double maxz) const
Get all the tables within a region of interest.
visualization_msgs::msg::MarkerArray getPlaceLocationsMarker(const std::vector< geometry_msgs::msg::PoseStamped > &poses) const
std::string findObjectTable(const geometry_msgs::msg::Pose &pose, double min_distance_from_edge=0.0, double min_vertical_offset=0.0) const
std::vector< geometry_msgs::msg::PoseStamped > generatePlacePoses(const std::string &table_name, const shapes::ShapeConstPtr &object_shape, const geometry_msgs::msg::Quaternion &object_orientation, double resolution, double delta_height=0.01, unsigned int num_heights=2) const
Generate possible place poses on the table for a given object. This chooses appropriate values for mi...
bool isInsideTableContour(const geometry_msgs::msg::Pose &pose, const object_recognition_msgs::msg::Table &table, double min_distance_from_edge=0.0, double min_vertical_offset=0.0) const
SemanticWorld(const rclcpp::Node::SharedPtr &node, const planning_scene::PlanningSceneConstPtr &planning_scene)
A (simple) semantic world representation for pick and place and other tasks. Currently this is used o...
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.hpp:89
Main namespace for MoveIt.
Definition: exceptions.hpp:43
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
This namespace includes the central class for representing planning contexts.