OptimalPlanning.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2011, Rice University
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 the Rice University 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: Luis G. Torres, Jonathan Gammell */
36 
37 #include <ompl/base/SpaceInformation.h>
38 #include <ompl/base/objectives/PathLengthOptimizationObjective.h>
39 #include <ompl/base/objectives/StateCostIntegralObjective.h>
40 #include <ompl/base/objectives/MaximizeMinClearanceObjective.h>
41 #include <ompl/base/spaces/RealVectorStateSpace.h>
42 // For ompl::msg::setLogLevel
43 #include "ompl/util/Console.h"
44 
45 // The supported optimal planners, in alphabetical order
46 #include <ompl/geometric/planners/bitstar/BITstar.h>
47 #include <ompl/geometric/planners/cforest/CForest.h>
48 #include <ompl/geometric/planners/fmt/FMT.h>
49 #include <ompl/geometric/planners/fmt/BFMT.h>
50 #include <ompl/geometric/planners/rrt/InformedRRTstar.h>
51 #include <ompl/geometric/planners/prm/PRMstar.h>
52 #include <ompl/geometric/planners/rrt/RRTstar.h>
53 
54 
55 // For boost program options
56 #include <boost/program_options.hpp>
57 // For string comparison (boost::iequals)
58 #include <boost/algorithm/string.hpp>
59 // For std::make_shared
60 #include <memory>
61 
62 #include <fstream>
63 
64 
65 namespace ob = ompl::base;
66 namespace og = ompl::geometric;
68 
69 // An enum of supported optimal planners, alphabetical order
70 enum optimalPlanner
71 {
72  PLANNER_BITSTAR,
73  PLANNER_CFOREST,
74  PLANNER_FMTSTAR,
75  PLANNER_BFMTSTAR,
76  PLANNER_INF_RRTSTAR,
77  PLANNER_PRMSTAR,
78  PLANNER_RRTSTAR
79 };
80 
81 // An enum of the supported optimization objectives, alphabetical order
82 enum planningObjective
83 {
84  OBJECTIVE_PATHCLEARANCE,
85  OBJECTIVE_PATHLENGTH,
86  OBJECTIVE_THRESHOLDPATHLENGTH,
87  OBJECTIVE_WEIGHTEDCOMBO
88 };
89 
90 // Parse the command-line arguments
91 bool argParse(int argc, char** argv, double *runTime, optimalPlanner *plannerPtr, planningObjective *objectivePtr, std::string *outputFilePtr);
92 
93 // Our "collision checker". For this demo, our robot's state space
94 // lies in [0,1]x[0,1], with a circular obstacle of radius 0.25
95 // centered at (0.5,0.5). Any states lying in this circular region are
96 // considered "in collision".
97 class ValidityChecker : public ob::StateValidityChecker
98 {
99 public:
100  ValidityChecker(const ob::SpaceInformationPtr& si) :
101  ob::StateValidityChecker(si) {}
102 
103  // Returns whether the given state's position overlaps the
104  // circular obstacle
105  bool isValid(const ob::State* state) const
106  {
107  return this->clearance(state) > 0.0;
108  }
109 
110  // Returns the distance from the given state's position to the
111  // boundary of the circular obstacle.
112  double clearance(const ob::State* state) const
113  {
114  // We know we're working with a RealVectorStateSpace in this
115  // example, so we downcast state into the specific type.
116  const ob::RealVectorStateSpace::StateType* state2D =
118 
119  // Extract the robot's (x,y) position from its state
120  double x = state2D->values[0];
121  double y = state2D->values[1];
122 
123  // Distance formula between two points, offset by the circle's
124  // radius
125  return sqrt((x-0.5)*(x-0.5) + (y-0.5)*(y-0.5)) - 0.25;
126  }
127 };
128 
129 ob::OptimizationObjectivePtr getPathLengthObjective(const ob::SpaceInformationPtr& si);
130 
131 ob::OptimizationObjectivePtr getThresholdPathLengthObj(const ob::SpaceInformationPtr& si);
132 
133 ob::OptimizationObjectivePtr getClearanceObjective(const ob::SpaceInformationPtr& si);
134 
135 ob::OptimizationObjectivePtr getBalancedObjective1(const ob::SpaceInformationPtr& si);
136 
137 ob::OptimizationObjectivePtr getBalancedObjective2(const ob::SpaceInformationPtr& si);
138 
139 ob::OptimizationObjectivePtr getPathLengthObjWithCostToGo(const ob::SpaceInformationPtr& si);
140 
141 ob::PlannerPtr allocatePlanner(ob::SpaceInformationPtr si, optimalPlanner plannerType)
142 {
143  switch (plannerType)
144  {
145  case PLANNER_BITSTAR:
146  {
147  return std::make_shared<og::BITstar>(si);
148  break;
149  }
150  case PLANNER_CFOREST:
151  {
152  return std::make_shared<og::CForest>(si);
153  break;
154  }
155  case PLANNER_FMTSTAR:
156  {
157  return std::make_shared<og::FMT>(si);
158  break;
159  }
160  case PLANNER_BFMTSTAR:
161  {
162  return std::make_shared<og::BFMT>(si);
163  break;
164  }
165  case PLANNER_INF_RRTSTAR:
166  {
167  return std::make_shared<og::InformedRRTstar>(si);
168  break;
169  }
170  case PLANNER_PRMSTAR:
171  {
172  return std::make_shared<og::PRMstar>(si);
173  break;
174  }
175  case PLANNER_RRTSTAR:
176  {
177  return std::make_shared<og::RRTstar>(si);
178  break;
179  }
180  default:
181  {
182  OMPL_ERROR("Planner-type enum is not implemented in allocation function.");
183  return ob::PlannerPtr(); // Address compiler warning re: no return value.
184  break;
185  }
186  }
187 }
188 
189 ob::OptimizationObjectivePtr allocateObjective(ob::SpaceInformationPtr si, planningObjective objectiveType)
190 {
191  switch (objectiveType)
192  {
193  case OBJECTIVE_PATHCLEARANCE:
194  return getClearanceObjective(si);
195  break;
196  case OBJECTIVE_PATHLENGTH:
197  return getPathLengthObjective(si);
198  break;
199  case OBJECTIVE_THRESHOLDPATHLENGTH:
200  return getThresholdPathLengthObj(si);
201  break;
202  case OBJECTIVE_WEIGHTEDCOMBO:
203  return getBalancedObjective1(si);
204  break;
205  default:
206  OMPL_ERROR("Optimization-objective enum is not implemented in allocation function.");
208  break;
209  }
210 }
211 
212 void plan(double runTime, optimalPlanner plannerType, planningObjective objectiveType, std::string outputFile)
213 {
214  // Construct the robot state space in which we're planning. We're
215  // planning in [0,1]x[0,1], a subset of R^2.
217 
218  // Set the bounds of space to be in [0,1].
219  space->as<ob::RealVectorStateSpace>()->setBounds(0.0, 1.0);
220 
221  // Construct a space information instance for this state space
223 
224  // Set the object used to check which states in the space are valid
225  si->setStateValidityChecker(ob::StateValidityCheckerPtr(new ValidityChecker(si)));
226 
227  si->setup();
228 
229  // Set our robot's starting state to be the bottom-left corner of
230  // the environment, or (0,0).
231  ob::ScopedState<> start(space);
232  start->as<ob::RealVectorStateSpace::StateType>()->values[0] = 0.0;
233  start->as<ob::RealVectorStateSpace::StateType>()->values[1] = 0.0;
234 
235  // Set our robot's goal state to be the top-right corner of the
236  // environment, or (1,1).
237  ob::ScopedState<> goal(space);
238  goal->as<ob::RealVectorStateSpace::StateType>()->values[0] = 1.0;
239  goal->as<ob::RealVectorStateSpace::StateType>()->values[1] = 1.0;
240 
241  // Create a problem instance
243 
244  // Set the start and goal states
245  pdef->setStartAndGoalStates(start, goal);
246 
247  // Create the optimization objective specified by our command-line argument.
248  // This helper function is simply a switch statement.
249  pdef->setOptimizationObjective(allocateObjective(si, objectiveType));
250 
251  // Construct the optimal planner specified by our command line argument.
252  // This helper function is simply a switch statement.
253  ob::PlannerPtr optimizingPlanner = allocatePlanner(si, plannerType);
254 
255  // Set the problem instance for our planner to solve
256  optimizingPlanner->setProblemDefinition(pdef);
257  optimizingPlanner->setup();
258 
259  // attempt to solve the planning problem in the given runtime
260  ob::PlannerStatus solved = optimizingPlanner->solve(runTime);
261 
262  if (solved)
263  {
264  // Output the length of the path found
265  std::cout
266  << optimizingPlanner->getName()
267  << " found a solution of length "
268  << pdef->getSolutionPath()->length()
269  << " with an optimization objective value of "
270  << pdef->getSolutionPath()->cost(pdef->getOptimizationObjective()) << std::endl;
271 
272  // If a filename was specified, output the path as a matrix to
273  // that file for visualization
274  if (!outputFile.empty())
275  {
276  std::ofstream outFile(outputFile.c_str());
277  std::static_pointer_cast<og::PathGeometric>(pdef->getSolutionPath())->
278  printAsMatrix(outFile);
279  outFile.close();
280  }
281  }
282  else
283  std::cout << "No solution found." << std::endl;
284 }
285 
286 int main(int argc, char** argv)
287 {
288  // The parsed arguments
289  double runTime;
290  optimalPlanner plannerType;
291  planningObjective objectiveType;
292  std::string outputFile;
293 
294  // Parse the arguments, returns true if successful, false otherwise
295  if (argParse(argc, argv, &runTime, &plannerType, &objectiveType, &outputFile))
296  {
297  // Plan
298  plan(runTime, plannerType, objectiveType, outputFile);
299 
300  // Return with success
301  return 0;
302  }
303  else
304  {
305  // Return with error
306  return -1;
307  }
308 }
309 
314 ob::OptimizationObjectivePtr getPathLengthObjective(const ob::SpaceInformationPtr& si)
315 {
317 }
318 
322 ob::OptimizationObjectivePtr getThresholdPathLengthObj(const ob::SpaceInformationPtr& si)
323 {
325  obj->setCostThreshold(ob::Cost(1.51));
326  return obj;
327 }
328 
341 class ClearanceObjective : public ob::StateCostIntegralObjective
342 {
343 public:
344  ClearanceObjective(const ob::SpaceInformationPtr& si) :
345  ob::StateCostIntegralObjective(si, true)
346  {
347  }
348 
349  // Our requirement is to maximize path clearance from obstacles,
350  // but we want to represent the objective as a path cost
351  // minimization. Therefore, we set each state's cost to be the
352  // reciprocal of its clearance, so that as state clearance
353  // increases, the state cost decreases.
354  ob::Cost stateCost(const ob::State* s) const
355  {
356  return ob::Cost(1 / si_->getStateValidityChecker()->clearance(s));
357  }
358 };
359 
362 ob::OptimizationObjectivePtr getClearanceObjective(const ob::SpaceInformationPtr& si)
363 {
364  return ob::OptimizationObjectivePtr(new ClearanceObjective(si));
365 }
366 
379 ob::OptimizationObjectivePtr getBalancedObjective1(const ob::SpaceInformationPtr& si)
380 {
382  ob::OptimizationObjectivePtr clearObj(new ClearanceObjective(si));
383 
385  opt->addObjective(lengthObj, 10.0);
386  opt->addObjective(clearObj, 1.0);
387 
388  return ob::OptimizationObjectivePtr(opt);
389 }
390 
394 ob::OptimizationObjectivePtr getBalancedObjective2(const ob::SpaceInformationPtr& si)
395 {
397  ob::OptimizationObjectivePtr clearObj(new ClearanceObjective(si));
398 
399  return 10.0*lengthObj + clearObj;
400 }
401 
405 ob::OptimizationObjectivePtr getPathLengthObjWithCostToGo(const ob::SpaceInformationPtr& si)
406 {
408  obj->setCostToGoHeuristic(&ob::goalRegionCostToGo);
409  return obj;
410 }
411 
413 bool argParse(int argc, char** argv, double* runTimePtr, optimalPlanner *plannerPtr, planningObjective *objectivePtr, std::string *outputFilePtr)
414 {
415  namespace bpo = boost::program_options;
416 
417  // Declare the supported options.
418  bpo::options_description desc("Allowed options");
419  desc.add_options()
420  ("help,h", "produce help message")
421  ("runtime,t", bpo::value<double>()->default_value(1.0), "(Optional) Specify the runtime in seconds. Defaults to 1 and must be greater than 0.")
422  ("planner,p", bpo::value<std::string>()->default_value("RRTstar"), "(Optional) Specify the optimal planner to use, defaults to RRTstar if not given. Valid options are BITstar, CForest, FMTstar, BFMTstar, InformedRRTstar, PRMstar, and RRTstar.") //Alphabetical order
423  ("objective,o", bpo::value<std::string>()->default_value("PathLength"), "(Optional) Specify the optimization objective, defaults to PathLength if not given. Valid options are PathClearance, PathLength, ThresholdPathLength, and WeightedLengthAndClearanceCombo.") //Alphabetical order
424  ("file,f", bpo::value<std::string>()->default_value(""), "(Optional) Specify an output path for the found solution path.")
425  ("info,i", bpo::value<unsigned int>()->default_value(0u), "(Optional) Set the OMPL log level. 0 for WARN, 1 for INFO, 2 for DEBUG. Defaults to WARN.");
426  bpo::variables_map vm;
427  bpo::store(bpo::parse_command_line(argc, argv, desc), vm);
428  bpo::notify(vm);
429 
430  // Check if the help flag has been given:
431  if (vm.count("help"))
432  {
433  std::cout << desc << std::endl;
434  return false;
435  }
436 
437  // Set the log-level
438  unsigned int logLevel = vm["info"].as<unsigned int>();
439 
440  // Switch to setting the log level:
441  if (logLevel == 0u)
442  {
443  ompl::msg::setLogLevel(ompl::msg::LOG_WARN);
444  }
445  else if (logLevel == 1u)
446  {
447  ompl::msg::setLogLevel(ompl::msg::LOG_INFO);
448  }
449  else if (logLevel == 2u)
450  {
451  ompl::msg::setLogLevel(ompl::msg::LOG_DEBUG);
452  }
453  else
454  {
455  std::cout << "Invalid log-level integer." << std::endl << std::endl << desc << std::endl;
456  return false;
457  }
458 
459  // Get the runtime as a double
460  *runTimePtr = vm["runtime"].as<double>();
461 
462  // Sanity check
463  if (*runTimePtr <= 0.0)
464  {
465  std::cout << "Invalid runtime." << std::endl << std::endl << desc << std::endl;
466  return false;
467  }
468 
469  // Get the specified planner as a string
470  std::string plannerStr = vm["planner"].as<std::string>();
471 
472  // Map the string to the enum
473  if (boost::iequals("BITstar", plannerStr))
474  {
475  *plannerPtr = PLANNER_BITSTAR;
476  }
477  else if (boost::iequals("CForest", plannerStr))
478  {
479  *plannerPtr = PLANNER_CFOREST;
480  }
481  else if (boost::iequals("FMTstar", plannerStr))
482  {
483  *plannerPtr = PLANNER_FMTSTAR;
484  }
485  else if (boost::iequals("BFMTstar", plannerStr))
486  {
487  *plannerPtr = PLANNER_BFMTSTAR;
488  }
489  else if (boost::iequals("InformedRRTstar", plannerStr))
490  {
491  *plannerPtr = PLANNER_INF_RRTSTAR;
492  }
493  else if (boost::iequals("PRMstar", plannerStr))
494  {
495  *plannerPtr = PLANNER_PRMSTAR;
496  }
497  else if (boost::iequals("RRTstar", plannerStr))
498  {
499  *plannerPtr = PLANNER_RRTSTAR;
500  }
501  else
502  {
503  std::cout << "Invalid planner string." << std::endl << std::endl << desc << std::endl;
504  return false;
505  }
506 
507  // Get the specified objective as a string
508  std::string objectiveStr = vm["objective"].as<std::string>();
509 
510  // Map the string to the enum
511  if (boost::iequals("PathClearance", objectiveStr))
512  {
513  *objectivePtr = OBJECTIVE_PATHCLEARANCE;
514  }
515  else if (boost::iequals("PathLength", objectiveStr))
516  {
517  *objectivePtr = OBJECTIVE_PATHLENGTH;
518  }
519  else if (boost::iequals("ThresholdPathLength", objectiveStr))
520  {
521  *objectivePtr = OBJECTIVE_THRESHOLDPATHLENGTH;
522  }
523  else if (boost::iequals("WeightedLengthAndClearanceCombo", objectiveStr))
524  {
525  *objectivePtr = OBJECTIVE_WEIGHTEDCOMBO;
526  }
527  else
528  {
529  std::cout << "Invalid objective string." << std::endl << std::endl << desc << std::endl;
530  return false;
531  }
532 
533  // Get the output file string and store it in the return pointer
534  *outputFilePtr = vm["file"].as<std::string>();
535 
536  // Looks like we parsed the arguments successfully
537  return true;
538 }
virtual Cost stateCost(const State *s) const
Returns a cost with a value of 1.
A shared pointer wrapper for ompl::base::ProblemDefinition.
This class allows for the definition of multiobjective optimal planning problems. Objectives are adde...
Definition of a scoped state.
Definition: ScopedState.h:56
A shared pointer wrapper for ompl::base::StateSpace.
void addObjective(const OptimizationObjectivePtr &objective, double weight)
Adds a new objective for this multiobjective. A weight must also be specified for specifying importan...
virtual double clearance(const State *) const
Report the distance to the nearest invalid state when starting from state. If the distance is negativ...
State StateType
Define the type of state allocated by this space.
Definition: StateSpace.h:80
A shared pointer wrapper for ompl::base::StateValidityChecker.
A shared pointer wrapper for ompl::base::Planner.
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
Defines optimization objectives where path cost can be represented as a path integral over a cost fun...
void setLogLevel(LogLevel level)
Set the minimum level of logging data to output. Messages with lower logging levels will not be recor...
Definition: Console.cpp:136
Abstract definition for a class checking the validity of states. The implementation of this class mus...
A class to store the exit status of Planner::solve()
Definition: PlannerStatus.h:48
A shared pointer wrapper for ompl::base::SpaceInformation.
A state space representing Rn. The distance function is the L2 norm.
The base class for space information. This contains all the information about the space planning is d...
An optimization objective which corresponds to optimizing path length.
Definition of an abstract state.
Definition: State.h:50
virtual bool isValid(const State *state) const =0
Return true if the state state is valid. Usually, this means at least collision checking. If it is possible that ompl::base::StateSpace::interpolate() or ompl::control::ControlSpace::propagate() return states that are outside of bounds, this function should also make a call to ompl::base::SpaceInformation::satisfiesBounds().
Definition of a problem to be solved. This includes the start state(s) for the system and a goal spec...
A shared pointer wrapper for ompl::base::OptimizationObjective.
Definition of a geometric path.
Definition: PathGeometric.h:60
const T * as() const
Cast this instance to a desired type.
Definition: State.h:74
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:47