6 #include <boost/geometry.hpp>
7 #include <boost/geometry/algorithms/append.hpp>
8 #include <boost/geometry/geometries/multi_point.hpp>
10 #include <Eigen/Geometry>
12 #include <SimoxUtility/color/cmaps/colormaps.h>
13 #include <VirtualRobot/Nodes/RobotNode.h>
14 #include <VirtualRobot/Robot.h>
27 #include <armarx/navigation/safety_guard/aron/LaserBasedProximityParams.aron.generated.h>
30 #include <range/v3/algorithm/min.hpp>
31 #include <range/v3/range/conversion.hpp>
32 #include <range/v3/view/filter.hpp>
33 #include <range/v3/view/reverse.hpp>
34 #include <range/v3/view/transform.hpp>
35 #include <range/v3/view/zip_with.hpp>
37 namespace rv = ::ranges::views;
45 inline core::TwistLimits
46 mergeSafetyLimits(
const std::vector<std::optional<core::TwistLimits>>& resultsAll)
48 const auto isValidFn = [](
const std::optional<core::TwistLimits>& result) ->
bool
49 {
return result.has_value(); };
51 const std::vector<core::TwistLimits> validResults =
52 resultsAll | rv::filter(isValidFn) |
54 [](
const std::optional<core::TwistLimits>& result) -> core::TwistLimits
55 {
return result.value(); }) |
59 if (validResults.empty())
64 const std::vector<float> linearLimits =
67 {
return result.linear; }) |
70 const std::vector<float> angularLimits =
73 {
return result.angular; }) |
76 const core::TwistLimits combinedResult{.linear =
ranges::min(linearLimits),
79 return combinedResult;
85 operator()(
const auto&
a,
const auto& b)
87 return a.first < b.first;
102 arondto::LaserBasedProximityParams dto;
112 arondto::LaserBasedProximityParams dto;
126 humanColorMap_(
simox::color::cmaps::BuPu().reversed()),
127 laserColorMap_(
simox::color::cmaps::OrRd().reversed())
136 auto layer =
viz.layer(
"safety_guard");
138 const std::optional<core::TwistLimits> resultHumans = safetyLimitsHumans(layer);
139 const std::optional<core::TwistLimits> resultLaserScanners =
140 safetyLimitsLaserScanners(layer);
146 mergeSafetyLimits({resultHumans, resultLaserScanners});
153 std::optional<core::TwistLimits>
154 LaserBasedProximity::safetyLimitsLaserScanners(
viz::Layer& layer)
const
169 const core::Pose robot_T_global{global_T_robot.inverse()};
174 boost::geometry::model::multi_point<util::geometry::point_type> robotNodes;
175 for (
const auto& node :
scene.
robot->getRobotNodes())
178 boost::geometry::append(robotNodes,
185 const auto minDistanceFn =
187 const memory::LaserScannerFeatures& features) -> std::pair<float, Eigen::Vector2f>
189 const core::Pose& global_T_sensor = features.frameGlobalPose;
192 const auto minDistanceFnInner =
193 [
this, &
convexHull, &global_T_sensor, &robot_T_global](
194 const memory::LaserScannerFeature& feature) -> std::pair<float, Eigen::Vector2f>
197 const std::vector<Eigen::Vector3f> points3dGlobal =
199 rv::transform([&global_T_sensor](
const Eigen::Vector2f& pt) -> Eigen::Vector3f
200 {
return global_T_sensor *
conv::to3D(pt); }) |
203 return calculateMinDistance(
convexHull, robot_T_global, points3dGlobal);
207 const std::vector<std::pair<float, Eigen::Vector2f>> distances =
208 features.features |
rv::transform(minDistanceFnInner) | ranges::to_vector;
213 const std::vector<std::pair<float, Eigen::Vector2f>> minDistanceToObstacles =
217 const std::pair<float, Eigen::Vector2f> minDistance =
218 ranges::min(minDistanceToObstacles, PairCompare());
228 const auto color = laserColorMap_(result.second);
233 Eigen::Vector2f pt(p.x(), p.y());
236 layer.
add(vizPoly.color(color).lineColor(color));
239 .fromTo(global_T_robot.translation(),
conv::to3D(minDistance.second))
247 std::optional<core::TwistLimits>
248 LaserBasedProximity::safetyLimitsHumans(
viz::Layer& layer)
const
259 const auto minimalSegmentDistanceToRobot =
260 [&global_T_robot](
const human::Human& human) ->
float
262 const Eigen::Isometry3f global_T_human =
conv::to3D(human.pose);
263 const Eigen::Isometry3f robot_T_human = global_T_robot.inverse() * global_T_human;
266 return robot_T_human.translation().norm();
279 const std::optional<float> minDistanceToHumans =
scene.
dynamicScene->humans.empty()
280 ? std::optional<float>{std::nullopt}
284 if (not minDistanceToHumans.has_value())
294 const auto color = humanColorMap_(result.second).with_alpha(1 - result.second + 0.1);
297 .position(global_T_robot.translation() + Eigen::Vector3f{0, 0, 10})
299 .
radius(minDistanceToHumans.value())
307 std::pair<float, Eigen::Vector2f>
308 LaserBasedProximity::calculateMinDistance(
311 const std::vector<Eigen::Vector3f>& globalPoints)
const
316 const std::vector<Eigen::Vector2f> points2dGlobal =
318 rv::transform([](
const Eigen::Vector3f& pt) -> Eigen::Vector2f
323 const std::vector<float> pointsDistanceRobotCenter =
325 rv::transform([&robot_T_global](
const Eigen::Vector3f& pt) ->
float
326 {
return conv::to2D(robot_T_global * pt).norm(); }) |
331 [
this, &
convexHull](
const Eigen::Vector2f& pt,
332 float distanceToCenter) -> std::pair<float, Eigen::Vector2f>
337 const float distanceUsingRobotRadius =
340 return {
std::min(distanceToConvexHull, distanceUsingRobotRadius), pt};
343 pointsDistanceRobotCenter),
347 std::pair<core::TwistLimits, float>
348 LaserBasedProximity::evaluateProximityField(
const ProximityFieldParams& proximityField,
349 float minDistance)
const
351 if (minDistance < proximityField.minDistance)
356 if (minDistance > proximityField.maxDistance)
362 const float proximityRange = proximityField.maxDistance - proximityField.minDistance;
363 const float clippedDistance =
364 std::min(minDistance, proximityField.maxDistance) - proximityField.minDistance;
366 const float fractionalDistance =
std::clamp(clippedDistance / proximityRange, 0.
F, 1.
F);
368 if (not proximityField.reduceVelocity)
373 const float d_s = std::pow(1 - fractionalDistance, proximityField.k);
375 const auto permissibleVelocity = [&proximityField](
const float d_s,
376 const float v_max) ->
float
377 {
return v_max / (1 + proximityField.lambda * d_s); };
379 const core::TwistLimits result{
383 return {result, fractionalDistance};