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 return std::min(cost, computeCost(position, collisionSetup));
482#if COSTMAP_BUILDER_SIMOX_CONTROL
483 CostmapBuilder::SharedObstacleColObjects
484 CostmapBuilder::buildSharedObstacleCollisionObjects()
const
486 armarx::core::time::ScopedStopWatch sw(
487 [](
const armarx::core::time::Duration& duration)
488 {
ARMARX_INFO <<
"Converting obstacles to collision objects took " << duration; });
490 using ScRobot = sc::simox::robot::Robot;
491 using ScCollisionRobot = sc::environment::CollisionRobot<hpp::fcl::OBBRSS>;
493 SharedObstacleColObjects colObjects;
504 const auto obstacle = ScRobot::CREATE_SIMPLE_WRAPPER(r);
507 const auto collisionRobot = std::make_unique<ScCollisionRobot>(*obstacle);
508 collisionRobot->update();
510 for (
auto& node : collisionRobot->getNodes())
512 for (std::size_t i = 0; i < node.size(); i++)
516 colObjects.emplace_back(
517 std::make_shared<hpp::fcl::CollisionObject>(node.getColObject(i)));
521 catch (
const std::exception& ex)
525 ARMARX_ERROR <<
"Failed to convert object `" << r->getName()
526 <<
"` to a collision robot. It will be MISSING from the costmap.\n"
531 for (
const auto& o : this->obstacles->getSceneObjects())
535 std::dynamic_pointer_cast<VirtualRobot::GraspableSensorizedObject>(o);
540 convert(VirtualRobot::RobotFactory::createRobot(*casted));
544 for (
const auto& o : this->articulatedObjects)
550 <<
" obstacles into " << colObjects.size() <<
" collision objects.";
557 CostmapBuilder::applyFnToCostmap(Costmap& costmap,
558 const std::function<
float(
const CollisionSetup& collisionSetup,
559 const std::optional<float>,
561 const Costmap::Index&)>& fn)
565 const VirtualRobot::SceneObjectSetPtr filteredObjects = filterObjectsForCostmap(costmap);
567#if COSTMAP_BUILDER_SIMOX_CONTROL
571 const SharedObstacleColObjects sharedObstacleColObjects =
572 builderParameters.calculationMethod == DistanceCalculator::SimoxControl
573 ? buildSharedObstacleCollisionObjects()
574 : SharedObstacleColObjects{};
578 const auto initializeCollisionSetup = [&]() -> CollisionSetup
580 armarx::core::time::ScopedStopWatch sw(
581 [](
const armarx::core::time::Duration& duration)
583 ARMARX_INFO <<
"Initializing collision setup on thread " << omp_get_thread_num()
584 <<
" took: " << duration;
587 switch (builderParameters.calculationMethod)
589 case DistanceCalculator::SimoxControl:
591#if COSTMAP_BUILDER_SIMOX_CONTROL
593 CollisionSetupSC collisionSetup;
595 using ScRobot = sc::simox::robot::Robot;
596 using ScCollisionRobot = sc::environment::CollisionRobot<hpp::fcl::OBBRSS>;
602 robot->clone(
"collision_robot_" + std::to_string(omp_get_thread_num()));
606 clonedRobot->setPrimitiveApproximationModel({
"navigation"},
false);
608 clonedRobot->setUpdateVisualization(
false);
611 collisionSetup.robot = ScRobot::CREATE_SIMPLE_WRAPPER(clonedRobot);
613 collisionSetup.collisionRobot =
614 std::make_unique<ScCollisionRobot>(*collisionSetup.robot,
617 std::vector<std::string>{
"navigation"});
619 collisionSetup.obstacleCollisionManager =
620 std::make_unique<hpp::fcl::DynamicAABBTreeCollisionManager>();
625 collisionSetup.obstacleColObjects.reserve(sharedObstacleColObjects.size());
626 for (
const auto& sharedColObject : sharedObstacleColObjects)
629 collisionSetup.obstacleColObjects.emplace_back(
630 std::make_unique<hpp::fcl::CollisionObject>(*sharedColObject));
635 for (
const auto& colObject : collisionSetup.obstacleColObjects)
637 collisionSetup.obstacleCollisionManager->registerObject(colObject.get());
640 collisionSetup.obstacleCollisionManager->setup();
642 return collisionSetup;
645 <<
"SimoxControl distance calculation requested but not supported. "
646 "Please compile with SimoxControl to use this calculation method.";
650 case DistanceCalculator::Simox:
652 CollisionSetupSx collisionSetup;
656 collisionSetup.collisionRobot =
657 robot->clone(
"collision_robot_" + std::to_string(omp_get_thread_num()));
660 collisionSetup.collisionRobot->setPrimitiveApproximationModel({
"navigation"},
663 collisionSetup.collisionRobot->setUpdateVisualization(
false);
668 collisionSetup.collisionRobot->hasRobotNode(robotCollisionModelName));
670 const auto collisionRobotNode =
671 collisionSetup.collisionRobot->getRobotNode(robotCollisionModelName);
674 collisionSetup.robotCollisionModel = collisionRobotNode->getCollisionModel();
676 <<
"Collision model not available. "
677 "Make sure that you load the robot correctly!";
680 collisionSetup.robotCollisionModel->scale(
681 builderParameters.collisionModelScaleFactor);
683 collisionSetup.filteredObstacles = filteredObjects;
688 return collisionSetup;
691 ARMARX_ERROR <<
"Invalid distance calculator specified";
696 if (costmap.mask.has_value())
701 const std::size_t c_x = costmap.grid.rows();
702 const std::size_t c_y = costmap.grid.cols();
704 robot->setUpdateVisualization(
false);
707 CollisionSetup collisionSetup;
709 const int threadNumDefault = omp_get_max_threads();
710 const int actualThreads = this->builderParameters.numThreads == 0
712 : this->builderParameters.numThreads;
713 ARMARX_INFO <<
"Using " << actualThreads <<
" threads.";
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++)
727 if (std::holds_alternative<std::monostate>(collisionSetup))
729 collisionSetup = initializeCollisionSetup();
732 ARMARX_CHECK(not std::holds_alternative<std::monostate>(collisionSetup));
735 for (
unsigned int y = 0; y < c_y; y++)
737 const Costmap::Index
index{x, y};
739 const auto maskVal = costmap.mask.has_value()
740 ? std::make_optional(costmap.mask.value()(x, y))
742 costmap.grid(x, y) = fn(collisionSetup, maskVal, costmap.grid(x, y),
index);
745 catch (
const std::exception& e)
747 ARMARX_ERROR <<
"Error during costmap calculation: " << e.what();
752 VirtualRobot::SceneObjectSetPtr
753 CostmapBuilder::filterObjectsForCostmap(
const Costmap& costmap)
755 armarx::core::time::ScopedStopWatch sw(
756 [](
const armarx::core::time::Duration& duration)
757 {
ARMARX_INFO <<
"Filtering objects took " << duration; });
759 VirtualRobot::SceneObjectSetPtr filtered(
new VirtualRobot::SceneObjectSet);
762 using Box = boost::geometry::model::box<Point>;
765 const auto toPoint = [](
const Eigen::Vector3f& vec) {
return Point(vec.x(), vec.y()); };
766 const auto toBox = [&
toPoint](
const VirtualRobot::BoundingBox& bb)
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);
781 for (
const auto&
object : this->obstacles->getSceneObjects())
783 const auto& colModel =
object->getCollisionModel();
787 Box bb = toBox(colModel->getGlobalBoundingBox());
791 if (boost::geometry::disjoint(costmapBB, bb))
793 float distance = boost::geometry::distance(costmapBB, bb);
794 if (
distance > builderParameters.maxFilterDistance)
801 filtered->addSceneObject(
object);
804 const std::size_t prevSize = this->obstacles->getSize();
805 const std::size_t filteredSize = filtered->getSize();
807 ARMARX_INFO <<
"Remaining objects for costmap calculation: " << filteredSize <<
"/"
809 <<
static_cast<float>(filteredSize) * 100.f /
static_cast<float>(prevSize)
812 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_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