37#include <SimoxUtility/json.h>
38#include <SimoxUtility/math/convert.h>
39#include <VirtualRobot/Robot.h>
42#include <Ice/Current.h>
45#include <DynamicObstacleAvoidance/Modulation.hpp>
46#include <DynamicObstacleAvoidance/Obstacle/Ellipsoid.hpp>
47#include <DynamicObstacleAvoidance/Utils/Plotting/PlottingTools.hpp>
48namespace doa = DynamicObstacleAvoidance;
60 std::shared_ptr<doa::Obstacle>
61 create_doa_obstacle(
const armarx::obstacledetection::Obstacle& obstacle)
63 Eigen::Vector3d pos(obstacle.posX, obstacle.posY, obstacle.posZ);
66 std::shared_ptr<doa::Ellipsoid> obstacle_ptr =
67 std::make_shared<doa::Ellipsoid>(obstacle.name,
68 doa::State(pos / 1000, ori),
69 Eigen::Array3d(obstacle.safetyMarginX / 1000,
70 obstacle.safetyMarginY / 1000,
71 obstacle.safetyMarginZ / 1000));
73 obstacle_ptr->set_axis_lengths(
74 Eigen::Array3d(obstacle.axisLengthX, obstacle.axisLengthY, obstacle.axisLengthZ) /
76 obstacle_ptr->set_reference_position(
77 Eigen::Vector3d(obstacle.refPosX, obstacle.refPosY, obstacle.refPosZ) / 1000);
78 obstacle_ptr->set_curvature_factor(
79 Eigen::Array3d(obstacle.curvatureX, obstacle.curvatureY, obstacle.curvatureZ));
90 from_json(
const nlohmann::json& j, armarx::obstacledetection::Obstacle& obstacle)
92 j.at(
"name").get_to(obstacle.name);
93 j.at(
"posX").get_to(obstacle.posX);
94 j.at(
"posY").get_to(obstacle.posY);
95 if (j.find(
"posZ") != j.end())
97 j.at(
"posZ").get_to(obstacle.posZ);
99 j.at(
"yaw").get_to(obstacle.yaw);
100 j.at(
"axisLengthX").get_to(obstacle.axisLengthX);
101 j.at(
"axisLengthY").get_to(obstacle.axisLengthY);
102 if (j.find(
"axisLengthZ") != j.end())
104 j.at(
"axisLengthZ").get_to(obstacle.axisLengthZ);
106 j.at(
"refPosX").get_to(obstacle.refPosX);
107 j.at(
"refPosY").get_to(obstacle.refPosY);
108 if (j.find(
"refPosZ") != j.end())
110 j.at(
"refPosZ").get_to(obstacle.refPosZ);
112 if (j.find(
"safetyMarginX") != j.end())
114 j.at(
"safetyMarginX").get_to(obstacle.safetyMarginX);
116 if (j.find(
"safetyMarginY") != j.end())
118 j.at(
"safetyMarginY").get_to(obstacle.safetyMarginY);
120 if (j.find(
"safetyMarginZ") != j.end())
122 j.at(
"safetyMarginZ").get_to(obstacle.safetyMarginZ);
124 if (j.find(
"curvatureX") != j.end())
126 j.at(
"curvatureX").get_to(obstacle.curvatureX);
128 if (j.find(
"curvatureY") != j.end())
130 j.at(
"curvatureY").get_to(obstacle.curvatureY);
132 if (j.find(
"curvatureZ") != j.end())
134 j.at(
"curvatureZ").get_to(obstacle.curvatureZ);
145 m_doa.cfg.only_2d =
true;
146 m_doa.cfg.aggregated =
true;
147 m_doa.cfg.agent_safety_margin = 0;
148 m_doa.cfg.local_modulation =
false;
149 m_doa.cfg.repulsion =
true;
150 m_doa.cfg.planar_modulation =
false;
151 m_doa.cfg.critical_distance = 1.0;
152 m_doa.cfg.weight_power = 2.0;
160 m_buf.needs_env_update =
false;
162 m_doa.env.set_aggregated(m_doa.cfg.aggregated);
164 if (not m_doa.load_obstacles_from_file.empty())
166 const std::filesystem::path scene_obstacles_path =
168 std::ifstream ifs(scene_obstacles_path);
169 nlohmann::json j = nlohmann::json::parse(ifs);
171 std::vector<obstacledetection::Obstacle> obstacles = j;
172 for (obstacledetection::Obstacle& obstacle : obstacles)
174 if (m_doa.cfg.only_2d)
177 obstacle.axisLengthZ = 0;
178 obstacle.refPosZ = 0;
179 obstacle.safetyMarginZ = 0;
180 obstacle.curvatureZ = 1;
183 sanity_check_obstacle(obstacle);
185 m_doa.env.add_obstacle(::create_doa_obstacle(obstacle));
204 this, &DSObstacleAvoidance::run_visualization, m_vis.task_interval);
208 if (m_watchdog.enabled)
211 this, &DSObstacleAvoidance::run_watchdog, m_watchdog.task_interval);
212 m_watchdog.task->start();
228 if (m_watchdog.enabled)
230 m_watchdog.task->stop();
250armarx::dsobstacleavoidance::Config
260 ARMARX_DEBUG <<
"Adding/updating obstacle: " << obstacle.name <<
".";
262 std::unique_lock l{m_buf.mutex};
264 sanity_check_obstacle(obstacle);
267 update.time = IceUtil::Time::now();
268 update.name = obstacle.name;
269 update.type = buffer::update_type::update;
271 m_buf.update_log.push_back(update);
272 m_buf.obstacles[obstacle.name] = obstacle;
273 m_buf.needs_env_update =
true;
275 ARMARX_DEBUG <<
"Added/updated obstacle: " << obstacle.name <<
".";
281 ARMARX_DEBUG <<
"Removing obstacle: " << obstacle_name <<
".";
283 std::unique_lock l{m_buf.mutex};
286 update.time = IceUtil::Time::now();
287 update.name = obstacle_name;
288 update.type = buffer::update_type::removal;
290 m_buf.update_log.push_back(update);
291 m_buf.needs_env_update =
true;
293 ARMARX_DEBUG <<
"Removed obstacle: " << obstacle_name <<
".";
296std::vector<armarx::obstacledetection::Obstacle>
300 std::shared_lock l{m_doa.mutex};
302 std::vector<obstacledetection::Obstacle> obstacle_list;
304 for (
auto& [doa_name, doa_obstacle] : m_doa.env)
307 std::shared_ptr<doa::Ellipsoid> doa_ellipsoid =
308 std::dynamic_pointer_cast<doa::Ellipsoid>(doa_obstacle);
309 obstacledetection::Obstacle obstacle;
311 obstacle.name = doa_name;
312 obstacle.posX = doa_ellipsoid->get_position()(0) * 1000;
313 obstacle.posY = doa_ellipsoid->get_position()(1) * 1000;
314 obstacle.posZ = doa_ellipsoid->get_position()(2) * 1000;
315 obstacle.yaw = simox::math::mat3f_to_rpy(
316 doa_ellipsoid->get_orientation().toRotationMatrix().cast<
float>())
318 obstacle.axisLengthX = doa_ellipsoid->get_axis_lengths(0) * 1000;
319 obstacle.axisLengthY = doa_ellipsoid->get_axis_lengths(1) * 1000;
320 obstacle.axisLengthZ = doa_ellipsoid->get_axis_lengths(2) * 1000;
321 obstacle.refPosX = doa_ellipsoid->get_reference_position()(0) * 1000;
322 obstacle.refPosY = doa_ellipsoid->get_reference_position()(1) * 1000;
323 obstacle.refPosZ = doa_ellipsoid->get_reference_position()(2) * 1000;
324 obstacle.safetyMarginX = doa_ellipsoid->get_safety_margin()(0) * 1000;
325 obstacle.safetyMarginY = doa_ellipsoid->get_safety_margin()(1) * 1000;
326 obstacle.safetyMarginZ = doa_ellipsoid->get_safety_margin()(2) * 1000;
327 obstacle.curvatureX = doa_ellipsoid->get_curvature_factor()(0);
328 obstacle.curvatureY = doa_ellipsoid->get_curvature_factor()(1);
329 obstacle.curvatureZ = doa_ellipsoid->get_curvature_factor()(2);
332 obstacle_list.push_back(std::move(obstacle));
336 return obstacle_list;
345 std::shared_lock l{m_doa.mutex};
348 doa::Agent doa_agent{agent.safety_margin};
350 Eigen::Vector3d agent_global_position = agent.pos.cast<
double>() / 1000;
351 Eigen::Vector3d agent_velocity = agent.desired_vel.cast<
double>() / 1000;
353 if (m_doa.cfg.only_2d)
355 agent_global_position(2) = 0;
356 agent_velocity(2) = 0;
359 doa_agent.set_position(agent_global_position);
360 doa_agent.set_linear_velocity(agent_velocity);
364 Eigen::Vector3d modulated_vel = doa::Modulation::modulate_velocity(doa_agent,
366 m_doa.cfg.local_modulation,
368 m_doa.cfg.planar_modulation,
369 m_doa.cfg.critical_distance,
370 m_doa.cfg.weight_power);
373 modulated_vel *= 1000;
376 if (m_doa.cfg.only_2d)
378 modulated_vel(2) = 0;
383 return modulated_vel.cast<
float>();
390 std::lock_guard l2{m_vis.mutex};
391 std::lock_guard l{m_doa.mutex};
394 std::vector<buffer::update> update_log;
395 std::map<std::string, obstacledetection::Obstacle> obstacles;
397 std::lock_guard l{m_buf.mutex};
399 if (not m_buf.needs_env_update)
405 update_log = m_buf.update_log;
406 obstacles = m_buf.obstacles;
407 m_buf.update_log.clear();
408 m_buf.obstacles.clear();
409 m_buf.needs_env_update =
false;
410 m_buf.last_env_update = IceUtil::Time::now();
417 doa::Environment::iterator it = m_doa.env.find(update.name);
421 case buffer::update_type::removal:
423 ARMARX_DEBUG <<
"Removing obstacle " << update.name <<
".";
424 if (it != m_doa.env.end())
426 m_doa.env.erase(update.name);
430 ARMARX_WARNING <<
"Tried to remove obstacle " << update.name <<
", but this "
431 <<
"obstacle is unknown to the environment.";
435 case buffer::update_type::update:
437 if (it != m_doa.env.end())
439 ARMARX_DEBUG <<
"Updating obstacle " << update.name <<
" with new values.";
440 m_doa.env.erase(update.name);
444 ARMARX_DEBUG <<
"Adding obstacle " << update.name <<
".";
447 const obstacledetection::Obstacle& obstacle = obstacles.at(update.name);
448 m_doa.env.add_obstacle(::create_doa_obstacle(obstacle));
457 m_vis.needs_update =
true;
460 ARMARX_VERBOSE <<
"Updating environment with " << update_log.size() <<
" updates took "
468 std::shared_lock l{m_doa.mutex};
470 const std::string filename_annotated =
getName() +
"_" + filename;
471 ARMARX_DEBUG <<
"Writing debug plots to `/tmp/" << filename_annotated <<
".png`.";
473 const bool show =
false;
476 doa::PlottingTools::plot_configuration(
477 m_doa.env.get_obstacle_list(), filename_annotated, show, not m_doa.cfg.only_2d);
481 ARMARX_WARNING <<
"Tried to write debug plots but failed, probably because "
482 <<
"`python3-matplotlib` are not installed.";
487armarx::DSObstacleAvoidance::sanity_check_obstacle(
488 const armarx::obstacledetection::Obstacle& obstacle)
const
490 ARMARX_DEBUG <<
"Sanity checking obstacle `" << obstacle.name <<
"`.";
492 const std::string curvature_error =
"Curvature must be greater than or equal to 1.";
493 const std::string z_comps_set_error =
"2D mode activated. Z-component values must not be set.";
499 if (m_doa.cfg.only_2d)
501 ARMARX_DEBUG <<
"Additionally sanity checking obstacle `" << obstacle.name
502 <<
"` for 2D compliance.";
512armarx::DSObstacleAvoidance::run_visualization()
515 using namespace armarx::viz;
517 std::lock_guard l{m_vis.mutex};
518 if (not m_vis.needs_update)
523 Layer layer_obs = arviz.layer(
"obstacles");
524 Layer layer_sms = arviz.layer(
"safety_margins");
525 Layer layer_rps = arviz.layer(
"reference_points");
526 Layer layer_bbs = arviz.layer(
"bounding_boxes");
528 for (
const obstacledetection::Obstacle& obstacle : getObstacles())
530 Color color = Color::orange(255, 128);
531 Color color_m = Color::orange(255, 64);
533 if (obstacle.name ==
"human")
535 color = Color::red(255, 128);
536 color_m = Color::red(255, 64);
538 else if (not m_doa.cfg.only_2d)
540 color = Color::blue(255, 128);
541 color_m = Color::blue(255, 64);
544 const double safetyMarginZ = m_doa.cfg.only_2d ? 1 : obstacle.safetyMarginZ;
545 const double posZ = m_doa.cfg.only_2d ? 1 : obstacle.posZ;
546 const double refPosZ = m_doa.cfg.only_2d ? 1 : obstacle.refPosZ;
547 const double axisLengthZ = m_doa.cfg.only_2d ? 1 : obstacle.axisLengthZ;
549 const Eigen::Matrix4f pose =
550 simox::math::pos_rpy_to_mat4f(obstacle.posX, obstacle.posY, posZ, 0, 0, obstacle.yaw);
551 const Eigen::Vector3f dim(obstacle.axisLengthX, obstacle.axisLengthY, axisLengthZ);
552 const Eigen::Vector3f sm(obstacle.safetyMarginX, obstacle.safetyMarginY, safetyMarginZ);
553 const Eigen::Vector3f curv(obstacle.curvatureX, obstacle.curvatureY, obstacle.curvatureZ);
555 if (m_doa.cfg.only_2d)
558 layer_obs.
add(Cylindroid{obstacle.name}
560 .axisLengths(dim.head<2>())
562 .curvature(curv.head<2>())
565 layer_sms.
add(Cylindroid{obstacle.name +
"_sm"}
567 .axisLengths((dim + sm).head<2>())
568 .height(dim(2) + sm(2))
569 .curvature(curv.head<2>())
575 Ellipsoid{obstacle.name}.pose(pose).axisLengths(dim).curvature(curv).color(color));
577 layer_sms.
add(Ellipsoid{obstacle.name +
"_sm"}
579 .axisLengths(dim + sm)
584 layer_rps.
add(
Sphere{obstacle.name +
"_rp"}
585 .position(Eigen::Vector3f(obstacle.refPosX, obstacle.refPosY, refPosZ))
587 .color(Color::green()));
589 layer_bbs.
add(
Box{obstacle.name +
"_bb"}.pose(pose).size(dim).color(color));
592 m_vis.needs_update =
false;
594 arviz.commit({layer_obs, layer_sms, layer_rps});
599armarx::DSObstacleAvoidance::run_watchdog()
601 bool needs_env_update;
602 bool was_recently_updated;
604 std::unique_lock l{m_buf.mutex};
605 needs_env_update = m_buf.needs_env_update;
606 was_recently_updated = IceUtil::Time::now() - m_buf.last_env_update <= m_watchdog.threshold;
609 if (needs_env_update and not was_recently_updated)
611 ARMARX_WARNING <<
"Environment has not been updated for over " << m_watchdog.threshold
612 <<
". Have you forgotten to call "
613 <<
"`updateEnvironment()` after adding, updating or removing obstacles? "
614 <<
"Forcing update now.";
625 def->optional(m_vis.enabled,
"visualize",
"Enable/disable visualization.");
626 def->optional(m_watchdog.enabled,
"udpate_watchdog",
"Run environment update watchdog.");
627 def->optional(m_doa.load_obstacles_from_file,
628 "load_obstacles_from",
629 "Path to JSON file to load initial obstacles from.");
632 def->optional(m_doa.cfg.only_2d,
"doa.only_2d",
"Only consider 2D.");
633 def->optional(m_doa.cfg.aggregated,
"doa.aggregated",
"Aggregated environment.");
634 def->optional(m_doa.cfg.agent_safety_margin,
"doa.agent_safety_margin",
"Agent safety margin.");
635 def->optional(m_doa.cfg.local_modulation,
"doa.local_modulation",
"Local modulation on/off.");
636 def->optional(m_doa.cfg.repulsion,
"doa.repulsion",
"Repulsion on/off.");
638 m_doa.cfg.planar_modulation,
"doa.planar_modulation",
"Planar modulation on/off.");
639 def->optional(m_doa.cfg.critical_distance,
"doa.critical_distance",
"Critical distance.");
640 def->optional(m_doa.cfg.weight_power,
"doa.weight_power",
"Weight power");
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
Default component property definition container.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
void onInitComponent() override
dsobstacleavoidance::Config getConfig(const Ice::Current &=Ice::emptyCurrent) override
void updateObstacle(const obstacledetection::Obstacle &obstacle, const Ice::Current &=Ice::emptyCurrent) override
static const std::string default_name
void onDisconnectComponent() override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void updateEnvironment(const Ice::Current &=Ice::emptyCurrent) override
void onConnectComponent() override
void writeDebugPlots(const std::string &filename, const Ice::Current &=Ice::emptyCurrent) override
Eigen::Vector3f modulateVelocity(const obstacleavoidance::Agent &agent, const Ice::Current &=Ice::emptyCurrent) override
void removeObstacle(const std::string &obstacle_name, const Ice::Current &=Ice::emptyCurrent) override
void onExitComponent() override
std::string getDefaultName() const override
std::vector< obstacledetection::Obstacle > getObstacles(const Ice::Current &=Ice::emptyCurrent) override
std::string getName() const
Retrieve name of object.
The periodic task executes one thread method repeatedly using the time period specified in the constr...
Measures the passed time between the construction or calling reset() and stop().
Duration stop()
Stops the timer and returns the measured duration.
uint32_t Color
RGBA color.
#define ARMARX_CHECK_GREATER_EQUAL(lhs, rhs)
This macro evaluates whether lhs is greater or equal (>=) rhs and if it turns out to be false it will...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#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.
Quaternion< double, 0 > Quaterniond
void from_json(const nlohmann::json &j, armarx::obstacledetection::Obstacle &obstacle)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void add(ElementT const &element)