moveit2
The MoveIt Motion Planning Framework for ROS 2.
trajectory_execution_manager.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Ioan Sucan */
36 
39 #include <geometric_shapes/check_isometry.h>
40 #include <tf2_eigen/tf2_eigen.hpp>
41 #include <moveit/utils/logger.hpp>
42 
44 {
45 
46 const std::string TrajectoryExecutionManager::EXECUTION_EVENT_TOPIC = "trajectory_execution_event";
47 
48 static const auto DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE = rclcpp::Duration::from_seconds(1);
49 static const double DEFAULT_CONTROLLER_GOAL_DURATION_MARGIN = 0.5; // allow 0.5s more than the expected execution time
50  // before triggering a trajectory cancel (applied
51  // after scaling)
52 static const double DEFAULT_CONTROLLER_GOAL_DURATION_SCALING =
53  1.1; // allow the execution of a trajectory to take more time than expected (scaled by a value > 1)
54 
55 TrajectoryExecutionManager::TrajectoryExecutionManager(const rclcpp::Node::SharedPtr& node,
56  const moveit::core::RobotModelConstPtr& robot_model,
57  const planning_scene_monitor::CurrentStateMonitorPtr& csm)
58  : node_(node), logger_(moveit::getLogger("trajectory_execution_manager")), robot_model_(robot_model), csm_(csm)
59 {
60  if (!node_->get_parameter("moveit_manage_controllers", manage_controllers_))
61  manage_controllers_ = false;
62  initialize();
63 }
64 
65 TrajectoryExecutionManager::TrajectoryExecutionManager(const rclcpp::Node::SharedPtr& node,
66  const moveit::core::RobotModelConstPtr& robot_model,
67  const planning_scene_monitor::CurrentStateMonitorPtr& csm,
68  bool manage_controllers)
69  : node_(node)
70  , logger_(moveit::getLogger("trajectory_execution_manager"))
71  , robot_model_(robot_model)
72  , csm_(csm)
73  , manage_controllers_(manage_controllers)
74 {
75  initialize();
76 }
77 
79 {
80  stopExecution(true);
81  if (private_executor_)
82  private_executor_->cancel();
83  if (private_executor_thread_.joinable())
84  private_executor_thread_.join();
85 }
86 
87 void TrajectoryExecutionManager::initialize()
88 {
89  verbose_ = false;
90  execution_complete_ = true;
91  current_context_ = -1;
93  execution_duration_monitoring_ = true;
94  execution_velocity_scaling_ = 1.0;
95  allowed_start_tolerance_ = 0.01;
96  wait_for_trajectory_completion_ = true;
97 
98  allowed_execution_duration_scaling_ = DEFAULT_CONTROLLER_GOAL_DURATION_SCALING;
99  allowed_goal_duration_margin_ = DEFAULT_CONTROLLER_GOAL_DURATION_MARGIN;
100 
101  // load controller-specific values for allowed_execution_duration_scaling and allowed_goal_duration_margin
102  loadControllerParams();
103 
104  // load the controller manager plugin
105  try
106  {
107  controller_manager_loader_ =
108  std::make_unique<pluginlib::ClassLoader<moveit_controller_manager::MoveItControllerManager>>(
109  "moveit_core", "moveit_controller_manager::MoveItControllerManager");
110  }
111  catch (pluginlib::PluginlibException& ex)
112  {
113  RCLCPP_FATAL_STREAM(logger_, "Exception while creating controller manager plugin loader: " << ex.what());
114  return;
115  }
116 
117  if (controller_manager_loader_)
118  {
119  std::string controller;
120 
121  if (!node_->get_parameter("moveit_controller_manager", controller))
122  {
123  const std::vector<std::string>& classes = controller_manager_loader_->getDeclaredClasses();
124  if (classes.size() == 1)
125  {
126  controller = classes[0];
127  RCLCPP_WARN(logger_,
128  "Parameter '~moveit_controller_manager' is not specified but only one "
129  "matching plugin was found: '%s'. Using that one.",
130  controller.c_str());
131  }
132  else
133  {
134  RCLCPP_FATAL(logger_, "Parameter '~moveit_controller_manager' not specified. This is needed to "
135  "identify the plugin to use for interacting with controllers. No paths can "
136  "be executed.");
137  return;
138  }
139  }
140 
141  // Deprecation warnings, October 2022
142  if (controller == "moveit_ros_control_interface/MoveItControllerManager")
143  {
144  RCLCPP_WARN(logger_, "moveit_ros_control_interface/MoveItControllerManager is deprecated. Replace with "
145  "`moveit_ros_control_interface/Ros2ControlManager.`");
146  }
147  if (controller == "moveit_ros_control_interface/MoveItMultiControllerManager")
148  {
149  RCLCPP_WARN(logger_, "moveit_ros_control_interface/MoveItMultiControllerManager is deprecated. Replace with "
150  "`moveit_ros_control_interface/Ros2ControlMultiManager.`");
151  }
152 
153  if (!controller.empty())
154  {
155  try
156  {
157  // We make a node called moveit_simple_controller_manager so it's able to
158  // receive callbacks on another thread. We then copy parameters from the move_group node
159  // and then add it to the multithreadedexecutor
160  rclcpp::NodeOptions opt;
161  opt.allow_undeclared_parameters(true);
162  opt.automatically_declare_parameters_from_overrides(true);
163  controller_mgr_node_.reset(new rclcpp::Node("moveit_simple_controller_manager", opt));
164 
165  auto all_params = node_->get_node_parameters_interface()->get_parameter_overrides();
166  for (const auto& param : all_params)
167  controller_mgr_node_->set_parameter(rclcpp::Parameter(param.first, param.second));
168 
169  controller_manager_ = controller_manager_loader_->createUniqueInstance(controller);
170  controller_manager_->initialize(controller_mgr_node_);
171  private_executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
172  private_executor_->add_node(controller_mgr_node_);
173 
174  // start executor on a different thread now
175  private_executor_thread_ = std::thread([this]() { private_executor_->spin(); });
176  }
177  catch (pluginlib::PluginlibException& ex)
178  {
179  RCLCPP_FATAL_STREAM(logger_, "Exception while loading controller manager '" << controller << "': " << ex.what());
180  }
181  }
182  }
183 
184  // other configuration steps
185  reloadControllerInformation();
186  // The default callback group for rclcpp::Node is MutuallyExclusive which means we cannot call
187  // receiveEvent while processing a different callback. To fix this we create a new callback group (the type is not
188  // important since we only use it to process one callback) and associate event_topic_subscriber_ with this callback group
189  auto callback_group = node_->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
190  auto options = rclcpp::SubscriptionOptions();
191  options.callback_group = callback_group;
192  event_topic_subscriber_ = node_->create_subscription<std_msgs::msg::String>(
193  EXECUTION_EVENT_TOPIC, rclcpp::SystemDefaultsQoS(),
194  [this](const std_msgs::msg::String::ConstSharedPtr& event) { return receiveEvent(event); }, options);
195 
196  controller_mgr_node_->get_parameter("trajectory_execution.execution_duration_monitoring",
197  execution_duration_monitoring_);
198  controller_mgr_node_->get_parameter("trajectory_execution.allowed_execution_duration_scaling",
199  allowed_execution_duration_scaling_);
200  controller_mgr_node_->get_parameter("trajectory_execution.allowed_goal_duration_margin",
201  allowed_goal_duration_margin_);
202  controller_mgr_node_->get_parameter("trajectory_execution.allowed_start_tolerance", allowed_start_tolerance_);
203 
204  if (manage_controllers_)
205  {
206  RCLCPP_INFO(logger_, "Trajectory execution is managing controllers");
207  }
208  else
209  {
210  RCLCPP_INFO(logger_, "Trajectory execution is not managing controllers");
211  }
212 
213  auto controller_mgr_parameter_set_callback = [this](const std::vector<rclcpp::Parameter>& parameters) {
214  auto result = rcl_interfaces::msg::SetParametersResult();
215  result.successful = true;
216  for (const auto& parameter : parameters)
217  {
218  const std::string& name = parameter.get_name();
219  if (name == "trajectory_execution.execution_duration_monitoring")
220  {
221  enableExecutionDurationMonitoring(parameter.as_bool());
222  }
223  else if (name == "trajectory_execution.allowed_execution_duration_scaling")
224  {
225  setAllowedExecutionDurationScaling(parameter.as_double());
226  }
227  else if (name == "trajectory_execution.allowed_goal_duration_margin")
228  {
229  setAllowedGoalDurationMargin(parameter.as_double());
230  }
231  else if (name == "trajectory_execution.execution_velocity_scaling")
232  {
233  setExecutionVelocityScaling(parameter.as_double());
234  }
235  else if (name == "trajectory_execution.allowed_start_tolerance")
236  {
237  setAllowedStartTolerance(parameter.as_double());
238  }
239  else if (name == "trajectory_execution.wait_for_trajectory_completion")
240  {
241  setWaitForTrajectoryCompletion(parameter.as_bool());
242  }
243  else
244  {
245  result.successful = false;
246  }
247  }
248  return result;
249  };
250  callback_handler_ = controller_mgr_node_->add_on_set_parameters_callback(controller_mgr_parameter_set_callback);
251 }
252 
254 {
255  execution_duration_monitoring_ = flag;
256 }
257 
259 {
260  allowed_execution_duration_scaling_ = scaling;
261 }
262 
264 {
265  allowed_goal_duration_margin_ = margin;
266 }
267 
269 {
270  execution_velocity_scaling_ = scaling;
271 }
272 
274 {
275  allowed_start_tolerance_ = tolerance;
276 }
277 
279 {
280  wait_for_trajectory_completion_ = flag;
281 }
282 
284 {
285  return manage_controllers_;
286 }
287 
288 const moveit_controller_manager::MoveItControllerManagerPtr& TrajectoryExecutionManager::getControllerManager() const
289 {
290  return controller_manager_;
291 }
292 
293 void TrajectoryExecutionManager::processEvent(const std::string& event)
294 {
295  if (event == "stop")
296  {
297  stopExecution(true);
298  }
299  else
300  {
301  RCLCPP_WARN_STREAM(logger_, "Unknown event type: '" << event << '\'');
302  }
303 }
304 
305 void TrajectoryExecutionManager::receiveEvent(const std_msgs::msg::String::ConstSharedPtr& event)
306 {
307  RCLCPP_INFO_STREAM(logger_, "Received event '" << event->data << '\'');
308  processEvent(event->data);
309 }
310 
311 bool TrajectoryExecutionManager::push(const moveit_msgs::msg::RobotTrajectory& trajectory, const std::string& controller)
312 {
313  if (controller.empty())
314  {
315  return push(trajectory, std::vector<std::string>());
316  }
317  else
318  {
319  return push(trajectory, std::vector<std::string>(1, controller));
320  }
321 }
322 
323 bool TrajectoryExecutionManager::push(const trajectory_msgs::msg::JointTrajectory& trajectory,
324  const std::string& controller)
325 {
326  if (controller.empty())
327  {
328  return push(trajectory, std::vector<std::string>());
329  }
330  else
331  {
332  return push(trajectory, std::vector<std::string>(1, controller));
333  }
334 }
335 
336 bool TrajectoryExecutionManager::push(const trajectory_msgs::msg::JointTrajectory& trajectory,
337  const std::vector<std::string>& controllers)
338 {
339  moveit_msgs::msg::RobotTrajectory traj;
340  traj.joint_trajectory = trajectory;
341  return push(traj, controllers);
342 }
343 
344 bool TrajectoryExecutionManager::push(const moveit_msgs::msg::RobotTrajectory& trajectory,
345  const std::vector<std::string>& controllers)
346 {
347  if (!execution_complete_)
348  {
349  RCLCPP_ERROR(logger_, "Cannot push a new trajectory while another is being executed");
350  return false;
351  }
352 
354  if (configure(*context, trajectory, controllers))
355  {
356  if (verbose_)
357  {
358  std::stringstream ss;
359  ss << "Pushed trajectory for execution using controllers [ ";
360  for (const std::string& controller : context->controllers_)
361  ss << controller << ' ';
362  ss << "]:" << '\n';
363  // TODO: Provide message serialization
364  // for (const moveit_msgs::msg::RobotTrajectory& trajectory_part : context->trajectory_parts_)
365  // ss << trajectory_part << '\n';
366  RCLCPP_INFO_STREAM(logger_, ss.str());
367  }
368  trajectories_.push_back(context);
369  return true;
370  }
371  else
372  {
373  delete context;
375  }
376 
377  return false;
378 }
379 
380 void TrajectoryExecutionManager::reloadControllerInformation()
381 {
382  known_controllers_.clear();
383  if (controller_manager_)
384  {
385  std::vector<std::string> names;
386  controller_manager_->getControllersList(names);
387  for (const std::string& name : names)
388  {
389  std::vector<std::string> joints;
390  controller_manager_->getControllerJoints(name, joints);
391  ControllerInformation ci;
392  ci.name_ = name;
393  ci.joints_.insert(joints.begin(), joints.end());
394  known_controllers_[ci.name_] = ci;
395  }
396 
397  names.clear();
398  controller_manager_->getActiveControllers(names);
399  for (const auto& active_name : names)
400  {
401  auto found_it = std::find_if(known_controllers_.begin(), known_controllers_.end(),
402  [&](const auto& known_controller) { return known_controller.first == active_name; });
403  if (found_it != known_controllers_.end())
404  {
405  found_it->second.state_.active_ = true;
406  }
407  }
408 
409  for (std::map<std::string, ControllerInformation>::iterator it = known_controllers_.begin();
410  it != known_controllers_.end(); ++it)
411  {
412  for (std::map<std::string, ControllerInformation>::iterator jt = known_controllers_.begin();
413  jt != known_controllers_.end(); ++jt)
414  {
415  if (it != jt)
416  {
417  std::vector<std::string> intersect;
418  std::set_intersection(it->second.joints_.begin(), it->second.joints_.end(), jt->second.joints_.begin(),
419  jt->second.joints_.end(), std::back_inserter(intersect));
420  if (!intersect.empty())
421  {
422  it->second.overlapping_controllers_.insert(jt->first);
423  jt->second.overlapping_controllers_.insert(it->first);
424  }
425  }
426  }
427  }
428  }
429  else
430  {
431  RCLCPP_ERROR(logger_, "Failed to reload controllers: `controller_manager_` does not exist.");
432  }
433 }
434 
435 void TrajectoryExecutionManager::updateControllerState(const std::string& controller, const rclcpp::Duration& age)
436 {
437  std::map<std::string, ControllerInformation>::iterator it = known_controllers_.find(controller);
438  if (it != known_controllers_.end())
439  {
440  updateControllerState(it->second, age);
441  }
442  else
443  {
444  RCLCPP_ERROR(logger_, "Controller '%s' is not known.", controller.c_str());
445  }
446 }
447 
448 void TrajectoryExecutionManager::updateControllerState(ControllerInformation& ci, const rclcpp::Duration& age)
449 {
450  if (node_->now() - ci.last_update_ >= age)
451  {
452  if (controller_manager_)
453  {
454  if (verbose_)
455  RCLCPP_INFO(logger_, "Updating information for controller '%s'.", ci.name_.c_str());
456  ci.state_ = controller_manager_->getControllerState(ci.name_);
457  ci.last_update_ = node_->now();
458  }
459  }
460  else if (verbose_)
461  RCLCPP_INFO(logger_, "Information for controller '%s' is assumed to be up to date.", ci.name_.c_str());
462 }
463 
464 void TrajectoryExecutionManager::updateControllersState(const rclcpp::Duration& age)
465 {
466  for (std::pair<const std::string, ControllerInformation>& known_controller : known_controllers_)
467  updateControllerState(known_controller.second, age);
468 }
469 
470 bool TrajectoryExecutionManager::checkControllerCombination(std::vector<std::string>& selected,
471  const std::set<std::string>& actuated_joints)
472 {
473  std::set<std::string> combined_joints;
474  for (const std::string& controller : selected)
475  {
476  const ControllerInformation& ci = known_controllers_[controller];
477  combined_joints.insert(ci.joints_.begin(), ci.joints_.end());
478  }
479 
480  if (verbose_)
481  {
482  std::stringstream ss, saj, sac;
483  for (const std::string& controller : selected)
484  ss << controller << ' ';
485  for (const std::string& actuated_joint : actuated_joints)
486  saj << actuated_joint << ' ';
487  for (const std::string& combined_joint : combined_joints)
488  sac << combined_joint << ' ';
489  RCLCPP_INFO(logger_, "Checking if controllers [ %s] operating on joints [ %s] cover joints [ %s]", ss.str().c_str(),
490  sac.str().c_str(), saj.str().c_str());
491  }
492 
493  return std::includes(combined_joints.begin(), combined_joints.end(), actuated_joints.begin(), actuated_joints.end());
494 }
495 
496 void TrajectoryExecutionManager::generateControllerCombination(std::size_t start_index, std::size_t controller_count,
497  const std::vector<std::string>& available_controllers,
498  std::vector<std::string>& selected_controllers,
499  std::vector<std::vector<std::string>>& selected_options,
500  const std::set<std::string>& actuated_joints)
501 {
502  if (selected_controllers.size() == controller_count)
503  {
504  if (checkControllerCombination(selected_controllers, actuated_joints))
505  selected_options.push_back(selected_controllers);
506  return;
507  }
508 
509  for (std::size_t i = start_index; i < available_controllers.size(); ++i)
510  {
511  bool overlap = false;
512  const ControllerInformation& ci = known_controllers_[available_controllers[i]];
513  for (std::size_t j = 0; j < selected_controllers.size() && !overlap; ++j)
514  {
515  if (ci.overlapping_controllers_.find(selected_controllers[j]) != ci.overlapping_controllers_.end())
516  overlap = true;
517  }
518  if (overlap)
519  continue;
520  selected_controllers.push_back(available_controllers[i]);
521  generateControllerCombination(i + 1, controller_count, available_controllers, selected_controllers,
522  selected_options, actuated_joints);
523  selected_controllers.pop_back();
524  }
525 }
526 
527 namespace
528 {
529 struct OrderPotentialControllerCombination
530 {
531  bool operator()(const std::size_t a, const std::size_t b) const
532  {
533  // preference is given to controllers marked as default
534  if (nrdefault[a] > nrdefault[b])
535  return true;
536  if (nrdefault[a] < nrdefault[b])
537  return false;
538 
539  // and then to ones that operate on fewer joints
540  if (nrjoints[a] < nrjoints[b])
541  return true;
542  if (nrjoints[a] > nrjoints[b])
543  return false;
544 
545  // and then to active ones
546  if (nractive[a] > nractive[b])
547  return true;
548  if (nractive[a] < nractive[b])
549  return false;
550 
551  return false;
552  }
553 
554  std::vector<std::vector<std::string>> selected_options;
555  std::vector<std::size_t> nrdefault;
556  std::vector<std::size_t> nrjoints;
557  std::vector<std::size_t> nractive;
558 };
559 } // namespace
560 
561 bool TrajectoryExecutionManager::findControllers(const std::set<std::string>& actuated_joints,
562  std::size_t controller_count,
563  const std::vector<std::string>& available_controllers,
564  std::vector<std::string>& selected_controllers)
565 {
566  // generate all combinations of controller_count controllers that operate on disjoint sets of joints
567  std::vector<std::string> work_area;
568  OrderPotentialControllerCombination order;
569  std::vector<std::vector<std::string>>& selected_options = order.selected_options;
570  generateControllerCombination(0, controller_count, available_controllers, work_area, selected_options,
571  actuated_joints);
572 
573  if (verbose_)
574  {
575  std::stringstream saj;
576  std::stringstream sac;
577  for (const std::string& available_controller : available_controllers)
578  sac << available_controller << ' ';
579  for (const std::string& actuated_joint : actuated_joints)
580  saj << actuated_joint << ' ';
581  RCLCPP_INFO(logger_, "Looking for %zu controllers among [ %s] that cover joints [ %s]. Found %zd options.",
582  controller_count, sac.str().c_str(), saj.str().c_str(), selected_options.size());
583  }
584 
585  // if none was found, this is a problem
586  if (selected_options.empty())
587  return false;
588 
589  // if only one was found, return it
590  if (selected_options.size() == 1)
591  {
592  selected_controllers.swap(selected_options[0]);
593  return true;
594  }
595 
596  // if more options were found, evaluate them all and return the best one
597 
598  // count how many default controllers are used in each reported option, and how many joints are actuated in total by
599  // the selected controllers,
600  // to use that in the ranking of the options
601  order.nrdefault.resize(selected_options.size(), 0);
602  order.nrjoints.resize(selected_options.size(), 0);
603  order.nractive.resize(selected_options.size(), 0);
604  for (std::size_t i = 0; i < selected_options.size(); ++i)
605  {
606  for (std::size_t k = 0; k < selected_options[i].size(); ++k)
607  {
608  updateControllerState(selected_options[i][k], DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE);
609  const ControllerInformation& ci = known_controllers_[selected_options[i][k]];
610 
611  if (ci.state_.default_)
612  order.nrdefault[i]++;
613  if (ci.state_.active_)
614  order.nractive[i]++;
615  order.nrjoints[i] += ci.joints_.size();
616  }
617  }
618 
619  // define a bijection to compute the raking of the found options
620  std::vector<std::size_t> bijection(selected_options.size(), 0);
621  for (std::size_t i = 0; i < selected_options.size(); ++i)
622  bijection[i] = i;
623 
624  // sort the options
625  std::sort(bijection.begin(), bijection.end(), order);
626 
627  // depending on whether we are allowed to load & unload controllers,
628  // we have different preference on deciding between options
629  if (!manage_controllers_)
630  {
631  // if we can't load different options at will, just choose one that is already loaded
632  for (std::size_t i = 0; i < selected_options.size(); ++i)
633  {
634  if (areControllersActive(selected_options[bijection[i]]))
635  {
636  selected_controllers.swap(selected_options[bijection[i]]);
637  return true;
638  }
639  }
640  }
641 
642  // otherwise, just use the first valid option
643  selected_controllers.swap(selected_options[bijection[0]]);
644  return true;
645 }
646 
647 bool TrajectoryExecutionManager::isControllerActive(const std::string& controller)
648 {
649  return areControllersActive(std::vector<std::string>(1, controller));
650 }
651 
652 bool TrajectoryExecutionManager::areControllersActive(const std::vector<std::string>& controllers)
653 {
654  for (const std::string& controller : controllers)
655  {
656  updateControllerState(controller, DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE);
657  std::map<std::string, ControllerInformation>::iterator it = known_controllers_.find(controller);
658  if (it == known_controllers_.end() || !it->second.state_.active_)
659  return false;
660  }
661  return true;
662 }
663 
664 bool TrajectoryExecutionManager::selectControllers(const std::set<std::string>& actuated_joints,
665  const std::vector<std::string>& available_controllers,
666  std::vector<std::string>& selected_controllers)
667 {
668  for (std::size_t i = 1; i <= available_controllers.size(); ++i)
669  {
670  if (findControllers(actuated_joints, i, available_controllers, selected_controllers))
671  {
672  // if we are not managing controllers, prefer to use active controllers even if there are more of them
673  if (!manage_controllers_ && !areControllersActive(selected_controllers))
674  {
675  std::vector<std::string> other_option;
676  for (std::size_t j = i + 1; j <= available_controllers.size(); ++j)
677  {
678  if (findControllers(actuated_joints, j, available_controllers, other_option))
679  {
680  if (areControllersActive(other_option))
681  {
682  selected_controllers = other_option;
683  break;
684  }
685  }
686  }
687  }
688  return true;
689  }
690  }
691  return false;
692 }
693 
694 bool TrajectoryExecutionManager::distributeTrajectory(const moveit_msgs::msg::RobotTrajectory& trajectory,
695  const std::vector<std::string>& controllers,
696  std::vector<moveit_msgs::msg::RobotTrajectory>& parts)
697 {
698  parts.clear();
699  parts.resize(controllers.size());
700 
701  std::set<std::string> actuated_joints_mdof;
702  actuated_joints_mdof.insert(trajectory.multi_dof_joint_trajectory.joint_names.begin(),
703  trajectory.multi_dof_joint_trajectory.joint_names.end());
704  std::set<std::string> actuated_joints_single;
705  for (const std::string& joint_name : trajectory.joint_trajectory.joint_names)
706  {
707  const moveit::core::JointModel* jm = robot_model_->getJointModel(joint_name);
708  if (jm)
709  {
710  if (jm->isPassive() || jm->getMimic() != nullptr || jm->getType() == moveit::core::JointModel::FIXED)
711  continue;
712  actuated_joints_single.insert(jm->getName());
713  }
714  }
715 
716  for (std::size_t i = 0; i < controllers.size(); ++i)
717  {
718  std::map<std::string, ControllerInformation>::iterator it = known_controllers_.find(controllers[i]);
719  if (it == known_controllers_.end())
720  {
721  RCLCPP_ERROR_STREAM(logger_, "Controller " << controllers[i] << " not found.");
722  return false;
723  }
724  std::vector<std::string> intersect_mdof;
725  std::set_intersection(it->second.joints_.begin(), it->second.joints_.end(), actuated_joints_mdof.begin(),
726  actuated_joints_mdof.end(), std::back_inserter(intersect_mdof));
727  std::vector<std::string> intersect_single;
728  std::set_intersection(it->second.joints_.begin(), it->second.joints_.end(), actuated_joints_single.begin(),
729  actuated_joints_single.end(), std::back_inserter(intersect_single));
730  if (intersect_mdof.empty() && intersect_single.empty())
731  RCLCPP_WARN_STREAM(logger_, "No joints to be distributed for controller " << controllers[i]);
732  {
733  if (!intersect_mdof.empty())
734  {
735  std::vector<std::string>& jnames = parts[i].multi_dof_joint_trajectory.joint_names;
736  jnames.insert(jnames.end(), intersect_mdof.begin(), intersect_mdof.end());
737  std::map<std::string, std::size_t> index;
738  for (std::size_t j = 0; j < trajectory.multi_dof_joint_trajectory.joint_names.size(); ++j)
739  index[trajectory.multi_dof_joint_trajectory.joint_names[j]] = j;
740  std::vector<std::size_t> bijection(jnames.size());
741  for (std::size_t j = 0; j < jnames.size(); ++j)
742  bijection[j] = index[jnames[j]];
743 
744  parts[i].multi_dof_joint_trajectory.header.frame_id = trajectory.multi_dof_joint_trajectory.header.frame_id;
745  parts[i].multi_dof_joint_trajectory.points.resize(trajectory.multi_dof_joint_trajectory.points.size());
746  for (std::size_t j = 0; j < trajectory.multi_dof_joint_trajectory.points.size(); ++j)
747  {
748  parts[i].multi_dof_joint_trajectory.points[j].time_from_start =
749  trajectory.multi_dof_joint_trajectory.points[j].time_from_start;
750  parts[i].multi_dof_joint_trajectory.points[j].transforms.resize(bijection.size());
751  for (std::size_t k = 0; k < bijection.size(); ++k)
752  {
753  parts[i].multi_dof_joint_trajectory.points[j].transforms[k] =
754  trajectory.multi_dof_joint_trajectory.points[j].transforms[bijection[k]];
755 
756  if (!trajectory.multi_dof_joint_trajectory.points[j].velocities.empty())
757  {
758  parts[i].multi_dof_joint_trajectory.points[j].velocities.resize(bijection.size());
759 
760  parts[i].multi_dof_joint_trajectory.points[j].velocities[0].linear.x =
761  trajectory.multi_dof_joint_trajectory.points[j].velocities[0].linear.x * execution_velocity_scaling_;
762 
763  parts[i].multi_dof_joint_trajectory.points[j].velocities[0].linear.y =
764  trajectory.multi_dof_joint_trajectory.points[j].velocities[0].linear.y * execution_velocity_scaling_;
765 
766  parts[i].multi_dof_joint_trajectory.points[j].velocities[0].linear.z =
767  trajectory.multi_dof_joint_trajectory.points[j].velocities[0].linear.z * execution_velocity_scaling_;
768 
769  parts[i].multi_dof_joint_trajectory.points[j].velocities[0].angular.x =
770  trajectory.multi_dof_joint_trajectory.points[j].velocities[0].angular.x * execution_velocity_scaling_;
771 
772  parts[i].multi_dof_joint_trajectory.points[j].velocities[0].angular.y =
773  trajectory.multi_dof_joint_trajectory.points[j].velocities[0].angular.y * execution_velocity_scaling_;
774 
775  parts[i].multi_dof_joint_trajectory.points[j].velocities[0].angular.z =
776  trajectory.multi_dof_joint_trajectory.points[j].velocities[0].angular.z * execution_velocity_scaling_;
777  }
778  }
779  }
780  }
781  if (!intersect_single.empty())
782  {
783  std::vector<std::string>& jnames = parts[i].joint_trajectory.joint_names;
784  jnames.insert(jnames.end(), intersect_single.begin(), intersect_single.end());
785  parts[i].joint_trajectory.header = trajectory.joint_trajectory.header;
786  std::map<std::string, std::size_t> index;
787  for (std::size_t j = 0; j < trajectory.joint_trajectory.joint_names.size(); ++j)
788  index[trajectory.joint_trajectory.joint_names[j]] = j;
789  std::vector<std::size_t> bijection(jnames.size());
790  for (std::size_t j = 0; j < jnames.size(); ++j)
791  bijection[j] = index[jnames[j]];
792  parts[i].joint_trajectory.points.resize(trajectory.joint_trajectory.points.size());
793  for (std::size_t j = 0; j < trajectory.joint_trajectory.points.size(); ++j)
794  {
795  parts[i].joint_trajectory.points[j].time_from_start = trajectory.joint_trajectory.points[j].time_from_start;
796  if (!trajectory.joint_trajectory.points[j].positions.empty())
797  {
798  parts[i].joint_trajectory.points[j].positions.resize(bijection.size());
799  for (std::size_t k = 0; k < bijection.size(); ++k)
800  {
801  parts[i].joint_trajectory.points[j].positions[k] =
802  trajectory.joint_trajectory.points[j].positions[bijection[k]];
803  }
804  }
805  if (!trajectory.joint_trajectory.points[j].velocities.empty())
806  {
807  parts[i].joint_trajectory.points[j].velocities.resize(bijection.size());
808  for (std::size_t k = 0; k < bijection.size(); ++k)
809  {
810  parts[i].joint_trajectory.points[j].velocities[k] =
811  trajectory.joint_trajectory.points[j].velocities[bijection[k]] * execution_velocity_scaling_;
812  }
813  }
814  if (!trajectory.joint_trajectory.points[j].accelerations.empty())
815  {
816  parts[i].joint_trajectory.points[j].accelerations.resize(bijection.size());
817  for (std::size_t k = 0; k < bijection.size(); ++k)
818  {
819  parts[i].joint_trajectory.points[j].accelerations[k] =
820  trajectory.joint_trajectory.points[j].accelerations[bijection[k]];
821  }
822  }
823  if (!trajectory.joint_trajectory.points[j].effort.empty())
824  {
825  parts[i].joint_trajectory.points[j].effort.resize(bijection.size());
826  for (std::size_t k = 0; k < bijection.size(); ++k)
827  {
828  parts[i].joint_trajectory.points[j].effort[k] =
829  trajectory.joint_trajectory.points[j].effort[bijection[k]];
830  }
831  }
832  }
833  }
834  }
835  }
836  return true;
837 }
838 
839 bool TrajectoryExecutionManager::validate(const TrajectoryExecutionContext& context) const
840 {
841  if (allowed_start_tolerance_ == 0) // skip validation on this magic number
842  return true;
843 
844  RCLCPP_INFO(logger_, "Validating trajectory with allowed_start_tolerance %g", allowed_start_tolerance_);
845 
846  moveit::core::RobotStatePtr current_state;
847  if (!csm_->waitForCurrentState(node_->now()) || !(current_state = csm_->getCurrentState()))
848  {
849  RCLCPP_WARN(logger_, "Failed to validate trajectory: couldn't receive full current joint state within 1s");
850  return false;
851  }
852 
853  for (const auto& trajectory : context.trajectory_parts_)
854  {
855  if (!trajectory.joint_trajectory.points.empty())
856  {
857  // Check single-dof trajectory
858  const std::vector<double>& positions = trajectory.joint_trajectory.points.front().positions;
859  const std::vector<std::string>& joint_names = trajectory.joint_trajectory.joint_names;
860  if (positions.size() != joint_names.size())
861  {
862  RCLCPP_ERROR(logger_, "Wrong trajectory: #joints: %zu != #positions: %zu", joint_names.size(), positions.size());
863  return false;
864  }
865 
866  for (std::size_t i = 0, end = joint_names.size(); i < end; ++i)
867  {
868  const moveit::core::JointModel* jm = current_state->getJointModel(joint_names[i]);
869  if (!jm)
870  {
871  RCLCPP_ERROR_STREAM(logger_, "Unknown joint in trajectory: " << joint_names[i]);
872  return false;
873  }
874 
875  double cur_position = current_state->getJointPositions(jm)[0];
876  double traj_position = positions[i];
877  // normalize positions and compare
878  jm->enforcePositionBounds(&cur_position);
879  jm->enforcePositionBounds(&traj_position);
880  if (jm->distance(&cur_position, &traj_position) > allowed_start_tolerance_)
881  {
882  RCLCPP_ERROR(logger_,
883  "\nInvalid Trajectory: start point deviates from current robot state more than %g"
884  "\njoint '%s': expected: %g, current: %g",
885  allowed_start_tolerance_, joint_names[i].c_str(), traj_position, cur_position);
886  return false;
887  }
888  }
889  }
890  if (!trajectory.multi_dof_joint_trajectory.points.empty())
891  {
892  // Check multi-dof trajectory
893  const std::vector<geometry_msgs::msg::Transform>& transforms =
894  trajectory.multi_dof_joint_trajectory.points.front().transforms;
895  const std::vector<std::string>& joint_names = trajectory.multi_dof_joint_trajectory.joint_names;
896  if (transforms.size() != joint_names.size())
897  {
898  RCLCPP_ERROR(logger_, "Wrong trajectory: #joints: %zu != #transforms: %zu", joint_names.size(),
899  transforms.size());
900  return false;
901  }
902 
903  for (std::size_t i = 0, end = joint_names.size(); i < end; ++i)
904  {
905  const moveit::core::JointModel* jm = current_state->getJointModel(joint_names[i]);
906  if (!jm)
907  {
908  RCLCPP_ERROR_STREAM(logger_, "Unknown joint in trajectory: " << joint_names[i]);
909  return false;
910  }
911 
912  // compute difference (offset vector and rotation angle) between current transform
913  // and start transform in trajectory
914  Eigen::Isometry3d cur_transform, start_transform;
915  // computeTransform() computes a valid isometry by contract
916  jm->computeTransform(current_state->getJointPositions(jm), cur_transform);
917  start_transform = tf2::transformToEigen(transforms[i]);
918  ASSERT_ISOMETRY(start_transform) // unsanitized input, could contain a non-isometry
919  Eigen::Vector3d offset = cur_transform.translation() - start_transform.translation();
920  Eigen::AngleAxisd rotation;
921  rotation.fromRotationMatrix(cur_transform.linear().transpose() * start_transform.linear());
922  if ((offset.array() > allowed_start_tolerance_).any() || rotation.angle() > allowed_start_tolerance_)
923  {
924  RCLCPP_ERROR_STREAM(logger_, "\nInvalid Trajectory: start point deviates from current robot state more than "
925  << allowed_start_tolerance_ << "\nmulti-dof joint '" << joint_names[i]
926  << "': pos delta: " << offset.transpose()
927  << " rot delta: " << rotation.angle());
928  return false;
929  }
930  }
931  }
932  }
933  return true;
934 }
935 
936 bool TrajectoryExecutionManager::configure(TrajectoryExecutionContext& context,
937  const moveit_msgs::msg::RobotTrajectory& trajectory,
938  const std::vector<std::string>& controllers)
939 {
940  if (trajectory.multi_dof_joint_trajectory.points.empty() && trajectory.joint_trajectory.points.empty())
941  {
942  // empty trajectories don't need to configure anything
943  return true;
944  }
945 
946  reloadControllerInformation();
947  std::set<std::string> actuated_joints;
948 
949  auto is_actuated = [this](const std::string& joint_name) -> bool {
950  const moveit::core::JointModel* jm = robot_model_->getJointModel(joint_name);
951  return (jm && !jm->isPassive() && !jm->getMimic() && jm->getType() != moveit::core::JointModel::FIXED);
952  };
953  for (const std::string& joint_name : trajectory.multi_dof_joint_trajectory.joint_names)
954  {
955  if (is_actuated(joint_name))
956  actuated_joints.insert(joint_name);
957  }
958  for (const std::string& joint_name : trajectory.joint_trajectory.joint_names)
959  {
960  if (is_actuated(joint_name))
961  actuated_joints.insert(joint_name);
962  }
963 
964  if (actuated_joints.empty())
965  {
966  RCLCPP_WARN(logger_, "The trajectory to execute specifies no joints");
967  return false;
968  }
969 
970  if (controllers.empty())
971  {
972  bool retry = true;
973  bool reloaded = false;
974  while (retry)
975  {
976  retry = false;
977  std::vector<std::string> all_controller_names;
978  for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
979  it != known_controllers_.end(); ++it)
980  all_controller_names.push_back(it->first);
981  if (selectControllers(actuated_joints, all_controller_names, context.controllers_))
982  {
983  if (distributeTrajectory(trajectory, context.controllers_, context.trajectory_parts_))
984  return true;
985  }
986  else
987  {
988  // maybe we failed because we did not have a complete list of controllers
989  if (!reloaded)
990  {
991  reloadControllerInformation();
992  reloaded = true;
993  retry = true;
994  }
995  }
996  }
997  }
998  else
999  {
1000  // check if the specified controllers are valid names;
1001  // if they appear not to be, try to reload the controller information, just in case they are new in the system
1002  bool reloaded = false;
1003  for (const std::string& controller : controllers)
1004  {
1005  if (known_controllers_.find(controller) == known_controllers_.end())
1006  {
1007  reloadControllerInformation();
1008  reloaded = true;
1009  break;
1010  }
1011  }
1012  if (reloaded)
1013  {
1014  for (const std::string& controller : controllers)
1015  {
1016  if (known_controllers_.find(controller) == known_controllers_.end())
1017  {
1018  RCLCPP_ERROR(logger_, "Controller '%s' is not known", controller.c_str());
1019  return false;
1020  }
1021  }
1022  }
1023  if (selectControllers(actuated_joints, controllers, context.controllers_))
1024  {
1025  if (distributeTrajectory(trajectory, context.controllers_, context.trajectory_parts_))
1026  return true;
1027  }
1028  }
1029  std::stringstream ss;
1030  for (const std::string& actuated_joint : actuated_joints)
1031  ss << actuated_joint << ' ';
1032  RCLCPP_ERROR(logger_, "Unable to identify any set of controllers that can actuate the specified joints: [ %s]",
1033  ss.str().c_str());
1034 
1035  std::stringstream ss2;
1036  std::map<std::string, ControllerInformation>::const_iterator mi;
1037  for (mi = known_controllers_.begin(); mi != known_controllers_.end(); ++mi)
1038  {
1039  ss2 << "controller '" << mi->second.name_ << "' controls joints:\n";
1040 
1041  std::set<std::string>::const_iterator ji;
1042  for (ji = mi->second.joints_.begin(); ji != mi->second.joints_.end(); ++ji)
1043  {
1044  ss2 << " " << *ji << '\n';
1045  }
1046  }
1047  RCLCPP_ERROR(logger_, "Known controllers and their joints:\n%s", ss2.str().c_str());
1048  return false;
1049 }
1050 
1052 {
1053  execute(ExecutionCompleteCallback(), auto_clear);
1054  return waitForExecution();
1055 }
1056 
1057 void TrajectoryExecutionManager::stopExecutionInternal()
1058 {
1059  // execution_state_mutex_ needs to have been locked by the caller
1060  for (moveit_controller_manager::MoveItControllerHandlePtr& active_handle : active_handles_)
1061  {
1062  try
1063  {
1064  active_handle->cancelExecution();
1065  }
1066  catch (std::exception& ex)
1067  {
1068  RCLCPP_ERROR(logger_, "Caught %s when canceling execution.", ex.what());
1069  }
1070  }
1071 }
1072 
1074 {
1075  if (!execution_complete_)
1076  {
1077  execution_state_mutex_.lock();
1078  if (!execution_complete_)
1079  {
1080  // we call cancel for all active handles; we know these are not being modified as we loop through them because of
1081  // the lock
1082  // we mark execution_complete_ as true ahead of time. Using this flag, executePart() will know that an external
1083  // trigger to stop has been received
1084  execution_complete_ = true;
1085  stopExecutionInternal();
1086 
1087  // we set the status here; executePart() will not set status when execution_complete_ is true ahead of time
1089  execution_state_mutex_.unlock();
1090  RCLCPP_INFO(logger_, "Stopped trajectory execution.");
1091 
1092  // wait for the execution thread to finish
1093  std::scoped_lock lock(execution_thread_mutex_);
1094  if (execution_thread_)
1095  {
1096  execution_thread_->join();
1097  execution_thread_.reset();
1098  }
1099 
1100  if (auto_clear)
1101  clear();
1102  }
1103  else
1104  execution_state_mutex_.unlock();
1105  }
1106  else if (execution_thread_) // just in case we have some thread waiting to be joined from some point in the past, we
1107  // join it now
1108  {
1109  std::scoped_lock lock(execution_thread_mutex_);
1110  if (execution_thread_)
1111  {
1112  execution_thread_->join();
1113  execution_thread_.reset();
1114  }
1115  }
1116 }
1117 
1119 {
1120  execute(callback, PathSegmentCompleteCallback(), auto_clear);
1121 }
1122 
1124  const PathSegmentCompleteCallback& part_callback, bool auto_clear)
1125 {
1126  // skip execution if no trajectory have been pushed
1127  // it crashes otherwise
1128  if (trajectories_.empty())
1129  return;
1130 
1131  stopExecution(false);
1132 
1133  // check whether first trajectory starts at current robot state
1134  if (!trajectories_.empty() && !validate(*trajectories_.front()))
1135  {
1136  last_execution_status_ = moveit_controller_manager::ExecutionStatus::ABORTED;
1137  if (auto_clear)
1138  clear();
1139  if (callback)
1140  callback(last_execution_status_);
1141  return;
1142  }
1143 
1144  // start the execution thread
1145  execution_complete_ = false;
1146  execution_thread_ = std::make_unique<std::thread>(&TrajectoryExecutionManager::executeThread, this, callback,
1147  part_callback, auto_clear);
1148 }
1149 
1151 {
1152  {
1153  std::unique_lock<std::mutex> ulock(execution_state_mutex_);
1154  while (!execution_complete_)
1155  execution_complete_condition_.wait(ulock);
1156  }
1157 
1158  // this will join the thread for executing sequences of trajectories
1159  stopExecution(false);
1160 
1161  return last_execution_status_;
1162 }
1163 
1165 {
1166  if (execution_complete_)
1167  {
1168  for (TrajectoryExecutionContext* trajectory : trajectories_)
1169  delete trajectory;
1170  trajectories_.clear();
1171  }
1172  else
1173  RCLCPP_ERROR(logger_, "Cannot push a new trajectory while another is being executed");
1174 }
1175 
1176 void TrajectoryExecutionManager::executeThread(const ExecutionCompleteCallback& callback,
1177  const PathSegmentCompleteCallback& part_callback, bool auto_clear)
1178 {
1179  // if we already got a stop request before we even started anything, we abort
1180  if (execution_complete_)
1181  {
1182  last_execution_status_ = moveit_controller_manager::ExecutionStatus::ABORTED;
1183  if (callback)
1184  callback(last_execution_status_);
1185  return;
1186  }
1187 
1188  RCLCPP_INFO(logger_, "Starting trajectory execution ...");
1189  // assume everything will be OK
1191 
1192  // execute each trajectory, one after the other (executePart() is blocking) or until one fails.
1193  // on failure, the status is set by executePart(). Otherwise, it will remain as set above (success)
1194  std::size_t i = 0;
1195  for (; i < trajectories_.size(); ++i)
1196  {
1197  bool epart = executePart(i);
1198  if (epart && part_callback)
1199  part_callback(i);
1200  if (!epart || execution_complete_)
1201  {
1202  ++i;
1203  break;
1204  }
1205  }
1206 
1207  // only report that execution finished successfully when the robot actually stopped moving
1208  if (last_execution_status_ == moveit_controller_manager::ExecutionStatus::SUCCEEDED)
1209  waitForRobotToStop(*trajectories_[i - 1]);
1210 
1211  RCLCPP_INFO(logger_, "Completed trajectory execution with status %s ...", last_execution_status_.asString().c_str());
1212 
1213  // notify whoever is waiting for the event of trajectory completion
1214  execution_state_mutex_.lock();
1215  execution_complete_ = true;
1216  execution_state_mutex_.unlock();
1217  execution_complete_condition_.notify_all();
1218 
1219  // clear the paths just executed, if needed
1220  if (auto_clear)
1221  clear();
1222 
1223  // call user-specified callback
1224  if (callback)
1225  callback(last_execution_status_);
1226 }
1227 
1228 bool TrajectoryExecutionManager::executePart(std::size_t part_index)
1229 {
1230  TrajectoryExecutionContext& context = *trajectories_[part_index];
1231 
1232  // first make sure desired controllers are active
1233  if (ensureActiveControllers(context.controllers_))
1234  {
1235  // stop if we are already asked to do so
1236  if (execution_complete_)
1237  return false;
1238 
1239  std::vector<moveit_controller_manager::MoveItControllerHandlePtr> handles;
1240  {
1241  std::scoped_lock slock(execution_state_mutex_);
1242  if (!execution_complete_)
1243  {
1244  // time indexing uses this member too, so we lock this mutex as well
1245  time_index_mutex_.lock();
1246  current_context_ = part_index;
1247  time_index_mutex_.unlock();
1248  active_handles_.resize(context.controllers_.size());
1249  for (std::size_t i = 0; i < context.controllers_.size(); ++i)
1250  {
1251  moveit_controller_manager::MoveItControllerHandlePtr h;
1252  try
1253  {
1254  h = controller_manager_->getControllerHandle(context.controllers_[i]);
1255  }
1256  catch (std::exception& ex)
1257  {
1258  RCLCPP_ERROR(logger_, "Caught %s when retrieving controller handle", ex.what());
1259  }
1260  if (!h)
1261  {
1262  active_handles_.clear();
1263  current_context_ = -1;
1264  last_execution_status_ = moveit_controller_manager::ExecutionStatus::ABORTED;
1265  RCLCPP_ERROR(logger_, "No controller handle for controller '%s'. Aborting.",
1266  context.controllers_[i].c_str());
1267  return false;
1268  }
1269  active_handles_[i] = h;
1270  }
1271  handles = active_handles_; // keep a copy for later, to avoid thread safety issues
1272  for (std::size_t i = 0; i < context.trajectory_parts_.size(); ++i)
1273  {
1274  bool ok = false;
1275  try
1276  {
1277  ok = active_handles_[i]->sendTrajectory(context.trajectory_parts_[i]);
1278  }
1279  catch (std::exception& ex)
1280  {
1281  RCLCPP_ERROR(logger_, "Caught %s when sending trajectory to controller", ex.what());
1282  }
1283  if (!ok)
1284  {
1285  for (std::size_t j = 0; j < i; ++j)
1286  {
1287  try
1288  {
1289  active_handles_[j]->cancelExecution();
1290  }
1291  catch (std::exception& ex)
1292  {
1293  RCLCPP_ERROR(logger_, "Caught %s when canceling execution", ex.what());
1294  }
1295  }
1296  RCLCPP_ERROR(logger_, "Failed to send trajectory part %zu of %zu to controller %s", i + 1,
1297  context.trajectory_parts_.size(), active_handles_[i]->getName().c_str());
1298  if (i > 0)
1299  RCLCPP_ERROR(logger_, "Cancelling previously sent trajectory parts");
1300  active_handles_.clear();
1301  current_context_ = -1;
1302  last_execution_status_ = moveit_controller_manager::ExecutionStatus::ABORTED;
1303  return false;
1304  }
1305  }
1306  }
1307  }
1308 
1309  // compute the expected duration of the trajectory and find the part of the trajectory that takes longest to execute
1310  rclcpp::Time current_time = node_->now();
1311  auto expected_trajectory_duration = rclcpp::Duration::from_seconds(0);
1312  int longest_part = -1;
1313  for (std::size_t i = 0; i < context.trajectory_parts_.size(); ++i)
1314  {
1315  auto d = rclcpp::Duration::from_seconds(0);
1316  if (!(context.trajectory_parts_[i].joint_trajectory.points.empty() &&
1317  context.trajectory_parts_[i].multi_dof_joint_trajectory.points.empty()))
1318  {
1319  if (rclcpp::Time(context.trajectory_parts_[i].joint_trajectory.header.stamp) > current_time)
1320  d = rclcpp::Time(context.trajectory_parts_[i].joint_trajectory.header.stamp) - current_time;
1321  if (rclcpp::Time(context.trajectory_parts_[i].multi_dof_joint_trajectory.header.stamp) > current_time)
1322  {
1323  d = std::max(d, rclcpp::Time(context.trajectory_parts_[i].multi_dof_joint_trajectory.header.stamp) -
1324  current_time);
1325  }
1326  d = d +
1327  std::max(context.trajectory_parts_[i].joint_trajectory.points.empty() ?
1328  rclcpp::Duration::from_seconds(0) :
1329  rclcpp::Duration(context.trajectory_parts_[i].joint_trajectory.points.back().time_from_start),
1330  context.trajectory_parts_[i].multi_dof_joint_trajectory.points.empty() ?
1331  rclcpp::Duration::from_seconds(0) :
1332  rclcpp::Duration(
1333  context.trajectory_parts_[i].multi_dof_joint_trajectory.points.back().time_from_start));
1334 
1335  if (longest_part < 0 ||
1336  std::max(context.trajectory_parts_[i].joint_trajectory.points.size(),
1337  context.trajectory_parts_[i].multi_dof_joint_trajectory.points.size()) >
1338  std::max(context.trajectory_parts_[longest_part].joint_trajectory.points.size(),
1339  context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.points.size()))
1340  longest_part = i;
1341  }
1342 
1343  // prefer controller-specific values over global ones if defined
1344  // TODO: the controller-specific parameters are static, but override
1345  // the global ones are configurable via dynamic reconfigure
1346  std::map<std::string, double>::const_iterator scaling_it =
1347  controller_allowed_execution_duration_scaling_.find(context.controllers_[i]);
1348  const double current_scaling = scaling_it != controller_allowed_execution_duration_scaling_.end() ?
1349  scaling_it->second :
1350  allowed_execution_duration_scaling_;
1351 
1352  std::map<std::string, double>::const_iterator margin_it =
1353  controller_allowed_goal_duration_margin_.find(context.controllers_[i]);
1354  const double current_margin = margin_it != controller_allowed_goal_duration_margin_.end() ?
1355  margin_it->second :
1356  allowed_goal_duration_margin_;
1357 
1358  // expected duration is the duration of the longest part
1359  expected_trajectory_duration =
1360  std::max(d * current_scaling + rclcpp::Duration::from_seconds(current_margin), expected_trajectory_duration);
1361  }
1362 
1363  // construct a map from expected time to state index, for easy access to expected state location
1364  if (longest_part >= 0)
1365  {
1366  std::scoped_lock slock(time_index_mutex_);
1367 
1368  if (context.trajectory_parts_[longest_part].joint_trajectory.points.size() >=
1369  context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.points.size())
1370  {
1371  auto d = rclcpp::Duration::from_seconds(0);
1372  if (rclcpp::Time(context.trajectory_parts_[longest_part].joint_trajectory.header.stamp) > current_time)
1373  d = rclcpp::Time(context.trajectory_parts_[longest_part].joint_trajectory.header.stamp) - current_time;
1374  for (trajectory_msgs::msg::JointTrajectoryPoint& point :
1375  context.trajectory_parts_[longest_part].joint_trajectory.points)
1376  time_index_.push_back(current_time + d + rclcpp::Duration(point.time_from_start));
1377  }
1378  else
1379  {
1380  auto d = rclcpp::Duration::from_seconds(0);
1381  if (rclcpp::Time(context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.header.stamp) > current_time)
1382  {
1383  d = rclcpp::Time(context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.header.stamp) -
1384  current_time;
1385  }
1386  for (trajectory_msgs::msg::MultiDOFJointTrajectoryPoint& point :
1387  context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.points)
1388  time_index_.push_back(current_time + d + rclcpp::Duration(point.time_from_start));
1389  }
1390  }
1391 
1392  bool result = true;
1393  for (moveit_controller_manager::MoveItControllerHandlePtr& handle : handles)
1394  {
1395  if (execution_duration_monitoring_)
1396  {
1397  if (!handle->waitForExecution(expected_trajectory_duration))
1398  {
1399  if (!execution_complete_ && node_->now() - current_time > expected_trajectory_duration)
1400  {
1401  RCLCPP_ERROR(logger_,
1402  "Controller is taking too long to execute trajectory (the expected upper "
1403  "bound for the trajectory execution was %lf seconds). Stopping trajectory.",
1404  expected_trajectory_duration.seconds());
1405  {
1406  std::scoped_lock slock(execution_state_mutex_);
1407  stopExecutionInternal(); // this is really tricky. we can't call stopExecution() here, so we call the
1408  // internal function only
1409  }
1411  result = false;
1412  break;
1413  }
1414  }
1415  }
1416  else
1417  handle->waitForExecution();
1418 
1419  // if something made the trajectory stop, we stop this thread too
1420  if (execution_complete_)
1421  {
1422  result = false;
1423  break;
1424  }
1425  else if (handle->getLastExecutionStatus() != moveit_controller_manager::ExecutionStatus::SUCCEEDED)
1426  {
1427  RCLCPP_WARN_STREAM(logger_, "Controller handle " << handle->getName() << " reports status "
1428  << handle->getLastExecutionStatus().asString());
1429  last_execution_status_ = handle->getLastExecutionStatus();
1430  result = false;
1431  }
1432  }
1433 
1434  // clear the active handles
1435  execution_state_mutex_.lock();
1436  active_handles_.clear();
1437 
1438  // clear the time index
1439  time_index_mutex_.lock();
1440  time_index_.clear();
1441  current_context_ = -1;
1442  time_index_mutex_.unlock();
1443 
1444  execution_state_mutex_.unlock();
1445  return result;
1446  }
1447  else
1448  {
1449  RCLCPP_ERROR(logger_, "Active status of required controllers can not be assured.");
1450  last_execution_status_ = moveit_controller_manager::ExecutionStatus::ABORTED;
1451  return false;
1452  }
1453 }
1454 
1455 bool TrajectoryExecutionManager::waitForRobotToStop(const TrajectoryExecutionContext& context, double wait_time)
1456 {
1457  // skip waiting for convergence?
1458  if (allowed_start_tolerance_ == 0 || !wait_for_trajectory_completion_)
1459  {
1460  RCLCPP_INFO(logger_, "Not waiting for trajectory completion");
1461  return true;
1462  }
1463 
1464  auto start = std::chrono::system_clock::now();
1465  double time_remaining = wait_time;
1466 
1467  moveit::core::RobotStatePtr prev_state, cur_state;
1468  prev_state = csm_->getCurrentState();
1469  prev_state->enforceBounds();
1470 
1471  // assume robot stopped when 3 consecutive checks yield the same robot state
1472  unsigned int no_motion_count = 0; // count iterations with no motion
1473  while (time_remaining > 0. && no_motion_count < 3)
1474  {
1475  if (!csm_->waitForCurrentState(node_->now(), time_remaining) || !(cur_state = csm_->getCurrentState()))
1476  {
1477  RCLCPP_WARN(logger_, "Failed to receive current joint state");
1478  return false;
1479  }
1480  cur_state->enforceBounds();
1481  std::chrono::duration<double> elapsed_seconds = std::chrono::system_clock::now() - start;
1482  time_remaining = wait_time - elapsed_seconds.count(); // remaining wait_time
1483 
1484  // check for motion in effected joints of execution context
1485  bool moved = false;
1486  for (const auto& trajectory : context.trajectory_parts_)
1487  {
1488  const std::vector<std::string>& joint_names = trajectory.joint_trajectory.joint_names;
1489  const std::size_t n = joint_names.size();
1490 
1491  for (std::size_t i = 0; i < n && !moved; ++i)
1492  {
1493  const moveit::core::JointModel* jm = cur_state->getJointModel(joint_names[i]);
1494  if (!jm)
1495  continue; // joint vanished from robot state (shouldn't happen), but we don't care
1496 
1497  if (fabs(cur_state->getJointPositions(jm)[0] - prev_state->getJointPositions(jm)[0]) > allowed_start_tolerance_)
1498  {
1499  moved = true;
1500  no_motion_count = 0;
1501  break;
1502  }
1503  }
1504  }
1505 
1506  if (!moved)
1507  ++no_motion_count;
1508 
1509  std::swap(prev_state, cur_state);
1510  }
1511 
1512  return time_remaining > 0;
1513 }
1514 
1516 {
1517  std::scoped_lock slock(time_index_mutex_);
1518  if (current_context_ < 0)
1519  return std::make_pair(-1, -1);
1520  if (time_index_.empty())
1521  return std::make_pair(static_cast<int>(current_context_), -1);
1522  std::vector<rclcpp::Time>::const_iterator time_index_it =
1523  std::lower_bound(time_index_.begin(), time_index_.end(), node_->now());
1524  int pos = time_index_it - time_index_.begin();
1525  return std::make_pair(static_cast<int>(current_context_), pos);
1526 }
1527 
1528 const std::vector<TrajectoryExecutionManager::TrajectoryExecutionContext*>&
1530 {
1531  return trajectories_;
1532 }
1533 
1535 {
1536  return last_execution_status_;
1537 }
1538 
1540 {
1541  const moveit::core::JointModelGroup* joint_model_group = robot_model_->getJointModelGroup(group);
1542  if (joint_model_group)
1543  {
1544  return ensureActiveControllersForJoints(joint_model_group->getJointModelNames());
1545  }
1546  else
1547  {
1548  return false;
1549  }
1550 }
1551 
1552 bool TrajectoryExecutionManager::ensureActiveControllersForJoints(const std::vector<std::string>& joints)
1553 {
1554  std::vector<std::string> all_controller_names;
1555  for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
1556  it != known_controllers_.end(); ++it)
1557  all_controller_names.push_back(it->first);
1558  std::vector<std::string> selected_controllers;
1559  std::set<std::string> jset;
1560  for (const std::string& joint : joints)
1561  {
1562  const moveit::core::JointModel* jm = robot_model_->getJointModel(joint);
1563  if (jm)
1564  {
1565  if (jm->isPassive() || jm->getMimic() != nullptr || jm->getType() == moveit::core::JointModel::FIXED)
1566  continue;
1567  jset.insert(joint);
1568  }
1569  }
1570 
1571  if (selectControllers(jset, all_controller_names, selected_controllers))
1572  {
1573  return ensureActiveControllers(selected_controllers);
1574  }
1575  else
1576  {
1577  return false;
1578  }
1579 }
1580 
1581 bool TrajectoryExecutionManager::ensureActiveController(const std::string& controller)
1582 {
1583  return ensureActiveControllers(std::vector<std::string>(1, controller));
1584 }
1585 
1586 bool TrajectoryExecutionManager::ensureActiveControllers(const std::vector<std::string>& controllers)
1587 {
1588  reloadControllerInformation();
1589 
1590  updateControllersState(DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE);
1591 
1592  if (manage_controllers_)
1593  {
1594  std::vector<std::string> controllers_to_activate;
1595  std::vector<std::string> controllers_to_deactivate;
1596  std::set<std::string> joints_to_be_activated;
1597  std::set<std::string> joints_to_be_deactivated;
1598  for (const std::string& controller : controllers)
1599  {
1600  std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.find(controller);
1601  if (it == known_controllers_.end())
1602  {
1603  std::stringstream stream;
1604  for (const auto& controller : known_controllers_)
1605  {
1606  stream << " `" << controller.first << '`';
1607  }
1608  RCLCPP_WARN_STREAM(logger_, "Controller " << controller << " is not known. Known controllers: " << stream.str());
1609  return false;
1610  }
1611  if (!it->second.state_.active_)
1612  {
1613  RCLCPP_DEBUG_STREAM(logger_, "Need to activate " << controller);
1614  controllers_to_activate.push_back(controller);
1615  joints_to_be_activated.insert(it->second.joints_.begin(), it->second.joints_.end());
1616  for (const std::string& overlapping_controller : it->second.overlapping_controllers_)
1617  {
1618  const ControllerInformation& ci = known_controllers_[overlapping_controller];
1619  if (ci.state_.active_)
1620  {
1621  controllers_to_deactivate.push_back(overlapping_controller);
1622  joints_to_be_deactivated.insert(ci.joints_.begin(), ci.joints_.end());
1623  }
1624  }
1625  }
1626  else
1627  RCLCPP_DEBUG_STREAM(logger_, "Controller " << controller << " is already active");
1628  }
1629  std::set<std::string> diff;
1630  std::set_difference(joints_to_be_deactivated.begin(), joints_to_be_deactivated.end(),
1631  joints_to_be_activated.begin(), joints_to_be_activated.end(), std::inserter(diff, diff.end()));
1632  if (!diff.empty())
1633  {
1634  // find the set of controllers that do not overlap with the ones we want to activate so far
1635  std::vector<std::string> possible_additional_controllers;
1636  for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
1637  it != known_controllers_.end(); ++it)
1638  {
1639  bool ok = true;
1640  for (const std::string& controller_to_activate : controllers_to_activate)
1641  {
1642  if (it->second.overlapping_controllers_.find(controller_to_activate) !=
1643  it->second.overlapping_controllers_.end())
1644  {
1645  ok = false;
1646  break;
1647  }
1648  }
1649  if (ok)
1650  possible_additional_controllers.push_back(it->first);
1651  }
1652 
1653  // out of the allowable controllers, try to find a subset of controllers that covers the joints to be actuated
1654  std::vector<std::string> additional_controllers;
1655  if (selectControllers(diff, possible_additional_controllers, additional_controllers))
1656  {
1657  controllers_to_activate.insert(controllers_to_activate.end(), additional_controllers.begin(),
1658  additional_controllers.end());
1659  }
1660  else
1661  {
1662  return false;
1663  }
1664  }
1665  if (!controllers_to_activate.empty() || !controllers_to_deactivate.empty())
1666  {
1667  if (controller_manager_)
1668  {
1669  // load controllers to be activated, if needed, and reset the state update cache
1670  for (const std::string& controller_to_activate : controllers_to_activate)
1671  {
1672  ControllerInformation& ci = known_controllers_[controller_to_activate];
1673  ci.last_update_ = rclcpp::Time(0, 0, RCL_ROS_TIME);
1674  }
1675  // reset the state update cache
1676  for (const std::string& controller_to_deactivate : controllers_to_deactivate)
1677  known_controllers_[controller_to_deactivate].last_update_ = rclcpp::Time(0, 0, RCL_ROS_TIME);
1678  return controller_manager_->switchControllers(controllers_to_activate, controllers_to_deactivate);
1679  }
1680  else
1681  return false;
1682  }
1683  else
1684  return true;
1685  }
1686  else
1687  {
1688  std::set<std::string> originally_active;
1689  for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
1690  it != known_controllers_.end(); ++it)
1691  {
1692  if (it->second.state_.active_)
1693  {
1694  originally_active.insert(it->first);
1695  }
1696  }
1697  return std::includes(originally_active.begin(), originally_active.end(), controllers.begin(), controllers.end());
1698  }
1699 }
1700 
1701 void TrajectoryExecutionManager::loadControllerParams()
1702 {
1703  // TODO: Revise XmlRpc parameter lookup
1704  // XmlRpc::XmlRpcValue controller_list;
1705  // if (node_->get_parameter("controller_list", controller_list) &&
1706  // controller_list.getType() == XmlRpc::XmlRpcValue::TypeArray)
1707  // {
1708  // for (int i = 0; i < controller_list.size(); ++i) // NOLINT(modernize-loop-convert)
1709  // {
1710  // XmlRpc::XmlRpcValue& controller = controller_list[i];
1711  // if (controller.hasMember("name"))
1712  // {
1713  // if (controller.hasMember("allowed_execution_duration_scaling"))
1714  // controller_allowed_execution_duration_scaling_[std::string(controller["name"])] =
1715  // controller["allowed_execution_duration_scaling"];
1716  // if (controller.hasMember("allowed_goal_duration_margin"))
1717  // controller_allowed_goal_duration_margin_[std::string(controller["name"])] =
1718  // controller["allowed_goal_duration_margin"];
1719  // }
1720  // }
1721  // }
1722 }
1723 } // namespace trajectory_execution_manager
const std::vector< std::string > & getJointModelNames() const
Get the names of the joints in this group. These are the names of the joints returned by getJointMode...
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
Definition: joint_model.h:117
const std::string & getName() const
Get the name of the joint.
Definition: joint_model.h:145
virtual double distance(const double *value1, const double *value2) const =0
Compute the distance between two joint states of the same model (represented by the variable values)
bool isPassive() const
Check if this joint is passive.
Definition: joint_model.h:440
virtual void computeTransform(const double *joint_values, Eigen::Isometry3d &transf) const =0
Given the joint values for a joint, compute the corresponding transform. The computed transform is gu...
bool enforcePositionBounds(double *values) const
Force the specified values to be inside bounds and normalized. Quaternions are normalized,...
Definition: joint_model.h:292
JointType getType() const
Get the type of joint.
Definition: joint_model.h:151
const JointModel * getMimic() const
Get the joint this one is mimicking.
Definition: joint_model.h:390
TrajectoryExecutionManager(const rclcpp::Node::SharedPtr &node, const moveit::core::RobotModelConstPtr &robot_model, const planning_scene_monitor::CurrentStateMonitorPtr &csm)
Load the controller manager plugin, start listening for events on a topic.
~TrajectoryExecutionManager()
Destructor. Cancels all running trajectories (if any)
void execute(const ExecutionCompleteCallback &callback=ExecutionCompleteCallback(), bool auto_clear=true)
Start the execution of pushed trajectories; this does not wait for completion, but calls a callback w...
void stopExecution(bool auto_clear=true)
Stop whatever executions are active, if any.
void setWaitForTrajectoryCompletion(bool flag)
Enable or disable waiting for trajectory completion.
bool ensureActiveControllersForGroup(const std::string &group)
Make sure the active controllers are such that trajectories that actuate joints in the specified grou...
const std::vector< TrajectoryExecutionContext * > & getTrajectories() const
Get the trajectories to be executed.
std::function< void(const moveit_controller_manager::ExecutionStatus &)> ExecutionCompleteCallback
bool ensureActiveController(const std::string &controller)
Make sure a particular controller is active.
moveit_controller_manager::ExecutionStatus getLastExecutionStatus() const
Return the controller status for the last attempted execution.
bool push(const moveit_msgs::msg::RobotTrajectory &trajectory, const std::string &controller="")
bool isManagingControllers() const
If this function returns true, then this instance of the manager is allowed to load/unload/switch con...
bool isControllerActive(const std::string &controller)
Check if a controller is active.
moveit_controller_manager::ExecutionStatus executeAndWait(bool auto_clear=true)
void setAllowedStartTolerance(double tolerance)
Set joint-value tolerance for validating trajectory's start point against current robot state.
void processEvent(const std::string &event)
Execute a named event (e.g., 'stop')
bool areControllersActive(const std::vector< std::string > &controllers)
Check if a set of controllers is active.
bool ensureActiveControllersForJoints(const std::vector< std::string > &joints)
Make sure the active controllers are such that trajectories that actuate joints in the specified set ...
bool ensureActiveControllers(const std::vector< std::string > &controllers)
Make sure a particular set of controllers are active.
const moveit_controller_manager::MoveItControllerManagerPtr & getControllerManager() const
Get the instance of the controller manager used (this is the plugin instance loaded)
std::vector< std::size_t > nrjoints
std::vector< std::size_t > nrdefault
std::vector< std::size_t > nractive
std::vector< std::vector< std::string > > selected_options
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.h:89
Main namespace for MoveIt.
Definition: exceptions.h:43
name
Definition: setup.py:7
std::string asString() const
Convert the execution status to a string.
Data structure that represents information necessary to execute a trajectory.
std::vector< std::string > controllers_
The controllers to use for executing the different trajectory parts;.