DART  6.10.1
PathPlanner.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011-2021, The DART development contributors
3  * All rights reserved.
4  *
5  * The list of contributors can be found at:
6  * https://github.com/dartsim/dart/blob/master/LICENSE
7  *
8  * This file is provided under the following "BSD-style" License:
9  * Redistribution and use in source and binary forms, with or
10  * without modification, are permitted provided that the following
11  * conditions are met:
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
42 #ifndef DART_PLANNING_PATHPLANNER_HPP_
43 #define DART_PLANNING_PATHPLANNER_HPP_
44 
45 #include <cstdio>
46 #include <iostream>
47 #include <limits>
48 #include <list>
49 #include <vector>
50 #include <Eigen/Core>
52 #include "dart/planning/RRT.hpp"
54 
55 namespace dart {
56 namespace planning {
57 
58 /* *********************************************************************************************
59  */
61 template <class R = RRT>
63 {
64 public:
65  bool connect;
68  double
70  double
72  std::size_t
76 
77  // NOTE: It is useful to keep the rrts around after planning for reuse,
78  // analysis, and etc.
79  R* start_rrt;
80  R* goal_rrt;
81 
82 public:
84  PathPlanner() : world(nullptr)
85  {
86  }
87 
91  bool bidirectional_ = true,
92  bool connect_ = true,
93  double stepSize_ = 0.1,
94  std::size_t maxNodes_ = 1e6,
95  double goalBias_ = 0.3)
96  : world(&world),
97  bidirectional(bidirectional_),
98  connect(connect_),
99  stepSize(stepSize_),
100  maxNodes(maxNodes_),
101  goalBias(goalBias_)
102  {
103  }
104 
106  virtual ~PathPlanner()
107  {
108  }
109 
111  bool planPath(
112  dynamics::Skeleton* robot,
113  const std::vector<int>& dofs,
114  const Eigen::VectorXd& start,
115  const Eigen::VectorXd& goal,
116  std::list<Eigen::VectorXd>& path)
117  {
118  std::vector<Eigen::VectorXd> startVector, goalVector;
119  startVector.push_back(start);
120  goalVector.push_back(goal);
121  return planPath(robot, dofs, startVector, goalVector, path);
122  }
123 
125  bool planPath(
126  dynamics::Skeleton* robot,
127  const std::vector<std::size_t>& dofs,
128  const std::vector<Eigen::VectorXd>& start,
129  const std::vector<Eigen::VectorXd>& goal,
130  std::list<Eigen::VectorXd>& path);
131 
132 private:
134  bool planSingleTreeRrt(
135  dynamics::Skeleton* robot,
136  const std::vector<int>& dofs,
137  const std::vector<Eigen::VectorXd>& start,
138  const Eigen::VectorXd& goal,
139  std::list<Eigen::VectorXd>& path);
140 
149  dynamics::Skeleton* robot,
150  const std::vector<int>& dofs,
151  const std::vector<Eigen::VectorXd>& start,
152  const std::vector<Eigen::VectorXd>& goal,
153  std::list<Eigen::VectorXd>& path);
154 };
155 
156 /* *********************************************************************************************
157  */
158 template <class R>
160  dynamics::Skeleton* robot,
161  const std::vector<std::size_t>& dofs,
162  const std::vector<Eigen::VectorXd>& start,
163  const std::vector<Eigen::VectorXd>& goal,
164  std::list<Eigen::VectorXd>& path)
165 {
166 
167  Eigen::VectorXd savedConfiguration = robot->getPositions(dofs);
168 
169  // ====================================================================
170  // Check for collisions in the start and goal configurations
171 
172  // Sift through the possible start configurations and eliminate those that are
173  // in collision
174  std::vector<Eigen::VectorXd> feasibleStart;
175  for (unsigned int i = 0; i < start.size(); i++)
176  {
177  robot->setPositions(dofs, start[i]);
178  if (!world->checkCollision())
179  feasibleStart.push_back(start[i]);
180  }
181 
182  // Return false if there are no feasible start configurations
183  if (feasibleStart.empty())
184  {
185  printf("WARNING: PathPlanner: Feasible start points are empty!\n");
186  return false;
187  }
188 
189  // Sift through the possible goal configurations and eliminate those that are
190  // in collision
191  std::vector<Eigen::VectorXd> feasibleGoal;
192  for (unsigned int i = 0; i < goal.size(); i++)
193  {
194  robot->setPositions(dofs, goal[i]);
195  if (!world->checkCollision())
196  feasibleGoal.push_back(goal[i]);
197  }
198 
199  // Return false if there are no feasible goal configurations
200  if (feasibleGoal.empty())
201  {
202  printf("WARNING: PathPlanner: Feasible goal points are empty!\n");
203  return false;
204  }
205 
206  // ====================================================================
207  // Make the correct RRT algorithm for the given method
208 
209  // Direct the search towards single or bidirectional
210  bool result = false;
211  if (bidirectional)
212  result
213  = planBidirectionalRrt(robot, dofs, feasibleStart, feasibleGoal, path);
214  else
215  {
216  if (feasibleGoal.size() > 1)
217  fprintf(stderr, "WARNING: planPath is using ONLY the first goal!\n");
218  result = planSingleTreeRrt(
219  robot, dofs, feasibleStart, feasibleGoal.front(), path);
220  }
221 
222  // Restore previous robot configuration
223  robot->setPositions(dofs, savedConfiguration);
224 
225  return result;
226 }
227 
228 /* *********************************************************************************************
229  */
230 template <class R>
232  dynamics::Skeleton* robot,
233  const std::vector<int>& dofs,
234  const std::vector<Eigen::VectorXd>& start,
235  const Eigen::VectorXd& goal,
236  std::list<Eigen::VectorXd>& path)
237 {
238 
239  const bool debug = false;
240 
241  // Initialize the RRT
242  start_rrt = new R(world, robot, dofs, start, stepSize);
243 
244  // Expand the tree until the goal is reached or the max # nodes is passed
245  typename R::StepResult result = R::STEP_PROGRESS;
246  double smallestGap = std::numeric_limits<double>::infinity();
247  std::size_t numNodes = start_rrt->getSize();
248  while (numNodes <= maxNodes)
249  {
250 
251  // Get the target node based on the bias
252  Eigen::VectorXd target;
253  double randomValue = ((double)rand()) / RAND_MAX;
254  if (randomValue < goalBias)
255  target = goal;
256  else
257  target = start_rrt->getRandomConfig();
258 
259  // Based on the method, either attempt to connect to the target directly or
260  // take a small step
261  if (connect)
262  start_rrt->connect(target);
263  else
264  start_rrt->tryStep(target);
265 
266  // Check if the goal is reached and create the path, if so
267  double gap = start_rrt->getGap(goal);
268  if (gap < stepSize)
269  {
270  if (debug)
271  std::cout << "Returning true, reached the goal" << std::endl;
272  start_rrt->tracePath(start_rrt->activeNode, path);
273  return true;
274  }
275 
276  // Update the number of nodes
277  numNodes = start_rrt->getSize();
278  }
279 
280  if (debug)
281  printf("numNodes: %lu\n", numNodes);
282 
283  // Maximum # of iterations are reached and path is not found - failed.
284  return false;
285 }
286 
287 /* *********************************************************************************************
288  */
289 template <class R>
291  dynamics::Skeleton* robot,
292  const std::vector<int>& dofs,
293  const std::vector<Eigen::VectorXd>& start,
294  const std::vector<Eigen::VectorXd>& goal,
295  std::list<Eigen::VectorXd>& path)
296 {
297 
298  const bool debug = false;
299 
300  // Initialize both the start and goal RRTs.
301  // NOTE: We use the pointers for the RRTs to swap their roles in extending
302  // towards a target (random or goal) node.
303  start_rrt = new R(world, robot, dofs, start, stepSize);
304  goal_rrt = new R(world, robot, dofs, goal, stepSize);
305  R* rrt1 = start_rrt;
306  R* rrt2 = goal_rrt;
307 
308  // Expand the tree until the trees meet or the max # nodes is passed
309  double smallestGap = std::numeric_limits<double>::infinity();
310  std::size_t numNodes = rrt1->getSize() + rrt2->getSize();
311  while (numNodes < maxNodes)
312  {
313 
314  // Swap the roles of the two RRTs. Remember, the first rrt reaches out to a
315  // target node and creates a new node and the second rrt reaches to _the new
316  // node_.
317  R* temp = rrt1;
318  rrt1 = rrt2;
319  rrt2 = temp;
320 
321  // Get the target node based on the bias
322  Eigen::VectorXd target;
323  double randomValue = ((double)rand()) / RAND_MAX;
324  if (randomValue < goalBias)
325  target = goal[0];
326  else
327  target = rrt1->getRandomConfig();
328 
329  // Based on the method, rrt1 either attempt to connect to the target
330  // directly or takes a step
331  if (connect)
332  rrt1->connect(target);
333  else
334  rrt1->tryStep(target);
335 
336  // rrt2 uses the last added node of rrt1 as a target and reaches out to it
337  // (connect or step) NOTE: If a node was not added, the nearest neighbor to
338  // the random node in rrt1 is used. NOTE: connect(x) and tryStep(x)
339  // functions return true if rrt2 can add the given node in the tree. In this
340  // case, this would imply that the two trees meet.
341  bool treesMet = false;
342  const Eigen::VectorXd& rrt2target = *(rrt1->configVector[rrt1->activeNode]);
343  if (connect)
344  treesMet = rrt2->connect(rrt2target);
345  else
346  treesMet = (rrt2->tryStep(rrt2target) == R::STEP_REACHED);
347 
348  // Check if the trees have met and create the path, if so.
349  if (treesMet)
350  {
351  start_rrt->tracePath(start_rrt->activeNode, path);
352  goal_rrt->tracePath(goal_rrt->activeNode, path, true);
353  return true;
354  }
355 
356  // Update the number of nodes in the two trees
357  numNodes = rrt1->getSize() + rrt2->getSize();
358 
359  // Print the gap between the trees in debug mode
360  if (debug)
361  {
362  double gap = rrt2->getGap(*(rrt1->configVector[rrt1->activeNode]));
363  if (gap < smallestGap)
364  {
365  smallestGap = gap;
366  std::cout << "Gap: " << smallestGap
367  << " Sizes: " << start_rrt->configVector.size() << "/"
368  << goal_rrt->configVector.size() << std::endl;
369  }
370  }
371  }
372 
373  // Maximum # of iterations are reached and path is not found - failed.
374  return false;
375 }
376 
377 } // namespace planning
378 } // namespace dart
379 
380 #endif // DART_PLANNING_PATHPLANNER_HPP_
CollisionResult * result
Collision result of DART.
Definition: FCLCollisionDetector.cpp:160
void setPositions(const Eigen::VectorXd &_positions)
Set the positions for all generalized coordinates.
Definition: MetaSkeleton.cpp:398
Eigen::VectorXd getPositions() const
Get the positions for all generalized coordinates.
Definition: MetaSkeleton.cpp:413
class Skeleton
Definition: Skeleton.hpp:59
The path planner class - a common interface to motion planners.
Definition: PathPlanner.hpp:63
virtual ~PathPlanner()
The destructor.
Definition: PathPlanner.hpp:106
PathPlanner(simulation::World &world, bool bidirectional_=true, bool connect_=true, double stepSize_=0.1, std::size_t maxNodes_=1e6, double goalBias_=0.3)
The desired constructor - you should use this one.
Definition: PathPlanner.hpp:89
bool planBidirectionalRrt(dynamics::Skeleton *robot, const std::vector< int > &dofs, const std::vector< Eigen::VectorXd > &start, const std::vector< Eigen::VectorXd > &goal, std::list< Eigen::VectorXd > &path)
Performs bidirectional RRT with the given options.
Definition: PathPlanner.hpp:290
std::size_t maxNodes
Maximum number of iterations the sampling would continue.
Definition: PathPlanner.hpp:73
R * goal_rrt
The second rrt if bidirectional search is executed.
Definition: PathPlanner.hpp:80
bool planPath(dynamics::Skeleton *robot, const std::vector< int > &dofs, const Eigen::VectorXd &start, const Eigen::VectorXd &goal, std::list< Eigen::VectorXd > &path)
Plan a path from a single start configuration to a single goal.
Definition: PathPlanner.hpp:111
simulation::WorldPtr world
The world that the robot is in (for obstacles and etc.)
Definition: PathPlanner.hpp:75
bool bidirectional
Whether two trees try to meet each other or one goes for the goal.
Definition: PathPlanner.hpp:66
double goalBias
Choose btw goal and random value (for goal-biased search)
Definition: PathPlanner.hpp:71
PathPlanner()
The default constructor.
Definition: PathPlanner.hpp:84
double stepSize
Step size from a node in the tree to the random/goal node.
Definition: PathPlanner.hpp:69
bool planSingleTreeRrt(dynamics::Skeleton *robot, const std::vector< int > &dofs, const std::vector< Eigen::VectorXd > &start, const Eigen::VectorXd &goal, std::list< Eigen::VectorXd > &path)
Performs a unidirectional RRT with the given options.
Definition: PathPlanner.hpp:231
bool connect
Whether we take a small step or shoot for the target node.
Definition: PathPlanner.hpp:65
R * start_rrt
The rrt for unidirectional search.
Definition: PathPlanner.hpp:79
class World
Definition: World.hpp:84
std::shared_ptr< World > WorldPtr
Definition: SmartPointer.hpp:41
Definition: BulletCollisionDetector.cpp:65
const bool debug
Definition: urdf_world_parser.cpp:52