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