BiEST.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2015, 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: Ryan Luna */
36 
37 #include "ompl/geometric/planners/est/BiEST.h"
38 #include "ompl/base/goals/GoalSampleableRegion.h"
39 #include "ompl/tools/config/SelfConfig.h"
40 #include <limits>
41 #include <cassert>
42 
43 ompl::geometric::BiEST::BiEST(const base::SpaceInformationPtr &si) : base::Planner(si, "BiEST")
44 {
46  specs_.directed = true;
47 
48  Planner::declareParam<double>("range", this, &BiEST::setRange, &BiEST::getRange, "0.:1.:10000.");
49 }
50 
51 ompl::geometric::BiEST::~BiEST()
52 {
53  freeMemory();
54 }
55 
57 {
58  Planner::setup();
59 
60  tools::SelfConfig sc(si_, getName());
61  sc.configurePlannerRange(maxDistance_);
62 
63  // Make the neighborhood radius smaller than sampling range to
64  // keep probabilities relatively high for rejection sampling
65  nbrhoodRadius_ = maxDistance_ / 3.0;
66 
67  if (!nnStart_)
68  nnStart_.reset(tools::SelfConfig::getDefaultNearestNeighbors<Motion *>(this));
69  if (!nnGoal_)
70  nnGoal_.reset(tools::SelfConfig::getDefaultNearestNeighbors<Motion *>(this));
71  nnStart_->setDistanceFunction([this](const Motion *a, const Motion *b)
72  {
73  return distanceFunction(a, b);
74  });
75  nnGoal_->setDistanceFunction([this](const Motion *a, const Motion *b)
76  {
77  return distanceFunction(a, b);
78  });
79 }
80 
82 {
83  Planner::clear();
84  sampler_.reset();
85  freeMemory();
86  if (nnStart_)
87  nnStart_->clear();
88  if (nnGoal_)
89  nnGoal_->clear();
90 
91  startMotions_.clear();
92  startPdf_.clear();
93 
94  goalMotions_.clear();
95  goalPdf_.clear();
96 
97  connectionPoint_ = std::make_pair<base::State *, base::State *>(nullptr, nullptr);
98 }
99 
101 {
102  for (auto &startMotion : startMotions_)
103  {
104  if (startMotion->state != nullptr)
105  si_->freeState(startMotion->state);
106  delete startMotion;
107  }
108 
109  for (auto &goalMotion : goalMotions_)
110  {
111  if (goalMotion->state != nullptr)
112  si_->freeState(goalMotion->state);
113  delete goalMotion;
114  }
115 }
116 
118 {
119  checkValidity();
120  auto *goal = dynamic_cast<base::GoalSampleableRegion *>(pdef_->getGoal().get());
121 
122  if (goal == nullptr)
123  {
124  OMPL_ERROR("%s: Unknown type of goal", getName().c_str());
126  }
127 
128  std::vector<Motion *> neighbors;
129 
130  while (const base::State *st = pis_.nextStart())
131  {
132  auto *motion = new Motion(si_);
133  si_->copyState(motion->state, st);
134  motion->root = motion->state;
135 
136  nnStart_->nearestR(motion, nbrhoodRadius_, neighbors);
137  addMotion(motion, startMotions_, startPdf_, nnStart_, neighbors);
138  }
139 
140  if (startMotions_.empty())
141  {
142  OMPL_ERROR("%s: There are no valid initial states!", getName().c_str());
144  }
145 
146  if (!goal->couldSample())
147  {
148  OMPL_ERROR("%s: Insufficient states in sampleable goal region", getName().c_str());
150  }
151 
152  if (!sampler_)
153  sampler_ = si_->allocValidStateSampler();
154 
155  OMPL_INFORM("%s: Starting planning with %u states already in datastructure", getName().c_str(),
156  startMotions_.size() + goalMotions_.size());
157 
158  base::State *xstate = si_->allocState();
159  auto *xmotion = new Motion();
160 
161  bool startTree = true;
162  bool solved = false;
164 
165  while (!ptc && !solved)
166  {
167  // Make sure goal tree has at least one state.
168  if (goalMotions_.empty() || pis_.getSampledGoalsCount() < goalMotions_.size() / 2)
169  {
170  const base::State *st = goalMotions_.empty() ? pis_.nextGoal(ptc) : pis_.nextGoal();
171  if (st != nullptr)
172  {
173  auto *motion = new Motion(si_);
174  si_->copyState(motion->state, st);
175  motion->root = motion->state;
176 
177  nnGoal_->nearestR(motion, nbrhoodRadius_, neighbors);
178  addMotion(motion, goalMotions_, goalPdf_, nnGoal_, neighbors);
179  }
180 
181  if (goalMotions_.empty())
182  {
183  OMPL_ERROR("%s: Unable to sample any valid states for goal tree", getName().c_str());
185  break;
186  }
187  }
188 
189  // Pointers to the tree structure we are expanding
190  std::vector<Motion *> &motions = startTree ? startMotions_ : goalMotions_;
191  PDF<Motion *> &pdf = startTree ? startPdf_ : goalPdf_;
192  std::shared_ptr<NearestNeighbors<Motion *>> nn = startTree ? nnStart_ : nnGoal_;
193 
194  // Select a state to expand from
195  Motion *existing = pdf.sample(rng_.uniform01());
196  assert(existing);
197 
198  // Sample a state in the neighborhood
199  if (!sampler_->sampleNear(xstate, existing->state, maxDistance_))
200  continue;
201 
202  // Compute neighborhood of candidate state
203  xmotion->state = xstate;
204  nn->nearestR(xmotion, nbrhoodRadius_, neighbors);
205 
206  // reject state with probability proportional to neighborhood density
207  if (!neighbors.empty() )
208  {
209  double p = 1.0 - (1.0 / neighbors.size());
210  if (rng_.uniform01() < p)
211  continue;
212  }
213 
214  // Is motion good?
215  if (si_->checkMotion(existing->state, xstate))
216  {
217  // create a motion
218  auto *motion = new Motion(si_);
219  si_->copyState(motion->state, xstate);
220  motion->parent = existing;
221  motion->root = existing->root;
222 
223  // add it to everything
224  addMotion(motion, motions, pdf, nn, neighbors);
225 
226  // try to connect this state to the other tree
227  // Get all states in the other tree within a maxDistance_ ball (bigger than "neighborhood" ball)
228  startTree ? nnGoal_->nearestR(motion, maxDistance_, neighbors) :
229  nnStart_->nearestR(motion, maxDistance_, neighbors);
230  for (size_t i = 0; i < neighbors.size() && !solved; ++i)
231  {
232  if (goal->isStartGoalPairValid(motion->root, neighbors[i]->root) &&
233  si_->checkMotion(motion->state, neighbors[i]->state)) // win! solution found.
234  {
235  connectionPoint_ = std::make_pair(motion->state, neighbors[i]->state);
236 
237  Motion *startMotion = startTree ? motion : neighbors[i];
238  Motion *goalMotion = startTree ? neighbors[i] : motion;
239 
240  Motion *solution = startMotion;
241  std::vector<Motion *> mpath1;
242  while (solution != nullptr)
243  {
244  mpath1.push_back(solution);
245  solution = solution->parent;
246  }
247 
248  solution = goalMotion;
249  std::vector<Motion *> mpath2;
250  while (solution != nullptr)
251  {
252  mpath2.push_back(solution);
253  solution = solution->parent;
254  }
255 
256  auto path(std::make_shared<PathGeometric>(si_));
257  path->getStates().reserve(mpath1.size() + mpath2.size());
258  for (int i = mpath1.size() - 1; i >= 0; --i)
259  path->append(mpath1[i]->state);
260  for (auto &i : mpath2)
261  path->append(i->state);
262 
263  pdef_->addSolutionPath(path, false, 0.0, getName());
264  solved = true;
265  }
266  }
267  }
268 
269  // swap trees for next iteration
270  startTree = !startTree;
271  }
272 
273  si_->freeState(xstate);
274  delete xmotion;
275 
276  OMPL_INFORM("%s: Created %u states (%u start + %u goal)", getName().c_str(),
277  startMotions_.size() + goalMotions_.size(), startMotions_.size(), goalMotions_.size());
278  return solved ? base::PlannerStatus::EXACT_SOLUTION : status;
279 }
280 
281 void ompl::geometric::BiEST::addMotion(Motion *motion, std::vector<Motion *> &motions, PDF<Motion *> &pdf,
282  const std::shared_ptr<NearestNeighbors<Motion *>> &nn,
283  const std::vector<Motion *> &neighbors)
284 {
285  // Updating neighborhood size counts
286  for (auto neighbor : neighbors)
287  {
288  PDF<Motion *>::Element *elem = neighbor->element;
289  double w = pdf.getWeight(elem);
290  pdf.update(elem, w / (w + 1.));
291  }
292 
293  motion->element = pdf.add(motion, 1. / (neighbors.size() + 1.)); // +1 for self
294  motions.push_back(motion);
295  nn->add(motion);
296 }
297 
299 {
300  Planner::getPlannerData(data);
301 
302  for (auto startMotion : startMotions_)
303  {
304  if (startMotion->parent == nullptr)
305  data.addStartVertex(base::PlannerDataVertex(startMotion->state, 1));
306  else
307  data.addEdge(base::PlannerDataVertex(startMotion->parent->state, 1),
308  base::PlannerDataVertex(startMotion->state, 1));
309  }
310 
311  for (auto goalMotion : goalMotions_)
312  {
313  if (goalMotion->parent == nullptr)
314  data.addGoalVertex(base::PlannerDataVertex(goalMotion->state, 2));
315  else
316  // The edges in the goal tree are reversed to be consistent with start tree
317  data.addEdge(base::PlannerDataVertex(goalMotion->state, 2),
318  base::PlannerDataVertex(goalMotion->parent->state, 2));
319  }
320 
321  // Add the edge connecting the two trees
322  data.addEdge(data.vertexIndex(connectionPoint_.first), data.vertexIndex(connectionPoint_.second));
323 }
_T & sample(double r) const
Returns a piece of data from the PDF according to the input sampling value, which must be between 0 a...
Definition: PDF.h:196
Motion * parent
The parent motion in the exploration tree.
Definition: BiEST.h:216
Element * add(const _T &d, const double w)
Adds a piece of data with a given weight to the PDF. Returns a corresponding Element,...
Definition: PDF.h:161
@ 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
base::State * state
The state contained by the motion.
Definition: BiEST.h:213
void clear() override
Clear all internal datastructures. Planner settings are not affected. Subsequent calls to solve() wil...
Definition: BiEST.cpp:81
Definition of an abstract state.
Definition: State.h:113
This class contains methods that automatically configure various parameters for motion planning....
Definition: SelfConfig.h:122
void setup() override
Perform extra configuration steps, if needed. This call will also issue a call to ompl::base::SpaceIn...
Definition: BiEST.cpp:56
void freeMemory()
Free the memory allocated by this planner.
Definition: BiEST.cpp:100
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68
@ TIMEOUT
The planner failed to find a solution.
A container that supports probabilistic sampling over weighted data.
Definition: PDF.h:80
PDF< Motion * >::Element * element
A pointer to the corresponding element in the probability distribution function.
Definition: BiEST.h:219
BiEST(const base::SpaceInformationPtr &si)
Constructor.
Definition: BiEST.cpp:43
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.
unsigned int vertexIndex(const PlannerDataVertex &v) const
Return the index for the vertex associated with the given data. INVALID_INDEX is returned if this ver...
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
double getWeight(const Element *elem) const
Returns the current weight of the given Element.
Definition: PDF.h:235
A class to store the exit status of Planner::solve()
The definition of a motion.
Definition: BiEST.h:199
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: BiEST.cpp:117
void update(Element *elem, const double w)
Updates the data in the given Element with a new weight value.
Definition: PDF.h:219
const base::State * root
The root node of the tree this motion is in.
Definition: BiEST.h:222
@ GOAL_SAMPLEABLE_REGION
This bit is set if casting to sampleable goal regions (ompl::base::GoalSampleableRegion) is possible.
Definition: GoalTypes.h:152
double getRange() const
Get the range the planner is using.
Definition: BiEST.h:188
@ EXACT_SOLUTION
The planner found an exact solution.
StatusType
The possible values of the status returned by a planner.
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...
void addMotion(Motion *motion, std::vector< Motion * > &motions, PDF< Motion * > &pdf, const std::shared_ptr< NearestNeighbors< Motion * >> &nn, const std::vector< Motion * > &neighbors)
Add a motion to the exploration tree.
Definition: BiEST.cpp:281
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
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: BiEST.cpp:298
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.
A class that will hold data contained in the PDF.
Definition: PDF.h:116
@ 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
GoalType recognizedGoal
The type of goal specification the planner can use.
Definition: Planner.h:253
void setRange(double distance)
Set the range the planner is supposed to use.
Definition: BiEST.h:178