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