RRT(Rapidly-exploring Random Tree、高速探索ランダム木) とは、ランダムに打った点へ向けて木を少しずつ伸ばし、障害物を避けながらスタートからゴールまでの経路を探す経路計画アルゴリズムです。A*はマス目(グリッド)の上で最短経路を探しましたが、ロボットアームの関節角度のような連続値の空間では、マス目に区切ること自体が重くなります。この記事ではRRTをPythonでゼロから実装し、実際に経路を見つけるところまで動かします。
この記事で分かること
- RRTとは、ランダムな点へ向けて木を少しずつ伸ばし、障害物を避けながら経路を探すアルゴリズム。LaValleが1998年に発表した
- グリッド化が重くなる連続・高次元の空間(ロボットアームの関節角度など)で、A*より扱いやすい
- 見つかる経路は最短とは限らない。最適性を求める改良版RRT*(Karaman & Frazzoli, 2011)も存在する
対象読者:A*などの経路探索を学んだあと、連続空間での経路計画に進みたい学習者・エンジニア
執筆:ゆるふわフィジカルAI編集部 最終更新日:2026年11月2日
RRTの1ステップ
点を打つ
空間からランダムに1点
最近傍を探す
木の中で一番近いノード
少し伸ばす
そちらへ一定の歩幅だけ進む
衝突確認
通れたら木に追加
なぜグリッドではなくRRTなのか
A*は空間をマス目に分割して探索します。2次元の地図ならマス目は現実的な数で済みますが、6自由度のロボットアームを「関節角度6個の空間」として扱うと、1軸あたり100分割しただけで100の6乗(1兆通り)のマスができてしまい、グリッド化が事実上不可能になります。RRTはマス目を作らず、空間からサンプリング(ランダムに点を抜き出す)するだけなので、次元が増えても扱いやすいのが利点です。
トレードオフもある
その代わりRRTが見つける経路は、A*のような最短経路の保証がありません。「通れる経路を素早く見つける」ことを優先した手法です。
準備
pip install numpy標準ライブラリの math と random も使います。
① 空間と障害物を決める
2次元の10×10の空間に、円形の障害物をいくつか置きます。
import random
import math
import numpy as np
random.seed(7)
# 障害物: (中心x, 中心y, 半径)
obstacles = [(5, 5, 1.5), (3, 7, 1.0), (7, 3, 1.2), (6, 7.5, 0.8)]
START = (0.5, 0.5)
GOAL = (9.5, 9.5)
BOUNDS = (0, 10)
STEP = 0.5 # 1回に伸ばす歩幅
GOAL_RADIUS = 0.5 # ここまで近づけばゴール扱い
MAX_ITERS = 5000
def collides(p):
for cx, cy, r in obstacles:
if math.hypot(p[0] - cx, p[1] - cy) <= r:
return True
return False
def segment_collides(a, b, samples=10):
for i in range(samples + 1):
t = i / samples
p = (a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t)
if collides(p):
return True
return Falsecollides は1点が障害物の中にあるかを見るだけの判定です。木を1本伸ばすたびに、その線分が障害物を突き抜けていないかも segment_collides で細かく確認します。点だけ見ると、障害物をすり抜けて反対側に着地してしまうことがあるためです。
② 木を伸ばす(steer)
ランダムな点のほうへ、歩幅 STEP の分だけ進んだ新しい点を返します。ランダム点がすでに近ければ、そのまま使います。
def steer(a, b, step):
dx, dy = b[0] - a[0], b[1] - a[1]
dist = math.hypot(dx, dy)
if dist <= step:
return b
return (a[0] + dx / dist * step, a[1] + dy / dist * step)一歩ずつしか伸ばさない理由
ランダム点まで一気に直線で結ぶと、途中の障害物を見逃しやすくなります。歩幅を小さく刻んで少しずつ伸ばし、そのたびに衝突確認することで、木全体が安全な経路だけで構成されます。
③ RRT本体
木(ノードの配列と、それぞれの親)を持ち、ループのたびに「ランダム点 → 最近傍 → 伸ばす → 衝突確認 → 木に追加」を繰り返します。5%の確率でゴール自体をランダム点として選ぶゴールバイアスを入れると、収束が早くなります。
nodes = [START]
parents = {0: None}
goal_reached_idx = None
for it in range(MAX_ITERS):
if random.random() < 0.05:
rnd = GOAL
else:
rnd = (random.uniform(*BOUNDS), random.uniform(*BOUNDS))
dists = [math.hypot(rnd[0] - n[0], rnd[1] - n[1]) for n in nodes]
nearest_idx = int(np.argmin(dists))
nearest = nodes[nearest_idx]
new_node = steer(nearest, rnd, STEP)
if collides(new_node) or segment_collides(nearest, new_node):
continue
nodes.append(new_node)
new_idx = len(nodes) - 1
parents[new_idx] = nearest_idx
if math.hypot(new_node[0] - GOAL[0], new_node[1] - GOAL[1]) <= GOAL_RADIUS:
goal_reached_idx = new_idx
break④ 実行して経路を見る
ゴールに着いたノードから親をたどって戻れば、スタートからの経路が復元できます。
print(f"サンプリング回数: {it + 1}")
print(f"木のノード数: {len(nodes)}")
if goal_reached_idx is not None:
path = []
idx = goal_reached_idx
while idx is not None:
path.append(nodes[idx])
idx = parents[idx]
path.reverse()
length = sum(
math.hypot(path[i + 1][0] - path[i][0], path[i + 1][1] - path[i][1])
for i in range(len(path) - 1)
)
print(f"ゴールまでの経路: ノード数 {len(path)}, 経路長 {length:.2f}")
print("経路の一部:", [(round(x, 2), round(y, 2)) for x, y in path[:5]])
else:
print("ゴールに到達できませんでした")サンプリング回数: 229
木のノード数: 175
ゴールまでの経路: ノード数 32, 経路長 15.50
経路の一部: [(0.5, 0.5), (0.58, 0.99), (1.02, 1.24), (1.51, 1.13), (2.01, 1.14)]229回のサンプリングで、4つの円形障害物を避けながらスタート(0.5, 0.5)からゴール(9.5, 9.5)までの経路が見つかりました。random.seed を変えたり STEP を小さくしたりすると、ノード数や経路長が変わるのを確認してみてください。
229回
サンプリング回数
175個
木のノード数
15.50
見つかった経路の長さ
直線距離は約12.7、見つかった経路は15.50
スタートからゴールの直線距離は約12.73なので、15.50はそれより長い回り道です。これがRRTの弱点で、見つかる経路は最短ではありません。最短に近づけたい場合は、この記事の最後で触れるRRT*という改良版を使います。
よくある質問
Q. RRTとA*は何が違いますか?
A. A*はマス目(グリッド)に区切った空間で最短経路を保証して探します。RRTはグリッドを作らずランダムな点をサンプリングして木を伸ばすので、ロボットアームの関節角度のような高次元・連続値の空間でも扱いやすい代わりに、見つかる経路の最短性は保証されません。
Q. RRTで見つかる経路は最短ルートになりますか?
A. いいえ、保証されません。RRTは「通れる経路を素早く見つけること」を優先したアルゴリズムで、スタートからゴールの直線距離より長い回り道になることがよくあります。経路の最適性に近づけたい場合は、Karaman と Frazzoli が2011年に発表したRRT*という改良版が使われます。
Q. RRTは実際のロボットのモーションプランニングで使われていますか?
A. はい。ロボットのモーションプランニングで広く使われるオープンソースライブラリOMPL(Open Motion Planning Library)には、RRTやRRT*を含む複数のサンプリングベース手法が実装として収録されており、ROSのMoveItなどからも利用されています。
まとめ
- RRTはランダムサンプリングで木を伸ばし、障害物を避けてゴールへの経路を探すアルゴリズム
- グリッド化が難しい連続・高次元の空間(ロボットアームの関節角度など)でA*より扱いやすい
- 代わりに見つかる経路は最短とは限らない。最適性を求めるならRRT*などの改良版を使う
- ゴールバイアス(低確率でゴール自体をサンプリング)を入れると収束が早くなる
📚 もう少し詳しく(背景と理論)気になる人向け・タップで開く
RRTはLaValleが1998年に発表した探索木で1、空間をランダムにサンプリングしながら木を伸ばすことで、明示的な空間分割(グリッド化)なしに探索できます。サンプリング回数が十分に多ければ、存在する経路をほぼ確実に見つけられる**確率的完全性(probabilistic completeness)**を持つことが知られていますが、見つかる経路の長さそのものは最適化されません。
この弱点に対して、Karaman と Frazzoli は2011年にRRT*(アールアールティー・スター)を発表しました2。RRT*は新しいノードを木に追加するたびに近傍のつながり方を見直し(リワイヤリング)、サンプリング回数が無限に近づくほど経路長が最適解に収束する漸近的最適性を持ちます。計算コストは増えますが、通常のRRTと同程度の計算量に収まることが示されています。
ロボットアームの逆運動学で扱った関節角度の空間(コンフィギュレーション空間)は、関節数が増えるほど次元が増え、Aのようなグリッド探索が指数的に重くなります。RRTやRRTはこうした高次元空間での経路計画に向いており、ロボットのモーションプランニングライブラリOMPLにも複数のバリエーションが実装されています3。
次の一歩 🌸
見つけた経路の上をロボットアームにたどらせる計算は逆運動学をPythonで実装する、グリッド上の最短経路はA*をPythonで実装する、センサーで地図を作る側はSLAMの基礎をPythonでへどうぞ。
参考資料・出典
- Rapidly-exploring random trees: A new tool for path planningSteven M. LaValle 公式ページ。RRTの原論文(1998年、Iowa State University テクニカルレポート98-11)の情報と関連文献一覧
- Sampling-based Algorithms for Optimal Motion PlanningKaraman & Frazzoli (2011)。RRT*・PRM*を提案した論文。The International Journal of Robotics Research 30(7)に掲載
- OMPL: Open Motion Planning LibraryRRT・RRT*を含むサンプリングベース経路計画アルゴリズムを実装したオープンソースライブラリの公式サイト
ゆるふわポータルでは、一次情報・公的資料にもとづき、専門用語をできるだけやさしく翻訳することを心がけています。内容に誤りや古い情報がある場合は、お問い合わせからご連絡ください。
執筆:ゆるふわフィジカルAI編集部 最終更新日:2026年11月2日
Footnotes
-
LaValle, S. M. (1998). "Rapidly-exploring random trees: A new tool for path planning." Technical Report 98-11, Dept. of Computer Science, Iowa State University. ↩
-
Karaman, S., & Frazzoli, E. (2011). "Sampling-based Algorithms for Optimal Motion Planning." The International Journal of Robotics Research, 30(7), 846–894. 本文中でPRMとRRTという2つの漸近的最適アルゴリズムを提案している。 ↩
-
OMPL(Open Motion Planning Library、ompl.kavrakilab.org)は、PRM・RRT・EST・SBL・KPIECEなど複数のサンプリングベース経路計画アルゴリズムを実装として収録した、ロボット向けのオープンソースライブラリ。 ↩
ゆるふわフィジカルAI