RRT¶
src.python_motion_planning.global_planner.sample_search.rrt.RRT
¶
Bases: SampleSearcher
Class for RRT motion planning.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
start
|
tuple
|
start point coordinate |
required |
goal
|
tuple
|
goal point coordinate |
required |
env
|
Env
|
environment |
required |
max_dist
|
float
|
Maximum expansion distance one step |
0.5
|
sample_num
|
int
|
Maximum number of sample points |
10000
|
goal_sample_rate
|
float
|
heuristic sample |
0.05
|
Examples:
Python Console Session
>>> import python_motion_planning as pmp
>>> planner = pmp.RRT((5, 5), (45, 25), pmp.Map(51, 31))
>>> cost, path, expand = planner.plan() # planning results only
>>> planner.plot.animation(path, str(planner), cost, expand) # animation
>>> planner.run() # run both planning and animation
References
[1] Rapidly-Exploring Random Trees: A New Tool for Path Planning
Source code in src\python_motion_planning\global_planner\sample_search\rrt.py
Python
class RRT(SampleSearcher):
"""
Class for RRT motion planning.
Parameters:
start (tuple): start point coordinate
goal (tuple): goal point coordinate
env (Env): environment
max_dist (float): Maximum expansion distance one step
sample_num (int): Maximum number of sample points
goal_sample_rate (float): heuristic sample
Examples:
>>> import python_motion_planning as pmp
>>> planner = pmp.RRT((5, 5), (45, 25), pmp.Map(51, 31))
>>> cost, path, expand = planner.plan() # planning results only
>>> planner.plot.animation(path, str(planner), cost, expand) # animation
>>> planner.run() # run both planning and animation
References:
[1] Rapidly-Exploring Random Trees: A New Tool for Path Planning
"""
def __init__(self, start: tuple, goal: tuple, env: Env, max_dist: float = 0.5,
sample_num: int = 10000, goal_sample_rate: float = 0.05) -> None:
super().__init__(start, goal, env)
# Maximum expansion distance one step
self.max_dist = max_dist
# Maximum number of sample points
self.sample_num = sample_num
# heuristic sample
self.goal_sample_rate = goal_sample_rate
def __str__(self) -> str:
return "Rapidly-exploring Random Tree(RRT)"
def plan(self) -> tuple:
"""
RRT motion plan function.
Returns:
cost (float): path cost
path (list): planning path
expand (list): expanded (sampled) nodes list
"""
# Sampled list
sample_list = {self.start.current: self.start}
# main loop
for _ in range(self.sample_num):
# generate a random node in the map
node_rand = self.generateRandomNode()
# visited
if node_rand.current in sample_list:
continue
# generate new node
node_new = self.getNearest(list(sample_list.values()), node_rand)
if node_new:
sample_list[node_new.current] = node_new
dist = self.dist(node_new, self.goal)
# goal found
if dist <= self.max_dist and not self.isCollision(node_new, self.goal):
self.goal.parent = node_new.current
self.goal.g = node_new.g + self.dist(self.goal, node_new)
sample_list[self.goal.current] = self.goal
cost, path = self.extractPath(sample_list)
return cost, path, list(sample_list.values())
return 0, None, list(sample_list.values())
def run(self) -> None:
"""
Running both plannig and animation.
"""
cost, path, expand = self.plan()
self.plot.animation(path, str(self), cost, expand)
def generateRandomNode(self) -> Node:
"""
Generate a random node to extend exploring tree.
Returns:
node (Node): a random node based on sampling
"""
if np.random.random() > self.goal_sample_rate:
current = (np.random.uniform(self.delta, self.env.x_range - self.delta),
np.random.uniform(self.delta, self.env.y_range - self.delta))
return Node(current, None, 0, 0)
return self.goal
def getNearest(self, node_list: list, node: Node) -> Node:
"""
Get the node from `node_list` that is nearest to `node`.
Parameters:
node_list (list): exploring list
node (Node): currently generated node
Returns:
node (Node): nearest node
"""
# find nearest neighbor
dist = [self.dist(node, nd) for nd in node_list]
node_near = node_list[int(np.argmin(dist))]
# regular and generate new node
dist, theta = self.dist(node_near, node), self.angle(node_near, node)
dist = min(self.max_dist, dist)
node_new = Node((node_near.x + dist * math.cos(theta),
(node_near.y + dist * math.sin(theta))),
node_near.current, node_near.g + dist, 0)
# obstacle check
if self.isCollision(node_new, node_near):
return None
return node_new
def extractPath(self, closed_list: dict) -> tuple:
"""
Extract the path based on the CLOSED list.
Parameters:
closed_list (dict): CLOSED list
Returns
cost (float): the cost of planning path
path (list): the planning path
"""
node = closed_list[self.goal.current]
path = [node.current]
cost = node.g
while node != self.start:
node_parent = closed_list[node.parent]
node = node_parent
path.append(node.current)
return cost, path
extractPath(closed_list)
¶
Extract the path based on the CLOSED list.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
closed_list
|
dict
|
CLOSED list |
required |
Returns cost (float): the cost of planning path path (list): the planning path
Source code in src\python_motion_planning\global_planner\sample_search\rrt.py
Python
def extractPath(self, closed_list: dict) -> tuple:
"""
Extract the path based on the CLOSED list.
Parameters:
closed_list (dict): CLOSED list
Returns
cost (float): the cost of planning path
path (list): the planning path
"""
node = closed_list[self.goal.current]
path = [node.current]
cost = node.g
while node != self.start:
node_parent = closed_list[node.parent]
node = node_parent
path.append(node.current)
return cost, path
generateRandomNode()
¶
Generate a random node to extend exploring tree.
Returns:
Name | Type | Description |
---|---|---|
node |
Node
|
a random node based on sampling |
Source code in src\python_motion_planning\global_planner\sample_search\rrt.py
Python
def generateRandomNode(self) -> Node:
"""
Generate a random node to extend exploring tree.
Returns:
node (Node): a random node based on sampling
"""
if np.random.random() > self.goal_sample_rate:
current = (np.random.uniform(self.delta, self.env.x_range - self.delta),
np.random.uniform(self.delta, self.env.y_range - self.delta))
return Node(current, None, 0, 0)
return self.goal
getNearest(node_list, node)
¶
Get the node from node_list
that is nearest to node
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
node_list
|
list
|
exploring list |
required |
node
|
Node
|
currently generated node |
required |
Returns:
Name | Type | Description |
---|---|---|
node |
Node
|
nearest node |
Source code in src\python_motion_planning\global_planner\sample_search\rrt.py
Python
def getNearest(self, node_list: list, node: Node) -> Node:
"""
Get the node from `node_list` that is nearest to `node`.
Parameters:
node_list (list): exploring list
node (Node): currently generated node
Returns:
node (Node): nearest node
"""
# find nearest neighbor
dist = [self.dist(node, nd) for nd in node_list]
node_near = node_list[int(np.argmin(dist))]
# regular and generate new node
dist, theta = self.dist(node_near, node), self.angle(node_near, node)
dist = min(self.max_dist, dist)
node_new = Node((node_near.x + dist * math.cos(theta),
(node_near.y + dist * math.sin(theta))),
node_near.current, node_near.g + dist, 0)
# obstacle check
if self.isCollision(node_new, node_near):
return None
return node_new
plan()
¶
RRT motion plan function.
Returns:
Name | Type | Description |
---|---|---|
cost |
float
|
path cost |
path |
list
|
planning path |
expand |
list
|
expanded (sampled) nodes list |
Source code in src\python_motion_planning\global_planner\sample_search\rrt.py
Python
def plan(self) -> tuple:
"""
RRT motion plan function.
Returns:
cost (float): path cost
path (list): planning path
expand (list): expanded (sampled) nodes list
"""
# Sampled list
sample_list = {self.start.current: self.start}
# main loop
for _ in range(self.sample_num):
# generate a random node in the map
node_rand = self.generateRandomNode()
# visited
if node_rand.current in sample_list:
continue
# generate new node
node_new = self.getNearest(list(sample_list.values()), node_rand)
if node_new:
sample_list[node_new.current] = node_new
dist = self.dist(node_new, self.goal)
# goal found
if dist <= self.max_dist and not self.isCollision(node_new, self.goal):
self.goal.parent = node_new.current
self.goal.g = node_new.g + self.dist(self.goal, node_new)
sample_list[self.goal.current] = self.goal
cost, path = self.extractPath(sample_list)
return cost, path, list(sample_list.values())
return 0, None, list(sample_list.values())