33#include <boost/geometry/algorithms/convex_hull.hpp>
34#include <boost/geometry/algorithms/disjoint.hpp>
35#include <boost/geometry/geometries/box.hpp>
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>
41#include <VirtualRobot/Robot.h>
42#include <VirtualRobot/RobotFactory.h>
43#include <VirtualRobot/SceneObjectSet.h>
44#include <VirtualRobot/VirtualRobot.h>
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>
67#include <hpp/fcl/broadphase/broadphase_dynamic_AABB_tree.h>
68#include <hpp/fcl/collision_object.h>
74#if COSTMAP_BUILDER_SIMOX_CONTROL
75 namespace sc = simox::control;
80 template <
class... Ts>
83 using Ts::operator()...;
86 template <
class... Ts>
89 const simox::meta::EnumNames<CostmapBuilder::DistanceCalculator>
95 const VirtualRobot::SceneObjectSetPtr& obstacles,
96 const std::vector<VirtualRobot::RobotPtr>& articulatedObjects,
97 const std::vector<Room>& rooms,
99 const std::string& robotCollisonModelName,
102 obstacles(obstacles),
103 articulatedObjects(articulatedObjects),
105 parameters(parameters),
106 robotCollisionModelName(robotCollisonModelName),
107 builderParameters(builderParameters)
111 ARMARX_CHECK(robot->hasRobotNode(robotCollisionModelName));
115 const auto& enabledRooms = this->builderParameters.
roomEnableList;
116 if (not enabledRooms.empty())
119 this->rooms.erase(std::remove_if(this->rooms.begin(),
121 [&enabledRooms](
const Room& room)
123 const bool roomIncluded =
124 std::find(enabledRooms.cbegin(),
126 room.name) != enabledRooms.cend();
127 if (not roomIncluded)
130 <<
"Room " << room.name
131 <<
" was found but is not included";
133 return not roomIncluded;
138 if (builderParameters.restrictToRooms)
141 <<
"No rooms enabled, even though restrict to rooms is true";
150 {
ARMARX_INFO <<
"Creating costmap took " << duration; });
152 ARMARX_INFO << articulatedObjects.size() <<
" articulated objects";
159 parameters.sceneBoundsMargin,
160 builderParameters.restrictToRooms);
166 if (builderParameters.restrictToRooms)
172 for (
const auto& room : rooms)
179 ARMARX_VERBOSE <<
"Restricted to rooms. Fraction of valid elements: "
186 {
ARMARX_INFO <<
"Filling costmap took " << duration; });
189 <<
costmap.getGrid().cols() <<
") and resolution "
206 {
ARMARX_INFO <<
"Extending costmap took " << duration; });
208 if (builderParameters.restrictToRooms)
218 {
ARMARX_INFO <<
"Filling costmap took " << duration; });
221 <<
costmap.getGrid().cols() <<
") and resolution "
248 ARMARX_VERBOSE <<
"Initializing empty mask: Fraction of valid elements: "
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;
267 Eigen::MatrixXf grid(c_x, c_y);
275 const CollisionSetup& collisionSetupVariant)
277 float cost = std::visit(
279 [&position](
const CollisionSetupSC& collisionSetup) ->
float
282#if COSTMAP_BUILDER_SIMOX_CONTROL
289 collisionSetup.robot->setGlobalPose(
290 sc::simox::utils::from_simox(globalPose.matrix()),
true);
292 collisionSetup.collisionRobot->update();
293 collisionSetup.collisionRobot->getCollisionManager()->update();
299 hpp::fcl::DistanceCallBackDefault defaultCallback;
300 collisionSetup.obstacleCollisionManager->distance(
301 collisionSetup.collisionRobot->getCollisionManager(), &defaultCallback);
303 const double minDistance = defaultCallback.data.result.min_distance;
305 return static_cast<float>(std::max(minDistance, 0.) * 1000);
308 <<
"SimoxControl distance calculation requested but not supported. "
309 "Please compile with SimoxControl to use this calculation method.";
313 [&position,
this](
const CollisionSetupSx& collisionSetup) ->
float
315 const auto& collisionRobot = collisionSetup.collisionRobot;
316 const auto& robotCollisionModel = collisionSetup.robotCollisionModel;
322 VirtualRobot::CollisionChecker::getGlobalCollisionChecker());
324 const VirtualRobot::SceneObjectSetPtr& actualObstacles = [&]()
326 if (collisionSetup.filteredObstacles)
328 return collisionSetup.filteredObstacles;
332 return this->obstacles;
338 collisionRobot->setGlobalPose(globalPose.matrix());
341 float distanceNonArticulated = std::numeric_limits<float>::max();
342 if (actualObstacles->getSize() > 0)
344 distanceNonArticulated =
345 VirtualRobot::CollisionChecker::getGlobalCollisionChecker()
346 ->calculateDistance(robotCollisionModel, actualObstacles);
350 float distanceArticulatedMin = std::numeric_limits<float>::max();
351 for (
const auto& articulatedObject : articulatedObjects)
353 for (
const auto& colModel : articulatedObject->getCollisionModels())
355 const float distanceArticulated =
356 VirtualRobot::CollisionChecker::getGlobalCollisionChecker()
357 ->calculateDistance(robotCollisionModel, colModel);
358 distanceArticulatedMin =
359 std::min(distanceArticulated, distanceArticulatedMin);
363 return std::min(distanceNonArticulated, distanceArticulatedMin);
409 [](
const std::monostate& _) ->
float
414 collisionSetupVariant);
417 cost = std::min(cost, builderParameters.maxDistance);
422 CostmapBuilder::fillGridCosts(Costmap& costmap)
424 applyFnToCostmap(costmap,
425 [&](
const CollisionSetup& collisionSetup,
426 const std::optional<float> maskOpt,
428 const Costmap::Index&
index) ->
float
430 const auto position = costmap.toPositionGlobal(
index);
433 if (maskOpt.has_value())
435 if (not maskOpt.value())
442 if( builderParameters.fakeModeForTesting)
444 return builderParameters.maxDistance;
447 return computeCost(position, collisionSetup);
452 CostmapBuilder::extendGridCosts(Costmap& costmap)
456 [&](
const CollisionSetup& collisionSetup,
457 const std::optional<float> maskOpt,
459 const Costmap::Index&
index) ->
float
461 const auto position = costmap.toPositionGlobal(
index);
465 if (maskOpt.has_value())
467 if (not maskOpt.value())
469 return std::min(0.F, cost);
478 if (builderParameters.fakeModeForTesting)
480 return std::min(cost, builderParameters.maxDistance);
483 return std::min(cost, computeCost(position, collisionSetup));
487#if COSTMAP_BUILDER_SIMOX_CONTROL
488 CostmapBuilder::SharedObstacleColObjects
489 CostmapBuilder::buildSharedObstacleCollisionObjects()
const
491 armarx::core::time::ScopedStopWatch sw(
492 [](
const armarx::core::time::Duration& duration)
493 {
ARMARX_INFO <<
"Converting obstacles to collision objects took " << duration; });
495 using ScRobot = sc::simox::robot::Robot;
496 using ScCollisionRobot = sc::environment::CollisionRobot<hpp::fcl::OBBRSS>;
498 SharedObstacleColObjects colObjects;
509 const auto obstacle = ScRobot::CREATE_SIMPLE_WRAPPER(r);
512 const auto collisionRobot = std::make_unique<ScCollisionRobot>(*obstacle);
513 collisionRobot->update();
515 for (
auto& node : collisionRobot->getNodes())
517 for (std::size_t i = 0; i < node.size(); i++)
521 colObjects.emplace_back(
522 std::make_shared<hpp::fcl::CollisionObject>(node.getColObject(i)));
526 catch (
const std::exception& ex)
530 ARMARX_ERROR <<
"Failed to convert object `" << r->getName()
531 <<
"` to a collision robot. It will be MISSING from the costmap.\n"
536 for (
const auto& o : this->obstacles->getSceneObjects())
540 std::dynamic_pointer_cast<VirtualRobot::GraspableSensorizedObject>(o);
545 convert(VirtualRobot::RobotFactory::createRobot(*casted));
549 for (
const auto& o : this->articulatedObjects)
555 <<
" obstacles into " << colObjects.size() <<
" collision objects.";
562 CostmapBuilder::applyFnToCostmap(Costmap& costmap,
563 const std::function<
float(
const CollisionSetup& collisionSetup,
564 const std::optional<float>,
566 const Costmap::Index&)>& fn)
568 if (builderParameters.fakeModeForTesting)
572 const std::size_t c_x = costmap.grid.rows();
573 const std::size_t c_y = costmap.grid.cols();
574 for (
unsigned int x = 0; x < c_x; x++)
576 for (
unsigned int y = 0; y < c_y; y++)
578 const Costmap::Index
index{x, y};
579 const auto maskVal = costmap.mask.has_value()
580 ? std::make_optional(costmap.mask.value()(x, y))
582 costmap.grid(x, y) = fn(CollisionSetup{}, maskVal, costmap.grid(x, y),
index);
589 const VirtualRobot::SceneObjectSetPtr filteredObjects = filterObjectsForCostmap(costmap);
591#if COSTMAP_BUILDER_SIMOX_CONTROL
595 const SharedObstacleColObjects sharedObstacleColObjects =
596 builderParameters.calculationMethod == DistanceCalculator::SimoxControl
597 ? buildSharedObstacleCollisionObjects()
598 : SharedObstacleColObjects{};
602 const auto initializeCollisionSetup = [&]() -> CollisionSetup
604 armarx::core::time::ScopedStopWatch sw(
605 [](
const armarx::core::time::Duration& duration)
607 ARMARX_INFO <<
"Initializing collision setup on thread " << omp_get_thread_num()
608 <<
" took: " << duration;
611 switch (builderParameters.calculationMethod)
613 case DistanceCalculator::SimoxControl:
615#if COSTMAP_BUILDER_SIMOX_CONTROL
617 CollisionSetupSC collisionSetup;
619 using ScRobot = sc::simox::robot::Robot;
620 using ScCollisionRobot = sc::environment::CollisionRobot<hpp::fcl::OBBRSS>;
626 robot->clone(
"collision_robot_" + std::to_string(omp_get_thread_num()));
630 clonedRobot->setPrimitiveApproximationModel({
"navigation"},
false);
632 clonedRobot->setUpdateVisualization(
false);
635 collisionSetup.robot = ScRobot::CREATE_SIMPLE_WRAPPER(clonedRobot);
637 collisionSetup.collisionRobot =
638 std::make_unique<ScCollisionRobot>(*collisionSetup.robot,
641 std::vector<std::string>{
"navigation"});
643 collisionSetup.obstacleCollisionManager =
644 std::make_unique<hpp::fcl::DynamicAABBTreeCollisionManager>();
649 collisionSetup.obstacleColObjects.reserve(sharedObstacleColObjects.size());
650 for (
const auto& sharedColObject : sharedObstacleColObjects)
653 collisionSetup.obstacleColObjects.emplace_back(
654 std::make_unique<hpp::fcl::CollisionObject>(*sharedColObject));
659 for (
const auto& colObject : collisionSetup.obstacleColObjects)
661 collisionSetup.obstacleCollisionManager->registerObject(colObject.get());
664 collisionSetup.obstacleCollisionManager->setup();
666 return collisionSetup;
669 <<
"SimoxControl distance calculation requested but not supported. "
670 "Please compile with SimoxControl to use this calculation method.";
674 case DistanceCalculator::Simox:
676 CollisionSetupSx collisionSetup;
680 collisionSetup.collisionRobot =
681 robot->clone(
"collision_robot_" + std::to_string(omp_get_thread_num()));
684 collisionSetup.collisionRobot->setPrimitiveApproximationModel({
"navigation"},
687 collisionSetup.collisionRobot->setUpdateVisualization(
false);
692 collisionSetup.collisionRobot->hasRobotNode(robotCollisionModelName));
694 const auto collisionRobotNode =
695 collisionSetup.collisionRobot->getRobotNode(robotCollisionModelName);
698 collisionSetup.robotCollisionModel = collisionRobotNode->getCollisionModel();
700 <<
"Collision model not available. "
701 "Make sure that you load the robot correctly!";
704 collisionSetup.robotCollisionModel->scale(
705 builderParameters.collisionModelScaleFactor);
707 collisionSetup.filteredObstacles = filteredObjects;
712 return collisionSetup;
715 ARMARX_ERROR <<
"Invalid distance calculator specified";
720 if (costmap.mask.has_value())
725 const std::size_t c_x = costmap.grid.rows();
726 const std::size_t c_y = costmap.grid.cols();
728 robot->setUpdateVisualization(
false);
731 CollisionSetup collisionSetup;
733 const int threadNumDefault = omp_get_max_threads();
734 const int actualThreads = this->builderParameters.numThreads == 0
736 : this->builderParameters.numThreads;
737 ARMARX_INFO <<
"Using " << actualThreads <<
" threads.";
742#pragma omp parallel for num_threads(actualThreads) \
743 schedule(dynamic) private(collisionSetup) default(shared)
744 for (
unsigned int x = 0; x < c_x; x++)
751 if (std::holds_alternative<std::monostate>(collisionSetup))
753 collisionSetup = initializeCollisionSetup();
756 ARMARX_CHECK(not std::holds_alternative<std::monostate>(collisionSetup));
759 for (
unsigned int y = 0; y < c_y; y++)
761 const Costmap::Index
index{x, y};
763 const auto maskVal = costmap.mask.has_value()
764 ? std::make_optional(costmap.mask.value()(x, y))
766 costmap.grid(x, y) = fn(collisionSetup, maskVal, costmap.grid(x, y),
index);
769 catch (
const std::exception& e)
771 ARMARX_ERROR <<
"Error during costmap calculation: " << e.what();
776 VirtualRobot::SceneObjectSetPtr
777 CostmapBuilder::filterObjectsForCostmap(
const Costmap& costmap)
779 armarx::core::time::ScopedStopWatch sw(
780 [](
const armarx::core::time::Duration& duration)
781 {
ARMARX_INFO <<
"Filtering objects took " << duration; });
783 VirtualRobot::SceneObjectSetPtr filtered(
new VirtualRobot::SceneObjectSet);
786 using Box = boost::geometry::model::box<Point>;
789 const auto toPoint = [](
const Eigen::Vector3f& vec) {
return Point(vec.x(), vec.y()); };
790 const auto toBox = [&
toPoint](
const VirtualRobot::BoundingBox& bb)
796 costmap.toPositionGlobal(Costmap::Index(0, 0)),
797 costmap.toPositionGlobal(Costmap::Index(0, costmap.grid.cols() - 1)),
798 costmap.toPositionGlobal(Costmap::Index(costmap.grid.rows() - 1, 0)),
799 costmap.toPositionGlobal(
800 Costmap::Index(costmap.grid.rows() - 1, costmap.grid.cols() - 1))});
801 boost::geometry::convex_hull(cornerPoints, costmapBB);
805 for (
const auto&
object : this->obstacles->getSceneObjects())
807 const auto& colModel =
object->getCollisionModel();
811 const auto globalBB = colModel->getGlobalBoundingBox();
812 if (!globalBB.getMin().allFinite() || !globalBB.getMax().allFinite())
815 <<
"` has a non-finite bounding box (likely missing "
816 "collision mesh) - excluding it from costmap filtering.";
819 Box bb = toBox(globalBB);
823 if (boost::geometry::disjoint(costmapBB, bb))
825 float distance = boost::geometry::distance(costmapBB, bb);
826 if (
distance > builderParameters.maxFilterDistance)
833 filtered->addSceneObject(
object);
836 const std::size_t prevSize = this->obstacles->getSize();
837 const std::size_t filteredSize = filtered->getSize();
839 ARMARX_INFO <<
"Remaining objects for costmap calculation: " << filteredSize <<
"/"
841 <<
static_cast<float>(filteredSize) * 100.f /
static_cast<float>(prevSize)
844 for (
const auto&
object : filtered->getSceneObjects())
#define ARMARX_CHECK_NOT_EMPTY(c)
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 ¶meters)
Costmap extend(Costmap costmap)
CostmapBuilder(const VirtualRobot::RobotPtr &robot, const VirtualRobot::SceneObjectSetPtr &obstacles, const std::vector< VirtualRobot::RobotPtr > &articulatedObjects, const std::vector< Room > &rooms, const Costmap::Parameters ¶meters, const std::string &robotCollisonModelName, const CostmapBuilderParams &builderParameters)
Costmap create(const SceneBounds &init=SceneBounds())
static void updateMask(Costmap &costmap)
#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.
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
std::shared_ptr< class Robot > RobotPtr
This file is part of ArmarX.
This file is part of ArmarX.
overloaded(Ts...) -> overloaded< Ts... >
void invalidateOutsideRooms(const std::vector< Room > &rooms, Costmap &costmap, const float footprintRadius)
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)
std::vector< Eigen::Vector3f > to3D(const std::vector< Eigen::Vector2f > &v)
boost::geometry::model::d2::point_xy< float > point_type
point_type toPoint(const Eigen::Vector2f &pt)
polygon_type toPolygon(const std::vector< Eigen::Vector2f > &hull)
boost::geometry::model::polygon< point_type > polygon_type
std::vector< core::Pose > convert(const std::vector< Eigen::Matrix4f > &wps)
objpose::ObjectPoseSeq articulatedObjects(objpose::ObjectPoseSeq objects)
::wykobi::polygon< float, 2 > Polygon
double distance(const Point &a, const Point &b)
std::vector< std::string > roomEnableList