moveit2
The MoveIt Motion Planning Framework for ROS 2.
planning_pipeline.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, Sebastian Jahr */
36 
38 #include <fmt/format.h>
39 #include <moveit/utils/logger.hpp>
40 
41 namespace
42 {
43 namespace
44 {
45 rclcpp::Logger getLogger()
46 {
47  return moveit::getLogger("moveit.ros.planning_pipeline");
48 }
49 } // namespace
50 
57 [[nodiscard]] std::vector<moveit_msgs::msg::Constraints>
58 getTrajectoryConstraints(const robot_trajectory::RobotTrajectoryPtr& trajectory)
59 {
60  const std::vector<std::string> joint_names =
61  trajectory->getFirstWayPoint().getJointModelGroup(trajectory->getGroupName())->getActiveJointModelNames();
62 
63  std::vector<moveit_msgs::msg::Constraints> trajectory_constraints;
64  // Iterate through waypoints and create a joint constraint for each
65  for (size_t waypoint_index = 0; waypoint_index < trajectory->getWayPointCount(); ++waypoint_index)
66  {
67  moveit_msgs::msg::Constraints new_waypoint_constraint;
68  // Iterate through joints and copy waypoint data to joint constraint
69  for (const auto& joint_name : joint_names)
70  {
71  moveit_msgs::msg::JointConstraint new_joint_constraint;
72  new_joint_constraint.joint_name = joint_name;
73  new_joint_constraint.position = trajectory->getWayPoint(waypoint_index).getVariablePosition(joint_name);
74  new_waypoint_constraint.joint_constraints.push_back(new_joint_constraint);
75  }
76  trajectory_constraints.push_back(new_waypoint_constraint);
77  }
78  return trajectory_constraints;
79 }
80 } // namespace
81 
82 namespace planning_pipeline
83 {
84 PlanningPipeline::PlanningPipeline(const moveit::core::RobotModelConstPtr& model,
85  const std::shared_ptr<rclcpp::Node>& node, const std::string& parameter_namespace)
86  : active_{ false }
87  , node_(node)
88  , parameter_namespace_(parameter_namespace)
89  , robot_model_(model)
90  , logger_(moveit::getLogger("moveit.ros.planning_pipeline"))
91 {
92  auto param_listener = planning_pipeline_parameters::ParamListener(node, parameter_namespace);
93  pipeline_parameters_ = param_listener.get_params();
94 
95  configure();
96 }
97 
98 PlanningPipeline::PlanningPipeline(const moveit::core::RobotModelConstPtr& model,
99  const std::shared_ptr<rclcpp::Node>& node, const std::string& parameter_namespace,
100  const std::vector<std::string>& planner_plugin_names,
101  const std::vector<std::string>& request_adapter_plugin_names,
102  const std::vector<std::string>& response_adapter_plugin_names)
103  : active_{ false }
104  , node_(node)
105  , parameter_namespace_(parameter_namespace)
106  , robot_model_(model)
107  , logger_(moveit::getLogger("moveit.ros.planning_pipeline"))
108 {
109  pipeline_parameters_.planning_plugins = planner_plugin_names;
110  pipeline_parameters_.request_adapters = request_adapter_plugin_names;
111  pipeline_parameters_.response_adapters = response_adapter_plugin_names;
112  configure();
113 }
114 
115 void PlanningPipeline::configure()
116 {
117  // If progress topic parameter is not empty, initialize publisher
118  if (!pipeline_parameters_.progress_topic.empty())
119  {
120  progress_publisher_ = node_->create_publisher<moveit_msgs::msg::PipelineState>(pipeline_parameters_.progress_topic,
121  rclcpp::SystemDefaultsQoS());
122  }
123 
124  // Create planner plugin loader
125  try
126  {
127  planner_plugin_loader_ = std::make_unique<pluginlib::ClassLoader<planning_interface::PlannerManager>>(
128  "moveit_core", "planning_interface::PlannerManager");
129  }
130  catch (pluginlib::PluginlibException& ex)
131  {
132  RCLCPP_FATAL(logger_, "Exception while creating planning plugin loader %s", ex.what());
133  throw;
134  }
135 
136  if (pipeline_parameters_.planning_plugins.empty() || pipeline_parameters_.planning_plugins.at(0) == "UNKNOWN")
137  {
138  const std::string classes_str = fmt::format("{}", fmt::join(planner_plugin_loader_->getDeclaredClasses(), ", "));
139  throw std::runtime_error("Planning plugin name is empty or not defined in namespace '" + parameter_namespace_ +
140  "'. Please choose one of the available plugins: " + classes_str);
141  }
142 
143  for (const auto& planner_name : pipeline_parameters_.planning_plugins)
144  {
145  planning_interface::PlannerManagerPtr planner_instance;
146 
147  // Load plugin
148  try
149  {
150  planner_instance = planner_plugin_loader_->createUniqueInstance(planner_name);
151  }
152  catch (pluginlib::PluginlibException& ex)
153  {
154  std::string classes_str = fmt::format("{}", fmt::join(planner_plugin_loader_->getDeclaredClasses(), ", "));
155  RCLCPP_FATAL(getLogger(),
156  "Exception while loading planner '%s': %s"
157  "Available plugins: %s",
158  planner_name.c_str(), ex.what(), classes_str.c_str());
159  throw;
160  }
161 
162  // Check if planner is not NULL
163  if (!planner_instance)
164  {
165  throw std::runtime_error("Unable to initialize planning plugin " + planner_name +
166  ". Planner instance is nullptr.");
167  }
168 
169  // Initialize planner
170  if (!planner_instance->initialize(robot_model_, node_, parameter_namespace_))
171  {
172  throw std::runtime_error("Unable to initialize planning plugin " + planner_name);
173  }
174  RCLCPP_INFO(getLogger(), "Successfully loaded planner '%s'", planner_instance->getDescription().c_str());
175  planner_map_.insert(std::make_pair(planner_name, planner_instance));
176  }
177 
178  // Load the planner request adapters
179  if (!pipeline_parameters_.request_adapters.empty())
180  {
181  try
182  {
183  request_adapter_plugin_loader_ =
184  std::make_unique<pluginlib::ClassLoader<planning_interface::PlanningRequestAdapter>>(
185  "moveit_core", "planning_interface::PlanningRequestAdapter");
186  }
187  catch (pluginlib::PluginlibException& ex)
188  {
189  RCLCPP_FATAL(logger_, "Exception while creating planning plugin loader %s", ex.what());
190  throw;
191  }
192 
193  if (request_adapter_plugin_loader_)
194  {
195  loadPluginVector<planning_interface::PlanningRequestAdapter>(node_, request_adapter_plugin_loader_,
196  planning_request_adapter_vector_,
197  pipeline_parameters_.request_adapters,
198  parameter_namespace_);
199  }
200  else
201  {
202  RCLCPP_WARN(logger_, "No planning request adapter names specified.");
203  }
204  }
205  else
206  {
207  RCLCPP_WARN(logger_, "No planning request adapter names specified.");
208  }
209  // Load the planner response adapters
210  if (!pipeline_parameters_.response_adapters.empty())
211  {
212  try
213  {
214  response_adapter_plugin_loader_ =
215  std::make_unique<pluginlib::ClassLoader<planning_interface::PlanningResponseAdapter>>(
216  "moveit_core", "planning_interface::PlanningResponseAdapter");
217  }
218  catch (pluginlib::PluginlibException& ex)
219  {
220  RCLCPP_FATAL(logger_, "Exception while creating planning plugin loader %s", ex.what());
221  throw;
222  }
223  if (response_adapter_plugin_loader_)
224  {
225  loadPluginVector<planning_interface::PlanningResponseAdapter>(node_, response_adapter_plugin_loader_,
226  planning_response_adapter_vector_,
227  pipeline_parameters_.response_adapters,
228  parameter_namespace_);
229  }
230  }
231  else
232  {
233  RCLCPP_WARN(logger_, "No planning response adapter names specified.");
234  }
235 }
236 
237 void PlanningPipeline::publishPipelineState(moveit_msgs::msg::MotionPlanRequest req,
239  const std::string& pipeline_stage) const
240 {
241  if (progress_publisher_)
242  {
243  moveit_msgs::msg::PipelineState progress;
244  progress.request = std::move(req);
245  res.getMessage(progress.response);
246  progress.pipeline_stage = pipeline_stage;
247  progress_publisher_->publish(progress);
248  }
249 }
250 
251 bool PlanningPipeline::generatePlan(const planning_scene::PlanningSceneConstPtr& planning_scene,
254  const bool publish_received_requests) const
255 {
256  assert(!planner_map_.empty());
257 
258  // Set planning pipeline active
259  active_ = true;
260 
261  // broadcast the request we are about to work on, if needed
262  if (publish_received_requests)
263  {
264  publishPipelineState(req, res, std::string(""));
265  }
266 
267  // ---------------------------------
268  // Solve the motion planning problem
269  // ---------------------------------
270 
271  planning_interface::MotionPlanRequest mutable_request = req;
272  try
273  {
274  // Call plan request adapter chain
275  for (const auto& req_adapter : planning_request_adapter_vector_)
276  {
277  assert(req_adapter);
278  RCLCPP_INFO(node_->get_logger(), "Calling PlanningRequestAdapter '%s'", req_adapter->getDescription().c_str());
279  const auto status = req_adapter->adapt(planning_scene, mutable_request);
280  res.error_code = status.val;
281  // Publish progress
282  publishPipelineState(mutable_request, res, req_adapter->getDescription());
283  // If adapter does not succeed, break chain and return false
284  if (!res.error_code)
285  {
286  RCLCPP_ERROR(node_->get_logger(),
287  "PlanningRequestAdapter '%s' failed, because '%s'. Aborting planning pipeline.",
288  req_adapter->getDescription().c_str(), status.message.c_str());
289  active_ = false;
290  return false;
291  }
292  }
293 
294  // Call planners
295  for (const auto& planner_name : pipeline_parameters_.planning_plugins)
296  {
297  const auto& planner = planner_map_.at(planner_name);
298  // Update reference trajectory with latest solution (if available)
299  if (res.trajectory)
300  {
301  mutable_request.trajectory_constraints.constraints = getTrajectoryConstraints(res.trajectory);
302  }
303 
304  // Try creating a planning context
305  planning_interface::PlanningContextPtr context =
306  planner->getPlanningContext(planning_scene, mutable_request, res.error_code);
307  if (!context)
308  {
309  RCLCPP_ERROR(node_->get_logger(),
310  "Failed to create PlanningContext for planner '%s'. Aborting planning pipeline.",
311  planner->getDescription().c_str());
312  res.error_code = moveit::core::MoveItErrorCode::PLANNING_FAILED;
313  active_ = false;
314  return false;
315  }
316 
317  // Run planner
318  RCLCPP_INFO(node_->get_logger(), "Calling Planner '%s'", planner->getDescription().c_str());
319  context->solve(res);
320  publishPipelineState(mutable_request, res, planner->getDescription());
321 
322  // If planner does not succeed, break chain and return false
323  if (!res.error_code)
324  {
325  RCLCPP_ERROR(node_->get_logger(), "Planner '%s' failed with error code %s", planner->getDescription().c_str(),
326  errorCodeToString(res.error_code).c_str());
327  active_ = false;
328  return false;
329  }
330  }
331 
332  // Call plan response adapter chain
333  if (res.error_code)
334  {
335  // Call plan request adapter chain
336  for (const auto& res_adapter : planning_response_adapter_vector_)
337  {
338  assert(res_adapter);
339  RCLCPP_INFO(node_->get_logger(), "Calling PlanningResponseAdapter '%s'", res_adapter->getDescription().c_str());
340  res_adapter->adapt(planning_scene, mutable_request, res);
341  publishPipelineState(mutable_request, res, res_adapter->getDescription());
342  // If adapter does not succeed, break chain and return false
343  if (!res.error_code)
344  {
345  RCLCPP_ERROR(node_->get_logger(), "PlanningResponseAdapter '%s' failed with error code %s",
346  res_adapter->getDescription().c_str(), errorCodeToString(res.error_code).c_str());
347  active_ = false;
348  return false;
349  }
350  }
351  }
352  }
353  catch (std::exception& ex)
354  {
355  RCLCPP_ERROR(logger_, "Exception caught: '%s'", ex.what());
356  // Set planning pipeline to inactive
357  active_ = false;
358  return false;
359  }
360 
361  // Make sure that planner id is set in the response
362  if (res.planner_id.empty())
363  {
364  RCLCPP_WARN(logger_,
365  "The planner plugin did not fill out the 'planner_id' field of the MotionPlanResponse. Setting "
366  "it to the planner ID name of the MotionPlanRequest assuming that the planner plugin does warn "
367  "you if it does not use the requested planner.");
368  res.planner_id = req.planner_id;
369  }
370 
371  // Set planning pipeline to inactive
372  active_ = false;
373  return static_cast<bool>(res);
374 }
375 
377 {
378  for (const auto& planner_pair : planner_map_)
379  {
380  if (planner_pair.second)
381  {
382  planner_pair.second->terminate();
383  }
384  }
385 }
386 } // namespace planning_pipeline
bool generatePlan(const planning_scene::PlanningSceneConstPtr &, const planning_interface::MotionPlanRequest &, planning_interface::MotionPlanResponse &, const bool, const bool, const bool) const
void terminate() const
Request termination, if a generatePlan() function is currently computing plans.
PlanningPipeline(const moveit::core::RobotModelConstPtr &model, const std::shared_ptr< rclcpp::Node > &node, const std::string &parameter_namespace)
Given a robot model, a ROS node, and a parameter namespace, initialize the planning pipeline....
std::string errorCodeToString(const MoveItErrorCode &error_code)
Convenience function to translated error message into string.
Main namespace for MoveIt.
Definition: exceptions.h:43
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
moveit_msgs::msg::MotionPlanRequest MotionPlanRequest
This namespace includes the central class for representing planning contexts.
Response to a planning query.
moveit::core::MoveItErrorCode error_code
robot_trajectory::RobotTrajectoryPtr trajectory
void getMessage(moveit_msgs::msg::MotionPlanResponse &msg) const
Construct a ROS message from struct data.