moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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>
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
63namespace moveit
64{
65namespace semantic_world
66{
67
68SemanticWorld::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
83visualization_msgs::msg::MarkerArray
84SemanticWorld::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
188object_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
205std::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
228std::vector<geometry_msgs::msg::PoseStamped>
229SemanticWorld::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
248std::vector<geometry_msgs::msg::PoseStamped>
249SemanticWorld::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
324std::vector<geometry_msgs::msg::PoseStamped>
325SemanticWorld::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
413bool 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
485std::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
498void 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
511void 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
534shapes::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
591shapes::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...
Main namespace for MoveIt.
Definition exceptions.h:43
This namespace includes the central class for representing planning contexts.