moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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>
40
41namespace
42{
43namespace
44{
45rclcpp::Logger getLogger()
46{
47 return moveit::getLogger("moveit.ros.planning_pipeline");
48}
49} // namespace
50
57[[nodiscard]] std::vector<moveit_msgs::msg::Constraints>
58getTrajectoryConstraints(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
82namespace planning_pipeline
83{
84PlanningPipeline::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
98PlanningPipeline::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
115void 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
237void 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
251bool 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....
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.