moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
test_planning_scene.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2011, 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: Ioan Sucan */
36
37#include <cstdint>
38#include <gtest/gtest.h>
43#include <urdf_parser/urdf_parser.h>
44#include <fstream>
45#include <sstream>
46#include <string>
47#include <tf2_eigen/tf2_eigen.hpp>
48#include <octomap_msgs/conversions.h>
49#include <octomap/octomap.h>
50
53
54// Test not setting the object's pose should use the shape pose as the object pose
55TEST(PlanningScene, TestOneShapeObjectPose)
56{
57 urdf::ModelInterfaceSharedPtr urdf_model = moveit::core::loadModelInterface("pr2");
58 srdf::ModelSharedPtr srdf_model = std::make_shared<srdf::Model>();
59 planning_scene::PlanningScene ps(urdf_model, srdf_model);
60
61 const std::string object_name = "object";
62 const Eigen::Isometry3d expected_transfrom = Eigen::Isometry3d::Identity() * Eigen::Translation3d(0.5, -0.25, 0.0);
63
64 moveit_msgs::msg::CollisionObject co;
65 co.header.frame_id = "base_footprint";
66 co.id = object_name;
67 co.operation = moveit_msgs::msg::CollisionObject::ADD;
68 co.primitives.push_back([] {
69 shape_msgs::msg::SolidPrimitive primitive;
70 primitive.type = shape_msgs::msg::SolidPrimitive::CYLINDER;
71 primitive.dimensions = { 0.25, 0.02 };
72 return primitive;
73 }());
74 co.primitive_poses.push_back(tf2::toMsg(expected_transfrom));
75
77
78 EXPECT_TRUE(expected_transfrom.isApprox(ps.getFrameTransform(object_name)));
79}
80
81TEST(PlanningScene, LoadRestore)
82{
83 urdf::ModelInterfaceSharedPtr urdf_model = moveit::core::loadModelInterface("pr2");
84 srdf::ModelSharedPtr srdf_model = std::make_shared<srdf::Model>();
85 planning_scene::PlanningScene ps(urdf_model, srdf_model);
86 moveit_msgs::msg::PlanningScene ps_msg;
87 ps.getPlanningSceneMsg(ps_msg);
88 EXPECT_EQ(ps.getName(), ps_msg.name);
89 EXPECT_EQ(ps.getRobotModel()->getName(), ps_msg.robot_model_name);
90 ps.setPlanningSceneMsg(ps_msg);
91 EXPECT_EQ(ps.getName(), ps_msg.name);
92 EXPECT_EQ(ps.getRobotModel()->getName(), ps_msg.robot_model_name);
93}
94
95TEST(PlanningScene, LoadOctomap)
96{
97 urdf::ModelInterfaceSharedPtr urdf_model = moveit::core::loadModelInterface("pr2");
98 srdf::ModelSharedPtr srdf_model(new srdf::Model());
99 planning_scene::PlanningScene ps(urdf_model, srdf_model);
100
101 { // check octomap before doing any operations on it
102 octomap_msgs::msg::OctomapWithPose msg;
103 ps.getOctomapMsg(msg);
104 EXPECT_TRUE(msg.octomap.id.empty());
105 EXPECT_TRUE(msg.octomap.data.empty());
106 }
107
108 { // fill PlanningScene's octomap
109 octomap::OcTree octomap(0.1);
110 octomap::point3d origin(0, 0, 0);
111 octomap::point3d end(0, 1, 2);
112 octomap.insertRay(origin, end);
113
114 // populate PlanningScene with octomap
115 moveit_msgs::msg::PlanningScene msg;
116 msg.is_diff = true;
117 octomap_msgs::fullMapToMsg(octomap, msg.world.octomap.octomap);
119
120 // validate octomap message
121 octomap_msgs::msg::OctomapWithPose octomap_msg;
122 ps.getOctomapMsg(octomap_msg);
123 EXPECT_EQ(octomap_msg.octomap.id, "OcTree");
124 EXPECT_EQ(octomap_msg.octomap.data.size(), msg.world.octomap.octomap.data.size());
125 }
126
127 { // verify that a PlanningScene msg with an empty octomap id does not modify the octomap
128 // create planning scene
129 moveit_msgs::msg::PlanningScene msg;
130 msg.is_diff = true;
132
133 octomap_msgs::msg::OctomapWithPose octomap_msg;
134 ps.getOctomapMsg(octomap_msg);
135 EXPECT_EQ(octomap_msg.octomap.id, "OcTree");
136 EXPECT_FALSE(octomap_msg.octomap.data.empty());
137 }
138
139 { // check that a non-empty octomap id, but empty octomap will clear the octomap
140 moveit_msgs::msg::PlanningScene msg;
141 msg.is_diff = true;
142 msg.world.octomap.octomap.id = "xxx";
144 EXPECT_FALSE(static_cast<bool>(ps.getWorld()->getObject(planning_scene::PlanningScene::OCTOMAP_NS)));
145 }
146}
147
148TEST(PlanningScene, LoadRestoreDiff)
149{
150 urdf::ModelInterfaceSharedPtr urdf_model = moveit::core::loadModelInterface("pr2");
151 srdf::ModelSharedPtr srdf_model = std::make_shared<srdf::Model>();
152 auto ps = std::make_shared<planning_scene::PlanningScene>(urdf_model, srdf_model);
153
154 collision_detection::World& world = *ps->getWorldNonConst();
155
156 /* add one object to ps's world */
157 Eigen::Isometry3d id = Eigen::Isometry3d::Identity();
158 world.addToObject("sphere", std::make_shared<const shapes::Sphere>(0.4), id);
159
160 /* ps can be written to and set from message */
161 moveit_msgs::msg::PlanningScene ps_msg;
162 ps_msg.robot_state.is_diff = true;
163 EXPECT_TRUE(moveit::core::isEmpty(ps_msg));
164 ps->getPlanningSceneMsg(ps_msg);
165 ps->setPlanningSceneMsg(ps_msg);
166 EXPECT_EQ(ps_msg.world.collision_objects.size(), 1u);
167 EXPECT_EQ(ps_msg.world.collision_objects[0].id, "sphere");
168 EXPECT_TRUE(world.hasObject("sphere"));
169
170 /* test diff scene on top of ps */
171 planning_scene::PlanningScenePtr next = ps->diff();
172 /* world is inherited from ps */
173 EXPECT_TRUE(next->getWorld()->hasObject("sphere"));
174
175 /* object in overlay is only added in overlay */
176 next->getWorldNonConst()->addToObject("sphere_in_next_only", std::make_shared<const shapes::Sphere>(0.5), id);
177 EXPECT_EQ(next->getWorld()->size(), 2u);
178 EXPECT_EQ(ps->getWorld()->size(), 1u);
179
180 /* the worlds used for collision detection contain one and two objects, respectively */
181 EXPECT_EQ(ps->getCollisionEnv()->getWorld()->size(), 1u);
182 EXPECT_EQ(ps->getCollisionEnvUnpadded()->getWorld()->size(), 1u);
183
184 EXPECT_EQ(next->getCollisionEnv()->getWorld()->size(), 2u);
185 EXPECT_EQ(next->getCollisionEnvUnpadded()->getWorld()->size(), 2u);
186
187 /* maintained diff contains only overlay object */
188 next->getPlanningSceneDiffMsg(ps_msg);
189 EXPECT_EQ(ps_msg.world.collision_objects.size(), 1u);
190
191 /* copy ps to next and apply diff */
192 next->decoupleParent();
193 moveit_msgs::msg::PlanningScene ps_msg2;
194
195 /* diff is empty now */
196 next->getPlanningSceneDiffMsg(ps_msg2);
197 EXPECT_EQ(ps_msg2.world.collision_objects.size(), 0u);
198
199 /* next's world contains both objects */
200 next->getPlanningSceneMsg(ps_msg);
201 EXPECT_EQ(ps_msg.world.collision_objects.size(), 2u);
202 ps->setPlanningSceneMsg(ps_msg);
203 EXPECT_EQ(ps->getWorld()->size(), 2u);
204 EXPECT_EQ(ps->getCollisionEnv()->getWorld()->size(), 2u);
205 EXPECT_EQ(ps->getCollisionEnvUnpadded()->getWorld()->size(), 2u);
206}
207
208TEST(PlanningScene, MakeAttachedDiff)
209{
210 urdf::ModelInterfaceSharedPtr urdf_model = moveit::core::loadModelInterface("pr2");
211 srdf::ModelSharedPtr srdf_model = std::make_shared<srdf::Model>();
212 auto ps = std::make_shared<planning_scene::PlanningScene>(urdf_model, srdf_model);
213
214 /* add a single object to ps's world */
215 collision_detection::World& world = *ps->getWorldNonConst();
216 Eigen::Isometry3d id = Eigen::Isometry3d::Identity();
217 world.addToObject("sphere", std::make_shared<const shapes::Sphere>(0.4), id);
218
219 /* attach object in diff */
220 planning_scene::PlanningScenePtr attached_object_diff_scene = ps->diff();
221
222 moveit_msgs::msg::AttachedCollisionObject att_obj;
223 att_obj.link_name = "r_wrist_roll_link";
224 att_obj.object.operation = moveit_msgs::msg::CollisionObject::ADD;
225 att_obj.object.id = "sphere";
226 attached_object_diff_scene->processAttachedCollisionObjectMsg(att_obj);
227
228 /* object is not in world anymore */
229 EXPECT_EQ(attached_object_diff_scene->getWorld()->size(), 0u);
230 /* it became part of the robot state though */
231 EXPECT_TRUE(attached_object_diff_scene->getCurrentState().hasAttachedBody("sphere"));
232
235 attached_object_diff_scene->checkCollision(req, res);
236 ps->checkCollision(req, res);
237}
238
239TEST(PlanningScene, isStateValid)
240{
241 moveit::core::RobotModelPtr robot_model = moveit::core::loadTestingRobotModel("pr2");
242 auto ps = std::make_shared<planning_scene::PlanningScene>(robot_model->getURDF(), robot_model->getSRDF());
243 moveit::core::RobotState current_state = ps->getCurrentState();
244 if (ps->isStateColliding(current_state, "left_arm"))
245 {
246 EXPECT_FALSE(ps->isStateValid(current_state, "left_arm"));
247 }
248}
249
250TEST(PlanningScene, loadGoodSceneGeometryNewFormat)
251{
252 moveit::core::RobotModelPtr robot_model = moveit::core::loadTestingRobotModel("pr2");
253 auto ps = std::make_shared<planning_scene::PlanningScene>(robot_model->getURDF(), robot_model->getSRDF());
254
255 std::istringstream good_scene_geometry;
256 good_scene_geometry.str("foobar_scene\n"
257 "* foo\n"
258 "0 0 0\n"
259 "0 0 0 1\n"
260 "1\n"
261 "box\n"
262 "2.58 1.36 0.31\n"
263 "1.49257 1.00222 0.170051\n"
264 "0 0 4.16377e-05 1\n"
265 "0 0 1 0.3\n"
266 "0\n"
267 "* bar\n"
268 "0 0 0\n"
269 "0 0 0 1\n"
270 "1\n"
271 "cylinder\n"
272 "0.02 0.0001\n"
273 "0.453709 0.499136 0.355051\n"
274 "0 0 4.16377e-05 1\n"
275 "1 0 0 1\n"
276 "0\n"
277 ".\n");
278 EXPECT_TRUE(ps->loadGeometryFromStream(good_scene_geometry));
279 EXPECT_EQ(ps->getName(), "foobar_scene");
280 EXPECT_TRUE(ps->getWorld()->hasObject("foo"));
281 EXPECT_TRUE(ps->getWorld()->hasObject("bar"));
282 EXPECT_FALSE(ps->getWorld()->hasObject("baz")); // Sanity check.
283}
284
285TEST(PlanningScene, loadGoodSceneGeometryOldFormat)
286{
287 moveit::core::RobotModelPtr robot_model = moveit::core::loadTestingRobotModel("pr2");
288 auto ps = std::make_shared<planning_scene::PlanningScene>(robot_model->getURDF(), robot_model->getSRDF());
289
290 std::istringstream good_scene_geometry;
291 good_scene_geometry.str("foobar_scene\n"
292 "* foo\n"
293 "2\n"
294 "box\n"
295 ".77 0.39 0.05\n"
296 "0 0 0.025\n"
297 "0 0 0 1\n"
298 "0.82 0.75 0.60 1\n"
299 "box\n"
300 ".77 0.39 0.05\n"
301 "0 0 1.445\n"
302 "0 0 0 1\n"
303 "0.82 0.75 0.60 1\n"
304 ".\n");
305 EXPECT_TRUE(ps->loadGeometryFromStream(good_scene_geometry));
306 EXPECT_EQ(ps->getName(), "foobar_scene");
307 EXPECT_TRUE(ps->getWorld()->hasObject("foo"));
308 EXPECT_FALSE(ps->getWorld()->hasObject("baz")); // Sanity check.
309}
310
311TEST(PlanningScene, loadBadSceneGeometry)
312{
313 moveit::core::RobotModelPtr robot_model = moveit::core::loadTestingRobotModel("pr2");
314 auto ps = std::make_shared<planning_scene::PlanningScene>(robot_model->getURDF(), robot_model->getSRDF());
315 std::istringstream empty_scene_geometry;
316
317 // This should fail since there is no planning scene name and no end of geometry marker.
318 EXPECT_FALSE(ps->loadGeometryFromStream(empty_scene_geometry));
319
320 std::istringstream malformed_scene_geometry;
321 malformed_scene_geometry.str("malformed_scene_geometry\n"
322 "* foo\n"
323 "0 0 0\n"
324 "0 0 0 1\n"
325 "1\n"
326 "box\n"
327 "2.58 1.36\n" /* Only two tokens; should be 3 */
328 "1.49257 1.00222 0.170051\n"
329 "0 0 4.16377e-05 1\n"
330 "0 0 1 0.3\n"
331 ".\n");
332 EXPECT_FALSE(ps->loadGeometryFromStream(malformed_scene_geometry));
333}
334
335// Test the setting of a new collision detector type. For now, only FCL is available in MoveIt2.
336// TODO(andyz): switch to a different type when one becomes available.
337TEST(PlanningScene, switchCollisionDetectorType)
338{
339 moveit::core::RobotModelPtr robot_model = moveit::core::loadTestingRobotModel("pr2");
340 auto ps = std::make_shared<planning_scene::PlanningScene>(robot_model->getURDF(), robot_model->getSRDF());
341 moveit::core::RobotState current_state = ps->getCurrentState();
342 if (ps->isStateColliding(current_state, "left_arm"))
343 {
344 EXPECT_FALSE(ps->isStateValid(current_state, "left_arm"));
345 }
346
347 ps->allocateCollisionDetector(collision_detection::CollisionDetectorAllocatorFCL::create());
348 if (ps->isStateColliding(current_state, "left_arm"))
349 {
350 EXPECT_FALSE(ps->isStateValid(current_state, "left_arm"));
351 }
352}
353
354TEST(PlanningScene, FailRetrievingNonExistentObject)
355{
356 moveit::core::RobotModelPtr robot_model = moveit::core::loadTestingRobotModel("pr2");
357 planning_scene::PlanningScene ps{ robot_model };
358 moveit_msgs::msg::CollisionObject obj;
359 EXPECT_FALSE(ps.getCollisionObjectMsg(obj, "non_existent_object"));
360}
361
362class CollisionDetectorTests : public testing::TestWithParam<const char*>
363{
364};
366{
367 const std::string plugin_name = GetParam();
368 SCOPED_TRACE(plugin_name);
369
370 urdf::ModelInterfaceSharedPtr urdf_model = moveit::core::loadModelInterface("pr2");
371 srdf::ModelSharedPtr srdf_model = std::make_shared<srdf::Model>();
372 // create parent scene
373 planning_scene::PlanningScenePtr parent = std::make_shared<planning_scene::PlanningScene>(urdf_model, srdf_model);
374
376 if (!loader.activate(plugin_name, parent))
377 {
378#if defined(GTEST_SKIP_)
379 GTEST_SKIP_("Failed to load collision plugin");
380#else
381 return;
382#endif
383 }
384
385 // create child scene
386 planning_scene::PlanningScenePtr child = parent->diff();
387
388 // create collision request variables
391 moveit::core::RobotState* state = new moveit::core::RobotState(child->getRobotModel());
392 state->setToDefaultValues();
393 state->update();
394
395 // there should be no collision with the environment
396 res.clear();
397 parent->getCollisionEnv()->checkRobotCollision(req, res, *state, parent->getAllowedCollisionMatrix());
398 EXPECT_FALSE(res.collision);
399 res.clear();
400 child->getCollisionEnv()->checkRobotCollision(req, res, *state, child->getAllowedCollisionMatrix());
401 EXPECT_FALSE(res.collision);
402
403 // create message to add a collision object at the world origin
404 moveit_msgs::msg::PlanningScene ps_msg;
405 ps_msg.is_diff = false;
406 moveit_msgs::msg::CollisionObject co;
407 co.header.frame_id = "base_link";
408 co.operation = moveit_msgs::msg::CollisionObject::ADD;
409 co.id = "box";
410 co.pose.orientation.w = 1.0;
411 {
412 shape_msgs::msg::SolidPrimitive sp;
413 sp.type = shape_msgs::msg::SolidPrimitive::BOX;
414 sp.dimensions = { 1., 1., 1. };
415 co.primitives.push_back(sp);
416 geometry_msgs::msg::Pose sp_pose;
417 sp_pose.orientation.w = 1.0;
418 co.primitive_poses.push_back(sp_pose);
419 }
420 ps_msg.world.collision_objects.push_back(co);
421
422 // add object to the parent planning scene
423 parent->usePlanningSceneMsg(ps_msg);
424
425 // the parent scene should be in collision
426 res.clear();
427 parent->getCollisionEnv()->checkRobotCollision(req, res, *state, parent->getAllowedCollisionMatrix());
428 EXPECT_TRUE(res.collision);
429
430 // the child scene was not updated yet, so no collision
431 res.clear();
432 child->getCollisionEnv()->checkRobotCollision(req, res, *state, child->getAllowedCollisionMatrix());
433 EXPECT_FALSE(res.collision);
434
435 // update the child scene
436 child->clearDiffs();
437
438 // child and parent scene should be in collision
439 res.clear();
440 parent->getCollisionEnv()->checkRobotCollision(req, res, *state, parent->getAllowedCollisionMatrix());
441 EXPECT_TRUE(res.collision);
442 res.clear();
443 child->getCollisionEnv()->checkRobotCollision(req, res, *state, child->getAllowedCollisionMatrix());
444 EXPECT_TRUE(res.collision);
445
446 child.reset();
447 parent.reset();
448}
449
450// Returns a planning scene diff message
451moveit_msgs::msg::PlanningScene createPlanningSceneDiff(const planning_scene::PlanningScene& ps,
452 const std::string& object_name, const int8_t operation,
453 const bool attach_object = false,
454 const bool create_object = true)
455{
456 // Helper function to create an object for RobotStateDiffBug
457 auto add_object = [](const std::string& object_name, const int8_t operation) {
458 moveit_msgs::msg::CollisionObject co;
459 co.header.frame_id = "panda_link0";
460 co.id = object_name;
461 co.operation = operation;
462 co.primitives.push_back([] {
463 shape_msgs::msg::SolidPrimitive primitive;
464 primitive.type = shape_msgs::msg::SolidPrimitive::SPHERE;
465 primitive.dimensions.push_back(1.0);
466 return primitive;
467 }());
468 co.primitive_poses.push_back([] {
469 geometry_msgs::msg::Pose pose;
470 pose.orientation.w = 1.0;
471 return pose;
472 }());
473 co.pose = co.primitive_poses[0];
474 return co;
475 };
476 // Helper function to create an attached object for RobotStateDiffBug
477 auto add_attached_object = [](const std::string& object_name, const int8_t operation) {
478 moveit_msgs::msg::AttachedCollisionObject aco;
479 aco.object.operation = operation;
480 aco.object.id = object_name;
481 aco.link_name = "panda_link0";
482 return aco;
483 };
484
485 auto new_ps = ps.diff();
486 if ((operation == moveit_msgs::msg::CollisionObject::REMOVE && !attach_object) ||
487 (operation == moveit_msgs::msg::CollisionObject::ADD && create_object))
488 new_ps->processCollisionObjectMsg(add_object(object_name, operation));
489 if (attach_object)
490 new_ps->processAttachedCollisionObjectMsg(add_attached_object(object_name, operation));
491 moveit_msgs::msg::PlanningScene scene_msg;
492 new_ps->getPlanningSceneDiffMsg(scene_msg);
493 return scene_msg;
494}
495
496// Returns collision objects names sorted alphabetically
498{
499 std::vector<moveit_msgs::msg::CollisionObject> collision_objects;
500 ps.getCollisionObjectMsgs(collision_objects);
501 std::set<std::string> collision_objects_names;
502 for (const auto& collision_object : collision_objects)
503 collision_objects_names.emplace(collision_object.id);
504 return collision_objects_names;
505}
506
507// Returns attached collision objects names sorted alphabetically
509{
510 std::vector<moveit_msgs::msg::AttachedCollisionObject> collision_objects;
511 ps.getAttachedCollisionObjectMsgs(collision_objects);
512 std::set<std::string> collision_objects_names;
513 for (const auto& collision_object : collision_objects)
514 collision_objects_names.emplace(collision_object.object.id);
515 return collision_objects_names;
516}
517
518TEST(PlanningScene, RobotStateDiffBug)
519{
520 auto urdf_model = moveit::core::loadModelInterface("panda");
521 auto srdf_model = std::make_shared<srdf::Model>();
522 auto ps = std::make_shared<planning_scene::PlanningScene>(urdf_model, srdf_model);
523
524 // Adding collision objects incrementally
525 {
526 const auto ps1 = createPlanningSceneDiff(*ps, "object1", moveit_msgs::msg::CollisionObject::ADD);
527 const auto ps2 = createPlanningSceneDiff(*ps, "object2", moveit_msgs::msg::CollisionObject::ADD);
528
529 ps->usePlanningSceneMsg(ps1);
530 ps->usePlanningSceneMsg(ps2);
531
532 EXPECT_EQ(getCollisionObjectsNames(*ps), (std::set<std::string>{ "object1", "object2" }));
533 }
534
535 // Removing a collision object
536 {
537 const auto ps1 = createPlanningSceneDiff(*ps, "object2", moveit_msgs::msg::CollisionObject::REMOVE);
538
539 ps->usePlanningSceneMsg(ps1);
540 EXPECT_EQ(getCollisionObjectsNames(*ps), (std::set<std::string>{ "object1" }));
541 }
542
543 // Adding attached collision objects incrementally
544 ps = std::make_shared<planning_scene::PlanningScene>(urdf_model, srdf_model);
545 {
546 const auto ps1 = createPlanningSceneDiff(*ps, "object1", moveit_msgs::msg::CollisionObject::ADD, true);
547 const auto ps2 = createPlanningSceneDiff(*ps, "object2", moveit_msgs::msg::CollisionObject::ADD, true);
548
549 ps->usePlanningSceneMsg(ps1);
550 ps->usePlanningSceneMsg(ps2);
551 EXPECT_TRUE(getCollisionObjectsNames(*ps).empty());
552 EXPECT_EQ(getAttachedCollisionObjectsNames(*ps), (std::set<std::string>{ "object1", "object2" }));
553 }
554
555 // Removing an attached collision object
556 {
557 const auto ps1 = createPlanningSceneDiff(*ps, "object2", moveit_msgs::msg::CollisionObject::REMOVE, true);
558 ps->usePlanningSceneMsg(ps1);
559
560 EXPECT_EQ(getCollisionObjectsNames(*ps), (std::set<std::string>{ "object2" }));
561 EXPECT_EQ(getAttachedCollisionObjectsNames(*ps), (std::set<std::string>{ "object1" }));
562 }
563
564 // Turn an existing collision object into an attached object
565 {
566 const auto ps1 = createPlanningSceneDiff(*ps, "object2", moveit_msgs::msg::CollisionObject::ADD, true, false);
567 ps->usePlanningSceneMsg(ps1);
568
569 EXPECT_TRUE(getCollisionObjectsNames(*ps).empty());
570 EXPECT_EQ(getAttachedCollisionObjectsNames(*ps), (std::set<std::string>{ "object1", "object2" }));
571 }
572
573 // Removing an attached collision object completely
574 {
575 auto ps1 = ps->diff();
576 moveit_msgs::msg::CollisionObject co;
577 co.id = "object2";
578 co.operation = moveit_msgs::msg::CollisionObject::REMOVE;
579 moveit_msgs::msg::AttachedCollisionObject aco;
580 aco.object = co;
581
582 ps1->processAttachedCollisionObjectMsg(aco); // detach
583 ps1->processCollisionObjectMsg(co); // and eventually remove object
584
585 moveit_msgs::msg::PlanningScene msg;
586 ps1->getPlanningSceneDiffMsg(msg);
587 ps->usePlanningSceneMsg(msg);
588
589 EXPECT_TRUE(getCollisionObjectsNames(*ps).empty());
590 EXPECT_EQ(getAttachedCollisionObjectsNames(*ps), (std::set<std::string>{ "object1" }));
591 }
592}
593
594TEST(PlanningScene, UpdateACMAfterObjectRemoval)
595{
596 auto robot_model = moveit::core::loadTestingRobotModel("panda");
597 auto ps = std::make_shared<planning_scene::PlanningScene>(robot_model);
598
599 const auto object_name = "object";
601 collision_request.group_name = "hand";
602 collision_request.verbose = true;
603
604 // Helper function to add an object to the planning scene
605 auto add_object = [&] {
606 const auto ps1 = createPlanningSceneDiff(*ps, object_name, moveit_msgs::msg::CollisionObject::ADD);
607 ps->usePlanningSceneMsg(ps1);
608 EXPECT_EQ(getCollisionObjectsNames(*ps), (std::set<std::string>{ object_name }));
609 };
610
611 // Helper function to attach the object to the robot
612 auto attach_object = [&] {
613 const auto ps1 = createPlanningSceneDiff(*ps, object_name, moveit_msgs::msg::CollisionObject::ADD, true);
614 ps->usePlanningSceneMsg(ps1);
615 EXPECT_EQ(getAttachedCollisionObjectsNames(*ps), (std::set<std::string>{ object_name }));
616 };
617
618 // Helper function to detach the object from the robot
619 auto detach_object = [&] {
620 const auto ps1 = createPlanningSceneDiff(*ps, object_name, moveit_msgs::msg::CollisionObject::REMOVE, true);
621 ps->usePlanningSceneMsg(ps1);
622 EXPECT_EQ(getAttachedCollisionObjectsNames(*ps), (std::set<std::string>{}));
623 };
624
625 // Modify the allowed collision matrix and make sure it is updated
626 auto modify_acm = [&] {
627 collision_detection::AllowedCollisionMatrix& acm = ps->getAllowedCollisionMatrixNonConst();
628 acm.setEntry(object_name, ps->getRobotModel()->getJointModelGroup("hand")->getLinkModelNamesWithCollisionGeometry(),
629 true);
630 EXPECT_TRUE(ps->getAllowedCollisionMatrix().hasEntry(object_name));
631 };
632
633 // Check collision
634 auto check_collision = [&] {
636 ps->checkCollision(collision_request, res);
637 return res.collision;
638 };
639
640 // Test removing a collision object using a diff
641 add_object();
642 EXPECT_TRUE(check_collision());
643 modify_acm();
644 EXPECT_FALSE(check_collision());
645 // Attach and detach the object from the robot to make sure that collision are still allowed
646 attach_object();
647 EXPECT_FALSE(check_collision());
648 detach_object();
649 EXPECT_FALSE(check_collision());
650 {
651 const auto ps1 = createPlanningSceneDiff(*ps, object_name, moveit_msgs::msg::CollisionObject::REMOVE);
652 ps->usePlanningSceneMsg(ps1);
653 EXPECT_EQ(getCollisionObjectsNames(*ps), (std::set<std::string>{}));
654 EXPECT_FALSE(ps->getAllowedCollisionMatrix().hasEntry(object_name));
655 }
656
657 // Test removing all objects
658 add_object();
659 // This should report a collision since it's a completely new object
660 EXPECT_TRUE(check_collision());
661 modify_acm();
662 EXPECT_FALSE(check_collision());
663 ps->removeAllCollisionObjects();
664 EXPECT_EQ(getCollisionObjectsNames(*ps), (std::set<std::string>{}));
665 EXPECT_FALSE(ps->getAllowedCollisionMatrix().hasEntry(object_name));
666}
667
668#ifndef INSTANTIATE_TEST_SUITE_P // prior to gtest 1.10
669#define INSTANTIATE_TEST_SUITE_P(...) INSTANTIATE_TEST_CASE_P(__VA_ARGS__)
670#endif
671
672// instantiate parameterized tests for common collision plugins
673INSTANTIATE_TEST_SUITE_P(PluginTests, CollisionDetectorTests, testing::Values("FCL", "Bullet"));
674
675int main(int argc, char** argv)
676{
677 testing::InitGoogleTest(&argc, argv);
678 return RUN_ALL_TESTS();
679}
Definition of a structure for the allowed collision matrix. All elements in the collision world are r...
void setEntry(const std::string &name1, const std::string &name2, bool allowed)
Set an entry corresponding to a pair of elements.
bool activate(const std::string &name, const planning_scene::PlanningScenePtr &scene)
Activate a specific collision plugin for the given planning scene instance.
Maintain a representation of the environment.
Definition world.hpp:59
bool hasObject(const std::string &object_id) const
Check if a particular object exists in the collision world.
Definition world.cpp:137
void addToObject(const std::string &object_id, const Eigen::Isometry3d &pose, const std::vector< shapes::ShapeConstPtr > &shapes, const EigenSTL::vector_Isometry3d &shape_poses)
Add a pose and shapes to an object in the map. This function makes repeated calls to addToObjectInter...
Definition world.cpp:77
Representation of a robot's state. This includes position, velocity, acceleration and effort.
void update(bool force=false)
Update all transforms.
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
This class maintains the representation of the environment as seen by a planning instance....
void getAttachedCollisionObjectMsgs(std::vector< moveit_msgs::msg::AttachedCollisionObject > &attached_collision_objs) const
Construct a vector of messages (attached_collision_objects) with the attached collision object data f...
void getPlanningSceneMsg(moveit_msgs::msg::PlanningScene &scene) const
Construct a message (scene) with all the necessary data so that the scene can be later reconstructed ...
const moveit::core::RobotModelConstPtr & getRobotModel() const
Get the kinematic model for which the planning scene is maintained.
bool processCollisionObjectMsg(const moveit_msgs::msg::CollisionObject &object)
const Eigen::Isometry3d & getFrameTransform(const std::string &id) const
Get the transform corresponding to the frame id. This will be known if id is a link name,...
bool getCollisionObjectMsg(moveit_msgs::msg::CollisionObject &collision_obj, const std::string &ns) const
Construct a message (collision_object) with the collision object data from the planning_scene for the...
void getCollisionObjectMsgs(std::vector< moveit_msgs::msg::CollisionObject > &collision_objs) const
Construct a vector of messages (collision_objects) with the collision object data for all objects in ...
PlanningScenePtr diff() const
Return a new child PlanningScene that uses this one as parent.
const collision_detection::WorldConstPtr & getWorld() const
Get the representation of the world.
bool setPlanningSceneDiffMsg(const moveit_msgs::msg::PlanningScene &scene)
Apply changes to this planning scene as diffs, even if the message itself is not marked as being a di...
bool getOctomapMsg(octomap_msgs::msg::OctomapWithPose &octomap) const
Construct a message (octomap) with the octomap data from the planning_scene.
const std::string & getName() const
Get the name of the planning scene. This is empty by default.
bool setPlanningSceneMsg(const moveit_msgs::msg::PlanningScene &scene)
Set this instance of a planning scene to be the same as the one serialized in the scene message,...
static const std::string OCTOMAP_NS
bool isEmpty(const moveit_msgs::msg::PlanningScene &msg)
Check if a message includes any information about a planning scene, or whether it is empty.
moveit::core::RobotModelPtr loadTestingRobotModel(const std::string &package_name, const std::string &urdf_relative_path, const std::string &srdf_relative_path)
Loads a robot model given a URDF and SRDF file in a package.
urdf::ModelInterfaceSharedPtr loadModelInterface(const std::string &robot_name)
Loads a URDF Model Interface from moveit_resources.
Representation of a collision checking request.
std::string group_name
The group name to check collisions for (optional; if empty, assume the complete robot)....
bool verbose
Flag indicating whether information about detected collisions should be reported.
Representation of a collision checking result.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW void clear()
Clear a previously stored result.
bool collision
True if collision was found, false otherwise.
int main(int argc, char **argv)
TEST(PlanningScene, TestOneShapeObjectPose)
#define INSTANTIATE_TEST_SUITE_P(...)
moveit_msgs::msg::PlanningScene createPlanningSceneDiff(const planning_scene::PlanningScene &ps, const std::string &object_name, const int8_t operation, const bool attach_object=false, const bool create_object=true)
TEST_P(CollisionDetectorTests, ClearDiff)
std::set< std::string > getAttachedCollisionObjectsNames(const planning_scene::PlanningScene &ps)
std::set< std::string > getCollisionObjectsNames(const planning_scene::PlanningScene &ps)