moveit2
The MoveIt Motion Planning Framework for ROS 2.
planning_pipeline_interfaces.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2023, PickNik 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 PickNik Inc. 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: Sebastian Jahr */
36 
38 #include <moveit/utils/logger.hpp>
39 
40 #include <thread>
41 
42 namespace moveit
43 {
44 namespace planning_pipeline_interfaces
45 {
46 
47 rclcpp::Logger getLogger()
48 {
49  return moveit::getLogger("planning_pipeline_interfaces");
50 }
51 
54  const ::planning_scene::PlanningSceneConstPtr& planning_scene,
55  const std::unordered_map<std::string, planning_pipeline::PlanningPipelinePtr>& planning_pipelines)
56 {
57  ::planning_interface::MotionPlanResponse motion_plan_response;
58  auto it = planning_pipelines.find(motion_plan_request.pipeline_id);
59  if (it == planning_pipelines.end())
60  {
61  RCLCPP_ERROR(getLogger(), "No planning pipeline available for name '%s'", motion_plan_request.pipeline_id.c_str());
62  motion_plan_response.error_code = moveit::core::MoveItErrorCode::FAILURE;
63  return motion_plan_response;
64  }
65  const planning_pipeline::PlanningPipelinePtr pipeline = it->second;
66  if (!pipeline->generatePlan(planning_scene, motion_plan_request, motion_plan_response))
67  {
68  motion_plan_response.error_code = moveit::core::MoveItErrorCode::FAILURE;
69  }
70  return motion_plan_response;
71 }
72 
73 const std::vector<::planning_interface::MotionPlanResponse> planWithParallelPipelines(
74  const std::vector<::planning_interface::MotionPlanRequest>& motion_plan_requests,
75  const ::planning_scene::PlanningSceneConstPtr& planning_scene,
76  const std::unordered_map<std::string, planning_pipeline::PlanningPipelinePtr>& planning_pipelines,
77  const StoppingCriterionFunction& stopping_criterion_callback,
78  const SolutionSelectionFunction& solution_selection_function)
79 {
80  // Create solutions container
81  PlanResponsesContainer plan_responses_container{ motion_plan_requests.size() };
82  std::vector<std::thread> planning_threads;
83  planning_threads.reserve(motion_plan_requests.size());
84 
85  // Print a warning if more parallel planning problems than available concurrent threads are defined. If
86  // std::thread::hardware_concurrency() is not defined, the command returns 0 so the check does not work
87  const auto hardware_concurrency = std::thread::hardware_concurrency();
88  if (motion_plan_requests.size() > hardware_concurrency && hardware_concurrency != 0)
89  {
90  RCLCPP_WARN(getLogger(),
91  "More parallel planning problems defined ('%ld') than possible to solve concurrently with the "
92  "hardware ('%d')",
93  motion_plan_requests.size(), hardware_concurrency);
94  }
95 
96  // Launch planning threads
97  for (const auto& request : motion_plan_requests)
98  {
99  auto planning_thread = std::thread([&]() {
100  auto plan_solution = ::planning_interface::MotionPlanResponse();
101  try
102  {
103  // Use planning scene if provided, otherwise the planning scene from planning scene monitor is used
104  plan_solution = planWithSinglePipeline(request, planning_scene, planning_pipelines);
105  }
106  catch (const std::exception& e)
107  {
108  RCLCPP_ERROR(getLogger(), "Planning pipeline '%s' threw exception '%s'", request.pipeline_id.c_str(), e.what());
110  plan_solution.error_code = moveit::core::MoveItErrorCode::FAILURE;
111  }
112  plan_solution.planner_id = request.planner_id;
113  plan_responses_container.pushBack(plan_solution);
114 
115  if (stopping_criterion_callback != nullptr)
116  {
117  if (stopping_criterion_callback(plan_responses_container, motion_plan_requests))
118  {
119  // Terminate planning pipelines
120  RCLCPP_INFO(getLogger(), "Stopping criterion met: Terminating planning pipelines that are still active");
121  for (const auto& request : motion_plan_requests)
122  {
123  try
124  {
125  const auto& planning_pipeline = planning_pipelines.at(request.pipeline_id);
126  if (planning_pipeline->isActive())
127  {
128  planning_pipeline->terminate();
129  }
130  }
131  catch (const std::out_of_range&)
132  {
133  RCLCPP_WARN(getLogger(), "Cannot terminate pipeline '%s' because no pipeline with that name exists",
134  request.pipeline_id.c_str());
135  }
136  }
137  }
138  }
139  });
140  planning_threads.push_back(std::move(planning_thread));
141  }
142 
143  // Wait for threads to finish
144  for (auto& planning_thread : planning_threads)
145  {
146  if (planning_thread.joinable())
147  {
148  planning_thread.join();
149  }
150  }
151 
152  // If a solution selection function is provided, it is used to compute the return value
153  if (solution_selection_function)
154  {
155  std::vector<::planning_interface::MotionPlanResponse> solutions;
156  solutions.reserve(1);
157  solutions.push_back(solution_selection_function(plan_responses_container.getSolutions()));
158  return solutions;
159  }
160 
161  // Otherwise, just return the unordered list of solutions
162  return plan_responses_container.getSolutions();
163 }
164 
165 std::unordered_map<std::string, planning_pipeline::PlanningPipelinePtr>
166 createPlanningPipelineMap(const std::vector<std::string>& pipeline_names,
167  const moveit::core::RobotModelConstPtr& robot_model, const rclcpp::Node::SharedPtr& node,
168  const std::string& parameter_namespace)
169 {
170  std::unordered_map<std::string, planning_pipeline::PlanningPipelinePtr> planning_pipelines;
171  for (const auto& planning_pipeline_name : pipeline_names)
172  {
173  // Check if pipeline already exists
174  if (planning_pipelines.count(planning_pipeline_name) > 0)
175  {
176  RCLCPP_WARN(getLogger(), "Skipping duplicate entry for planning pipeline '%s'.", planning_pipeline_name.c_str());
177  continue;
178  }
179 
180  // Create planning pipeline
181  planning_pipeline::PlanningPipelinePtr pipeline = std::make_shared<planning_pipeline::PlanningPipeline>(
182  robot_model, node, parameter_namespace + planning_pipeline_name);
183 
184  if (!pipeline)
185  {
186  RCLCPP_ERROR(getLogger(), "Failed to initialize planning pipeline '%s'.", planning_pipeline_name.c_str());
187  continue;
188  }
189 
190  planning_pipelines[planning_pipeline_name] = pipeline;
191  }
192 
193  return planning_pipelines;
194 }
195 
196 } // namespace planning_pipeline_interfaces
197 } // namespace moveit
A container to thread-safely store multiple MotionPlanResponses.
::planning_interface::MotionPlanResponse planWithSinglePipeline(const ::planning_interface::MotionPlanRequest &motion_plan_request, const ::planning_scene::PlanningSceneConstPtr &planning_scene, const std::unordered_map< std::string, planning_pipeline::PlanningPipelinePtr > &planning_pipelines)
Function to calculate the MotionPlanResponse for a given MotionPlanRequest and a PlanningScene.
std::unordered_map< std::string, planning_pipeline::PlanningPipelinePtr > createPlanningPipelineMap(const std::vector< std::string > &pipeline_names, const moveit::core::RobotModelConstPtr &robot_model, const rclcpp::Node::SharedPtr &node, const std::string &parameter_namespace=std::string())
Utility function to create a map of named planning pipelines.
std::function< bool(const PlanResponsesContainer &plan_responses_container, const std::vector<::planning_interface::MotionPlanRequest > &plan_requests)> StoppingCriterionFunction
A stopping criterion callback function for the parallel planning API of planning component.
const std::vector<::planning_interface::MotionPlanResponse > planWithParallelPipelines(const std::vector<::planning_interface::MotionPlanRequest > &motion_plan_requests, const ::planning_scene::PlanningSceneConstPtr &planning_scene, const std::unordered_map< std::string, planning_pipeline::PlanningPipelinePtr > &planning_pipelines, const StoppingCriterionFunction &stopping_criterion_callback=nullptr, const SolutionSelectionFunction &solution_selection_function=nullptr)
Function to solve multiple planning problems in parallel threads with multiple planning pipelines at ...
std::function<::planning_interface::MotionPlanResponse(const std::vector<::planning_interface::MotionPlanResponse > &solutions)> SolutionSelectionFunction
A solution callback function type for the parallel planning API of planning component.
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