14#include <boost/geometry.hpp>
15#include <boost/geometry/algorithms/append.hpp>
16#include <boost/geometry/geometries/multi_point.hpp>
19#include <Eigen/Geometry>
21#include <range/v3/algorithm/all_of.hpp>
22#include <range/v3/algorithm/min.hpp>
23#include <range/v3/all.hpp>
24#include <range/v3/range/conversion.hpp>
25#include <range/v3/view/enumerate.hpp>
26#include <range/v3/view/filter.hpp>
27#include <range/v3/view/reverse.hpp>
28#include <range/v3/view/transform.hpp>
29#include <range/v3/view/zip_with.hpp>
31#include <SimoxUtility/color/Color.h>
32#include <SimoxUtility/color/cmaps/colormaps.h>
33#include <SimoxUtility/shapes/OrientedBox.h>
34#include <VirtualRobot/Nodes/RobotNode.h>
35#include <VirtualRobot/Robot.h>
55#include <armarx/navigation/safety_guard/aron/LaserBasedProximityParams.aron.generated.h>
60namespace rv = ::ranges::views;
68 inline core::TwistLimits
69 mergeSafetyLimits(
const std::vector<std::optional<core::TwistLimits>>& resultsAll)
71 const auto isValidFn =
72 [](
const std::optional<core::TwistLimits>& result)
noexcept ->
bool
73 {
return result.has_value(); };
75 const std::vector<core::TwistLimits> validResults =
76 resultsAll | rv::filter(isValidFn) |
78 [](
const std::optional<core::TwistLimits>& result)
noexcept -> core::TwistLimits
79 {
return result.value(); }) |
83 if (validResults.empty())
88 const std::vector<float> linearLimits =
90 rv::transform([](
const core::TwistLimits& result)
noexcept ->
float
91 {
return result.linear; }) |
94 const std::vector<float> angularLimits =
96 rv::transform([](
const core::TwistLimits& result)
noexcept ->
float
97 {
return result.angular; }) |
100 const core::TwistLimits combinedResult{.linear = ranges::min(linearLimits),
101 .angular = ranges::min(angularLimits)};
103 return combinedResult;
118 arondto::LaserBasedProximityParams dto;
128 arondto::LaserBasedProximityParams dto;
143 generalConfig(generalConfig),
144 humanColorMap_(
simox::color::cmaps::BuPu().reversed()),
145 laserColorMap_(
simox::color::cmaps::OrRd().reversed())
154 auto layer =
viz.layer(
"safety_guard");
156 const std::optional<core::TwistLimits> resultHumans = safetyLimitsHumans(layer);
157 const std::optional<core::TwistLimits> resultLaserScanners =
158 safetyLimitsLaserScanners(layer, global_V_movement);
164 mergeSafetyLimits({resultHumans, resultLaserScanners});
171 std::optional<core::TwistLimits>
172 LaserBasedProximity::safetyLimitsLaserScanners(
viz::Layer& layer,
173 const Eigen::Vector2f& global_V_movement)
const
190 debugObserver.setDebugObserverDatafield(
"numLaserScannerFeatures",
192 debugObserver.setDebugObserverDatafield(
193 "numFirstLaserScannerFeatures",
203 const core::Pose robot_T_global{global_T_robot.inverse()};
208 boost::geometry::model::multi_point<util::geometry::point_type> robotNodes;
209 for (
const auto& node :
scene.robot->getRobotNodes())
212 boost::geometry::append(robotNodes,
215 boost::geometry::convex_hull(robotNodes, robotConvexHull);
219 const auto minDistanceFn =
220 [
this, &robotConvexHull, &robot_T_global](
const memory::LaserScannerFeatures& features)
221 -> std::vector<DistanceAndClosestPoint>
223 const core::Pose& global_T_sensor = features.frameGlobalPose;
226 const auto minDistanceFnInner =
227 [
this, &robotConvexHull, &global_T_sensor, &robot_T_global](
228 const memory::LaserScannerFeature& feature) -> DistanceAndClosestPoint
231 const std::vector<Eigen::Vector3f> points3dGlobal =
233 rv::transform([&global_T_sensor](
const Eigen::Vector2f& pt) -> Eigen::Vector3f
234 {
return global_T_sensor *
conv::to3D(pt); }) |
237 return calculateMinDistance(robotConvexHull, robot_T_global, points3dGlobal);
242 const std::vector<DistanceAndClosestPoint> distances =
243 features.features | rv::transform(minDistanceFnInner) | ranges::to_vector;
248 laserScannerFeatureHistory_.push_back(
scene.dynamicScene->laserScannerFeatures);
250 const std::vector<memory::LaserScannerFeatures> accumulatedLaserScannerFeatures =
251 laserScannerFeatureHistory_ | ranges::views::join |
252 ranges::to<std::vector<memory::LaserScannerFeatures>>;
254 const std::vector<DistanceAndClosestPoint> minDistanceToObstacles =
255 accumulatedLaserScannerFeatures | rv::transform(minDistanceFn) | ranges::views::join |
258 const auto result = [&]() -> std::optional<InternalVelocityLimitResult>
260 switch (
params.laserScannerProximityField.mode)
265 return velocityLimitsDirectionDependent(
266 global_V_movement, minDistanceToObstacles, global_T_robot);
268 return velocityLimitsDirectionIndependent(minDistanceToObstacles);
272 <<
static_cast<int>(
params.laserScannerProximityField.mode);
281 simox::color::Color color = simox::Color::blue();
282 viz::Polygon vizPoly(
"robot_convex_hull");
283 for (
const auto& p : rv::reverse(robotConvexHull.outer()))
285 Eigen::Vector2f pt(p.x(), p.y());
288 layer.
add(vizPoly.color(color).lineColor(color));
291 layer.
add(viz::Line(
"robot_movement_Dir")
292 .fromTo(global_T_robot.translation(),
293 global_T_robot.translation() +
294 conv::to3D(global_V_movement).normalized() * 1000)
296 .color(simox::Color::blue()));
299 for (
const auto& [i, region] : ranges::views::enumerate(
params.ignoredRegions))
301 viz::Polygon vizRegion(
"ignored_region_" + std::to_string(i));
302 vizRegion.addPoint(Eigen::Vector3f(region.min().x(), region.min().y(), 15));
303 vizRegion.addPoint(Eigen::Vector3f(region.max().x(), region.min().y(), 15));
304 vizRegion.addPoint(Eigen::Vector3f(region.max().x(), region.max().y(), 15));
305 vizRegion.addPoint(Eigen::Vector3f(region.min().x(), region.max().y(), 15));
306 layer.
add(vizRegion.color(simox::Color::magenta()));
310 for (
const auto& [i, obj] :
311 ranges::views::enumerate(
scene.dynamicScene->attachedObjects))
313 const auto& oobb = obj->oobbGlobal();
316 simox::OrientedBoxf inflatedOobb{
317 oobb->transformation_centered(),
318 oobb->dimensions() + Eigen::Vector3f::Ones() *
params.attachedObjectsInflation};
320 viz::Box vizObj(
"attached_object_" + std::to_string(i));
321 vizObj.set(inflatedOobb);
322 layer.
add(vizObj.color(simox::Color::magenta()));
326 if (not result.has_value())
332 if (result->closestPoint.has_value())
335 const auto color = laserColorMap_.at(
336 std::abs(result->twistLimits.linear), 0, generalConfig.maxVel.linear);
339 viz::Line(
"nearest_obstacle")
340 .fromTo(global_T_robot.translation(),
conv::to3D(result->closestPoint.value()))
345 return result->twistLimits;
348 std::optional<core::TwistLimits>
349 LaserBasedProximity::safetyLimitsHumans(viz::Layer& layer)
const
351 if (not
params.enableHumans)
360 const auto minimalSegmentDistanceToRobot =
361 [&global_T_robot](
const human::Human& human) ->
float
363 const Eigen::Isometry3f global_T_human =
conv::to3D(human.pose);
364 const Eigen::Isometry3f robot_T_human = global_T_robot.inverse() * global_T_human;
367 return robot_T_human.translation().norm();
370 if (
scene.dynamicScene->humans.empty())
376 const auto distanceRobotToHumans =
scene.dynamicScene->humans |
377 rv::transform(minimalSegmentDistanceToRobot) |
380 const std::optional<float> minDistanceToHumans =
scene.dynamicScene->humans.empty()
381 ? std::optional<float>{std::nullopt}
382 : ranges::min(distanceRobotToHumans);
385 if (not minDistanceToHumans.has_value())
391 evaluateProximityField(
params.humanProximityField, minDistanceToHumans.value());
395 const auto color = humanColorMap_(result.second).with_alpha(1 - result.second + 0.1);
397 layer.
add(viz::Cylinder(
"distance_humans")
398 .position(global_T_robot.translation() + Eigen::Vector3f{0, 0, 10})
399 .direction(Eigen::Vector3f::UnitZ())
400 .radius(minDistanceToHumans.value())
408 LaserBasedProximity::DistanceAndClosestPoint
409 LaserBasedProximity::calculateMinDistance(
412 const std::vector<Eigen::Vector3f>& globalPoints)
const
417 const std::vector<Eigen::Vector2f> points2dGlobal =
419 rv::transform([](
const Eigen::Vector3f& pt) -> Eigen::Vector2f
423 if (isFeatureIgnored(globalPoints, points2dGlobal) or globalPoints.empty())
426 return {.distance = std::numeric_limits<float>::max(),
427 .closestPoint = Eigen::Vector2f::Zero(),
432 const std::vector<float> pointsDistanceRobotCenter =
434 rv::transform([&robot_T_global](
const Eigen::Vector3f& pt) ->
float
435 {
return conv::to2D(robot_T_global * pt).norm(); }) |
460 const auto distancesAndClosestPoint =
462 [
this, &convexHull, &globalPoints](
463 const Eigen::Vector2f& pt,
float distanceToCenter) -> DistanceAndClosestPoint
465 const auto distanceToRobotConvexHull =
466 static_cast<float>(boost::geometry::distance(
469 const float distanceUsingRobotRadius =
470 std::max(distanceToCenter -
params.robotRadius, 0.F);
473 std::min(distanceToRobotConvexHull, distanceUsingRobotRadius),
475 .clusterSize = globalPoints.size()};
478 pointsDistanceRobotCenter) |
482 distancesAndClosestPoint, std::less{}, &DistanceAndClosestPoint::distance);
485 std::pair<core::TwistLimits, float>
487 float minDistance)
const
489 if (minDistance < proximityField.safetyDistance)
494 if (minDistance > proximityField.influenceDistance)
500 const float proximityRange =
501 proximityField.influenceDistance - proximityField.safetyDistance;
502 const float clippedDistance =
503 std::min(minDistance, proximityField.influenceDistance) - proximityField.safetyDistance;
505 const float fractionalDistance = std::clamp(clippedDistance / proximityRange, 0.F, 1.F);
507 if (not proximityField.reduceVelocity)
512 const float d_s = std::pow(1 - fractionalDistance, proximityField.k);
514 const auto permissibleVelocity = [&proximityField](
const float d_s,
515 const float v_max) ->
float
516 {
return v_max / (1 + proximityField.lambda * d_s); };
518 const core::TwistLimits result{
519 .linear = permissibleVelocity(d_s, generalConfig.maxVel.linear),
520 .angular = permissibleVelocity(d_s, generalConfig.maxVel.angular)};
522 return {result, fractionalDistance};
525 template <
class T,
class Func>
527 allPointsInside(
const std::vector<T>& points, Func isInsideFunc)
529 for (
const auto& p : points)
531 if (not isInsideFunc(p))
541 LaserBasedProximity::isFeatureIgnored(
542 const std::vector<Eigen::Vector3f>& featurePoints3DGlobal,
543 const std::vector<Eigen::Vector2f>& featurePoints2DGlobal)
const
547 const std::size_t numMinPointsInCluster = 20;
549 if (featurePoints3DGlobal.size() < numMinPointsInCluster)
557 for (
const auto& region :
params.ignoredRegions)
561 if (allPointsInside(featurePoints2DGlobal,
562 [®ion](
const Eigen::Vector2f& p) {
return region.contains(p); }))
570 for (
const auto* o :
scene.dynamicScene->attachedObjects)
572 const auto& oobbGlobal = o->oobbGlobal();
575 simox::OrientedBoxf inflatedOobb{oobbGlobal->transformation_centered(),
576 oobbGlobal->dimensions() +
577 Eigen::Vector3f::Ones() *
578 params.attachedObjectsInflation};
580 if (allPointsInside(featurePoints3DGlobal,
581 [&inflatedOobb](
const Eigen::Vector3f& p)
582 {
return inflatedOobb.contains(p); }))
592 LaserBasedProximity::InternalVelocityLimitResult
593 LaserBasedProximity::velocityLimitsDirectionDependent(
594 const Eigen::Vector2f& global_V_movement,
595 const std::vector<DistanceAndClosestPoint>& minDistanceToObstacles,
596 const Eigen::Isometry3f& global_T_robot)
const
599 float maxPermissibleRelativeVelocityLinear = generalConfig.maxVel.linear;
601 std::optional<Eigen::Vector2f> minPoint = std::nullopt;
602 std::optional<float> minDistance = std::nullopt;
604 const Eigen::Isometry3f robot_T_global = global_T_robot.inverse();
607 auto& debugObserver = *
context.debugObserver;
609 debugObserver.setDebugObserverDatafield(
"numObstacles", minDistanceToObstacles.size());
611 std::size_t closestClusterSize = 0;
614 for (
const auto& [
distance, global_P_obstacle_pt, clusterSize] : minDistanceToObstacles)
616 const float ds =
params.laserScannerProximityField.safetyDistance;
617 const float di =
params.laserScannerProximityField.influenceDistance;
626 float velocity_damper = generalConfig.maxVel.linear * (d - ds) / (di - ds);
630 const Eigen::Vector2f minPointGlobal = global_P_obstacle_pt;
635 const Eigen::Vector2f directionRobotToObstacle =
636 (minPointGlobal - global_T_robot.translation().head<2>()).normalized();
643 const float relativeVelocityScaling =
644 directionRobotToObstacle.dot(global_V_movement.normalized());
650 if (relativeVelocityScaling < 0)
657 maxPermissibleRelativeVelocityLinear = 0;
658 minPoint = minPointGlobal;
660 closestClusterSize = clusterSize;
665 if (std::abs(relativeVelocityScaling) > 1e-4)
667 const float thisMaxPermissibileRelVelLinear =
668 velocity_damper / relativeVelocityScaling;
671 if (thisMaxPermissibileRelVelLinear < maxPermissibleRelativeVelocityLinear)
673 maxPermissibleRelativeVelocityLinear = thisMaxPermissibileRelVelLinear;
674 minPoint = minPointGlobal;
676 closestClusterSize = clusterSize;
681 debugObserver.setDebugObserverDatafield(
"maxPermissibleRelativeVelocityLinear",
682 maxPermissibleRelativeVelocityLinear);
684 if (minDistance.has_value())
686 debugObserver.setDebugObserverDatafield(
"minDistance", minDistance.value());
687 debugObserver.setDebugObserverDatafield(
"closestClusterSize", closestClusterSize);
690 core::TwistLimits result{.linear = std::max<float>(0, maxPermissibleRelativeVelocityLinear),
691 .angular = generalConfig.maxVel.angular};
692 return {.twistLimits = result, .minDistance = minDistance, .closestPoint = minPoint};
695 LaserBasedProximity::InternalVelocityLimitResult
696 LaserBasedProximity::velocityLimitsDirectionIndependent(
697 const std::vector<DistanceAndClosestPoint>& minDistanceToObstacles)
const
699 const DistanceAndClosestPoint minDistance =
700 ranges::min(minDistanceToObstacles, std::less{}, &DistanceAndClosestPoint::distance);
703 evaluateProximityField(
params.laserScannerProximityField, minDistance.distance);
705 return {.twistLimits = result.first,
706 .minDistance = minDistance.distance,
707 .closestPoint = minDistance.closestPoint};
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
SafetyGuardResult computeSafetyLimits(const Eigen::Vector2f &global_V_movement) override
LaserBasedProximityParams Params
LaserBasedProximity(const Params ¶ms, const core::GeneralConfig &generalConfig, const core::Scene &scene, const Context &ctx)
SafetyGuard(const core::Scene &scene, const Context &ctx)
const core::Scene & scene
#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_INFO
The normal logging level.
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_VERBOSE
The logging level for verbose information.
std::shared_ptr< Dict > DictPtr
std::vector< Eigen::Vector2f > to2D(const std::vector< Eigen::Vector3f > &v)
std::vector< Eigen::Vector3f > to3D(const std::vector< Eigen::Vector2f > &v)
This file is part of ArmarX.
void fromAron(const arondto::ProximityFieldParams &dto, ProximityFieldParams &bo)
void toAron(arondto::ProximityFieldParams &dto, const ProximityFieldParams &bo)
boost::geometry::model::d2::point_xy< float > point_type
boost::geometry::model::polygon< point_type > polygon_type
This file is part of ArmarX.
double distance(const Point &a, const Point &b)
VirtualRobot::RobotPtr robot
std::optional< core::DynamicScene > dynamicScene
static TwistLimits ZeroLimits()
static TwistLimits NoLimits()
static LaserBasedProximityParams FromAron(const aron::data::DictPtr &dict)
aron::data::DictPtr toAron() const override
Algorithms algorithm() const override
std::experimental::observer_ptr< DebugObserverComponentPluginUser > debugObserver
void add(ElementT const &element)