LazyLBTRRT.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, Mark Moll */
36 
37 #include "ompl/geometric/planners/rrt/LazyLBTRRT.h"
38 #include "ompl/tools/config/SelfConfig.h"
39 #include <limits>
40 #include <boost/foreach.hpp>
41 #include <boost/math/constants/constants.hpp>
42 
43 namespace
44 {
45  int getK(unsigned int n, double k_rrg)
46  {
47  return std::ceil(k_rrg * log((double)(n + 1)));
48  }
49 }
50 
51 ompl::geometric::LazyLBTRRT::LazyLBTRRT(const base::SpaceInformationPtr &si)
52  : base::Planner(si, "LazyLBTRRT")
53 {
55  specs_.directed = true;
56 
57  Planner::declareParam<double>("range", this, &LazyLBTRRT::setRange, &LazyLBTRRT::getRange, "0.:1.:10000.");
58  Planner::declareParam<double>("goal_bias", this, &LazyLBTRRT::setGoalBias, &LazyLBTRRT::getGoalBias, "0.:.05:1.");
59  Planner::declareParam<double>("epsilon", this, &LazyLBTRRT::setApproximationFactor,
61 
62  addPlannerProgressProperty("iterations INTEGER", [this]
63  {
64  return getIterationCount();
65  });
66  addPlannerProgressProperty("best cost REAL", [this]
67  {
68  return getBestCost();
69  });
70 }
71 
72 ompl::geometric::LazyLBTRRT::~LazyLBTRRT()
73 {
74  freeMemory();
75 }
76 
78 {
79  Planner::clear();
80  sampler_.reset();
81  freeMemory();
82  if (nn_)
83  nn_->clear();
84  graphLb_.clear();
85  graphApx_.clear();
86  lastGoalMotion_ = nullptr;
87 
88  iterations_ = 0;
89  bestCost_ = std::numeric_limits<double>::infinity();
90 }
91 
93 {
94  Planner::setup();
95  tools::SelfConfig sc(si_, getName());
96  sc.configurePlannerRange(maxDistance_);
97 
98  if (!nn_)
99  nn_.reset(tools::SelfConfig::getDefaultNearestNeighbors<Motion *>(this));
100  nn_->setDistanceFunction([this](const Motion *a, const Motion *b)
101  {
102  return distanceFunction(a, b);
103  });
104 }
105 
107 {
108  if (!idToMotionMap_.empty())
109  {
110  for (auto &i : idToMotionMap_)
111  {
112  if (i->state_ != nullptr)
113  si_->freeState(i->state_);
114  delete i;
115  }
116  idToMotionMap_.clear();
117  }
118  delete LPAstarApx_;
119  delete LPAstarLb_;
120 }
121 
123 {
124  checkValidity();
125  // update goal and check validity
126  base::Goal *goal = pdef_->getGoal().get();
127  auto *goal_s = dynamic_cast<base::GoalSampleableRegion *>(goal);
128 
129  if (goal == nullptr || goal_s == nullptr)
130  {
131  OMPL_ERROR("%s: Unknown type of goal", getName().c_str());
133  }
134 
135  if (!goal_s->couldSample())
136  {
137  OMPL_ERROR("%s: Insufficient states in sampleable goal region", getName().c_str());
139  }
140 
141  while (const base::State *st = pis_.nextStart())
142  {
143  startMotion_ = createMotion(goal_s, st);
144  break;
145  }
146 
147  if (nn_->size() == 0)
148  {
149  OMPL_ERROR("%s: There are no valid initial states!", getName().c_str());
151  }
152 
153  if (!sampler_)
154  sampler_ = si_->allocStateSampler();
155 
156  OMPL_INFORM("%s: Starting planning with %u states already in datastructure", getName().c_str(), nn_->size());
157 
158  bool solved = false;
159 
160  auto *rmotion = new Motion(si_);
161  base::State *xstate = si_->allocState();
162 
163  goalMotion_ = createGoalMotion(goal_s);
164 
165  CostEstimatorLb costEstimatorLb(goal, idToMotionMap_);
166  LPAstarLb_ = new LPAstarLb(startMotion_->id_, goalMotion_->id_, graphLb_, costEstimatorLb); // rooted at source
167  CostEstimatorApx costEstimatorApx(this);
168  LPAstarApx_ = new LPAstarApx(goalMotion_->id_, startMotion_->id_, graphApx_, costEstimatorApx); // rooted at target
169  double approxdif = std::numeric_limits<double>::infinity();
170  // e+e/d. K-nearest RRT*
171  double k_rrg = boost::math::constants::e<double>() +
172  boost::math::constants::e<double>() / (double)si_->getStateSpace()->getDimension();
173 
175  // step (1) - RRT
177  bestCost_ = std::numeric_limits<double>::infinity();
178  rrt(ptc, goal_s, xstate, rmotion, approxdif);
179  if (!ptc())
180  {
181  solved = true;
182 
184  // step (2) - Lazy construction of G_lb
186  idToMotionMap_.push_back(goalMotion_);
187  int k = getK(idToMotionMap_.size(), k_rrg);
188  std::vector<Motion *> nnVec;
189  nnVec.reserve(k);
190  BOOST_FOREACH (Motion *motion, idToMotionMap_)
191  {
192  nn_->nearestK(motion, k, nnVec);
193  BOOST_FOREACH (Motion *neighbor, nnVec)
194  if (neighbor->id_ != motion->id_ && !edgeExistsLb(motion, neighbor))
195  addEdgeLb(motion, neighbor, distanceFunction(motion, neighbor));
196  }
197  idToMotionMap_.pop_back();
198  closeBounds(ptc);
199  }
200 
202  // step (3) - anytime planning
204  while (!ptc())
205  {
206  std::tuple<Motion *, base::State *, double> res = rrtExtend(goal_s, xstate, rmotion, approxdif);
207  Motion *nmotion = std::get<0>(res);
208  base::State *dstate = std::get<1>(res);
209  double d = std::get<2>(res);
210 
211  iterations_++;
212  if (dstate != nullptr)
213  {
214  /* create a motion */
215  Motion *motion = createMotion(goal_s, dstate);
216  addEdgeApx(nmotion, motion, d);
217  addEdgeLb(nmotion, motion, d);
218 
219  int k = getK(nn_->size(), k_rrg);
220  std::vector<Motion *> nnVec;
221  nnVec.reserve(k);
222  nn_->nearestK(motion, k, nnVec);
223 
224  BOOST_FOREACH (Motion *neighbor, nnVec)
225  if (neighbor->id_ != motion->id_ && !edgeExistsLb(motion, neighbor))
226  addEdgeLb(motion, neighbor, distanceFunction(motion, neighbor));
227 
228  closeBounds(ptc);
229  }
230 
231  std::list<std::size_t> pathApx;
232  double costApx = LPAstarApx_->computeShortestPath(pathApx);
233  if (bestCost_ > costApx)
234  {
235  OMPL_INFORM("%s: approximation cost = %g", getName().c_str(), costApx);
236  bestCost_ = costApx;
237  }
238  }
239 
240  if (solved)
241  {
242  std::list<std::size_t> pathApx;
243  LPAstarApx_->computeShortestPath(pathApx);
244 
245  /* set the solution path */
246  auto path(std::make_shared<PathGeometric>(si_));
247 
248  // the path is in reverse order
249  for (auto rit = pathApx.rbegin(); rit != pathApx.rend(); ++rit)
250  path->append(idToMotionMap_[*rit]->state_);
251 
252  pdef_->addSolutionPath(path, !solved, 0);
253  }
254 
255  si_->freeState(xstate);
256  if (rmotion->state_ != nullptr)
257  si_->freeState(rmotion->state_);
258  delete rmotion;
259 
260  OMPL_INFORM("%s: Created %u states", getName().c_str(), nn_->size());
261 
262  return {solved, !solved};
263 }
264 
265 std::tuple<ompl::geometric::LazyLBTRRT::Motion *, ompl::base::State *, double> ompl::geometric::LazyLBTRRT::rrtExtend(
266  const base::GoalSampleableRegion *goal_s, base::State *xstate, Motion *rmotion, double &approxdif)
267 {
268  base::State *rstate = rmotion->state_;
269  sampleBiased(goal_s, rstate);
270  /* find closest state in the tree */
271  Motion *nmotion = nn_->nearest(rmotion);
272  base::State *dstate = rstate;
273 
274  /* find state to add */
275  double d = distanceFunction(nmotion->state_, rstate);
276  if (d > maxDistance_)
277  {
278  si_->getStateSpace()->interpolate(nmotion->state_, rstate, maxDistance_ / d, xstate);
279  dstate = xstate;
280  d = maxDistance_;
281  }
282 
283  if (!checkMotion(nmotion->state_, dstate))
284  return std::make_tuple((Motion *)nullptr, (base::State *)nullptr, 0.0);
285 
286  // motion is valid
287  double dist = 0.0;
288  bool sat = goal_s->isSatisfied(dstate, &dist);
289  if (sat)
290  {
291  approxdif = dist;
292  }
293  if (dist < approxdif)
294  {
295  approxdif = dist;
296  }
297 
298  return std::make_tuple(nmotion, dstate, d);
299 }
300 
301 void ompl::geometric::LazyLBTRRT::rrt(const base::PlannerTerminationCondition &ptc, base::GoalSampleableRegion *goal_s,
302  base::State *xstate, Motion *rmotion, double &approxdif)
303 {
304  while (!ptc())
305  {
306  std::tuple<Motion *, base::State *, double> res = rrtExtend(goal_s, xstate, rmotion, approxdif);
307  Motion *nmotion = std::get<0>(res);
308  base::State *dstate = std::get<1>(res);
309  double d = std::get<2>(res);
310 
311  iterations_++;
312  if (dstate != nullptr)
313  {
314  /* create a motion */
315  Motion *motion = createMotion(goal_s, dstate);
316  addEdgeApx(nmotion, motion, d);
317 
318  if (motion == goalMotion_)
319  return;
320  }
321  }
322 }
323 
325 {
326  Planner::getPlannerData(data);
327 
328  if (lastGoalMotion_ != nullptr)
329  data.addGoalVertex(base::PlannerDataVertex(lastGoalMotion_->state_));
330 
331  for (unsigned int i = 0; i < idToMotionMap_.size(); ++i)
332  {
333  const base::State *parent = idToMotionMap_[i]->state_;
334  if (boost::in_degree(i, graphApx_) == 0)
336  if (boost::out_degree(i, graphApx_) == 0)
338  else
339  {
340  boost::graph_traits<BoostGraph>::out_edge_iterator ei, ei_end;
341  for (boost::tie(ei, ei_end) = boost::out_edges(i, graphApx_); ei != ei_end; ++ei)
342  {
343  std::size_t v = boost::target(*ei, graphApx_);
344  data.addEdge(base::PlannerDataVertex(idToMotionMap_[v]->state_), base::PlannerDataVertex(parent));
345  }
346  }
347  }
348 }
349 
351 {
352  /* sample random state (with goal biasing) */
353  if ((goal_s != nullptr) && rng_.uniform01() < goalBias_ && goal_s->canSample())
354  goal_s->sampleGoal(rstate);
355  else
356  sampler_->sampleUniform(rstate);
357 };
358 
359 ompl::geometric::LazyLBTRRT::Motion *ompl::geometric::LazyLBTRRT::createMotion(const base::GoalSampleableRegion *goal_s,
360  const base::State *st)
361 {
362  if (goal_s->isSatisfied(st))
363  return goalMotion_;
364 
365  auto *motion = new Motion(si_);
366  si_->copyState(motion->state_, st);
367  motion->id_ = idToMotionMap_.size();
368  nn_->add(motion);
369  idToMotionMap_.push_back(motion);
370  addVertex(motion);
371 
372  return motion;
373 }
374 
376 ompl::geometric::LazyLBTRRT::createGoalMotion(const base::GoalSampleableRegion *goal_s)
377 {
378  ompl::base::State *gstate = si_->allocState();
379  goal_s->sampleGoal(gstate);
380 
381  auto *motion = new Motion(si_);
382  motion->state_ = gstate;
383  motion->id_ = idToMotionMap_.size();
384  idToMotionMap_.push_back(motion);
385  addVertex(motion);
386 
387  return motion;
388 }
389 
390 void ompl::geometric::LazyLBTRRT::closeBounds(const base::PlannerTerminationCondition &ptc)
391 {
392  std::list<std::size_t> pathApx;
393  double costApx = LPAstarApx_->computeShortestPath(pathApx);
394  std::list<std::size_t> pathLb;
395  double costLb = LPAstarLb_->computeShortestPath(pathLb);
396 
397  while (costApx > (1. + epsilon_) * costLb)
398  {
399  if (ptc())
400  return;
401 
402  auto pathLbIter = pathLb.end();
403  pathLbIter--;
404  std::size_t v = *pathLbIter;
405  pathLbIter--;
406  std::size_t u = *pathLbIter;
407 
408  while (edgeExistsApx(u, v))
409  {
410  v = u;
411  --pathLbIter;
412  u = *pathLbIter;
413  }
414 
415  Motion *motionU = idToMotionMap_[u];
416  Motion *motionV = idToMotionMap_[v];
417  if (checkMotion(motionU, motionV))
418  {
419  // note that we change the direction between u and v due to the diff in definition between Apx and LB
420  addEdgeApx(motionV, motionU,
421  distanceFunction(motionU, motionV)); // the distance here can be obtained from the LB graph
422  pathApx.clear();
423  costApx = LPAstarApx_->computeShortestPath(pathApx);
424  }
425  else // the edge (u,v) was not collision free
426  {
427  removeEdgeLb(motionU, motionV);
428  pathLb.clear();
429  costLb = LPAstarLb_->computeShortestPath(pathLb);
430  }
431  }
432 }
std::size_t id_
The id of the motion.
Definition: LazyLBTRRT.h:249
@ UNRECOGNIZED_GOAL_TYPE
The goal is of a type that a planner does not recognize.
void configurePlannerRange(double &range)
Compute what a good length for motion segments is.
Definition: SelfConfig.cpp:225
void getPlannerData(base::PlannerData &data) const override
Get information about the current run of the motion planner. Repeated calls to this function will upd...
Definition: LazyLBTRRT.cpp:324
void setApproximationFactor(double epsilon)
Set the apprimation factor.
Definition: LazyLBTRRT.h:218
Definition of an abstract state.
Definition: State.h:113
void setGoalBias(double goalBias)
Set the goal bias.
Definition: LazyLBTRRT.h:177
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:467
This class contains methods that automatically configure various parameters for motion planning....
Definition: SelfConfig.h:122
bool canSample() const
Return true if maxSampleCount() > 0, since in this case samples can certainly be produced.
double getApproximationFactor() const
Get the apprimation factor.
Definition: LazyLBTRRT.h:384
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
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68
void setRange(double distance)
Set the range the planner is supposed to use.
Definition: LazyLBTRRT.h:193
double getRange() const
Get the range the planner is using.
Definition: LazyLBTRRT.h:199
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
Definition: PlannerData.h:238
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
PlannerSpecs specs_
The specifications of the planner (its capabilities)
Definition: Planner.h:486
@ INVALID_GOAL
Invalid goal state.
bool directed
Flag indicating whether the planner is able to account for the fact that the validity of a motion fro...
Definition: Planner.h:269
A class to store the exit status of Planner::solve()
base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc) override
Function that can solve the motion planning problem. This function can be called multiple times on th...
Definition: LazyLBTRRT.cpp:122
void sampleBiased(const base::GoalSampleableRegion *goal_s, base::State *rstate)
sample with goal biasing
Definition: LazyLBTRRT.cpp:350
Abstract definition of goals.
Definition: Goal.h:126
virtual void sampleGoal(State *st) const =0
Sample a state in the goal region.
bool isSatisfied(const State *st) const override
Equivalent to calling isSatisfied(const State *, double *) with a nullptr second argument.
Definition: GoalRegion.cpp:47
Representation of a motion.
Definition: LazyLBTRRT.h:236
double getGoalBias() const
Get the goal bias the planner is using.
Definition: LazyLBTRRT.h:183
void freeMemory()
Free the memory allocated by this planner.
Definition: LazyLBTRRT.cpp:106
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...
LazyLBTRRT(const base::SpaceInformationPtr &si)
Constructor.
Definition: LazyLBTRRT.cpp:51
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
bool approximateSolutions
Flag indicating whether the planner is able to compute approximate solutions.
Definition: Planner.h:259
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...
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 a goal region that can be sampled.
@ INVALID_START
Invalid start state or no start state specified.
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition: PlannerData.h:122
void setup() override
Perform extra configuration steps, if needed. This call will also issue a call to ompl::base::SpaceIn...
Definition: LazyLBTRRT.cpp:92
void clear() override
Clear all internal datastructures. Planner settings are not affected. Subsequent calls to solve() wil...
Definition: LazyLBTRRT.cpp:77