moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
collision_common.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, Jia Pan */
36
38#include <geometric_shapes/shapes.h>
40#include <rclcpp/logger.hpp>
41#include <rclcpp/logging.hpp>
43
44#if (MOVEIT_FCL_VERSION >= FCL_VERSION_CHECK(0, 6, 0))
45#include <fcl/geometry/bvh/BVH_model.h>
46#include <fcl/geometry/octree/octree.h>
47#else
48#include <fcl/BVH/BVH_model.h>
49#include <fcl/shape/geometric_shapes.h>
50#include <fcl/octree.h>
51#endif
52
53#include <memory>
54#include <type_traits>
55#include <mutex>
56
57namespace collision_detection
58{
59namespace
60{
61rclcpp::Logger getLogger()
62{
63 return moveit::getLogger("moveit.core.moveit_collision_detection_fcl");
64}
65} // namespace
66
68{
69 CollisionData* cdata = reinterpret_cast<CollisionData*>(data);
70 if (cdata->done_)
71 return true;
72 const CollisionGeometryData* cd1 = static_cast<const CollisionGeometryData*>(o1->collisionGeometry()->getUserData());
73 const CollisionGeometryData* cd2 = static_cast<const CollisionGeometryData*>(o2->collisionGeometry()->getUserData());
74
75 // do not collision check geoms part of the same object / link / attached body
76 if (cd1->sameObject(*cd2))
77 return false;
78
79 // If active components are specified
80 if (cdata->active_components_only_)
81 {
82 const moveit::core::LinkModel* l1 =
84 cd1->ptr.link :
85 (cd1->type == BodyTypes::ROBOT_ATTACHED ? cd1->ptr.ab->getAttachedLink() : nullptr);
86 const moveit::core::LinkModel* l2 =
88 cd2->ptr.link :
89 (cd2->type == BodyTypes::ROBOT_ATTACHED ? cd2->ptr.ab->getAttachedLink() : nullptr);
90
91 // If neither of the involved components is active
92 if ((!l1 || cdata->active_components_only_->find(l1) == cdata->active_components_only_->end()) &&
93 (!l2 || cdata->active_components_only_->find(l2) == cdata->active_components_only_->end()))
94 return false;
95 }
96
97 // use the collision matrix (if any) to avoid certain collision checks
99 bool always_allow_collision = false;
100 if (cdata->acm_)
101 {
103 bool found = cdata->acm_->getAllowedCollision(cd1->getID(), cd2->getID(), type);
104 if (found)
105 {
106 // if we have an entry in the collision matrix, we read it
107 if (type == AllowedCollision::ALWAYS)
108 {
109 always_allow_collision = true;
110 if (cdata->req_->verbose)
111 {
112 RCLCPP_DEBUG(getLogger(),
113 "Collision between '%s' (type '%s') and '%s' (type '%s') is always allowed. "
114 "No contacts are computed.",
115 cd1->getID().c_str(), cd1->getTypeString().c_str(), cd2->getID().c_str(),
116 cd2->getTypeString().c_str());
117 }
118 }
119 else if (type == AllowedCollision::CONDITIONAL)
120 {
121 cdata->acm_->getAllowedCollision(cd1->getID(), cd2->getID(), dcf);
122 if (cdata->req_->verbose)
123 {
124 RCLCPP_DEBUG(getLogger(), "Collision between '%s' and '%s' is conditionally allowed", cd1->getID().c_str(),
125 cd2->getID().c_str());
126 }
127 }
128 }
129 }
130
131 // check if a link is touching an attached object
133 {
134 const std::set<std::string>& tl = cd2->ptr.ab->getTouchLinks();
135 if (tl.find(cd1->getID()) != tl.end())
136 {
137 always_allow_collision = true;
138 if (cdata->req_->verbose)
139 {
140 RCLCPP_DEBUG(getLogger(), "Robot link '%s' is allowed to touch attached object '%s'. No contacts are computed.",
141 cd1->getID().c_str(), cd2->getID().c_str());
142 }
143 }
144 }
145 else if (cd2->type == BodyTypes::ROBOT_LINK && cd1->type == BodyTypes::ROBOT_ATTACHED)
146 {
147 const std::set<std::string>& tl = cd1->ptr.ab->getTouchLinks();
148 if (tl.find(cd2->getID()) != tl.end())
149 {
150 always_allow_collision = true;
151 if (cdata->req_->verbose)
152 {
153 RCLCPP_DEBUG(getLogger(), "Robot link '%s' is allowed to touch attached object '%s'. No contacts are computed.",
154 cd2->getID().c_str(), cd1->getID().c_str());
155 }
156 }
157 }
158
159 // bodies attached to the same link should not collide
160 // If one of the attached objects lists the other in touch links set, then collisions are also allowed
162 {
163 if (cd1->ptr.ab->getAttachedLink() == cd2->ptr.ab->getAttachedLink())
164 {
165 always_allow_collision = true;
166 }
167 else
168 {
169 const std::set<std::string>& tl1 = cd1->ptr.ab->getTouchLinks();
170 const std::set<std::string>& tl2 = cd2->ptr.ab->getTouchLinks();
171 if (tl1.find(cd2->getID()) != tl1.end() || tl2.find(cd1->getID()) != tl2.end())
172 {
173 always_allow_collision = true;
174 }
175 }
176 if (always_allow_collision && cdata->req_->verbose)
177 {
178 RCLCPP_DEBUG(getLogger(),
179 "Attached object '%s' is allowed to touch attached object '%s'. No contacts are computed.",
180 cd2->getID().c_str(), cd1->getID().c_str());
181 }
182 }
183
184 // if collisions are always allowed, we are done
185 if (always_allow_collision)
186 return false;
187
188 if (cdata->req_->verbose)
189 {
190 RCLCPP_DEBUG(getLogger(), "Actually checking collisions between %s and %s", cd1->getID().c_str(),
191 cd2->getID().c_str());
192 }
193
194 // see if we need to compute a contact
195 std::size_t want_contact_count{ 0 };
196 if (cdata->req_->contacts)
197 {
198 if (cdata->res_->contact_count < cdata->req_->max_contacts)
199 {
200 std::size_t have;
201 if (cd1->getID() < cd2->getID())
202 {
203 std::pair<std::string, std::string> cp(cd1->getID(), cd2->getID());
204 have = cdata->res_->contacts.find(cp) != cdata->res_->contacts.end() ? cdata->res_->contacts[cp].size() : 0;
205 }
206 else
207 {
208 std::pair<std::string, std::string> cp(cd2->getID(), cd1->getID());
209 have = cdata->res_->contacts.find(cp) != cdata->res_->contacts.end() ? cdata->res_->contacts[cp].size() : 0;
210 }
211 if (have < cdata->req_->max_contacts_per_pair)
212 {
213 want_contact_count =
214 std::min(cdata->req_->max_contacts_per_pair - have, cdata->req_->max_contacts - cdata->res_->contact_count);
215 }
216 }
217 }
218
219 if (dcf)
220 {
221 // if we have a decider for allowed contacts, we need to look at all the contacts
222 bool enable_cost = cdata->req_->cost;
223 std::size_t num_max_cost_sources = cdata->req_->max_cost_sources;
224 bool enable_contact = true;
225 fcl::CollisionResultd col_result;
226 int num_contacts = fcl::collide(o1, o2,
227 fcl::CollisionRequestd(std::numeric_limits<size_t>::max(), enable_contact,
228 num_max_cost_sources, enable_cost),
229 col_result);
230 if (num_contacts > 0)
231 {
232 if (cdata->req_->verbose)
233 {
234 RCLCPP_INFO(getLogger(),
235 "Found %d contacts between '%s' and '%s'. "
236 "These contacts will be evaluated to check if they are accepted or not",
237 num_contacts, cd1->getID().c_str(), cd2->getID().c_str());
238 }
239 Contact c;
240 const std::pair<std::string, std::string>& pc = cd1->getID() < cd2->getID() ?
241 std::make_pair(cd1->getID(), cd2->getID()) :
242 std::make_pair(cd2->getID(), cd1->getID());
243 for (int i = 0; i < num_contacts; ++i)
244 {
245 fcl2contact(col_result.getContact(i), c);
246 // if the contact is not allowed, we have a collision
247 if (!dcf(c))
248 {
249 // store the contact, if it is needed
250 if (want_contact_count > 0)
251 {
252 --want_contact_count;
253 cdata->res_->contacts[pc].push_back(c);
254 cdata->res_->contact_count++;
255 if (cdata->req_->verbose)
256 {
257 RCLCPP_INFO(getLogger(), "Found unacceptable contact between '%s' and '%s'. Contact was stored.",
258 cd1->getID().c_str(), cd2->getID().c_str());
259 }
260 }
261 else if (cdata->req_->verbose)
262 {
263 RCLCPP_INFO(getLogger(),
264 "Found unacceptable contact between '%s' (type '%s') and '%s' "
265 "(type '%s'). Contact was stored.",
266 cd1->getID().c_str(), cd1->getTypeString().c_str(), cd2->getID().c_str(),
267 cd2->getTypeString().c_str());
268 }
269 cdata->res_->collision = true;
270 if (want_contact_count == 0)
271 break;
272 }
273 }
274 }
275
276 if (enable_cost)
277 {
278 std::vector<fcl::CostSourced> cost_sources;
279 col_result.getCostSources(cost_sources);
280
281 CostSource cs;
282 for (auto& cost_source : cost_sources)
283 {
284 fcl2costsource(cost_source, cs);
285 cdata->res_->cost_sources.insert(cs);
286 while (cdata->res_->cost_sources.size() > cdata->req_->max_cost_sources)
287 cdata->res_->cost_sources.erase(--cdata->res_->cost_sources.end());
288 }
289 }
290 }
291 else
292 {
293 if (want_contact_count > 0)
294 {
295 // otherwise, we need to compute more things
296 bool enable_cost = cdata->req_->cost;
297 std::size_t num_max_cost_sources = cdata->req_->max_cost_sources;
298 bool enable_contact{ true };
299
300 fcl::CollisionResultd col_result;
301 int num_contacts =
302 fcl::collide(o1, o2,
303 fcl::CollisionRequestd(want_contact_count, enable_contact, num_max_cost_sources, enable_cost),
304 col_result);
305 if (num_contacts > 0)
306 {
307 int num_contacts_initial = num_contacts;
308
309 // make sure we don't get more contacts than we want
310 if (want_contact_count >= static_cast<std::size_t>(num_contacts))
311 {
312 want_contact_count -= num_contacts;
313 }
314 else
315 {
316 num_contacts = want_contact_count;
317 want_contact_count = 0;
318 }
319
320 if (cdata->req_->verbose)
321 {
322 RCLCPP_INFO(getLogger(),
323 "Found %d contacts between '%s' (type '%s') and '%s' (type '%s'), "
324 "which constitute a collision. %d contacts will be stored",
325 num_contacts_initial, cd1->getID().c_str(), cd1->getTypeString().c_str(), cd2->getID().c_str(),
326 cd2->getTypeString().c_str(), num_contacts);
327 }
328
329 const std::pair<std::string, std::string>& pc = cd1->getID() < cd2->getID() ?
330 std::make_pair(cd1->getID(), cd2->getID()) :
331 std::make_pair(cd2->getID(), cd1->getID());
332 cdata->res_->collision = true;
333 for (int i = 0; i < num_contacts; ++i)
334 {
335 Contact c;
336 fcl2contact(col_result.getContact(i), c);
337 cdata->res_->contacts[pc].push_back(c);
338 cdata->res_->contact_count++;
339 }
340 }
341
342 if (enable_cost)
343 {
344 std::vector<fcl::CostSourced> cost_sources;
345 col_result.getCostSources(cost_sources);
346
347 CostSource cs;
348 for (auto& cost_source : cost_sources)
349 {
350 fcl2costsource(cost_source, cs);
351 cdata->res_->cost_sources.insert(cs);
352 while (cdata->res_->cost_sources.size() > cdata->req_->max_cost_sources)
353 cdata->res_->cost_sources.erase(--cdata->res_->cost_sources.end());
354 }
355 }
356 }
357 else
358 {
359 bool enable_cost = cdata->req_->cost;
360 std::size_t num_max_cost_sources = cdata->req_->max_cost_sources;
361 bool enable_contact = false;
362 fcl::CollisionResultd col_result;
363 int num_contacts = fcl::collide(
364 o1, o2, fcl::CollisionRequestd(1, enable_contact, num_max_cost_sources, enable_cost), col_result);
365 if (num_contacts > 0)
366 {
367 cdata->res_->collision = true;
368 if (cdata->req_->verbose)
369 {
370 RCLCPP_INFO(getLogger(),
371 "Found a contact between '%s' (type '%s') and '%s' (type '%s'), "
372 "which constitutes a collision. "
373 "Contact information is not stored.",
374 cd1->getID().c_str(), cd1->getTypeString().c_str(), cd2->getID().c_str(),
375 cd2->getTypeString().c_str());
376 }
377 }
378
379 if (enable_cost)
380 {
381 std::vector<fcl::CostSourced> cost_sources;
382 col_result.getCostSources(cost_sources);
383
384 CostSource cs;
385 for (auto& cost_source : cost_sources)
386 {
387 fcl2costsource(cost_source, cs);
388 cdata->res_->cost_sources.insert(cs);
389 while (cdata->res_->cost_sources.size() > cdata->req_->max_cost_sources)
390 cdata->res_->cost_sources.erase(--cdata->res_->cost_sources.end());
391 }
392 }
393 }
394 }
395
396 if (cdata->res_->collision)
397 {
398 if (!cdata->req_->contacts || cdata->res_->contact_count >= cdata->req_->max_contacts)
399 {
400 if (!cdata->req_->cost)
401 cdata->done_ = true;
402 if (cdata->req_->verbose)
403 {
404 RCLCPP_INFO(getLogger(),
405 "Collision checking is considered complete (collision was found and %u contacts are stored)",
406 static_cast<unsigned int>(cdata->res_->contact_count));
407 }
408 }
409 }
410
411 if (!cdata->done_ && cdata->req_->is_done)
412 {
413 cdata->done_ = cdata->req_->is_done(*cdata->res_);
414 if (cdata->done_ && cdata->req_->verbose)
415 {
416 RCLCPP_INFO(getLogger(),
417 "Collision checking is considered complete due to external callback. "
418 "%s was found. %u contacts are stored.",
419 cdata->res_->collision ? "Collision" : "No collision",
420 static_cast<unsigned int>(cdata->res_->contact_count));
421 }
422 }
423
424 return cdata->done_;
425}
426
431{
432 using ShapeKey = shapes::ShapeConstWeakPtr;
433 using ShapeMap = std::map<ShapeKey, FCLGeometryConstPtr, std::owner_less<ShapeKey>>;
434
436 {
437 }
438
439 void bumpUseCount(bool force = false)
440 {
441 clean_count_++;
442
443 // clean-up for cache (we don't want to keep infinitely large number of weak ptrs stored)
444 if (clean_count_ > MAX_CLEAN_COUNT || force)
445 {
446 clean_count_ = 0;
447 for (auto it = map_.begin(); it != map_.end();)
448 {
449 auto nit = it;
450 ++nit;
451 if (it->first.expired())
452 map_.erase(it);
453 it = nit;
454 }
455 // RCLCPP_DEBUG(getLogger(), "Cleaning up cache for FCL objects that correspond to static
456 // shapes. Cache size
457 // reduced from %u
458 // to %u", from, static_cast<unsigned int>(map_.size()));
459 }
460 }
461
462 static const unsigned int MAX_CLEAN_COUNT = 100; // every this many uses of the cache, a cleaning operation is
463 // executed (this is only removal of expired entries)
466
468 unsigned int clean_count_;
469};
470
471bool distanceCallback(fcl::CollisionObjectd* o1, fcl::CollisionObjectd* o2, void* data, double& /*min_dist*/)
472{
473 DistanceData* cdata = reinterpret_cast<DistanceData*>(data);
474
475 const CollisionGeometryData* cd1 = static_cast<const CollisionGeometryData*>(o1->collisionGeometry()->getUserData());
476 const CollisionGeometryData* cd2 = static_cast<const CollisionGeometryData*>(o2->collisionGeometry()->getUserData());
477
478 // do not distance check for geoms part of the same object / link / attached body
479 if (cd1->sameObject(*cd2))
480 return false;
481
482 // If active components are specified
483 if (cdata->req->active_components_only)
484 {
485 const moveit::core::LinkModel* l1 =
487 cd1->ptr.link :
488 (cd1->type == BodyTypes::ROBOT_ATTACHED ? cd1->ptr.ab->getAttachedLink() : nullptr);
489 const moveit::core::LinkModel* l2 =
491 cd2->ptr.link :
492 (cd2->type == BodyTypes::ROBOT_ATTACHED ? cd2->ptr.ab->getAttachedLink() : nullptr);
493
494 // If neither of the involved components is active
495 if ((!l1 || cdata->req->active_components_only->find(l1) == cdata->req->active_components_only->end()) &&
496 (!l2 || cdata->req->active_components_only->find(l2) == cdata->req->active_components_only->end()))
497 {
498 return false;
499 }
500 }
501
502 // use the collision matrix (if any) to avoid certain distance checks
503 bool always_allow_collision = false;
504 if (cdata->req->acm)
505 {
507 bool found = cdata->req->acm->getAllowedCollision(cd1->getID(), cd2->getID(), type);
508 if (found)
509 {
510 // if we have an entry in the collision matrix, we read it
511 if (type == AllowedCollision::ALWAYS)
512 {
513 always_allow_collision = true;
514 if (cdata->req->verbose)
515 {
516 RCLCPP_DEBUG(getLogger(), "Collision between '%s' and '%s' is always allowed. No distances are computed.",
517 cd1->getID().c_str(), cd2->getID().c_str());
518 }
519 }
520 }
521 }
522
523 // check if a link is touching an attached object
525 {
526 const std::set<std::string>& tl = cd2->ptr.ab->getTouchLinks();
527 if (tl.find(cd1->getID()) != tl.end())
528 {
529 always_allow_collision = true;
530 if (cdata->req->verbose)
531 {
532 RCLCPP_DEBUG(getLogger(),
533 "Robot link '%s' is allowed to touch attached object '%s'. No distances are computed.",
534 cd1->getID().c_str(), cd2->getID().c_str());
535 }
536 }
537 }
538 else if (cd2->type == BodyTypes::ROBOT_LINK && cd1->type == BodyTypes::ROBOT_ATTACHED)
539 {
540 const std::set<std::string>& tl = cd1->ptr.ab->getTouchLinks();
541 if (tl.find(cd2->getID()) != tl.end())
542 {
543 always_allow_collision = true;
544 if (cdata->req->verbose)
545 {
546 RCLCPP_DEBUG(getLogger(),
547 "Robot link '%s' is allowed to touch attached object '%s'. No distances are computed.",
548 cd2->getID().c_str(), cd1->getID().c_str());
549 }
550 }
551 }
552
553 if (always_allow_collision)
554 {
555 return false;
556 }
557 if (cdata->req->verbose)
558 {
559 RCLCPP_DEBUG(getLogger(), "Actually checking collisions between %s and %s", cd1->getID().c_str(),
560 cd2->getID().c_str());
561 }
562
563 double dist_threshold = cdata->req->distance_threshold;
564
565 const std::pair<const std::string&, const std::string&> pc =
566 cd1->getID() < cd2->getID() ? std::make_pair(std::cref(cd1->getID()), std::cref(cd2->getID())) :
567 std::make_pair(std::cref(cd2->getID()), std::cref(cd1->getID()));
568
569 DistanceMap::iterator it = cdata->res->distances.find(pc);
570
571 // GLOBAL search: for efficiency, distance_threshold starts at the smallest distance between any pairs found so far
572 if (cdata->req->type == DistanceRequestType::GLOBAL)
573 {
574 dist_threshold = cdata->res->minimum_distance.distance;
575 }
576 // Check if a distance between this pair has been found yet. Decrease threshold_distance if so, to narrow the search
577 else if (it != cdata->res->distances.end())
578 {
579 if (cdata->req->type == DistanceRequestType::LIMITED)
580 {
581 // If at the limit for a given pair just return
582 if (it->second.size() >= cdata->req->max_contacts_per_body)
583 {
584 return cdata->done;
585 }
586 }
587 else if (cdata->req->type == DistanceRequestType::SINGLE)
588 {
589 dist_threshold = it->second[0].distance;
590 }
591 }
592
593 fcl::DistanceResultd fcl_result;
594 fcl_result.min_distance = dist_threshold;
595 // fcl::distance segfaults when given an octree with a null root pointer (using FCL 0.6.1)
596 if ((o1->getObjectType() == fcl::OT_OCTREE &&
597 !std::static_pointer_cast<const fcl::OcTreed>(o1->collisionGeometry())->getRoot()) ||
598 (o2->getObjectType() == fcl::OT_OCTREE &&
599 !std::static_pointer_cast<const fcl::OcTreed>(o2->collisionGeometry())->getRoot()))
600 {
601 return false;
602 }
603 double distance = fcl::distance(o1, o2, fcl::DistanceRequestd(cdata->req->enable_nearest_points), fcl_result);
604
605 // Check if either object is already in the map. If not add it or if present
606 // check to see if the new distance is closer. If closer remove the existing
607 // one and add the new distance information.
608 if (distance < dist_threshold)
609 {
610 // thread_local storage makes this variable persistent. We do not clear it at every iteration because all members
611 // get overwritten.
612 thread_local DistanceResultsData dist_result;
613 dist_result.distance = fcl_result.min_distance;
614
615 // Careful here: Get the collision geometry data again, since FCL might
616 // swap o1 and o2 in the result.
617 const CollisionGeometryData* res_cd1 = static_cast<const CollisionGeometryData*>(fcl_result.o1->getUserData());
618 const CollisionGeometryData* res_cd2 = static_cast<const CollisionGeometryData*>(fcl_result.o2->getUserData());
619
620#if (MOVEIT_FCL_VERSION >= FCL_VERSION_CHECK(0, 6, 0))
621 dist_result.nearest_points[0] = fcl_result.nearest_points[0];
622 dist_result.nearest_points[1] = fcl_result.nearest_points[1];
623#else
624 dist_result.nearest_points[0] = Eigen::Map<const Eigen::Vector3d>(fcl_result.nearest_points[0].data.vs);
625 dist_result.nearest_points[1] = Eigen::Map<const Eigen::Vector3d>(fcl_result.nearest_points[1].data.vs);
626#endif
627 dist_result.link_names[0] = res_cd1->getID();
628 dist_result.link_names[1] = res_cd2->getID();
629 dist_result.body_types[0] = res_cd1->type;
630 dist_result.body_types[1] = res_cd2->type;
631 if (cdata->req->enable_nearest_points)
632 {
633 dist_result.normal = (dist_result.nearest_points[1] - dist_result.nearest_points[0]).normalized();
634 }
635
636 if (distance <= 0 && cdata->req->enable_signed_distance)
637 {
638 dist_result.nearest_points[0].setZero();
639 dist_result.nearest_points[1].setZero();
640 dist_result.normal.setZero();
641
642 fcl::CollisionRequestd coll_req;
643 thread_local fcl::CollisionResultd coll_res;
644 coll_res.clear(); // thread_local storage makes the variable persistent. Ensure that it is cleared!
645 coll_req.enable_contact = true;
646 coll_req.num_max_contacts = 200;
647 std::size_t contacts = fcl::collide(o1, o2, coll_req, coll_res);
648 if (contacts > 0)
649 {
650 double max_dist = 0;
651 int max_index = 0;
652 for (std::size_t i = 0; i < contacts; ++i)
653 {
654 const fcl::Contactd& contact = coll_res.getContact(i);
655 if (contact.penetration_depth > max_dist)
656 {
657 max_dist = contact.penetration_depth;
658 max_index = i;
659 }
660 }
661
662 const fcl::Contactd& contact = coll_res.getContact(max_index);
663 dist_result.distance = -contact.penetration_depth;
664
665#if (MOVEIT_FCL_VERSION >= FCL_VERSION_CHECK(0, 6, 0))
666 dist_result.nearest_points[0] = contact.pos;
667 dist_result.nearest_points[1] = contact.pos;
668#else
669 dist_result.nearest_points[0] = Eigen::Map<const Eigen::Vector3d>(contact.pos.data.vs);
670 dist_result.nearest_points[1] = Eigen::Map<const Eigen::Vector3d>(contact.pos.data.vs);
671#endif
672
673 if (cdata->req->enable_nearest_points)
674 {
675#if (MOVEIT_FCL_VERSION >= FCL_VERSION_CHECK(0, 6, 0))
676 Eigen::Vector3d normal(contact.normal);
677#else
678 Eigen::Vector3d normal(contact.normal.data.vs);
679#endif
680
681 // Check order of o1/o2 again, we might need to flip the normal
682 if (contact.o1 == o1->collisionGeometry().get())
683 {
684 dist_result.normal = normal;
685 }
686 else
687 {
688 dist_result.normal = -normal;
689 }
690 }
691 }
692 }
693
694 if (dist_result.distance < cdata->res->minimum_distance.distance)
695 {
696 cdata->res->minimum_distance = dist_result;
697 }
698
699 if (dist_result.distance <= 0)
700 {
701 cdata->res->collision = true;
702 }
703
704 if (cdata->req->type != DistanceRequestType::GLOBAL)
705 {
706 if (it == cdata->res->distances.end())
707 {
708 std::vector<DistanceResultsData> data;
709 data.reserve(cdata->req->type == DistanceRequestType::SINGLE ? 1 : cdata->req->max_contacts_per_body);
710 data.push_back(dist_result);
711 cdata->res->distances.insert(std::make_pair(pc, data));
712 }
713 else
714 {
715 if (cdata->req->type == DistanceRequestType::ALL)
716 {
717 it->second.push_back(dist_result);
718 }
719 else if (cdata->req->type == DistanceRequestType::SINGLE)
720 {
721 if (dist_result.distance < it->second[0].distance)
722 it->second[0] = dist_result;
723 }
724 else if (cdata->req->type == DistanceRequestType::LIMITED)
725 {
726 assert(it->second.size() < cdata->req->max_contacts_per_body);
727 it->second.push_back(dist_result);
728 }
729 }
730 }
731
732 if (!cdata->req->enable_signed_distance && cdata->res->collision)
733 {
734 cdata->done = true;
735 }
736 }
737
738 return cdata->done;
739}
740
741/* Templated function to get a different cache for each of the template arguments combinations.
742 *
743 * The returned cache is a quasi-singleton for each thread as it is created \e thread_local. */
744template <typename BV, typename T>
746{
747 /* The cache is created thread_local, that is each thread calling
748 * this quasi-singleton function will get its own instance. Once
749 * the thread joins/exits, the cache gets deleted.
750 * Reasoning is that multi-threaded planners (eg OMPL) or user-code
751 * will often need to do collision checks with the same object
752 * simultaneously (especially true for attached objects). Having only
753 * one global cache leads to many cache misses. Also as the cache can
754 * only be accessed by one thread we don't need any locking.
755 */
756 static thread_local FCLShapeCache cache;
757 return cache;
758}
759
765template <typename BV, typename T>
766FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, const T* data, int shape_index)
767{
768 using ShapeKey = shapes::ShapeConstWeakPtr;
769 using ShapeMap = std::map<ShapeKey, FCLGeometryConstPtr, std::owner_less<ShapeKey>>;
770
771 FCLShapeCache& cache = getShapeCache<BV, T>();
772
773 shapes::ShapeConstWeakPtr wptr(shape);
774 {
775 ShapeMap::const_iterator cache_it = cache.map_.find(wptr);
776 if (cache_it != cache.map_.end())
777 {
778 if (cache_it->second->collision_geometry_data_->ptr.raw == data)
779 {
780 // RCLCPP_DEBUG(getLogger(), "Collision data structures for object %s retrieved from
781 // cache.",
782 // cache_it->second->collision_geometry_data_->getID().c_str());
783 return cache_it->second;
784 }
785 else if (cache_it->second.unique())
786 {
787 const_cast<FCLGeometry*>(cache_it->second.get())->updateCollisionGeometryData(data, shape_index, false);
788 // RCLCPP_DEBUG(getLogger(), "Collision data structures for object %s retrieved from
789 // cache after updating
790 // the source
791 // object.", cache_it->second->collision_geometry_data_->getID().c_str());
792 return cache_it->second;
793 }
794 }
795 }
796
797 // attached objects could have previously been World::Object; we try to move them
798 // from their old cache to the new one, if possible. the code is not pretty, but should help
799 // when we attach/detach objects that are in the world
800 if (std::is_same<T, moveit::core::AttachedBody>::value)
801 {
802 // get the cache that corresponds to objects; maybe this attached object used to be a world object
803 FCLShapeCache& othercache = getShapeCache<BV, World::Object>();
804
805 // attached bodies could be just moved from the environment.
806 auto cache_it = othercache.map_.find(wptr);
807 if (cache_it != othercache.map_.end())
808 {
809 if (cache_it->second.unique())
810 {
811 // remove from old cache
812 FCLGeometryConstPtr obj_cache = cache_it->second;
813 othercache.map_.erase(cache_it);
814 // update the CollisionGeometryData; nobody has a pointer to this, so we can safely modify it
815 const_cast<FCLGeometry*>(obj_cache.get())->updateCollisionGeometryData(data, shape_index, true);
816
817 // RCLCPP_DEBUG(getLogger(), "Collision data structures for attached body %s retrieved
818 // from the cache for
819 // world objects.",
820 // obj_cache->collision_geometry_data_->getID().c_str());
821
822 // add to the new cache
823 cache.map_[wptr] = obj_cache;
824 cache.bumpUseCount();
825 return obj_cache;
826 }
827 }
828 }
829 else
830 // world objects could have previously been attached objects; we try to move them
831 // from their old cache to the new one, if possible. the code is not pretty, but should help
832 // when we attach/detach objects that are in the world
833 if (std::is_same<T, World::Object>::value)
834 {
835 // get the cache that corresponds to objects; maybe this attached object used to be a world object
836 FCLShapeCache& othercache = getShapeCache<BV, moveit::core::AttachedBody>();
837
838 // attached bodies could be just moved from the environment.
839 auto cache_it = othercache.map_.find(wptr);
840 if (cache_it != othercache.map_.end())
841 {
842 if (cache_it->second.unique())
843 {
844 // remove from old cache
845 FCLGeometryConstPtr obj_cache = cache_it->second;
846 othercache.map_.erase(cache_it);
847
848 // update the CollisionGeometryData; nobody has a pointer to this, so we can safely modify it
849 const_cast<FCLGeometry*>(obj_cache.get())->updateCollisionGeometryData(data, shape_index, true);
850
851 // RCLCPP_DEBUG(getLogger(), "Collision data structures for world object %s retrieved
852 // from the cache for
853 // attached
854 // bodies.",
855 // obj_cache->collision_geometry_data_->getID().c_str());
856
857 // add to the new cache
858 cache.map_[wptr] = obj_cache;
859 cache.bumpUseCount();
860 return obj_cache;
861 }
862 }
863 }
864
865 fcl::CollisionGeometryd* cg_g = nullptr;
866 // handle cases individually
867 switch (shape->type)
868 {
869 case shapes::PLANE:
870 {
871 const shapes::Plane* p = static_cast<const shapes::Plane*>(shape.get());
872 cg_g = new fcl::Planed(p->a, p->b, p->c, p->d);
873 }
874 break;
875 case shapes::SPHERE:
876 {
877 const shapes::Sphere* s = static_cast<const shapes::Sphere*>(shape.get());
878 cg_g = new fcl::Sphered(s->radius);
879 }
880 break;
881 case shapes::BOX:
882 {
883 const shapes::Box* s = static_cast<const shapes::Box*>(shape.get());
884 const double* size = s->size;
885 cg_g = new fcl::Boxd(size[0], size[1], size[2]);
886 }
887 break;
888 case shapes::CYLINDER:
889 {
890 const shapes::Cylinder* s = static_cast<const shapes::Cylinder*>(shape.get());
891 cg_g = new fcl::Cylinderd(s->radius, s->length);
892 }
893 break;
894 case shapes::CONE:
895 {
896 const shapes::Cone* s = static_cast<const shapes::Cone*>(shape.get());
897 cg_g = new fcl::Coned(s->radius, s->length);
898 }
899 break;
900 case shapes::MESH:
901 {
902 auto g = new fcl::BVHModel<BV>();
903 const shapes::Mesh* mesh = static_cast<const shapes::Mesh*>(shape.get());
904 if (mesh->vertex_count > 0 && mesh->triangle_count > 0)
905 {
906 std::vector<fcl::Triangle> tri_indices(mesh->triangle_count);
907 for (unsigned int i = 0; i < mesh->triangle_count; ++i)
908 {
909 tri_indices[i] =
910 fcl::Triangle(mesh->triangles[3 * i], mesh->triangles[3 * i + 1], mesh->triangles[3 * i + 2]);
911 }
912
913 std::vector<fcl::Vector3d> points(mesh->vertex_count);
914 for (unsigned int i = 0; i < mesh->vertex_count; ++i)
915 points[i] = fcl::Vector3d(mesh->vertices[3 * i], mesh->vertices[3 * i + 1], mesh->vertices[3 * i + 2]);
916
917 g->beginModel();
918 g->addSubModel(points, tri_indices);
919 g->endModel();
920 }
921 cg_g = g;
922 }
923 break;
924 case shapes::OCTREE:
925 {
926 const shapes::OcTree* g = static_cast<const shapes::OcTree*>(shape.get());
927 cg_g = new fcl::OcTreed(g->octree);
928 }
929 break;
930 default:
931 RCLCPP_ERROR(getLogger(), "This shape type (%d) is not supported using FCL yet", static_cast<int>(shape->type));
932 cg_g = nullptr;
933 }
934
935 if (cg_g)
936 {
937 cg_g->computeLocalAABB();
938 FCLGeometryConstPtr res = std::make_shared<const FCLGeometry>(cg_g, data, shape_index);
939 cache.map_[wptr] = res;
940 cache.bumpUseCount();
941 return res;
942 }
943 return FCLGeometryConstPtr();
944}
945
946FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, const moveit::core::LinkModel* link,
947 int shape_index)
948{
949 return createCollisionGeometry<fcl::OBBRSSd, moveit::core::LinkModel>(shape, link, shape_index);
950}
951
952FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, const moveit::core::AttachedBody* ab,
953 int shape_index)
954{
955 return createCollisionGeometry<fcl::OBBRSSd, moveit::core::AttachedBody>(shape, ab, shape_index);
956}
957
958FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, const World::Object* obj)
959{
960 return createCollisionGeometry<fcl::OBBRSSd, World::Object>(shape, obj, 0);
961}
962
965template <typename BV, typename T>
966FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, double scale, double padding,
967 const T* data, int shape_index)
968{
969 if (fabs(scale - 1.0) <= std::numeric_limits<double>::epsilon() &&
970 fabs(padding) <= std::numeric_limits<double>::epsilon())
971 {
972 return createCollisionGeometry<BV, T>(shape, data, shape_index);
973 }
974 else
975 {
976 shapes::ShapePtr scaled_shape(shape->clone());
977 scaled_shape->scaleAndPadd(scale, padding);
978 return createCollisionGeometry<BV, T>(scaled_shape, data, shape_index);
979 }
980}
981
982FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, double scale, double padding,
983 const moveit::core::LinkModel* link, int shape_index)
984{
985 return createCollisionGeometry<fcl::OBBRSSd, moveit::core::LinkModel>(shape, scale, padding, link, shape_index);
986}
987
988FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, double scale, double padding,
989 const moveit::core::AttachedBody* ab, int shape_index)
990{
991 return createCollisionGeometry<fcl::OBBRSSd, moveit::core::AttachedBody>(shape, scale, padding, ab, shape_index);
992}
993
994FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr& shape, double scale, double padding,
995 const World::Object* obj)
996{
997 return createCollisionGeometry<fcl::OBBRSSd, World::Object>(shape, scale, padding, obj, 0);
998}
999
1001{
1002 FCLShapeCache& cache1 = getShapeCache<fcl::OBBRSSd, World::Object>();
1003 {
1004 cache1.bumpUseCount(true);
1005 }
1006 FCLShapeCache& cache2 = getShapeCache<fcl::OBBRSSd, moveit::core::AttachedBody>();
1007 {
1008 cache2.bumpUseCount(true);
1009 }
1010}
1011
1012void CollisionData::enableGroup(const moveit::core::RobotModelConstPtr& robot_model)
1013{
1014 if (robot_model->hasJointModelGroup(req_->group_name))
1015 {
1016 active_components_only_ = &robot_model->getJointModelGroup(req_->group_name)->getUpdatedLinkModelsSet();
1017 }
1018 else
1019 {
1020 active_components_only_ = nullptr;
1021 }
1022}
1023
1025{
1026 std::vector<fcl::CollisionObjectd*> collision_objects(collision_objects_.size());
1027 for (std::size_t i = 0; i < collision_objects_.size(); ++i)
1028 collision_objects[i] = collision_objects_[i].get();
1029 if (!collision_objects.empty())
1030 manager->registerObjects(collision_objects);
1031}
1032
1034{
1035 for (auto& collision_object : collision_objects_)
1036 manager->unregisterObject(collision_object.get());
1037}
1038
1040{
1041 collision_objects_.clear();
1042 collision_geometry_.clear();
1043}
1044} // namespace collision_detection
bool getAllowedCollision(const std::string &name1, const std::string &name2, DecideContactFn &fn) const
Get the allowed collision predicate between two elements. Return true if a predicate for entry is inc...
Object defining bodies that can be attached to robot links.
const LinkModel * getAttachedLink() const
Get the model of the link this body is attached to.
const std::set< std::string > & getTouchLinks() const
Get the links that the attached body is allowed to touch.
A link from the robot. Contains the constant transform applied to the link and its geometry.
@ CONDITIONAL
The collision is allowed depending on a predicate evaluated on the produced contact....
@ ALWAYS
Collisions between a particular pair of bodies does not imply that the robot configuration is in coll...
@ ROBOT_ATTACHED
A body attached to a robot link.
void fcl2contact(const fcl::Contactd &fc, Contact &c)
Transforms an FCL contact into a MoveIt contact point.
FCLGeometryConstPtr createCollisionGeometry(const shapes::ShapeConstPtr &shape, const moveit::core::LinkModel *link, int shape_index)
Create new FCLGeometry object out of robot link model.
FCLShapeCache & getShapeCache()
void cleanCollisionGeometryCache()
Increases the counter of the caches which can trigger the cleaning of expired entries from them.
bool collisionCallback(fcl::CollisionObjectd *o1, fcl::CollisionObjectd *o2, void *data)
Callback function used by the FCLManager used for each pair of collision objects to calculate object ...
std::function< bool(collision_detection::Contact &)> DecideContactFn
Signature of predicate that decides whether a contact is allowed or not (when AllowedCollision::Type ...
void fcl2costsource(const fcl::CostSourced &fcs, CostSource &cs)
Transforms the FCL internal representation to the MoveIt CostSource data structure.
bool distanceCallback(fcl::CollisionObjectd *o1, fcl::CollisionObjectd *o2, void *data, double &min_dist)
Callback function used by the FCLManager used for each pair of collision objects to calculate collisi...
fcl::CollisionObject CollisionObjectd
fcl::DistanceResult DistanceResultd
fcl::BroadPhaseCollisionManager BroadPhaseCollisionManagerd
fcl::Sphere Sphered
fcl::DistanceRequest DistanceRequestd
fcl::OcTree OcTreed
fcl::Box Boxd
fcl::CollisionGeometry CollisionGeometryd
fcl::Contact Contactd
fcl::CollisionResult CollisionResultd
fcl::Cylinder Cylinderd
fcl::CollisionRequest CollisionRequestd
fcl::Plane Planed
fcl::Cone Coned
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition logger.cpp:79
Data structure which is passed to the collision callback function of the collision manager.
const std::set< const moveit::core::LinkModel * > * active_components_only_
If the collision request includes a group name, this set contains the pointers to the link models tha...
const AllowedCollisionMatrix * acm_
The user-specified collision matrix (may be nullptr).
void enableGroup(const moveit::core::RobotModelConstPtr &robot_model)
Compute active_components_only_ based on the joint group specified in req_.
bool done_
Flag indicating whether collision checking is complete.
CollisionResult * res_
The user-specified response location.
const CollisionRequest * req_
The collision request passed by the user.
Wrapper around world, link and attached objects' geometry data.
bool sameObject(const CollisionGeometryData &other) const
Check if two CollisionGeometryData objects point to the same source object.
const std::string & getID() const
Returns the name which is saved in the member pointed to in ptr.
std::string getTypeString() const
Returns a string of the corresponding type.
const moveit::core::AttachedBody * ab
union collision_detection::CollisionGeometryData::@0 ptr
Points to the type of body which contains the geometry.
BodyType type
Indicates the body type of the object.
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.
std::size_t max_cost_sources
When costs are computed, this value defines how many of the top cost sources should be returned.
bool contacts
If true, compute contacts. Otherwise only a binary collision yes/no is reported.
bool cost
If true, a collision cost is computed.
std::size_t max_contacts
Overall maximum number of contacts to compute.
std::function< bool(const CollisionResult &)> is_done
Function call that decides whether collision detection should stop.
std::size_t max_contacts_per_pair
Maximum number of contacts to compute per pair of bodies (multiple bodies may be in contact at differ...
std::set< CostSource > cost_sources
These are the individual cost sources when costs are computed.
bool collision
True if collision was found, false otherwise.
std::size_t contact_count
Number of contacts returned.
Definition of a contact point.
When collision costs are computed, this structure contains information about the partial cost incurre...
Data structure which is passed to the distance callback function of the collision manager.
DistanceResult * res
Distance query results information.
const DistanceRequest * req
Distance query request information.
bool done
Indicates if distance query is finished.
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.
DistanceMap distances
A map of distance data for each link in the req.active_components_only.
bool collision
Indicates if two objects were in collision.
DistanceResultsData minimum_distance
ResultsData for the two objects with the minimum distance.
Generic representation of the distance information for a pair of objects.
BodyType body_types[2]
The object body type.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW Eigen::Vector3d nearest_points[2]
The nearest points.
std::string link_names[2]
The object link names.
double distance
The distance between two objects. If two objects are in collision, distance <= 0.
Bundles the CollisionGeometryData and FCL collision geometry representation into a single class.
std::vector< FCLCollisionObjectPtr > collision_objects_
void unregisterFrom(fcl::BroadPhaseCollisionManagerd *manager)
std::vector< FCLGeometryConstPtr > collision_geometry_
Geometry data corresponding to collision_objects_.
void registerTo(fcl::BroadPhaseCollisionManagerd *manager)
Cache for an arbitrary type of shape. It is assigned during the execution of createCollisionGeometry(...
ShapeMap map_
Map of weak pointers to the FCLGeometry.
static const unsigned int MAX_CLEAN_COUNT
unsigned int clean_count_
Counts cache usage and triggers clearing of cache when \m MAX_CLEAN_COUNT is exceeded.
std::map< ShapeKey, FCLGeometryConstPtr, std::owner_less< ShapeKey > > ShapeMap
A representation of an object.
Definition world.hpp:79