LBTRRT.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2015, Tel Aviv 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 Tel Aviv 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: Oren Salzman, Sertac Karaman, Ioan Sucan, Mark Moll */
36 
37 #include "ompl/geometric/planners/rrt/LBTRRT.h"
38 #include "ompl/base/goals/GoalSampleableRegion.h"
39 #include "ompl/tools/config/SelfConfig.h"
40 #include <limits>
41 #include <math.h>
42 #include <boost/math/constants/constants.hpp>
43 
45  base::Planner(si, "LBTRRT"),
46  goalBias_(0.05),
47  maxDistance_(0.0),
48  epsilon_(0.4),
49  lastGoalMotion_(nullptr),
50  iterations_(0)
51 {
53  specs_.directed = true;
54 
55  Planner::declareParam<double>("range", this, &LBTRRT::setRange, &LBTRRT::getRange, "0.:1.:10000.");
56  Planner::declareParam<double>("goal_bias", this, &LBTRRT::setGoalBias, &LBTRRT::getGoalBias, "0.:.05:1.");
57  Planner::declareParam<double>("epsilon", this, &LBTRRT::setApproximationFactor, &LBTRRT::getApproximationFactor, "0.:.1:10.");
58 
59  addPlannerProgressProperty("iterations INTEGER",
60  std::bind(&LBTRRT::getIterationCount, this));
61  addPlannerProgressProperty("best cost REAL",
62  std::bind(&LBTRRT::getBestCost, this));
63 }
64 
65 ompl::geometric::LBTRRT::~LBTRRT()
66 {
67  freeMemory();
68 }
69 
71 {
72  Planner::clear();
73  sampler_.reset();
74  freeMemory();
75  if (nn_)
76  nn_->clear();
77  lastGoalMotion_ = nullptr;
78 
79  iterations_ = 0;
80  bestCost_ = std::numeric_limits<double>::infinity();
81 }
82 
84 {
85  Planner::setup();
86  tools::SelfConfig sc(si_, getName());
87  sc.configurePlannerRange(maxDistance_);
88 
89  if (!nn_)
90  nn_.reset(tools::SelfConfig::getDefaultNearestNeighbors<Motion*>(this));
91  nn_->setDistanceFunction(std::bind(&LBTRRT::distanceFunction, this, std::placeholders::_1, std::placeholders::_2));
92 }
93 
95 {
96  if (idToMotionMap_.size() > 0)
97  {
98  for (unsigned int i = 0 ; i < idToMotionMap_.size() ; ++i)
99  {
100  if (idToMotionMap_[i]->state_)
101  si_->freeState(idToMotionMap_[i]->state_);
102  delete idToMotionMap_[i];
103  }
104  }
105 }
106 
108 {
109  checkValidity();
110  // update goal and check validity
111  base::Goal *goal = pdef_->getGoal().get();
112  base::GoalSampleableRegion *goal_s = dynamic_cast<base::GoalSampleableRegion*>(goal);
113 
114  if (!goal)
115  {
116  OMPL_ERROR("%s: Goal undefined", getName().c_str());
118  }
119 
120  // update start and check validity
121  while (const base::State *st = pis_.nextStart())
122  {
123  Motion *motion = new Motion(si_);
124  si_->copyState(motion->state_, st);
125  motion->id_ = nn_->size();
126  idToMotionMap_.push_back(motion);
127  nn_->add(motion);
128  lowerBoundGraph_.addVertex(motion->id_);
129  }
130 
131  if (nn_->size() == 0)
132  {
133  OMPL_ERROR("%s: There are no valid initial states!", getName().c_str());
135  }
136 
137  if (nn_->size() > 1)
138  {
139  OMPL_ERROR("%s: There are multiple start states - currently not supported!", getName().c_str());
141  }
142 
143  if (!sampler_)
144  sampler_ = si_->allocStateSampler();
145 
146  OMPL_INFORM("%s: Starting planning with %u states already in datastructure", getName().c_str(), nn_->size());
147 
148  Motion *solution = lastGoalMotion_;
149  Motion *approxSol = nullptr;
150  double approxdif = std::numeric_limits<double>::infinity();
151  // e*(1+1/d) K-nearest constant, as used in RRT*
152  double k_rrg = boost::math::constants::e<double>() +
153  boost::math::constants::e<double>() / (double)si_->getStateDimension();
154 
155  Motion *rmotion = new Motion(si_);
156  base::State *rstate = rmotion->state_;
157  base::State *xstate = si_->allocState();
158  unsigned int statesGenerated = 0;
159 
160  bestCost_ = lastGoalMotion_ ? lastGoalMotion_->costApx_ : std::numeric_limits<double>::infinity();
161  while (ptc() == false)
162  {
163  iterations_++;
164  /* sample random state (with goal biasing) */
165  if (goal_s && rng_.uniform01() < goalBias_ && goal_s->canSample())
166  goal_s->sampleGoal(rstate);
167  else
168  sampler_->sampleUniform(rstate);
169 
170  /* find closest state in the tree */
171  Motion *nmotion = nn_->nearest(rmotion);
172  base::State *dstate = rstate;
173 
174  /* find state to add */
175  double d = si_->distance(nmotion->state_, rstate);
176  if (d == 0) // this takes care of the case that the goal is a single point and we re-sample it multiple times
177  continue;
178  if (d > maxDistance_)
179  {
180  si_->getStateSpace()->interpolate(nmotion->state_, rstate, maxDistance_ / d, xstate);
181  dstate = xstate;
182  }
183 
184  if (checkMotion(nmotion->state_, dstate))
185  {
186  statesGenerated++;
187  /* create a motion */
188  Motion *motion = new Motion(si_);
189  si_->copyState(motion->state_, dstate);
190 
191  /* update fields */
192  double distN = distanceFunction(nmotion, motion);
193 
194  motion->id_ = nn_->size();
195  idToMotionMap_.push_back(motion);
196  lowerBoundGraph_.addVertex(motion->id_);
197  motion->parentApx_ = nmotion;
198 
199  std::list<std::size_t> dummy;
200  lowerBoundGraph_.addEdge(nmotion->id_, motion->id_, distN, false, dummy);
201 
202  motion->costLb_ = nmotion->costLb_ + distN;
203  motion->costApx_ = nmotion->costApx_ + distN;
204  nmotion->childrenApx_.push_back(motion);
205 
206  std::vector<Motion*> nnVec;
207  unsigned int k = std::ceil(k_rrg * log((double)(nn_->size() + 1)));
208  nn_->nearestK(motion, k, nnVec);
209  nn_->add(motion); // if we add the motion before the nearestK call, we will get ourselves...
210 
211  IsLessThan isLessThan(this, motion);
212  std::sort(nnVec.begin(), nnVec.end(), isLessThan);
213 
214  //-------------------------------------------------//
215  // Rewiring Part (i) - find best parent of motion //
216  //-------------------------------------------------//
217  if (motion->parentApx_ != nnVec.front())
218  {
219  for (std::size_t i(0); i < nnVec.size(); ++i)
220  {
221  Motion *potentialParent = nnVec[i];
222  double dist = distanceFunction(potentialParent, motion);
223  considerEdge(potentialParent, motion, dist);
224  }
225  }
226 
227  //------------------------------------------------------------------//
228  // Rewiring Part (ii) //
229  // check if motion may be a better parent to one of its neighbors //
230  //------------------------------------------------------------------//
231  for (std::size_t i(0); i < nnVec.size(); ++i)
232  {
233  Motion *child = nnVec[i];
234  double dist = distanceFunction(motion, child);
235  considerEdge(motion, child, dist);
236  }
237 
238  double dist = 0.0;
239  bool sat = goal->isSatisfied(motion->state_, &dist);
240 
241  if (sat)
242  {
243  approxdif = dist;
244  solution = motion;
245  }
246  if (dist < approxdif)
247  {
248  approxdif = dist;
249  approxSol = motion;
250  }
251 
252  if (solution != nullptr && bestCost_ != solution->costApx_)
253  {
254  OMPL_INFORM("%s: approximation cost = %g", getName().c_str(),
255  solution->costApx_);
256  bestCost_ = solution->costApx_;
257  }
258  }
259  }
260 
261  bool solved = false;
262  bool approximate = false;
263 
264  if (solution == nullptr)
265  {
266  solution = approxSol;
267  approximate = true;
268  }
269 
270  if (solution != nullptr)
271  {
272  lastGoalMotion_ = solution;
273 
274  /* construct the solution path */
275  std::vector<Motion*> mpath;
276  while (solution != nullptr)
277  {
278  mpath.push_back(solution);
279  solution = solution->parentApx_;
280  }
281 
282  /* set the solution path */
283  PathGeometric *path = new PathGeometric(si_);
284  for (int i = mpath.size() - 1 ; i >= 0 ; --i)
285  path->append(mpath[i]->state_);
286  // Add the solution path.
287  base::PathPtr bpath(path);
288  base::PlannerSolution psol(bpath);
289  psol.setPlannerName(getName());
290  if (approximate)
291  psol.setApproximate(approxdif);
292  pdef_->addSolutionPath(psol);
293  solved = true;
294  }
295 
296  si_->freeState(xstate);
297  if (rmotion->state_)
298  si_->freeState(rmotion->state_);
299  delete rmotion;
300 
301  OMPL_INFORM("%s: Created %u states", getName().c_str(), statesGenerated);
302 
303  return base::PlannerStatus(solved, approximate);
304 }
305 
306 void ompl::geometric::LBTRRT::considerEdge(Motion *parent, Motion *child, double c)
307 {
308  // optimization - check if the bounded approximation invariant
309  // will be violated after the edge insertion (at least for the child node)
310  // if this is the case - perform the local planning
311  // this prevents the update of the graph due to the edge insertion and then the re-update as it is removed
312  double potential_cost = parent->costLb_ + c;
313  if (child->costApx_ > (1 + epsilon_) * potential_cost)
314  if (!checkMotion(parent, child))
315  return;
316 
317  // update lowerBoundGraph_
318  std::list<std::size_t> affected;
319 
320  lowerBoundGraph_.addEdge(parent->id_, child->id_, c, true, affected);
321 
322  // now, check if the bounded apprimation invariant has been violated for each affected vertex
323  // insert them into a priority queue ordered according to the lb cost
324  std::list<std::size_t>::iterator iter;
325  IsLessThanLB isLessThanLB(this);
326  Lb_queue queue(isLessThanLB);
327 
328  for (iter = affected.begin(); iter != affected.end(); ++iter)
329  {
330  Motion *m = getMotion(*iter);
331  m->costLb_ = lowerBoundGraph_.getShortestPathCost(*iter);
332  if (m->costApx_ > (1 + epsilon_) * m->costLb_)
333  queue.insert(m);
334  }
335 
336  while (queue.empty() == false)
337  {
338  Motion *motion = *(queue.begin());
339  queue.erase(queue.begin());
340 
341  if (motion->costApx_ > (1 + epsilon_) * motion->costLb_)
342  {
343  Motion *potential_parent = getMotion(lowerBoundGraph_.getShortestPathParent(motion->id_));
344  if (checkMotion(potential_parent, motion))
345  {
346  double delta = lazilyUpdateApxParent(motion, potential_parent);
347  updateChildCostsApx(motion, delta);
348  }
349  else
350  {
351  affected.clear();
352 
353  lowerBoundGraph_.removeEdge(potential_parent->id_, motion->id_, true, affected);
354 
355  for (iter = affected.begin(); iter != affected.end(); ++iter)
356  {
357  Motion *affected = getMotion(*iter);
358  Lb_queue_iter lb_queue_iter = queue.find(affected);
359  if (lb_queue_iter != queue.end())
360  {
361  queue.erase(lb_queue_iter);
362  affected->costLb_ = lowerBoundGraph_.getShortestPathCost(affected->id_);
363  if (affected->costApx_ > (1 + epsilon_) * affected->costLb_)
364  queue.insert(affected);
365  }
366  else
367  {
368  affected->costLb_ = lowerBoundGraph_.getShortestPathCost(affected->id_);
369  }
370  }
371 
372  motion->costLb_ = lowerBoundGraph_.getShortestPathCost(motion->id_);
373  if (motion->costApx_ > (1 + epsilon_) * motion->costLb_)
374  queue.insert(motion);
375 
376  // optimization - we can remove the opposite edge
377  lowerBoundGraph_.removeEdge(motion->id_, potential_parent->id_, false, affected);
378  }
379  }
380  }
381 
382  return;
383 }
384 
386 {
387  Planner::getPlannerData(data);
388 
389  std::vector<Motion*> motions;
390  if (nn_)
391  nn_->list(motions);
392 
393  if (lastGoalMotion_)
394  data.addGoalVertex(base::PlannerDataVertex(lastGoalMotion_->state_));
395 
396  for (unsigned int i = 0 ; i < motions.size() ; ++i)
397  {
398  if (motions[i]->parentApx_ == nullptr)
399  data.addStartVertex(base::PlannerDataVertex(motions[i]->state_));
400  else
401  data.addEdge(base::PlannerDataVertex(motions[i]->parentApx_->state_),
402  base::PlannerDataVertex(motions[i]->state_));
403  }
404 }
405 
407 {
408  for (std::size_t i = 0; i < m->childrenApx_.size(); ++i)
409  {
410  Motion* child = m->childrenApx_[i];
411  child->costApx_ += delta;
412  updateChildCostsApx(child, delta);
413  }
414 }
415 
416 
418 {
419  double dist = distanceFunction(parent, child);
420  removeFromParentApx(child);
421  double deltaApx = parent->costApx_ + dist - child->costApx_;
422  child->parentApx_ = parent;
423  parent->childrenApx_.push_back(child);
424  child->costApx_ = parent->costApx_ + dist;
425 
426  return deltaApx;
427 }
428 
430 {
431  std::vector<Motion*>& vec = m->parentApx_->childrenApx_;
432  for (std::vector<Motion*>::iterator it = vec.begin (); it != vec.end(); ++it)
433  if (*it == m)
434  {
435  vec.erase(it);
436  break;
437  }
438 }
void updateChildCostsApx(Motion *m, double delta)
update the child cost of the approximation tree
Definition: LBTRRT.cpp:406
bool approximateSolutions
Flag indicating whether the planner is able to compute approximate solutions.
Definition: Planner.h:212
void addPlannerProgressProperty(const std::string &progressPropertyName, const PlannerProgressProperty &prop)
Add a planner progress property called progressPropertyName with a property querying function prop to...
Definition: Planner.h:392
double costApx_
The approximation cost.
Definition: LBTRRT.h:193
comparator - metric is the lower bound cost
Definition: LBTRRT.h:221
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique...
Definition: PlannerData.h:163
void setApproximationFactor(double epsilon)
Set the apprimation factor.
Definition: LBTRRT.h:134
void setApproximate(double difference)
Specify that the solution is approximate and set the difference to the goal.
void log(const char *file, int line, LogLevel level, const char *m,...)
Root level logging function. This should not be invoked directly, but rather used via a logging macro...
Definition: Console.cpp:120
void freeMemory()
Free the memory allocated by this planner.
Definition: LBTRRT.cpp:94
virtual base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc)
Function that can solve the motion planning problem. This function can be called multiple times on th...
Definition: LBTRRT.cpp:107
LBTRRT(const base::SpaceInformationPtr &si)
Constructor.
Definition: LBTRRT.cpp:44
Representation of a solution to a planning problem.
base::State * state_
The state contained by the motion.
Definition: LBTRRT.h:181
double getGoalBias() const
Get the goal bias the planner is using.
Definition: LBTRRT.h:103
unsigned int addGoalVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
Abstract definition of goals.
Definition: Goal.h:62
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
virtual void getPlannerData(base::PlannerData &data) const
Get information about the current run of the motion planner. Repeated calls to this function will upd...
Definition: LBTRRT.cpp:385
void append(const base::State *state)
Append state to the end of this path. The memory for state is copied.
bool directed
Flag indicating whether the planner is able to account for the fact that the validity of a motion fro...
Definition: Planner.h:220
virtual void sampleGoal(State *st) const =0
Sample a state in the goal region.
double distanceFunction(const Motion *a, const Motion *b) const
Compute distance between motions (actually distance between contained states)
Definition: LBTRRT.h:253
void removeFromParentApx(Motion *m)
remove motion from its parent in the approximation tree
Definition: LBTRRT.cpp:429
double getApproximationFactor() const
Get the apprimation factor.
Definition: LBTRRT.h:140
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition: PlannerData.h:59
Invalid start state or no start state specified.
Definition: PlannerStatus.h:56
Abstract definition of a goal region that can be sampled.
virtual void setup()
Perform extra configuration steps, if needed. This call will also issue a call to ompl::base::SpaceIn...
Definition: LBTRRT.cpp:83
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
A class to store the exit status of Planner::solve()
Definition: PlannerStatus.h:48
virtual bool addEdge(unsigned int v1, unsigned int v2, const PlannerDataEdge &edge=PlannerDataEdge(), Cost weight=Cost(1.0))
Adds a directed edge between the given vertex indexes. An optional edge structure and weight can be s...
bool canSample() const
Return true if maxSampleCount() > 0, since in this case samples can certainly be produced.
A shared pointer wrapper for ompl::base::SpaceInformation.
unsigned int addStartVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
Definition of an abstract state.
Definition: State.h:50
void setRange(double distance)
Set the range the planner is supposed to use.
Definition: LBTRRT.h:113
virtual bool isSatisfied(const State *st) const =0
Return true if the state satisfies the goal constraints.
void considerEdge(Motion *parent, Motion *child, double c)
consider an edge for addition to the roadmap
Definition: LBTRRT.cpp:306
std::vector< Motion * > childrenApx_
The children in the approximation tree.
Definition: LBTRRT.h:195
double getRange() const
Get the range the planner is using.
Definition: LBTRRT.h:119
Motion * parentApx_
The parent motion in the approximation tree.
Definition: LBTRRT.h:191
PlannerSpecs specs_
The specifications of the planner (its capabilities)
Definition: Planner.h:410
double lazilyUpdateApxParent(Motion *child, Motion *parent)
lazily update the parent in the approximation tree without updating costs to cildren ...
Definition: LBTRRT.cpp:417
virtual void clear()
Clear all internal datastructures. Planner settings are not affected. Subsequent calls to solve() wil...
Definition: LBTRRT.cpp:70
comparator - metric is the cost to reach state via a specific state
Definition: LBTRRT.h:199
void configurePlannerRange(double &range)
Compute what a good length for motion segments is.
Definition: SelfConfig.cpp:230
This class contains methods that automatically configure various parameters for motion planning...
Definition: SelfConfig.h:60
double costLb_
The lower bound cost of the motion while it is stored in the lowerBoundGraph_ and this may seem redun...
Definition: LBTRRT.h:189
Definition of a geometric path.
Definition: PathGeometric.h:60
void setGoalBias(double goalBias)
Set the goal bias.
Definition: LBTRRT.h:97
Representation of a motion.
Definition: LBTRRT.h:162
std::size_t id_
unique id of the motion
Definition: LBTRRT.h:183
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