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/informedtrees/AITstar.h>
47 #include <ompl/geometric/planners/informedtrees/BITstar.h>
48 #include <ompl/geometric/planners/informedtrees/EITstar.h>
49 #include <ompl/geometric/planners/informedtrees/EIRMstar.h>
50 #include <ompl/geometric/planners/cforest/CForest.h>
51 #include <ompl/geometric/planners/fmt/FMT.h>
52 #include <ompl/geometric/planners/fmt/BFMT.h>
53 #include <ompl/geometric/planners/prm/PRMstar.h>
54 #include <ompl/geometric/planners/rrt/InformedRRTstar.h>
55 #include <ompl/geometric/planners/rrt/RRTstar.h>
56 #include <ompl/geometric/planners/rrt/SORRTstar.h>
57 
58 // For boost program options
59 #include <boost/program_options.hpp>
60 // For string comparison (boost::iequals)
61 #include <boost/algorithm/string.hpp>
62 // For std::make_shared
63 #include <memory>
64 
65 #include <fstream>
66 
67 namespace ob = ompl::base;
68 namespace og = ompl::geometric;
70 
71 // An enum of supported optimal planners, alphabetical order
72 enum optimalPlanner
73 {
74  PLANNER_AITSTAR,
75  PLANNER_BFMTSTAR,
76  PLANNER_BITSTAR,
77  PLANNER_CFOREST,
78  PLANNER_EITSTAR,
79  PLANNER_EIRMSTAR,
80  PLANNER_FMTSTAR,
81  PLANNER_INF_RRTSTAR,
82  PLANNER_PRMSTAR,
83  PLANNER_RRTSTAR,
84  PLANNER_SORRTSTAR,
85 };
86 
87 // An enum of the supported optimization objectives, alphabetical order
88 enum planningObjective
89 {
90  OBJECTIVE_PATHCLEARANCE,
91  OBJECTIVE_PATHLENGTH,
92  OBJECTIVE_THRESHOLDPATHLENGTH,
93  OBJECTIVE_WEIGHTEDCOMBO
94 };
95 
96 // Parse the command-line arguments
97 bool argParse(int argc, char **argv, double *runTimePtr, optimalPlanner *plannerPtr, planningObjective *objectivePtr,
98  std::string *outputFilePtr);
99 
100 // Our "collision checker". For this demo, our robot's state space
101 // lies in [0,1]x[0,1], with a circular obstacle of radius 0.25
102 // centered at (0.5,0.5). Any states lying in this circular region are
103 // considered "in collision".
104 class ValidityChecker : public ob::StateValidityChecker
105 {
106 public:
107  ValidityChecker(const ob::SpaceInformationPtr &si) : ob::StateValidityChecker(si)
108  {
109  }
110 
111  // Returns whether the given state's position overlaps the
112  // circular obstacle
113  bool isValid(const ob::State *state) const override
114  {
115  return this->clearance(state) > 0.0;
116  }
117 
118  // Returns the distance from the given state's position to the
119  // boundary of the circular obstacle.
120  double clearance(const ob::State *state) const override
121  {
122  // We know we're working with a RealVectorStateSpace in this
123  // example, so we downcast state into the specific type.
124  const auto *state2D = state->as<ob::RealVectorStateSpace::StateType>();
125 
126  // Extract the robot's (x,y) position from its state
127  double x = state2D->values[0];
128  double y = state2D->values[1];
129 
130  // Distance formula between two points, offset by the circle's
131  // radius
132  return sqrt((x - 0.5) * (x - 0.5) + (y - 0.5) * (y - 0.5)) - 0.25;
133  }
134 };
135 
136 ob::OptimizationObjectivePtr getPathLengthObjective(const ob::SpaceInformationPtr &si);
137 
138 ob::OptimizationObjectivePtr getThresholdPathLengthObj(const ob::SpaceInformationPtr &si);
139 
140 ob::OptimizationObjectivePtr getClearanceObjective(const ob::SpaceInformationPtr &si);
141 
142 ob::OptimizationObjectivePtr getBalancedObjective1(const ob::SpaceInformationPtr &si);
143 
144 ob::OptimizationObjectivePtr getBalancedObjective2(const ob::SpaceInformationPtr &si);
145 
146 ob::OptimizationObjectivePtr getPathLengthObjWithCostToGo(const ob::SpaceInformationPtr &si);
147 
148 ob::PlannerPtr allocatePlanner(ob::SpaceInformationPtr si, optimalPlanner plannerType)
149 {
150  switch (plannerType)
151  {
152  case PLANNER_AITSTAR:
153  {
154  return std::make_shared<og::AITstar>(si);
155  break;
156  }
157  case PLANNER_BFMTSTAR:
158  {
159  return std::make_shared<og::BFMT>(si);
160  break;
161  }
162  case PLANNER_BITSTAR:
163  {
164  return std::make_shared<og::BITstar>(si);
165  break;
166  }
167  case PLANNER_CFOREST:
168  {
169  return std::make_shared<og::CForest>(si);
170  break;
171  }
172  case PLANNER_EITSTAR:
173  {
174  return std::make_shared<og::EITstar>(si);
175  break;
176  }
177  case PLANNER_EIRMSTAR:
178  {
179  return std::make_shared<og::EIRMstar>(si);
180  break;
181  }
182  case PLANNER_FMTSTAR:
183  {
184  return std::make_shared<og::FMT>(si);
185  break;
186  }
187  case PLANNER_INF_RRTSTAR:
188  {
189  return std::make_shared<og::InformedRRTstar>(si);
190  break;
191  }
192  case PLANNER_PRMSTAR:
193  {
194  return std::make_shared<og::PRMstar>(si);
195  break;
196  }
197  case PLANNER_RRTSTAR:
198  {
199  return std::make_shared<og::RRTstar>(si);
200  break;
201  }
202  case PLANNER_SORRTSTAR:
203  {
204  return std::make_shared<og::SORRTstar>(si);
205  break;
206  }
207  default:
208  {
209  OMPL_ERROR("Planner-type enum is not implemented in allocation function.");
210  return ob::PlannerPtr(); // Address compiler warning re: no return value.
211  break;
212  }
213  }
214 }
215 
216 ob::OptimizationObjectivePtr allocateObjective(const ob::SpaceInformationPtr &si, planningObjective objectiveType)
217 {
218  switch (objectiveType)
219  {
220  case OBJECTIVE_PATHCLEARANCE:
221  return getClearanceObjective(si);
222  break;
223  case OBJECTIVE_PATHLENGTH:
224  return getPathLengthObjective(si);
225  break;
226  case OBJECTIVE_THRESHOLDPATHLENGTH:
227  return getThresholdPathLengthObj(si);
228  break;
229  case OBJECTIVE_WEIGHTEDCOMBO:
230  return getBalancedObjective1(si);
231  break;
232  default:
233  OMPL_ERROR("Optimization-objective enum is not implemented in allocation function.");
234  return ob::OptimizationObjectivePtr();
235  break;
236  }
237 }
238 
239 void plan(double runTime, optimalPlanner plannerType, planningObjective objectiveType, const std::string &outputFile)
240 {
241  // Construct the robot state space in which we're planning. We're
242  // planning in [0,1]x[0,1], a subset of R^2.
243  auto space(std::make_shared<ob::RealVectorStateSpace>(2));
244 
245  // Set the bounds of space to be in [0,1].
246  space->setBounds(0.0, 1.0);
247 
248  // Construct a space information instance for this state space
249  auto si(std::make_shared<ob::SpaceInformation>(space));
250 
251  // Set the object used to check which states in the space are valid
252  si->setStateValidityChecker(std::make_shared<ValidityChecker>(si));
253 
254  si->setup();
255 
256  // Set our robot's starting state to be the bottom-left corner of
257  // the environment, or (0,0).
258  ob::ScopedState<> start(space);
259  start->as<ob::RealVectorStateSpace::StateType>()->values[0] = 0.0;
260  start->as<ob::RealVectorStateSpace::StateType>()->values[1] = 0.0;
261 
262  // Set our robot's goal state to be the top-right corner of the
263  // environment, or (1,1).
264  ob::ScopedState<> goal(space);
265  goal->as<ob::RealVectorStateSpace::StateType>()->values[0] = 1.0;
266  goal->as<ob::RealVectorStateSpace::StateType>()->values[1] = 1.0;
267 
268  // Create a problem instance
269  auto pdef(std::make_shared<ob::ProblemDefinition>(si));
270 
271  // Set the start and goal states
272  pdef->setStartAndGoalStates(start, goal);
273 
274  // Create the optimization objective specified by our command-line argument.
275  // This helper function is simply a switch statement.
276  pdef->setOptimizationObjective(allocateObjective(si, objectiveType));
277 
278  // Construct the optimal planner specified by our command line argument.
279  // This helper function is simply a switch statement.
280  ob::PlannerPtr optimizingPlanner = allocatePlanner(si, plannerType);
281 
282  // Set the problem instance for our planner to solve
283  optimizingPlanner->setProblemDefinition(pdef);
284  optimizingPlanner->setup();
285 
286  // attempt to solve the planning problem in the given runtime
287  ob::PlannerStatus solved = optimizingPlanner->solve(runTime);
288 
289  if (solved)
290  {
291  // Output the length of the path found
292  std::cout << optimizingPlanner->getName() << " found a solution of length " << pdef->getSolutionPath()->length()
293  << " with an optimization objective value of "
294  << pdef->getSolutionPath()->cost(pdef->getOptimizationObjective()) << std::endl;
295 
296  // If a filename was specified, output the path as a matrix to
297  // that file for visualization
298  if (!outputFile.empty())
299  {
300  std::ofstream outFile(outputFile.c_str());
301  std::static_pointer_cast<og::PathGeometric>(pdef->getSolutionPath())->printAsMatrix(outFile);
302  outFile.close();
303  }
304  }
305  else
306  std::cout << "No solution found." << std::endl;
307 }
308 
309 int main(int argc, char **argv)
310 {
311  // The parsed arguments
312  double runTime;
313  optimalPlanner plannerType;
314  planningObjective objectiveType;
315  std::string outputFile;
316 
317  // Parse the arguments, returns true if successful, false otherwise
318  if (argParse(argc, argv, &runTime, &plannerType, &objectiveType, &outputFile))
319  {
320  // Plan
321  plan(runTime, plannerType, objectiveType, outputFile);
322 
323  // Return with success
324  return 0;
325  }
326  // Return with error
327  return -1;
328 }
329 
334 ob::OptimizationObjectivePtr getPathLengthObjective(const ob::SpaceInformationPtr &si)
335 {
336  return std::make_shared<ob::PathLengthOptimizationObjective>(si);
337 }
338 
342 ob::OptimizationObjectivePtr getThresholdPathLengthObj(const ob::SpaceInformationPtr &si)
343 {
344  auto obj(std::make_shared<ob::PathLengthOptimizationObjective>(si));
345  obj->setCostThreshold(ob::Cost(1.51));
346  return obj;
347 }
348 
361 class ClearanceObjective : public ob::StateCostIntegralObjective
362 {
363 public:
364  ClearanceObjective(const ob::SpaceInformationPtr &si) : ob::StateCostIntegralObjective(si, true)
365  {
366  }
367 
368  // Our requirement is to maximize path clearance from obstacles,
369  // but we want to represent the objective as a path cost
370  // minimization. Therefore, we set each state's cost to be the
371  // reciprocal of its clearance, so that as state clearance
372  // increases, the state cost decreases.
373  ob::Cost stateCost(const ob::State *s) const override
374  {
375  return ob::Cost(1 / (si_->getStateValidityChecker()->clearance(s) + std::numeric_limits<double>::min()));
376  }
377 };
378 
381 ob::OptimizationObjectivePtr getClearanceObjective(const ob::SpaceInformationPtr &si)
382 {
383  return std::make_shared<ClearanceObjective>(si);
384 }
385 
398 ob::OptimizationObjectivePtr getBalancedObjective1(const ob::SpaceInformationPtr &si)
399 {
400  auto lengthObj(std::make_shared<ob::PathLengthOptimizationObjective>(si));
401  auto clearObj(std::make_shared<ClearanceObjective>(si));
402  auto opt(std::make_shared<ob::MultiOptimizationObjective>(si));
403  opt->addObjective(lengthObj, 10.0);
404  opt->addObjective(clearObj, 1.0);
405 
406  return ob::OptimizationObjectivePtr(opt);
407 }
408 
412 ob::OptimizationObjectivePtr getBalancedObjective2(const ob::SpaceInformationPtr &si)
413 {
414  auto lengthObj(std::make_shared<ob::PathLengthOptimizationObjective>(si));
415  auto clearObj(std::make_shared<ClearanceObjective>(si));
416 
417  return 10.0 * lengthObj + clearObj;
418 }
419 
423 ob::OptimizationObjectivePtr getPathLengthObjWithCostToGo(const ob::SpaceInformationPtr &si)
424 {
425  auto obj(std::make_shared<ob::PathLengthOptimizationObjective>(si));
426  obj->setCostToGoHeuristic(&ob::goalRegionCostToGo);
427  return obj;
428 }
429 
431 bool argParse(int argc, char **argv, double *runTimePtr, optimalPlanner *plannerPtr, planningObjective *objectivePtr,
432  std::string *outputFilePtr)
433 {
434  namespace bpo = boost::program_options;
435 
436  // Declare the supported options.
437  bpo::options_description desc("Allowed options");
438  desc.add_options()("help,h", "produce help message")(
439  "runtime,t", bpo::value<double>()->default_value(1.0),
440  "(Optional) Specify the runtime in seconds. Defaults to 1 and "
441  "must be greater than 0.")("planner,p", bpo::value<std::string>()->default_value("RRTstar"),
442  "(Optional) Specify the optimal planner to use, defaults to RRTstar if not given. "
443  "Valid options are AITstar, "
444  "BFMTstar, BITstar, CForest, EITstar, EIRMstar, FMTstar, InformedRRTstar, PRMstar, RRTstar, "
445  "and SORRTstar.") // Alphabetical order
446  ("objective,o", bpo::value<std::string>()->default_value("PathLength"),
447  "(Optional) Specify the optimization objective, defaults to PathLength if not given. Valid options are "
448  "PathClearance, PathLength, ThresholdPathLength, and WeightedLengthAndClearanceCombo.") // Alphabetical order
449  ("file,f", bpo::value<std::string>()->default_value(""),
450  "(Optional) Specify an output path for the found solution path.")(
451  "info,i", bpo::value<unsigned int>()->default_value(0u),
452  "(Optional) Set the OMPL log level. 0 for WARN, 1 for INFO, 2 for DEBUG. Defaults to WARN.");
453  bpo::variables_map vm;
454  bpo::store(bpo::parse_command_line(argc, argv, desc), vm);
455  bpo::notify(vm);
456 
457  // Check if the help flag has been given:
458  if (vm.count("help") != 0u)
459  {
460  std::cout << desc << std::endl;
461  return false;
462  }
463 
464  // Set the log-level
465  unsigned int logLevel = vm["info"].as<unsigned int>();
466 
467  // Switch to setting the log level:
468  if (logLevel == 0u)
469  {
470  ompl::msg::setLogLevel(ompl::msg::LOG_WARN);
471  }
472  else if (logLevel == 1u)
473  {
474  ompl::msg::setLogLevel(ompl::msg::LOG_INFO);
475  }
476  else if (logLevel == 2u)
477  {
478  ompl::msg::setLogLevel(ompl::msg::LOG_DEBUG);
479  }
480  else
481  {
482  std::cout << "Invalid log-level integer." << std::endl << std::endl << desc << std::endl;
483  return false;
484  }
485 
486  // Get the runtime as a double
487  *runTimePtr = vm["runtime"].as<double>();
488 
489  // Sanity check
490  if (*runTimePtr <= 0.0)
491  {
492  std::cout << "Invalid runtime." << std::endl << std::endl << desc << std::endl;
493  return false;
494  }
495 
496  // Get the specified planner as a string
497  std::string plannerStr = vm["planner"].as<std::string>();
498 
499  // Map the string to the enum
500  if (boost::iequals("AITstar", plannerStr))
501  {
502  *plannerPtr = PLANNER_AITSTAR;
503  }
504  else if (boost::iequals("BFMTstar", plannerStr))
505  {
506  *plannerPtr = PLANNER_BFMTSTAR;
507  }
508  else if (boost::iequals("BITstar", plannerStr))
509  {
510  *plannerPtr = PLANNER_BITSTAR;
511  }
512  else if (boost::iequals("CForest", plannerStr))
513  {
514  *plannerPtr = PLANNER_CFOREST;
515  }
516  else if (boost::iequals("EITstar", plannerStr))
517  {
518  *plannerPtr = PLANNER_EITSTAR;
519  }
520  else if (boost::iequals("EIRMstar", plannerStr))
521  {
522  *plannerPtr = PLANNER_EIRMSTAR;
523  }
524  else if (boost::iequals("FMTstar", plannerStr))
525  {
526  *plannerPtr = PLANNER_FMTSTAR;
527  }
528  else if (boost::iequals("InformedRRTstar", plannerStr))
529  {
530  *plannerPtr = PLANNER_INF_RRTSTAR;
531  }
532  else if (boost::iequals("PRMstar", plannerStr))
533  {
534  *plannerPtr = PLANNER_PRMSTAR;
535  }
536  else if (boost::iequals("RRTstar", plannerStr))
537  {
538  *plannerPtr = PLANNER_RRTSTAR;
539  }
540  else if (boost::iequals("SORRTstar", plannerStr))
541  {
542  *plannerPtr = PLANNER_SORRTSTAR;
543  }
544  else
545  {
546  std::cout << "Invalid planner string." << std::endl << std::endl << desc << std::endl;
547  return false;
548  }
549 
550  // Get the specified objective as a string
551  std::string objectiveStr = vm["objective"].as<std::string>();
552 
553  // Map the string to the enum
554  if (boost::iequals("PathClearance", objectiveStr))
555  {
556  *objectivePtr = OBJECTIVE_PATHCLEARANCE;
557  }
558  else if (boost::iequals("PathLength", objectiveStr))
559  {
560  *objectivePtr = OBJECTIVE_PATHLENGTH;
561  }
562  else if (boost::iequals("ThresholdPathLength", objectiveStr))
563  {
564  *objectivePtr = OBJECTIVE_THRESHOLDPATHLENGTH;
565  }
566  else if (boost::iequals("WeightedLengthAndClearanceCombo", objectiveStr))
567  {
568  *objectivePtr = OBJECTIVE_WEIGHTEDCOMBO;
569  }
570  else
571  {
572  std::cout << "Invalid objective string." << std::endl << std::endl << desc << std::endl;
573  return false;
574  }
575 
576  // Get the output file string and store it in the return pointer
577  *outputFilePtr = vm["file"].as<std::string>();
578 
579  // Looks like we parsed the arguments successfully
580  return true;
581 }
virtual double clearance(const State *) const
Report the distance to the nearest invalid state when starting from state. If the distance is negativ...
This namespace contains code that is specific to planning under geometric constraints.
Definition: GeneticSearch.h:79
Definition of an abstract state.
Definition: State.h:113
This namespace contains sampling based planning routines shared by both planning under geometric cons...
Defines optimization objectives where path cost can be represented as a path integral over a cost fun...
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:111
const T * as() const
Cast this instance to a desired type.
Definition: State.h:162
ompl::base::State StateType
Define the type of state allocated by this space.
Definition: StateSpace.h:142
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
StateValidityChecker(SpaceInformation *si)
Constructor.
A class to store the exit status of Planner::solve()
Cost stateCost(const State *s) const override
Returns a cost with a value of 1.
virtual bool isValid(const State *state) const =0
Return true if the state state is valid. Usually, this means at least collision checking....
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
Definition of a scoped state.
Definition: ScopedState.h:120
Abstract definition for a class checking the validity of states. The implementation of this class mus...