CostmapBuilder.cpp
Go to the documentation of this file.
1/**
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @author Fabian Reister ( fabian dot reister at kit dot edu )
17 * @date 2022
18 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19 * GNU General Public License
20 */
21
22#include "CostmapBuilder.h"
23
24#include <algorithm>
25#include <cstddef>
26#include <functional>
27#include <memory>
28#include <optional>
29#include <string>
30#include <variant>
31#include <vector>
32
33#include <boost/geometry/algorithms/convex_hull.hpp>
34#include <boost/geometry/algorithms/disjoint.hpp>
35#include <boost/geometry/geometries/box.hpp>
36
37#include <SimoxUtility/meta/enum/EnumNames.hpp>
38#include <VirtualRobot/CollisionDetection/CollisionChecker.h>
39#include <VirtualRobot/CollisionDetection/CollisionModel.h>
40#include <VirtualRobot/Nodes/RobotNode.h> // IWYU pragma: keep
41#include <VirtualRobot/Robot.h> // IWYU pragma: keep
42#include <VirtualRobot/RobotFactory.h>
43#include <VirtualRobot/SceneObjectSet.h>
44#include <VirtualRobot/VirtualRobot.h>
45
51
59
60#include <omp.h>
61
62#if COSTMAP_BUILDER_SIMOX_CONTROL
63#include <simox/control/environment/CollisionRobot.h>
64#include <simox/control/impl/simox/robot/Robot.h>
65#include <simox/control/impl/simox/utils/conversion.h>
66
67#include <hpp/fcl/broadphase/broadphase_dynamic_AABB_tree.h>
68#include <hpp/fcl/collision_object.h>
69#endif
70
72{
73
74#if COSTMAP_BUILDER_SIMOX_CONTROL
75 namespace sc = simox::control;
76#endif
77
78
79 // helper type for the visitor
80 template <class... Ts>
81 struct overloaded : Ts...
82 {
83 using Ts::operator()...;
84 };
85 // explicit deduction guide (not needed as of C++20)
86 template <class... Ts>
87 overloaded(Ts...) -> overloaded<Ts...>;
88
89 const simox::meta::EnumNames<CostmapBuilder::DistanceCalculator>
93
95 const VirtualRobot::SceneObjectSetPtr& obstacles,
96 const std::vector<VirtualRobot::RobotPtr>& articulatedObjects,
97 const std::vector<Room>& rooms,
98 const Costmap::Parameters& parameters,
99 const std::string& robotCollisonModelName,
100 const CostmapBuilderParams& builderParameters) :
101 robot(robot),
102 obstacles(obstacles),
103 articulatedObjects(articulatedObjects),
104 rooms(rooms),
105 parameters(parameters),
106 robotCollisionModelName(robotCollisonModelName),
107 builderParameters(builderParameters)
108 {
109 ARMARX_CHECK_NOT_NULL(robot) << "Robot must be set";
110 ARMARX_CHECK_NOT_NULL(obstacles);
111 ARMARX_CHECK(robot->hasRobotNode(robotCollisionModelName));
112
113 ARMARX_CHECK_NONNEGATIVE(this->builderParameters.numThreads);
114
115 const auto& enabledRooms = this->builderParameters.roomEnableList;
116 if (not enabledRooms.empty())
117 {
118 // if roomEnableList is specified, remove all other rooms from the provided list
119 this->rooms.erase(std::remove_if(this->rooms.begin(),
120 this->rooms.end(),
121 [&enabledRooms](const Room& room)
122 {
123 const bool roomIncluded =
124 std::find(enabledRooms.cbegin(),
125 enabledRooms.cend(),
126 room.name) != enabledRooms.cend();
127 if (not roomIncluded)
128 {
129 ARMARX_VERBOSE
130 << "Room " << room.name
131 << " was found but is not included";
132 }
133 return not roomIncluded;
134 }),
135 this->rooms.end());
136 }
137
138 if (builderParameters.restrictToRooms)
139 {
141 << "No rooms enabled, even though restrict to rooms is true";
142 }
143 }
144
145 Costmap
147 {
149 [](const armarx::core::time::Duration& duration)
150 { ARMARX_INFO << "Creating costmap took " << duration; });
151
152 ARMARX_INFO << articulatedObjects.size() << " articulated objects";
153 ARMARX_INFO << obstacles->getSize() << " objects";
154
155 const auto sceneBounds = computeSceneBounds(obstacles,
156 articulatedObjects,
157 init,
158 rooms,
159 parameters.sceneBoundsMargin,
160 builderParameters.restrictToRooms);
161 const auto grid = createUniformGrid(sceneBounds, parameters);
162
163 ARMARX_VERBOSE << "Created grid";
164 Costmap costmap(grid, parameters, sceneBounds);
165
166 if (builderParameters.restrictToRooms)
167 {
168 initializeEmptyMask(costmap);
169
171 ARMARX_VERBOSE << "Restricting to rooms:";
172 for (const auto& room : rooms)
173 {
174 ARMARX_VERBOSE << " - " << room.name;
175 }
176
177 invalidateOutsideRooms(rooms, costmap, builderParameters.robotFootprintRadius);
178
179 ARMARX_VERBOSE << "Restricted to rooms. Fraction of valid elements: "
180 << costmap.mask->cast<float>().sum() / costmap.mask->size();
181 }
182
183 {
185 [](const armarx::core::time::Duration& duration)
186 { ARMARX_INFO << "Filling costmap took " << duration; });
187
188 ARMARX_VERBOSE << "Filling grid with size (" << costmap.getGrid().rows() << "/"
189 << costmap.getGrid().cols() << ") and resolution "
190 << costmap.params().cellSize;
191 fillGridCosts(costmap);
192 ARMARX_VERBOSE << "Filled grid";
193 }
194
196 ARMARX_VERBOSE << "Initialized mask";
197
198 return costmap;
199 }
200
201 Costmap
203 {
205 [](const armarx::core::time::Duration& duration)
206 { ARMARX_INFO << "Extending costmap took " << duration; });
207
208 if (builderParameters.restrictToRooms)
209 {
210 // mask out any cell not within a room
211 ARMARX_VERBOSE << "Restricting to rooms";
212 invalidateOutsideRooms(rooms, costmap, builderParameters.robotFootprintRadius);
213 }
214
215 {
217 [](const armarx::core::time::Duration& duration)
218 { ARMARX_INFO << "Filling costmap took " << duration; });
219
220 ARMARX_VERBOSE << "Extending grid with size (" << costmap.getGrid().rows() << "/"
221 << costmap.getGrid().cols() << ") and resolution "
222 << costmap.params().cellSize;
223 extendGridCosts(costmap);
224 ARMARX_VERBOSE << "Filled grid";
225 }
226
228 ARMARX_VERBOSE << "Initialized mask";
229
230 return costmap;
231 }
232
233 void
235 {
236 costmap.mask = costmap.grid.array() > 0.F;
237
238 ARMARX_VERBOSE << "Update mask: Fraction of valid elements: "
239 << costmap.mask->cast<float>().sum() / costmap.mask->size();
240 }
241
242 void
243 CostmapBuilder::initializeEmptyMask(Costmap& costmap)
244 {
245 costmap.mask = costmap.grid.array() >= -42.F; // FIXME: magic number, just same size
246 costmap.mask->setOnes();
247
248 ARMARX_VERBOSE << "Initializing empty mask: Fraction of valid elements: "
249 << costmap.mask->cast<float>().sum() / costmap.mask->size();
250 }
251
252 Eigen::MatrixXf
254 const Costmap::Parameters& parameters)
255 {
257
258 ARMARX_VERBOSE << "Scene bounds are " << sceneBounds.min << " and " << sceneBounds.max;
259
260 //+1 for explicit rounding up
261 size_t c_x = (sceneBounds.max.x() - sceneBounds.min.x()) / parameters.cellSize + 1;
262 size_t c_y = (sceneBounds.max.y() - sceneBounds.min.y()) / parameters.cellSize + 1;
263
264 ARMARX_VERBOSE << "Grid size: " << c_x << ", " << c_y;
265
266 ARMARX_VERBOSE << "Resetting grid";
267 Eigen::MatrixXf grid(c_x, c_y);
268 grid.setZero();
269
270 return grid;
271 }
272
273 float
274 CostmapBuilder::computeCost(const Costmap::Position& position,
275 const CollisionSetup& collisionSetupVariant)
276 {
277 float cost = std::visit(
279 [&position](const CollisionSetupSC& collisionSetup) -> float
280 {
281 // SimoxControl implementation to calculate distance
282#if COSTMAP_BUILDER_SIMOX_CONTROL
283 ARMARX_CHECK_NOT_NULL(collisionSetup.robot);
284 ARMARX_CHECK_NOT_NULL(collisionSetup.collisionRobot);
285 ARMARX_CHECK_NOT_NULL(collisionSetup.obstacleCollisionManager);
286
287 // Remember: SimoxControl uses meters.
288 const core::Pose globalPose(Eigen::Translation3f(conv::to3D(position)));
289 collisionSetup.robot->setGlobalPose(
290 sc::simox::utils::from_simox(globalPose.matrix()), true);
291
292 collisionSetup.collisionRobot->update();
293 collisionSetup.collisionRobot->getCollisionManager()->update();
294
295 // The obstacles are static: their collision objects and the broadphase
296 // manager are built once in `initializeCollisionSetup` and never move, so
297 // there is nothing to update here.
298
299 hpp::fcl::DistanceCallBackDefault defaultCallback;
300 collisionSetup.obstacleCollisionManager->distance(
301 collisionSetup.collisionRobot->getCollisionManager(), &defaultCallback);
302
303 const double minDistance = defaultCallback.data.result.min_distance;
304
305 return static_cast<float>(std::max(minDistance, 0.) * 1000);
306#else
308 << "SimoxControl distance calculation requested but not supported. "
309 "Please compile with SimoxControl to use this calculation method.";
310 return 0.F;
311#endif
312 },
313 [&position, this](const CollisionSetupSx& collisionSetup) -> float
314 {
315 const auto& collisionRobot = collisionSetup.collisionRobot;
316 const auto& robotCollisionModel = collisionSetup.robotCollisionModel;
317
319 ARMARX_CHECK_NOT_NULL(collisionRobot);
320 ARMARX_CHECK_NOT_NULL(robotCollisionModel);
322 VirtualRobot::CollisionChecker::getGlobalCollisionChecker());
323
324 const VirtualRobot::SceneObjectSetPtr& actualObstacles = [&]()
325 {
326 if (collisionSetup.filteredObstacles)
327 {
328 return collisionSetup.filteredObstacles;
329 }
330 else
331 {
332 return this->obstacles;
333 }
334 }();
335 ARMARX_CHECK_NOT_NULL(actualObstacles);
336
337 const core::Pose globalPose(Eigen::Translation3f(conv::to3D(position)));
338 collisionRobot->setGlobalPose(globalPose.matrix());
339
340 // distance to non-articulated objects
341 float distanceNonArticulated = std::numeric_limits<float>::max();
342 if (actualObstacles->getSize() > 0)
343 {
344 distanceNonArticulated =
345 VirtualRobot::CollisionChecker::getGlobalCollisionChecker()
346 ->calculateDistance(robotCollisionModel, actualObstacles);
347 }
348
349 // distance to articulated objects
350 float distanceArticulatedMin = std::numeric_limits<float>::max();
351 for (const auto& articulatedObject : articulatedObjects)
352 {
353 for (const auto& colModel : articulatedObject->getCollisionModels())
354 {
355 const float distanceArticulated =
356 VirtualRobot::CollisionChecker::getGlobalCollisionChecker()
357 ->calculateDistance(robotCollisionModel, colModel);
358 distanceArticulatedMin =
359 std::min(distanceArticulated, distanceArticulatedMin);
360 }
361 }
362
363 return std::min(distanceNonArticulated, distanceArticulatedMin);
364
365
366 // Eigen::Vector3f P1;
367 // Eigen::Vector3f P2;
368 // int id1, id2;
369
370 // float minDistance = std::numeric_limits<float>::max();
371
372 // // TODO omp...
373 // for (size_t i = 0; i < obstacles->getSize(); i++)
374 // {
375 // // cheap collision check
376 // // VirtualRobot::BoundingBox obstacleBbox =
377 // // obstacles->getSceneObject(i)->getCollisionModel()->getBoundingBox(true);
378 // // if (not intersects(robotBbox, obstacleBbox))
379 // // {
380 // // continue;
381 // // }
382
383 // // precise collision check
384 // const float dist =
385 // VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance(
386 // robotCollisionModel,
387 // obstacles->getSceneObject(i)->getCollisionModel(),
388 // P1,
389 // P2,
390 // &id1,
391 // &id2);
392
393 // // check if objects collide
394 // if ((dist <= parameters.cellSize / 2) or
395 // VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->checkCollision(
396 // robotCollisionModel, obstacles->getSceneObject(i)->getCollisionModel()))
397 // {
398 // minDistance = 0;
399 // break;
400 // }
401
402 // minDistance = std::min(minDistance, dist);
403 // }
404 // // return n->position.x() >= sceneBounds.min.x() && n->position.x() <= sceneBounds.max.x() &&
405 // // n->position.y() >= sceneBounds.min.y() && n->position.y() <= sceneBounds.max.y();
406
407 // return minDistance;
408 },
409 [](const std::monostate& _) -> float
410 {
411 ARMARX_ERROR << "Invalid collision setup";
412 return 0.F;
413 }},
414 collisionSetupVariant);
415
416 // Cap distance at maxDistance
417 cost = std::min(cost, builderParameters.maxDistance);
418 return cost;
419 }
420
421 void
422 CostmapBuilder::fillGridCosts(Costmap& costmap)
423 {
424 applyFnToCostmap(costmap,
425 [&](const CollisionSetup& collisionSetup,
426 const std::optional<float> maskOpt,
427 const float /*cost*/,
428 const Costmap::Index& index) -> float
429 {
430 const auto position = costmap.toPositionGlobal(index);
431
432 // consider mask if available and skip if not valid
433 if (maskOpt.has_value())
434 {
435 if (not maskOpt.value())
436 {
437 // important to set a value <= 0 here, so updateMask wont override mask later
438 return 0.F;
439 }
440 }
441
442 if( builderParameters.fakeModeForTesting)
443 {
444 return builderParameters.maxDistance; // similar distance everywhere
445 }
446
447 return computeCost(position, collisionSetup);
448 });
449 }
450
451 void
452 CostmapBuilder::extendGridCosts(Costmap& costmap)
453 {
454 applyFnToCostmap(
455 costmap,
456 [&](const CollisionSetup& collisionSetup,
457 const std::optional<float> maskOpt,
458 const float cost,
459 const Costmap::Index& index) -> float
460 {
461 const auto position = costmap.toPositionGlobal(index);
462
463 // consider mask if available and skip if not valid
464 // as mask is updated to respect rooms above, this will also ignore areas outside any rooms
465 if (maskOpt.has_value())
466 {
467 if (not maskOpt.value())
468 {
469 return std::min(0.F, cost);
470 }
471 }
472
473 if (cost < 0.F)
474 {
475 return cost;
476 }
477
478 return std::min(cost, computeCost(position, collisionSetup));
479 });
480 }
481
482#if COSTMAP_BUILDER_SIMOX_CONTROL
483 CostmapBuilder::SharedObstacleColObjects
484 CostmapBuilder::buildSharedObstacleCollisionObjects() const
485 {
486 armarx::core::time::ScopedStopWatch sw(
487 [](const armarx::core::time::Duration& duration)
488 { ARMARX_INFO << "Converting obstacles to collision objects took " << duration; });
489
490 using ScRobot = sc::simox::robot::Robot;
491 using ScCollisionRobot = sc::environment::CollisionRobot<hpp::fcl::OBBRSS>;
492
493 SharedObstacleColObjects colObjects;
494
495 // Note: this must not be moved into the OpenMP region. The mesh extraction below reads
496 // the *shared* VirtualRobot::CollisionModel of the source scene object (its global pose
497 // in particular), while the vertices are transformed back to local using the pose of the
498 // per-robot node. Building this concurrently lets those two poses disagree, which bakes
499 // the obstacle's BVH at the wrong place for whichever thread lost the race.
500 const auto convert = [&colObjects](const VirtualRobot::RobotPtr& r)
501 {
502 try
503 {
504 const auto obstacle = ScRobot::CREATE_SIMPLE_WRAPPER(r);
505 ARMARX_CHECK_NOT_NULL(obstacle);
506
507 const auto collisionRobot = std::make_unique<ScCollisionRobot>(*obstacle);
508 collisionRobot->update();
509
510 for (auto& node : collisionRobot->getNodes())
511 {
512 for (std::size_t i = 0; i < node.size(); i++)
513 {
514 // Copying the collision object keeps its BVH geometry alive through the
515 // geometry's shared_ptr, so the intermediate robots can be dropped here.
516 colObjects.emplace_back(
517 std::make_shared<hpp::fcl::CollisionObject>(node.getColObject(i)));
518 }
519 }
520 }
521 catch (const std::exception& ex)
522 {
523 // Do not fail the whole costmap over one bad object, but never let this pass
524 // unnoticed either: the obstacle is missing from the costmap entirely.
525 ARMARX_ERROR << "Failed to convert object `" << r->getName()
526 << "` to a collision robot. It will be MISSING from the costmap.\n"
527 << ex.what();
528 }
529 };
530
531 for (const auto& o : this->obstacles->getSceneObjects())
532 {
533 // Here, the scene objects are ManipulationObjects
534 const auto casted =
535 std::dynamic_pointer_cast<VirtualRobot::GraspableSensorizedObject>(o);
536 ARMARX_CHECK_NOT_NULL(casted) << "Scene object is not a graspable sensorized object";
537
538 // static obstacles first have to be converted to a Robot,
539 // then converted to simox control
540 convert(VirtualRobot::RobotFactory::createRobot(*casted));
541 }
542
543 // articulated objects already are Robots, thus they can be immediately converted
544 for (const auto& o : this->articulatedObjects)
545 {
546 convert(o);
547 }
548
549 ARMARX_INFO << "Converted " << (this->obstacles->getSize() + this->articulatedObjects.size())
550 << " obstacles into " << colObjects.size() << " collision objects.";
551
552 return colObjects;
553 }
554#endif
555
556 void
557 CostmapBuilder::applyFnToCostmap(Costmap& costmap,
558 const std::function<float(const CollisionSetup& collisionSetup,
559 const std::optional<float>,
560 const float,
561 const Costmap::Index&)>& fn)
562 {
563
564 // filter scene objects once, not in every thread
565 const VirtualRobot::SceneObjectSetPtr filteredObjects = filterObjectsForCostmap(costmap);
566
567#if COSTMAP_BUILDER_SIMOX_CONTROL
568 // Convert the obstacles once, serially (see buildSharedObstacleCollisionObjects). The
569 // resulting geometry is immutable and is shared by all threads; each thread only gets its
570 // own lightweight transform wrappers.
571 const SharedObstacleColObjects sharedObstacleColObjects =
572 builderParameters.calculationMethod == DistanceCalculator::SimoxControl
573 ? buildSharedObstacleCollisionObjects()
574 : SharedObstacleColObjects{};
575#endif
576
577 // called from every thread to initialize the private collision data
578 const auto initializeCollisionSetup = [&]() -> CollisionSetup
579 {
580 armarx::core::time::ScopedStopWatch sw(
581 [](const armarx::core::time::Duration& duration)
582 {
583 ARMARX_INFO << "Initializing collision setup on thread " << omp_get_thread_num()
584 << " took: " << duration;
585 });
586
587 switch (builderParameters.calculationMethod)
588 {
589 case DistanceCalculator::SimoxControl:
590 {
591#if COSTMAP_BUILDER_SIMOX_CONTROL
592 ARMARX_VERBOSE << "Initializing collision setup";
593 CollisionSetupSC collisionSetup;
594
595 using ScRobot = sc::simox::robot::Robot;
596 using ScCollisionRobot = sc::environment::CollisionRobot<hpp::fcl::OBBRSS>;
597
598 // create copy of this robot instance
599 ARMARX_DEBUG << "Copying robot";
601 VirtualRobot::RobotPtr clonedRobot =
602 robot->clone("collision_robot_" + std::to_string(omp_get_thread_num()));
603 ARMARX_CHECK_NOT_NULL(clonedRobot);
604
605 // replace the standard collision model by our custom one
606 clonedRobot->setPrimitiveApproximationModel({"navigation"}, false);
607
608 clonedRobot->setUpdateVisualization(false);
609
610 // will copy RobotPtr and keep it alive after leaving the scope
611 collisionSetup.robot = ScRobot::CREATE_SIMPLE_WRAPPER(clonedRobot);
612 ARMARX_CHECK_NOT_NULL(collisionSetup.robot);
613 collisionSetup.collisionRobot =
614 std::make_unique<ScCollisionRobot>(*collisionSetup.robot,
615 std::string(),
616 false,
617 std::vector<std::string>{"navigation"});
618
619 collisionSetup.obstacleCollisionManager =
620 std::make_unique<hpp::fcl::DynamicAABBTreeCollisionManager>();
621
622 // Thread-local copies of the shared obstacle collision objects. This only
623 // duplicates transform and AABB - the BVH geometry is shared - so it is cheap
624 // compared to converting every obstacle in every thread.
625 collisionSetup.obstacleColObjects.reserve(sharedObstacleColObjects.size());
626 for (const auto& sharedColObject : sharedObstacleColObjects)
627 {
628 ARMARX_CHECK_NOT_NULL(sharedColObject);
629 collisionSetup.obstacleColObjects.emplace_back(
630 std::make_unique<hpp::fcl::CollisionObject>(*sharedColObject));
631 }
632
633 // register only after the vector is fully populated: the manager stores raw
634 // pointers to the collision objects
635 for (const auto& colObject : collisionSetup.obstacleColObjects)
636 {
637 collisionSetup.obstacleCollisionManager->registerObject(colObject.get());
638 }
639
640 collisionSetup.obstacleCollisionManager->setup();
641
642 return collisionSetup;
643#else
645 << "SimoxControl distance calculation requested but not supported. "
646 "Please compile with SimoxControl to use this calculation method.";
647 return {};
648#endif
649 }
650 case DistanceCalculator::Simox:
651 {
652 CollisionSetupSx collisionSetup;
653
654 ARMARX_DEBUG << "Copying robot";
656 collisionSetup.collisionRobot =
657 robot->clone("collision_robot_" + std::to_string(omp_get_thread_num()));
658
659 // replace the standard collision model by our custom one
660 collisionSetup.collisionRobot->setPrimitiveApproximationModel({"navigation"},
661 false);
662
663 collisionSetup.collisionRobot->setUpdateVisualization(false);
664 ARMARX_DEBUG << "Copying done";
665
666 ARMARX_CHECK_NOT_NULL(collisionSetup.collisionRobot);
668 collisionSetup.collisionRobot->hasRobotNode(robotCollisionModelName));
669
670 const auto collisionRobotNode =
671 collisionSetup.collisionRobot->getRobotNode(robotCollisionModelName);
672 ARMARX_CHECK_NOT_NULL(collisionRobotNode);
673
674 collisionSetup.robotCollisionModel = collisionRobotNode->getCollisionModel();
675 ARMARX_CHECK_NOT_NULL(collisionSetup.robotCollisionModel)
676 << "Collision model not available. "
677 "Make sure that you load the robot correctly!";
678
679 // scale collision model
680 collisionSetup.robotCollisionModel->scale(
681 builderParameters.collisionModelScaleFactor);
682
683 collisionSetup.filteredObstacles = filteredObjects;
684
685 ARMARX_CHECK_NOT_NULL(collisionSetup.collisionRobot);
686 ARMARX_CHECK_NOT_NULL(collisionSetup.robotCollisionModel);
687
688 return collisionSetup;
689 }
690 default:
691 ARMARX_ERROR << "Invalid distance calculator specified";
692 return {};
693 }
694 };
695
696 if (costmap.mask.has_value())
697 {
698 ARMARX_VERBOSE << "Costmap provides mask.";
699 }
700
701 const std::size_t c_x = costmap.grid.rows();
702 const std::size_t c_y = costmap.grid.cols();
703
704 robot->setUpdateVisualization(false);
705
706 // a per-thread local variable (through `private` directive below)
707 CollisionSetup collisionSetup;
708
709 const int threadNumDefault = omp_get_max_threads();
710 const int actualThreads = this->builderParameters.numThreads == 0
711 ? threadNumDefault
712 : this->builderParameters.numThreads;
713 ARMARX_INFO << "Using " << actualThreads << " threads.";
714
715
716// `schedule(dynamic)`: with a room mask, rows that lie entirely outside every room return
717// almost immediately, so a static split leaves threads idle.
718#pragma omp parallel for num_threads(actualThreads) \
719 schedule(dynamic) private(collisionSetup) default(shared)
720 for (unsigned int x = 0; x < c_x; x++)
721 {
722 // any exception (like those generated by ARMARX_CHECK) need to be caught in the executing thread
723 try
724 {
725
726 // we have to initialize the collision setup for each thread
727 if (std::holds_alternative<std::monostate>(collisionSetup))
728 {
729 collisionSetup = initializeCollisionSetup();
730 ARMARX_VERBOSE << "Collision setup created successfully";
731 }
732 ARMARX_CHECK(not std::holds_alternative<std::monostate>(collisionSetup));
733
734
735 for (unsigned int y = 0; y < c_y; y++)
736 {
737 const Costmap::Index index{x, y};
738
739 const auto maskVal = costmap.mask.has_value()
740 ? std::make_optional(costmap.mask.value()(x, y))
741 : std::nullopt;
742 costmap.grid(x, y) = fn(collisionSetup, maskVal, costmap.grid(x, y), index);
743 }
744 }
745 catch (const std::exception& e)
746 {
747 ARMARX_ERROR << "Error during costmap calculation: " << e.what();
748 }
749 }
750 }
751
752 VirtualRobot::SceneObjectSetPtr
753 CostmapBuilder::filterObjectsForCostmap(const Costmap& costmap)
754 {
755 armarx::core::time::ScopedStopWatch sw(
756 [](const armarx::core::time::Duration& duration)
757 { ARMARX_INFO << "Filtering objects took " << duration; });
758
759 VirtualRobot::SceneObjectSetPtr filtered(new VirtualRobot::SceneObjectSet);
760
762 using Box = boost::geometry::model::box<Point>;
764
765 const auto toPoint = [](const Eigen::Vector3f& vec) { return Point(vec.x(), vec.y()); };
766 const auto toBox = [&toPoint](const VirtualRobot::BoundingBox& bb)
767 { return Box(toPoint(bb.getMin()), toPoint(bb.getMax())); };
768
769 Polygon costmapBB;
770 {
771 Polygon cornerPoints = util::geometry::toPolygon(std::vector<Eigen::Vector2f>{
772 costmap.toPositionGlobal(Costmap::Index(0, 0)),
773 costmap.toPositionGlobal(Costmap::Index(0, costmap.grid.cols() - 1)),
774 costmap.toPositionGlobal(Costmap::Index(costmap.grid.rows() - 1, 0)),
775 costmap.toPositionGlobal(
776 Costmap::Index(costmap.grid.rows() - 1, costmap.grid.cols() - 1))});
777 boost::geometry::convex_hull(cornerPoints, costmapBB);
778 }
779
780
781 for (const auto& object : this->obstacles->getSceneObjects())
782 {
783 const auto& colModel = object->getCollisionModel();
784
785 if (colModel)
786 {
787 Box bb = toBox(colModel->getGlobalBoundingBox());
788
789 // if bb of object is disjoint to bb of costmap we check the distance between them
790 // otherwise, we always include the object
791 if (boost::geometry::disjoint(costmapBB, bb))
792 {
793 float distance = boost::geometry::distance(costmapBB, bb);
794 if (distance > builderParameters.maxFilterDistance)
795 {
796 // if the distance is greater than configured, we skip this object
797 continue;
798 }
799 }
800
801 filtered->addSceneObject(object);
802 }
803 }
804 const std::size_t prevSize = this->obstacles->getSize();
805 const std::size_t filteredSize = filtered->getSize();
806
807 ARMARX_INFO << "Remaining objects for costmap calculation: " << filteredSize << "/"
808 << prevSize << " ("
809 << static_cast<float>(filteredSize) * 100.f / static_cast<float>(prevSize)
810 << "%)";
811
812 for (const auto& object : filtered->getSceneObjects())
813 {
814 ARMARX_VERBOSE << "- " << object->getName();
815 }
816
817
818 return filtered;
819 }
820
821} // namespace armarx::navigation::algorithms
uint8_t index
#define ARMARX_CHECK_NOT_EMPTY(c)
Represents a duration.
Definition Duration.h:17
Measures the time this stop watch was inside the current scope.
static const simox::meta::EnumNames< DistanceCalculator > DistanceCalculatorNames
static Eigen::MatrixXf createUniformGrid(const SceneBounds &sceneBounds, const Costmap::Parameters &parameters)
CostmapBuilder(const VirtualRobot::RobotPtr &robot, const VirtualRobot::SceneObjectSetPtr &obstacles, const std::vector< VirtualRobot::RobotPtr > &articulatedObjects, const std::vector< Room > &rooms, const Costmap::Parameters &parameters, const std::string &robotCollisonModelName, const CostmapBuilderParams &builderParameters)
Costmap create(const SceneBounds &init=SceneBounds())
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_CHECK_NONNEGATIVE(number)
Check whether number is nonnegative (>= 0).
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:179
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:194
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:182
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:185
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
This file is part of ArmarX.
overloaded(Ts...) -> overloaded< Ts... >
void invalidateOutsideRooms(const std::vector< Room > &rooms, Costmap &costmap, const float footprintRadius)
Definition util.cpp:495
SceneBounds computeSceneBounds(const VirtualRobot::SceneObjectSetPtr &obstacles, const std::vector< VirtualRobot::RobotPtr > &articulatedObjects, const SceneBounds &init, const std::vector< Room > &rooms, const float margin, const bool restrictToRooms)
Definition util.cpp:62
std::vector< Eigen::Vector3f > to3D(const std::vector< Eigen::Vector2f > &v)
Definition eigen.cpp:14
Eigen::Isometry3f Pose
Definition basic_types.h:31
boost::geometry::model::d2::point_xy< float > point_type
Definition geometry.h:35
point_type toPoint(const Eigen::Vector2f &pt)
Definition geometry.cpp:43
polygon_type toPolygon(const std::vector< Eigen::Vector2f > &hull)
Definition geometry.cpp:31
boost::geometry::model::polygon< point_type > polygon_type
Definition geometry.h:36
std::vector< core::Pose > convert(const std::vector< Eigen::Matrix4f > &wps)
Definition Component.cpp:93
objpose::ObjectPoseSeq articulatedObjects(objpose::ObjectPoseSeq objects)
Definition util.cpp:94
::wykobi::polygon< float, 2 > Polygon
Eigen::Vector3f Point
double distance(const Point &a, const Point &b)
Definition point.hpp:95
#define ARMARX_TRACE
Definition trace.h:75