ProblemDefinition.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2010, 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: Ioan Sucan */
36 
37 #include "ompl/base/ProblemDefinition.h"
38 #include "ompl/base/goals/GoalState.h"
39 #include "ompl/base/goals/GoalStates.h"
40 #include "ompl/base/OptimizationObjective.h"
41 #include "ompl/control/SpaceInformation.h"
42 #include "ompl/control/PathControl.h"
43 #include "ompl/tools/config/MagicConstants.h"
44 #include <sstream>
45 #include <algorithm>
46 #include <mutex>
47 
49 namespace ompl
50 {
51  namespace base
52  {
53 
54  class ProblemDefinition::PlannerSolutionSet
55  {
56  public:
57 
58  PlannerSolutionSet()
59  {
60  }
61 
62  void add(const PlannerSolution &s)
63  {
64  std::lock_guard<std::mutex> slock(lock_);
65  int index = solutions_.size();
66  solutions_.push_back(s);
67  solutions_.back().index_ = index;
68  std::sort(solutions_.begin(), solutions_.end());
69  }
70 
71  void clear()
72  {
73  std::lock_guard<std::mutex> slock(lock_);
74  solutions_.clear();
75  }
76 
77  std::vector<PlannerSolution> getSolutions()
78  {
79  std::lock_guard<std::mutex> slock(lock_);
80  std::vector<PlannerSolution> copy = solutions_;
81  return copy;
82  }
83 
84  bool isApproximate()
85  {
86  std::lock_guard<std::mutex> slock(lock_);
87  bool result = false;
88  if (!solutions_.empty())
89  result = solutions_[0].approximate_;
90  return result;
91  }
92 
93  bool isOptimized()
94  {
95  std::lock_guard<std::mutex> slock(lock_);
96  bool result = false;
97  if (!solutions_.empty())
98  result = solutions_[0].optimized_;
99  return result;
100  }
101 
102  double getDifference()
103  {
104  std::lock_guard<std::mutex> slock(lock_);
105  double diff = -1.0;
106  if (!solutions_.empty())
107  diff = solutions_[0].difference_;
108  return diff;
109  }
110 
111  PathPtr getTopSolution()
112  {
113  std::lock_guard<std::mutex> slock(lock_);
114  PathPtr copy;
115  if (!solutions_.empty())
116  copy = solutions_[0].path_;
117  return copy;
118  }
119 
120  bool getTopSolution(PlannerSolution& solution)
121  {
122  std::lock_guard<std::mutex> slock(lock_);
123 
124  if (!solutions_.empty())
125  {
126  solution = solutions_[0];
127  return true;
128  }
129  else
130  {
131  return false;
132  }
133  }
134 
135  std::size_t getSolutionCount()
136  {
137  std::lock_guard<std::mutex> slock(lock_);
138  std::size_t result = solutions_.size();
139  return result;
140  }
141 
142  private:
143 
144  std::vector<PlannerSolution> solutions_;
145  std::mutex lock_;
146  };
147  }
148 }
150 
152 {
153  if (!approximate_ && b.approximate_)
154  return true;
155  if (approximate_ && !b.approximate_)
156  return false;
157  if (approximate_ && b.approximate_)
158  return difference_ < b.difference_;
159  if (optimized_ && !b.optimized_)
160  return true;
161  if (!optimized_ && b.optimized_)
162  return false;
163  return opt_ ? opt_->isCostBetterThan(cost_, b.cost_) : length_ < b.length_;
164 }
165 
166 ompl::base::ProblemDefinition::ProblemDefinition(const SpaceInformationPtr &si) : si_(si), solutions_(new PlannerSolutionSet())
167 {
168 }
169 
170 void ompl::base::ProblemDefinition::setStartAndGoalStates(const State *start, const State *goal, const double threshold)
171 {
172  clearStartStates();
173  addStartState(start);
174  setGoalState(goal, threshold);
175 }
176 
177 void ompl::base::ProblemDefinition::setGoalState(const State *goal, const double threshold)
178 {
179  clearGoal();
180  GoalState *gs = new GoalState(si_);
181  gs->setState(goal);
182  gs->setThreshold(threshold);
183  setGoal(GoalPtr(gs));
184 }
185 
186 bool ompl::base::ProblemDefinition::hasStartState(const State *state, unsigned int *startIndex) const
187 {
188  for (unsigned int i = 0 ; i < startStates_.size() ; ++i)
189  if (si_->equalStates(state, startStates_[i]))
190  {
191  if (startIndex)
192  *startIndex = i;
193  return true;
194  }
195  return false;
196 }
197 
198 bool ompl::base::ProblemDefinition::fixInvalidInputState(State *state, double dist, bool start, unsigned int attempts)
199 {
200  bool result = false;
201 
202  bool b = si_->satisfiesBounds(state);
203  bool v = false;
204  if (b)
205  {
206  v = si_->isValid(state);
207  if (!v)
208  OMPL_DEBUG("%s state is not valid", start ? "Start" : "Goal");
209  }
210  else
211  OMPL_DEBUG("%s state is not within space bounds", start ? "Start" : "Goal");
212 
213  if (!b || !v)
214  {
215  std::stringstream ss;
216  si_->printState(state, ss);
217  ss << " within distance " << dist;
218  OMPL_DEBUG("Attempting to fix %s state %s", start ? "start" : "goal", ss.str().c_str());
219 
220  State *temp = si_->allocState();
221  if (si_->searchValidNearby(temp, state, dist, attempts))
222  {
223  si_->copyState(state, temp);
224  result = true;
225  }
226  else
227  OMPL_WARN("Unable to fix %s state", start ? "start" : "goal");
228  si_->freeState(temp);
229  }
230 
231  return result;
232 }
233 
234 bool ompl::base::ProblemDefinition::fixInvalidInputStates(double distStart, double distGoal, unsigned int attempts)
235 {
236  bool result = true;
237 
238  // fix start states
239  for (unsigned int i = 0 ; i < startStates_.size() ; ++i)
240  if (!fixInvalidInputState(startStates_[i], distStart, true, attempts))
241  result = false;
242 
243  // fix goal state
244  GoalState *goal = dynamic_cast<GoalState*>(goal_.get());
245  if (goal)
246  {
247  if (!fixInvalidInputState(const_cast<State*>(goal->getState()), distGoal, false, attempts))
248  result = false;
249  }
250 
251  // fix goal state
252  GoalStates *goals = dynamic_cast<GoalStates*>(goal_.get());
253  if (goals)
254  {
255  for (unsigned int i = 0; i < goals->getStateCount(); ++i)
256  if (!fixInvalidInputState(const_cast<State*>(goals->getState(i)), distGoal, false, attempts))
257  result = false;
258  }
259 
260  return result;
261 }
262 
263 void ompl::base::ProblemDefinition::getInputStates(std::vector<const State*> &states) const
264 {
265  states.clear();
266  for (unsigned int i = 0 ; i < startStates_.size() ; ++i)
267  states.push_back(startStates_[i]);
268 
269  GoalState *goal = dynamic_cast<GoalState*>(goal_.get());
270  if (goal)
271  states.push_back(goal->getState());
272 
273  GoalStates *goals = dynamic_cast<GoalStates*>(goal_.get());
274  if (goals)
275  for (unsigned int i = 0; i < goals->getStateCount(); ++i)
276  states.push_back (goals->getState(i));
277 }
278 
280 {
281  PathPtr path;
282  if (control::SpaceInformationPtr sic = std::dynamic_pointer_cast<control::SpaceInformation, SpaceInformation>(si_))
283  {
284  unsigned int startIndex;
285  if (isTrivial(&startIndex, nullptr))
286  {
288  pc->append(startStates_[startIndex]);
289  control::Control *null = sic->allocControl();
290  sic->nullControl(null);
291  pc->append(startStates_[startIndex], null, 0.0);
292  sic->freeControl(null);
293  path.reset(pc);
294  }
295  else
296  {
297  control::Control *nc = sic->allocControl();
298  State *result1 = sic->allocState();
299  State *result2 = sic->allocState();
300  sic->nullControl(nc);
301 
302  for (unsigned int k = 0 ; k < startStates_.size() && !path ; ++k)
303  {
304  const State *start = startStates_[k];
305  if (start && si_->isValid(start) && si_->satisfiesBounds(start))
306  {
307  sic->copyState(result1, start);
308  for (unsigned int i = 0 ; i < sic->getMaxControlDuration() && !path ; ++i)
309  if (sic->propagateWhileValid(result1, nc, 1, result2))
310  {
311  if (goal_->isSatisfied(result2))
312  {
314  pc->append(start);
315  pc->append(result2, nc, (i + 1) * sic->getPropagationStepSize());
316  path.reset(pc);
317  break;
318  }
319  std::swap(result1, result2);
320  }
321  }
322  }
323  sic->freeState(result1);
324  sic->freeState(result2);
325  sic->freeControl(nc);
326  }
327  }
328  else
329  {
330  std::vector<const State*> states;
331  GoalState *goal = dynamic_cast<GoalState*>(goal_.get());
332  if (goal)
333  if (si_->isValid(goal->getState()) && si_->satisfiesBounds(goal->getState()))
334  states.push_back(goal->getState());
335  GoalStates *goals = dynamic_cast<GoalStates*>(goal_.get());
336  if (goals)
337  for (unsigned int i = 0; i < goals->getStateCount(); ++i)
338  if (si_->isValid(goals->getState(i)) && si_->satisfiesBounds(goals->getState(i)))
339  states.push_back(goals->getState(i));
340 
341  if (states.empty())
342  {
343  unsigned int startIndex;
344  if (isTrivial(&startIndex))
345  {
346  geometric::PathGeometric *pg = new geometric::PathGeometric(si_, startStates_[startIndex], startStates_[startIndex]);
347  path.reset(pg);
348  }
349  }
350  else
351  {
352  for (unsigned int i = 0 ; i < startStates_.size() && !path ; ++i)
353  {
354  const State *start = startStates_[i];
355  if (start && si_->isValid(start) && si_->satisfiesBounds(start))
356  {
357  for (unsigned int j = 0 ; j < states.size() && !path ; ++j)
358  if (si_->checkMotion(start, states[j]))
359  {
360  geometric::PathGeometric *pg = new geometric::PathGeometric(si_, start, states[j]);
361  path.reset(pg);
362  break;
363  }
364  }
365  }
366  }
367  }
368 
369  return path;
370 }
371 
372 bool ompl::base::ProblemDefinition::isTrivial(unsigned int *startIndex, double *distance) const
373 {
374  if (!goal_)
375  {
376  OMPL_ERROR("Goal undefined");
377  return false;
378  }
379 
380  for (unsigned int i = 0 ; i < startStates_.size() ; ++i)
381  {
382  const State *start = startStates_[i];
383  if (start && si_->isValid(start) && si_->satisfiesBounds(start))
384  {
385  double dist;
386  if (goal_->isSatisfied(start, &dist))
387  {
388  if (startIndex)
389  *startIndex = i;
390  if (distance)
391  *distance = dist;
392  return true;
393  }
394  }
395  else
396  {
397  OMPL_ERROR("Initial state is in collision!");
398  }
399  }
400 
401  return false;
402 }
403 
405 {
406  return solutions_->getSolutionCount() > 0;
407 }
408 
410 {
411  return solutions_->getSolutionCount();
412 }
413 
415 {
416  return solutions_->getTopSolution();
417 }
418 
420 {
421  return solutions_->getTopSolution(solution);
422 }
423 
424 void ompl::base::ProblemDefinition::addSolutionPath(const PathPtr &path, bool approximate, double difference, const std::string& plannerName) const
425 {
426  PlannerSolution sol(path);
427  if (approximate)
428  sol.setApproximate(difference);
429  sol.setPlannerName(plannerName);
430  addSolutionPath(sol);
431 }
432 
434 {
435  if (sol.approximate_)
436  OMPL_INFORM("ProblemDefinition: Adding approximate solution from planner %s", sol.plannerName_.c_str());
437  solutions_->add(sol);
438 }
439 
441 {
442  return solutions_->isApproximate();
443 }
444 
446 {
447  return solutions_->isOptimized();
448 }
449 
451 {
452  return solutions_->getDifference();
453 }
454 
455 std::vector<ompl::base::PlannerSolution> ompl::base::ProblemDefinition::getSolutions() const
456 {
457  return solutions_->getSolutions();
458 }
459 
461 {
462  solutions_->clear();
463 }
464 
465 void ompl::base::ProblemDefinition::print(std::ostream &out) const
466 {
467  out << "Start states:" << std::endl;
468  for (unsigned int i = 0 ; i < startStates_.size() ; ++i)
469  si_->printState(startStates_[i], out);
470  if (goal_)
471  goal_->print(out);
472  else
473  out << "Goal = nullptr" << std::endl;
474  if (optimizationObjective_)
475  {
476  optimizationObjective_->print(out);
477  out << "Average state cost: " << optimizationObjective_->averageStateCost(magic::TEST_STATE_COUNT) << std::endl;
478  }
479  else
480  out << "OptimizationObjective = nullptr" << std::endl;
481  out << "There are " << solutions_->getSolutionCount() << " solutions" << std::endl;
482 }
483 
485 {
486  return nonExistenceProof_.get();
487 }
488 
490 {
491  nonExistenceProof_.reset();
492 }
493 
495 {
496  return nonExistenceProof_;
497 }
498 
500 {
501  nonExistenceProof_ = nonExistenceProof;
502 }
void setApproximate(double difference)
Specify that the solution is approximate and set the difference to the goal.
bool optimized_
True if the solution was optimized to meet the specified optimization criterion.
Representation of a solution to a planning problem.
void clearSolutionNonExistenceProof()
Removes any existing instance of SolutionNonExistenceProof.
void append(const base::State *state)
Append state to the end of the path; it is assumed state is the first state, so no control is applied...
virtual std::size_t getStateCount() const
Return the number of valid goal states.
Definition: GoalStates.cpp:117
Definition of an abstract control.
Definition: Control.h:48
A shared pointer wrapper for ompl::base::SolutionNonExistenceProof.
const State * getState() const
Get the goal state.
Definition: GoalState.cpp:79
Definition of a goal state.
Definition: GoalState.h:50
void print(std::ostream &out=std::cout) const
Print information about the start and goal states and the optimization objective. ...
bool isTrivial(unsigned int *startIndex=nullptr, double *distance=nullptr) const
A problem is trivial if a given starting state already in the goal region, so we need no motion plann...
double getSolutionDifference() const
Get the distance to the desired goal for the top solution. Return -1.0 if there are no solutions avai...
void setGoalState(const State *goal, const double threshold=std::numeric_limits< double >::epsilon())
A simple form of setting the goal. This is called by setStartAndGoalStates(). A more general form is ...
void setStartAndGoalStates(const State *start, const State *goal, const double threshold=std::numeric_limits< double >::epsilon())
In the simplest case possible, we have a single starting state and a single goal state.
Definition of a set of goal states.
Definition: GoalStates.h:50
Definition of a control path.
Definition: PathControl.h:60
virtual const State * getState(unsigned int index) const
Return a pointer to the indexth state in the state list.
Definition: GoalStates.cpp:109
PathPtr getSolutionPath() const
Return the top solution path, if one is found. The top path is the shortest one that was found...
bool hasOptimizedSolution() const
Return true if the top found solution is optimized (satisfies the specified optimization objective) ...
bool hasSolution() const
Returns true if a solution path has been found (could be approximate)
bool approximate_
True if goal was not achieved, but an approximate solution was found.
bool operator<(const PlannerSolution &b) const
Define a ranking for solutions.
void clearSolutionPaths() const
Forget the solution paths (thread safe). Memory is freed.
void addSolutionPath(const PathPtr &path, bool approximate=false, double difference=-1.0, const std::string &plannerName="Unknown") const
Add a solution path in a thread-safe manner. Multiple solutions can be set for a goal. If a solution does not reach the desired goal it is considered approximate. Optionally, the distance between the desired goal and the one actually achieved is set by difference. Optionally, the name of the planner that generated the solution.
void setState(const State *st)
Set the goal state.
Definition: GoalState.cpp:67
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
bool hasApproximateSolution() const
Return true if the top found solution is approximate (does not actually reach the desired goal...
void setSolutionNonExistenceProof(const SolutionNonExistenceProofPtr &nonExistenceProof)
Set the instance of SolutionNonExistenceProof for this problem definition.
bool fixInvalidInputStates(double distStart, double distGoal, unsigned int attempts)
Many times the start or goal state will barely touch an obstacle. In this case, we may want to automa...
A shared pointer wrapper for ompl::base::SpaceInformation.
Cost cost_
The cost of this solution path, with respect to the optimization objective.
Definition of an abstract state.
Definition: State.h:50
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
PathPtr isStraightLinePathValid() const
Check if a straight line path is valid. If it is, return an instance of a path that represents the st...
std::vector< PlannerSolution > getSolutions() const
Get all the solution paths available for this goal.
A shared pointer wrapper for ompl::control::SpaceInformation.
bool fixInvalidInputState(State *state, double dist, bool start, unsigned int attempts)
Helper function for fixInvalidInputStates(). Attempts to fix an individual state. ...
#define OMPL_DEBUG(fmt,...)
Log a formatted debugging string.
Definition: Console.h:70
OptimizationObjectivePtr opt_
Optimization objective that was used to optimize this solution.
bool hasStartState(const State *state, unsigned int *startIndex=nullptr) const
Check whether a specified starting state is already included in the problem definition and optionally...
bool getSolution(PlannerSolution &solution) const
Return true if a top solution is found, with the top solution passed by reference in the function hea...
void setThreshold(double threshold)
Set the distance to the goal that is allowed for a state to be considered in the goal region...
Definition: GoalRegion.h:81
std::string plannerName_
Name of planner type that generated this solution, as received from Planner::getName() ...
bool hasSolutionNonExistenceProof() const
Returns true if the problem definition has a proof of non existence for a solution.
static const unsigned int TEST_STATE_COUNT
When multiple states need to be generated as part of the computation of various information (usually ...
A shared pointer wrapper for ompl::base::Goal.
std::size_t getSolutionCount() const
Get the number of solutions already found.
Definition of a geometric path.
Definition: PathGeometric.h:60
const SolutionNonExistenceProofPtr & getSolutionNonExistenceProof() const
Retrieve a pointer to the SolutionNonExistenceProof instance for this problem definition.
double difference_
The achieved difference between the found solution and the desired goal.
void getInputStates(std::vector< const State * > &states) const
Get all the input states. This includes start states and states that are part of goal regions that ca...
double length_
For efficiency reasons, keep the length of the path as well.
void setPlannerName(const std::string &name)
Set the name of the planner used to compute this solution.
A shared pointer wrapper for ompl::base::Path.
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68