Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

GenericRRT.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef _GENERIC_RRT_H_
00003 #define _GENERIC_RRT_H_
00004 
00005 #include <vector>
00006 #include <ostream>
00007 #include <cmath>
00008 
00009 #include "Shared/Measures.h"  // AngTwoPi
00010 
00011 using namespace DualCoding;
00012 
00013 //================ RRTNodeBase ================
00014 
00015 //! Base class for RRT nodes used by GenericRRT
00016 /*! Subclasses of RRTNodeBase must provide a NodeValue_t type, a
00017   CollisionChecker class, and the following methods: distance,
00018   generateSample, interpolate, toString.
00019 */
00020 class RRTNodeBase {
00021 public:
00022   RRTNodeBase(unsigned int p) : parent(p) {}
00023   unsigned int parent;
00024   enum Interp_t { COLLISION, APPROACHED, REACHED };
00025   //! returns a value in the range [minVal, maxVal), i.e. inclusive minVal, exclusive maxVal
00026   static float randRange(float minVal, float maxVal) {
00027     return (maxVal - minVal) * (rand()/((float)RAND_MAX)) + minVal;
00028   }
00029   virtual ~RRTNodeBase() {}
00030   virtual std::string toString() const = 0;
00031 };
00032 
00033 //================ AdmissibilityPredicate ================
00034 
00035 //! Base class for admissibility predicates to determine whether a node is admissible.
00036 /*! Subclasses of AdmissibilityPredicateBase must provide the following method:
00037   admissible(NodeValue_t, std::vector<NODE>&, unsigned int);
00038 */
00039 
00040 template<typename NODE>
00041 class AdmissibilityPredicate {
00042 public:
00043   //! constructor
00044   AdmissibilityPredicate() {}
00045   
00046   //! destructor
00047   virtual ~AdmissibilityPredicate() {}
00048   
00049   //! test to see if node is admissible
00050   virtual bool admissible(typename NODE::NodeValue_t q, std::vector<NODE>& tree, unsigned int parent) = 0;
00051 };
00052 
00053 //================ GenericRRT ================
00054 
00055 class GenericRRTBase {
00056 public:
00057   enum PlanPathResultCode {SUCCESS, START_COLLIDES, END_COLLIDES, MAX_ITER};
00058   
00059   //! Relays planner success/failure, as well as obstacles in the case of initial collision states
00060   /*! If a collision occurs, the colliding obstacles will also be included.
00061    Otherwise, they will be set to NULL. */
00062   template <size_t N>
00063   class PlannerResult {
00064   public:
00065     GenericRRTBase::PlanPathResultCode code;
00066     PlannerObstacle<N> *movingObstacle;
00067     PlannerObstacle<N> *collidingObstacle;
00068     
00069     PlannerResult() : code(), movingObstacle(NULL), collidingObstacle(NULL) {}
00070     
00071     ~PlannerResult() {
00072       delete movingObstacle;
00073       delete collidingObstacle;
00074     }
00075 
00076     //! Copy constructor
00077     PlannerResult(const PlannerResult &other) : 
00078       code(other.code), 
00079       movingObstacle(other.movingObstacle ? dynamic_cast<PlannerObstacle<N>*>(other.movingObstacle->clone()) : NULL), 
00080       collidingObstacle(other.collidingObstacle ? dynamic_cast<PlannerObstacle<N>*>(other.collidingObstacle->clone()) : NULL) {}
00081 
00082     //! Assignment operator
00083     PlannerResult& operator=(const PlannerResult &other) {
00084       delete movingObstacle;
00085       delete collidingObstacle;
00086       code = other.code;
00087       movingObstacle = other.movingObstacle ? dynamic_cast<PlannerObstacle<N>*>(other.movingObstacle->clone()) : NULL;
00088       collidingObstacle = other.collidingObstacle ? dynamic_cast<PlannerObstacle<N>*>(other.collidingObstacle->clone()) : NULL;
00089       return *this;
00090     }
00091 
00092   };
00093   
00094   virtual ~GenericRRTBase() {}
00095 
00096   typedef PlannerResult<2> PlannerResult2D;
00097   typedef PlannerResult<3> PlannerResult3D;
00098 };
00099 
00100 //! Generic RRT implementation that makes no assumptions about the nature of the state space; those details are handled by the NODE template argument, which must be a subclass of RRTNodeBase
00101 template<typename NODE, size_t N>
00102 class GenericRRT : public GenericRRTBase {
00103 public:
00104   typedef typename NODE::NodeValue_t NodeValue_t;
00105 protected:
00106 
00107   NodeValue_t lowerLimits;  //!< lower limits on state q
00108 
00109   NodeValue_t upperLimits;  //!< upper limits on state q
00110 
00111   NodeValue_t extendingInterpolationStep;  //!< interpolation steps for q while extending
00112 
00113   NodeValue_t smoothingInterpolationStep;  //!< interpolation steps for q while smoothing
00114 
00115   typename NODE::CollisionChecker *cc;  //!< collision checker
00116 
00117   AdmissibilityPredicate<NODE> *predicate;  //!< admissibility predicate
00118 
00119 public:
00120   //! Constructor; will delete @a collCheck argument when destructed
00121   GenericRRT(typename NODE::CollisionChecker *collCheck, AdmissibilityPredicate<NODE> *predicate=NULL);
00122 
00123   //! Copy constructor
00124   GenericRRT(const GenericRRT &other) :
00125     GenericRRTBase(other), 
00126     lowerLimits(other.lowerLimits), upperLimits(other.upperLimits),
00127     extendingInterpolationStep(other.extendingInterpolationStep),
00128     smoothingInterpolationStep(other.smoothingInterpolationStep),
00129     cc(other.cc), predicate(other.predicate) {}
00130 
00131 
00132   //! Destructor: deletes the collision checker
00133   virtual ~GenericRRT() { delete cc; delete predicate; }
00134 
00135   //! Set lower and upper value limits for each dimension of the space
00136   void setLimits(const NodeValue_t &_lowerLimits, const NodeValue_t &_upperLimits) {
00137     lowerLimits = _lowerLimits;
00138     upperLimits = _upperLimits;
00139   }
00140 
00141   //! Set interpolation step size for each dimension of the space for both extending and smoothing
00142   virtual void setInterpolation(const NodeValue_t &_interpolationStep) {
00143     setSmoothingInterpolation(_interpolationStep);
00144     setExtendingInterpolation(_interpolationStep);
00145   }
00146 
00147   //! Set interpolation step size for each dimension of the space for extending
00148   virtual void setExtendingInterpolation(const NodeValue_t &_extendingInterpolationStep) {
00149     extendingInterpolationStep = _extendingInterpolationStep;
00150   }
00151 
00152   //! Set interpolation step size for each dimension of the space for smoothing
00153   virtual void setSmoothingInterpolation(const NodeValue_t &_smoothingInterpolationStep) {
00154     smoothingInterpolationStep = _smoothingInterpolationStep;
00155   }
00156 
00157   //! Plan a path from start to end
00158   virtual PlannerResult<N> planPath(const NodeValue_t &start,
00159                                     const NodeValue_t &end,
00160                                     unsigned int maxIterations,
00161                                     std::vector<NodeValue_t> *pathResult=NULL,
00162                                     std::vector<NODE> *treeStartResult=NULL,
00163                                     std::vector<NODE> *treeEndResult=NULL);
00164 
00165   typename NODE::CollisionChecker* getCC() { return cc; }
00166 
00167   virtual void dumpTree(const std::vector<NODE> &tree, const std::string &header="");
00168 
00169   void addObstaclesToShapeSpace(ShapeSpace &space, const fmat::Transform &t=fmat::Transform()) const;
00170 
00171 protected:
00172   //! Set up initial search trees
00173   virtual void initialize(const NodeValue_t &start, std::vector<NODE> &treeStartResult,
00174         const NodeValue_t &end, std::vector<NODE> &treeEndResult);
00175   virtual void buildPath(const std::vector<NODE> *treeStart, const std::vector<NODE> *treeEnd, 
00176        std::vector<NodeValue_t> &path);
00177 
00178   void addNode(std::vector<NODE> *tree, const NodeValue_t &q, unsigned int parent);
00179 
00180 private:
00181   unsigned int nearestNode(std::vector<NODE> *tree, const NodeValue_t &target);
00182   RRTNodeBase::Interp_t extend(std::vector<NODE> *tree, const NodeValue_t &q, bool truncate, bool searchingBackwards);
00183 
00184   GenericRRT& operator=(const GenericRRT&);   //!< Don't call this
00185 };
00186 
00187 //================ IMPLEMENTATION ================
00188 
00189 template<typename NODE, size_t N>
00190 GenericRRT<NODE, N>::GenericRRT(typename NODE::CollisionChecker *collCheck,
00191            AdmissibilityPredicate<NODE> *_predicate) :
00192   lowerLimits(), upperLimits(), extendingInterpolationStep(), smoothingInterpolationStep(), cc(collCheck), predicate(_predicate) {}
00193 
00194 template<typename NODE, size_t N>
00195 void GenericRRT<NODE, N>::initialize(const NodeValue_t &start, std::vector<NODE> &treeStart,
00196                                      const NodeValue_t &end, std::vector<NODE> &treeEnd) {
00197   addNode(&treeStart, start, 0);
00198   addNode(&treeEnd, end, 0);
00199 }
00200 
00201 template<typename NODE, size_t N>
00202 GenericRRTBase::PlannerResult<N>
00203 GenericRRT<NODE, N>::planPath(const NodeValue_t &start, const NodeValue_t &end,
00204                               unsigned int maxIterations,
00205                               std::vector<NodeValue_t> *pathResult,
00206                               std::vector<NODE> *treeStartResult, std::vector<NODE> *treeEndResult) {
00207   PlannerResult<N> result;
00208   
00209   // setup state storage
00210   std::vector<NODE> privateTreeStart, privateTreeEnd;
00211   std::vector<NODE> *treeStart = treeStartResult ? treeStartResult : &privateTreeStart;
00212   std::vector<NODE> *treeEnd = treeEndResult ? treeEndResult : &privateTreeEnd;
00213   treeStart->clear();
00214   treeEnd->clear();
00215 
00216   // add initial configs in if we're searching in a circle around the designated endpoint
00217   initialize(start, *treeStart, end, *treeEnd);
00218 
00219   std::vector<NODE> *A = treeStart;
00220   std::vector<NODE> *B = treeEnd;
00221   
00222   // check start/end configurations
00223   if ( cc->collides(start, &result) ) {
00224     result.code = START_COLLIDES;
00225     return result;
00226   }
00227   
00228   // Don't check for end collision if we're using an array of endpoints,
00229   // because the end node is a dummy at the center of the actual endpoint set.
00230   // Check size <= 2 instead of size == 1 because we might need one extra node
00231   // if walking backward.
00232   if ( treeEnd->size() <= 2 && cc->collides(end, &result) ) {
00233     result.code = END_COLLIDES;
00234     return result;
00235   }
00236 
00237   unsigned int iter = 0;
00238   bool searchingBackwards = false;
00239   // first test for direct path from start to end if there is a unique end
00240   if ( treeEnd->size() == 1 && extend(A, end, false, searchingBackwards) == RRTNodeBase::REACHED )
00241     /* we're done */ ;
00242   else
00243     // there is no direct path, so build the tree
00244     while ( iter++ < maxIterations ) {
00245       NodeValue_t qrand;
00246       if ( iter == 1 && (*B)[0].parent < B->size() )  // tree must have at least one non-dummy node
00247         qrand = (*B)[nearestNode(B, start)].q;
00248       else
00249         NODE::generateSample(lowerLimits,upperLimits,qrand);
00250       if ( extend(A, qrand, true, searchingBackwards) != RRTNodeBase::COLLISION ) {
00251         if ( extend(B, A->back().q, true, !searchingBackwards) == RRTNodeBase::REACHED )
00252           break;
00253   swap(A,B);
00254   searchingBackwards = ! searchingBackwards;
00255       }
00256     }
00257 
00258   if ( iter >= maxIterations ) {
00259     // dumpTree(*treeStart,"treeStart: ================");
00260     // dumpTree(*treeEnd,"treeEnd: ================");
00261     result.code = MAX_ITER;
00262     return result;
00263   }
00264 
00265   if ( pathResult != NULL )
00266     buildPath(treeStart, treeEnd, *pathResult);
00267 
00268   result.code = SUCCESS;
00269   return result;
00270 }
00271 
00272 // nearestNode
00273 template<typename NODE, size_t N>
00274 unsigned int GenericRRT<NODE, N>::nearestNode(std::vector<NODE> *tree, const NodeValue_t &target) {
00275   // Root node's parent field contains the index of the first
00276   // matchable node for nearestNode.  This allows us to initialize the
00277   // end tree with special nodes that contribute to a path but can not
00278   // be matched directly.  Use in ShapeSpacePlannerXYTheta.
00279   unsigned int nearest = (*tree)[0].parent;  // index of first matchable node
00280   float dist = (*tree)[nearest].distance(target);
00281   for (unsigned int i = nearest+1; i < tree->size(); i++) {
00282     float d = (*tree)[i].distance(target);
00283     if (d < dist) {
00284       nearest = i;
00285       dist = d;
00286     }
00287   }
00288   return nearest;
00289 }
00290 
00291 // extend
00292 template<typename NODE, size_t N>
00293 RRTNodeBase::Interp_t GenericRRT<NODE, N>::extend(std::vector<NODE> *tree, const NodeValue_t &target,
00294               bool truncate, bool searchingBackwards) {
00295   unsigned int nearest = nearestNode(tree, target);
00296   NodeValue_t reached;
00297   // interpolate towards target
00298   RRTNodeBase::Interp_t result = 
00299     NODE::interpolate((*tree)[nearest].q, target, extendingInterpolationStep, truncate, cc, reached, searchingBackwards);
00300   if ( result != RRTNodeBase::COLLISION ) {
00301     if ( predicate != NULL && predicate->admissible(reached, *tree, nearest) == false )
00302       result = RRTNodeBase::COLLISION;  // pretend inadmissible node causes a collision
00303     else
00304       addNode(tree, reached, nearest);
00305   }
00306   return result;
00307 }
00308 
00309 // addNode
00310 template<typename NODE, size_t N>
00311 void GenericRRT<NODE, N>::addNode(std::vector<NODE> *tree, const NodeValue_t &q, unsigned int parent) {
00312   tree->push_back(NODE(q, parent));
00313 }
00314 
00315 // buildPath
00316 template<typename NODE, size_t N>
00317 void GenericRRT<NODE, N>::buildPath(const std::vector<NODE> *treeStart, const std::vector<NODE> *treeEnd,
00318           std::vector<NodeValue_t> &path) {
00319 
00320   // connection point is last value in each tree
00321   unsigned int n = treeStart->size() - 1;
00322   while (n != 0) {
00323     n = (*treeStart)[n].parent;
00324     path.push_back((*treeStart)[n].q);
00325   }
00326 
00327   std::reverse(path.begin(), path.end());
00328   path.push_back(treeEnd->back().q);
00329 
00330   n = treeEnd->size() - 1;
00331   while (n != 0) {
00332     n = (*treeEnd)[n].parent;
00333     path.push_back((*treeEnd)[n].q);
00334   }
00335 
00336   // smooth path
00337   size_t maxIter = max((size_t)20, 2*path.size());
00338 
00339   for (size_t i = 0; i < maxIter; i++) {
00340     int a = rand() % path.size();
00341     int b = rand() % path.size();
00342     if (a > b)
00343       std::swap(a,b);
00344     else if (a == b)
00345       continue;
00346 
00347     NodeValue_t dummy;
00348     if ( NODE::interpolate(path[a], path[b], smoothingInterpolationStep, false, cc, dummy, true) == RRTNodeBase::REACHED ) {
00349       path.erase(path.begin()+a+1,path.begin()+b);
00350     }
00351   }
00352 
00353   for (size_t i = 0; i+2 < path.size(); i++) {
00354     size_t j;
00355     for (j = i + 2; j < path.size(); j++) {
00356       NodeValue_t dummy;
00357       if ( NODE::interpolate(path[i], path[j], smoothingInterpolationStep, false, cc, dummy, true) != RRTNodeBase::REACHED )
00358   break;
00359     }
00360     if (j > i + 3 && j < path.size())
00361       path.erase(path.begin()+i+1, path.begin()+j-1);
00362   }
00363 }
00364 
00365 template<typename NODE, size_t N>
00366 void GenericRRT<NODE, N>::dumpTree(const std::vector<NODE> &tree, const std::string &header) {
00367   if ( header.size() > 0 )
00368     std::cout << header << std::endl;
00369   for ( size_t i = 0; i < tree.size(); i++ )
00370     if ( tree[i].parent != 0 )
00371       std::cout << " " << tree[i].toString() << "   " << tree[tree[i].parent].toString() << std::endl;
00372 }
00373 
00374 template<typename NODE, size_t N>
00375 void GenericRRT<NODE, N>::addObstaclesToShapeSpace(ShapeSpace &space, const fmat::Transform &t) const {
00376   cc->addObstaclesToShapeSpace(space,t);
00377 }
00378 
00379 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:41 2016 by Doxygen 1.6.3