moveit2
The MoveIt Motion Planning Framework for ROS 2.
test_collision_common_panda.hpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2019, Jens Petit
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 the copyright holder 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: Jens Petit */
36 
37 #pragma once
38 
45 
46 #include <urdf_parser/urdf_parser.h>
47 #include <geometric_shapes/shape_operations.h>
48 
49 #include <gtest/gtest.h>
50 #include <sstream>
51 #include <algorithm>
52 #include <ctype.h>
53 #include <fstream>
54 
56 inline void setToHome(moveit::core::RobotState& panda_state)
57 {
58  panda_state.setToDefaultValues();
59  double joint2 = -0.785;
60  double joint4 = -2.356;
61  double joint6 = 1.571;
62  double joint7 = 0.785;
63  panda_state.setJointPositions("panda_joint2", &joint2);
64  panda_state.setJointPositions("panda_joint4", &joint4);
65  panda_state.setJointPositions("panda_joint6", &joint6);
66  panda_state.setJointPositions("panda_joint7", &joint7);
67  panda_state.update();
68 }
69 
70 template <class CollisionAllocatorType>
71 class CollisionDetectorPandaTest : public testing::Test
72 {
73 public:
74  std::shared_ptr<CollisionAllocatorType> value_;
75 
76 protected:
77  void SetUp() override
78  {
79  value_ = std::make_shared<CollisionAllocatorType>();
81  robot_model_ok_ = static_cast<bool>(robot_model_);
82 
83  acm_ = std::make_shared<collision_detection::AllowedCollisionMatrix>(*robot_model_->getSRDF());
84 
85  cenv_ = value_->allocateEnv(robot_model_);
86 
87  robot_state_ = std::make_shared<moveit::core::RobotState>(robot_model_);
89  }
90 
91  void TearDown() override
92  {
93  }
94 
96 
97  moveit::core::RobotModelPtr robot_model_;
98 
99  collision_detection::CollisionEnvPtr cenv_;
100 
101  collision_detection::AllowedCollisionMatrixPtr acm_;
102 
103  moveit::core::RobotStatePtr robot_state_;
104 };
105 
107 
110 {
111  ASSERT_TRUE(this->robot_model_ok_);
112 }
113 
116 {
119  this->cenv_->checkSelfCollision(req, res, *this->robot_state_, *this->acm_);
120  ASSERT_FALSE(res.collision);
121 }
122 
125 {
126  // Sets the joints into a colliding configuration
127  double joint2 = 0.15;
128  double joint4 = -3.0;
129  this->robot_state_->setJointPositions("panda_joint2", &joint2);
130  this->robot_state_->setJointPositions("panda_joint4", &joint4);
131  this->robot_state_->update();
132 
135  this->cenv_->checkSelfCollision(req, res, *this->robot_state_, *this->acm_);
136  ASSERT_TRUE(res.collision);
137 }
138 
141 {
144 
145  shapes::Shape* shape = new shapes::Box(.1, .1, .1);
146  shapes::ShapeConstPtr shape_ptr(shape);
147 
148  Eigen::Isometry3d pos1 = Eigen::Isometry3d::Identity();
149  pos1.translation().z() = 0.3;
150  this->cenv_->getWorld()->addToObject("box", pos1, shape_ptr, Eigen::Isometry3d::Identity());
151 
152  this->cenv_->checkSelfCollision(req, res, *this->robot_state_, *this->acm_);
153  ASSERT_FALSE(res.collision);
154  res.clear();
155 
156  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
157  ASSERT_TRUE(res.collision);
158  res.clear();
159 
160  pos1.translation().z() = 0.25;
161  this->cenv_->getWorld()->moveObject("box", pos1);
162  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
163  ASSERT_FALSE(res.collision);
164  res.clear();
165 
166  pos1.translation().z() = 0.05;
167  this->cenv_->getWorld()->moveObject("box", pos1);
168  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
169  ASSERT_TRUE(res.collision);
170  res.clear();
171 
172  pos1.translation().z() = 0.25;
173  this->cenv_->getWorld()->moveObject("box", pos1);
174  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
175  ASSERT_FALSE(res.collision);
176  res.clear();
177 
178  this->cenv_->getWorld()->moveObject("box", pos1);
179  ASSERT_FALSE(res.collision);
180 }
181 
184 {
187  req.max_contacts = 10;
188  req.contacts = true;
189  req.verbose = true;
190 
191  shapes::Shape* shape = new shapes::Box(.4, .4, .4);
192  shapes::ShapeConstPtr shape_ptr(shape);
193 
194  Eigen::Isometry3d pos1 = Eigen::Isometry3d::Identity();
195  pos1.translation().z() = 0.3;
196  this->cenv_->getWorld()->addToObject("box", pos1, shape_ptr, Eigen::Isometry3d::Identity());
197 
198  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
199  ASSERT_TRUE(res.collision);
200  ASSERT_GE(res.contact_count, 3u);
201  res.clear();
202 }
203 
206 {
208  req.contacts = true;
209  req.max_contacts = 10;
211 
212  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
213  ASSERT_FALSE(res.collision);
214  res.clear();
215 
216  // Adding the box right in front of the robot hand
217  shapes::Shape* shape = new shapes::Box(0.1, 0.1, 0.1);
218  shapes::ShapeConstPtr shape_ptr(shape);
219 
220  Eigen::Isometry3d pos{ Eigen::Isometry3d::Identity() };
221  pos.translation().x() = 0.43;
222  pos.translation().y() = 0;
223  pos.translation().z() = 0.55;
224  this->cenv_->getWorld()->addToObject("box", pos, shape_ptr, Eigen::Isometry3d::Identity());
225 
226  this->cenv_->setLinkPadding("panda_hand", 0.08);
227  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
228  ASSERT_TRUE(res.collision);
229  res.clear();
230 
231  this->cenv_->setLinkPadding("panda_hand", 0.0);
232  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
233  ASSERT_FALSE(res.collision);
234 }
235 
238 {
240  req.distance = true;
242  this->cenv_->checkSelfCollision(req, res, *this->robot_state_, *this->acm_);
243  ASSERT_FALSE(res.collision);
244  EXPECT_NEAR(res.distance, 0.022, 0.001);
245 }
246 
248 {
250  req.distance = true;
252 
253  // Adding the box right in front of the robot hand
254  shapes::Shape* shape = new shapes::Box(0.1, 0.1, 0.1);
255  shapes::ShapeConstPtr shape_ptr(shape);
256 
257  Eigen::Isometry3d pos{ Eigen::Isometry3d::Identity() };
258  pos.translation().x() = 0.43;
259  pos.translation().y() = 0;
260  pos.translation().z() = 0.55;
261  this->cenv_->getWorld()->addToObject("box", pos, shape_ptr, Eigen::Isometry3d::Identity());
262 
263  this->cenv_->setLinkPadding("panda_hand", 0.0);
264  this->cenv_->checkRobotCollision(req, res, *this->robot_state_, *this->acm_);
265  ASSERT_FALSE(res.collision);
266  EXPECT_NEAR(res.distance, 0.029, 0.01);
267 }
268 
269 template <class CollisionAllocatorType>
270 class DistanceCheckPandaTest : public CollisionDetectorPandaTest<CollisionAllocatorType>
271 {
272 };
273 
275 
277 {
278  std::set<const moveit::core::LinkModel*> active_components{ this->robot_model_->getLinkModel("panda_hand") };
281  req.active_components_only = &active_components;
282  req.enable_signed_distance = true;
283 
284  random_numbers::RandomNumberGenerator rng(0x47110815);
285  double min_distance = std::numeric_limits<double>::max();
286  for (int i = 0; i < 10; ++i)
287  {
289 
290  shapes::ShapeConstPtr shape = std::make_shared<const shapes::Cylinder>(rng.uniform01(), rng.uniform01());
291  Eigen::Isometry3d pose{ Eigen::Isometry3d::Identity() };
292  pose.translation() =
293  Eigen::Vector3d(rng.uniformReal(0.1, 2.0), rng.uniformReal(0.1, 2.0), rng.uniformReal(1.2, 1.7));
294  double quat[4];
295  rng.quaternion(quat);
296  pose.linear() = Eigen::Quaterniond(quat[0], quat[1], quat[2], quat[3]).toRotationMatrix();
297 
298  this->cenv_->getWorld()->addToObject("collection", Eigen::Isometry3d::Identity(), shape, pose);
299  this->cenv_->getWorld()->removeObject("object");
300  this->cenv_->getWorld()->addToObject("object", pose, shape, Eigen::Isometry3d::Identity());
301 
302  this->cenv_->distanceRobot(req, res, *this->robot_state_);
303  auto& distances1 = res.distances[std::pair<std::string, std::string>("collection", "panda_hand")];
304  auto& distances2 = res.distances[std::pair<std::string, std::string>("object", "panda_hand")];
305  ASSERT_EQ(distances1.size(), 1u) << "no distance reported for collection/panda_hand";
306  ASSERT_EQ(distances2.size(), 1u) << "no distance reported for object/panda_hand";
307 
308  double collection_distance = distances1[0].distance;
309  min_distance = std::min(min_distance, distances2[0].distance);
310  ASSERT_NEAR(collection_distance, min_distance, 1e-5)
311  << "distance to collection is greater than distance to minimum of individual objects after " << i << " rounds";
312  }
313 }
314 
315 // FCL < 0.6 completely fails the DistancePoints test, so we have two test
316 // suites, one with and one without the test.
317 template <class CollisionAllocatorType>
318 class DistanceFullPandaTest : public DistanceCheckPandaTest<CollisionAllocatorType>
319 {
320 };
321 
323 
326 {
327  // Adding the box right in front of the robot hand
328  shapes::Box* shape = new shapes::Box(0.1, 0.1, 0.1);
329  shapes::ShapeConstPtr shape_ptr(shape);
330 
331  Eigen::Isometry3d pos{ Eigen::Isometry3d::Identity() };
332  pos.translation().x() = 0.43;
333  pos.translation().y() = 0;
334  pos.translation().z() = 0.55;
335  this->cenv_->getWorld()->addToObject("box", shape_ptr, pos);
336 
338  req.acm = &*this->acm_;
340  req.enable_nearest_points = true;
341  req.max_contacts_per_body = 1;
342 
344  this->cenv_->distanceRobot(req, res, *this->robot_state_);
345 
346  // Checks a particular point is inside the box
347  auto check_in_box = [&](const Eigen::Vector3d& p) {
348  Eigen::Vector3d in_box = pos.inverse() * p;
349 
350  constexpr double eps = 1e-5;
351  EXPECT_LE(std::abs(in_box.x()), shape->size[0] + eps);
352  EXPECT_LE(std::abs(in_box.y()), shape->size[1] + eps);
353  EXPECT_LE(std::abs(in_box.z()), shape->size[2] + eps);
354  };
355 
356  // Check that all points reported on "box" are actually on the box and not
357  // on the robot
358  for (auto& distance : res.distances)
359  {
360  for (auto& pair : distance.second)
361  {
362  if (pair.link_names[0] == "box")
363  {
364  check_in_box(pair.nearest_points[0]);
365  }
366  else if (pair.link_names[1] == "box")
367  {
368  check_in_box(pair.nearest_points[1]);
369  }
370  else
371  {
372  ADD_FAILURE() << "Unrecognized link names";
373  }
374  }
375  }
376 }
377 
378 REGISTER_TYPED_TEST_SUITE_P(CollisionDetectorPandaTest, InitOK, DefaultNotInCollision, LinksInCollision,
379  RobotWorldCollision_1, RobotWorldCollision_2, PaddingTest, DistanceSelf, DistanceWorld);
380 
382 
collision_detection::CollisionEnvPtr cenv_
moveit::core::RobotModelPtr robot_model_
std::shared_ptr< CollisionAllocatorType > value_
moveit::core::RobotStatePtr robot_state_
collision_detection::AllowedCollisionMatrixPtr acm_
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.hpp:90
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...
void setJointPositions(const std::string &joint_name, const double *position)
@ SINGLE
Find the global minimum for each pair.
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.hpp:89
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.
double distance(const urdf::Pose &transform)
Definition: pr2_arm_ik.hpp:59
Representation of a collision checking request.
bool verbose
Flag indicating whether information about detected collisions should be reported.
bool contacts
If true, compute contacts. Otherwise only a binary collision yes/no is reported.
std::size_t max_contacts
Overall maximum number of contacts to compute.
bool distance
If true, compute proximity distance.
Representation of a collision checking result.
double distance
Closest distance between two bodies.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW void clear()
Clear a previously stored result.
bool collision
True if collision was found, false otherwise.
std::size_t contact_count
Number of contacts returned.
Representation of a distance-reporting request.
const std::set< const moveit::core::LinkModel * > * active_components_only
The set of active components to check.
std::size_t max_contacts_per_body
Maximum number of contacts to store for bodies (multiple bodies may be within distance threshold)
bool enable_signed_distance
Indicate if a signed distance should be calculated in a collision.
const AllowedCollisionMatrix * acm
The allowed collision matrix used to filter checks.
bool enable_nearest_points
Indicate if nearest point information should be calculated.
Result of a distance request.
DistanceMap distances
A map of distance data for each link in the req.active_components_only.
TYPED_TEST_CASE_P(CollisionDetectorPandaTest)
void setToHome(moveit::core::RobotState &panda_state)
Brings the panda robot in user defined home position.
TYPED_TEST_P(CollisionDetectorPandaTest, InitOK)
Correct setup testing.
REGISTER_TYPED_TEST_SUITE_P(CollisionDetectorPandaTest, InitOK, DefaultNotInCollision, LinksInCollision, RobotWorldCollision_1, RobotWorldCollision_2, PaddingTest, DistanceSelf, DistanceWorld)