model_based_planning_context.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 
37 #include <boost/algorithm/string/trim.hpp>
38 #include <boost/algorithm/string/split.hpp>
39 
47 
51 
52 #include <ompl/config.h>
53 #include <ompl/base/samplers/UniformValidStateSampler.h>
54 #include <ompl/base/goals/GoalLazySamples.h>
55 #include <ompl/tools/config/SelfConfig.h>
56 #include <ompl/base/spaces/SE3StateSpace.h>
57 #include <ompl/datastructures/PDF.h>
58 // TODO: remove when ROS Melodic and older are no longer supported
59 #if OMPL_VERSION_VALUE < 1005000
60 #include <ompl/base/PlannerTerminationCondition.h>
61 #else
62 // IterationTerminationCondition was moved to a separate file and
63 // CostConvergenceTerminationCondition was added in OMPL 1.5.0.
64 #include <ompl/base/terminationconditions/IterationTerminationCondition.h>
65 #include <ompl/base/terminationconditions/CostConvergenceTerminationCondition.h>
66 #endif
67 
68 #include "ompl/base/objectives/PathLengthOptimizationObjective.h"
69 #include "ompl/base/objectives/MechanicalWorkOptimizationObjective.h"
70 #include "ompl/base/objectives/MinimaxObjective.h"
71 #include "ompl/base/objectives/StateCostIntegralObjective.h"
72 #include "ompl/base/objectives/MaximizeMinClearanceObjective.h"
73 #include <ompl/geometric/planners/prm/LazyPRM.h>
74 
75 namespace ompl_interface
76 {
77 constexpr char LOGNAME[] = "model_based_planning_context";
78 } // namespace ompl_interface
79 
82  : planning_interface::PlanningContext(name, spec.state_space_->getJointModelGroup()->getName())
83  , spec_(spec)
84  , complete_initial_robot_state_(spec.state_space_->getRobotModel())
85  , ompl_simple_setup_(spec.ompl_simple_setup_)
86  , ompl_benchmark_(*ompl_simple_setup_)
87  , ompl_parallel_plan_(ompl_simple_setup_->getProblemDefinition())
88  , ptc_(nullptr)
89  , last_plan_time_(0.0)
90  , last_simplify_time_(0.0)
91  , max_goal_samples_(0)
92  , max_state_sampling_attempts_(0)
93  , max_goal_sampling_attempts_(0)
94  , max_planning_threads_(0)
95  , max_solution_segment_length_(0.0)
96  , minimum_waypoint_count_(0)
97  , multi_query_planning_enabled_(false) // maintain "old" behavior by default
98  , simplify_solutions_(true)
99  , interpolate_(true)
100  , hybridize_(true)
101 {
103 
104  constraints_library_ = std::make_shared<ConstraintsLibrary>(this);
105 }
106 
107 void ompl_interface::ModelBasedPlanningContext::configure(const ros::NodeHandle& nh, bool use_constraints_approximations)
108 {
109  loadConstraintApproximations(nh);
110  if (!use_constraints_approximations)
111  {
112  setConstraintsApproximations(ConstraintsLibraryPtr());
113  }
114  complete_initial_robot_state_.update();
115  ompl_simple_setup_->getStateSpace()->computeSignature(space_signature_);
116  ompl_simple_setup_->getStateSpace()->setStateSamplerAllocator(
117  [this](const ompl::base::StateSpace* ss) { return allocPathConstrainedSampler(ss); });
118 
119  // convert the input state to the corresponding OMPL state
120  ompl::base::ScopedState<> ompl_start_state(spec_.state_space_);
121  spec_.state_space_->copyToOMPLState(ompl_start_state.get(), getCompleteInitialRobotState());
122  ompl_simple_setup_->setStartState(ompl_start_state);
123  ompl_simple_setup_->setStateValidityChecker(ob::StateValidityCheckerPtr(new StateValidityChecker(this)));
124 
125  if (path_constraints_ && constraints_library_)
126  {
127  const ConstraintApproximationPtr& constraint_approx =
128  constraints_library_->getConstraintApproximation(path_constraints_msg_);
129  if (constraint_approx)
130  {
131  getOMPLStateSpace()->setInterpolationFunction(constraint_approx->getInterpolationFunction());
132  ROS_INFO_NAMED(LOGNAME, "Using precomputed interpolation states");
133  }
134  }
135 
136  useConfig();
137  if (ompl_simple_setup_->getGoal())
138  ompl_simple_setup_->setup();
139 }
140 
142 {
143  if (!spec_.state_space_)
144  {
145  ROS_ERROR_NAMED(LOGNAME, "No state space is configured yet");
146  return;
147  }
148  ob::ProjectionEvaluatorPtr projection_eval = getProjectionEvaluator(peval);
149  if (projection_eval)
150  spec_.state_space_->registerDefaultProjection(projection_eval);
151 }
152 
153 ompl::base::ProjectionEvaluatorPtr
155 {
156  if (peval.find_first_of("link(") == 0 && peval[peval.length() - 1] == ')')
157  {
158  std::string link_name = peval.substr(5, peval.length() - 6);
159  if (getRobotModel()->hasLinkModel(link_name))
160  return ob::ProjectionEvaluatorPtr(new ProjectionEvaluatorLinkPose(this, link_name));
161  else
163  "Attempted to set projection evaluator with respect to position of link '%s', "
164  "but that link is not known to the kinematic model.",
165  link_name.c_str());
166  }
167  else if (peval.find_first_of("joints(") == 0 && peval[peval.length() - 1] == ')')
168  {
169  std::string joints = peval.substr(7, peval.length() - 8);
170  boost::replace_all(joints, ",", " ");
171  std::vector<unsigned int> j;
172  std::stringstream ss(joints);
173  while (ss.good() && !ss.eof())
174  {
175  std::string joint;
176  ss >> joint >> std::ws;
177  if (getJointModelGroup()->hasJointModel(joint))
178  {
179  unsigned int variable_count = getJointModelGroup()->getJointModel(joint)->getVariableCount();
180  if (variable_count > 0)
181  {
182  int idx = getJointModelGroup()->getVariableGroupIndex(joint);
183  for (unsigned int q = 0; q < variable_count; ++q)
184  j.push_back(idx + q);
185  }
186  else
187  ROS_WARN_NAMED(LOGNAME, "%s: Ignoring joint '%s' in projection since it has 0 DOF", name_.c_str(),
188  joint.c_str());
189  }
190  else
192  "%s: Attempted to set projection evaluator with respect to value of joint "
193  "'%s', but that joint is not known to the group '%s'.",
194  name_.c_str(), joint.c_str(), getGroupName().c_str());
195  }
196  if (j.empty())
197  ROS_ERROR_NAMED(LOGNAME, "%s: No valid joints specified for joint projection", name_.c_str());
198  else
199  return ob::ProjectionEvaluatorPtr(new ProjectionEvaluatorJointValue(this, j));
200  }
201  else
202  ROS_ERROR_NAMED(LOGNAME, "Unable to allocate projection evaluator based on description: '%s'", peval.c_str());
203  return ob::ProjectionEvaluatorPtr();
204 }
205 
206 ompl::base::StateSamplerPtr
207 ompl_interface::ModelBasedPlanningContext::allocPathConstrainedSampler(const ompl::base::StateSpace* state_space) const
208 {
209  if (spec_.state_space_.get() != state_space)
210  {
211  ROS_ERROR_NAMED(LOGNAME, "%s: Attempted to allocate a state sampler for an unknown state space", name_.c_str());
212  return ompl::base::StateSamplerPtr();
213  }
214 
215  ROS_DEBUG_NAMED(LOGNAME, "%s: Allocating a new state sampler (attempts to use path constraints)", name_.c_str());
216 
217  if (path_constraints_)
218  {
219  if (constraints_library_)
220  {
221  const ConstraintApproximationPtr& constraint_approx =
222  constraints_library_->getConstraintApproximation(path_constraints_msg_);
223  if (constraint_approx)
224  {
225  ompl::base::StateSamplerAllocator state_sampler_allocator =
226  constraint_approx->getStateSamplerAllocator(path_constraints_msg_);
227  if (state_sampler_allocator)
228  {
229  ompl::base::StateSamplerPtr state_sampler = state_sampler_allocator(state_space);
230  if (state_sampler)
231  {
233  "%s: Using precomputed state sampler (approximated constraint space) for constraint '%s'",
234  name_.c_str(), path_constraints_msg_.name.c_str());
235  return state_sampler;
236  }
237  }
238  }
239  }
240 
241  constraint_samplers::ConstraintSamplerPtr constraint_sampler;
242  if (spec_.constraint_sampler_manager_)
243  constraint_sampler = spec_.constraint_sampler_manager_->selectSampler(getPlanningScene(), getGroupName(),
244  path_constraints_->getAllConstraints());
245 
246  if (constraint_sampler)
247  {
248  ROS_INFO_NAMED(LOGNAME, "%s: Allocating specialized state sampler for state space", name_.c_str());
249  return ob::StateSamplerPtr(new ConstrainedSampler(this, constraint_sampler));
250  }
251  }
252  ROS_DEBUG_NAMED(LOGNAME, "%s: Allocating default state sampler for state space", name_.c_str());
253  return state_space->allocDefaultStateSampler();
254 }
255 
257 {
258  const std::map<std::string, std::string>& config = spec_.config_;
259  if (config.empty())
260  return;
261  std::map<std::string, std::string> cfg = config;
262 
263  // set the distance between waypoints when interpolating and collision checking.
264  auto it = cfg.find("longest_valid_segment_fraction");
265  // If one of the two variables is set.
266  if (it != cfg.end() || max_solution_segment_length_ != 0.0)
267  {
268  // clang-format off
269  double longest_valid_segment_fraction_config = (it != cfg.end())
270  ? moveit::core::toDouble(it->second) // value from config file if there
271  : 0.01; // default value in OMPL.
272  double longest_valid_segment_fraction_final = longest_valid_segment_fraction_config;
273  if (max_solution_segment_length_ > 0.0)
274  {
275  // If this parameter is specified too, take the most conservative of the two variables,
276  // i.e. the one that uses the shorter segment length.
277  longest_valid_segment_fraction_final = std::min(
278  longest_valid_segment_fraction_config,
279  max_solution_segment_length_ / spec_.state_space_->getMaximumExtent()
280  );
281  }
282  // clang-format on
283 
284  // convert to string using no locale
285  cfg["longest_valid_segment_fraction"] = moveit::core::toString(longest_valid_segment_fraction_final);
286  }
287 
288  // set the projection evaluator
289  it = cfg.find("projection_evaluator");
290  if (it != cfg.end())
291  {
292  setProjectionEvaluator(boost::trim_copy(it->second));
293  cfg.erase(it);
294  }
295 
296  if (cfg.empty())
297  return;
298 
299  std::string optimizer;
300  ompl::base::OptimizationObjectivePtr objective;
301  it = cfg.find("optimization_objective");
302  if (it != cfg.end())
303  {
304  optimizer = it->second;
305  cfg.erase(it);
306 
307  if (optimizer == "PathLengthOptimizationObjective")
308  {
309  objective =
310  std::make_shared<ompl::base::PathLengthOptimizationObjective>(ompl_simple_setup_->getSpaceInformation());
311  }
312  else if (optimizer == "MinimaxObjective")
313  {
314  objective = std::make_shared<ompl::base::MinimaxObjective>(ompl_simple_setup_->getSpaceInformation());
315  }
316  else if (optimizer == "StateCostIntegralObjective")
317  {
318  objective = std::make_shared<ompl::base::StateCostIntegralObjective>(ompl_simple_setup_->getSpaceInformation());
319  }
320  else if (optimizer == "MechanicalWorkOptimizationObjective")
321  {
322  objective =
323  std::make_shared<ompl::base::MechanicalWorkOptimizationObjective>(ompl_simple_setup_->getSpaceInformation());
324  }
325  else if (optimizer == "MaximizeMinClearanceObjective")
326  {
327  objective =
328  std::make_shared<ompl::base::MaximizeMinClearanceObjective>(ompl_simple_setup_->getSpaceInformation());
329  }
330  else
331  {
332  objective =
333  std::make_shared<ompl::base::PathLengthOptimizationObjective>(ompl_simple_setup_->getSpaceInformation());
334  }
335 
336  ompl_simple_setup_->setOptimizationObjective(objective);
337  }
338 
339  // Don't clear planner data if multi-query planning is enabled
340  it = cfg.find("multi_query_planning_enabled");
341  if (it != cfg.end())
342  multi_query_planning_enabled_ = boost::lexical_cast<bool>(it->second);
343 
344  // check whether the path returned by the planner should be interpolated
345  it = cfg.find("interpolate");
346  if (it != cfg.end())
347  {
348  interpolate_ = boost::lexical_cast<bool>(it->second);
349  cfg.erase(it);
350  }
351 
352  // check whether solution paths from parallel planning should be hybridized
353  it = cfg.find("hybridize");
354  if (it != cfg.end())
355  {
356  hybridize_ = boost::lexical_cast<bool>(it->second);
357  cfg.erase(it);
358  }
359 
360  // remove the 'type' parameter; the rest are parameters for the planner itself
361  it = cfg.find("type");
362  if (it == cfg.end())
363  {
364  if (name_ != getGroupName())
365  ROS_WARN_NAMED(LOGNAME, "%s: Attribute 'type' not specified in planner configuration", name_.c_str());
366  }
367  else
368  {
369  std::string type = it->second;
370  cfg.erase(it);
371  const std::string planner_name = getGroupName() + "/" + name_;
372  ompl_simple_setup_->setPlannerAllocator(
373  [planner_name, &spec = this->spec_, allocator = spec_.planner_selector_(type)](
374  const ompl::base::SpaceInformationPtr& si) { return allocator(si, planner_name, spec); });
376  "Planner configuration '%s' will use planner '%s'. "
377  "Additional configuration parameters will be set when the planner is constructed.",
378  name_.c_str(), type.c_str());
379  }
380 
381  // call the setParams() after setup(), so we know what the params are
382  ompl_simple_setup_->getSpaceInformation()->setup();
383  ompl_simple_setup_->getSpaceInformation()->params().setParams(cfg, true);
384  // call setup() again for possibly new param values
385  ompl_simple_setup_->getSpaceInformation()->setup();
386 }
387 
388 void ompl_interface::ModelBasedPlanningContext::setPlanningVolume(const moveit_msgs::WorkspaceParameters& wparams)
389 {
390  if (wparams.min_corner.x == wparams.max_corner.x && wparams.min_corner.x == 0.0 &&
391  wparams.min_corner.y == wparams.max_corner.y && wparams.min_corner.y == 0.0 &&
392  wparams.min_corner.z == wparams.max_corner.z && wparams.min_corner.z == 0.0)
393  ROS_WARN_NAMED(LOGNAME, "It looks like the planning volume was not specified.");
394 
396  "%s: Setting planning volume (affects SE2 & SE3 joints only) to x = [%f, %f], y = "
397  "[%f, %f], z = [%f, %f]",
398  name_.c_str(), wparams.min_corner.x, wparams.max_corner.x, wparams.min_corner.y, wparams.max_corner.y,
399  wparams.min_corner.z, wparams.max_corner.z);
400 
401  spec_.state_space_->setPlanningVolume(wparams.min_corner.x, wparams.max_corner.x, wparams.min_corner.y,
402  wparams.max_corner.y, wparams.min_corner.z, wparams.max_corner.z);
403 }
404 
406 {
407  ompl::time::point start = ompl::time::now();
408  ob::PlannerTerminationCondition ptc = constructPlannerTerminationCondition(timeout, start);
409  registerTerminationCondition(ptc);
410  ompl_simple_setup_->simplifySolution(ptc);
411  last_simplify_time_ = ompl_simple_setup_->getLastSimplificationTime();
412  unregisterTerminationCondition();
413 }
414 
416 {
417  if (ompl_simple_setup_->haveSolutionPath())
418  {
419  og::PathGeometric& pg = ompl_simple_setup_->getSolutionPath();
420 
421  // Find the number of states that will be in the interpolated solution.
422  // This is what interpolate() does internally.
423  unsigned int eventual_states = 1;
424  std::vector<ompl::base::State*> states = pg.getStates();
425  for (size_t i = 0; i < states.size() - 1; i++)
426  {
427  eventual_states += ompl_simple_setup_->getStateSpace()->validSegmentCount(states[i], states[i + 1]);
428  }
429 
430  if (eventual_states < minimum_waypoint_count_)
431  {
432  // If that's not enough states, use the minimum amount instead.
433  pg.interpolate(minimum_waypoint_count_);
434  }
435  else
436  {
437  // Interpolate the path to have as the exact states that are checked when validating motions.
438  pg.interpolate();
439  }
440  }
441 }
442 
443 void ompl_interface::ModelBasedPlanningContext::convertPath(const ompl::geometric::PathGeometric& pg,
445 {
446  moveit::core::RobotState ks = complete_initial_robot_state_;
447  for (std::size_t i = 0; i < pg.getStateCount(); ++i)
448  {
449  spec_.state_space_->copyToRobotState(ks, pg.getState(i));
450  traj.addSuffixWayPoint(ks, 0.0);
451  }
452 }
453 
455 {
456  traj.clear();
457  if (ompl_simple_setup_->haveSolutionPath())
458  convertPath(ompl_simple_setup_->getSolutionPath(), traj);
459  return ompl_simple_setup_->haveSolutionPath();
460 }
461 
463 {
464  if (ompl_simple_setup_->getStateValidityChecker())
465  static_cast<StateValidityChecker*>(ompl_simple_setup_->getStateValidityChecker().get())->setVerbose(flag);
466 }
467 
469 {
470  // ******************* set up the goal representation, based on goal constraints
471 
472  std::vector<ob::GoalPtr> goals;
473  for (kinematic_constraints::KinematicConstraintSetPtr& goal_constraint : goal_constraints_)
474  {
475  constraint_samplers::ConstraintSamplerPtr constraint_sampler;
476  if (spec_.constraint_sampler_manager_)
477  constraint_sampler = spec_.constraint_sampler_manager_->selectSampler(getPlanningScene(), getGroupName(),
478  goal_constraint->getAllConstraints());
479  if (constraint_sampler)
480  {
481  ob::GoalPtr goal = ob::GoalPtr(new ConstrainedGoalSampler(this, goal_constraint, constraint_sampler));
482  goals.push_back(goal);
483  }
484  }
485 
486  if (!goals.empty())
487  return goals.size() == 1 ? goals[0] : ompl::base::GoalPtr(new GoalSampleableRegionMux(goals));
488  else
489  ROS_ERROR_NAMED(LOGNAME, "Unable to construct goal representation");
490 
491  return ob::GoalPtr();
492 }
493 
494 ompl::base::PlannerTerminationCondition
496  const ompl::time::point& start)
497 {
498  auto it = spec_.config_.find("termination_condition");
499  if (it == spec_.config_.end())
500  return ob::timedPlannerTerminationCondition(timeout - ompl::time::seconds(ompl::time::now() - start));
501  std::string termination_string = it->second;
502  std::vector<std::string> termination_and_params;
503  boost::split(termination_and_params, termination_string, boost::is_any_of("[ ,]"));
504 
505  if (termination_and_params.empty())
506  ROS_ERROR_NAMED(LOGNAME, "Termination condition not specified");
507  // Terminate if a maximum number of iterations is exceeded or a timeout occurs.
508  // The semantics of "iterations" are planner-specific, but typically it corresponds to the number of times
509  // an attempt was made to grow a roadmap/tree.
510  else if (termination_and_params[0] == "Iteration")
511  {
512  if (termination_and_params.size() > 1)
513  return ob::plannerOrTerminationCondition(
514  ob::timedPlannerTerminationCondition(timeout - ompl::time::seconds(ompl::time::now() - start)),
515  ob::IterationTerminationCondition(std::stoul(termination_and_params[1])));
516  else
517  ROS_ERROR_NAMED(LOGNAME, "Missing argument to Iteration termination condition");
518  }
519 // TODO: remove when ROS Melodic and older are no longer supported
520 #if OMPL_VERSION_VALUE >= 1005000
521  // Terminate if the cost has converged or a timeout occurs.
522  // Only useful for anytime/optimizing planners.
523  else if (termination_and_params[0] == "CostConvergence")
524  {
525  std::size_t solutions_window = 10u;
526  double epsilon = 0.1;
527  if (termination_and_params.size() > 1)
528  {
529  solutions_window = std::stoul(termination_and_params[1]);
530  if (termination_and_params.size() > 2)
531  epsilon = moveit::core::toDouble(termination_and_params[2]);
532  }
533  return ob::plannerOrTerminationCondition(
534  ob::timedPlannerTerminationCondition(timeout - ompl::time::seconds(ompl::time::now() - start)),
535  ob::CostConvergenceTerminationCondition(ompl_simple_setup_->getProblemDefinition(), solutions_window, epsilon));
536  }
537 #endif
538  // Terminate as soon as an exact solution is found or a timeout occurs.
539  // This modifies the behavior of anytime/optimizing planners to terminate upon discovering
540  // the first feasible solution.
541  else if (termination_and_params[0] == "ExactSolution")
542  {
543  return ob::plannerOrTerminationCondition(
544  ob::timedPlannerTerminationCondition(timeout - ompl::time::seconds(ompl::time::now() - start)),
545  ob::exactSolnPlannerTerminationCondition(ompl_simple_setup_->getProblemDefinition()));
546  }
547  else
548  ROS_ERROR_NAMED(LOGNAME, "Unknown planner termination condition");
549 
550  // return a planner termination condition to suppress compiler warning
551  return ob::plannerAlwaysTerminatingCondition();
552 }
553 
555  const moveit::core::RobotState& complete_initial_robot_state)
556 {
557  complete_initial_robot_state_ = complete_initial_robot_state;
558  complete_initial_robot_state_.update();
559 }
560 
562 {
563  if (!multi_query_planning_enabled_)
564  ompl_simple_setup_->clear();
565 // TODO: remove when ROS Melodic and older are no longer supported
566 #if OMPL_VERSION_VALUE >= 1005000
567  else
568  {
569  // For LazyPRM and LazyPRMstar we assume that the environment *could* have changed
570  // This means that we need to reset the validity flags for every node and edge in
571  // the roadmap. For PRM and PRMstar we assume that the environment is static. If
572  // this is not the case, then multi-query planning should not be enabled.
573  auto planner = dynamic_cast<ompl::geometric::LazyPRM*>(ompl_simple_setup_->getPlanner().get());
574  if (planner != nullptr)
575  planner->clearValidity();
576  }
577 #endif
578  ompl_simple_setup_->clearStartStates();
579  ompl_simple_setup_->setGoal(ob::GoalPtr());
580  ompl_simple_setup_->setStateValidityChecker(ob::StateValidityCheckerPtr());
581  path_constraints_.reset();
582  goal_constraints_.clear();
583  getOMPLStateSpace()->setInterpolationFunction(InterpolationFunction());
584 }
585 
586 bool ompl_interface::ModelBasedPlanningContext::setPathConstraints(const moveit_msgs::Constraints& path_constraints,
587  moveit_msgs::MoveItErrorCodes* /*error*/)
588 {
589  // ******************* set the path constraints to use
590  path_constraints_ = std::make_shared<kinematic_constraints::KinematicConstraintSet>(getRobotModel());
591  path_constraints_->add(path_constraints, getPlanningScene()->getTransforms());
592  path_constraints_msg_ = path_constraints;
593 
594  return true;
595 }
596 
598  const std::vector<moveit_msgs::Constraints>& goal_constraints, const moveit_msgs::Constraints& path_constraints,
599  moveit_msgs::MoveItErrorCodes* error)
600 {
601  // ******************* check if the input is correct
602  goal_constraints_.clear();
603  for (const moveit_msgs::Constraints& goal_constraint : goal_constraints)
604  {
605  moveit_msgs::Constraints constr = kinematic_constraints::mergeConstraints(goal_constraint, path_constraints);
606  kinematic_constraints::KinematicConstraintSetPtr kset(
607  new kinematic_constraints::KinematicConstraintSet(getRobotModel()));
608  kset->add(constr, getPlanningScene()->getTransforms());
609  if (!kset->empty())
610  goal_constraints_.push_back(kset);
611  }
612 
613  if (goal_constraints_.empty())
614  {
615  ROS_WARN_NAMED(LOGNAME, "%s: No goal constraints specified. There is no problem to solve.", name_.c_str());
616  if (error)
617  error->val = moveit_msgs::MoveItErrorCodes::INVALID_GOAL_CONSTRAINTS;
618  return false;
619  }
620 
621  ob::GoalPtr goal = constructGoal();
622  ompl_simple_setup_->setGoal(goal);
623  return static_cast<bool>(goal);
624 }
625 
626 bool ompl_interface::ModelBasedPlanningContext::benchmark(double timeout, unsigned int count,
627  const std::string& filename)
628 {
629  ompl_benchmark_.clearPlanners();
630  ompl_simple_setup_->setup();
631  ompl_benchmark_.addPlanner(ompl_simple_setup_->getPlanner());
632  ompl_benchmark_.setExperimentName(getRobotModel()->getName() + "_" + getGroupName() + "_" +
633  getPlanningScene()->getName() + "_" + name_);
634 
635  ot::Benchmark::Request req;
636  req.maxTime = timeout;
637  req.runCount = count;
638  req.displayProgress = true;
639  req.saveConsoleOutput = false;
640  ompl_benchmark_.benchmark(req);
641  return filename.empty() ? ompl_benchmark_.saveResultsToFile() : ompl_benchmark_.saveResultsToFile(filename.c_str());
642 }
643 
645 {
646  bool gls = ompl_simple_setup_->getGoal()->hasType(ob::GOAL_LAZY_SAMPLES);
647  if (gls)
648  static_cast<ob::GoalLazySamples*>(ompl_simple_setup_->getGoal().get())->startSampling();
649  else
650  // we know this is a GoalSampleableMux by elimination
651  static_cast<GoalSampleableRegionMux*>(ompl_simple_setup_->getGoal().get())->startSampling();
652 }
653 
655 {
656  bool gls = ompl_simple_setup_->getGoal()->hasType(ob::GOAL_LAZY_SAMPLES);
657  if (gls)
658  static_cast<ob::GoalLazySamples*>(ompl_simple_setup_->getGoal().get())->stopSampling();
659  else
660  // we know this is a GoalSampleableMux by elimination
661  static_cast<GoalSampleableRegionMux*>(ompl_simple_setup_->getGoal().get())->stopSampling();
662 }
663 
665 {
666  // clear previously computed solutions
667  ompl_simple_setup_->getProblemDefinition()->clearSolutionPaths();
668  const ob::PlannerPtr planner = ompl_simple_setup_->getPlanner();
669  if (planner && !multi_query_planning_enabled_)
670  planner->clear();
671  startSampling();
672  ompl_simple_setup_->getSpaceInformation()->getMotionValidator()->resetMotionCounter();
673 }
674 
676 {
677  stopSampling();
678  int v = ompl_simple_setup_->getSpaceInformation()->getMotionValidator()->getValidMotionCount();
679  int iv = ompl_simple_setup_->getSpaceInformation()->getMotionValidator()->getInvalidMotionCount();
680  ROS_DEBUG_NAMED(LOGNAME, "There were %d valid motions and %d invalid motions.", v, iv);
681 }
682 
684 {
685  res.error_code_ = solve(request_.allowed_planning_time, request_.num_planning_attempts);
686  if (res.error_code_.val == moveit_msgs::MoveItErrorCodes::SUCCESS)
687  {
688  double ptime = getLastPlanTime();
689  if (simplify_solutions_)
690  {
691  simplifySolution(request_.allowed_planning_time - ptime);
692  ptime += getLastSimplifyTime();
693  }
694 
695  if (interpolate_)
696  interpolateSolution();
697 
698  // fill the response
699  ROS_DEBUG_NAMED(LOGNAME, "%s: Returning successful solution with %lu states", getName().c_str(),
700  getOMPLSimpleSetup()->getSolutionPath().getStateCount());
701 
702  res.trajectory_ = std::make_shared<robot_trajectory::RobotTrajectory>(getRobotModel(), getGroupName());
703  getSolutionPath(*res.trajectory_);
704  res.planning_time_ = ptime;
705  return true;
706  }
707  else
708  {
709  ROS_INFO_NAMED(LOGNAME, "Unable to solve the planning problem");
710  return false;
711  }
712 }
713 
715 {
716  res.error_code_ = solve(request_.allowed_planning_time, request_.num_planning_attempts);
717  if (res.error_code_.val == moveit_msgs::MoveItErrorCodes::SUCCESS)
718  {
719  res.trajectory_.reserve(3);
720 
721  // add info about planned solution
722  double ptime = getLastPlanTime();
723  res.processing_time_.push_back(ptime);
724  res.description_.emplace_back("plan");
725  res.trajectory_.resize(res.trajectory_.size() + 1);
726  res.trajectory_.back() = std::make_shared<robot_trajectory::RobotTrajectory>(getRobotModel(), getGroupName());
727  getSolutionPath(*res.trajectory_.back());
728 
729  // simplify solution if time remains
730  if (simplify_solutions_)
731  {
732  simplifySolution(request_.allowed_planning_time - ptime);
733  res.processing_time_.push_back(getLastSimplifyTime());
734  res.description_.emplace_back("simplify");
735  res.trajectory_.resize(res.trajectory_.size() + 1);
736  res.trajectory_.back() = std::make_shared<robot_trajectory::RobotTrajectory>(getRobotModel(), getGroupName());
737  getSolutionPath(*res.trajectory_.back());
738  }
739 
740  if (interpolate_)
741  {
742  ompl::time::point start_interpolate = ompl::time::now();
743  interpolateSolution();
744  res.processing_time_.push_back(ompl::time::seconds(ompl::time::now() - start_interpolate));
745  res.description_.emplace_back("interpolate");
746  res.trajectory_.resize(res.trajectory_.size() + 1);
747  res.trajectory_.back() = std::make_shared<robot_trajectory::RobotTrajectory>(getRobotModel(), getGroupName());
748  getSolutionPath(*res.trajectory_.back());
749  }
750 
751  ROS_DEBUG_NAMED(LOGNAME, "%s: Returning successful solution with %lu states", getName().c_str(),
752  getOMPLSimpleSetup()->getSolutionPath().getStateCount());
753  return true;
754  }
755  else
756  {
757  ROS_INFO_NAMED(LOGNAME, "Unable to solve the planning problem");
758  return false;
759  }
760 }
761 
762 const moveit_msgs::MoveItErrorCodes ompl_interface::ModelBasedPlanningContext::solve(double timeout, unsigned int count)
763 {
764  moveit::tools::Profiler::ScopedBlock sblock("PlanningContext:Solve");
765  ompl::time::point start = ompl::time::now();
766  preSolve();
767 
768  moveit_msgs::MoveItErrorCodes result;
769  result.val = moveit_msgs::MoveItErrorCodes::FAILURE;
770  ob::PlannerTerminationCondition ptc = constructPlannerTerminationCondition(timeout, start);
771  registerTerminationCondition(ptc);
772  if (count <= 1 || multi_query_planning_enabled_) // multi-query planners should always run in single instances
773  {
774  ROS_DEBUG_NAMED(LOGNAME, "%s: Solving the planning problem once...", name_.c_str());
775  result.val = errorCode(ompl_simple_setup_->solve(ptc));
776  last_plan_time_ = ompl_simple_setup_->getLastPlanComputationTime();
777  }
778  else
779  {
780  ROS_DEBUG_NAMED(LOGNAME, "%s: Solving the planning problem %u times...", name_.c_str(), count);
781  ompl_parallel_plan_.clearHybridizationPaths();
782 
783  auto plan_parallel = [this, &ptc](unsigned int num_planners) {
784  ompl_parallel_plan_.clearPlanners();
785  if (ompl_simple_setup_->getPlannerAllocator())
786  for (unsigned int i = 0; i < num_planners; ++i)
787  ompl_parallel_plan_.addPlannerAllocator(ompl_simple_setup_->getPlannerAllocator());
788  else
789  for (unsigned int i = 0; i < num_planners; ++i)
790  ompl_parallel_plan_.addPlanner(ompl::tools::SelfConfig::getDefaultPlanner(ompl_simple_setup_->getGoal()));
791 
792  return errorCode(ompl_parallel_plan_.solve(ptc, 1, num_planners, hybridize_));
793  };
794 
795  if (count <= max_planning_threads_)
796  {
797  result.val = plan_parallel(count);
798  }
799  else
800  {
801  int n = count / max_planning_threads_;
802  for (int i = 0; i < n && result.val != moveit_msgs::MoveItErrorCodes::SUCCESS && !ptc(); ++i)
803  result.val = plan_parallel(max_planning_threads_);
804  if (result.val != moveit_msgs::MoveItErrorCodes::SUCCESS && !ptc())
805  result.val = plan_parallel(count % max_planning_threads_);
806  }
807  last_plan_time_ = ompl::time::seconds(ompl::time::now() - start);
808  }
809  unregisterTerminationCondition();
810  postSolve();
811  return result;
812 }
813 
814 void ompl_interface::ModelBasedPlanningContext::registerTerminationCondition(const ob::PlannerTerminationCondition& ptc)
815 {
816  std::unique_lock<std::mutex> slock(ptc_lock_);
817  ptc_ = &ptc;
818 }
819 
821 {
822  std::unique_lock<std::mutex> slock(ptc_lock_);
823  ptc_ = nullptr;
824 }
825 
826 int32_t ompl_interface::ModelBasedPlanningContext::errorCode(const ompl::base::PlannerStatus& status)
827 {
828  auto result = moveit_msgs::MoveItErrorCodes::PLANNING_FAILED;
829  switch (ompl::base::PlannerStatus::StatusType(status))
830  {
831  case ompl::base::PlannerStatus::UNKNOWN:
832  ROS_WARN_NAMED(LOGNAME, "Motion planning failed for an unknown reason");
833  result = moveit_msgs::MoveItErrorCodes::PLANNING_FAILED;
834  break;
835  case ompl::base::PlannerStatus::INVALID_START:
836  ROS_WARN_NAMED(LOGNAME, "Invalid start state");
837  result = moveit_msgs::MoveItErrorCodes::START_STATE_INVALID;
838  break;
839  case ompl::base::PlannerStatus::INVALID_GOAL:
840  ROS_WARN_NAMED(LOGNAME, "Invalid goal state");
841  result = moveit_msgs::MoveItErrorCodes::GOAL_STATE_INVALID;
842  break;
843  case ompl::base::PlannerStatus::UNRECOGNIZED_GOAL_TYPE:
844  ROS_WARN_NAMED(LOGNAME, "Unrecognized goal type");
845  result = moveit_msgs::MoveItErrorCodes::UNRECOGNIZED_GOAL_TYPE;
846  break;
847  case ompl::base::PlannerStatus::TIMEOUT:
848  ROS_WARN_NAMED(LOGNAME, "Timed out");
849  result = moveit_msgs::MoveItErrorCodes::TIMED_OUT;
850  break;
851  case ompl::base::PlannerStatus::APPROXIMATE_SOLUTION:
852  ROS_WARN_NAMED(LOGNAME, "Solution is approximate. This usually indicates a failure.");
853  result = moveit_msgs::MoveItErrorCodes::PLANNING_FAILED;
854  break;
855  case ompl::base::PlannerStatus::EXACT_SOLUTION:
856  result = moveit_msgs::MoveItErrorCodes::SUCCESS;
857  break;
858  case ompl::base::PlannerStatus::CRASH:
859  ROS_WARN_NAMED(LOGNAME, "OMPL crashed!");
860  result = moveit_msgs::MoveItErrorCodes::CRASH;
861  break;
862  case ompl::base::PlannerStatus::ABORT:
863  ROS_WARN_NAMED(LOGNAME, "OMPL was aborted");
864  result = moveit_msgs::MoveItErrorCodes::ABORT;
865  break;
866  default:
867  // This should never happen
868  ROS_WARN_NAMED(LOGNAME, "Unexpected PlannerStatus code from OMPL.");
869  result = moveit_msgs::MoveItErrorCodes::PLANNING_FAILED;
870  }
871  return result;
872 }
873 
875 {
876  std::unique_lock<std::mutex> slock(ptc_lock_);
877  if (ptc_)
878  ptc_->terminate();
879  return true;
880 }
881 
883 {
884  std::string constraint_path;
885  if (nh.getParam("constraint_approximations_path", constraint_path))
886  {
887  constraints_library_->saveConstraintApproximations(constraint_path);
888  return true;
889  }
890  ROS_WARN_NAMED(LOGNAME, "ROS param 'constraint_approximations' not found. Unable to save constraint approximations");
891  return false;
892 }
893 
895 {
896  std::string constraint_path;
897  if (nh.getParam("constraint_approximations_path", constraint_path))
898  {
899  constraints_library_->loadConstraintApproximations(constraint_path);
900  std::stringstream ss;
901  constraints_library_->printConstraintApproximations(ss);
902  ROS_INFO_STREAM(ss.str());
903  return true;
904  }
905  return false;
906 }
ompl_interface::ModelBasedPlanningContext::terminate
bool terminate() override
Definition: model_based_planning_context.cpp:874
lexical_casts.h
epsilon
double epsilon
ompl_interface::ModelBasedPlanningContext::loadConstraintApproximations
bool loadConstraintApproximations(const ros::NodeHandle &nh)
Look up param server 'constraint_approximations' and use its value as the path to load constraint app...
Definition: model_based_planning_context.cpp:894
ompl_interface::ConstrainedSampler
Definition: constrained_sampler.h:79
model_based_planning_context.h
ompl_interface::ModelBasedPlanningContext::useConfig
virtual void useConfig()
Definition: model_based_planning_context.cpp:256
planning_interface::MotionPlanResponse
ompl_interface::ModelBasedPlanningContext::startSampling
void startSampling()
Definition: model_based_planning_context.cpp:644
utils.h
ros::NodeHandle::getParam
bool getParam(const std::string &key, bool &b) const
getName
ROSCONSOLE_CONSOLE_IMPL_DECL std::string getName(void *handle)
planning_interface::MotionPlanResponse::error_code_
moveit::core::MoveItErrorCode error_code_
projection_evaluators.h
ompl_interface::ModelBasedPlanningContext::getProjectionEvaluator
virtual ob::ProjectionEvaluatorPtr getProjectionEvaluator(const std::string &peval) const
Definition: model_based_planning_context.cpp:154
ompl_interface::ModelBasedPlanningContext::setPlanningVolume
void setPlanningVolume(const moveit_msgs::WorkspaceParameters &wparams)
Definition: model_based_planning_context.cpp:388
ompl_interface
The MoveIt interface to OMPL.
Definition: constrained_goal_sampler.h:46
ompl_interface::ModelBasedPlanningContext::stopSampling
void stopSampling()
Definition: model_based_planning_context.cpp:654
robot_trajectory::RobotTrajectory::addSuffixWayPoint
RobotTrajectory & addSuffixWayPoint(const moveit::core::RobotState &state, double dt)
ompl_interface::ProjectionEvaluatorJointValue
Definition: projection_evaluators.h:71
ROS_ERROR_NAMED
#define ROS_ERROR_NAMED(name,...)
moveit::core::RobotState
ompl_interface::ProjectionEvaluatorLinkPose
Definition: projection_evaluators.h:55
ompl_interface::ModelBasedPlanningContext::ModelBasedPlanningContext
ModelBasedPlanningContext(const std::string &name, const ModelBasedPlanningContextSpecification &spec)
Definition: model_based_planning_context.cpp:80
robot_trajectory::RobotTrajectory
constrained_goal_sampler.h
ompl_interface::ConstrainedGoalSampler
Definition: constrained_goal_sampler.h:83
ompl_interface::ModelBasedPlanningContext::setCompleteInitialState
void setCompleteInitialState(const moveit::core::RobotState &complete_initial_robot_state)
Definition: model_based_planning_context.cpp:554
ompl_interface::ModelBasedPlanningContext::convertPath
void convertPath(const og::PathGeometric &pg, robot_trajectory::RobotTrajectory &traj) const
Definition: model_based_planning_context.cpp:443
ompl_interface::ModelBasedPlanningContext::constructPlannerTerminationCondition
virtual ob::PlannerTerminationCondition constructPlannerTerminationCondition(double timeout, const ompl::time::point &start)
Definition: model_based_planning_context.cpp:495
ompl_interface::ModelBasedPlanningContext::simplifySolution
void simplifySolution(double timeout)
Definition: model_based_planning_context.cpp:405
moveit::core::RobotState::update
void update(bool force=false)
name
std::string name
planning_interface::MotionPlanResponse::trajectory_
robot_trajectory::RobotTrajectoryPtr trajectory_
ROS_INFO_NAMED
#define ROS_INFO_NAMED(name,...)
ompl_interface::ModelBasedPlanningContext::postSolve
void postSolve()
Definition: model_based_planning_context.cpp:675
ROS_DEBUG_NAMED
#define ROS_DEBUG_NAMED(name,...)
ompl_interface::StateValidityChecker
An interface for a OMPL state validity checker.
Definition: state_validity_checker.h:80
ompl_interface::LOGNAME
constexpr char LOGNAME[]
Definition: constrained_goal_sampler.cpp:78
constraints_library.h
state_validity_checker.h
robot_trajectory::RobotTrajectory::clear
RobotTrajectory & clear()
planning_interface::MotionPlanDetailedResponse::trajectory_
std::vector< robot_trajectory::RobotTrajectoryPtr > trajectory_
kinematic_constraints::mergeConstraints
moveit_msgs::Constraints mergeConstraints(const moveit_msgs::Constraints &first, const moveit_msgs::Constraints &second)
ompl_interface::ModelBasedPlanningContext::setGoalConstraints
bool setGoalConstraints(const std::vector< moveit_msgs::Constraints > &goal_constraints, const moveit_msgs::Constraints &path_constraints, moveit_msgs::MoveItErrorCodes *error)
Definition: model_based_planning_context.cpp:597
ompl_interface::ModelBasedPlanningContext::getSolutionPath
bool getSolutionPath(robot_trajectory::RobotTrajectory &traj) const
Definition: model_based_planning_context.cpp:454
ompl_interface::ModelBasedPlanningContext::setPathConstraints
bool setPathConstraints(const moveit_msgs::Constraints &path_constraints, moveit_msgs::MoveItErrorCodes *error)
Definition: model_based_planning_context.cpp:586
kinematic_constraints::KinematicConstraintSet
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
constrained_sampler.h
ompl_interface::ModelBasedPlanningContext::interpolateSolution
void interpolateSolution()
Definition: model_based_planning_context.cpp:415
ompl_interface::ModelBasedPlanningContext::benchmark
bool benchmark(double timeout, unsigned int count, const std::string &filename="")
Definition: model_based_planning_context.cpp:626
ompl_interface::GoalSampleableRegionMux
Definition: goal_union.h:74
ompl_interface::ModelBasedPlanningContextSpecification
Definition: model_based_planning_context.h:95
ompl_interface::ModelBasedPlanningContext::unregisterTerminationCondition
void unregisterTerminationCondition()
Definition: model_based_planning_context.cpp:820
start
ROSCPP_DECL void start()
ompl_interface::ModelBasedPlanningContext::setVerboseStateValidityChecks
void setVerboseStateValidityChecks(bool flag)
Definition: model_based_planning_context.cpp:462
ompl_interface::ModelBasedPlanningContext::preSolve
void preSolve()
Definition: model_based_planning_context.cpp:664
ompl_interface::ModelBasedPlanningContext::configure
virtual void configure(const ros::NodeHandle &nh, bool use_constraints_approximations)
Configure ompl_simple_setup_ and optionally the constraints_library_.
Definition: model_based_planning_context.cpp:107
planning_interface
ompl_interface::ModelBasedPlanningContext::registerTerminationCondition
void registerTerminationCondition(const ob::PlannerTerminationCondition &ptc)
Definition: model_based_planning_context.cpp:814
ompl_interface::ModelBasedPlanningContext::constraints_library_
ConstraintsLibraryPtr constraints_library_
Definition: model_based_planning_context.h:462
moveit::tools::Profiler::ScopedBlock
ROS_WARN_NAMED
#define ROS_WARN_NAMED(name,...)
planning_interface::MotionPlanDetailedResponse::error_code_
moveit::core::MoveItErrorCode error_code_
ompl_interface::InterpolationFunction
std::function< bool(const ompl::base::State *from, const ompl::base::State *to, const double t, ompl::base::State *state)> InterpolationFunction
Definition: model_based_state_space.h:81
ompl_interface::ModelBasedPlanningContext::saveConstraintApproximations
bool saveConstraintApproximations(const ros::NodeHandle &nh)
Look up param server 'constraint_approximations' and use its value as the path to save constraint app...
Definition: model_based_planning_context.cpp:882
ompl_interface::ModelBasedPlanningContext::errorCode
int32_t errorCode(const ompl::base::PlannerStatus &status)
Convert OMPL PlannerStatus to moveit_msgs::msg::MoveItErrorCode.
Definition: model_based_planning_context.cpp:826
profiler.h
goal_union.h
planning_interface::MotionPlanResponse::planning_time_
double planning_time_
moveit::core::toDouble
double toDouble(const std::string &s)
planning_interface::MotionPlanDetailedResponse::description_
std::vector< std::string > description_
moveit::core::toString
std::string toString(double d)
ompl_interface::ModelBasedPlanningContext::complete_initial_robot_state_
moveit::core::RobotState complete_initial_robot_state_
Definition: model_based_planning_context.h:411
ompl_interface::ModelBasedPlanningContext::allocPathConstrainedSampler
virtual ob::StateSamplerPtr allocPathConstrainedSampler(const ompl::base::StateSpace *ss) const
Definition: model_based_planning_context.cpp:207
ros::NodeHandle
planning_interface::MotionPlanDetailedResponse
ompl_interface::ModelBasedPlanningContext::constructGoal
virtual ob::GoalPtr constructGoal()
Definition: model_based_planning_context.cpp:468
ompl_interface::ModelBasedPlanningContext::solve
bool solve(planning_interface::MotionPlanResponse &res) override
Definition: model_based_planning_context.cpp:683
planning_interface::MotionPlanDetailedResponse::processing_time_
std::vector< double > processing_time_
ompl_interface::ModelBasedPlanningContext::clear
void clear() override
Definition: model_based_planning_context.cpp:561
ompl_interface::ModelBasedPlanningContext::setProjectionEvaluator
void setProjectionEvaluator(const std::string &peval)
Definition: model_based_planning_context.cpp:141


ompl
Author(s): Ioan Sucan
autogenerated on Fri May 3 2024 02:29:39