← Autonomy lab

04 / 06 · RRT* · global planning · numpy only

RRT*

Throw random points at the map, grow a tree toward them, and keep rewiring it until the route through the pillars is close to the shortest one. Then fly it.

One plan
111–178 ms
Tree size
~1,000–1,400 nodes
Offline sweep
40 / 40 seeds
Tree path → smoothed
12.66 → 12.04 m
Gazebo · x500 with 2D lidar · sped up

How it works

A tree that keeps getting shorter

01 · Sample and extend

Reach toward random points

Pick a random point in the box around start and goal (8% of the time, the goal itself). Find the nearest node and extend it up to 0.8 m toward the point, if that edge clears every pillar padded by 0.5 m, checked every 0.1 m.

This is a wall the potential field cannot get past; for a global planner it is routine.

02 · Choose a parent, rewire

The star in RRT*

A new node takes the cheapest parent it can reach within 1.6 m. Then each neighbour checks whether going through the new node would be shorter, and switches if so.

When a node switches, the saving flows down its whole subtree, a step most tutorial versions skip. The orange flashes are rewires.

03 · Keep refining

Don't stop at the first route

After the first path to the goal appears, the tree keeps growing for 1,500 more iterations (4,000 at most), and the best path keeps getting shorter. The goal candidate is re-picked over all nodes that can see it, so an older one improved by rewiring is never missed.

04 · Smooth and fly

Cut the corners, then slow for them

A line-of-sight prune and 120 random shortcuts, each re-checked against the pillars, straighten the path. A waypoint follower flies it with velocity setpoints and slows before sharp turns.

The slowdown came from Gazebo: a 90° turn at 1.5 m/s overshot the 0.5 m padding and clipped a pillar.

treebest path · rewireflown

Try it

Plan to anywhere

Click or tap to set a goal: a fresh tree grows from where the drone is, then it flies the smoothed path. A 2D re-implementation of rrtstar_core.py with the same parameters; 1.2 m pillars stay flyable because the cruise is at 2 m.

The search, written out

Numbers are the defaults in rrtstar_core.py. c(x) is the length of the tree path from the start to node x; every edge is checked against the same free-space test every 0.1 m.

free space

x is free ⇔ for every pillar:  |xxy − cxy| ≥ r + 0.5  or  alt(x) ≥ top + 0.5

each iteration

xrand = g with p = 0.08, else uniform in the box
samplestart and goal ± 4 m, altitude 0.8–3.2 m
xnew = xnear + min(0.8, |xrand − xnear|) · û
extendfrom the nearest node, at most 0.8 m
parent = argminx ∈ N c(x) + |x − xnew|,  N = nodes within 1.6 m
choose parentcheapest arrival with a free edge
c(xnew) + |xnew − x| < c(x) ⇒ re-parent x
rewirethe RRT* step; the cost drop is pushed down x's whole subtree
stop 1500 iterations after the first path, at most 4000
budgeta node within 0.8 m of the goal with a free edge counts as a path

then fly it

v = 1.5 · (1 − 0.65 · min(θ / 90°, 1)) within 1.5 m of a turn of θ
cornersafter line-of-sight pruning and 120 random shortcuts
v = min(1.5, 1.2 d) to the last waypoint
arrivewaypoints switch at 0.4 m, the goal at 0.5 m

Results

Gazebo, headless.
scenarioresultfollowflightmin clearancere-plans
default course, fixed (1.5 m/s)reached8.5 s11.6 m0.50 m0
default course, lidar (1.5 m/s)reached9.3 s12.8 m0.52 m0
forest, fixed (1.0 m/s)reached20.6 s16.4 m0.52 m0
forest, lidar (1.0 m/s)reached35.0 s45.7 m0.13 m9, all successful

The forest-with-lidar run is the cost of exploring what it cannot see yet: the first path crosses an area later found blocked, pillars behind the wall appear one by one, and each one invalidates the path and triggers a re-plan. Clearance cannot be guaranteed for surfaces not yet observed. A grow-only lidar map also keeps every projection error forever, so it uses full-attitude projection, an altitude band and a yaw-rate gate.

What the drone sees

RViz shows the whole search tree, which is the most useful view of RRT*: thin blue lines are the tree, the green line is the planned path.

RViz · blue: search tree · green: planned path · cyan: flown

Python · numpy · ROS 2 Humble · PX4 v1.14 · Gazebo Garden · 2D lidar