moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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
40#include <geometric_shapes/check_isometry.h>
41#include <memory>
42#include <tf2_eigen/tf2_eigen.hpp>
44
45#include <rclcpp/qos.hpp>
46
48{
49
50const std::string TrajectoryExecutionManager::EXECUTION_EVENT_TOPIC = "trajectory_execution_event";
51
52static const auto DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE = rclcpp::Duration::from_seconds(1);
53static const double DEFAULT_CONTROLLER_GOAL_DURATION_MARGIN = 0.5; // allow 0.5s more than the expected execution time
54 // before triggering a trajectory cancel (applied
55 // after scaling)
56static const double DEFAULT_CONTROLLER_GOAL_DURATION_SCALING =
57 1.1; // allow the execution of a trajectory to take more time than expected (scaled by a value > 1)
58static const bool DEFAULT_CONTROL_MULTI_DOF_JOINT_VARIABLES = false;
59
61 const moveit::core::RobotModelConstPtr& robot_model,
62 const planning_scene_monitor::CurrentStateMonitorPtr& csm)
63 : node_(node)
64 , logger_(moveit::getLogger("moveit.ros.trajectory_execution_manager"))
65 , robot_model_(robot_model)
66 , csm_(csm)
67{
68 if (!node_->get_parameter("moveit_manage_controllers", manage_controllers_))
69 manage_controllers_ = false;
70 initialize();
71}
72
74 const moveit::core::RobotModelConstPtr& robot_model,
75 const planning_scene_monitor::CurrentStateMonitorPtr& csm,
76 bool manage_controllers)
77 : node_(node)
78 , logger_(moveit::getLogger("moveit.ros.trajectory_execution_manager"))
79 , robot_model_(robot_model)
80 , csm_(csm)
81 , manage_controllers_(manage_controllers)
82{
83 initialize();
84}
85
87{
88 stopExecution(true);
89 if (private_executor_)
90 private_executor_->cancel();
91 if (private_executor_thread_.joinable())
92 private_executor_thread_.join();
93}
94
95void TrajectoryExecutionManager::initialize()
96{
97 verbose_ = false;
98 execution_complete_ = true;
99 current_context_ = -1;
101 execution_duration_monitoring_ = true;
102 execution_velocity_scaling_ = 1.0;
103 allowed_start_tolerance_ = 0.01;
104 allowed_start_tolerance_joints_.clear();
105 wait_for_trajectory_completion_ = true;
106 control_multi_dof_joint_variables_ = DEFAULT_CONTROL_MULTI_DOF_JOINT_VARIABLES;
107
108 allowed_execution_duration_scaling_ = DEFAULT_CONTROLLER_GOAL_DURATION_SCALING;
109 allowed_goal_duration_margin_ = DEFAULT_CONTROLLER_GOAL_DURATION_MARGIN;
110
111 // load the controller manager plugin
112 try
113 {
114 controller_manager_loader_ =
115 std::make_unique<pluginlib::ClassLoader<moveit_controller_manager::MoveItControllerManager>>(
116 "moveit_core", "moveit_controller_manager::MoveItControllerManager");
117 }
118 catch (pluginlib::PluginlibException& ex)
119 {
120 RCLCPP_FATAL_STREAM(logger_, "Exception while creating controller manager plugin loader: " << ex.what());
121 return;
122 }
123
124 if (controller_manager_loader_)
125 {
126 std::string controller;
127
128 if (!node_->get_parameter("moveit_controller_manager", controller))
129 {
130 const std::vector<std::string>& classes = controller_manager_loader_->getDeclaredClasses();
131 if (classes.size() == 1)
132 {
133 controller = classes[0];
134 RCLCPP_WARN(logger_,
135 "Parameter '~moveit_controller_manager' is not specified but only one "
136 "matching plugin was found: '%s'. Using that one.",
137 controller.c_str());
138 }
139 else
140 {
141 RCLCPP_FATAL(logger_, "Parameter '~moveit_controller_manager' not specified. This is needed to "
142 "identify the plugin to use for interacting with controllers. No paths can "
143 "be executed.");
144 return;
145 }
146 }
147
148 // Deprecation warnings, October 2022
149 if (controller == "moveit_ros_control_interface/MoveItControllerManager")
150 {
151 RCLCPP_WARN(logger_, "moveit_ros_control_interface/MoveItControllerManager is deprecated. Replace with "
152 "`moveit_ros_control_interface/Ros2ControlManager.`");
153 }
154 if (controller == "moveit_ros_control_interface/MoveItMultiControllerManager")
155 {
156 RCLCPP_WARN(logger_, "moveit_ros_control_interface/MoveItMultiControllerManager is deprecated. Replace with "
157 "`moveit_ros_control_interface/Ros2ControlMultiManager.`");
158 }
159
160 if (!controller.empty())
161 {
162 try
163 {
164 // We make a node called moveit_simple_controller_manager so it's able to
165 // receive callbacks on another thread. We then copy parameters from the move_group node
166 // and then add it to the multithreadedexecutor
167 rclcpp::NodeOptions opt;
168 opt.allow_undeclared_parameters(true);
169 opt.automatically_declare_parameters_from_overrides(true);
170 controller_mgr_node_ =
171 std::make_shared<rclcpp::Node>("moveit_simple_controller_manager", node_->get_namespace(), opt);
172
173 auto all_params = node_->get_node_parameters_interface()->get_parameter_overrides();
174 for (const auto& param : all_params)
175 controller_mgr_node_->set_parameter(rclcpp::Parameter(param.first, param.second));
176
177 controller_manager_ = controller_manager_loader_->createUniqueInstance(controller);
178 controller_manager_->initialize(controller_mgr_node_);
179 private_executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
180 private_executor_->add_node(controller_mgr_node_);
181
182 // start executor on a different thread now
183 private_executor_thread_ = std::thread([this]() { private_executor_->spin(); });
184 }
185 catch (pluginlib::PluginlibException& ex)
186 {
187 RCLCPP_FATAL_STREAM(logger_, "Exception while loading controller manager '" << controller << "': " << ex.what());
188 }
189 }
190 }
191
192 // other configuration steps
193 reloadControllerInformation();
194
195 // load controller-specific values for allowed_execution_duration_scaling and allowed_goal_duration_margin
196 loadControllerParams();
197
198 // The default callback group for rclcpp::Node is MutuallyExclusive which means we cannot call
199 // receiveEvent while processing a different callback. To fix this we create a new callback group (the type is not
200 // important since we only use it to process one callback) and associate event_topic_subscriber_ with this callback group
201 auto callback_group = node_->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
202 auto options = rclcpp::SubscriptionOptions();
203 options.callback_group = callback_group;
204 event_topic_subscriber_ = node_->create_subscription<std_msgs::msg::String>(
205 EXECUTION_EVENT_TOPIC, rclcpp::ServicesQoS(),
206 [this](const std_msgs::msg::String::ConstSharedPtr& event) { return receiveEvent(event); }, options);
207
208 controller_mgr_node_->get_parameter("trajectory_execution.execution_duration_monitoring",
209 execution_duration_monitoring_);
210 controller_mgr_node_->get_parameter("trajectory_execution.allowed_execution_duration_scaling",
211 allowed_execution_duration_scaling_);
212 controller_mgr_node_->get_parameter("trajectory_execution.allowed_goal_duration_margin",
213 allowed_goal_duration_margin_);
214 controller_mgr_node_->get_parameter("trajectory_execution.allowed_start_tolerance", allowed_start_tolerance_);
215 controller_mgr_node_->get_parameter("trajectory_execution.control_multi_dof_joint_variables",
216 control_multi_dof_joint_variables_);
217
218 initializeAllowedStartToleranceJoints();
219
220 if (manage_controllers_)
221 {
222 RCLCPP_INFO(logger_, "Trajectory execution is managing controllers");
223 }
224 else
225 {
226 RCLCPP_INFO(logger_, "Trajectory execution is not managing controllers");
227 }
228
229 auto controller_mgr_parameter_set_callback = [this](const std::vector<rclcpp::Parameter>& parameters) {
230 auto result = rcl_interfaces::msg::SetParametersResult();
231 result.successful = true;
232 for (const auto& parameter : parameters)
233 {
234 const std::string& name = parameter.get_name();
235 if (name == "trajectory_execution.execution_duration_monitoring")
236 {
237 enableExecutionDurationMonitoring(parameter.as_bool());
238 }
239 else if (name == "trajectory_execution.allowed_execution_duration_scaling")
240 {
241 setAllowedExecutionDurationScaling(parameter.as_double());
242 }
243 else if (name == "trajectory_execution.allowed_goal_duration_margin")
244 {
245 setAllowedGoalDurationMargin(parameter.as_double());
246 }
247 else if (name == "trajectory_execution.execution_velocity_scaling")
248 {
249 setExecutionVelocityScaling(parameter.as_double());
250 }
251 else if (name == "trajectory_execution.allowed_start_tolerance")
252 {
253 setAllowedStartTolerance(parameter.as_double());
254 }
255 else if (name.find("trajectory_execution.allowed_start_tolerance_joints.") == 0)
256 {
257 setAllowedStartToleranceJoint(name, parameter.as_double());
258 }
259 else if (name == "trajectory_execution.wait_for_trajectory_completion")
260 {
261 setWaitForTrajectoryCompletion(parameter.as_bool());
262 }
263 else
264 {
265 result.successful = false;
266 }
267 }
268 return result;
269 };
270 callback_handler_ = controller_mgr_node_->add_on_set_parameters_callback(controller_mgr_parameter_set_callback);
271}
272
274{
275 execution_duration_monitoring_ = flag;
276}
277
279{
280 return execution_duration_monitoring_;
281}
282
284{
285 allowed_execution_duration_scaling_ = scaling;
286}
287
289{
290 return allowed_execution_duration_scaling_;
291}
292
294{
295 allowed_goal_duration_margin_ = margin;
296}
297
299{
300 return allowed_goal_duration_margin_;
301}
302
304{
305 execution_velocity_scaling_ = scaling;
306}
307
309{
310 return execution_velocity_scaling_;
311}
312
314{
315 allowed_start_tolerance_ = tolerance;
316}
317
319{
320 return allowed_start_tolerance_;
321}
322
324{
325 wait_for_trajectory_completion_ = flag;
326}
327
329{
330 return wait_for_trajectory_completion_;
331}
332
334{
335 return manage_controllers_;
336}
337
338const moveit_controller_manager::MoveItControllerManagerPtr& TrajectoryExecutionManager::getControllerManager() const
339{
340 return controller_manager_;
341}
342
343void TrajectoryExecutionManager::processEvent(const std::string& event)
344{
345 if (event == "stop")
346 {
347 stopExecution(true);
348 }
349 else
350 {
351 RCLCPP_WARN_STREAM(logger_, "Unknown event type: '" << event << '\'');
352 }
353}
354
355void TrajectoryExecutionManager::receiveEvent(const std_msgs::msg::String::ConstSharedPtr& event)
356{
357 RCLCPP_INFO_STREAM(logger_, "Received event '" << event->data << '\'');
358 processEvent(event->data);
359}
360
361bool TrajectoryExecutionManager::push(const moveit_msgs::msg::RobotTrajectory& trajectory, const std::string& controller)
362{
363 if (controller.empty())
364 {
365 return push(trajectory, std::vector<std::string>());
366 }
367 else
368 {
369 return push(trajectory, std::vector<std::string>(1, controller));
370 }
371}
372
373bool TrajectoryExecutionManager::push(const trajectory_msgs::msg::JointTrajectory& trajectory,
374 const std::string& controller)
375{
376 if (controller.empty())
377 {
378 return push(trajectory, std::vector<std::string>());
379 }
380 else
381 {
382 return push(trajectory, std::vector<std::string>(1, controller));
383 }
384}
385
386bool TrajectoryExecutionManager::push(const trajectory_msgs::msg::JointTrajectory& trajectory,
387 const std::vector<std::string>& controllers)
388{
389 moveit_msgs::msg::RobotTrajectory traj;
390 traj.joint_trajectory = trajectory;
391 return push(traj, controllers);
392}
393
394bool TrajectoryExecutionManager::push(const moveit_msgs::msg::RobotTrajectory& trajectory,
395 const std::vector<std::string>& controllers)
396{
397 if (!execution_complete_)
398 {
399 RCLCPP_ERROR(logger_, "Cannot push a new trajectory while another is being executed");
400 return false;
401 }
402
403 // Optionally, convert multi dof waypoints to joint states and replace trajectory for execution
404 std::optional<moveit_msgs::msg::RobotTrajectory> replaced_trajectory;
405 if (control_multi_dof_joint_variables_ && !trajectory.multi_dof_joint_trajectory.points.empty())
406 {
407 // We convert the trajectory message into a RobotTrajectory first,
408 // since the conversion to a combined JointTrajectory depends on the local variables
409 // of the Multi-DOF joint types.
410 moveit::core::RobotState reference_state(robot_model_);
411 reference_state.setToDefaultValues();
412 robot_trajectory::RobotTrajectory tmp_trajectory(robot_model_);
413 tmp_trajectory.setRobotTrajectoryMsg(reference_state, trajectory);
414
415 // Combine all joints for filtering the joint trajectory waypoints
416 std::vector<std::string> all_trajectory_joints = trajectory.joint_trajectory.joint_names;
417 for (const auto& mdof_joint : trajectory.multi_dof_joint_trajectory.joint_names)
418 {
419 all_trajectory_joints.push_back(mdof_joint);
420 }
421
422 // Convert back to single joint trajectory including the MDOF joint variables, e.g. position/x, position/y, ...
423 const auto joint_trajectory =
424 robot_trajectory::toJointTrajectory(tmp_trajectory, true /* include_mdof_joints */, all_trajectory_joints);
425
426 // Check success of conversion
427 // This should never happen when using MoveIt's interfaces, but users can pass anything into TEM::push() directly
428 if (!joint_trajectory.has_value())
429 {
430 RCLCPP_ERROR(logger_, "Failed to convert multi-DOF trajectory to joint trajectory, aborting execution!");
431 return false;
432 }
433
434 // Create a new robot trajectory message that only contains the combined joint trajectory
435 RCLCPP_DEBUG(logger_, "Successfully converted multi-DOF trajectory to joint trajectory for execution.");
436 replaced_trajectory = moveit_msgs::msg::RobotTrajectory();
437 replaced_trajectory->joint_trajectory = joint_trajectory.value();
438 }
439
441 if (configure(*context, replaced_trajectory.value_or(trajectory), controllers))
442 {
443 if (verbose_)
444 {
445 std::stringstream ss;
446 ss << "Pushed trajectory for execution using controllers [ ";
447 for (const std::string& controller : context->controllers_)
448 ss << controller << ' ';
449 ss << "]:" << '\n';
450 // TODO: Provide message serialization
451 // for (const moveit_msgs::msg::RobotTrajectory& trajectory_part : context->trajectory_parts_)
452 // ss << trajectory_part << '\n';
453 RCLCPP_INFO_STREAM(logger_, ss.str());
454 }
455 trajectories_.push_back(context);
456 return true;
457 }
458 else
459 {
460 delete context;
462 }
463
464 return false;
465}
466
467void TrajectoryExecutionManager::reloadControllerInformation()
468{
469 known_controllers_.clear();
470 if (controller_manager_)
471 {
472 std::vector<std::string> names;
473 controller_manager_->getControllersList(names);
474 for (const std::string& name : names)
475 {
476 std::vector<std::string> joints;
477 controller_manager_->getControllerJoints(name, joints);
478 ControllerInformation ci;
479 ci.name_ = name;
480 ci.joints_.insert(joints.begin(), joints.end());
481 known_controllers_[ci.name_] = ci;
482 }
483
484 names.clear();
485 controller_manager_->getActiveControllers(names);
486 for (const auto& active_name : names)
487 {
488 auto found_it = std::find_if(known_controllers_.begin(), known_controllers_.end(),
489 [&](const auto& known_controller) { return known_controller.first == active_name; });
490 if (found_it != known_controllers_.end())
491 {
492 found_it->second.state_.active_ = true;
493 }
494 }
495
496 for (std::map<std::string, ControllerInformation>::iterator it = known_controllers_.begin();
497 it != known_controllers_.end(); ++it)
498 {
499 for (std::map<std::string, ControllerInformation>::iterator jt = known_controllers_.begin();
500 jt != known_controllers_.end(); ++jt)
501 {
502 if (it != jt)
503 {
504 std::vector<std::string> intersect;
505 std::set_intersection(it->second.joints_.begin(), it->second.joints_.end(), jt->second.joints_.begin(),
506 jt->second.joints_.end(), std::back_inserter(intersect));
507 if (!intersect.empty())
508 {
509 it->second.overlapping_controllers_.insert(jt->first);
510 jt->second.overlapping_controllers_.insert(it->first);
511 }
512 }
513 }
514 }
515 }
516 else
517 {
518 RCLCPP_ERROR(logger_, "Failed to reload controllers: `controller_manager_` does not exist.");
519 }
520}
521
522void TrajectoryExecutionManager::updateControllerState(const std::string& controller, const rclcpp::Duration& age)
523{
524 std::map<std::string, ControllerInformation>::iterator it = known_controllers_.find(controller);
525 if (it != known_controllers_.end())
526 {
527 updateControllerState(it->second, age);
528 }
529 else
530 {
531 RCLCPP_ERROR(logger_, "Controller '%s' is not known.", controller.c_str());
532 }
533}
534
535void TrajectoryExecutionManager::updateControllerState(ControllerInformation& ci, const rclcpp::Duration& age)
536{
537 if (node_->now() - ci.last_update_ >= age)
538 {
539 if (controller_manager_)
540 {
541 if (verbose_)
542 RCLCPP_INFO(logger_, "Updating information for controller '%s'.", ci.name_.c_str());
543 ci.state_ = controller_manager_->getControllerState(ci.name_);
544 ci.last_update_ = node_->now();
545 }
546 }
547 else if (verbose_)
548 RCLCPP_INFO(logger_, "Information for controller '%s' is assumed to be up to date.", ci.name_.c_str());
549}
550
551void TrajectoryExecutionManager::updateControllersState(const rclcpp::Duration& age)
552{
553 for (std::pair<const std::string, ControllerInformation>& known_controller : known_controllers_)
554 updateControllerState(known_controller.second, age);
555}
556
557bool TrajectoryExecutionManager::checkControllerCombination(std::vector<std::string>& selected,
558 const std::set<std::string>& actuated_joints)
559{
560 std::set<std::string> combined_joints;
561 for (const std::string& controller : selected)
562 {
563 const ControllerInformation& ci = known_controllers_[controller];
564 combined_joints.insert(ci.joints_.begin(), ci.joints_.end());
565 }
566
567 if (verbose_)
568 {
569 std::stringstream ss, saj, sac;
570 for (const std::string& controller : selected)
571 ss << controller << ' ';
572 for (const std::string& actuated_joint : actuated_joints)
573 saj << actuated_joint << ' ';
574 for (const std::string& combined_joint : combined_joints)
575 sac << combined_joint << ' ';
576 RCLCPP_INFO(logger_, "Checking if controllers [ %s] operating on joints [ %s] cover joints [ %s]", ss.str().c_str(),
577 sac.str().c_str(), saj.str().c_str());
578 }
579
580 return std::includes(combined_joints.begin(), combined_joints.end(), actuated_joints.begin(), actuated_joints.end());
581}
582
583void TrajectoryExecutionManager::generateControllerCombination(std::size_t start_index, std::size_t controller_count,
584 const std::vector<std::string>& available_controllers,
585 std::vector<std::string>& selected_controllers,
586 std::vector<std::vector<std::string>>& selected_options,
587 const std::set<std::string>& actuated_joints)
588{
589 if (selected_controllers.size() == controller_count)
590 {
591 if (checkControllerCombination(selected_controllers, actuated_joints))
592 selected_options.push_back(selected_controllers);
593 return;
594 }
595
596 for (std::size_t i = start_index; i < available_controllers.size(); ++i)
597 {
598 bool overlap = false;
599 const ControllerInformation& ci = known_controllers_[available_controllers[i]];
600 for (std::size_t j = 0; j < selected_controllers.size() && !overlap; ++j)
601 {
602 if (ci.overlapping_controllers_.find(selected_controllers[j]) != ci.overlapping_controllers_.end())
603 overlap = true;
604 }
605 if (overlap)
606 continue;
607 selected_controllers.push_back(available_controllers[i]);
608 generateControllerCombination(i + 1, controller_count, available_controllers, selected_controllers,
609 selected_options, actuated_joints);
610 selected_controllers.pop_back();
611 }
612}
613
614namespace
615{
616struct OrderPotentialControllerCombination
617{
618 bool operator()(const std::size_t a, const std::size_t b) const
619 {
620 // preference is given to controllers marked as default
621 if (nrdefault[a] > nrdefault[b])
622 return true;
623 if (nrdefault[a] < nrdefault[b])
624 return false;
625
626 // and then to ones that operate on fewer joints
627 if (nrjoints[a] < nrjoints[b])
628 return true;
629 if (nrjoints[a] > nrjoints[b])
630 return false;
631
632 // and then to active ones
633 if (nractive[a] > nractive[b])
634 return true;
635 if (nractive[a] < nractive[b])
636 return false;
637
638 return false;
639 }
640
641 std::vector<std::vector<std::string>> selected_options;
642 std::vector<std::size_t> nrdefault;
643 std::vector<std::size_t> nrjoints;
644 std::vector<std::size_t> nractive;
645};
646} // namespace
647
648bool TrajectoryExecutionManager::findControllers(const std::set<std::string>& actuated_joints,
649 std::size_t controller_count,
650 const std::vector<std::string>& available_controllers,
651 std::vector<std::string>& selected_controllers)
652{
653 // generate all combinations of controller_count controllers that operate on disjoint sets of joints
654 std::vector<std::string> work_area;
655 OrderPotentialControllerCombination order;
656 std::vector<std::vector<std::string>>& selected_options = order.selected_options;
657 generateControllerCombination(0, controller_count, available_controllers, work_area, selected_options,
658 actuated_joints);
659
660 if (verbose_)
661 {
662 std::stringstream saj;
663 std::stringstream sac;
664 for (const std::string& available_controller : available_controllers)
665 sac << available_controller << ' ';
666 for (const std::string& actuated_joint : actuated_joints)
667 saj << actuated_joint << ' ';
668 RCLCPP_INFO(logger_, "Looking for %zu controllers among [ %s] that cover joints [ %s]. Found %zd options.",
669 controller_count, sac.str().c_str(), saj.str().c_str(), selected_options.size());
670 }
671
672 // if none was found, this is a problem
673 if (selected_options.empty())
674 return false;
675
676 // if only one was found, return it
677 if (selected_options.size() == 1)
678 {
679 selected_controllers.swap(selected_options[0]);
680 return true;
681 }
682
683 // if more options were found, evaluate them all and return the best one
684
685 // count how many default controllers are used in each reported option, and how many joints are actuated in total by
686 // the selected controllers,
687 // to use that in the ranking of the options
688 order.nrdefault.resize(selected_options.size(), 0);
689 order.nrjoints.resize(selected_options.size(), 0);
690 order.nractive.resize(selected_options.size(), 0);
691 for (std::size_t i = 0; i < selected_options.size(); ++i)
692 {
693 for (std::size_t k = 0; k < selected_options[i].size(); ++k)
694 {
695 updateControllerState(selected_options[i][k], DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE);
696 const ControllerInformation& ci = known_controllers_[selected_options[i][k]];
697
698 if (ci.state_.default_)
699 order.nrdefault[i]++;
700 if (ci.state_.active_)
701 order.nractive[i]++;
702 order.nrjoints[i] += ci.joints_.size();
703 }
704 }
705
706 // define a bijection to compute the raking of the found options
707 std::vector<std::size_t> bijection(selected_options.size(), 0);
708 for (std::size_t i = 0; i < selected_options.size(); ++i)
709 bijection[i] = i;
710
711 // sort the options
712 std::sort(bijection.begin(), bijection.end(), order);
713
714 // depending on whether we are allowed to load & unload controllers,
715 // we have different preference on deciding between options
716 if (!manage_controllers_)
717 {
718 // if we can't load different options at will, just choose one that is already loaded
719 for (std::size_t i = 0; i < selected_options.size(); ++i)
720 {
721 if (areControllersActive(selected_options[bijection[i]]))
722 {
723 selected_controllers.swap(selected_options[bijection[i]]);
724 return true;
725 }
726 }
727 }
728
729 // otherwise, just use the first valid option
730 selected_controllers.swap(selected_options[bijection[0]]);
731 return true;
732}
733
734bool TrajectoryExecutionManager::isControllerActive(const std::string& controller)
735{
736 return areControllersActive(std::vector<std::string>(1, controller));
737}
738
739bool TrajectoryExecutionManager::areControllersActive(const std::vector<std::string>& controllers)
740{
741 for (const std::string& controller : controllers)
742 {
743 updateControllerState(controller, DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE);
744 std::map<std::string, ControllerInformation>::iterator it = known_controllers_.find(controller);
745 if (it == known_controllers_.end() || !it->second.state_.active_)
746 return false;
747 }
748 return true;
749}
750
751bool TrajectoryExecutionManager::selectControllers(const std::set<std::string>& actuated_joints,
752 const std::vector<std::string>& available_controllers,
753 std::vector<std::string>& selected_controllers)
754{
755 for (std::size_t i = 1; i <= available_controllers.size(); ++i)
756 {
757 if (findControllers(actuated_joints, i, available_controllers, selected_controllers))
758 {
759 // if we are not managing controllers, prefer to use active controllers even if there are more of them
760 if (!manage_controllers_ && !areControllersActive(selected_controllers))
761 {
762 std::vector<std::string> other_option;
763 for (std::size_t j = i + 1; j <= available_controllers.size(); ++j)
764 {
765 if (findControllers(actuated_joints, j, available_controllers, other_option))
766 {
767 if (areControllersActive(other_option))
768 {
769 selected_controllers = other_option;
770 break;
771 }
772 }
773 }
774 }
775 return true;
776 }
777 }
778 return false;
779}
780
781bool TrajectoryExecutionManager::distributeTrajectory(const moveit_msgs::msg::RobotTrajectory& trajectory,
782 const std::vector<std::string>& controllers,
783 std::vector<moveit_msgs::msg::RobotTrajectory>& parts)
784{
785 parts.clear();
786 parts.resize(controllers.size());
787
788 std::set<std::string> actuated_joints_mdof;
789 actuated_joints_mdof.insert(trajectory.multi_dof_joint_trajectory.joint_names.begin(),
790 trajectory.multi_dof_joint_trajectory.joint_names.end());
791 std::set<std::string> actuated_joints_single;
792 for (const std::string& joint_name : trajectory.joint_trajectory.joint_names)
793 {
794 const moveit::core::JointModel* jm = robot_model_->getJointOfVariable(joint_name);
795 if (jm)
796 {
797 if (jm->isPassive() || jm->getMimic() != nullptr || jm->getType() == moveit::core::JointModel::FIXED)
798 continue;
799 actuated_joints_single.insert(joint_name);
800 }
801 }
802
803 for (std::size_t i = 0; i < controllers.size(); ++i)
804 {
805 std::map<std::string, ControllerInformation>::iterator it = known_controllers_.find(controllers[i]);
806 if (it == known_controllers_.end())
807 {
808 RCLCPP_ERROR_STREAM(logger_, "Controller " << controllers[i] << " not found.");
809 return false;
810 }
811 std::vector<std::string> intersect_mdof;
812 std::set_intersection(it->second.joints_.begin(), it->second.joints_.end(), actuated_joints_mdof.begin(),
813 actuated_joints_mdof.end(), std::back_inserter(intersect_mdof));
814 std::vector<std::string> intersect_single;
815 std::set_intersection(it->second.joints_.begin(), it->second.joints_.end(), actuated_joints_single.begin(),
816 actuated_joints_single.end(), std::back_inserter(intersect_single));
817 if (intersect_mdof.empty() && intersect_single.empty())
818 RCLCPP_WARN_STREAM(logger_, "No joints to be distributed for controller " << controllers[i]);
819 {
820 if (!intersect_mdof.empty())
821 {
822 std::vector<std::string>& jnames = parts[i].multi_dof_joint_trajectory.joint_names;
823 jnames.insert(jnames.end(), intersect_mdof.begin(), intersect_mdof.end());
824 std::map<std::string, std::size_t> index;
825 for (std::size_t j = 0; j < trajectory.multi_dof_joint_trajectory.joint_names.size(); ++j)
826 index[trajectory.multi_dof_joint_trajectory.joint_names[j]] = j;
827 std::vector<std::size_t> bijection(jnames.size());
828 for (std::size_t j = 0; j < jnames.size(); ++j)
829 bijection[j] = index[jnames[j]];
830
831 parts[i].multi_dof_joint_trajectory.header.frame_id = trajectory.multi_dof_joint_trajectory.header.frame_id;
832 parts[i].multi_dof_joint_trajectory.points.resize(trajectory.multi_dof_joint_trajectory.points.size());
833 for (std::size_t j = 0; j < trajectory.multi_dof_joint_trajectory.points.size(); ++j)
834 {
835 parts[i].multi_dof_joint_trajectory.points[j].time_from_start =
836 trajectory.multi_dof_joint_trajectory.points[j].time_from_start;
837 parts[i].multi_dof_joint_trajectory.points[j].transforms.resize(bijection.size());
838 for (std::size_t k = 0; k < bijection.size(); ++k)
839 {
840 parts[i].multi_dof_joint_trajectory.points[j].transforms[k] =
841 trajectory.multi_dof_joint_trajectory.points[j].transforms[bijection[k]];
842
843 if (!trajectory.multi_dof_joint_trajectory.points[j].velocities.empty())
844 {
845 parts[i].multi_dof_joint_trajectory.points[j].velocities.resize(bijection.size());
846
847 parts[i].multi_dof_joint_trajectory.points[j].velocities[0].linear.x =
848 trajectory.multi_dof_joint_trajectory.points[j].velocities[0].linear.x * execution_velocity_scaling_;
849
850 parts[i].multi_dof_joint_trajectory.points[j].velocities[0].linear.y =
851 trajectory.multi_dof_joint_trajectory.points[j].velocities[0].linear.y * execution_velocity_scaling_;
852
853 parts[i].multi_dof_joint_trajectory.points[j].velocities[0].linear.z =
854 trajectory.multi_dof_joint_trajectory.points[j].velocities[0].linear.z * execution_velocity_scaling_;
855
856 parts[i].multi_dof_joint_trajectory.points[j].velocities[0].angular.x =
857 trajectory.multi_dof_joint_trajectory.points[j].velocities[0].angular.x * execution_velocity_scaling_;
858
859 parts[i].multi_dof_joint_trajectory.points[j].velocities[0].angular.y =
860 trajectory.multi_dof_joint_trajectory.points[j].velocities[0].angular.y * execution_velocity_scaling_;
861
862 parts[i].multi_dof_joint_trajectory.points[j].velocities[0].angular.z =
863 trajectory.multi_dof_joint_trajectory.points[j].velocities[0].angular.z * execution_velocity_scaling_;
864 }
865 }
866 }
867 }
868 if (!intersect_single.empty())
869 {
870 std::vector<std::string>& jnames = parts[i].joint_trajectory.joint_names;
871 jnames.insert(jnames.end(), intersect_single.begin(), intersect_single.end());
872 parts[i].joint_trajectory.header = trajectory.joint_trajectory.header;
873 std::map<std::string, std::size_t> index;
874 for (std::size_t j = 0; j < trajectory.joint_trajectory.joint_names.size(); ++j)
875 index[trajectory.joint_trajectory.joint_names[j]] = j;
876 std::vector<std::size_t> bijection(jnames.size());
877 for (std::size_t j = 0; j < jnames.size(); ++j)
878 bijection[j] = index[jnames[j]];
879 parts[i].joint_trajectory.points.resize(trajectory.joint_trajectory.points.size());
880 for (std::size_t j = 0; j < trajectory.joint_trajectory.points.size(); ++j)
881 {
882 parts[i].joint_trajectory.points[j].time_from_start = trajectory.joint_trajectory.points[j].time_from_start;
883 if (!trajectory.joint_trajectory.points[j].positions.empty())
884 {
885 parts[i].joint_trajectory.points[j].positions.resize(bijection.size());
886 for (std::size_t k = 0; k < bijection.size(); ++k)
887 {
888 parts[i].joint_trajectory.points[j].positions[k] =
889 trajectory.joint_trajectory.points[j].positions[bijection[k]];
890 }
891 }
892 if (!trajectory.joint_trajectory.points[j].velocities.empty())
893 {
894 parts[i].joint_trajectory.points[j].velocities.resize(bijection.size());
895 for (std::size_t k = 0; k < bijection.size(); ++k)
896 {
897 parts[i].joint_trajectory.points[j].velocities[k] =
898 trajectory.joint_trajectory.points[j].velocities[bijection[k]] * execution_velocity_scaling_;
899 }
900 }
901 if (!trajectory.joint_trajectory.points[j].accelerations.empty())
902 {
903 parts[i].joint_trajectory.points[j].accelerations.resize(bijection.size());
904 for (std::size_t k = 0; k < bijection.size(); ++k)
905 {
906 parts[i].joint_trajectory.points[j].accelerations[k] =
907 trajectory.joint_trajectory.points[j].accelerations[bijection[k]];
908 }
909 }
910 if (!trajectory.joint_trajectory.points[j].effort.empty())
911 {
912 parts[i].joint_trajectory.points[j].effort.resize(bijection.size());
913 for (std::size_t k = 0; k < bijection.size(); ++k)
914 {
915 parts[i].joint_trajectory.points[j].effort[k] =
916 trajectory.joint_trajectory.points[j].effort[bijection[k]];
917 }
918 }
919 }
920 }
921 }
922 }
923 return true;
924}
925
926bool TrajectoryExecutionManager::validate(const TrajectoryExecutionContext& context) const
927{
928 if (allowed_start_tolerance_ == 0 && allowed_start_tolerance_joints_.empty()) // skip validation on this magic number
929 return true;
930
931 if (allowed_start_tolerance_joints_.empty())
932 {
933 RCLCPP_INFO_STREAM(logger_, "Validating trajectory with allowed_start_tolerance " << allowed_start_tolerance_);
934 }
935 else
936 {
937 std::ostringstream ss;
938 for (const auto& [joint_name, joint_start_tolerance] : allowed_start_tolerance_joints_)
939 {
940 if (ss.tellp() > 1)
941 ss << ", "; // skip the comma at the end
942 ss << joint_name << ": " << joint_start_tolerance;
943 }
944 RCLCPP_INFO_STREAM(logger_, "Validating trajectory with allowed_start_tolerance "
945 << allowed_start_tolerance_ << " and allowed_start_tolerance_joints {" << ss.str()
946 << "}");
947 }
948
949 moveit::core::RobotStatePtr current_state;
950 if (!csm_->waitForCurrentState(node_->now()) || !(current_state = csm_->getCurrentState()))
951 {
952 RCLCPP_WARN(logger_, "Failed to validate trajectory: couldn't receive full current joint state within 1s");
953 return false;
954 }
955 moveit::core::RobotState reference_state(*current_state);
956 for (const auto& trajectory : context.trajectory_parts_)
957 {
958 if (!trajectory.joint_trajectory.points.empty())
959 {
960 // Check single-dof trajectory
961 const std::vector<double>& positions = trajectory.joint_trajectory.points.front().positions;
962 const std::vector<std::string>& joint_names = trajectory.joint_trajectory.joint_names;
963 if (positions.size() != joint_names.size())
964 {
965 RCLCPP_ERROR(logger_, "Wrong trajectory: #joints: %zu != #positions: %zu", joint_names.size(), positions.size());
966 return false;
967 }
968
969 std::set<const moveit::core::JointModel*> joints;
970 for (const auto& joint_name : joint_names)
971 {
972 const moveit::core::JointModel* jm = robot_model_->getJointOfVariable(joint_name);
973 if (!jm)
974 {
975 RCLCPP_ERROR_STREAM(logger_, "Unknown joint in trajectory: " << joint_name);
976 return false;
977 }
978
979 joints.insert(jm);
980 }
981
982 // Copy all variable positions to reference state, and then compare start state joint distance within bounds
983 // Note on multi-DOF joints: Instead of comparing the translation and rotation distances like it's done for
984 // the multi-dof trajectory, this check will use the joint's internal distance implementation instead.
985 // This is more accurate, but may require special treatment for cases like the diff drive's turn path geometry.
986 reference_state.setVariablePositions(joint_names, positions);
987
988 for (const auto joint : joints)
989 {
990 const double joint_start_tolerance = getAllowedStartToleranceJoint(joint->getName());
991 reference_state.enforcePositionBounds(joint);
992 current_state->enforcePositionBounds(joint);
993 if (joint_start_tolerance != 0 && reference_state.distance(*current_state, joint) > joint_start_tolerance)
994 {
995 RCLCPP_ERROR(logger_,
996 "Invalid Trajectory: start point deviates from current robot state more than %g at joint '%s'."
997 "\nEnable DEBUG for detailed state info.",
998 joint_start_tolerance, joint->getName().c_str());
999 RCLCPP_DEBUG(logger_, "| Joint | Expected | Current |");
1000 for (const auto& joint_name : joint_names)
1001 {
1002 RCLCPP_DEBUG(logger_, "| %s | %g | %g |", joint_name.c_str(),
1003 reference_state.getVariablePosition(joint_name),
1004 current_state->getVariablePosition(joint_name));
1005 }
1006 return false;
1007 }
1008 }
1009 }
1010
1011 if (!trajectory.multi_dof_joint_trajectory.points.empty())
1012 {
1013 // Check multi-dof trajectory
1014 const std::vector<geometry_msgs::msg::Transform>& transforms =
1015 trajectory.multi_dof_joint_trajectory.points.front().transforms;
1016 const std::vector<std::string>& joint_names = trajectory.multi_dof_joint_trajectory.joint_names;
1017 if (transforms.size() != joint_names.size())
1018 {
1019 RCLCPP_ERROR(logger_, "Wrong trajectory: #joints: %zu != #transforms: %zu", joint_names.size(),
1020 transforms.size());
1021 return false;
1022 }
1023
1024 for (std::size_t i = 0, end = joint_names.size(); i < end; ++i)
1025 {
1026 const moveit::core::JointModel* jm = current_state->getJointModel(joint_names[i]);
1027 if (!jm)
1028 {
1029 RCLCPP_ERROR_STREAM(logger_, "Unknown joint in trajectory: " << joint_names[i]);
1030 return false;
1031 }
1032
1033 // compute difference (offset vector and rotation angle) between current transform
1034 // and start transform in trajectory
1035 Eigen::Isometry3d cur_transform, start_transform;
1036 // computeTransform() computes a valid isometry by contract
1037 jm->computeTransform(current_state->getJointPositions(jm), cur_transform);
1038 start_transform = tf2::transformToEigen(transforms[i]);
1039 ASSERT_ISOMETRY(start_transform) // unsanitized input, could contain a non-isometry
1040 Eigen::Vector3d offset = cur_transform.translation() - start_transform.translation();
1041 Eigen::AngleAxisd rotation;
1042 rotation.fromRotationMatrix(cur_transform.linear().transpose() * start_transform.linear());
1043 const double joint_start_tolerance = getAllowedStartToleranceJoint(joint_names[i]);
1044 if (joint_start_tolerance != 0 &&
1045 ((offset.array() > joint_start_tolerance).any() || rotation.angle() > joint_start_tolerance))
1046 {
1047 RCLCPP_ERROR_STREAM(logger_, "\nInvalid Trajectory: start point deviates from current robot state more than "
1048 << joint_start_tolerance << "\nmulti-dof joint '" << joint_names[i]
1049 << "': pos delta: " << offset.transpose()
1050 << " rot delta: " << rotation.angle());
1051 return false;
1052 }
1053 }
1054 }
1055 }
1056 return true;
1057}
1058
1059bool TrajectoryExecutionManager::configure(TrajectoryExecutionContext& context,
1060 const moveit_msgs::msg::RobotTrajectory& trajectory,
1061 const std::vector<std::string>& controllers)
1062{
1063 if (trajectory.multi_dof_joint_trajectory.points.empty() && trajectory.joint_trajectory.points.empty())
1064 {
1065 // empty trajectories don't need to configure anything
1066 return true;
1067 }
1068
1069 reloadControllerInformation();
1070 std::set<std::string> actuated_joints;
1071
1072 auto is_actuated = [this](const std::string& joint_name) -> bool {
1073 const moveit::core::JointModel* jm = robot_model_->getJointOfVariable(joint_name);
1074 return (jm && !jm->isPassive() && !jm->getMimic() && jm->getType() != moveit::core::JointModel::FIXED);
1075 };
1076 for (const std::string& joint_name : trajectory.multi_dof_joint_trajectory.joint_names)
1077 {
1078 if (is_actuated(joint_name))
1079 actuated_joints.insert(joint_name);
1080 }
1081 for (const std::string& joint_name : trajectory.joint_trajectory.joint_names)
1082 {
1083 if (is_actuated(joint_name))
1084 actuated_joints.insert(joint_name);
1085 }
1086
1087 if (actuated_joints.empty())
1088 {
1089 RCLCPP_WARN(logger_, "The trajectory to execute specifies no joints");
1090 return false;
1091 }
1092
1093 if (controllers.empty())
1094 {
1095 bool retry = true;
1096 bool reloaded = false;
1097 while (retry)
1098 {
1099 retry = false;
1100 std::vector<std::string> all_controller_names;
1101 for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
1102 it != known_controllers_.end(); ++it)
1103 all_controller_names.push_back(it->first);
1104 if (selectControllers(actuated_joints, all_controller_names, context.controllers_))
1105 {
1106 if (distributeTrajectory(trajectory, context.controllers_, context.trajectory_parts_))
1107 return true;
1108 }
1109 else
1110 {
1111 // maybe we failed because we did not have a complete list of controllers
1112 if (!reloaded)
1113 {
1114 reloadControllerInformation();
1115 reloaded = true;
1116 retry = true;
1117 }
1118 }
1119 }
1120 }
1121 else
1122 {
1123 // check if the specified controllers are valid names;
1124 // if they appear not to be, try to reload the controller information, just in case they are new in the system
1125 bool reloaded = false;
1126 for (const std::string& controller : controllers)
1127 {
1128 if (known_controllers_.find(controller) == known_controllers_.end())
1129 {
1130 reloadControllerInformation();
1131 reloaded = true;
1132 break;
1133 }
1134 }
1135 if (reloaded)
1136 {
1137 for (const std::string& controller : controllers)
1138 {
1139 if (known_controllers_.find(controller) == known_controllers_.end())
1140 {
1141 std::stringstream stream;
1142 for (const auto& controller : known_controllers_)
1143 {
1144 stream << " `" << controller.first << '`';
1145 }
1146 RCLCPP_ERROR_STREAM(logger_,
1147 "Controller " << controller << " is not known. Known controllers: " << stream.str());
1148 return false;
1149 }
1150 }
1151 }
1152 if (selectControllers(actuated_joints, controllers, context.controllers_))
1153 {
1154 if (distributeTrajectory(trajectory, context.controllers_, context.trajectory_parts_))
1155 return true;
1156 }
1157 }
1158 std::stringstream ss;
1159 for (const std::string& actuated_joint : actuated_joints)
1160 ss << actuated_joint << ' ';
1161 RCLCPP_ERROR(logger_, "Unable to identify any set of controllers that can actuate the specified joints: [ %s]",
1162 ss.str().c_str());
1163
1164 std::stringstream ss2;
1165 std::map<std::string, ControllerInformation>::const_iterator mi;
1166 for (mi = known_controllers_.begin(); mi != known_controllers_.end(); ++mi)
1167 {
1168 ss2 << "controller '" << mi->second.name_ << "' controls joints:\n";
1169
1170 std::set<std::string>::const_iterator ji;
1171 for (ji = mi->second.joints_.begin(); ji != mi->second.joints_.end(); ++ji)
1172 {
1173 ss2 << " " << *ji << '\n';
1174 }
1175 }
1176 RCLCPP_ERROR(logger_, "Known controllers and their joints:\n%s", ss2.str().c_str());
1177
1178 if (!trajectory.multi_dof_joint_trajectory.joint_names.empty())
1179 {
1180 RCLCPP_WARN(logger_, "Hint: You can control multi-dof waypoints as joint trajectory by setting "
1181 "`trajectory_execution.control_multi_dof_joint_variables=True`");
1182 }
1183
1184 return false;
1185}
1186
1192
1193void TrajectoryExecutionManager::stopExecutionInternal()
1194{
1195 // execution_state_mutex_ needs to have been locked by the caller
1196 for (moveit_controller_manager::MoveItControllerHandlePtr& active_handle : active_handles_)
1197 {
1198 try
1199 {
1200 active_handle->cancelExecution();
1201 }
1202 catch (std::exception& ex)
1203 {
1204 RCLCPP_ERROR(logger_, "Caught %s when canceling execution.", ex.what());
1205 }
1206 }
1207}
1208
1210{
1211 if (!execution_complete_)
1212 {
1213 execution_state_mutex_.lock();
1214 if (!execution_complete_)
1215 {
1216 // we call cancel for all active handles; we know these are not being modified as we loop through them because of
1217 // the lock
1218 // we mark execution_complete_ as true ahead of time. Using this flag, executePart() will know that an external
1219 // trigger to stop has been received
1220 execution_complete_ = true;
1221 stopExecutionInternal();
1222
1223 // we set the status here; executePart() will not set status when execution_complete_ is true ahead of time
1225 execution_state_mutex_.unlock();
1226 RCLCPP_INFO(logger_, "Stopped trajectory execution.");
1227
1228 // wait for the execution thread to finish
1229 std::scoped_lock lock(execution_thread_mutex_);
1230 if (execution_thread_)
1231 {
1232 execution_thread_->join();
1233 execution_thread_.reset();
1234 }
1235
1236 if (auto_clear)
1237 clear();
1238 }
1239 else
1240 execution_state_mutex_.unlock();
1241 }
1242 else if (execution_thread_) // just in case we have some thread waiting to be joined from some point in the past, we
1243 // join it now
1244 {
1245 std::scoped_lock lock(execution_thread_mutex_);
1246 if (execution_thread_)
1247 {
1248 execution_thread_->join();
1249 execution_thread_.reset();
1250 }
1251 }
1252}
1253
1255{
1256 execute(callback, PathSegmentCompleteCallback(), auto_clear);
1257}
1258
1260 const PathSegmentCompleteCallback& part_callback, bool auto_clear)
1261{
1262 // skip execution if no trajectory have been pushed
1263 // it crashes otherwise
1264 if (trajectories_.empty())
1265 return;
1266
1267 stopExecution(false);
1268
1269 // check whether first trajectory starts at current robot state
1270 if (!trajectories_.empty() && !validate(*trajectories_.front()))
1271 {
1273 if (auto_clear)
1274 clear();
1275 if (callback)
1276 callback(last_execution_status_);
1277 return;
1278 }
1279
1280 // start the execution thread
1281 execution_complete_ = false;
1282 execution_thread_ = std::make_unique<std::thread>(&TrajectoryExecutionManager::executeThread, this, callback,
1283 part_callback, auto_clear);
1284}
1285
1287{
1288 {
1289 std::unique_lock<std::mutex> ulock(execution_state_mutex_);
1290 while (!execution_complete_)
1291 execution_complete_condition_.wait(ulock);
1292 }
1293
1294 // this will join the thread for executing sequences of trajectories
1295 stopExecution(false);
1296
1297 return last_execution_status_;
1298}
1299
1300void TrajectoryExecutionManager::clear()
1301{
1302 if (execution_complete_)
1303 {
1304 std::scoped_lock slock(execution_state_mutex_);
1305 for (TrajectoryExecutionContext* trajectory : trajectories_)
1306 delete trajectory;
1307 trajectories_.clear();
1308 }
1309 else
1310 RCLCPP_FATAL(logger_, "Expecting execution_complete_ to be true!");
1311}
1312
1313void TrajectoryExecutionManager::executeThread(const ExecutionCompleteCallback& callback,
1314 const PathSegmentCompleteCallback& part_callback, bool auto_clear)
1315{
1316 // if we already got a stop request before we even started anything, we abort
1317 if (execution_complete_)
1318 {
1320 if (callback)
1321 callback(last_execution_status_);
1322 return;
1323 }
1324
1325 RCLCPP_INFO(logger_, "Starting trajectory execution ...");
1326 // assume everything will be OK
1328
1329 // execute each trajectory, one after the other (executePart() is blocking) or until one fails.
1330 // on failure, the status is set by executePart(). Otherwise, it will remain as set above (success)
1331 std::size_t i = 0;
1332 for (; i < trajectories_.size(); ++i)
1333 {
1334 bool epart = executePart(i);
1335 if (epart && part_callback)
1336 part_callback(i);
1337 if (!epart || execution_complete_)
1338 {
1339 ++i;
1340 break;
1341 }
1342 }
1343
1344 // only report that execution finished successfully when the robot actually stopped moving
1345 if (last_execution_status_ == moveit_controller_manager::ExecutionStatus::SUCCEEDED)
1346 {
1347 std::scoped_lock slock(execution_state_mutex_);
1348 if (!execution_complete_)
1349 {
1350 waitForRobotToStop(*trajectories_[i - 1]);
1351 }
1352 }
1353
1354 RCLCPP_INFO(logger_, "Completed trajectory execution with status %s ...", last_execution_status_.asString().c_str());
1355
1356 // notify whoever is waiting for the event of trajectory completion
1357 execution_state_mutex_.lock();
1358 execution_complete_ = true;
1359 execution_state_mutex_.unlock();
1360 execution_complete_condition_.notify_all();
1361
1362 // clear the paths just executed, if needed
1363 if (auto_clear)
1364 clear();
1365
1366 // call user-specified callback
1367 if (callback)
1368 callback(last_execution_status_);
1369}
1370
1371bool TrajectoryExecutionManager::executePart(std::size_t part_index)
1372{
1373 TrajectoryExecutionContext& context = *trajectories_[part_index];
1374
1375 // first make sure desired controllers are active
1376 if (ensureActiveControllers(context.controllers_))
1377 {
1378 // stop if we are already asked to do so
1379 if (execution_complete_)
1380 return false;
1381
1382 std::vector<moveit_controller_manager::MoveItControllerHandlePtr> handles;
1383 {
1384 std::scoped_lock slock(execution_state_mutex_);
1385 if (!execution_complete_)
1386 {
1387 // time indexing uses this member too, so we lock this mutex as well
1388 time_index_mutex_.lock();
1389 current_context_ = part_index;
1390 time_index_mutex_.unlock();
1391 active_handles_.resize(context.controllers_.size());
1392 for (std::size_t i = 0; i < context.controllers_.size(); ++i)
1393 {
1394 moveit_controller_manager::MoveItControllerHandlePtr h;
1395 try
1396 {
1397 h = controller_manager_->getControllerHandle(context.controllers_[i]);
1398 }
1399 catch (std::exception& ex)
1400 {
1401 RCLCPP_ERROR(logger_, "Caught %s when retrieving controller handle", ex.what());
1402 }
1403 if (!h)
1404 {
1405 active_handles_.clear();
1406 current_context_ = -1;
1408 RCLCPP_ERROR(logger_, "No controller handle for controller '%s'. Aborting.",
1409 context.controllers_[i].c_str());
1410 return false;
1411 }
1412 active_handles_[i] = h;
1413 }
1414 handles = active_handles_; // keep a copy for later, to avoid thread safety issues
1415 for (std::size_t i = 0; i < context.trajectory_parts_.size(); ++i)
1416 {
1417 bool ok = false;
1418 try
1419 {
1420 ok = active_handles_[i]->sendTrajectory(context.trajectory_parts_[i]);
1421 }
1422 catch (std::exception& ex)
1423 {
1424 RCLCPP_ERROR(logger_, "Caught %s when sending trajectory to controller", ex.what());
1425 }
1426 if (!ok)
1427 {
1428 for (std::size_t j = 0; j < i; ++j)
1429 {
1430 try
1431 {
1432 active_handles_[j]->cancelExecution();
1433 }
1434 catch (std::exception& ex)
1435 {
1436 RCLCPP_ERROR(logger_, "Caught %s when canceling execution", ex.what());
1437 }
1438 }
1439 RCLCPP_ERROR(logger_, "Failed to send trajectory part %zu of %zu to controller %s", i + 1,
1440 context.trajectory_parts_.size(), active_handles_[i]->getName().c_str());
1441 if (i > 0)
1442 RCLCPP_ERROR(logger_, "Cancelling previously sent trajectory parts");
1443 active_handles_.clear();
1444 current_context_ = -1;
1446 return false;
1447 }
1448 }
1449 }
1450 }
1451
1452 // compute the expected duration of the trajectory and find the part of the trajectory that takes longest to execute
1453 rclcpp::Time current_time = node_->now();
1454 auto expected_trajectory_duration = rclcpp::Duration::from_seconds(0);
1455 int longest_part = -1;
1456 for (std::size_t i = 0; i < context.trajectory_parts_.size(); ++i)
1457 {
1458 auto d = rclcpp::Duration::from_seconds(0);
1459 if (!(context.trajectory_parts_[i].joint_trajectory.points.empty() &&
1460 context.trajectory_parts_[i].multi_dof_joint_trajectory.points.empty()))
1461 {
1462 if (rclcpp::Time(context.trajectory_parts_[i].joint_trajectory.header.stamp) > current_time)
1463 d = rclcpp::Time(context.trajectory_parts_[i].joint_trajectory.header.stamp) - current_time;
1464 if (rclcpp::Time(context.trajectory_parts_[i].multi_dof_joint_trajectory.header.stamp) > current_time)
1465 {
1466 d = std::max(d, rclcpp::Time(context.trajectory_parts_[i].multi_dof_joint_trajectory.header.stamp) -
1467 current_time);
1468 }
1469 d = d +
1470 std::max(context.trajectory_parts_[i].joint_trajectory.points.empty() ?
1471 rclcpp::Duration::from_seconds(0) :
1472 rclcpp::Duration(context.trajectory_parts_[i].joint_trajectory.points.back().time_from_start),
1473 context.trajectory_parts_[i].multi_dof_joint_trajectory.points.empty() ?
1474 rclcpp::Duration::from_seconds(0) :
1475 rclcpp::Duration(
1476 context.trajectory_parts_[i].multi_dof_joint_trajectory.points.back().time_from_start));
1477
1478 if (longest_part < 0 ||
1479 std::max(context.trajectory_parts_[i].joint_trajectory.points.size(),
1480 context.trajectory_parts_[i].multi_dof_joint_trajectory.points.size()) >
1481 std::max(context.trajectory_parts_[longest_part].joint_trajectory.points.size(),
1482 context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.points.size()))
1483 longest_part = i;
1484 }
1485
1486 // prefer controller-specific values over global ones if defined
1487 // TODO: the controller-specific parameters are static, but override
1488 // the global ones are configurable via dynamic reconfigure
1489 std::map<std::string, double>::const_iterator scaling_it =
1490 controller_allowed_execution_duration_scaling_.find(context.controllers_[i]);
1491 const double current_scaling = scaling_it != controller_allowed_execution_duration_scaling_.end() ?
1492 scaling_it->second :
1493 allowed_execution_duration_scaling_;
1494
1495 std::map<std::string, double>::const_iterator margin_it =
1496 controller_allowed_goal_duration_margin_.find(context.controllers_[i]);
1497 const double current_margin = margin_it != controller_allowed_goal_duration_margin_.end() ?
1498 margin_it->second :
1499 allowed_goal_duration_margin_;
1500
1501 // expected duration is the duration of the longest part
1502 expected_trajectory_duration =
1503 std::max(d * current_scaling + rclcpp::Duration::from_seconds(current_margin), expected_trajectory_duration);
1504 }
1505
1506 // construct a map from expected time to state index, for easy access to expected state location
1507 if (longest_part >= 0)
1508 {
1509 std::scoped_lock slock(time_index_mutex_);
1510
1511 if (context.trajectory_parts_[longest_part].joint_trajectory.points.size() >=
1512 context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.points.size())
1513 {
1514 auto d = rclcpp::Duration::from_seconds(0);
1515 if (rclcpp::Time(context.trajectory_parts_[longest_part].joint_trajectory.header.stamp) > current_time)
1516 d = rclcpp::Time(context.trajectory_parts_[longest_part].joint_trajectory.header.stamp) - current_time;
1517 for (trajectory_msgs::msg::JointTrajectoryPoint& point :
1518 context.trajectory_parts_[longest_part].joint_trajectory.points)
1519 time_index_.push_back(current_time + d + rclcpp::Duration(point.time_from_start));
1520 }
1521 else
1522 {
1523 auto d = rclcpp::Duration::from_seconds(0);
1524 if (rclcpp::Time(context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.header.stamp) > current_time)
1525 {
1526 d = rclcpp::Time(context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.header.stamp) -
1527 current_time;
1528 }
1529 for (trajectory_msgs::msg::MultiDOFJointTrajectoryPoint& point :
1530 context.trajectory_parts_[longest_part].multi_dof_joint_trajectory.points)
1531 time_index_.push_back(current_time + d + rclcpp::Duration(point.time_from_start));
1532 }
1533 }
1534
1535 bool result = true;
1536 for (moveit_controller_manager::MoveItControllerHandlePtr& handle : handles)
1537 {
1538 if (execution_duration_monitoring_)
1539 {
1540 if (!handle->waitForExecution(expected_trajectory_duration))
1541 {
1542 if (!execution_complete_ && node_->now() - current_time > expected_trajectory_duration)
1543 {
1544 RCLCPP_ERROR(logger_,
1545 "Controller is taking too long to execute trajectory (the expected upper "
1546 "bound for the trajectory execution was %lf seconds). Stopping trajectory.",
1547 expected_trajectory_duration.seconds());
1548 {
1549 std::scoped_lock slock(execution_state_mutex_);
1550 stopExecutionInternal(); // this is really tricky. we can't call stopExecution() here, so we call the
1551 // internal function only
1552 }
1554 result = false;
1555 break;
1556 }
1557 }
1558 }
1559 else
1560 handle->waitForExecution();
1561
1562 // if something made the trajectory stop, we stop this thread too
1563 if (execution_complete_)
1564 {
1565 result = false;
1566 break;
1567 }
1568 else if (handle->getLastExecutionStatus() != moveit_controller_manager::ExecutionStatus::SUCCEEDED)
1569 {
1570 RCLCPP_WARN_STREAM(logger_, "Controller handle " << handle->getName() << " reports status "
1571 << handle->getLastExecutionStatus().asString());
1572 last_execution_status_ = handle->getLastExecutionStatus();
1573 result = false;
1574 }
1575 }
1576
1577 // clear the active handles
1578 execution_state_mutex_.lock();
1579 active_handles_.clear();
1580
1581 // clear the time index
1582 time_index_mutex_.lock();
1583 time_index_.clear();
1584 current_context_ = -1;
1585 time_index_mutex_.unlock();
1586
1587 execution_state_mutex_.unlock();
1588 return result;
1589 }
1590 else
1591 {
1592 RCLCPP_ERROR(logger_, "Active status of required controllers can not be assured.");
1594 return false;
1595 }
1596}
1597
1598bool TrajectoryExecutionManager::waitForRobotToStop(const TrajectoryExecutionContext& context, double wait_time)
1599{
1600 // skip waiting for convergence?
1601 if ((allowed_start_tolerance_ == 0 && allowed_start_tolerance_joints_.empty()) || !wait_for_trajectory_completion_)
1602 {
1603 RCLCPP_INFO(logger_, "Not waiting for trajectory completion");
1604 return true;
1605 }
1606
1607 auto start = std::chrono::system_clock::now();
1608 double time_remaining = wait_time;
1609
1610 moveit::core::RobotStatePtr prev_state, cur_state;
1611 prev_state = csm_->getCurrentState();
1612 prev_state->enforceBounds();
1613
1614 // assume robot stopped when 3 consecutive checks yield the same robot state
1615 unsigned int no_motion_count = 0; // count iterations with no motion
1616 while (time_remaining > 0. && no_motion_count < 3)
1617 {
1618 if (!csm_->waitForCurrentState(node_->now(), time_remaining) || !(cur_state = csm_->getCurrentState()))
1619 {
1620 RCLCPP_WARN(logger_, "Failed to receive current joint state");
1621 return false;
1622 }
1623 cur_state->enforceBounds();
1624 std::chrono::duration<double> elapsed_seconds = std::chrono::system_clock::now() - start;
1625 time_remaining = wait_time - elapsed_seconds.count(); // remaining wait_time
1626
1627 // check for motion in effected joints of execution context
1628 bool moved = false;
1629 for (const auto& trajectory : context.trajectory_parts_)
1630 {
1631 const std::vector<std::string>& joint_names = trajectory.joint_trajectory.joint_names;
1632 const std::size_t n = joint_names.size();
1633
1634 for (std::size_t i = 0; i < n && !moved; ++i)
1635 {
1636 const moveit::core::JointModel* jm = robot_model_->getJointOfVariable(joint_names[i]);
1637 if (!jm)
1638 continue; // joint vanished from robot state (shouldn't happen), but we don't care
1639
1640 const double joint_start_tolerance = getAllowedStartToleranceJoint(joint_names[i]);
1641 if (fabs(jm->distance(cur_state->getJointPositions(jm), prev_state->getJointPositions(jm))) >
1642 joint_start_tolerance)
1643 {
1644 moved = true;
1645 no_motion_count = 0;
1646 break;
1647 }
1648 }
1649 }
1650
1651 if (!moved)
1652 ++no_motion_count;
1653
1654 std::swap(prev_state, cur_state);
1655 }
1656
1657 return time_remaining > 0;
1658}
1659
1661{
1662 std::scoped_lock slock(time_index_mutex_);
1663 if (current_context_ < 0)
1664 return std::make_pair(-1, -1);
1665 if (time_index_.empty())
1666 return std::make_pair(static_cast<int>(current_context_), -1);
1667 std::vector<rclcpp::Time>::const_iterator time_index_it =
1668 std::lower_bound(time_index_.begin(), time_index_.end(), node_->now());
1669 int pos = time_index_it - time_index_.begin();
1670 return std::make_pair(static_cast<int>(current_context_), pos);
1671}
1672
1673const std::vector<TrajectoryExecutionManager::TrajectoryExecutionContext*>&
1675{
1676 return trajectories_;
1677}
1678
1683
1685{
1686 const moveit::core::JointModelGroup* joint_model_group = robot_model_->getJointModelGroup(group);
1687 if (joint_model_group)
1688 {
1689 return ensureActiveControllersForJoints(joint_model_group->getJointModelNames());
1690 }
1691 else
1692 {
1693 return false;
1694 }
1695}
1696
1697bool TrajectoryExecutionManager::ensureActiveControllersForJoints(const std::vector<std::string>& joints)
1698{
1699 std::vector<std::string> all_controller_names;
1700 for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
1701 it != known_controllers_.end(); ++it)
1702 all_controller_names.push_back(it->first);
1703 std::vector<std::string> selected_controllers;
1704 std::set<std::string> jset;
1705 for (const std::string& joint : joints)
1706 {
1707 const moveit::core::JointModel* jm = robot_model_->getJointModel(joint);
1708 if (jm)
1709 {
1710 if (jm->isPassive() || jm->getMimic() != nullptr || jm->getType() == moveit::core::JointModel::FIXED)
1711 continue;
1712 jset.insert(joint);
1713 }
1714 }
1715
1716 if (selectControllers(jset, all_controller_names, selected_controllers))
1717 {
1718 return ensureActiveControllers(selected_controllers);
1719 }
1720 else
1721 {
1722 return false;
1723 }
1724}
1725
1727{
1728 return ensureActiveControllers(std::vector<std::string>(1, controller));
1729}
1730
1731bool TrajectoryExecutionManager::ensureActiveControllers(const std::vector<std::string>& controllers)
1732{
1733 reloadControllerInformation();
1734
1735 updateControllersState(DEFAULT_CONTROLLER_INFORMATION_VALIDITY_AGE);
1736
1737 if (manage_controllers_)
1738 {
1739 std::vector<std::string> controllers_to_activate;
1740 std::vector<std::string> controllers_to_deactivate;
1741 std::set<std::string> joints_to_be_activated;
1742 std::set<std::string> joints_to_be_deactivated;
1743 for (const std::string& controller : controllers)
1744 {
1745 std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.find(controller);
1746 if (it == known_controllers_.end())
1747 {
1748 std::stringstream stream;
1749 for (const auto& controller : known_controllers_)
1750 {
1751 stream << " `" << controller.first << '`';
1752 }
1753 RCLCPP_WARN_STREAM(logger_, "Controller " << controller << " is not known. Known controllers: " << stream.str());
1754 return false;
1755 }
1756 if (!it->second.state_.active_)
1757 {
1758 RCLCPP_DEBUG_STREAM(logger_, "Need to activate " << controller);
1759 controllers_to_activate.push_back(controller);
1760 joints_to_be_activated.insert(it->second.joints_.begin(), it->second.joints_.end());
1761 for (const std::string& overlapping_controller : it->second.overlapping_controllers_)
1762 {
1763 const ControllerInformation& ci = known_controllers_[overlapping_controller];
1764 if (ci.state_.active_)
1765 {
1766 controllers_to_deactivate.push_back(overlapping_controller);
1767 joints_to_be_deactivated.insert(ci.joints_.begin(), ci.joints_.end());
1768 }
1769 }
1770 }
1771 else
1772 RCLCPP_DEBUG_STREAM(logger_, "Controller " << controller << " is already active");
1773 }
1774 std::set<std::string> diff;
1775 std::set_difference(joints_to_be_deactivated.begin(), joints_to_be_deactivated.end(),
1776 joints_to_be_activated.begin(), joints_to_be_activated.end(), std::inserter(diff, diff.end()));
1777 if (!diff.empty())
1778 {
1779 // find the set of controllers that do not overlap with the ones we want to activate so far
1780 std::vector<std::string> possible_additional_controllers;
1781 for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
1782 it != known_controllers_.end(); ++it)
1783 {
1784 bool ok = true;
1785 for (const std::string& controller_to_activate : controllers_to_activate)
1786 {
1787 if (it->second.overlapping_controllers_.find(controller_to_activate) !=
1788 it->second.overlapping_controllers_.end())
1789 {
1790 ok = false;
1791 break;
1792 }
1793 }
1794 if (ok)
1795 possible_additional_controllers.push_back(it->first);
1796 }
1797
1798 // out of the allowable controllers, try to find a subset of controllers that covers the joints to be actuated
1799 std::vector<std::string> additional_controllers;
1800 if (selectControllers(diff, possible_additional_controllers, additional_controllers))
1801 {
1802 controllers_to_activate.insert(controllers_to_activate.end(), additional_controllers.begin(),
1803 additional_controllers.end());
1804 }
1805 else
1806 {
1807 return false;
1808 }
1809 }
1810 if (!controllers_to_activate.empty() || !controllers_to_deactivate.empty())
1811 {
1812 if (controller_manager_)
1813 {
1814 // load controllers to be activated, if needed, and reset the state update cache
1815 for (const std::string& controller_to_activate : controllers_to_activate)
1816 {
1817 ControllerInformation& ci = known_controllers_[controller_to_activate];
1818 ci.last_update_ = rclcpp::Time(0, 0, RCL_ROS_TIME);
1819 }
1820 // reset the state update cache
1821 for (const std::string& controller_to_deactivate : controllers_to_deactivate)
1822 known_controllers_[controller_to_deactivate].last_update_ = rclcpp::Time(0, 0, RCL_ROS_TIME);
1823 return controller_manager_->switchControllers(controllers_to_activate, controllers_to_deactivate);
1824 }
1825 else
1826 return false;
1827 }
1828 else
1829 return true;
1830 }
1831 else
1832 {
1833 std::set<std::string> originally_active;
1834 for (std::map<std::string, ControllerInformation>::const_iterator it = known_controllers_.begin();
1835 it != known_controllers_.end(); ++it)
1836 {
1837 if (it->second.state_.active_)
1838 {
1839 originally_active.insert(it->first);
1840 }
1841 }
1842 return std::includes(originally_active.begin(), originally_active.end(), controllers.begin(), controllers.end());
1843 }
1844}
1845
1846void TrajectoryExecutionManager::loadControllerParams()
1847{
1848 for (const auto& controller : known_controllers_)
1849 {
1850 const std::string& controller_name = controller.first;
1851 for (const auto& controller_manager_name : controller_manager_loader_->getDeclaredClasses())
1852 {
1853 const std::string parameter_prefix =
1854 controller_manager_loader_->getClassPackage(controller_manager_name) + "." + controller_name;
1855
1856 double allowed_execution_duration_scaling;
1857 if (node_->get_parameter(parameter_prefix + ".allowed_execution_duration_scaling",
1858 allowed_execution_duration_scaling))
1859 controller_allowed_execution_duration_scaling_.insert({ controller_name, allowed_execution_duration_scaling });
1860
1861 double allowed_goal_duration_margin;
1862 if (node_->get_parameter(parameter_prefix + ".allowed_goal_duration_margin", allowed_goal_duration_margin))
1863 controller_allowed_goal_duration_margin_.insert({ controller_name, allowed_goal_duration_margin });
1864 }
1865 }
1866}
1867
1868double TrajectoryExecutionManager::getAllowedStartToleranceJoint(const std::string& joint_name) const
1869{
1870 auto start_tolerance_it = allowed_start_tolerance_joints_.find(joint_name);
1871 return start_tolerance_it != allowed_start_tolerance_joints_.end() ? start_tolerance_it->second :
1872 allowed_start_tolerance_;
1873}
1874
1875void TrajectoryExecutionManager::setAllowedStartToleranceJoint(const std::string& parameter_name,
1876 double joint_start_tolerance)
1877{
1878 if (joint_start_tolerance < 0)
1879 {
1880 RCLCPP_WARN(logger_, "%s has a negative value. The start tolerance value for that joint was not updated.",
1881 parameter_name.c_str());
1882 return;
1883 }
1884
1885 // get the joint name by removing the parameter prefix if necessary
1886 std::string joint_name = parameter_name;
1887 const std::string parameter_prefix = "trajectory_execution.allowed_start_tolerance_joints.";
1888 if (parameter_name.find(parameter_prefix) == 0)
1889 joint_name = joint_name.substr(parameter_prefix.length()); // remove prefix
1890
1891 if (!robot_model_->hasJointModel(joint_name))
1892 {
1893 RCLCPP_WARN(logger_,
1894 "Joint '%s' was not found in the robot model. "
1895 "The start tolerance value for that joint was not updated.",
1896 joint_name.c_str());
1897 return;
1898 }
1899
1900 allowed_start_tolerance_joints_.insert_or_assign(joint_name, joint_start_tolerance);
1901}
1902
1903void TrajectoryExecutionManager::initializeAllowedStartToleranceJoints()
1904{
1905 allowed_start_tolerance_joints_.clear();
1906
1907 // retrieve all parameters under "trajectory_execution.allowed_start_tolerance_joints"
1908 // that correspond to existing joints in the robot model
1909 for (const auto& joint_name : robot_model_->getJointModelNames())
1910 {
1911 double joint_start_tolerance;
1912 const std::string parameter_name = "trajectory_execution.allowed_start_tolerance_joints." + joint_name;
1913 if (node_->get_parameter(parameter_name, joint_start_tolerance))
1914 {
1915 if (joint_start_tolerance < 0)
1916 {
1917 RCLCPP_WARN(logger_,
1918 "%s has a negative value. The start tolerance value for that joint "
1919 "will default to allowed_start_tolerance.",
1920 parameter_name.c_str());
1921 continue;
1922 }
1923 allowed_start_tolerance_joints_.insert({ joint_name, joint_start_tolerance });
1924 }
1925 }
1926}
1927
1928} // 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....
const JointModel * getMimic() const
Get the joint this one is mimicking.
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.
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...
JointType getType() const
Get the type of joint.
Representation of a robot's state. This includes position, velocity, acceleration and effort.
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
Maintain a sequence of waypoints and the time durations between these waypoints.
RobotTrajectory & setRobotTrajectoryMsg(const moveit::core::RobotState &reference_state, const trajectory_msgs::msg::JointTrajectory &trajectory)
Copy the content of the trajectory message into this class. The trajectory message itself is not requ...
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.
double allowedStartTolerance() const
Get the current joint-value for validating trajectory's start point against current robot state.
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.
double allowedExecutionDurationScaling() const
Get the current scaling of the duration of a trajectory to get the allowed duration of 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.
bool executionDurationMonitoring() const
Get the current status of the monitoring of trajectory execution duration.
double allowedGoalDurationMargin() const
Get the current margin of the duration of a trajectory to get the allowed duration of execution.
bool waitForTrajectoryCompletion() const
Get the current state of waiting for the trajectory being completed.
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')
double executionVelocityScaling() const
Get the current scaling of the execution velocities.
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
Main namespace for MoveIt.
std::optional< trajectory_msgs::msg::JointTrajectory > toJointTrajectory(const RobotTrajectory &trajectory, bool include_mdof_joints=false, const std::vector< std::string > &joint_filter={})
Converts a RobotTrajectory to a JointTrajectory message.
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;.