collision_scene_fcl_latest.cpp
Go to the documentation of this file.
1 //
2 // Copyright (c) 2018-2020, University of Edinburgh, University of Oxford
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of nor the names of its contributors may be used to
14 // endorse or promote products derived from this software without specific
15 // prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 //
29 
31 #include <exotica_core/factory.h>
32 #include <exotica_core/scene.h>
33 
36 
38 
39 #define CONTINUOUS_COLLISION_USE_ADVANCED_SETTINGS
40 // #define CONTINUOUS_COLLISION_DEBUG
41 
42 namespace exotica
43 {
44 // This function was the source of a massive bug that reappeared in July 2018, July 2019, and November 2020.
45 // It was mostly due to a symbol crash between the two fcl_conversion implementations. I.e., the naming and
46 // namespace is now kept different from the implementation in the CollisionSceneFCLDefault
47 inline fcl::Transform3d transformKDLToFCL(const KDL::Frame& frame)
48 {
49  fcl::Transform3d ret;
50  ret.translation() = Eigen::Map<const Eigen::Vector3d>(frame.p.data);
51  ret.linear() = Eigen::Map<const Eigen::Matrix3d>(frame.M.data);
52  return ret;
53 }
54 
55 void transformFCLToKDL(const fcl::Transform3d& tf, KDL::Frame& frame)
56 {
57  Eigen::Map<Eigen::Vector3d>(frame.p.data) = tf.translation();
58  Eigen::Map<Eigen::Matrix3d>(frame.M.data) = tf.linear().matrix();
59 }
60 
61 inline bool IsRobotLink(std::shared_ptr<KinematicElement> e)
62 {
63  return e->is_robot_link || e->closest_robot_link.lock();
64 }
65 
67 {
68  if (debug_) HIGHLIGHT_NAMED("CollisionSceneFCLLatest", "FCL version: " << FCL_VERSION);
69 
70  broad_phase_collision_manager_.reset(new fcl::DynamicAABBTreeCollisionManagerd());
71 }
72 
73 void CollisionSceneFCLLatest::UpdateCollisionObjects(const std::map<std::string, std::weak_ptr<KinematicElement>>& objects)
74 {
75  kinematic_elements_map_ = objects;
76 
77  kinematic_elements_.clear();
78  kinematic_elements_.reserve(objects.size());
79 
80  fcl_cache_.clear();
81  fcl_cache_.reserve(objects.size());
82 
83  fcl_objects_.clear();
84  fcl_objects_.reserve(objects.size());
85 
86  fcl_objects_map_.clear();
87  fcl_robot_objects_map_.clear();
88  fcl_world_objects_map_.clear();
89 
90  long i = 0;
91 
92  auto world_links_to_exclude_from_collision_scene = scene_.lock()->get_world_links_to_exclude_from_collision_scene();
93  for (const auto& object : objects)
94  {
95  // Check whether object is excluded as a world collision object:
96  // TODO: This works differently than in the Scene: There it's the original link name, here the frame_name!
97  if (world_links_to_exclude_from_collision_scene.count(object.first) > 0)
98  {
99  if (debug_) HIGHLIGHT_NAMED("CollisionSceneFCLLatest::UpdateCollisionObject", object.first << " is excluded, skipping.");
100  }
101  else
102  {
103  if (debug_) HIGHLIGHT_NAMED("CollisionSceneFCLLatest::UpdateCollisionObject", "Creating " << object.first);
104 
105  std::shared_ptr<fcl::CollisionObjectd> new_object = ConstructFclCollisionObject(i, object.second.lock());
106 
107  fcl_cache_.emplace_back(new_object);
108  fcl_objects_.emplace_back(new_object.get());
109  kinematic_elements_.emplace_back(object.second);
110 
111  fcl_objects_map_[object.first].emplace_back(new_object.get());
112  // Check whether this is a robot or environment link:
113  if (IsRobotLink(object.second.lock()))
114  {
115  fcl_robot_objects_map_[object.first].emplace_back(new_object.get());
116  }
117  else
118  {
119  fcl_world_objects_map_[object.first].emplace_back(new_object.get());
120  }
121 
122  ++i;
123  }
124  }
125 
126  // Register objects with the BroadPhaseCollisionManager
130 }
131 
133 {
134  for (fcl::CollisionObjectd* collision_object : fcl_objects_)
135  {
136  if (!collision_object)
137  {
138  ThrowPretty("Collision object pointer is dead.");
139  }
140  std::shared_ptr<KinematicElement> element = kinematic_elements_[reinterpret_cast<long>(collision_object->getUserData())].lock();
141  if (!element)
142  {
143  ThrowPretty("Expired pointer, this should not happen - make sure to call UpdateCollisionObjects() after UpdateSceneFrames()");
144  }
145 
146  // Check for NaNs
147  if (std::isnan(element->frame.p.data[0]) || std::isnan(element->frame.p.data[1]) || std::isnan(element->frame.p.data[2]))
148  {
149  ThrowPretty("Transform for " << element->segment.getName() << " contains NaNs.");
150  }
151 
152  collision_object->setTransform(transformKDLToFCL(element->frame));
153  collision_object->computeAABB();
154  }
155 }
156 
157 // This function was originally copied from 'moveit_core/collision_detection_fcl/src/collision_common.cpp'
158 // https://github.com/ros-planning/moveit/blob/kinetic-devel/moveit_core/collision_detection_fcl/src/collision_common.cpp#L520
159 // and then modified for use in EXOTica.
160 std::shared_ptr<fcl::CollisionObjectd> CollisionSceneFCLLatest::ConstructFclCollisionObject(long kinematic_element_id, std::shared_ptr<KinematicElement> element)
161 {
162  shapes::ShapePtr shape(element->shape->clone());
163 
164  // Apply scaling and padding
165  if (IsRobotLink(element))
166  {
167  if (robot_link_scale_ != 1.0 || robot_link_padding_ > 0.0)
168  {
169  shape->scaleAndPadd(robot_link_scale_, robot_link_padding_);
170  }
171  }
172  else
173  {
174  if (world_link_scale_ != 1.0 || world_link_padding_ > 0.0)
175  {
176  shape->scaleAndPadd(world_link_scale_, world_link_padding_);
177  }
178  }
179 
180  // Replace primitive shapes with meshes if desired (e.g. if primitives are unstable)
182  {
183  if (static_cast<int>(shape->type) < 6) // The regular enum type comparisons start to fail at times :/
184  {
185  shape.reset(reinterpret_cast<shapes::Shape*>(shapes::createMeshFromShape(shape.get())));
186  }
187  }
188 
189  std::shared_ptr<fcl::CollisionGeometryd> geometry;
190  switch (shape->type)
191  {
192  case shapes::PLANE:
193  {
194  auto p = dynamic_cast<const shapes::Plane*>(shape.get());
195  geometry.reset(new fcl::Planed(p->a, p->b, p->c, p->d));
196  }
197  break;
198  case shapes::SPHERE:
199  {
200  auto s = dynamic_cast<const shapes::Sphere*>(shape.get());
201  geometry.reset(new fcl::Sphered(s->radius));
202  }
203  break;
204  case shapes::BOX:
205  {
206  auto s = dynamic_cast<const shapes::Box*>(shape.get());
207  const double* size = s->size;
208  geometry.reset(new fcl::Boxd(size[0], size[1], size[2]));
209  }
210  break;
211  case shapes::CYLINDER:
212  {
213  auto s = dynamic_cast<const shapes::Cylinder*>(shape.get());
214  bool degenerate_capsule = (s->length <= 2 * s->radius);
215  if (!replace_cylinders_with_capsules_ || degenerate_capsule)
216  {
217  geometry.reset(new fcl::Cylinderd(s->radius, s->length));
218  }
219  else
220  {
221  geometry.reset(new fcl::Capsuled(s->radius, s->length - 2 * s->radius));
222  }
223  }
224  break;
225  case shapes::CONE:
226  {
227  auto s = dynamic_cast<const shapes::Cone*>(shape.get());
228  geometry.reset(new fcl::Coned(s->radius, s->length));
229  }
230  break;
231  case shapes::MESH:
232  {
233  auto g = new fcl::BVHModel<fcl::OBBRSSd>();
234  auto mesh = dynamic_cast<const shapes::Mesh*>(shape.get());
235  if (mesh->vertex_count > 0 && mesh->triangle_count > 0)
236  {
237  std::vector<fcl::Triangle> tri_indices(mesh->triangle_count);
238  for (unsigned int i = 0; i < mesh->triangle_count; ++i)
239  tri_indices[i] =
240  fcl::Triangle(mesh->triangles[3 * i], mesh->triangles[3 * i + 1], mesh->triangles[3 * i + 2]);
241 
242  std::vector<fcl::Vector3d> points(mesh->vertex_count);
243  for (unsigned int i = 0; i < mesh->vertex_count; ++i)
244  points[i] = fcl::Vector3d(mesh->vertices[3 * i], mesh->vertices[3 * i + 1], mesh->vertices[3 * i + 2]);
245 
246  g->beginModel();
247  g->addSubModel(points, tri_indices);
248  g->endModel();
249  }
250  geometry.reset(g);
251  }
252  break;
253  case shapes::OCTREE:
254  {
255  auto g = dynamic_cast<const shapes::OcTree*>(shape.get());
256  geometry.reset(new fcl::OcTreed(ToStdPtr(g->octree)));
257  }
258  break;
259  default:
260  ThrowPretty("This shape type (" << ((int)shape->type) << ") is not supported using FCL yet");
261  }
262  geometry->computeLocalAABB();
263  geometry->setUserData(reinterpret_cast<void*>(kinematic_element_id));
264  std::shared_ptr<fcl::CollisionObjectd> ret(new fcl::CollisionObjectd(geometry));
265  ret->setUserData(reinterpret_cast<void*>(kinematic_element_id));
266 
267  return ret;
268 }
269 
270 bool CollisionSceneFCLLatest::IsAllowedToCollide(const std::string& o1, const std::string& o2, const bool& self)
271 {
272  std::shared_ptr<KinematicElement> e1 = GetKinematicElementFromMapByName(o1);
273  std::shared_ptr<KinematicElement> e2 = GetKinematicElementFromMapByName(o2);
274 
275  bool isRobot1 = IsRobotLink(e1);
276  bool isRobot2 = IsRobotLink(e2);
277  // Don't check collisions between world objects
278  if (!isRobot1 && !isRobot2) return false;
279  // Skip self collisions if requested
280  if (isRobot1 && isRobot2 && !self) return false;
281  // Skip collisions between shapes within the same objects
282  if (e1->parent.lock() == e2->parent.lock()) return false;
283  // Skip collisions between bodies attached to the same object
284  if (e1->closest_robot_link.lock() && e2->closest_robot_link.lock() && e1->closest_robot_link.lock() == e2->closest_robot_link.lock()) return false;
285 
286  if (isRobot1 && isRobot2)
287  {
288  const std::string& name1 = e1->closest_robot_link.lock() ? e1->closest_robot_link.lock()->segment.getName() : e1->parent.lock()->segment.getName();
289  const std::string& name2 = e2->closest_robot_link.lock() ? e2->closest_robot_link.lock()->segment.getName() : e2->parent.lock()->segment.getName();
290  return acm_.getAllowedCollision(name1, name2);
291  }
292  return true;
293 }
294 
295 bool CollisionSceneFCLLatest::IsAllowedToCollide(fcl::CollisionObjectd* o1, fcl::CollisionObjectd* o2, bool self, CollisionSceneFCLLatest* scene)
296 {
297  std::shared_ptr<KinematicElement> e1 = scene->kinematic_elements_[reinterpret_cast<long>(o1->getUserData())].lock();
298  std::shared_ptr<KinematicElement> e2 = scene->kinematic_elements_[reinterpret_cast<long>(o2->getUserData())].lock();
299 
300  bool isRobot1 = IsRobotLink(e1);
301  bool isRobot2 = IsRobotLink(e2);
302  // Don't check collisions between world objects
303  if (!isRobot1 && !isRobot2) return false;
304  // Skip self collisions if requested
305  if (isRobot1 && isRobot2 && !self) return false;
306  // Skip collisions between shapes within the same objects
307  if (e1->parent.lock() == e2->parent.lock()) return false;
308  // Skip collisions between bodies attached to the same object
309  if (e1->closest_robot_link.lock() && e2->closest_robot_link.lock() && e1->closest_robot_link.lock() == e2->closest_robot_link.lock()) return false;
310 
311  if (isRobot1 && isRobot2)
312  {
313  const std::string& name1 = e1->closest_robot_link.lock() ? e1->closest_robot_link.lock()->segment.getName() : e1->parent.lock()->segment.getName();
314  const std::string& name2 = e2->closest_robot_link.lock() ? e2->closest_robot_link.lock()->segment.getName() : e2->parent.lock()->segment.getName();
315  return scene->acm_.getAllowedCollision(name1, name2);
316  }
317  return true;
318 }
319 
320 void CollisionSceneFCLLatest::CheckCollision(fcl::CollisionObjectd* o1, fcl::CollisionObjectd* o2, CollisionData* data)
321 {
322  data->request.num_max_contacts = 1000;
323  data->request.gjk_solver_type = fcl::GST_LIBCCD;
324  data->result.clear();
325  fcl::collide(o1, o2, data->request, data->result);
326  if (data->safe_distance > 0.0 && o1->getAABB().distance(o2->getAABB()) < data->safe_distance)
327  {
328  fcl::DistanceRequestd req;
329  fcl::DistanceResultd res;
330  req.enable_nearest_points = false;
331  fcl::distance(o1, o2, req, res);
332  // Add fake contact when distance is smaller than the safety distance.
333  if (res.min_distance < data->safe_distance) data->result.addContact(fcl::Contactd());
334  }
335 }
336 
337 bool CollisionSceneFCLLatest::CollisionCallback(fcl::CollisionObjectd* o1, fcl::CollisionObjectd* o2, void* data)
338 {
339  CollisionData* data_ = reinterpret_cast<CollisionData*>(data);
340 
341  if (!IsAllowedToCollide(o1, o2, data_->self, data_->scene)) return false;
342 
343  CheckCollision(o1, o2, data_);
344  return data_->result.isCollision();
345 }
346 
347 void CollisionSceneFCLLatest::ComputeDistance(fcl::CollisionObjectd* o1, fcl::CollisionObjectd* o2, DistanceData* data)
348 {
349  // Setup proxy.
350  CollisionProxy p;
351  p.e1 = data->scene->kinematic_elements_[reinterpret_cast<long>(o1->getUserData())].lock();
352  p.e2 = data->scene->kinematic_elements_[reinterpret_cast<long>(o2->getUserData())].lock();
353 
354  // New logic as of August 2018:
355  // - Use LIBCCD as comment in Drake suggests it is more reliable now.
356  // - If in collision, use the deepest contact of the collide callback.
357  // - If not in collision, run distance query.
358 
359  // Step 0: Run collision check:
360  fcl::CollisionRequestd tmp_req;
361  fcl::CollisionResultd tmp_res;
362  tmp_req.num_max_contacts = 1000;
363  tmp_req.enable_contact = true;
364 
365  // The following comment and Step 1 code is copied from Drake (BSD license):
366  // https://github.com/RobotLocomotion/drake/blob/0aa7f713eb029fea7d47109992762ed6d8d1d457/geometry/proximity_engine.cc
367  // NOTE: As of 5/1/2018 the GJK implementation of Libccd appears to be
368  // superior to FCL's "independent" implementation. Furthermore, libccd
369  // appears to behave badly if its gjk tolerance is much tighter than
370  // 2e-12. Until this changes, we explicitly specify these parameters rather
371  // than relying on FCL's defaults.
372  tmp_req.gjk_tolerance = 2e-12;
373  tmp_req.gjk_solver_type = fcl::GJKSolverType::GST_LIBCCD;
374 
375  fcl::collide(o1, o2, tmp_req, tmp_res);
376 
377  // Step 1: If in collision, extract contact point.
378  if (tmp_res.isCollision())
379  {
380  // TODO: Issue #364: https://github.com/ipab-slmc/exotica/issues/364
381  // As of 0.5.94, this does not work for primitive-vs-mesh (but does for mesh-vs-primitive):
382  if ((o1->getObjectType() == fcl::OBJECT_TYPE::OT_GEOM && o2->getObjectType() == fcl::OBJECT_TYPE::OT_BVH) || (o1->getObjectType() == fcl::OBJECT_TYPE::OT_BVH && o2->getObjectType() == fcl::OBJECT_TYPE::OT_GEOM)) // || (o1->getObjectType() == fcl::OBJECT_TYPE::OT_BVH && o2->getObjectType() == fcl::OBJECT_TYPE::OT_BVH))
383  {
384  // WARNING("As of 0.5.94, this function does not work for primitive-vs-mesh and vice versa. Do not expect the contact points or distances to be accurate at all.");
385  }
386 
387  // Some of the logic below is copied from Drake
388  std::vector<fcl::Contactd> contacts;
389  tmp_res.getContacts(contacts);
390  if (!contacts.empty())
391  {
392  size_t deepest_penetration_depth_index = -1;
393  double deepest_penetration_depth = -1;
394  for (size_t i = 0; i < contacts.size(); ++i)
395  {
396  if (std::abs(contacts[i].penetration_depth) > deepest_penetration_depth)
397  {
398  deepest_penetration_depth = std::abs(contacts[i].penetration_depth);
399  deepest_penetration_depth_index = i;
400  }
401  }
402 
403  const fcl::Contactd& contact = tmp_res.getContact(deepest_penetration_depth_index);
404  // By convention, Drake requires the contact normal to point out of B and
405  // into A. FCL uses the opposite convention.
406  fcl::Vector3d normal = -contact.normal;
407 
408  // Signed distance is negative when penetration depth is positive.
409  double signed_distance = -std::abs(contact.penetration_depth);
410 
411  if (signed_distance > 0) ThrowPretty("In collision but positive signed distance? " << signed_distance);
412 
413  // FCL returns a single contact point, but PointPair expects two, one on
414  // the surface of body A (Ac) and one on the surface of body B (Bc).
415  // Choose points along the line defined by the contact point and normal,
416  // equi-distant to the contact point. Recall that signed_distance is
417  // strictly non-positive, so signed_distance * normal points out of
418  // A and into B.
419  const fcl::Vector3d p_WAc{contact.pos + 0.5 * signed_distance * normal};
420  const fcl::Vector3d p_WBc{contact.pos - 0.5 * signed_distance * normal};
421 
422  p.distance = signed_distance;
423  p.normal1 = -contact.normal;
424  p.normal2 = contact.normal;
425 
426  p.contact1 = p_WAc;
427  p.contact2 = p_WBc;
428 
429  data->Distance = std::min(data->Distance, p.distance);
430  data->proxies.push_back(p);
431 
432  return;
433  }
434  else
435  {
436  ThrowPretty("[This should not happen] In contact but did not return any contact points.");
437  }
438  }
439 
440  // Step 2: If not in collision, run old distance logic.
441  data->request.enable_nearest_points = true;
442  data->request.enable_signed_distance = true; // Added in FCL 0.6.0 (i.e., >0.5.90)
443  data->request.distance_tolerance = 1e-6;
444  data->request.gjk_solver_type = fcl::GST_LIBCCD;
445  data->result.clear();
446 
447  double min_dist = fcl::distance(o1, o2, data->request, data->result);
448 
449  // If -1 is returned, the returned query is a touching contact (or not implemented).
450  bool touching_contact = false;
451  p.distance = min_dist;
452  if (min_dist != data->result.min_distance)
453  {
454  // This can mean this has not been implemented and results may be arbitrary.
455  HIGHLIGHT_NAMED("Discrepancy", min_dist << " vs " << data->result.min_distance);
456  }
457 
458  if (min_dist == -1 || std::abs(min_dist) < 1e-9)
459  {
460  touching_contact = true;
461  p.distance = 0.0;
462  }
463 
464  KDL::Vector c1, c2;
465 
466  // We need some special treatise here, cf. https://github.com/flexible-collision-library/fcl/issues/171#issuecomment-413368821
467  // Case 3: Primitive vs Mesh - both returned in local frame, and additionally swapped.
468  if (o1->getObjectType() == fcl::OBJECT_TYPE::OT_GEOM && o2->getObjectType() == fcl::OBJECT_TYPE::OT_BVH)
469  {
470  // NOTE! The nearest points are in the wrong order now
471  c1 = KDL::Vector(data->result.nearest_points[1](0), data->result.nearest_points[1](1), data->result.nearest_points[1](2));
472  c2 = KDL::Vector(data->result.nearest_points[0](0), data->result.nearest_points[0](1), data->result.nearest_points[0](2));
473  }
474  // The default case that, in theory, should work for all cases.
475  else
476  {
477  c1 = KDL::Vector(data->result.nearest_points[0](0), data->result.nearest_points[0](1), data->result.nearest_points[0](2));
478  c2 = KDL::Vector(data->result.nearest_points[1](0), data->result.nearest_points[1](1), data->result.nearest_points[1](2));
479  }
480 
481  // Check if NaN
482  if (std::isnan(c1(0)) || std::isnan(c1(1)) || std::isnan(c1(2)) || std::isnan(c2(0)) || std::isnan(c2(1)) || std::isnan(c2(2)))
483  {
484  // LIBCCD queries require unreasonably high tolerances, i.e. we may not
485  // be able to compute contacts because one of those borderline cases.
486  // Hence, when we encounter a NaN for a _sphere_, we will replace it
487  // with the shape centre.
488  if (data->request.gjk_solver_type == fcl::GST_LIBCCD)
489  {
490  HIGHLIGHT_NAMED("computeDistanceLibCCD",
491  "Contact1 between " << p.e1->segment.getName() << " and " << p.e2->segment.getName() << " contains NaN"
492  << ", where ShapeType1: " << p.e1->shape->type << " and ShapeType2: " << p.e2->shape->type << " and distance: " << p.distance << " and solver: " << data->request.gjk_solver_type);
493  // To avoid downstream issues, replace contact point with shape centre
494  if ((std::isnan(c1(0)) || std::isnan(c1(1)) || std::isnan(c1(2))) && p.e1->shape->type == shapes::ShapeType::SPHERE) c1 = p.e1->frame.p;
495  if ((std::isnan(c2(0)) || std::isnan(c2(1)) || std::isnan(c2(2))) && p.e2->shape->type == shapes::ShapeType::SPHERE) c2 = p.e1->frame.p;
496  }
497  else
498  {
499  // TODO(#277): Any other NaN is a serious issue which we should investigate separately, so display helpful error message:
500  HIGHLIGHT_NAMED("ComputeDistance",
501  "Contact1 between " << p.e1->segment.getName() << " and " << p.e2->segment.getName() << " contains NaN"
502  << ", where ShapeType1: " << p.e1->shape->type << " and ShapeType2: " << p.e2->shape->type << " and distance: " << p.distance << " and solver: " << data->request.gjk_solver_type);
503  HIGHLIGHT("c1:" << data->result.nearest_points[0](0) << "," << data->result.nearest_points[0](1) << "," << data->result.nearest_points[0](2));
504  HIGHLIGHT("c2:" << data->result.nearest_points[1](0) << "," << data->result.nearest_points[1](1) << "," << data->result.nearest_points[1](2));
505  }
506  }
507 
508  p.contact1 = Eigen::Map<Eigen::Vector3d>(c1.data);
509  p.contact2 = Eigen::Map<Eigen::Vector3d>(c2.data);
510 
511  // On touching contact, the normal would be ill-defined. Thus, use the shape centre of the opposite shape as a proxy contact.
512  if (touching_contact)
513  {
514  c1 = p.e2->frame.p;
515  c2 = p.e1->frame.p;
516  }
517 
518  KDL::Vector n1 = c2 - c1;
519  KDL::Vector n2 = c1 - c2;
520  n1.Normalize();
521  n2.Normalize();
522  p.normal1 = Eigen::Map<Eigen::Vector3d>(n1.data);
523  p.normal2 = Eigen::Map<Eigen::Vector3d>(n2.data);
524 
525  data->Distance = std::min(data->Distance, p.distance);
526  data->proxies.push_back(p);
527 }
528 
529 bool CollisionSceneFCLLatest::CollisionCallbackDistance(fcl::CollisionObjectd* o1, fcl::CollisionObjectd* o2, void* data, double& dist)
530 {
531  DistanceData* data_ = reinterpret_cast<DistanceData*>(data);
532 
533  if (!IsAllowedToCollide(o1, o2, data_->self, data_->scene)) return false;
534  ComputeDistance(o1, o2, data_);
535  return false;
536 }
537 
538 bool CollisionSceneFCLLatest::IsStateValid(bool self, double safe_distance)
539 {
541 
542  CollisionData data(this);
543  data.self = self;
544  data.safe_distance = safe_distance;
546  return !data.result.isCollision();
547 }
548 
549 bool CollisionSceneFCLLatest::IsCollisionFree(const std::string& o1, const std::string& o2, double safe_distance)
550 {
552 
553  // TODO: Redo this logic using prior built maps
554  std::vector<fcl::CollisionObjectd*> shapes1;
555  std::vector<fcl::CollisionObjectd*> shapes2;
556  for (fcl::CollisionObjectd* o : fcl_objects_)
557  {
558  std::shared_ptr<KinematicElement> e = kinematic_elements_[reinterpret_cast<long>(o->getUserData())].lock();
559 
560  // TODO: These following two lines fuzzy the definition of what o1 and o2 are: They can be either the name of the link (e.g., base_link) or the name of the collision object (e.g., base_link_collision_0). We should standardise the API on either!
561  if (e->segment.getName() == o1 || e->parent.lock()->segment.getName() == o1) shapes1.push_back(o);
562  if (e->segment.getName() == o2 || e->parent.lock()->segment.getName() == o2) shapes2.push_back(o);
563  }
564  if (shapes1.size() == 0) ThrowPretty("Can't find object '" << o1 << "'!");
565  if (shapes2.size() == 0) ThrowPretty("Can't find object '" << o2 << "'!");
566  CollisionData data(this);
567  data.safe_distance = safe_distance;
568  for (fcl::CollisionObjectd* s1 : shapes1)
569  {
570  for (fcl::CollisionObjectd* s2 : shapes2)
571  {
572  CheckCollision(s1, s2, &data);
573  if (data.result.isCollision()) return false;
574  }
575  }
576  return true;
577 }
578 
579 std::vector<CollisionProxy> CollisionSceneFCLLatest::GetCollisionDistance(bool self)
580 {
582 
583  DistanceData data(this);
584  data.self = self;
586  return data.proxies;
587 }
588 
589 std::vector<CollisionProxy> CollisionSceneFCLLatest::GetCollisionDistance(const std::string& o1, const std::string& o2)
590 {
592 
593  // TODO: Redo logic with prior built maps.
594  std::vector<fcl::CollisionObjectd*> shapes1;
595  std::vector<fcl::CollisionObjectd*> shapes2;
596  for (fcl::CollisionObjectd* o : fcl_objects_)
597  {
598  std::shared_ptr<KinematicElement> e = kinematic_elements_[reinterpret_cast<long>(o->getUserData())].lock();
599 
600  // TODO: These following two lines fuzzy the definition of what o1 and o2 are: They can be either the name of the link (e.g., base_link) or the name of the collision object (e.g., base_link_collision_0). We should standardise the API on either!
601  if (e->segment.getName() == o1 || e->parent.lock()->segment.getName() == o1) shapes1.push_back(o);
602  if (e->segment.getName() == o2 || e->parent.lock()->segment.getName() == o2) shapes2.push_back(o);
603  }
604  if (shapes1.size() == 0) ThrowPretty("Can't find object '" << o1 << "'!");
605  if (shapes2.size() == 0) ThrowPretty("Can't find object '" << o2 << "'!");
606  DistanceData data(this);
607  for (fcl::CollisionObjectd* s1 : shapes1)
608  {
609  for (fcl::CollisionObjectd* s2 : shapes2)
610  {
611  ComputeDistance(s1, s2, &data);
612  }
613  }
614  return data.proxies;
615 }
616 
617 std::vector<CollisionProxy> CollisionSceneFCLLatest::GetCollisionDistance(const std::string& o1, const bool& self)
618 {
619  return GetCollisionDistance(o1, self, false);
620 }
621 
623  const std::string& o1, const bool& self, const bool& disable_collision_scene_update)
624 {
625  if (!always_externally_updated_collision_scene_ && !disable_collision_scene_update) UpdateCollisionObjectTransforms();
626 
627  std::vector<fcl::CollisionObjectd*> shapes1;
628  std::vector<fcl::CollisionObjectd*> shapes2;
629  DistanceData data(this);
630  data.self = self;
631 
632  // Iterate over all fcl_objects_ to find all collision links that belong to
633  // object o1
634  for (fcl::CollisionObjectd* o : fcl_objects_)
635  {
636  std::shared_ptr<KinematicElement> e = kinematic_elements_[reinterpret_cast<long>(o->getUserData())].lock();
637  // TODO: These following two lines fuzzy the definition of what o1 and o2 are: They can be either the name of the link (e.g., base_link) or the name of the collision object (e.g., base_link_collision_0). We should standardise the API on either!
638  if (e->segment.getName() == o1 || e->parent.lock()->segment.getName() == o1)
639  shapes1.push_back(o);
640  }
641 
642  // Iterate over all fcl_objects_ to find all objects o1 is allowed to collide
643  // with
644  for (fcl::CollisionObjectd* o : fcl_objects_)
645  {
646  std::shared_ptr<KinematicElement> e = kinematic_elements_[reinterpret_cast<long>(o->getUserData())].lock();
647  // Collision Object does not belong to o1
648  if (e->segment.getName() != o1 || e->parent.lock()->segment.getName() != o1)
649  {
650  bool allowedToCollide = false;
651  for (fcl::CollisionObjectd* o1_shape : shapes1)
652  {
653  if (IsAllowedToCollide(o1_shape, o, data.self, data.scene))
654  {
655  allowedToCollide = true;
656  break;
657  }
658  }
659 
660  if (allowedToCollide) shapes2.push_back(o);
661  }
662  }
663 
664  // There are no objects o1 is allowed to collide with, return the empty proxies vector
665  if (shapes1.size() == 0 || shapes2.size() == 0) return data.proxies;
666 
667  for (fcl::CollisionObjectd* s1 : shapes1)
668  {
669  for (fcl::CollisionObjectd* s2 : shapes2)
670  {
671  ComputeDistance(s1, s2, &data);
672  }
673  }
674  return data.proxies;
675 }
676 
677 std::vector<CollisionProxy> CollisionSceneFCLLatest::GetCollisionDistance(const std::vector<std::string>& objects, const bool& self)
678 {
680 
681  std::vector<CollisionProxy> proxies;
682  for (const auto& o1 : objects)
683  AppendVector(proxies, GetCollisionDistance(o1, self, true));
684 
685  return proxies;
686 }
687 
688 std::vector<CollisionProxy> CollisionSceneFCLLatest::GetRobotToRobotCollisionDistance(double check_margin)
689 {
690  DistanceData data(this);
691  data.self = true;
692 
693  // For each robot collision object to each robot collision object
694  for (auto it1 : fcl_robot_objects_map_)
695  {
696  for (auto it2 : fcl_robot_objects_map_)
697  {
698  if (IsAllowedToCollide(it1.first, it2.first, true))
699  {
700  // Both it1.second and it2.second are vectors of collision objects, so we need to iterate through each:
701  for (auto o1 : it1.second)
702  {
703  for (auto o2 : it2.second)
704  {
705  // Check whether the AABB is less than the check_margin, if so, perform a collision distance call
706  if (o1->getAABB().distance(o2->getAABB()) < check_margin)
707  {
708  ComputeDistance(o1, o2, &data);
709  }
710  }
711  }
712  }
713  }
714  }
715  return data.proxies;
716 }
717 
718 std::vector<CollisionProxy> CollisionSceneFCLLatest::GetRobotToWorldCollisionDistance(double check_margin)
719 {
720  DistanceData data(this);
721  data.self = false;
722 
723  // For each robot collision object to each robot collision object
724  for (auto it1 : fcl_robot_objects_map_)
725  {
726  for (auto it2 : fcl_world_objects_map_)
727  {
728  if (IsAllowedToCollide(it1.first, it2.first, false))
729  {
730  // Both it1.second and it2.second are vectors of collision objects, so we need to iterate through each:
731  for (auto o1 : it1.second)
732  {
733  for (auto o2 : it2.second)
734  {
735  // Check whether the AABB is less than the check_margin, if so, perform a collision distance call
736  if (o1->getAABB().distance(o2->getAABB()) < check_margin)
737  {
738  ComputeDistance(o1, o2, &data);
739  }
740  }
741  }
742  }
743  }
744  }
745  return data.proxies;
746 }
747 
748 Eigen::Vector3d CollisionSceneFCLLatest::GetTranslation(const std::string& name)
749 {
750  auto element = GetKinematicElementFromMapByName(name);
751  return Eigen::Map<Eigen::Vector3d>(element->frame.p.data);
752 }
753 
755 {
757 }
758 
760 {
762 }
763 
765  const std::string& o1, const KDL::Frame& tf1_beg, const KDL::Frame& tf1_end,
766  const std::string& o2, const KDL::Frame& tf2_beg, const KDL::Frame& tf2_end)
767 {
769 
771 
772  fcl::CollisionObjectd* shape1 = nullptr;
773  fcl::CollisionObjectd* shape2 = nullptr;
774 
775  for (fcl::CollisionObjectd* o : fcl_objects_)
776  {
777  std::shared_ptr<KinematicElement> e = kinematic_elements_[reinterpret_cast<long>(o->getUserData())].lock();
778  if (e->segment.getName() == o1)
779  {
780  shape1 = o;
781  ret.e1 = e;
782  }
783 
784  if (e->segment.getName() == o2)
785  {
786  shape2 = o;
787  ret.e2 = e;
788  }
789  }
790 
791  if (shape1 == nullptr) ThrowPretty("o1 not found.");
792  if (shape2 == nullptr) ThrowPretty("o2 not found.");
793 
794  CollisionData data(this);
795  const bool allowedToCollide = IsAllowedToCollide(shape1, shape2, data.self, data.scene);
796 
797  if (!allowedToCollide)
798  {
799  ret.in_collision = false;
800  ret.time_of_contact = 1.0;
801  return ret;
802  }
803 
804  fcl::Transform3d tf1_beg_fcl = transformKDLToFCL(tf1_beg);
805  fcl::Transform3d tf1_end_fcl = transformKDLToFCL(tf1_end);
806  fcl::Transform3d tf2_beg_fcl = transformKDLToFCL(tf2_beg);
807  fcl::Transform3d tf2_end_fcl = transformKDLToFCL(tf2_end);
808 
809  if (!tf1_beg_fcl.matrix().allFinite())
810  {
811  std::stringstream ss;
812  ss << std::setprecision(20);
813  ss << "[tf1_beg_fcl] is not finite\n"
814  << tf1_beg_fcl.matrix() << "\n"
815  << ToString(tf1_beg) << "\n";
816  throw std::logic_error(ss.str());
817  }
818  if (!tf1_end_fcl.matrix().allFinite())
819  {
820  std::stringstream ss;
821  ss << std::setprecision(20);
822  ss << "[tf1_end_fcl] is not finite\n"
823  << tf1_end_fcl.matrix() << "\n"
824  << ToString(tf1_end) << "\n";
825  throw std::logic_error(ss.str());
826  }
827  if (!tf2_beg_fcl.matrix().allFinite())
828  {
829  std::stringstream ss;
830  ss << std::setprecision(20);
831  ss << "[tf2_beg_fcl] is not finite\n"
832  << tf2_beg_fcl.matrix() << "\n"
833  << ToString(tf2_beg) << "\n";
834  throw std::logic_error(ss.str());
835  }
836  if (!tf2_end_fcl.matrix().allFinite())
837  {
838  std::stringstream ss;
839  ss << std::setprecision(20);
840  ss << "[tf2_end_fcl] is not finite\n"
841  << tf2_end_fcl.matrix() << "\n"
842  << ToString(tf2_end) << "\n";
843  throw std::logic_error(ss.str());
844  }
845 
846  // If neither object has motion, only ran a normal collision check.
847  // HIGHLIGHT_NAMED("tf1_beg_fcl", "\n" << tf1_beg_fcl.matrix());
848  // HIGHLIGHT_NAMED("tf1_end_fcl", "\n" << tf1_end_fcl.matrix());
849  // HIGHLIGHT_NAMED("tf2_beg_fcl", "\n" << tf2_beg_fcl.matrix());
850  // HIGHLIGHT_NAMED("tf2_end_fcl", "\n" << tf2_end_fcl.matrix());
851  // if (tf1_beg_fcl.isApprox(tf1_end_fcl) && tf2_beg_fcl.isApprox(tf2_end_fcl))
852  // {
853  // HIGHLIGHT("Yeah, no motion here.");
854  // }
855 
856  fcl::ContinuousCollisionRequestd request = fcl::ContinuousCollisionRequestd();
857 
858 #ifdef CONTINUOUS_COLLISION_USE_ADVANCED_SETTINGS
859  request.num_max_iterations = 100; // default 10
860  request.toc_err = 1e-5; // default 1e-4
861 
862  // GST_LIBCCD, GST_INDEP
863  // request.gjk_solver_type = fcl::GST_INDEP;
864 
865  // CCDM_TRANS, CCDM_LINEAR, CCDM_SCREW, CCDM_SPLINE
866  request.ccd_motion_type = fcl::CCDMotionType::CCDM_SCREW;
867 
868  // CCDC_NAIVE, CCDC_CONSERVATIVE_ADVANCEMENT, CCDC_RAY_SHOOTING, CCDC_POLYNOMIAL_SOLVER
869  // As of 2018-06-27, only CCDC_NAIVE appears to work reliably on both primitives and meshes.
870  // Cf. https://github.com/flexible-collision-library/fcl/issues/120
871  request.ccd_solver_type = fcl::CCDC_NAIVE;
872 
873  // If both are primitives, let's use conservative advancement
874  if (shape1->getObjectType() == fcl::OBJECT_TYPE::OT_GEOM && shape2->getObjectType() == fcl::OBJECT_TYPE::OT_GEOM)
875  {
876  request.ccd_solver_type = fcl::CCDC_CONSERVATIVE_ADVANCEMENT;
877  }
878 #endif
879 
880  fcl::ContinuousCollisionResultd result;
881  double time_of_contact = fcl::continuousCollide(
882  shape1->collisionGeometry().get(), tf1_beg_fcl, tf1_end_fcl,
883  shape2->collisionGeometry().get(), tf2_beg_fcl, tf2_end_fcl,
884  request, result);
885 
886 #ifdef CONTINUOUS_COLLISION_DEBUG
887  HIGHLIGHT_NAMED("ContinuousCollisionResult", "return=" << time_of_contact << " is_collide: " << result.is_collide << " time_of_contact: " << result.time_of_contact << " contact_tf1: " << result.contact_tf1.translation().transpose() << " contact_tf2: " << result.contact_tf2.translation().transpose());
888 #else
889  (void)time_of_contact;
890 #endif
891 
892  if (result.is_collide)
893  {
894  if (!result.contact_tf1.matrix().allFinite())
895  {
896  std::stringstream ss;
897  ss << "result.contact_tf1 is not finite\n"
898  << result.contact_tf1.matrix() << "\n";
899  throw std::logic_error(ss.str());
900  }
901  if (!result.contact_tf2.matrix().allFinite())
902  {
903  std::stringstream ss;
904  ss << "result.contact_tf2 is not finite\n"
905  << result.contact_tf2.matrix() << "\n";
906  throw std::logic_error(ss.str());
907  }
908  }
909 
910  ret.in_collision = result.is_collide;
911  ret.time_of_contact = result.time_of_contact;
912 
913  // If in contact, compute contact point
914  if (ret.in_collision)
915  {
916 #if 0
917  // Run distance query
918  fcl::DistanceRequestd distance_req;
919  fcl::DistanceResultd distance_res;
920  distance_req.enable_nearest_points = true;
921  distance_req.enable_signed_distance = true;
922  // distance_req.distance_tolerance = 1e-6;
923  distance_req.gjk_solver_type = fcl::GST_LIBCCD;
924  // distance_res.clear();
925 
926  double min_dist = fcl::distance(shape1, shape2, distance_req, distance_res);
927 
928  ret.penetration_depth = distance_res.min_distance;
929  ret.contact_pos = distance_res.nearest_points[0];
930  ret.contact_normal = (distance_res.nearest_points[1] - distance_res.nearest_points[0]).normalized();
931 #else
932  fcl::CollisionRequestd contact_req;
933  contact_req.enable_contact = true;
934  contact_req.num_max_contacts = 1000;
935  contact_req.gjk_tolerance = 2e-12;
936  contact_req.gjk_solver_type = fcl::GJKSolverType::GST_LIBCCD;
937  fcl::CollisionResultd contact_res;
938  size_t num_contacts = fcl::collide(shape1->collisionGeometry().get(), result.contact_tf1, shape2->collisionGeometry().get(), result.contact_tf2, contact_req, contact_res);
939  if (num_contacts > 0)
940  {
941  std::vector<fcl::Contactd> contacts;
942  contact_res.getContacts(contacts);
943  ret.penetration_depth = std::numeric_limits<double>::min();
944  for (const auto& contact : contacts)
945  {
946  if (contact.penetration_depth > ret.penetration_depth)
947  {
948  if (!contact.pos.allFinite())
949  {
950  std::stringstream ss;
951  ss << std::setprecision(20);
952  ss << "Error with configuration"
953  << "\n Shape 1: " << shape1->collisionGeometry().get()
954  << "\n X_FS1\n"
955  << result.contact_tf1.matrix()
956  << "\n Shape 2: " << shape2->collisionGeometry().get()
957  << "\n X_FS2\n"
958  << result.contact_tf2.matrix()
959  << "\n Solver: " << contact_req.gjk_solver_type;
960  throw std::logic_error(ss.str());
961  }
962 
963  ret.penetration_depth = contact.penetration_depth;
964  ret.contact_pos = contact.pos;
965  ret.contact_normal = -contact.normal;
966  }
967  }
968 #ifdef CONTINUOUS_COLLISION_DEBUG
969  HIGHLIGHT_NAMED("In collision, contacts: ", num_contacts << ", penetration=" << ret.penetration_depth << ", pos: " << ret.contact_pos.transpose());
970 #endif
971  }
972  else
973  {
974  ret.penetration_depth = 0.0;
975  ret.in_collision = false;
976  }
977 #endif
978  }
979 
980  transformFCLToKDL(result.contact_tf1, ret.contact_tf1);
981  transformFCLToKDL(result.contact_tf2, ret.contact_tf2);
982 
983 #ifdef CONTINUOUS_COLLISION_DEBUG
984  if (!ret.contact_pos.allFinite())
985  {
986  ThrowPretty("Contact position is not finite!");
987  }
988 #endif
989 
990  return ret;
991 }
992 } // namespace exotica
bool IsAllowedToCollide(const std::string &o1, const std::string &o2, const bool &self) override
std::vector< std::shared_ptr< fcl::CollisionObjectd > > fcl_cache_
std::vector< std::string > GetCollisionRobotLinks() override
Gets the collision robot links.
#define HIGHLIGHT(x)
void UpdateCollisionObjectTransforms() override
Updates collision object transformations from the kinematic tree.
std::weak_ptr< Scene > scene_
static bool CollisionCallbackDistance(fcl::CollisionObjectd *o1, fcl::CollisionObjectd *o2, void *data, double &dist)
Eigen::Vector3d normal2
bool IsRobotLink(std::shared_ptr< KinematicElement > e)
double Normalize(double eps=epsilon)
Eigen::Vector3d GetTranslation(const std::string &name) override
XmlRpcServer s
bool replace_primitive_shapes_with_meshes_
#define ThrowPretty(m)
static void CheckCollision(fcl::CollisionObjectd *o1, fcl::CollisionObjectd *o2, CollisionData *data)
std::vector< Key > GetKeysFromMap(const std::map< Key, Val > &map)
bool IsCollisionFree(const std::string &o1, const std::string &o2, double safe_distance=0.0) override
std::shared_ptr< Shape > ShapePtr
std::shared_ptr< KinematicElement > e1
std::shared_ptr< fcl::CollisionObjectd > ConstructFclCollisionObject(long i, std::shared_ptr< KinematicElement > element)
std::vector< fcl::CollisionObjectd * > fcl_objects_
Rotation M
std::vector< CollisionProxy > GetCollisionDistance(bool self) override
Computes collision distances.
TFSIMD_FORCE_INLINE Vector3 normalized() const
std::string ToString(const KDL::Frame &s)
fcl::Transform3d transformKDLToFCL(const KDL::Frame &frame)
Eigen::Vector3d contact1
std::shared_ptr< T > ToStdPtr(const boost::shared_ptr< T > &p)
bool IsStateValid(bool self=true, double safe_distance=0.0) override
Check if the whole robot is valid (collision only).
#define REGISTER_COLLISION_SCENE_TYPE(TYPE, DERIV)
Eigen::Vector3d contact2
double data[3]
std::vector< CollisionProxy > GetRobotToWorldCollisionDistance(double check_margin) override
std::vector< CollisionProxy > GetRobotToRobotCollisionDistance(double check_margin) override
ContinuousCollisionProxy ContinuousCollisionCheck(const std::string &o1, const KDL::Frame &tf1_beg, const KDL::Frame &tf1_end, const std::string &o2, const KDL::Frame &tf2_beg, const KDL::Frame &tf2_end) override
Performs a continuous collision check between two objects with a linear interpolation between two giv...
std::shared_ptr< KinematicElement > GetKinematicElementFromMapByName(const std::string &frame_name)
std::map< std::string, std::weak_ptr< KinematicElement > > kinematic_elements_map_
bool always_externally_updated_collision_scene_
void transformFCLToKDL(const fcl::Transform3d &tf, KDL::Frame &frame)
std::shared_ptr< fcl::BroadPhaseCollisionManagerd > broad_phase_collision_manager_
Mesh * createMeshFromShape(const Sphere &sphere)
#define HIGHLIGHT_NAMED(name, x)
void UpdateCollisionObjects(const std::map< std::string, std::weak_ptr< KinematicElement >> &objects) override
Creates the collision scene from kinematic elements.
AllowedCollisionMatrix acm_
std::map< std::string, std::vector< fcl::CollisionObjectd * > > fcl_world_objects_map_
static void ComputeDistance(fcl::CollisionObjectd *o1, fcl::CollisionObjectd *o2, DistanceData *data)
std::vector< std::string > GetCollisionWorldLinks() override
Gets the collision world links.
bool getAllowedCollision(const std::string &name1, const std::string &name2) const
std::shared_ptr< KinematicElement > e2
void AppendVector(std::vector< Val > &orig, const std::vector< Val > &extra)
static bool CollisionCallback(fcl::CollisionObjectd *o1, fcl::CollisionObjectd *o2, void *data)
std::map< std::string, std::vector< fcl::CollisionObjectd * > > fcl_robot_objects_map_
std::vector< std::weak_ptr< KinematicElement > > kinematic_elements_
std::shared_ptr< KinematicElement > e2
std::shared_ptr< KinematicElement > e1
std::map< std::string, std::vector< fcl::CollisionObjectd * > > fcl_objects_map_
Eigen::Vector3d normal1
double data[9]


exotica_collision_scene_fcl_latest
Author(s):
autogenerated on Sat Apr 10 2021 02:35:24