Skip to content
This repository was archived by the owner on Mar 16, 2024. It is now read-only.

Commit 685a0bb

Browse files
committed
Merge pull request #24 from joshhting/master
Implemented adaptive stepsize control
2 parents 79f570e + da5cbff commit 685a0bb

File tree

13 files changed

+195
-12
lines changed

13 files changed

+195
-12
lines changed

src/2dplane/2dplane.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* trying to find a path to
1616
*
1717
* @param step The fixed step size that the tree uses. This is the
18-
* maximum distance between nodes.
18+
* maximum distance between nodes unless adaptive stepsize control is utilized.
1919
*
2020
* @return An RRT::Tree with its callbacks and parameters configured.
2121
* You'll probably want to override the transitionValidator callback

src/2dplane/GridStateSpace.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using namespace Eigen;
77
using namespace std;
88

9+
#include <iostream>
10+
911

1012
GridStateSpace::GridStateSpace(float width, float height, int discretizedWidth, int discretizedHeight):
1113
PlaneStateSpace(width, height),
@@ -16,6 +18,29 @@ bool GridStateSpace::stateValid(const Vector2f &pt) const {
1618
return PlaneStateSpace::stateValid(pt) && !_obstacleGrid.obstacleAt(_obstacleGrid.gridSquareForLocation(pt));
1719
}
1820

21+
Vector2f GridStateSpace::intermediateState(const Vector2f &source, const Vector2f &target, float minStepSize, float maxStepSize) const {
22+
bool debug;
23+
24+
Vector2f delta = target - source;
25+
delta = delta / delta.norm(); // unit vector
26+
float dist = _obstacleGrid.nearestObstacleDist(source, maxStepSize * 2);
27+
28+
29+
float stepSize = (dist / maxStepSize) * minStepSize; // scale based on how far we are from obstacles
30+
if (stepSize > maxStepSize) stepSize = maxStepSize;
31+
if (stepSize < minStepSize) stepSize = minStepSize;
32+
if (debug) {
33+
cout << "ASC intermediateState" << endl;
34+
cout << " stepsize: " << minStepSize << endl;
35+
cout << " nearest obs dist: " << dist << endl;
36+
cout << " maximum stepsize: " << maxStepSize << endl;
37+
cout << " new step: " << stepSize << endl;
38+
}
39+
40+
Vector2f val = source + delta * stepSize;
41+
return val;
42+
}
43+
1944
bool GridStateSpace::transitionValid(const Vector2f &from, const Vector2f &to) const {
2045
// make sure we're within bounds
2146
if (!stateValid(to)) return false;

src/2dplane/GridStateSpace.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ class GridStateSpace : public PlaneStateSpace {
1919
bool stateValid(const Eigen::Vector2f &pt) const;
2020
bool transitionValid(const Eigen::Vector2f &from, const Eigen::Vector2f &to) const;
2121

22+
Eigen::Vector2f intermediateState(const Eigen::Vector2f &source, const Eigen::Vector2f &target, float minStepSize, float maxStepSize) const;
23+
2224
const ObstacleGrid &obstacleGrid() const;
2325
ObstacleGrid &obstacleGrid();
2426

25-
2627
private:
2728
ObstacleGrid _obstacleGrid;
2829
};

src/2dplane/ObstacleGrid.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include "ObstacleGrid.hpp"
22
#include <stdlib.h>
3+
#include <iostream>
34

45
using namespace Eigen;
6+
using namespace std;
57

68

79
ObstacleGrid::ObstacleGrid(float width, float height, int discretizedWidth, int discretizedHeight) {
@@ -24,6 +26,36 @@ Vector2i ObstacleGrid::gridSquareForLocation(const Vector2f &loc) const {
2426
loc.y() / height() * discretizedHeight());
2527
}
2628

29+
float ObstacleGrid::nearestObstacleDist(const Vector2f &state, float maxDist) const {
30+
//x and y are the indices of the cell that state is located in
31+
float x = (state.x() / (_width / _discretizedWidth));
32+
float y = (state.y() / (_height / _discretizedHeight));
33+
int xSearchRad = maxDist * _discretizedWidth / _width;
34+
int ySearchRad = maxDist * _discretizedHeight / _height;
35+
//here we loop through the cells around (x,y) to find the minimum distance of the point to the nearest obstacle
36+
for (int i = x - xSearchRad; i <= x + xSearchRad; i++) {
37+
for (int j = y - ySearchRad; j <= y + ySearchRad; j++) {
38+
bool obs = obstacleAt(i, j);
39+
if (obs) {
40+
float xDist = (x-i)*_width / _discretizedWidth;
41+
float yDist = (y-j)*_height / _discretizedHeight;
42+
float dist = sqrtf(powf(xDist, 2) + powf(yDist, 2));
43+
if (dist < maxDist) {
44+
maxDist = dist;
45+
}
46+
}
47+
}
48+
}
49+
50+
// the boundaries of the grid count as obstacles
51+
maxDist = std::min(maxDist, state.x()); // left boundary
52+
maxDist = std::min(maxDist, width() - state.x()); // right boundary
53+
maxDist = std::min(maxDist, state.y()); // top boundary
54+
maxDist = std::min(maxDist, height() - state.y()); // bottom boundary
55+
56+
return maxDist;
57+
}
58+
2759
void ObstacleGrid::clear() {
2860
for (int x = 0; x < discretizedWidth(); x++) {
2961
for (int y = 0; y < discretizedHeight(); y++) {

src/2dplane/ObstacleGrid.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ class ObstacleGrid {
1414

1515
Eigen::Vector2i gridSquareForLocation(const Eigen::Vector2f &loc) const;
1616

17+
/**
18+
* Finds the distance from state to its neareset obstacle. Only searches up to maxDist around
19+
* state so as to not waste time checking far away and irrelevant obstacles.
20+
*
21+
* @param state The location to search with respect to for the nearest obstacle dist
22+
* @param maxDist The maximum vertical and horizontal distance from state to search for obstacles
23+
*/
24+
float nearestObstacleDist(const Eigen::Vector2f &state, float maxDist) const;
1725
void clear();
1826
bool &obstacleAt(int x, int y);
1927
bool obstacleAt(int x, int y) const;

src/2dplane/PlaneStateSpace.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ float PlaneStateSpace::width() const {
3939
float PlaneStateSpace::height() const {
4040
return _height;
4141
}
42+

src/BiRRT.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ namespace RRT
3636
return _goalTree;
3737
}
3838

39+
bool isASCEnabled() const {
40+
return _startTree.isASCEnabled();
41+
}
42+
void setASCEnabled(bool checked) {
43+
_startTree.setASCEnabled(checked);
44+
_goalTree.setASCEnabled(checked);
45+
}
46+
3947
float goalBias() const {
4048
return _startTree.goalBias();
4149
}
@@ -76,6 +84,14 @@ namespace RRT
7684
_goalTree.setStepSize(stepSize);
7785
}
7886

87+
float maxStepSize() const {
88+
return _startTree.maxStepSize();
89+
}
90+
void setMaxStepSize(float stepSize) {
91+
_startTree.setMaxStepSize(stepSize);
92+
_goalTree.setMaxStepSize(stepSize);
93+
}
94+
7995
float goalMaxDist() const {
8096
return _startTree.goalMaxDist();
8197
}

src/StateSpace.hpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
/**
55
* A state space represents the set of possible states for a planning problem.
6-
* This includes the obstacles that may bee present and what state transitions are valid.
6+
* This includes the obstacles that may be present and what state transitions are valid.
77
* This class is abstract and must be subclassed in order to provide actual functionality.
88
*/
99
template<typename T>
@@ -27,6 +27,18 @@ class StateSpace {
2727
*/
2828
virtual T intermediateState(const T &source, const T &target, float stepSize) const = 0;
2929

30+
/**
31+
* An overloaded version designed for use in adaptive stepsize control.
32+
*
33+
* @param source The node in the tree to extend from
34+
* @param target The point in the space to extend to
35+
* @param minStepSize The minimum allowable stepsize the intermediate state will be extended from source
36+
* @param maxStepSize The maximum allowable stepsize the intermediate state will be extended from source
37+
*
38+
* @return A state in the direction of @target from @source.state()
39+
*/
40+
virtual T intermediateState(const T &source, const T &target, float minStepSize, float maxStepSize) const = 0;
41+
3042
/**
3143
* @brief Calculate the distance between two states
3244
*
@@ -56,4 +68,8 @@ class StateSpace {
5668
* @return A boolean indicating validity
5769
*/
5870
virtual bool transitionValid(const T &from, const T &to) const = 0;
71+
72+
protected:
73+
float _minStepSize;
74+
float _maxStepSize;
5975
};

src/Tree.hpp

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
#include <functional>
88
#include <stdexcept>
99
#include <stdlib.h>
10-
10+
#include <iostream>
1111

1212
namespace RRT
1313
{
1414
/**
15-
* Base class for an rrt tree node
15+
* Base class for an RRT tree node.
1616
*
1717
* @param T The datatype representing the state in the space the RRT
1818
* will be searching.
@@ -23,7 +23,7 @@ namespace RRT
2323
Node(const T &state, Node<T> *parent = nullptr) {
2424
_parent = parent;
2525
_state = state;
26-
26+
2727
if (_parent) {
2828
_parent->_children.push_back(this);
2929
}
@@ -72,6 +72,11 @@ namespace RRT
7272
* placed in callbacks (C++ lambdas), which must be supplied by the
7373
* user of this class.
7474
*
75+
* If adaptive stepsize control (ASC) is enabled, then the stepsize for extending new nodes from
76+
* the tree will be dynamically updated depending on how close the nearest obstacle is. If there are
77+
* no nearby obstacles, then the stepsize will be extended in order to safely cover more ground. If
78+
* there are nearby obstacles, then the stepsize will shrink so that the RRT can take more precise steps.
79+
*
7580
* USAGE:
7681
* 1) Create a new Tree with the appropriate StateSpace
7782
* RRT::Tree<My2dPoint> tree(stateSpace);
@@ -80,7 +85,10 @@ namespace RRT
8085
* tree->setStartState(s);
8186
* tree->setGoalState(g);
8287
*
83-
* 3) Run the RRT algorithm! This can be done in one of two ways:
88+
* 3) (Optional) If adaptive stepsize control is enabled:
89+
* _stateSpace->setMaxStepSize sets the maximum stepsize the tree can take for any step.
90+
*
91+
* 4) Run the RRT algorithm! This can be done in one of two ways:
8492
* Option 1) Call the run() method - it will grow the tree
8593
* until it finds a solution or runs out of iterations.
8694
*
@@ -89,7 +97,7 @@ namespace RRT
8997
* Either way works fine, just choose whatever works best for your
9098
* application.
9199
*
92-
* 4) Use getPath() to get the series of states that make up the solution
100+
* 5) Use getPath() to get the series of states that make up the solution
93101
*
94102
* @param T The type that represents a state within the state-space that
95103
* the tree is searching. This could be a 2D Point or something else,
@@ -103,7 +111,9 @@ namespace RRT
103111

104112
// default values
105113
setStepSize(0.1);
114+
setMaxStepSize(5);
106115
setMaxIterations(1000);
116+
setASCEnabled(false);
107117
setGoalBias(0);
108118
setWaypointBias(0);
109119
setGoalMaxDist(0.1);
@@ -134,6 +144,16 @@ namespace RRT
134144
}
135145

136146

147+
/**
148+
* Whether or not the tree is to run with adaptive stepsize control.
149+
*/
150+
bool isASCEnabled() const {
151+
return _isASCEnabled;
152+
}
153+
void setASCEnabled(bool checked) {
154+
_isASCEnabled = checked;
155+
}
156+
137157
/**
138158
* @brief The chance we extend towards the goal rather than a random point.
139159
* @details At each iteration of the RRT algorithm, we extend() towards a particular state. The goalBias
@@ -187,6 +207,14 @@ namespace RRT
187207
_stepSize = stepSize;
188208
}
189209

210+
/// Max step size used in ASC
211+
float maxStepSize() const {
212+
return _maxStepSize;
213+
}
214+
void setMaxStepSize(float maxStep) {
215+
_maxStepSize = maxStep;
216+
}
217+
190218

191219
/**
192220
* @brief How close we have to get to the goal in order to consider it reached.
@@ -279,22 +307,33 @@ namespace RRT
279307
* Grow the tree in the direction of @state
280308
*
281309
* @return the new tree Node (may be nullptr if we hit Obstacles)
310+
* @param target The point to extend the tree to
282311
* @param source The Node to connect from. If source == nullptr, then
283312
* the closest tree point is used
284313
*/
285314
virtual Node<T> *extend(const T &target, Node<T> *source = nullptr) {
286315
// if we weren't given a source point, try to find a close node
287316
if (!source) {
288-
source = nearest(target);
317+
source = nearest(target, nullptr);
289318
if (!source) {
290319
return nullptr;
291320
}
292321
}
293-
322+
294323
// Get a state that's in the direction of @target from @source.
295324
// This should take a step in that direction, but not go all the
296325
// way unless the they're really close together.
297-
T intermediateState = _stateSpace->intermediateState(source->state(), target, stepSize());
326+
T intermediateState;
327+
if (_isASCEnabled) {
328+
intermediateState = _stateSpace->intermediateState(
329+
source->state(),
330+
target,
331+
stepSize(),
332+
maxStepSize()
333+
);
334+
} else {
335+
intermediateState = _stateSpace->intermediateState(source->state(), target, stepSize());
336+
}
298337

299338
// Make sure there's actually a direct path from @source to
300339
// @intermediateState. If not, abort
@@ -413,6 +452,8 @@ namespace RRT
413452

414453
int _maxIterations;
415454

455+
bool _isASCEnabled;
456+
416457
float _goalBias;
417458

418459
/// used for Extended RRTs where growth is biased towards waypoints from previously grown tree
@@ -422,6 +463,7 @@ namespace RRT
422463
float _goalMaxDist;
423464

424465
float _stepSize;
466+
float _maxStepSize;
425467

426468
std::shared_ptr<StateSpace<T>> _stateSpace;
427469
};

src/TreeTest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,35 @@ TEST(Tree, GetPath) {
6666
tree->getPath(path, tree->lastNode(), false);
6767
ASSERT_TRUE(path.size() > 1);
6868
}
69+
70+
TEST(Tree, ASC) {
71+
//test adaptive stepsize control
72+
Tree<Vector2f> *tree = TreeFor2dPlane(
73+
make_shared<GridStateSpace>(50, 50, 50, 50),
74+
Vector2f(40, 40), // goal point
75+
5); // step size
76+
77+
// give it plenty of iterations so it's not likely to fail
78+
const int maxIterations = 10000;
79+
tree->setMaxIterations(maxIterations);
80+
tree->setGoalMaxDist(5);
81+
tree->setMaxStepSize(10);
82+
tree->setASCEnabled(true);
83+
84+
tree->setStartState(Vector2f(10, 10));
85+
bool success = tree->run(); // run with the given starting point
86+
ASSERT_TRUE(success);
87+
88+
vector<Vector2f> path;
89+
tree->getPath(path, tree->lastNode(), true);
90+
91+
// Check to see if the nodes in the tree have uniform stepsize or varied. Stepsizes should vary
92+
bool varied = false;
93+
for (int i = 1; !varied && i < path.size() - 2; i++) {
94+
Vector2f pathA = path[i] - path[i - 1];
95+
Vector2f pathB = path[i] - path[i + 1];
96+
float n = pathA.norm() / pathB.norm();
97+
if (n < 0.99 || n > 1.01) varied = true;
98+
}
99+
ASSERT_TRUE(varied);
100+
}

0 commit comments

Comments
 (0)