35 #include <Eigen/Geometry>
38 #include <SimoxUtility/math.h>
49 v = std::numeric_limits<float>::infinity();
53 invalidate(Eigen::Vector2f&
v)
55 v.x() = std::numeric_limits<float>::infinity();
56 v.y() = std::numeric_limits<float>::infinity();
60 invalidate(Eigen::Vector3f&
v)
62 v.x() = std::numeric_limits<float>::infinity();
63 v.y() = std::numeric_limits<float>::infinity();
64 v.z() = std::numeric_limits<float>::infinity();
69 invalidate(std::deque<T>& d)
92 "ObstacleAvoidingPlatformUnit";
113 if (!hasRobot(
"robot"))
115 m_robot = addRobot(
"robot", VirtualRobot::RobotIO::eStructure);
118 invalidate(m_control_data.target_vel);
119 invalidate(m_control_data.target_rot_vel);
120 invalidate(m_control_data.target_pos);
121 invalidate(m_control_data.target_ori);
122 invalidate(m_viz.start);
123 invalidate(m_control_data.vel_history);
133 schedule_high_level_control_loop(control_mode::none);
146 const float target_pos_y,
147 const float target_ori,
148 const float pos_reached_threshold,
149 const float ori_reached_threshold,
152 using namespace simox::math;
154 std::scoped_lock l{m_control_data.mutex};
156 m_control_data.target_pos = Eigen::Vector2f{target_pos_x, target_pos_y};
157 m_control_data.target_ori = periodic_clamp<float>(target_ori, -
M_PI,
M_PI);
158 m_control_data.pos_reached_threshold = pos_reached_threshold;
159 m_control_data.ori_reached_threshold = ori_reached_threshold;
162 invalidate(m_control_data.vel_history);
164 invalidate(m_control_data.target_vel);
165 invalidate(m_control_data.target_rot_vel);
167 schedule_high_level_control_loop(control_mode::position);
172 const float target_vel_y,
173 const float target_rot_vel,
176 using namespace simox::math;
178 std::scoped_lock l{m_control_data.mutex};
180 m_control_data.target_vel = Eigen::Vector2f{target_vel_x, target_vel_y};
181 m_control_data.target_rot_vel = periodic_clamp<float>(target_rot_vel, -
M_PI,
M_PI);
183 invalidate(m_control_data.target_pos);
184 invalidate(m_control_data.target_ori);
189 schedule_high_level_control_loop(control_mode::velocity);
194 const float target_pos_delta_y,
195 const float target_delta_ori,
196 const float pos_reached_threshold,
197 const float ori_reached_threshold,
198 const Ice::Current& current)
200 using namespace simox::math;
203 std::unique_lock lock{m_control_data.mutex};
204 synchronizeLocalClone(m_robot);
205 const Eigen::Vector2f agent_pos = m_robot->getGlobalPosition().head<2>();
206 const float agent_ori = mat4f_to_rpy(m_robot->getGlobalPose()).z();
210 moveTo(agent_pos.x() + target_pos_delta_x,
211 agent_pos.y() + target_pos_delta_y,
212 agent_ori + target_delta_ori,
213 pos_reached_threshold,
214 ori_reached_threshold,
220 const float max_rot_vel,
223 std::scoped_lock l{m_control_data.mutex};
224 m_control_data.max_vel = max_pos_vel;
225 m_control_data.max_rot_vel = max_rot_vel;
226 m_platform->setMaxVelocities(max_pos_vel, max_rot_vel);
232 schedule_high_level_control_loop(control_mode::none);
233 m_platform->stopPlatform();
237 armarx::ObstacleAvoidingPlatformUnit::schedule_high_level_control_loop(control_mode mode)
239 std::scoped_lock l{m_control_loop.mutex};
242 if (m_control_loop.mode == mode)
248 if (m_control_loop.mode != mode and m_control_loop.task)
251 const bool join =
true;
252 m_control_loop.task->stop(join);
256 if (mode == control_mode::none)
264 m_control_loop.mode = mode;
265 m_control_loop.task =
new RunningTask<ObstacleAvoidingPlatformUnit>(
266 this, &ObstacleAvoidingPlatformUnit::high_level_control_loop);
267 m_control_loop.task->start();
271 armarx::ObstacleAvoidingPlatformUnit::high_level_control_loop()
273 const control_mode mode = m_control_loop.mode;
278 CycleUtil cu{m_control_loop.cycle_time};
279 while (not m_control_loop.task->isStopped())
281 const velocities vels = get_velocities();
285 m[
"err_dist"] =
new Variant{vels.err_dist};
286 m[
"err_angular_dist"] =
new Variant{vels.err_angular_dist};
288 m[
"target_global_x"] =
new Variant{vels.target_global.x()};
289 m[
"target_global_y"] =
new Variant{vels.target_global.y()};
290 m[
"target_global_abs"] =
new Variant{vels.target_global.norm()};
292 m[
"target_local_x"] =
new Variant{vels.target_local.x()};
293 m[
"target_local_y"] =
new Variant{vels.target_local.y()};
294 m[
"target_local_abs"] =
new Variant(vels.target_local.norm());
295 m[
"target_rot"] =
new Variant{vels.target_rot};
297 m[
"modulated_global_x"] =
new Variant{vels.modulated_global.x()};
298 m[
"modulated_global_y"] =
new Variant{vels.modulated_global.y()};
299 m[
"modulated_global_abs"] =
new Variant{vels.modulated_global.norm()};
301 m[
"modulated_local_x"] =
new Variant{vels.modulated_local.x()};
302 m[
"modulated_local_y"] =
new Variant{vels.modulated_local.y()};
303 m[
"modulated_local_abs"] =
new Variant{vels.modulated_local.norm()};
305 setDebugObserverChannel(
"ObstacleAvoidingPlatformCtrl", m);
307 m_platform->move(vels.modulated_local.x(), vels.modulated_local.y(), vels.target_rot);
309 cu.waitForCycleDuration();
312 catch (
const std::exception& e)
314 ARMARX_ERROR <<
"Error occured while running control loop.\n" << e.what();
318 ARMARX_ERROR <<
"Unknown error occured while running control loop.";
322 invalidate(m_control_data.vel_history);
324 m_platform->move(0, 0, 0);
325 m_platform->stopPlatform();
326 m_control_loop.mode = control_mode::none;
333 armarx::ObstacleAvoidingPlatformUnit::velocities
334 armarx::ObstacleAvoidingPlatformUnit::get_velocities()
336 using namespace simox::math;
339 std::scoped_lock l{m_control_data.mutex};
342 update_agent_dependent_values();
343 const Eigen::Vector2f target_vel = get_target_velocity();
344 const float target_rot_vel = get_target_rotational_velocity();
347 const Eigen::Vector2f modulated_vel = [
this, &target_vel]
349 obstacleavoidance::Agent agent;
350 agent.safety_margin = m_control_data.agent_safety_margin;
351 agent.pos = Eigen::Vector3f{m_control_data.agent_pos.x(), m_control_data.agent_pos.y(), 0};
352 agent.desired_vel = Eigen::Vector3f{target_vel.x(), target_vel.y(), 0};
354 const Eigen::Vector2f raw = m_obstacle_avoidance->modulateVelocity(agent).head<2>();
355 return post_process_vel(target_vel, norm_max(raw, m_control_data.max_vel));
358 ARMARX_CHECK(modulated_vel.allFinite()) <<
"Velocity contains non-finite values.";
361 ARMARX_DEBUG <<
"Target velocity: " << target_vel.transpose()
362 <<
"; norm: " << target_vel.norm() <<
"; " << target_rot_vel;
363 ARMARX_DEBUG <<
"Modulated velocity: " << modulated_vel.transpose() << modulated_vel.norm();
365 const auto r = Eigen::Rotation2D(m_control_data.agent_ori).toRotationMatrix().inverse();
368 vels.target_local = r * target_vel;
369 vels.target_global = target_vel;
370 vels.modulated_local = r * modulated_vel;
371 vels.modulated_global = modulated_vel;
372 vels.target_rot = target_rot_vel;
373 vels.err_dist = m_control_data.target_dist;
374 vels.err_angular_dist = m_control_data.target_angular_dist;
380 armarx::ObstacleAvoidingPlatformUnit::get_target_velocity()
const
382 using namespace simox::math;
384 Eigen::Vector2f uncapped_target_vel = Eigen::Vector2f::Zero();
386 if (m_control_loop.mode == control_mode::position )
388 uncapped_target_vel =
389 (m_control_data.target_pos - m_control_data.agent_pos) * m_control_data.kp;
391 else if (m_control_loop.mode == control_mode::velocity)
393 uncapped_target_vel = m_control_data.target_vel;
398 return norm_max(uncapped_target_vel, m_control_data.max_vel);
402 armarx::ObstacleAvoidingPlatformUnit::get_target_rotational_velocity()
const
404 using namespace simox::math;
406 float uncapped_target_rot_vel = 0;
408 if (m_control_loop.mode == control_mode::position )
410 m_rot_pid_controller.update(m_control_data.target_angular_dist, 0);
411 uncapped_target_rot_vel = -m_rot_pid_controller.getControlValue();
413 else if (m_control_loop.mode == control_mode::velocity)
415 uncapped_target_rot_vel = m_control_data.target_rot_vel;
420 return std::copysign(
std::min(std::fabs(uncapped_target_rot_vel), m_control_data.max_rot_vel),
421 uncapped_target_rot_vel);
425 armarx::ObstacleAvoidingPlatformUnit::update_agent_dependent_values()
427 using namespace simox::math;
429 synchronizeLocalClone(m_robot);
430 m_control_data.agent_pos = m_robot->getGlobalPosition().head<2>();
431 m_control_data.agent_ori =
432 periodic_clamp<float>(mat4f_to_rpy(m_robot->getGlobalPose()).z(), -
M_PI,
M_PI);
438 if (m_control_loop.mode == control_mode::position)
445 m_control_data.target_dist = (m_control_data.target_pos - m_control_data.agent_pos).
norm();
446 m_control_data.target_angular_dist = periodic_clamp<float>(
447 m_control_data.target_ori - m_control_data.agent_ori, -
M_PI,
M_PI);
452 ARMARX_DEBUG <<
"Distance to target: " << m_control_data.target_dist <<
" mm and "
453 << m_control_data.target_angular_dist <<
" rad.";
458 invalidate(m_control_data.target_dist);
459 invalidate(m_control_data.target_angular_dist);
464 armarx::ObstacleAvoidingPlatformUnit::target_position_reached()
const
466 if (m_control_loop.mode == control_mode::position)
468 return m_control_data.target_dist < m_control_data.pos_reached_threshold;
476 armarx::ObstacleAvoidingPlatformUnit::target_orientation_reached()
const
478 if (m_control_loop.mode == control_mode::position)
480 return std::fabs(m_control_data.target_angular_dist) < m_control_data.ori_reached_threshold;
488 armarx::ObstacleAvoidingPlatformUnit::target_reached()
const
490 if (m_control_loop.mode == control_mode::position)
492 return target_position_reached() and target_orientation_reached();
499 armarx::ObstacleAvoidingPlatformUnit::post_process_vel(const
Eigen::Vector2f& target_vel,
500 const
Eigen::Vector2f& modulated_vel)
506 m_control_data.vel_history.push_front(std::make_tuple(target_vel, modulated_vel));
507 const unsigned max_real_buffer_size =
508 std::max(m_control_data.amount_smoothing, m_control_data.amount_max_vel);
509 if (m_control_data.vel_history.size() > max_real_buffer_size)
511 m_control_data.vel_history.resize(max_real_buffer_size);
515 const Eigen::Vector2f mean_modulated_vel = calculate_mean_modulated_vel();
517 const bool is_near_target = this->is_near_target(mean_modulated_vel);
521 is_near_target ? m_control_data.min_vel_near_target : m_control_data.min_vel_general;
522 if (target_vel.norm() < min_vel)
524 min_vel = target_vel.norm();
532 const float max_vel = is_near_target ? target_vel.norm() : calculate_adaptive_max_vel();
538 return simox::math::norm_clamp(mean_modulated_vel, min_vel, max_vel);
542 armarx::ObstacleAvoidingPlatformUnit::is_near_target(
543 const Eigen::Vector2f& control_velocity)
const noexcept
545 if (m_control_data.target_dist < m_control_data.pos_near_threshold)
547 const Eigen::Vector2f target_direction =
548 m_control_data.target_pos - m_control_data.agent_pos;
549 const Eigen::Vector2f control_direction = control_velocity / control_velocity.norm();
551 const float sim = simox::math::cosine_similarity(target_direction, control_direction);
554 if (sim > cos(M_PI_4))
564 armarx::ObstacleAvoidingPlatformUnit::calculate_mean_modulated_vel()
const
566 const unsigned adaptive_buffer_size = calculate_adaptive_smoothing_buffer_size();
567 const unsigned elements =
568 std::min<unsigned>(m_control_data.vel_history.size(), adaptive_buffer_size);
569 auto end = m_control_data.vel_history.begin();
570 std::advance(end, elements);
573 [elements](
const Eigen::Vector2f& vel,
574 const std::tuple<Eigen::Vector2f, Eigen::Vector2f>& vels) -> Eigen::Vector2f
575 {
return vel + std::get<1>(vels) * (1. / elements); };
577 return std::accumulate(
578 m_control_data.vel_history.begin(), end, Eigen::Vector2f{0, 0},
transform);
582 armarx::ObstacleAvoidingPlatformUnit::calculate_adaptive_smoothing_buffer_size()
const
585 const float min_buffer_size_dist = 200;
586 const float min_buffer_size = 3;
588 const float max_buffer_size_dist = 1500;
589 const float max_buffer_size = m_control_data.amount_smoothing;
593 if (m_control_loop.mode == control_mode::position)
597 (max_buffer_size - min_buffer_size) / (max_buffer_size_dist - min_buffer_size_dist);
598 const float b = min_buffer_size - (m * min_buffer_size_dist);
602 return static_cast<unsigned>(
603 std::clamp(m * m_control_data.target_dist + b, min_buffer_size, max_buffer_size));
608 return min_buffer_size;
613 armarx::ObstacleAvoidingPlatformUnit::calculate_adaptive_max_vel()
const
615 using namespace simox::math;
617 if (m_control_loop.mode == control_mode::position and m_control_data.adaptive_max_vel_exp > 0)
619 std::vector<float> angular_similarities;
621 const unsigned elements =
622 std::min<unsigned>(m_control_data.vel_history.size(), m_control_data.amount_max_vel);
623 auto end = m_control_data.vel_history.begin();
624 std::advance(end, elements);
627 [elements](
const std::tuple<Eigen::Vector2f, Eigen::Vector2f>& vels) ->
float
629 const auto& [desired_vel, modulated_vel] = vels;
631 if (desired_vel.isZero() and modulated_vel.isZero())
635 else if (desired_vel.isZero() xor modulated_vel.isZero())
640 return angular_similarity(desired_vel, modulated_vel) / elements;
652 std::back_inserter(angular_similarities),
655 const float mean_angular_similarity = std::accumulate(
656 angular_similarities.begin(), angular_similarities.end(), 0.f, std::plus<float>());
657 const float max_vel_factor =
658 std::pow(mean_angular_similarity, m_control_data.adaptive_max_vel_exp);
660 return max_vel_factor * m_control_data.max_vel;
664 return m_control_data.max_vel;
669 armarx::ObstacleAvoidingPlatformUnit::visualize()
671 const Eigen::Vector2f zero = Eigen::Vector2f::Zero();
673 vels.target_local = zero;
674 vels.target_global = zero;
675 vels.modulated_local = zero;
676 vels.modulated_global = zero;
683 armarx::ObstacleAvoidingPlatformUnit::visualize(
const velocities& vels)
687 if (not m_viz.enabled)
692 Eigen::Vector3f agent_pos{m_control_data.agent_pos.x(), m_control_data.agent_pos.y(), 0};
695 Layer l_prog = arviz.layer(
"progress");
696 if (m_control_loop.mode == control_mode::position)
698 const float min_keypoint_dist = 50;
701 if (not m_viz.start.allFinite())
703 m_viz.start = agent_pos;
706 const Eigen::Vector3f& last_keypoint_pos =
707 m_viz.path.
size() >= 1 ? m_viz.path.back() : m_viz.start;
710 if ((last_keypoint_pos - agent_pos).
norm() > min_keypoint_dist)
712 m_viz.path.push_back(agent_pos);
716 if (not target_reached())
723 for (
unsigned i = 0; i < m_viz.path.size(); ++i)
726 .position(m_viz.path[i])
732 const Eigen::Vector3f
target{
733 m_control_data.target_pos.x(), m_control_data.target_pos.y(), 0};
743 invalidate(m_viz.start);
748 Layer l_vels = arviz.layer(
"velocities");
749 if (m_control_loop.mode != control_mode::none)
751 const float min_velocity = 15;
752 const Eigen::Vector3f from1{agent_pos + Eigen::Vector3f{0, 0, 2000}};
753 const Eigen::Vector3f from2 = from1 + Eigen::Vector3f{0, 0, 200};
754 const Eigen::Vector3f original{vels.target_global.x(), vels.target_global.y(), 0};
755 const Eigen::Vector3f modulated{vels.modulated_global.x(), vels.modulated_global.y(), 0};
757 if (original.norm() > min_velocity)
760 .
fromTo(from1, from1 + original * 5)
765 if (modulated.norm() > min_velocity)
768 .
fromTo(from2, from2 + modulated * 5)
775 Layer l_agnt = arviz.layer(
"agent");
776 if (m_control_loop.mode != control_mode::none)
781 if (m_control_data.agent_safety_margin > 0)
784 .
pose(simox::math::pos_rpy_to_mat4f(agent_pos, -M_PI_2, 0, 0))
786 .radius(m_control_data.agent_safety_margin)
791 arviz.commit({l_prog, l_vels, l_agnt});
799 def->component(m_platform,
"Platform");
800 def->component(m_obstacle_avoidance,
"PlatformObstacleAvoidance");
803 def->optional(m_control_data.adaptive_max_vel_exp,
804 "adaptive_max_vel_exponent",
805 "Adaptive max vel exponent. This throttles the max_vel adaptively "
806 "depending on the angle between target velocity and modulated velocity. "
808 def->optional(m_control_data.min_vel_near_target,
809 "min_vel_near_target",
810 "Velocity in [mm/s] "
811 "the robot should at least set when near the target");
812 def->optional(m_control_data.min_vel_general,
814 "Velocity in [mm/s] the robot "
815 "should at least set on general");
816 def->optional(m_control_data.pos_near_threshold,
817 "pos_near_threshold",
818 "Distance in [mm] after "
819 "which the robot is considered to be near the target for min velocity, "
823 def->optional(m_control_data.kp,
"control.pos.kp");
824 def->optional(m_rot_pid_controller.Kp,
"control.rot.kp");
825 def->optional(m_rot_pid_controller.Ki,
"control.rot.ki");
826 def->optional(m_rot_pid_controller.Kd,
"control.rot.kd");
827 def->optional(m_control_loop.cycle_time,
"control.pose.cycle_time",
"Control loop cycle time.");
830 def->optional(m_control_data.agent_safety_margin,
"doa.agent_safety_margin");