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 */
36 
42 #include <moveit_msgs/DisplayTrajectory.h>
43 #include <visualization_msgs/MarkerArray.h>
44 #include <boost/tokenizer.hpp>
45 #include <boost/algorithm/string/join.hpp>
46 #include <sstream>
47 
48 const std::string planning_pipeline::PlanningPipeline::DISPLAY_PATH_TOPIC = "display_planned_path";
49 const std::string planning_pipeline::PlanningPipeline::MOTION_PLAN_REQUEST_TOPIC = "motion_plan_request";
50 const std::string planning_pipeline::PlanningPipeline::MOTION_CONTACTS_TOPIC = "display_contacts";
51 
52 planning_pipeline::PlanningPipeline::PlanningPipeline(const moveit::core::RobotModelConstPtr& model,
53  const ros::NodeHandle& pipeline_nh,
54  const std::string& planner_plugin_param_name,
55  const std::string& adapter_plugins_param_name)
56  : active_{ false }, pipeline_nh_(pipeline_nh), private_nh_("~"), robot_model_(model)
57 {
58  std::string planner;
59  if (pipeline_nh_.getParam(planner_plugin_param_name, planner))
60  planner_plugin_name_ = planner;
61 
62  std::string adapters;
63  if (pipeline_nh_.getParam(adapter_plugins_param_name, adapters))
64  {
65  boost::char_separator<char> sep(" ");
66  boost::tokenizer<boost::char_separator<char>> tok(adapters, sep);
67  for (boost::tokenizer<boost::char_separator<char>>::iterator beg = tok.begin(); beg != tok.end(); ++beg)
68  adapter_plugin_names_.push_back(*beg);
69  }
70 
71  configure();
72 }
73 
74 planning_pipeline::PlanningPipeline::PlanningPipeline(const moveit::core::RobotModelConstPtr& model,
75  const ros::NodeHandle& pipeline_nh,
76  const std::string& planner_plugin_name,
77  const std::vector<std::string>& adapter_plugin_names)
78  : active_{ false }
79  , pipeline_nh_(pipeline_nh)
80  , private_nh_("~")
81  , planner_plugin_name_(planner_plugin_name)
82  , adapter_plugin_names_(adapter_plugin_names)
83  , robot_model_(model)
84 {
85  configure();
86 }
87 
89 {
90  check_solution_paths_ = false; // this is set to true below
91  publish_received_requests_ = false;
92  display_computed_motion_plans_ = false; // this is set to true below
93 
94  // load the planning plugin
95  try
96  {
97  planner_plugin_loader_ = std::make_unique<pluginlib::ClassLoader<planning_interface::PlannerManager>>(
98  "moveit_core", "planning_interface::PlannerManager");
99  }
101  {
102  ROS_FATAL_STREAM("Exception while creating planning plugin loader " << ex.what());
103  }
104 
105  std::vector<std::string> classes;
106  if (planner_plugin_loader_)
107  classes = planner_plugin_loader_->getDeclaredClasses();
108  if (planner_plugin_name_.empty() && classes.size() == 1)
109  {
110  planner_plugin_name_ = classes[0];
111  ROS_INFO("No '~planning_plugin' parameter specified, but only '%s' planning plugin is available. Using that one.",
112  planner_plugin_name_.c_str());
113  }
114  if (planner_plugin_name_.empty() && classes.size() > 1)
115  {
116  planner_plugin_name_ = classes[0];
117  ROS_INFO("Multiple planning plugins available. You should specify the '~planning_plugin' parameter. Using '%s' for "
118  "now.",
119  planner_plugin_name_.c_str());
120  }
121  try
122  {
123  planner_instance_ = planner_plugin_loader_->createUniqueInstance(planner_plugin_name_);
124  if (!planner_instance_->initialize(robot_model_, pipeline_nh_.getNamespace()))
125  throw std::runtime_error("Unable to initialize planning plugin");
126  ROS_INFO_STREAM("Using planning interface '" << planner_instance_->getDescription() << "'");
127  }
129  {
130  ROS_ERROR_STREAM("Exception while loading planner '"
131  << planner_plugin_name_ << "': " << ex.what() << std::endl
132  << "Available plugins: " << boost::algorithm::join(classes, ", "));
133  }
134 
135  // load the planner request adapters
136  if (!adapter_plugin_names_.empty())
137  {
138  std::vector<planning_request_adapter::PlanningRequestAdapterConstPtr> ads;
139  try
140  {
141  adapter_plugin_loader_ =
142  std::make_unique<pluginlib::ClassLoader<planning_request_adapter::PlanningRequestAdapter>>(
143  "moveit_core", "planning_request_adapter::PlanningRequestAdapter");
144  }
146  {
147  ROS_ERROR_STREAM("Exception while creating planning plugin loader " << ex.what());
148  }
149 
150  if (adapter_plugin_loader_)
151  for (const std::string& adapter_plugin_name : adapter_plugin_names_)
152  {
153  planning_request_adapter::PlanningRequestAdapterPtr ad;
154  try
155  {
156  ad = adapter_plugin_loader_->createUniqueInstance(adapter_plugin_name);
157  }
159  {
160  ROS_ERROR_STREAM("Exception while loading planning adapter plugin '" << adapter_plugin_name
161  << "': " << ex.what());
162  }
163  if (ad)
164  {
165  ad->initialize(pipeline_nh_);
166  ads.push_back(std::move(ad));
167  }
168  }
169  if (!ads.empty())
170  {
171  adapter_chain_ = std::make_unique<planning_request_adapter::PlanningRequestAdapterChain>();
172  for (planning_request_adapter::PlanningRequestAdapterConstPtr& ad : ads)
173  {
174  ROS_INFO_STREAM("Using planning request adapter '" << ad->getDescription() << "'");
175  adapter_chain_->addAdapter(ad);
176  }
177  }
178  }
179  displayComputedMotionPlans(true);
180  checkSolutionPaths(true);
181 }
182 
184 {
185  if (display_computed_motion_plans_ && !flag)
186  display_path_publisher_.shutdown();
187  else if (!display_computed_motion_plans_ && flag)
188  display_path_publisher_ = private_nh_.advertise<moveit_msgs::DisplayTrajectory>(DISPLAY_PATH_TOPIC, 10, true);
189  display_computed_motion_plans_ = flag;
190 }
191 
193 {
194  if (publish_received_requests_ && !flag)
195  received_request_publisher_.shutdown();
196  else if (!publish_received_requests_ && flag)
197  received_request_publisher_ =
198  private_nh_.advertise<moveit_msgs::MotionPlanRequest>(MOTION_PLAN_REQUEST_TOPIC, 10, true);
199  publish_received_requests_ = flag;
200 }
201 
203 {
204  if (check_solution_paths_ && !flag)
205  contacts_publisher_.shutdown();
206  else if (!check_solution_paths_ && flag)
207  contacts_publisher_ = private_nh_.advertise<visualization_msgs::MarkerArray>(MOTION_CONTACTS_TOPIC, 100, true);
208  check_solution_paths_ = flag;
209 }
210 
211 bool planning_pipeline::PlanningPipeline::generatePlan(const planning_scene::PlanningSceneConstPtr& planning_scene,
214 {
215  std::vector<std::size_t> dummy;
216  return generatePlan(planning_scene, req, res, dummy);
217 }
218 
219 bool planning_pipeline::PlanningPipeline::generatePlan(const planning_scene::PlanningSceneConstPtr& planning_scene,
222  std::vector<std::size_t>& adapter_added_state_index) const
223 {
224  // Set planning pipeline active
225  active_ = true;
226 
227  // broadcast the request we are about to work on, if needed
228  if (publish_received_requests_)
229  received_request_publisher_.publish(request);
230  adapter_added_state_index.clear();
231 
232  if (!planner_instance_)
233  {
234  ROS_ERROR("No planning plugin loaded. Cannot plan.");
235  // Set planning pipeline to inactive
236  active_ = false;
237  return false;
238  }
239 
240  // resolve constraint frames
242  kinematic_constraints::resolveConstraintFrames(planning_scene->getCurrentState(), req.path_constraints);
243  for (moveit_msgs::Constraints& constraint : req.goal_constraints)
244  kinematic_constraints::resolveConstraintFrames(planning_scene->getCurrentState(), constraint);
245 
246  bool solved = false;
247  try
248  {
249  if (adapter_chain_)
250  {
251  solved = adapter_chain_->adaptAndPlan(planner_instance_, planning_scene, req, res, adapter_added_state_index);
252  if (!adapter_added_state_index.empty())
253  {
254  std::stringstream ss;
255  for (std::size_t added_index : adapter_added_state_index)
256  ss << added_index << " ";
257  ROS_INFO("Planning adapters have added states at index positions: [ %s]", ss.str().c_str());
258  }
259  }
260  else
261  {
262  planning_interface::PlanningContextPtr context =
263  planner_instance_->getPlanningContext(planning_scene, req, res.error_code_);
264  solved = context ? context->solve(res) : false;
265  }
266  }
267  catch (std::exception& ex)
268  {
269  ROS_ERROR("Exception caught: '%s'", ex.what());
270  // Set planning pipeline to inactive
271  active_ = false;
272  return false;
273  }
274  bool valid = true;
275 
276  if (solved && res.trajectory_)
277  {
278  std::size_t state_count = res.trajectory_->getWayPointCount();
279  ROS_DEBUG_STREAM("Motion planner reported a solution path with " << state_count << " states");
280  if (check_solution_paths_)
281  {
282  visualization_msgs::MarkerArray arr;
283  visualization_msgs::Marker m;
284  m.action = visualization_msgs::Marker::DELETEALL;
285  arr.markers.push_back(m);
286 
287  std::vector<std::size_t> index;
288  if (!planning_scene->isPathValid(*res.trajectory_, req.path_constraints, req.group_name, false, &index))
289  {
290  // check to see if there is any problem with the states that are found to be invalid
291  // they are considered ok if they were added by a planning request adapter
292  bool problem = false;
293  for (std::size_t i = 0; i < index.size() && !problem; ++i)
294  {
295  bool found = false;
296  for (std::size_t added_index : adapter_added_state_index)
297  if (index[i] == added_index)
298  {
299  found = true;
300  break;
301  }
302  if (!found)
303  problem = true;
304  }
305  if (problem)
306  {
307  if (index.size() == 1 && index[0] == 0) // ignore cases when the robot starts at invalid location
308  ROS_DEBUG("It appears the robot is starting at an invalid state, but that is ok.");
309  else
310  {
311  valid = false;
312  res.error_code_.val = moveit_msgs::MoveItErrorCodes::INVALID_MOTION_PLAN;
313 
314  // display error messages
315  std::stringstream ss;
316  for (std::size_t it : index)
317  ss << it << " ";
318  ROS_ERROR_STREAM("Computed path is not valid. Invalid states at index locations: [ "
319  << ss.str() << "] out of " << state_count
320  << ". Explanations follow in command line. Contacts are published on "
321  << private_nh_.resolveName(MOTION_CONTACTS_TOPIC));
322 
323  // call validity checks in verbose mode for the problematic states
324  for (std::size_t it : index)
325  {
326  // check validity with verbose on
327  const moveit::core::RobotState& robot_state = res.trajectory_->getWayPoint(it);
328  planning_scene->isStateValid(robot_state, req.path_constraints, req.group_name, true);
329 
330  // compute the contacts if any
333  c_req.contacts = true;
334  c_req.max_contacts = 10;
335  c_req.max_contacts_per_pair = 3;
336  c_req.verbose = false;
337  planning_scene->checkCollision(c_req, c_res, robot_state);
338  if (c_res.contact_count > 0)
339  {
340  visualization_msgs::MarkerArray arr_i;
342  c_res.contacts);
343  arr.markers.insert(arr.markers.end(), arr_i.markers.begin(), arr_i.markers.end());
344  }
345  }
346  ROS_ERROR_STREAM("Completed listing of explanations for invalid states.");
347  }
348  }
349  else
350  ROS_DEBUG("Planned path was found to be valid, except for states that were added by planning request "
351  "adapters, but that is ok.");
352  }
353  else
354  ROS_DEBUG("Planned path was found to be valid when rechecked");
355  contacts_publisher_.publish(arr);
356  }
357  }
358 
359  // display solution path if needed
360  if (display_computed_motion_plans_ && solved)
361  {
362  moveit_msgs::DisplayTrajectory disp;
363  disp.model_id = robot_model_->getName();
364  disp.trajectory.resize(1);
365  res.trajectory_->getRobotTrajectoryMsg(disp.trajectory[0]);
366  moveit::core::robotStateToRobotStateMsg(res.trajectory_->getFirstWayPoint(), disp.trajectory_start);
367  display_path_publisher_.publish(disp);
368  }
369 
370  if (!solved)
371  {
372  // This should alert the user if planning failed because of contradicting constraints.
373  // Could be checked more thoroughly, but it is probably not worth going to that length.
374  bool stacked_constraints = false;
375  if (req.path_constraints.position_constraints.size() > 1 || req.path_constraints.orientation_constraints.size() > 1)
376  stacked_constraints = true;
377  for (const auto& constraint : req.goal_constraints)
378  {
379  if (constraint.position_constraints.size() > 1 || constraint.orientation_constraints.size() > 1)
380  stacked_constraints = true;
381  }
382  if (stacked_constraints)
383  ROS_WARN("More than one constraint is set. If your move_group does not have multiple end effectors/arms, this is "
384  "unusual. Are you using a move_group_interface and forgetting to call clearPoseTargets() or "
385  "equivalent?");
386  }
387  // Set planning pipeline to inactive
388  active_ = false;
389  return solved && valid;
390 }
391 
393 {
394  if (planner_instance_)
395  planner_instance_->terminate();
396 }
ROS_ERROR_STREAM
#define ROS_ERROR_STREAM(args)
collision_tools.h
planning_pipeline::PlanningPipeline::DISPLAY_PATH_TOPIC
static const std::string DISPLAY_PATH_TOPIC
When motion plans are computed and they are supposed to be automatically displayed,...
Definition: planning_pipeline.h:125
planning_interface::MotionPlanResponse
utils.h
ROS_FATAL_STREAM
#define ROS_FATAL_STREAM(args)
kinematic_constraints::resolveConstraintFrames
bool resolveConstraintFrames(const moveit::core::RobotState &state, moveit_msgs::Constraints &constraints)
planning_pipeline::PlanningPipeline::terminate
void terminate() const
Request termination, if a generatePlan() function is currently computing plans.
Definition: planning_pipeline.cpp:392
planning_interface::MotionPlanResponse::error_code_
moveit::core::MoveItErrorCode error_code_
collision_detection::getCollisionMarkersFromContacts
void getCollisionMarkersFromContacts(visualization_msgs::MarkerArray &arr, const std::string &frame_id, const CollisionResult::ContactMap &con)
ROS_DEBUG
#define ROS_DEBUG(...)
planning_pipeline::PlanningPipeline::MOTION_CONTACTS_TOPIC
static const std::string MOTION_CONTACTS_TOPIC
When contacts are found in the solution path reported by a planner, they can be published as markers ...
Definition: planning_pipeline.h:133
moveit::core::RobotState
planning_pipeline::PlanningPipeline::MOTION_PLAN_REQUEST_TOPIC
static const std::string MOTION_PLAN_REQUEST_TOPIC
When motion planning requests are received and they are supposed to be automatically published,...
Definition: planning_pipeline.h:129
ROS_DEBUG_STREAM
#define ROS_DEBUG_STREAM(args)
planning_pipeline::PlanningPipeline::generatePlan
bool generatePlan(const planning_scene::PlanningSceneConstPtr &planning_scene, const planning_interface::MotionPlanRequest &req, planning_interface::MotionPlanResponse &res) const
Call the motion planner plugin and the sequence of planning request adapters (if any).
Definition: planning_pipeline.cpp:211
collision_detection::CollisionRequest
pluginlib::PluginlibException
planning_pipeline::PlanningPipeline::checkSolutionPaths
void checkSolutionPaths(bool flag)
Pass a flag telling the pipeline whether or not to re-check the solution paths reported by the planne...
Definition: planning_pipeline.cpp:202
planning_pipeline::PlanningPipeline::PlanningPipeline
PlanningPipeline(const moveit::core::RobotModelConstPtr &model, const ros::NodeHandle &pipeline_nh=ros::NodeHandle("~"), const std::string &planning_plugin_param_name="planning_plugin", const std::string &adapter_plugins_param_name="request_adapters")
Given a robot model (model), a node handle (pipeline_nh), initialize the planning pipeline.
Definition: planning_pipeline.cpp:52
planning_pipeline::PlanningPipeline::configure
void configure()
Definition: planning_pipeline.cpp:88
planning_interface::MotionPlanResponse::trajectory_
robot_trajectory::RobotTrajectoryPtr trajectory_
planning_pipeline::PlanningPipeline::displayComputedMotionPlans
void displayComputedMotionPlans(bool flag)
Pass a flag telling the pipeline whether or not to publish the computed motion plans on DISPLAY_PATH_...
Definition: planning_pipeline.cpp:183
planning_pipeline::PlanningPipeline::publishReceivedRequests
void publishReceivedRequests(bool flag)
Pass a flag telling the pipeline whether or not to publish the received motion planning requests on M...
Definition: planning_pipeline.cpp:192
collision_detection::CollisionResult
collision_detection::CollisionRequest::max_contacts
std::size_t max_contacts
collision_detection::CollisionRequest::verbose
bool verbose
ROS_ERROR
#define ROS_ERROR(...)
ROS_WARN
#define ROS_WARN(...)
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
planning_interface::MotionPlanRequest
moveit_msgs::MotionPlanRequest MotionPlanRequest
collision_detection::CollisionResult::contacts
ContactMap contacts
planning_pipeline.h
collision_detection::CollisionRequest::contacts
bool contacts
index
unsigned int index
conversions.h
collision_detection::CollisionRequest::max_contacts_per_pair
std::size_t max_contacts_per_pair
trajectory_tools.h
ROS_INFO
#define ROS_INFO(...)
planning_scene
collision_detection::CollisionResult::contact_count
std::size_t contact_count
moveit::core::robotStateToRobotStateMsg
void robotStateToRobotStateMsg(const RobotState &state, moveit_msgs::RobotState &robot_state, bool copy_attached_bodies=true)
ros::NodeHandle


planning
Author(s): Ioan Sucan , Sachin Chitta
autogenerated on Thu Nov 21 2024 03:24:18