PathGeometric.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2008, Willow Garage, Inc.
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 Willow Garage 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/geometric/PathGeometric.h"
38 #include "ompl/base/samplers/UniformValidStateSampler.h"
39 #include "ompl/base/OptimizationObjective.h"
40 #include "ompl/base/ScopedState.h"
41 #include <algorithm>
42 #include <cmath>
43 #include <limits>
44 #include <boost/math/constants/constants.hpp>
45 
47 {
48  copyFrom(path);
49 }
50 
52 {
53  states_.resize(1);
54  states_[0] = si_->cloneState(state);
55 }
56 
58 {
59  states_.resize(2);
60  states_[0] = si_->cloneState(state1);
61  states_[1] = si_->cloneState(state2);
62 }
63 
65 {
66  if (this != &other)
67  {
68  freeMemory();
69  si_ = other.si_;
70  copyFrom(other);
71  }
72  return *this;
73 }
74 
76 {
77  states_.resize(other.states_.size());
78  for (unsigned int i = 0 ; i < states_.size() ; ++i)
79  states_[i] = si_->cloneState(other.states_[i]);
80 }
81 
83 {
84  for (unsigned int i = 0 ; i < states_.size() ; ++i)
85  si_->freeState(states_[i]);
86 }
87 
89 {
90  if (states_.empty()) return opt->identityCost();
91  // Compute path cost by accumulating the cost along the path
92  base::Cost cost(opt->initialCost(states_.front()));
93  for (std::size_t i = 1; i < states_.size(); ++i)
94  cost = opt->combineCosts(cost, opt->motionCost(states_[i - 1], states_[i]));
95  cost = opt->combineCosts(cost, opt->terminalCost(states_.back()));
96  return cost;
97 }
98 
100 {
101  double L = 0.0;
102  for (unsigned int i = 1 ; i < states_.size() ; ++i)
103  L += si_->distance(states_[i-1], states_[i]);
104  return L;
105 }
106 
108 {
109  double c = 0.0;
110  for (unsigned int i = 0 ; i < states_.size() ; ++i)
111  c += si_->getStateValidityChecker()->clearance(states_[i]);
112  if (states_.empty())
113  c = std::numeric_limits<double>::infinity();
114  else
115  c /= (double)states_.size();
116  return c;
117 }
118 
120 {
121  double s = 0.0;
122  if (states_.size() > 2)
123  {
124  double a = si_->distance(states_[0], states_[1]);
125  for (unsigned int i = 2 ; i < states_.size() ; ++i)
126  {
127  // view the path as a sequence of segments, and look at the triangles it forms:
128  // s1
129  // /\ s4
130  // a / \ b |
131  // / \ |
132  // /......\_______|
133  // s0 c s2 s3
134  //
135  // use Pythagoras generalized theorem to find the cos of the angle between segments a and b
136  double b = si_->distance(states_[i-1], states_[i]);
137  double c = si_->distance(states_[i-2], states_[i]);
138  double acosValue = (a*a + b*b - c*c) / (2.0*a*b);
139 
140  if (acosValue > -1.0 && acosValue < 1.0)
141  {
142  // the smoothness is actually the outside angle of the one we compute
143  double angle = (boost::math::constants::pi<double>() - acos(acosValue));
144 
145  // and we normalize by the length of the segments
146  double k = 2.0 * angle / (a + b);
147  s += k * k;
148  }
149  a = b;
150  }
151  }
152  return s;
153 }
154 
156 {
157  // make sure state validity checker is set
158  if (!si_->isSetup())
159  si_->setup();
160 
161  bool result = true;
162  if (states_.size() > 0)
163  {
164  if (si_->isValid(states_[0]))
165  {
166  int last = states_.size() - 1;
167  for (int j = 0 ; result && j < last ; ++j)
168  if (!si_->checkMotion(states_[j], states_[j + 1]))
169  result = false;
170  }
171  else
172  result = false;
173  }
174 
175  return result;
176 }
177 
178 void ompl::geometric::PathGeometric::print(std::ostream &out) const
179 {
180  out << "Geometric path with " << states_.size() << " states" << std::endl;
181  for (unsigned int i = 0 ; i < states_.size() ; ++i)
182  si_->printState(states_[i], out);
183  out << std::endl;
184 }
186 {
187  const base::StateSpace* space(si_->getStateSpace().get());
188  std::vector<double> reals;
189  for (unsigned int i = 0 ; i < states_.size() ; ++i)
190  {
191  space->copyToReals(reals, states_[i]);
192  std::copy(reals.begin(), reals.end(), std::ostream_iterator<double>(out, " "));
193  out << std::endl;
194  }
195  out << std::endl;
196 }
197 
198 std::pair<bool, bool> ompl::geometric::PathGeometric::checkAndRepair(unsigned int attempts)
199 {
200  if (states_.empty())
201  return std::make_pair(true, true);
202  if (states_.size() == 1)
203  {
204  bool result = si_->isValid(states_[0]);
205  return std::make_pair(result, result);
206  }
207 
208  // a path with invalid endpoints cannot be fixed; planners should not return such paths anyway
209  const int n1 = states_.size();
210  if (!si_->isValid(states_[0]) || !si_->isValid(states_[n1 - 1]))
211  return std::make_pair(false, false);
212 
213  base::State *temp = nullptr;
214  base::UniformValidStateSampler *uvss = nullptr;
215  bool result = true;
216 
217  for (int i = 1 ; i < n1 ; ++i)
218  if (!si_->checkMotion(states_[i-1], states_[i]))
219  {
220  // we now compute a state around which to sample
221  if (!temp)
222  temp = si_->allocState();
223  if (!uvss)
224  {
225  uvss = new base::UniformValidStateSampler(si_.get());
226  uvss->setNrAttempts(attempts);
227  }
228 
229  // and a radius of sampling around that state
230  double radius = 0.0;
231 
232  if (si_->isValid(states_[i]))
233  {
234  si_->copyState(temp, states_[i]);
235  radius = si_->distance(states_[i-1], states_[i]);
236  }
237  else
238  {
239  unsigned int nextValid = n1;
240  for (int j = i + 1 ; j < n1 ; ++j)
241  if (si_->isValid(states_[j]))
242  {
243  nextValid = j;
244  break;
245  }
246  // we know nextValid will be initialised because n1 is certainly valid.
247  si_->getStateSpace()->interpolate(states_[i - 1], states_[nextValid], 0.5, temp);
248  radius = std::max(si_->distance(states_[i-1], temp), si_->distance(states_[i-1], states_[i]));
249  }
250 
251  bool success = false;
252 
253  for (unsigned int a = 0 ; a < attempts ; ++a)
254  if (uvss->sampleNear(states_[i], temp, radius))
255  {
256  if (si_->checkMotion(states_[i-1], states_[i]))
257  {
258  success = true;
259  break;
260  }
261  }
262  else
263  break;
264  if (!success)
265  {
266  result = false;
267  break;
268  }
269  }
270 
271  // free potentially allocated memory
272  if (temp)
273  si_->freeState(temp);
274  bool originalValid = uvss == nullptr;
275  if (uvss)
276  delete uvss;
277 
278  return std::make_pair(originalValid, result);
279 }
280 
282 {
283  if (states_.size() < 2)
284  return;
285  std::vector<base::State*> newStates(1, states_[0]);
286  for (unsigned int i = 1 ; i < states_.size() ; ++i)
287  {
288  base::State *temp = si_->allocState();
289  si_->getStateSpace()->interpolate(newStates.back(), states_[i], 0.5, temp);
290  newStates.push_back(temp);
291  newStates.push_back(states_[i]);
292  }
293  states_.swap(newStates);
294 }
295 
297 {
298  unsigned int n = 0;
299  const int n1 = states_.size() - 1;
300  for (int i = 0 ; i < n1 ; ++i)
301  n += si_->getStateSpace()->validSegmentCount(states_[i], states_[i + 1]);
302  interpolate(n);
303 }
304 
305 void ompl::geometric::PathGeometric::interpolate(unsigned int requestCount)
306 {
307  if (requestCount < states_.size() || states_.size() < 2)
308  return;
309 
310  unsigned int count = requestCount;
311 
312  // the remaining length of the path we need to add states along
313  double remainingLength = length();
314 
315  // the new array of states this path will have
316  std::vector<base::State*> newStates;
317  const int n1 = states_.size() - 1;
318 
319  for (int i = 0 ; i < n1 ; ++i)
320  {
321  base::State *s1 = states_[i];
322  base::State *s2 = states_[i + 1];
323 
324  newStates.push_back(s1);
325 
326  // the maximum number of states that can be added on the current motion (without its endpoints)
327  // such that we can at least fit the remaining states
328  int maxNStates = count + i - states_.size();
329 
330  if (maxNStates > 0)
331  {
332  // compute an approximate number of states the following segment needs to contain; this includes endpoints
333  double segmentLength = si_->distance(s1, s2);
334  int ns = i + 1 == n1 ? maxNStates + 2 : (int)floor(0.5 + (double)count * segmentLength / remainingLength) + 1;
335 
336  // if more than endpoints are needed
337  if (ns > 2)
338  {
339  ns -= 2; // subtract endpoints
340 
341  // make sure we don't add too many states
342  if (ns > maxNStates)
343  ns = maxNStates;
344 
345  // compute intermediate states
346  std::vector<base::State*> block;
347  unsigned int ans = si_->getMotionStates(s1, s2, block, ns, false, true);
348  // sanity checks
349  if ((int)ans != ns || block.size() != ans)
350  throw Exception("Internal error in path interpolation. Incorrect number of intermediate states. Please contact the developers.");
351 
352  newStates.insert(newStates.end(), block.begin(), block.end());
353  }
354  else
355  ns = 0;
356 
357  // update what remains to be done
358  count -= (ns + 1);
359  remainingLength -= segmentLength;
360  }
361  else
362  count--;
363  }
364 
365  // add the last state
366  newStates.push_back(states_[n1]);
367  states_.swap(newStates);
368  if (requestCount != states_.size())
369  throw Exception("Internal error in path interpolation. This should never happen. Please contact the developers.");
370 }
371 
373 {
374  std::reverse(states_.begin(), states_.end());
375 }
376 
378 {
379  freeMemory();
380  states_.resize(2);
381  states_[0] = si_->allocState();
382  states_[1] = si_->allocState();
383  base::StateSamplerPtr ss = si_->allocStateSampler();
384  ss->sampleUniform(states_[0]);
385  ss->sampleUniform(states_[1]);
386 }
387 
389 {
390  freeMemory();
391  states_.resize(2);
392  states_[0] = si_->allocState();
393  states_[1] = si_->allocState();
395  uvss->setNrAttempts(attempts);
396  bool ok = false;
397  for (unsigned int i = 0 ; i < attempts ; ++i)
398  {
399  if (uvss->sample(states_[0]) && uvss->sample(states_[1]))
400  if (si_->checkMotion(states_[0], states_[1]))
401  {
402  ok = true;
403  break;
404  }
405  }
406  delete uvss;
407  if (!ok)
408  {
409  freeMemory();
410  states_.clear();
411  }
412  return ok;
413 }
414 
415 void ompl::geometric::PathGeometric::overlay(const PathGeometric &over, unsigned int startIndex)
416 {
417  if (startIndex > states_.size())
418  throw Exception("Index on path is out of bounds");
419  const base::StateSpacePtr &sm = over.si_->getStateSpace();
420  const base::StateSpacePtr &dm = si_->getStateSpace();
421  bool copy = !states_.empty();
422  for (unsigned int i = 0, j = startIndex ; i < over.states_.size() ; ++i, ++j)
423  {
424  if (j == states_.size())
425  {
426  base::State *s = si_->allocState();
427  if (copy)
428  si_->copyState(s, states_.back());
429  states_.push_back(s);
430  }
431 
432  copyStateData(dm, states_[j], sm, over.states_[i]);
433  }
434 }
435 
437 {
438  states_.push_back(si_->cloneState(state));
439 }
440 
442 {
443  if (path.si_->getStateSpace()->getName() == si_->getStateSpace()->getName())
444  {
445  PathGeometric copy(path);
446  states_.insert(states_.end(), copy.states_.begin(), copy.states_.end());
447  copy.states_.clear();
448  }
449  else
450  overlay(path, states_.size());
451 }
452 
454 {
455  states_.insert(states_.begin(), si_->cloneState(state));
456 }
457 
459 {
460  int index = getClosestIndex(state);
461  if (index > 0)
462  {
463  if ((std::size_t)(index + 1) < states_.size())
464  {
465  double b = si_->distance(state, states_[index-1]);
466  double a = si_->distance(state, states_[index+1]);
467  if (b > a)
468  ++index;
469  }
470  for (int i = 0 ; i < index ; ++i)
471  si_->freeState(states_[i]);
472  states_.erase(states_.begin(), states_.begin() + index);
473  }
474 }
475 
477 {
478  int index = getClosestIndex(state);
479  if (index >= 0)
480  {
481  if (index > 0 && (std::size_t)(index + 1) < states_.size())
482  {
483  double b = si_->distance(state, states_[index-1]);
484  double a = si_->distance(state, states_[index+1]);
485  if (b < a)
486  --index;
487  }
488  if ((std::size_t)(index + 1) < states_.size())
489  {
490  for (std::size_t i = index + 1 ; i < states_.size() ; ++i)
491  si_->freeState(states_[i]);
492  states_.resize(index + 1);
493  }
494  }
495 }
496 
498 {
499  if (states_.empty())
500  return -1;
501 
502  int index = 0;
503  double min_d = si_->distance(states_[0], state);
504  for (std::size_t i = 1 ; i < states_.size() ; ++i)
505  {
506  double d = si_->distance(states_[i], state);
507  if (d < min_d)
508  {
509  min_d = d;
510  index = i;
511  }
512  }
513  return index;
514 }
virtual bool sample(State *state)
Sample a state. Return false in case of failure.
void keepAfter(const base::State *state)
Keep the part of the path that is after state (getClosestIndex() is used to find out which way-point ...
void freeMemory()
Free the memory corresponding to the states on this path.
void interpolate()
Insert a number of states in a path so that the path is made up of (approximately) the states checked...
A shared pointer wrapper for ompl::base::StateSpace.
A shared pointer wrapper for ompl::base::StateSampler.
void copyFrom(const PathGeometric &other)
Copy data to this path from another path instance.
void overlay(const PathGeometric &over, unsigned int startIndex=0)
Overlay the path over on top of the current path. States are added to the current path if needed (by ...
AdvancedStateCopyOperation copyStateData(const StateSpacePtr &destS, State *dest, const StateSpacePtr &sourceS, const State *source)
Copy data from source (state from space sourceS) to dest (state from space destS) on a component by c...
void random()
Set this path to a random segment.
virtual void printAsMatrix(std::ostream &out) const
Print the path as a real-valued matrix where the i-th row represents the i-th state along the path...
void append(const base::State *state)
Append state to the end of this path. The memory for state is copied.
PathGeometric(const base::SpaceInformationPtr &si)
Construct a path instance for a given space information.
Definition: PathGeometric.h:65
virtual double length() const
Compute the length of a geometric path (sum of lengths of segments that make up the path) ...
A state sampler that only samples valid states, uniformly.
std::pair< bool, bool > checkAndRepair(unsigned int attempts)
Check if the path is valid. If it is not, attempts are made to fix the path by sampling around invali...
SpaceInformationPtr si_
The space information this path is part of.
Definition: Path.h:126
virtual base::Cost cost(const base::OptimizationObjectivePtr &obj) const
The sum of the costs for the sequence of segments that make up the path, computed using OptimizationO...
double clearance() const
Compute the clearance of the way-points along the path (no interpolation is performed). Detailed formula follows.
bool randomValid(unsigned int attempts)
Set this path to a random valid segment. Sample attempts times for valid segments. Returns true on success.
int getClosestIndex(const base::State *state) const
Get the index of the way-point along the path that is closest to state. Returns -1 for an empty path...
void reverse()
Reverse the path.
A shared pointer wrapper for ompl::base::SpaceInformation.
void prepend(const base::State *state)
Prepend state to the start of this path. The memory for state is copied.
Representation of a space in which planning can be performed. Topology specific sampling, interpolation and distance are defined.
Definition: StateSpace.h:72
Definition of an abstract state.
Definition: State.h:50
PathGeometric & operator=(const PathGeometric &other)
Assignment operator.
The exception type for ompl.
Definition: Exception.h:47
A shared pointer wrapper for ompl::base::OptimizationObjective.
double smoothness() const
Compute a notion of smoothness for this path. The closer the value is to 0, the smoother the path...
void keepBefore(const base::State *state)
Keep the part of the path that is before state (getClosestIndex() is used to find out which way-point...
void subdivide()
Add a state at the middle of each segment.
std::vector< base::State * > states_
The list of states that make up the path.
Definition of a geometric path.
Definition: PathGeometric.h:60
virtual bool check() const
Check if the path is valid.
void copyToReals(std::vector< double > &reals, const State *source) const
Copy all the real values from a state source to the array reals using getValueAddressAtLocation() ...
Definition: StateSpace.cpp:324
virtual bool sampleNear(State *state, const State *near, const double distance)
Sample a state near another, within specified distance. Return false, in case of failure.
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:47
virtual void print(std::ostream &out) const
Print the path to a stream.
void setNrAttempts(unsigned int attempts)
Finding a valid sample usually requires performing multiple attempts. This call allows setting the nu...