惑星探査ローバーの移動機構と走行力学 — 他の天体を走るロボット

もしあなたが砂漠のど真ん中でリモコンカーを走らせるとしたら、どんなことに注意するでしょうか。砂に車輪が沈み込まないか、岩を乗り越えられるか、急な傾斜で転倒しないか — これらはすべて、火星や月面でローバー(探査車)を走らせるエンジニアたちが日々向き合っている課題です。

しかも、地球の砂漠ならスタックしたら助けに行けますが、火星でスタックしたローバーを助ける人はいません。NASAのSpirit(スピリット)は2009年に砂地にはまり、そのまま二度と動けなくなりました。ローバーにとって「走れなくなること」は、数十億ドルのミッションの終了を意味します。だからこそ、惑星探査ローバーの移動機構の設計は、宇宙工学の中でも最も実践的かつ挑戦的な分野の一つです。

惑星探査ローバーの移動機構を理解すると、以下のような幅広い応用が見えてきます。

  • ロボット工学全般: ローバーのサスペンション機構やスリップ制御は、不整地走行ロボット全般に応用される基盤技術です
  • 地上の無人車両: 災害救助ロボット、農業ロボット、鉱山探査車両などが同じ走行力学に基づいて設計されています
  • 将来の有人探査: 月面基地建設や火星有人探査では、より大型のローバーが必要になり、走行力学の知見がそのまま活きます

本記事の内容

  • 惑星表面環境が移動機構に課す制約
  • 車輪の力学モデル(駆動力、走行抵抗、スリップ率)
  • ロッカーボギー機構の原理とキネマティクス
  • 各種移動方式の比較(車輪・クローラ・脚・ハイブリッド)
  • 主要ローバーの技術比較(Sojourner → Spirit/Opportunity → Curiosity → Perseverance → 玉兔2号)
  • Pythonによる走行力学シミュレーション

前提知識

この記事を読む前に、以下の知識があると理解が深まります。

  • 古典力学の基礎(力のつり合い、トルク、摩擦力)
  • ベクトルと座標変換の基本(回転行列、幾何学的関係)

惑星表面環境の特殊性 — なぜ地球の車両設計をそのまま使えないのか

重力の違い

地球の車を火星に持っていけばうまく走るかというと、そう単純ではありません。惑星探査ローバーの設計において、最初に直面する根本的な制約は重力加速度の違いです。

各天体の表面重力を比較してみましょう。

天体 表面重力加速度 $g$ (m/s²) 地球比
地球 9.81 1.00
火星 3.72 0.38
1.62 0.17
タイタン 1.35 0.14

重力が小さいということは、車体を地面に押し付ける力(垂直抗力)が小さくなることを意味します。車輪が地面を「掴む」力(トラクション)は垂直抗力に比例するため、低重力環境では同じ車輪でもグリップが激減するのです。火星では地球の約38%、月ではわずか17%しかトラクションが得られません。

一方で、ローバーの質量は変わりません(重力が変わっても慣性は同じ)。つまり、動き出したローバーを止めるのに必要な力も変わらないのに、ブレーキの効きは低下するという厄介な状況が生じます。

地形の過酷さ

惑星表面の地形も、地球の道路とはまったく異なります。火星の表面は大きく分けて以下の3種類の地形で構成されています。

  1. 砂地・レゴリス: 粒径 0.1〜数mm の細粒土壌が広がります。車輪が沈み込みやすく、スリップの原因になります
  2. 岩場: 直径数cm〜数mの岩が散乱しています。車輪直径以上の岩は乗り越えられないため、回避が必要です
  3. 傾斜地: クレーターの縁や丘陵地帯では、20°〜30°の傾斜が普通に存在します

特に砂地での走行は深刻な問題です。車輪が砂に沈み込むと、前進に必要な力が急激に増大します。NASAのSpiritは、2009年5月に「Troy」と名付けられた砂地にはまり、左前輪がすでに故障していたこともあって、約8か月の救出作業にもかかわらず脱出できませんでした。

温度環境と真空

火星の表面温度は昼間でも約 $-20$°C、夜間は $-80$°C まで下がります。月面に至っては昼間 $+120$°C、夜間 $-180$°C という約300°Cの温度差に晒されます。この温度変化は、潤滑剤の粘度、金属部品の膨張収縮、ゴムの硬化など、移動機構のあらゆる要素に影響を及ぼします。

月面では真空環境のため、通常のグリースが蒸発してしまいます。そのため、二硫化モリブデンなどの固体潤滑剤が使われます。

ここまでで、惑星表面がローバーにとっていかに厳しい環境かがわかりました。では、このような過酷な環境で車輪はどのように力を伝え、ローバーを前に進めるのでしょうか。次に、車輪の力学モデルを詳しく見ていきましょう。

車輪の力学モデル — 駆動力・走行抵抗・スリップ率

剛性車輪と変形可能地盤

地球上の自動車のタイヤは弾性変形するゴム製ですが、惑星探査ローバーの車輪はアルミニウム製の剛性車輪です。なぜゴムタイヤを使わないのかというと、真空中でのゴムのガス放出(アウトガス)、極端な温度変化による劣化、そして長寿命(数年以上の運用)を確保できないためです。

剛性車輪が柔らかい砂地を走行する場合、車輪自体は変形しませんが、地面(土壌)の方が変形します。このような「剛体が変形する媒質の上を転がる」問題は、テラメカニクス(terramechanics)という学問分野で扱われます。

車輪に作用する力

ローバーの1つの車輪に作用する力を整理しましょう。車輪の半径を $r$、車軸に加わる垂直荷重を $W$、車輪の角速度を $\omega$、車軸に印加する駆動トルクを $\tau$ とします。

車輪が地面と接触している部分には、以下の3つの力が作用します。

$$ \begin{equation} \text{垂直方向: } N = W + F_z^{\text{dyn}} \end{equation} $$

ここで $N$ は地面からの垂直抗力、$F_z^{\text{dyn}}$ は動的な荷重変動(加減速時の荷重移動)です。静的な場合は $N = W$ と近似できます。

水平方向には駆動力(牽引力)$F_D$ と走行抵抗 $F_R$ が作用します。

$$ \begin{equation} \text{水平方向(前進方向): } F_{\text{net}} = F_D – F_R \end{equation} $$

車輪が前進するためには、駆動力 $F_D$ が走行抵抗 $F_R$ を上回る必要があります。これは直感的にも明らかです — ペダルを漕ぐ力が砂の抵抗より大きくなければ、自転車は前に進みません。

スリップ率の定義

車輪の力学を理解するうえで最も重要な概念の一つがスリップ率(slip ratio)です。

日常的な感覚で考えてみましょう。氷の上で車のタイヤを回すと、タイヤは高速で回転するのに車はなかなか前に進みません。これは、タイヤの周速度と車体の実際の前進速度の間にズレがあるためです。このズレを定量化するのがスリップ率です。

スリップ率 $s$ は、車輪の周速度 $r\omega$ と車体の実際の前進速度 $v$ を用いて次のように定義されます。

駆動時(アクセルを踏んでいる場合、$r\omega \geq v$):

$$ \begin{equation} s = \frac{r\omega – v}{r\omega} \end{equation} $$

制動時(ブレーキをかけている場合、$v \geq r\omega$):

$$ \begin{equation} s = \frac{r\omega – v}{v} \end{equation} $$

スリップ率の物理的意味を整理します。

スリップ率 $s$ 状態
$s = 0$ 純転がり(滑りなし)
$0 < s < 1$ 部分的なスリップ(通常走行)
$s = 1$ 完全スリップ(車輪空転、前進速度ゼロ)
$s < 0$ 制動スリップ(ブレーキ)

惑星探査ローバーにとって特に問題になるのは、砂地で $s$ が大きくなる(スリップが増大する)状況です。スリップ率が約0.4〜0.5を超えると、車輪は砂を掘り始め、逆に沈み込みが増大して状況が悪化するという正のフィードバックに陥ります。NASAのSpiritが砂地で動けなくなったのも、まさにこのメカニズムです。

牽引力とスリップ率の関係 — Janosi-Hanamotoモデル

砂地での車輪の牽引力とスリップ率の関係を定量的にモデル化する方法として、Janosi-Hanamoroモデルが広く用いられています。このモデルでは、土壌のせん断応力 $\tau_s$ がスリップ変位 $j$ に応じて指数関数的に飽和する様子を表現します。

まず、土壌のせん断応力 $\tau_s$ は、砂地の場合にモール・クーロンの破壊規準に従います。

$$ \begin{equation} \tau_{\max} = c + \sigma \tan\phi \end{equation} $$

ここで $c$ は土壌の粘着力(砂の場合はほぼゼロ)、$\sigma$ は垂直応力、$\phi$ は内部摩擦角です。火星の砂(レゴリス)では、$c \approx 0 \sim 1$ kPa、$\phi \approx 30° \sim 40°$ 程度の値が報告されています。

Janosi-Hanamoroモデルでは、せん断応力の発達を次のように表現します。

$$ \begin{equation} \tau_s(j) = \tau_{\max} \left(1 – e^{-j/K}\right) \end{equation} $$

ここで $j$ はせん断変位、$K$ はせん断変形係数(土壌の種類によって決まる定数で、単位はm)です。

せん断変位 $j$ とスリップ率 $s$ の関係を導きましょう。車輪の接地長さを $l$ とすると、接地点での土壌のせん断変位は、車輪の接地開始点からの距離 $x$ に比例して次のように表されます。

$$ \begin{equation} j(x) = s \cdot x \end{equation} $$

この式は直感的には「スリップ率 $s$ の分だけ車輪が土壌に対してずれる」ことを意味しています。$s = 0$(滑りなし)なら $j = 0$、$s = 1$(完全空転)なら $j = x$ となり、接地点全体にわたって車輪と地面が完全にずれていることを表します。

車輪全体の牽引力 $F_D$ は、接地面全体にわたるせん断応力の積分で求められます。接地幅を $b$(車輪幅)とすると、

$$ F_D = b \int_0^l \tau_s(j(x)) \, dx $$

式(6)と式(7)を代入すると、

$$ F_D = b \int_0^l \tau_{\max} \left(1 – e^{-sx/K}\right) dx $$

この積分を実行しましょう。まず $\tau_{\max}$ と $b$ は $x$ に依存しないので外に出します。

$$ F_D = b \, \tau_{\max} \int_0^l \left(1 – e^{-sx/K}\right) dx $$

括弧内を展開して項ごとに積分すると、

$$ F_D = b \, \tau_{\max} \left[ x + \frac{K}{s} e^{-sx/K} \right]_0^l $$

上端 $x = l$ と下端 $x = 0$ を代入すると、

$$ F_D = b \, \tau_{\max} \left[ l + \frac{K}{s} e^{-sl/K} – \frac{K}{s} \right] $$

整理して最終的な牽引力の式を得ます。

$$ \begin{equation} F_D = b \, l \, \tau_{\max} \left[ 1 – \frac{K}{sl} \left(1 – e^{-sl/K}\right) \right] \end{equation} $$

この式の物理的意味を読み取ってみましょう。

  • $s \to 0$(スリップなし)のとき: 指数関数をテイラー展開すると $F_D \to 0$ となり、滑りがなければ牽引力は発生しません。これは「タイヤが地面に対してわずかでも滑らないとグリップが生まれない」という直感に合います。
  • $s \to 1$(完全空転)のとき: $F_D \to b \, l \, \tau_{\max} [1 – (K/l)(1 – e^{-l/K})]$ となり、牽引力は飽和に近づきます。
  • $K$ が小さい(硬い地面)ほど、少ないスリップで最大牽引力に達します。$K$ が大きい(柔らかい砂地)ほど、より多くのスリップが必要です。

走行抵抗(圧密抵抗)

砂地を走行するとき、車輪は地面を踏み固めながら進みます。この「土を押し固める」ために消費されるエネルギーが走行抵抗(圧密抵抗)$F_R$ です。

ベッカーの沈下モデル(Bekker model)では、車輪が土壌に沈み込む深さ $z_0$ における圧力を次のようにモデル化します。

$$ \begin{equation} p(z) = \left(\frac{k_c}{b} + k_\phi\right) z^n \end{equation} $$

ここで $k_c$ は粘着力に関する沈下係数、$k_\phi$ は摩擦角に関する沈下係数、$b$ は車輪幅、$n$ は沈下指数(土壌パラメータ)です。これらはすべて土壌の性質を表す定数で、実験的に決定されます。

車輪の沈下深さ $z_0$ は、接地面積にかかる荷重と土壌の支持力がつり合う条件から決まります。剛性車輪(半径 $r$)の場合、幾何学的な関係から接地長さ $l$ と沈下深さ $z_0$ の間に次の近似が成り立ちます。

$$ \begin{equation} l \approx \sqrt{2 r z_0} \end{equation} $$

この式は、円の弧と弦の幾何学から導かれます。$z_0 \ll r$(沈下が車輪半径に比べて小さい)の仮定のもとで、接地長さは車輪半径と沈下深さの幾何平均に比例します。

圧密抵抗 $F_R$ は、車輪が通過した後にできる溝(車輪の轍)の深さ $z_0$ まで土壌を圧密するのに必要な仕事率から求まります。

$$ \begin{equation} F_R = b \int_0^{z_0} p(z) \, dz = b \left(\frac{k_c}{b} + k_\phi\right) \frac{z_0^{n+1}}{n+1} \end{equation} $$

この式から読み取れる重要なポイントは以下の通りです。

  1. 沈下 $z_0$ が深いほど抵抗が急激に増大する: $n$ は一般に $0.5 \sim 1.5$ の値をとるため、$z_0^{n+1}$ は沈下の1.5〜2.5乗で増大します
  2. 車輪幅 $b$ を大きくすると沈下が減る: 接地面積が増えて接地圧が下がるためです。ただし質量も増えるのでトレードオフがあります
  3. 車輪半径 $r$ を大きくすると有利: 式(10)より、$r$ が大きいほど同じ $z_0$ に対して接地長さ $l$ が大きくなり、荷重を広く分散できます

ここまでで、車輪の力学モデル — 牽引力と走行抵抗の両面 — を定式化しました。これらの式を使えば、ローバーが特定の土壌条件でどの程度のスリップ率で走行可能か、あるいはスタックするかを予測できます。

では次に、これらの力学モデルを Python で実装し、スリップ率と牽引力の関係を可視化してみましょう。

Pythonで見る車輪の力学 — 牽引力曲線とスリップの関係

まず、Janosi-Hanamoroモデルによる牽引力のスリップ率依存性を可視化します。地球、火星、月の重力環境それぞれでの牽引力の変化を比較してみましょう。

import numpy as np
import matplotlib.pyplot as plt

# --- 車輪パラメータ(Curiosity相当)---
r_wheel = 0.25       # 車輪半径 [m]
b_wheel = 0.40       # 車輪幅 [m]
mass_rover = 899      # ローバー質量 [kg]
n_wheels = 6          # 車輪数

# --- 土壌パラメータ(火星レゴリス相当) ---
c_soil = 0.5e3        # 粘着力 [Pa](砂地、ほぼ0に近い)
phi_soil = np.radians(35)  # 内部摩擦角 [rad]
K_shear = 0.02        # せん断変形係数 [m]

# --- 各天体の重力加速度 ---
g_earth = 9.81
g_mars = 3.72
g_moon = 1.62

def traction_force(slip, W, b, l, c, phi, K):
    """Janosi-Hanamoroモデルによる牽引力"""
    sigma = W / (b * l)  # 平均接地圧
    tau_max = c + sigma * np.tan(phi)
    # スリップ率0で0除算を避ける
    s_safe = np.where(slip > 1e-10, slip, 1e-10)
    F_D = b * l * tau_max * (1 - (K / (s_safe * l)) * (1 - np.exp(-s_safe * l / K)))
    return F_D

# スリップ率の範囲
slip_range = np.linspace(0.001, 0.99, 500)

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# --- 左パネル: 各天体での牽引力 ---
for g_val, label, color in [(g_earth, 'Earth (g=9.81)', '#2196F3'),
                              (g_mars, 'Mars (g=3.72)', '#FF5722'),
                              (g_moon, 'Moon (g=1.62)', '#9E9E9E')]:
    W_per_wheel = mass_rover * g_val / n_wheels
    z0 = 0.03  # 仮定: 沈下深さ 3cm
    l_contact = np.sqrt(2 * r_wheel * z0)
    F_D = traction_force(slip_range, W_per_wheel, b_wheel, l_contact,
                         c_soil, phi_soil, K_shear)
    axes[0].plot(slip_range * 100, F_D, label=label, color=color, linewidth=2)

axes[0].set_xlabel('Slip ratio [%]', fontsize=12)
axes[0].set_ylabel('Traction force per wheel [N]', fontsize=12)
axes[0].set_title('Traction Force vs Slip Ratio (Janosi-Hanamoto)', fontsize=13)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)
axes[0].set_xlim(0, 100)

# --- 右パネル: 正味駆動力(牽引力 - 圧密抵抗)---
# ベッカーモデル: 圧密抵抗
kc = 1.4e3    # [N/m^(n+1)]
kphi = 820e3  # [N/m^(n+2)]
n_bekker = 1.0

def compaction_resistance(z0, b, kc, kphi, n):
    """ベッカーモデルによる圧密抵抗"""
    return b * (kc / b + kphi) * z0**(n + 1) / (n + 1)

# 火星環境でのスリップ率 vs 正味駆動力
g_val = g_mars
W_per_wheel = mass_rover * g_val / n_wheels
z0_values = [0.01, 0.03, 0.05, 0.08]  # 沈下深さ [m]

for z0 in z0_values:
    l_contact = np.sqrt(2 * r_wheel * z0)
    F_D = traction_force(slip_range, W_per_wheel, b_wheel, l_contact,
                         c_soil, phi_soil, K_shear)
    F_R = compaction_resistance(z0, b_wheel, kc, kphi, n_bekker)
    F_net = F_D - F_R
    axes[1].plot(slip_range * 100, F_net,
                 label=f'Sinkage z₀ = {z0*100:.0f} cm', linewidth=2)

axes[1].axhline(y=0, color='red', linestyle='--', alpha=0.5, label='F_net = 0 (stuck)')
axes[1].set_xlabel('Slip ratio [%]', fontsize=12)
axes[1].set_ylabel('Net driving force per wheel [N]', fontsize=12)
axes[1].set_title('Net Driving Force on Mars', fontsize=13)
axes[1].legend(fontsize=10)
axes[1].grid(True, alpha=0.3)
axes[1].set_xlim(0, 100)

plt.tight_layout()
plt.savefig('rover_traction.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフからいくつかの重要な特徴が読み取れます。

左パネルの牽引力曲線を見ると、3つの天体で牽引力に大きな差があることがわかります。地球環境では1車輪あたり数百Nの牽引力が得られますが、月面ではその1/6以下に落ち込みます。これは重力が小さいほど垂直抗力が低下し、土壌との摩擦(せん断強度)が減少するためです。また、スリップ率が約20〜30%の領域で牽引力が飽和に近づく様子も見て取れます。

右パネルの正味駆動力(牽引力から圧密抵抗を引いたもの)は、さらに実用的な情報を与えてくれます。沈下深さ $z_0$ が大きい(砂が柔らかい)ほど圧密抵抗が急増し、正味駆動力がゼロ以下になる領域が広がります。$z_0 = 8$ cm の深い沈下条件では、どのスリップ率でもほとんど正味の駆動力が得られない、つまりスタックの状態に陥ることが見て取れます。これがまさにSpiritが経験した状況です。

次に、もう一つ重要な可視化として、車輪直径と沈下深さの関係を見てみましょう。

import numpy as np
import matplotlib.pyplot as plt

# --- パラメータ ---
mass_rover = 899     # [kg] Curiosity相当
n_wheels = 6
g_mars = 3.72        # [m/s^2]
b_wheel = 0.40       # [m]
W_per_wheel = mass_rover * g_mars / n_wheels

# ベッカー土壌パラメータ(火星レゴリス)
kc = 1.4e3
kphi = 820e3
n_bekker = 1.0

# 車輪直径の範囲
diameters = np.linspace(0.15, 0.80, 200)  # [m]

# 沈下深さの計算(荷重と土壌支持力の平衡から近似的に求める)
def compute_sinkage(W, b, r, kc, kphi, n):
    """荷重平衡から沈下深さを推定(反復法)"""
    z0 = 0.01  # 初期推定
    for _ in range(100):
        l = np.sqrt(2 * r * z0) if z0 > 0 else 0.01
        # 接地面での平均圧力 = 荷重 / 接地面積
        p_mean = W / (b * l)
        # ベッカーモデルでの圧力-沈下関係から沈下を更新
        z0_new = (p_mean / (kc / b + kphi))**(1.0 / n)
        if abs(z0_new - z0) < 1e-6:
            break
        z0 = 0.5 * z0 + 0.5 * z0_new  # 緩和法で安定化
    return z0

sinkages = []
for d in diameters:
    r = d / 2
    z0 = compute_sinkage(W_per_wheel, b_wheel, r, kc, kphi, n_bekker)
    sinkages.append(z0 * 100)  # cm変換

# プロット
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(diameters * 100, sinkages, 'b-', linewidth=2.5)

# 実際のローバーの車輪直径をマーク
rovers = {
    'Sojourner': 13,
    'Spirit/Opportunity': 26,
    'Curiosity': 50,
    'Perseverance': 52.5,
}
for name, diam in rovers.items():
    r = diam / 200  # mに変換
    z0 = compute_sinkage(W_per_wheel, b_wheel, r, kc, kphi, n_bekker)
    ax.plot(diam, z0 * 100, 'o', markersize=10, zorder=5)
    ax.annotate(name, (diam, z0 * 100), textcoords="offset points",
                xytext=(10, 5), fontsize=10)

ax.set_xlabel('Wheel diameter [cm]', fontsize=12)
ax.set_ylabel('Sinkage depth [cm]', fontsize=12)
ax.set_title('Wheel Diameter vs Sinkage Depth on Mars', fontsize=13)
ax.grid(True, alpha=0.3)
ax.set_xlim(10, 85)
plt.tight_layout()
plt.savefig('rover_sinkage.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフから、車輪直径が大きくなるほど沈下深さが小さくなるという明確な傾向が確認できます。これは車輪半径が大きいほど接地面積が広がり、接地圧が低下するためです。Sojourner(直径13cm)のような小さな車輪は深く沈み込みますが、Curiosity/Perseverance級(直径50cm超)になると沈下は大幅に抑えられます。歴代のローバーが世代を重ねるごとに車輪を大きくしてきた理由が、この力学モデルから定量的に理解できます。

ただし、車輪を大きくすると質量やモーターの要求トルクも増加するため、実際の設計では沈下低減と質量ペナルティのトレードオフを慎重に検討する必要があります。

ここまで車輪単体の力学を見てきました。しかし、ローバーの走行性能を決めるのは車輪だけではありません。6つの車輪をどのように車体につなぎ、凹凸のある地形に車輪を追従させるか — つまりサスペンション機構が極めて重要です。次に、火星探査ローバーの代名詞ともいえるロッカーボギー機構を詳しく見ていきましょう。

ロッカーボギー機構 — 火星探査の標準サスペンション

なぜ特殊なサスペンション機構が必要なのか

自動車のサスペンションは、バネとダンパー(ショックアブソーバー)を組み合わせた弾性機構です。道路の凹凸を吸収し、乗り心地を良くすることが主な目的です。

しかし惑星探査ローバーでは、バネやダンパーは使いたくありません。理由は以下の通りです。

  1. 信頼性: バネは疲労破壊の可能性があり、数年間にわたるミッションで故障のリスクが高まります
  2. 真空環境: 油圧ダンパーの液体は蒸発・漏出のリスクがあります
  3. 温度: 極端な温度変化でバネ定数やダンパー特性が変動します
  4. 軽量化: バネ・ダンパーは余分な質量です

そこで、JPL(Jet Propulsion Laboratory)のエンジニア Don Bickler が1989年に発明したのがロッカーボギー機構(Rocker-Bogie suspension)です。この機構は、バネもダンパーも使わない純粋なリンク機構でありながら、車輪直径以上の障害物を乗り越え、最大45°の傾斜でも安定して走行できるという驚異的な性能を持っています。

機構の基本構造

ロッカーボギー機構は、左右それぞれに「ロッカー」と「ボギー」という2つのリンクを組み合わせた構造です。

ボギー(Bogie): 前輪と中輪をつなぐ短いリンクです。2つの車輪が1つのピボット(回転軸)で車体側のロッカーに接続されています。

ロッカー(Rocker): ボギーのピボットと後輪をつなぐ長いリンクです。ロッカーの中間点付近で車体(ディファレンシャルバー)に接続されています。

ディファレンシャルバー: 左右のロッカーをつなぐ差動機構です。左側のロッカーが上がれば右側が下がる、というシーソーのような動きをします。

この構成により、6つの車輪は独立に上下動することができ、かつ車体は常に6輪の平均的な高さに保たれます。

キネマティクスの解析

ロッカーボギー機構のキネマティクス(運動学)を2次元で解析してみましょう。簡単のため、ローバーを横から見た側面図で考えます。

座標系を以下のように設定します。$x$ 軸を前進方向(水平)、$z$ 軸を鉛直上方にとります。

ボギーリンクについて、前輪の接地点 $\bm{P}_1 = (x_1, z_1)$ と中輪の接地点 $\bm{P}_2 = (x_2, z_2)$ が地形によって決まるとします。ボギーのピボット点 $\bm{P}_B$ は、2輪の間のリンク上にあります。ボギーリンクの長さ(前輪からピボットまで)を $L_{b1}$、中輪からピボットまでを $L_{b2}$ とします。

ボギーの傾斜角 $\theta_B$ は、前輪と中輪の高さの差から求まります。

$$ \begin{equation} \theta_B = \arctan\left(\frac{z_2 – z_1}{x_2 – x_1}\right) \end{equation} $$

ボギーのピボット点の位置は、前輪位置からボギーリンクに沿って求めます。

$$ \begin{equation} \bm{P}_B = \bm{P}_1 + L_{b1} \begin{pmatrix} \cos\theta_B \\ \sin\theta_B \end{pmatrix} \end{equation} $$

次に、ロッカーリンクについて考えます。ロッカーはボギーピボット $\bm{P}_B$ と後輪の接地点 $\bm{P}_3 = (x_3, z_3)$ をつなぎます。ロッカーの傾斜角 $\theta_R$ は、

$$ \begin{equation} \theta_R = \arctan\left(\frac{z_3 – z_{B}}{x_3 – x_{B}}\right) \end{equation} $$

ここで $(x_B, z_B)$ はボギーピボットの座標です。

車体の姿勢角(ピッチ角)$\theta_{\text{body}}$ は、左右のロッカー角の平均で決まります。ディファレンシャルバーの機構により、

$$ \begin{equation} \theta_{\text{body}} = \frac{\theta_R^{\text{left}} + \theta_R^{\text{right}}}{2} \end{equation} $$

この式が意味することは、片側のロッカーが岩を乗り越えて傾いても、車体はその傾きの半分しか傾かないということです。6輪が独立に地形に追従しつつ、車体の姿勢変動を最小限に抑える — これがロッカーボギー機構の核心的な利点です。

障害物乗り越え能力

ロッカーボギー機構の最も印象的な特性は、車輪直径と同程度の高さの障害物を乗り越えられることです。

障害物の乗り越えは、以下のステップで進行します。

  1. 前輪が障害物に接触: 前輪が岩に当たると、ボギーリンクが回転し、前輪が持ち上がります。このとき中輪と後輪は地面に接地したままで、車体を支えています
  2. 前輪が障害物の上に乗る: 前輪が岩の上に乗ると、ボギーリンクが傾斜した状態で安定します
  3. 中輪が障害物に接触: 中輪が岩に達すると、今度はボギーのピボットを中心に前輪と中輪が再配置されます
  4. 後輪が障害物に接触: 最後に後輪が岩に達し、ロッカーリンクの回転により乗り越えます

この過程で重要なのは、常に少なくとも4輪以上が地面に接地していることです。6輪のうち1輪が障害物を乗り越えている最中でも、残りの5輪で車体を安定に支持できます。

乗り越え可能な障害物の高さ $h_{\max}$ は、概略的に次のように推定されます。

$$ \begin{equation} h_{\max} \approx r_{\text{wheel}} \times (1 + \sin\alpha_{\max}) \end{equation} $$

ここで $\alpha_{\max}$ は車輪が障害物に対して乗り上がれる最大角度で、車輪と岩の摩擦係数に依存します。一般に、$h_{\max}$ は車輪半径の1.5倍程度まで可能とされています。Curiosityの車輪半径は25cmなので、理論上は約37cm程度の障害物まで乗り越え可能です。

ここまでの理論を踏まえて、ロッカーボギー機構のキネマティクスをPythonでシミュレーションしてみましょう。

Pythonによるロッカーボギーのシミュレーション

ロッカーボギー機構の動作を可視化するために、不整地上を走行する際の車体姿勢の変化をシミュレーションします。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle

class RockerBogie:
    """ロッカーボギー機構の2Dキネマティクスモデル"""

    def __init__(self, L_bogie_front=0.40, L_bogie_rear=0.40,
                 L_rocker_front=0.55, L_rocker_rear=0.65,
                 wheel_radius=0.25, wheel_base_y=0.80):
        """
        Parameters
        ----------
        L_bogie_front : float - ボギーピボットから前輪までの距離 [m]
        L_bogie_rear  : float - ボギーピボットから中輪までの距離 [m]
        L_rocker_front: float - ロッカーピボットからボギーピボットまでの距離 [m]
        L_rocker_rear : float - ロッカーピボットから後輪までの距離 [m]
        wheel_radius  : float - 車輪半径 [m]
        wheel_base_y  : float - 左右車輪間距離(片側)[m]
        """
        self.Lbf = L_bogie_front
        self.Lbr = L_bogie_rear
        self.Lrf = L_rocker_front
        self.Lrr = L_rocker_rear
        self.r = wheel_radius
        self.y_offset = wheel_base_y

    def compute_kinematics(self, terrain_func, x_body):
        """
        車体中心のx座標を指定して、各車輪位置と車体姿勢を計算する

        Parameters
        ----------
        terrain_func : callable - x -> z の地形関数
        x_body       : float   - 車体中心のx座標

        Returns
        -------
        dict : 各部の位置と角度
        """
        # 車輪のx座標(車体中心からの相対位置)
        x_front = x_body + self.Lrf + self.Lbf  # 前輪
        x_mid = x_body + self.Lrf - self.Lbr    # 中輪
        x_rear = x_body - self.Lrr               # 後輪

        # 各車輪の接地点高さ(地形 + 車輪半径)
        z_front = terrain_func(x_front) + self.r
        z_mid = terrain_func(x_mid) + self.r
        z_rear = terrain_func(x_rear) + self.r

        # ボギー角度
        dx_bogie = x_mid - x_front
        dz_bogie = z_mid - z_front
        theta_bogie = np.arctan2(dz_bogie, abs(dx_bogie))

        # ボギーピボットの位置
        bogie_pivot_x = x_front + self.Lbf * np.cos(theta_bogie)
        bogie_pivot_z = z_front + self.Lbf * np.sin(theta_bogie)

        # ロッカー角度
        dx_rocker = x_rear - bogie_pivot_x
        dz_rocker = z_rear - bogie_pivot_z
        theta_rocker = np.arctan2(dz_rocker, abs(dx_rocker))

        # 車体ピッチ角(ディファレンシャルで半分に)
        theta_body = theta_rocker  # 2D片側モデルなので近似

        # 車体中心の高さ
        z_body = (bogie_pivot_z + z_rear) / 2 + 0.30  # 車体オフセット

        return {
            'wheels': [(x_front, z_front), (x_mid, z_mid), (x_rear, z_rear)],
            'bogie_pivot': (bogie_pivot_x, bogie_pivot_z),
            'theta_bogie': theta_bogie,
            'theta_rocker': theta_rocker,
            'theta_body': np.degrees(theta_body),
            'body_center': (x_body, z_body),
        }


def terrain_with_obstacles(x):
    """岩場を模擬した地形関数"""
    z = np.zeros_like(np.atleast_1d(x).astype(float))
    x = np.atleast_1d(x).astype(float)
    # 障害物1: 高さ15cmの岩(x=2.0付近)
    mask1 = (x > 1.8) & (x < 2.2)
    z[mask1] = 0.15 * np.cos((x[mask1] - 2.0) / 0.2 * np.pi / 2)
    # 障害物2: 高さ20cmの岩(x=4.0付近)
    mask2 = (x > 3.7) & (x < 4.3)
    z[mask2] = 0.20 * np.cos((x[mask2] - 4.0) / 0.3 * np.pi / 2)
    # なだらかな起伏
    z += 0.05 * np.sin(x * 0.8)
    return z if len(z) > 1 else z[0]


# シミュレーション実行
rb = RockerBogie()
x_positions = np.linspace(0.5, 6.0, 300)

body_pitch_angles = []
body_heights = []

for x_b in x_positions:
    result = rb.compute_kinematics(terrain_with_obstacles, x_b)
    body_pitch_angles.append(result['theta_body'])
    body_heights.append(result['body_center'][1])

# --- 可視化 ---
fig, axes = plt.subplots(2, 1, figsize=(12, 8), gridspec_kw={'height_ratios': [2, 1]})

# 上段: 地形と車体高さ
x_terrain = np.linspace(0, 7, 1000)
z_terrain = terrain_with_obstacles(x_terrain)

axes[0].fill_between(x_terrain, z_terrain, -0.1, color='#8B7355', alpha=0.3, label='Terrain')
axes[0].plot(x_terrain, z_terrain, color='#8B7355', linewidth=2)
axes[0].plot(x_positions, body_heights, 'b-', linewidth=2, label='Body height')
axes[0].set_ylabel('Height [m]', fontsize=12)
axes[0].set_title('Rocker-Bogie Traversal over Rocky Terrain', fontsize=13)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)
axes[0].set_xlim(0, 6.5)

# 特定位置でのローバー姿勢を描画
for x_snapshot in [1.0, 2.0, 3.0, 4.0, 5.0]:
    result = rb.compute_kinematics(terrain_with_obstacles, x_snapshot)
    wheels = result['wheels']
    bp = result['bogie_pivot']

    for wx, wz in wheels:
        circle = Circle((wx, wz), rb.r, fill=False, edgecolor='red', linewidth=1.5)
        axes[0].add_patch(circle)

    # ボギーリンク(前輪-中輪)
    axes[0].plot([wheels[0][0], bp[0]], [wheels[0][1], bp[1]], 'k-', linewidth=1.5)
    axes[0].plot([wheels[1][0], bp[0]], [wheels[1][1], bp[1]], 'k-', linewidth=1.5)
    # ロッカーリンク(ボギーピボット-後輪)
    axes[0].plot([bp[0], wheels[2][0]], [bp[1], wheels[2][1]], 'k-', linewidth=1.5)

axes[0].set_ylim(-0.1, 1.2)

# 下段: ピッチ角の変化
axes[1].plot(x_positions, body_pitch_angles, 'r-', linewidth=2)
axes[1].set_xlabel('Body x-position [m]', fontsize=12)
axes[1].set_ylabel('Body pitch [deg]', fontsize=12)
axes[1].set_title('Body Pitch Angle during Traversal', fontsize=13)
axes[1].grid(True, alpha=0.3)
axes[1].set_xlim(0, 6.5)
axes[1].axhline(y=0, color='gray', linestyle='--', alpha=0.5)

plt.tight_layout()
plt.savefig('rocker_bogie_sim.png', dpi=150, bbox_inches='tight')
plt.show()

上段のグラフでは、岩場を通過するロッカーボギー機構の様子が可視化されています。5つのスナップショット位置で、車輪、ボギーリンク、ロッカーリンクの配置が描かれています。注目すべきは、地形に凹凸があっても車体の高さがほぼ一定に保たれている点です。車輪が岩を乗り越えるとリンク機構が回転し、その変位を吸収しています。

下段のピッチ角の推移を見ると、岩($x \approx 2.0$ と $x \approx 4.0$)を通過する際にピッチ角が変動していますが、その振幅は比較的小さく抑えられています。もしリジッド(剛体)フレームで6輪をつないでいたら、1つの車輪が岩に乗り上げた瞬間に車体全体が大きく傾き、最悪の場合は他の車輪が地面から浮いてトラクションを失います。ロッカーボギー機構は、リンクの柔軟な回転によってこの問題を解決しています。

次に、ロッカーボギー以外にもさまざまな移動方式が提案されています。それぞれの特徴と使い分けを比較してみましょう。

各種移動方式の比較 — 車輪・クローラ・脚・ハイブリッド

車輪方式

これまで詳しく見てきたように、車輪方式は惑星探査ローバーの主流です。車輪方式の利点と欠点を整理します。

利点:

  • エネルギー効率が高い(硬い平坦面での転がり抵抗が最小)
  • 機構が比較的単純で信頼性が高い
  • 速度制御とステアリングが容易
  • 長い運用実績(1997年 Sojourner 以降、全ての火星ローバーが採用)

欠点:

  • 柔らかい砂地でスリップ・沈下しやすい
  • 車輪直径を大幅に超える段差は乗り越えられない
  • 深いクレバスは通過不可能

車輪方式のエネルギー効率を定量的に評価する指標として、比走行抵抗(specific resistance)$\varepsilon$ が用いられます。

$$ \begin{equation} \varepsilon = \frac{F_R}{W} \end{equation} $$

この値は、車体重量あたりの走行抵抗を表します。硬い平面での車輪の比走行抵抗は $\varepsilon \approx 0.01 \sim 0.05$ であり、後述するクローラ方式($\varepsilon \approx 0.05 \sim 0.15$)や脚方式($\varepsilon \approx 0.15 \sim 0.30$)と比較して圧倒的に小さい値です。

クローラ(履帯)方式

戦車やブルドーザーで馴染みのある履帯(キャタピラ)方式は、接地面積が大きいため砂地での走破性に優れます。

利点:

  • 広い接地面積により接地圧が低く、柔らかい砂地に強い
  • 段差の乗り越え能力が高い
  • スリップ率が低い

欠点:

  • 機構が複雑(多数の転輪、テンション機構、トラックリンク)
  • 質量が大きい
  • 故障箇所が多く、真空・極低温環境での信頼性に不安がある
  • ステアリングに差動(スキッドステア)が必要で、旋回時のエネルギー消費が大きい

惑星探査ではクローラ方式は主流になっていません。その主な理由は、多数の可動部が長期ミッションでの故障リスクを高めるからです。ただし、極めて柔らかい表面(月面の微細レゴリスなど)を想定した将来のミッションでは、クローラ方式が再検討される可能性もあります。

脚方式

昆虫や動物のように脚で歩くロボットです。6脚(ヘキサポッド)や4脚型が研究されています。

利点:

  • 離散的な足場を選んで歩けるため、不整地走破性が極めて高い
  • 大きな段差やクレバスを跨いで越えられる
  • 足の配置を変えることで車体姿勢を能動的に制御できる

欠点:

  • エネルギー効率が低い(脚を持ち上げるたびにエネルギーを消費する)
  • 歩行速度が遅い
  • 制御が複雑(多自由度の脚を協調制御する必要がある)
  • 信頼性(多数のアクチュエータとセンサ)

脚方式のエネルギー効率の悪さは、輸送コスト(cost of transport)$c_T$ で定量的に表されます。

$$ \begin{equation} c_T = \frac{P}{m g v} \end{equation} $$

ここで $P$ は消費電力、$m$ は車体質量、$g$ は重力加速度、$v$ は移動速度です。車輪方式の $c_T$ が $0.01 \sim 0.1$ 程度であるのに対し、脚方式は $1 \sim 10$ 程度と1〜2桁大きくなります。太陽光パネルや原子力電池のエネルギー供給量が限られる惑星探査では、この効率の差は致命的です。

ハイブリッド方式(車輪-脚複合)

車輪の効率と脚の走破性を両立しようとするのがハイブリッド方式です。各脚の先端に車輪を取り付けた「車輪-脚」(wheel-leg)ロボットです。

ESA(欧州宇宙機関)が研究していたSherpaTTや、スイスETHのANYmal(車輪付きバージョン)がこの方式の代表例です。平坦な地形では車輪で高速・高効率に移動し、障害物に遭遇したら脚モードに切り替えて乗り越えます。

利点:

  • 平坦地での高効率走行と不整地での高走破性を両立
  • 車体高さを能動的に調整可能

欠点:

  • 機構・制御ともに最も複雑
  • 質量が大きい
  • 宇宙環境での実績がない

移動方式の定量比較

各方式の特性を表にまとめます。

方式 比走行抵抗 $\varepsilon$ 最大段差 最大速度 信頼性 制御複雑度
車輪 0.01〜0.05 $\approx 1.5r$
クローラ 0.05〜0.15 $\approx 2h$
0.15〜0.30 $\approx L_{\text{leg}}$
ハイブリッド 0.02〜0.10 $\approx L_{\text{leg}}$ 中〜高 低〜中 最高

ここで $r$ は車輪半径、$h$ はクローラの高さ、$L_{\text{leg}}$ は脚長です。

結局のところ、現時点までの惑星探査では車輪方式 + ロッカーボギーサスペンションが圧倒的な実績を持っています。エネルギー効率、信頼性、重量のバランスが最も優れているためです。

では、これらの移動方式の特性をPythonで比較・可視化してみましょう。

移動方式のエネルギー効率比較 — Pythonによる可視化

各移動方式の輸送コストを傾斜角に対してプロットし、どの条件でどの方式が有利かを定量的に比較します。

import numpy as np
import matplotlib.pyplot as plt

# --- パラメータ ---
g_mars = 3.72  # [m/s^2]
mass = 200     # [kg] 中型ローバー想定

# 各方式の簡易モデル
# 輸送コスト c_T = P / (m * g * v) のモデル

def cost_of_transport_wheel(slope_deg, mu_roll=0.03, eta_motor=0.8):
    """車輪方式の輸送コスト"""
    slope_rad = np.radians(slope_deg)
    # 走行抵抗: 転がり抵抗 + 重力成分
    c_T = (mu_roll * np.cos(slope_rad) + np.sin(slope_rad)) / eta_motor
    return c_T

def cost_of_transport_track(slope_deg, mu_roll=0.08, eta_motor=0.7):
    """クローラ方式の輸送コスト"""
    slope_rad = np.radians(slope_deg)
    c_T = (mu_roll * np.cos(slope_rad) + np.sin(slope_rad)) / eta_motor
    return c_T

def cost_of_transport_leg(slope_deg, c0=0.5, eta_motor=0.6):
    """脚方式の輸送コスト(歩行のオーバーヘッドが大きい)"""
    slope_rad = np.radians(slope_deg)
    # 基本コスト(脚の上げ下げ) + 勾配成分
    c_T = (c0 + np.sin(slope_rad)) / eta_motor
    return c_T

def cost_of_transport_hybrid(slope_deg, mu_roll=0.04, c0_leg=0.2, eta_motor=0.75,
                              transition_slope=15):
    """ハイブリッド方式(低勾配は車輪、高勾配は脚寄り)"""
    slope_rad = np.radians(slope_deg)
    # 勾配に応じて車輪モードと脚モードをブレンド
    alpha = np.clip((slope_deg - transition_slope) / 10, 0, 1)
    c_wheel = (mu_roll * np.cos(slope_rad) + np.sin(slope_rad))
    c_leg = (c0_leg + np.sin(slope_rad))
    c_T = ((1 - alpha) * c_wheel + alpha * c_leg) / eta_motor
    return c_T

slopes = np.linspace(0, 35, 200)

fig, ax = plt.subplots(figsize=(10, 6))

methods = [
    ('Wheel (Rocker-Bogie)', cost_of_transport_wheel, '#2196F3', '-'),
    ('Tracked (Crawler)', cost_of_transport_track, '#FF9800', '--'),
    ('Legged (Hexapod)', cost_of_transport_leg, '#F44336', '-.'),
    ('Hybrid (Wheel-Leg)', cost_of_transport_hybrid, '#4CAF50', ':'),
]

for name, func, color, ls in methods:
    c_T = func(slopes)
    ax.plot(slopes, c_T, label=name, color=color, linewidth=2.5, linestyle=ls)

ax.set_xlabel('Terrain slope [deg]', fontsize=12)
ax.set_ylabel('Cost of Transport $c_T$ [-]', fontsize=12)
ax.set_title('Cost of Transport vs Terrain Slope (Mars Gravity)', fontsize=13)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 35)
ax.set_ylim(0, 1.5)

# 典型的な火星地形の傾斜範囲を示す
ax.axvspan(0, 10, alpha=0.08, color='green', label='_Typical flat')
ax.axvspan(15, 25, alpha=0.08, color='orange', label='_Hilly terrain')
ax.text(5, 1.4, 'Flat terrain', fontsize=10, ha='center', color='green')
ax.text(20, 1.4, 'Hilly terrain', fontsize=10, ha='center', color='orange')

plt.tight_layout()
plt.savefig('cost_of_transport.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフからいくつかの重要な知見が読み取れます。

まず、平坦地(勾配0〜10°)では車輪方式が圧倒的に効率的であることが明確です。輸送コスト $c_T$ が他の方式の数分の1であり、限られたエネルギー予算で最も長い距離を走行できます。火星の探査エリアの大部分は比較的平坦なため、車輪方式が選ばれるのは合理的です。

脚方式は平坦地でも $c_T$ が高く、これは脚を持ち上げて下ろす動作自体にエネルギーが必要なためです。一方、ハイブリッド方式は平坦地では車輪モードで走行するため車輪方式に近い効率を示し、傾斜が増すにつれて脚モードに移行することで走破性を確保します。

急勾配(25°以上)では全方式で $c_T$ が急増しますが、これは重力に逆らって登るためのエネルギーが支配的になるためです。この領域ではどの方式を選んでも大きな差はなく、むしろ走破性(スリップせずに登れるか)が制約条件になります。

では、ここまでの理論的知見を踏まえて、実際に宇宙で活躍した(あるいは活躍中の)ローバーの技術仕様を比較してみましょう。

主要ローバーの技術比較 — Sojourner から Perseverance まで

世代ごとの進化

惑星探査ローバーは、1997年の Sojourner 以来、約30年にわたって進化を続けてきました。各ローバーの主要仕様を比較表にまとめます。

項目 Sojourner (1997) Spirit/Opportunity (2004) Curiosity (2012) Perseverance (2021) 玉兔2号 (2019)
質量 10.6 kg 185 kg 899 kg 1,025 kg 140 kg
車輪数 6 6 6 6 6
車輪直径 13 cm 26 cm 50 cm 52.5 cm 30 cm
車輪材質 アルミ アルミ アルミ アルミ(チタンスポーク) アルミ合金
サスペンション ロッカーボギー ロッカーボギー ロッカーボギー ロッカーボギー ロッカーボギー
最高速度 1 cm/s 5 cm/s 4 cm/s 4.2 cm/s (AutoNav: 120 m/h) 5.5 cm/s
電源 太陽電池 太陽電池 RTG (原子力電池) RTG 太陽電池
設計寿命 7 sol 90 sol 687 sol (1火星年) 687 sol 3か月
実運用期間 83 sol Spirit: 2,208 sol / Opportunity: 5,111 sol 運用中(4,000+ sol) 運用中(1,800+ sol) 運用中(2,200+ sol)
ステアリング 前輪のみ 前後各2輪 前後各2輪 前後各2輪 前後各2輪
自律走行 なし 基本的(AutoNav v1) 改良版 AutoNav AutoNav v2(高速化) 基本的自律走行

Sojourner — はじめの一歩

1997年、Mars Pathfinderミッションで火星に降り立ったSojournerは、人類が別の惑星に送り込んだ最初のローバーです。質量わずか10.6kg、レゴブロック程度のサイズの小さなロボットでしたが、ロッカーボギー機構の有効性を実証しました。

Sojournerの車輪直径はわずか13cmで、先ほどのシミュレーションで見たように沈下が比較的大きくなる寸法です。しかし車体が非常に軽いため、接地圧が低く、実際には問題なく走行できました。車輪にはクリート(突起)が付けられており、砂地でのグリップを向上させています。

Spirit と Opportunity — 長距離走行の実現

2004年に火星の反対側にそれぞれ着陸したSpirit(スピリット)とOpportunity(オポチュニティ)は、ほぼ同一設計の双子ローバーです。Sojournerの約17倍の質量を持ち、車輪直径も26cmに拡大されました。

特筆すべきは、設計寿命90 sol(火星日)に対して、Opportunityは驚異の5,111 sol(約14年)にわたって運用されたことです。この長寿命は、ロッカーボギー機構をはじめとする移動機構の信頼性の高さを実証しています。

一方でSpiritは、2006年に右前輪のモーターが故障し、5輪で後方走行(壊れた車輪を引きずる)を余儀なくされました。そして2009年にTroyの砂地でスタックし、最終的にミッション終了となりました。この経験は、砂地走行の危険性とスリップ制御の重要性を強く印象づけました。

Curiosity — 大型化と原子力電池

2012年に着陸したCuriosity(キュリオシティ)は、ローバーの設計を一変させました。質量899kg — 小型自動車サイズのこのローバーは、原子力電池(RTG: Radioisotope Thermoelectric Generator)を搭載し、太陽光に依存しない電力供給を実現しました。これにより、砂嵐で太陽電池が覆われてミッションが終了するリスク(Opportunityが最終的に経験した事態)を排除しています。

車輪直径は50cmに拡大され、走行性能は大幅に向上しました。しかし、アルミ製の車輪に想定以上の損傷(岩による穿孔)が発生し、JPLは走行経路の選定に細心の注意を払うことになりました。

Perseverance — 改良された車輪と高速自律走行

2021年着陸のPerseverance(パーシヴィアランス)は、Curiosityの教訓を反映した最新のローバーです。

車輪設計はCuriosityから大幅に改良されました。Curiosityの車輪に生じた損傷の主原因は、薄いアルミスキンに鋭利な岩が集中荷重を与えたことでした。Perseveranceでは以下の改善が施されています。

  1. トレッドパターンの変更: Curiosityの「ヘの字」パターンから、わずかに湾曲した直線パターンに変更し、応力集中を緩和
  2. 車輪スキンの厚肉化: 薄い部分を厚くし、耐穿孔性を向上
  3. チタンスポーク: アルミスポークからチタンに変更し、強度と耐久性を向上
  4. 車輪直径の微増: 50cm → 52.5cm

また、AutoNav(自律ナビゲーションシステム)が大幅に高速化され、自律走行時の速度が大幅に向上しました。地形を自動で認識し、安全な経路を計画して走行する能力が格段に進歩しています。

玉兔2号 — 月の裏側を走るローバー

中国の嫦娥4号ミッション(2019年)で月の裏側に着陸した玉兔2号(Yutu-2)は、月面ローバーとして注目すべき存在です。

質量140kg、車輪直径30cmの玉兔2号は、月面の低重力(地球の約1/6)環境に最適化されています。月のレゴリスは火星とは異なり、非常に微細な粒子(平均粒径 $\sim 70$ μm)で構成されています。このため、車輪の沈下特性が火星とは異なり、特に静止摩擦からの起動時に砂を巻き上げやすいという特徴があります。

玉兔2号もロッカーボギー機構を採用していますが、月面特有の設計上の工夫として、太陽高度が高い(温度が極端に上がる)時間帯には走行を停止し、熱制御を優先する運用が行われています。

ここまで各ローバーの仕様を見てきました。次に、これらのローバーの仕様を入力データとして、各ローバーの理論的な走行性能をPythonでシミュレーション・比較してみましょう。

各ローバーの走行性能シミュレーション

各ローバーの仕様を用いて、砂地での最大登坂角度とスリップ率の関係をシミュレーションします。

import numpy as np
import matplotlib.pyplot as plt

# --- 各ローバーの仕様 ---
rovers = {
    'Sojourner': {
        'mass': 10.6, 'n_wheels': 6, 'r_wheel': 0.065,
        'b_wheel': 0.08, 'color': '#9C27B0'
    },
    'Spirit/Opportunity': {
        'mass': 185, 'n_wheels': 6, 'r_wheel': 0.13,
        'b_wheel': 0.16, 'color': '#2196F3'
    },
    'Curiosity': {
        'mass': 899, 'n_wheels': 6, 'r_wheel': 0.25,
        'b_wheel': 0.40, 'color': '#FF5722'
    },
    'Perseverance': {
        'mass': 1025, 'n_wheels': 6, 'r_wheel': 0.2625,
        'b_wheel': 0.40, 'color': '#4CAF50'
    },
    'Yutu-2': {
        'mass': 140, 'n_wheels': 6, 'r_wheel': 0.15,
        'b_wheel': 0.20, 'color': '#FF9800'
    },
}

# 火星レゴリス土壌パラメータ
c_soil = 0.5e3       # [Pa]
phi_soil = np.radians(35)
K_shear = 0.02       # [m]
kc = 1.4e3
kphi = 820e3
n_bekker = 1.0

g_mars = 3.72  # [m/s^2](玉兔2号は月面だが比較のため火星で統一)

def compute_max_slope(mass, n_wheels, r_wheel, b_wheel, g,
                      c, phi, K, kc, kphi, n_bek, slip=0.2):
    """与えられたスリップ率での最大登坂角度を二分法で求める"""
    W_total = mass * g

    for slope_deg in np.arange(0, 45, 0.5):
        slope_rad = np.radians(slope_deg)
        # 斜面上の荷重配分(簡略化: 均等配分)
        W_normal = W_total * np.cos(slope_rad) / n_wheels  # 法線方向荷重/1輪
        F_gravity = W_total * np.sin(slope_rad)  # 重力の斜面方向成分

        # 沈下深さの推定
        z0 = 0.01
        for _ in range(50):
            l = np.sqrt(2 * r_wheel * z0) if z0 > 0 else 0.01
            p_mean = W_normal / (b_wheel * l)
            z0_new = (p_mean / (kc / b_wheel + kphi))**(1.0 / n_bek)
            if abs(z0_new - z0) < 1e-7:
                break
            z0 = 0.5 * z0 + 0.5 * z0_new

        l = np.sqrt(2 * r_wheel * z0)

        # 牽引力(全輪合計)
        sigma = W_normal / (b_wheel * l)
        tau_max = c + sigma * np.tan(phi)
        s_safe = max(slip, 1e-10)
        F_D_per_wheel = b_wheel * l * tau_max * (
            1 - (K / (s_safe * l)) * (1 - np.exp(-s_safe * l / K))
        )
        F_D_total = F_D_per_wheel * n_wheels

        # 圧密抵抗(全輪合計)
        F_R_per_wheel = b_wheel * (kc / b_wheel + kphi) * z0**(n_bek + 1) / (n_bek + 1)
        F_R_total = F_R_per_wheel * n_wheels

        # 正味駆動力 = 牽引力 - 圧密抵抗 - 重力斜面成分
        F_net = F_D_total - F_R_total - F_gravity
        if F_net < 0:
            return slope_deg
    return 45.0

# --- スリップ率を変えた最大登坂角度 ---
slip_range = np.linspace(0.05, 0.60, 100)

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 左パネル: スリップ率 vs 最大登坂角度
for name, spec in rovers.items():
    max_slopes = []
    for s in slip_range:
        ms = compute_max_slope(
            spec['mass'], spec['n_wheels'], spec['r_wheel'], spec['b_wheel'],
            g_mars, c_soil, phi_soil, K_shear, kc, kphi, n_bekker, slip=s
        )
        max_slopes.append(ms)
    axes[0].plot(slip_range * 100, max_slopes, label=name,
                 color=spec['color'], linewidth=2.5)

axes[0].set_xlabel('Slip ratio [%]', fontsize=12)
axes[0].set_ylabel('Max climbable slope [deg]', fontsize=12)
axes[0].set_title('Maximum Slope vs Slip Ratio on Mars', fontsize=13)
axes[0].legend(fontsize=10)
axes[0].grid(True, alpha=0.3)
axes[0].set_xlim(5, 60)

# 右パネル: 各ローバーの質量対走行性能レーダー
# 正規化した比較指標
labels = ['Wheel Diameter', 'Mass Efficiency', 'Contact Area', 'Max Slope\n(s=0.2)']
n_labels = len(labels)
angles_radar = np.linspace(0, 2 * np.pi, n_labels, endpoint=False).tolist()
angles_radar += angles_radar[:1]

for name, spec in rovers.items():
    # 各指標を0-1に正規化
    d_norm = spec['r_wheel'] * 2 / 0.525  # Perseveranceを基準
    # 質量効率 = 車輪面積 / 車体質量
    area = spec['b_wheel'] * np.sqrt(2 * spec['r_wheel'] * 0.03) * spec['n_wheels']
    mass_eff = area / spec['mass'] * 1000
    mass_eff_norm = min(mass_eff / 5.0, 1.0)
    # 接地面積
    contact = spec['b_wheel'] * np.sqrt(2 * spec['r_wheel'] * 0.03)
    contact_norm = contact / 0.10  # 正規化

    ms_20 = compute_max_slope(
        spec['mass'], spec['n_wheels'], spec['r_wheel'], spec['b_wheel'],
        g_mars, c_soil, phi_soil, K_shear, kc, kphi, n_bekker, slip=0.2
    )
    slope_norm = ms_20 / 30.0

    values = [d_norm, mass_eff_norm, contact_norm, slope_norm]
    values += values[:1]

    axes[1].set_frame_on(False)

# 棒グラフで代替(レーダーはmatplotlibで煩雑なため)
axes[1].clear()
rover_names = list(rovers.keys())
metrics = {
    'Wheel diam [cm]': [rovers[n]['r_wheel'] * 200 for n in rover_names],
    'Max slope [deg]\n(slip=20%)': [],
}
for name in rover_names:
    spec = rovers[name]
    ms = compute_max_slope(
        spec['mass'], spec['n_wheels'], spec['r_wheel'], spec['b_wheel'],
        g_mars, c_soil, phi_soil, K_shear, kc, kphi, n_bekker, slip=0.2
    )
    metrics['Max slope [deg]\n(slip=20%)'].append(ms)

x = np.arange(len(rover_names))
width = 0.35

bars1 = axes[1].bar(x - width/2, metrics['Wheel diam [cm]'], width,
                     label='Wheel diameter [cm]', color='#42A5F5', alpha=0.8)
axes[1].set_ylabel('Wheel diameter [cm]', fontsize=11, color='#42A5F5')
axes[1].set_ylim(0, 60)

ax2 = axes[1].twinx()
bars2 = ax2.bar(x + width/2, metrics['Max slope [deg]\n(slip=20%)'], width,
                label='Max slope [deg]', color='#EF5350', alpha=0.8)
ax2.set_ylabel('Max climbable slope [deg]', fontsize=11, color='#EF5350')
ax2.set_ylim(0, 40)

axes[1].set_xticks(x)
axes[1].set_xticklabels(rover_names, rotation=25, ha='right', fontsize=9)
axes[1].set_title('Rover Comparison: Wheel Size vs Max Slope', fontsize=13)
axes[1].grid(True, alpha=0.2, axis='y')

fig.legend(loc='upper right', bbox_to_anchor=(0.98, 0.95), fontsize=10)
plt.tight_layout()
plt.savefig('rover_comparison.png', dpi=150, bbox_inches='tight')
plt.show()

左パネルのグラフから、スリップ率が増加するにつれて最大登坂角度が増大し、やがて飽和する傾向が全ローバーに共通して見られます。これは、ある程度のスリップがないと牽引力が発生しないというJanosi-Hanamoroモデルの特性を反映しています。ただし、スリップ率が大きすぎると(50%以上)、実際にはグラウザー(車輪の突起)が砂を掘り込んで沈下が進行するため、このモデルの予測は楽観的になります。

各ローバーを比較すると、Curiosity/Perseveranceは大きな車輪のおかげで最大登坂角度が高い値を示しています。一方、Sojournerは車輪が小さいにもかかわらず、車体が非常に軽い(10.6kg)ため接地圧が低く、意外にも健闘しています。

右パネルの棒グラフでは、車輪直径と最大登坂角度の関係がローバーごとに並べて示されています。車輪直径が大きいほど最大登坂角度が高い傾向がありますが、車体質量の影響も無視できません。Yutu-2は車輪がSpirit/Opportunity並みですが、質量が軽いため走行性能は比較的良好です。

ここまでの解析で、ローバーの移動機構と走行力学の全体像が見えてきました。最後に、砂地での走行ダイナミクスをリアルタイムでシミュレーションする統合モデルを構築しましょう。

統合走行シミュレーション — 砂地での加速・定常走行・スタック

ここでは、ローバーが砂地を走行する際の時間発展をシミュレーションします。モーターが一定トルクを出力した場合に、スリップ率と前進速度がどのように変化するかを追跡します。

import numpy as np
import matplotlib.pyplot as plt

# --- ローバーパラメータ(Curiosity相当) ---
mass = 899          # [kg]
n_wheels = 6
r_wheel = 0.25      # [m]
b_wheel = 0.40      # [m]
g_mars = 3.72       # [m/s^2]
I_wheel = 0.5       # [kg m^2] 車輪の慣性モーメント

# --- 土壌パラメータ ---
c_soil = 0.5e3      # [Pa]
phi_soil = np.radians(35)
K_shear = 0.02      # [m]
kc = 1.4e3
kphi = 820e3
n_bekker = 1.0

# --- 沈下深さ計算 ---
def compute_sinkage(W, b, r, kc, kphi, n):
    z0 = 0.01
    for _ in range(100):
        l = np.sqrt(2 * r * z0) if z0 > 0 else 0.01
        p_mean = W / (b * l)
        z0_new = (p_mean / (kc / b + kphi))**(1.0 / n)
        if abs(z0_new - z0) < 1e-6:
            break
        z0 = 0.5 * z0 + 0.5 * z0_new
    return z0

# --- 牽引力 ---
def traction(slip, W, b, r, z0, c, phi, K):
    l = np.sqrt(2 * r * z0)
    sigma = W / (b * l)
    tau_max = c + sigma * np.tan(phi)
    s_safe = max(abs(slip), 1e-10)
    F_D = b * l * tau_max * (1 - (K / (s_safe * l)) * (1 - np.exp(-s_safe * l / K)))
    return F_D * np.sign(slip) if slip != 0 else 0

# --- 圧密抵抗 ---
def compaction_resistance(z0, b, kc, kphi, n):
    return b * (kc / b + kphi) * z0**(n + 1) / (n + 1)

# --- シミュレーション ---
dt = 0.01           # 時間刻み [s]
t_max = 120         # シミュレーション時間 [s]
n_steps = int(t_max / dt)

# 3つの土壌条件でシミュレーション
soil_conditions = {
    'Hard soil (K=0.01)': {'K': 0.01, 'kphi_factor': 1.5},
    'Medium soil (K=0.02)': {'K': 0.02, 'kphi_factor': 1.0},
    'Soft soil (K=0.04)': {'K': 0.04, 'kphi_factor': 0.5},
}

fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)

for soil_name, soil_params in soil_conditions.items():
    K = soil_params['K']
    kphi_local = kphi * soil_params['kphi_factor']

    # モーターが出力する一定トルク [Nm](1輪あたり)
    tau_motor = 8.0

    # 状態変数
    v = 0.0         # 車体前進速度 [m/s]
    omega = 0.0     # 車輪角速度 [rad/s]
    x_pos = 0.0     # 走行距離 [m]

    # 記録配列
    t_arr = np.zeros(n_steps)
    v_arr = np.zeros(n_steps)
    slip_arr = np.zeros(n_steps)
    x_arr = np.zeros(n_steps)

    W_per_wheel = mass * g_mars / n_wheels

    for i in range(n_steps):
        t = i * dt
        t_arr[i] = t

        # 沈下深さ(速度依存: 高スリップほど掘り込む)
        z0_base = compute_sinkage(W_per_wheel, b_wheel, r_wheel, kc, kphi_local, n_bekker)
        slip = (r_wheel * omega - v) / max(r_wheel * omega, 0.001)
        slip = np.clip(slip, 0, 0.99)
        # 高スリップで沈下が増大する効果を追加
        z0 = z0_base * (1 + 2.0 * slip**2)

        # 牽引力(1輪あたり)
        F_D_wheel = traction(slip, W_per_wheel, b_wheel, r_wheel, z0,
                             c_soil, phi_soil, K)

        # 圧密抵抗(1輪あたり)
        F_R_wheel = compaction_resistance(z0, b_wheel, kc, kphi_local, n_bekker)

        # 車体の運動方程式
        F_net_total = n_wheels * (F_D_wheel - F_R_wheel)
        a_body = F_net_total / mass
        v += a_body * dt
        v = max(v, 0)  # 後退しない
        x_pos += v * dt

        # 車輪の回転運動方程式
        # tau_motor = I_wheel * d(omega)/dt + r_wheel * F_D_wheel
        alpha_wheel = (tau_motor - r_wheel * F_D_wheel) / I_wheel
        omega += alpha_wheel * dt
        omega = max(omega, 0)

        v_arr[i] = v
        slip_arr[i] = slip
        x_arr[i] = x_pos

    axes[0].plot(t_arr, v_arr * 100, linewidth=2, label=soil_name)
    axes[1].plot(t_arr, slip_arr * 100, linewidth=2, label=soil_name)
    axes[2].plot(t_arr, x_arr, linewidth=2, label=soil_name)

axes[0].set_ylabel('Velocity [cm/s]', fontsize=12)
axes[0].set_title('Rover Driving Dynamics on Different Soils (Mars)', fontsize=13)
axes[0].legend(fontsize=10)
axes[0].grid(True, alpha=0.3)

axes[1].set_ylabel('Slip ratio [%]', fontsize=12)
axes[1].legend(fontsize=10)
axes[1].grid(True, alpha=0.3)

axes[2].set_xlabel('Time [s]', fontsize=12)
axes[2].set_ylabel('Distance [m]', fontsize=12)
axes[2].legend(fontsize=10)
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('driving_dynamics.png', dpi=150, bbox_inches='tight')
plt.show()

この走行ダイナミクスシミュレーションから、3つの土壌条件での振る舞いの違いが鮮明に浮かび上がります。

硬い土壌(K=0.01) では、ローバーは速やかに加速し、比較的短い時間で定常速度に達します。スリップ率は低い値(20%以下)で安定し、走行距離も順調に伸びています。これは理想的な走行条件です。

中程度の土壌(K=0.02) では、加速がやや遅くなり、定常速度も硬い土壌に比べて低下します。スリップ率は中程度の値で安定します。Curiosityが通常走行する火星の地表はこの程度の条件に相当します。

柔らかい土壌(K=0.04) では、状況が劇的に変化します。スリップ率が高い値まで上昇し、車輪が砂を掘り込むことで沈下が増大し、さらにスリップが増大するという正のフィードバックループに陥ります。前進速度はほぼゼロに近づき、ローバーは事実上スタック状態になります。走行距離のグラフを見ても、柔らかい土壌ではほとんど距離を稼げていないことがわかります。

このシミュレーションは、Spiritが経験したスタックのメカニズムを定性的に再現しています。実際の運用では、車輪の回転速度を下げる、前進と後退を繰り返す、ステアリングを切るなどの脱出戦略が試みられますが、沈下が深くなりすぎると回復は困難です。

スリップ率モニタリングとスタック予防

ビジュアルオドメトリによるスリップ検出

先ほどのシミュレーションで見たように、柔らかい砂地でのスタックを防ぐには、スリップ率をリアルタイムに監視し、危険な領域に入る前に対処することが不可欠です。

実際のローバーでは、車輪のエンコーダ(回転数センサ)から求めた「移動したはずの距離」と、カメラによるビジュアルオドメトリ(VO)から求めた「実際に移動した距離」を比較してスリップ率を推定しています。

$$ \begin{equation} s_{\text{estimated}} = \frac{d_{\text{wheel}} – d_{\text{VO}}}{d_{\text{wheel}}} \end{equation} $$

ここで $d_{\text{wheel}} = r \cdot \Delta\theta$(エンコーダから求めた移動距離)、$d_{\text{VO}}$ はビジュアルオドメトリから求めた実移動距離です。

Curiosity と Perseverance では、スリップ率が閾値(通常40〜50%)を超えると自動的に停止する安全機構が実装されています。これにより、Spiritのようなスタックのリスクを大幅に低減しています。

スリップ率に基づく走行制御

スリップ率を監視するだけでなく、能動的にスリップを制御する手法も研究されています。基本的なアプローチは以下の通りです。

  1. スリップ制限制御: 各車輪のスリップ率が目標値(通常10〜20%)を超えないように駆動トルクを調整する
  2. 牽引力最適化: 牽引力が最大になるスリップ率(通常15〜25%程度)を維持するように制御する
  3. 経路変更: スリップが大きい領域を検知したら、より硬い地盤の経路に迂回する

スリップ制限制御の基本的なフィードバック制御則は以下のように表されます。

$$ \begin{equation} \tau_{\text{cmd}} = \tau_{\text{nom}} – K_p (s – s_{\text{target}}) – K_d \dot{s} \end{equation} $$

ここで $\tau_{\text{cmd}}$ は指令トルク、$\tau_{\text{nom}}$ は公称トルク、$s_{\text{target}}$ は目標スリップ率、$K_p$ と $K_d$ はPD制御ゲインです。スリップ率が目標を超えるとトルクを下げてスリップを抑制します。

このような制御手法を導入することで、Spiritが経験したようなスタックを未然に防げる可能性が高まります。しかし、すでに深く沈下してしまった後では、いかなる制御を施しても脱出は困難です。「予防」が何より重要であるという教訓が、Spirit/Opportunityの運用から得られた最大の知見の一つです。

ここまでの解析を、スリップ制御シミュレーションで確認してみましょう。

import numpy as np
import matplotlib.pyplot as plt

# --- ローバーパラメータ(Curiosity相当) ---
mass = 899
n_wheels = 6
r_wheel = 0.25
b_wheel = 0.40
g_mars = 3.72
I_wheel = 0.5

# --- 土壌パラメータ(柔らかい砂地) ---
c_soil = 0.5e3
phi_soil = np.radians(35)
K_shear = 0.04       # 柔らかい砂地
kc = 1.4e3
kphi = 820e3 * 0.5   # 柔らかい砂地
n_bekker = 1.0

# --- シミュレーション ---
dt = 0.01
t_max = 120
n_steps = int(t_max / dt)
W_per_wheel = mass * g_mars / n_wheels

def compute_sinkage(W, b, r, kc, kphi, n):
    z0 = 0.01
    for _ in range(100):
        l = np.sqrt(2 * r * z0) if z0 > 0 else 0.01
        p_mean = W / (b * l)
        z0_new = (p_mean / (kc / b + kphi))**(1.0 / n)
        if abs(z0_new - z0) < 1e-6:
            break
        z0 = 0.5 * z0 + 0.5 * z0_new
    return z0

def traction(slip, W, b, r, z0, c, phi, K):
    l = np.sqrt(2 * r * z0)
    sigma = W / (b * l)
    tau_max = c + sigma * np.tan(phi)
    s_safe = max(abs(slip), 1e-10)
    F_D = b * l * tau_max * (1 - (K / (s_safe * l)) * (1 - np.exp(-s_safe * l / K)))
    return F_D

def compaction_resistance(z0, b, kc, kphi, n):
    return b * (kc / b + kphi) * z0**(n + 1) / (n + 1)

def simulate_drive(use_slip_control=False, s_target=0.20, Kp=20.0, Kd=5.0):
    """走行シミュレーション(スリップ制御あり/なし)"""
    v = 0.0
    omega = 0.0
    x_pos = 0.0
    tau_motor_base = 8.0
    slip_prev = 0.0

    t_arr = np.zeros(n_steps)
    v_arr = np.zeros(n_steps)
    slip_arr = np.zeros(n_steps)
    x_arr = np.zeros(n_steps)
    z0_arr = np.zeros(n_steps)

    for i in range(n_steps):
        t = i * dt
        t_arr[i] = t

        z0_base = compute_sinkage(W_per_wheel, b_wheel, r_wheel, kc, kphi, n_bekker)
        slip = (r_wheel * omega - v) / max(r_wheel * omega, 0.001)
        slip = np.clip(slip, 0, 0.99)
        z0 = z0_base * (1 + 2.0 * slip**2)

        # スリップ制御
        if use_slip_control:
            slip_error = slip - s_target
            slip_rate = (slip - slip_prev) / dt
            tau_motor = tau_motor_base - Kp * slip_error - Kd * slip_rate
            tau_motor = np.clip(tau_motor, 0, tau_motor_base * 1.5)
        else:
            tau_motor = tau_motor_base

        F_D_wheel = traction(slip, W_per_wheel, b_wheel, r_wheel, z0,
                             c_soil, phi_soil, K_shear)
        F_R_wheel = compaction_resistance(z0, b_wheel, kc, kphi, n_bekker)

        F_net_total = n_wheels * (F_D_wheel - F_R_wheel)
        a_body = F_net_total / mass
        v += a_body * dt
        v = max(v, 0)
        x_pos += v * dt

        alpha_wheel = (tau_motor - r_wheel * F_D_wheel) / I_wheel
        omega += alpha_wheel * dt
        omega = max(omega, 0)

        v_arr[i] = v
        slip_arr[i] = slip
        x_arr[i] = x_pos
        z0_arr[i] = z0 * 100  # cm

        slip_prev = slip

    return t_arr, v_arr, slip_arr, x_arr, z0_arr

# 2つのケースをシミュレーション
t1, v1, s1, x1, z1 = simulate_drive(use_slip_control=False)
t2, v2, s2, x2, z2 = simulate_drive(use_slip_control=True)

fig, axes = plt.subplots(4, 1, figsize=(12, 12), sharex=True)

# 速度
axes[0].plot(t1, v1 * 100, 'r-', linewidth=2, label='No slip control')
axes[0].plot(t2, v2 * 100, 'b-', linewidth=2, label='With slip control (target=20%)')
axes[0].set_ylabel('Velocity [cm/s]', fontsize=12)
axes[0].set_title('Effect of Slip Control on Soft Sand', fontsize=13)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)

# スリップ率
axes[1].plot(t1, s1 * 100, 'r-', linewidth=2, label='No slip control')
axes[1].plot(t2, s2 * 100, 'b-', linewidth=2, label='With slip control')
axes[1].axhline(y=20, color='green', linestyle='--', alpha=0.5, label='Target slip (20%)')
axes[1].axhline(y=50, color='orange', linestyle='--', alpha=0.5, label='Danger zone (50%)')
axes[1].set_ylabel('Slip ratio [%]', fontsize=12)
axes[1].legend(fontsize=10)
axes[1].grid(True, alpha=0.3)

# 走行距離
axes[2].plot(t1, x1, 'r-', linewidth=2, label='No slip control')
axes[2].plot(t2, x2, 'b-', linewidth=2, label='With slip control')
axes[2].set_ylabel('Distance [m]', fontsize=12)
axes[2].legend(fontsize=11)
axes[2].grid(True, alpha=0.3)

# 沈下深さ
axes[3].plot(t1, z1, 'r-', linewidth=2, label='No slip control')
axes[3].plot(t2, z2, 'b-', linewidth=2, label='With slip control')
axes[3].set_xlabel('Time [s]', fontsize=12)
axes[3].set_ylabel('Sinkage [cm]', fontsize=12)
axes[3].legend(fontsize=11)
axes[3].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('slip_control.png', dpi=150, bbox_inches='tight')
plt.show()

このシミュレーション結果は、スリップ制御の有無による走行性能の劇的な違いを示しています。

スリップ制御なし(赤線)の場合、柔らかい砂地でモーターを一定トルクで回すと、車輪が空転してスリップ率が急上昇します。スリップが増大すると沈下が深くなり、沈下が深くなるとさらにスリップが増大するという悪循環に陥り、最終的にスタックします。走行距離はほとんど伸びず、沈下深さは時間とともに増大し続けます。

スリップ制御あり(青線)の場合、PD制御によってスリップ率が目標の20%付近に維持されています。スリップが目標を超えると駆動トルクが自動的に低減され、車輪の空転を防止します。その結果、沈下は制御なしの場合に比べて大幅に抑えられ、ローバーは低速ながらも着実に前進を続けています。同じ120秒間での走行距離は、スリップ制御ありの方が圧倒的に長くなっています。

この結果は、「速く走ろうとするほど遅くなる」という砂地走行の逆説を示しています。トルクを抑えてスリップを制御した方が、結果として長い距離を走れるのです。

まとめ

本記事では、惑星探査ローバーの移動機構と走行力学について、基礎理論からシミュレーションまでを解説しました。

  • 惑星表面環境の特殊性: 低重力、砂地、岩場、極端な温度変化がローバーの移動機構に独自の制約を課します。特に低重力によるトラクション低下は、地球の車両設計の常識が通用しないことを意味します

  • 車輪の力学モデル: Janosi-Hanamoroモデルにより、スリップ率と牽引力の関係を定量的に記述できます。ベッカーモデルによる圧密抵抗と組み合わせることで、ローバーが走行可能な条件を予測できます

  • ロッカーボギー機構: バネもダンパーも使わない純粋なリンク機構でありながら、車輪直径以上の障害物乗り越え能力を持つ優れたサスペンション設計です。1997年のSojourner以来、全ての火星ローバーで採用されています

  • 各種移動方式の比較: 車輪、クローラ、脚、ハイブリッドの各方式にはそれぞれ利点と欠点があり、惑星探査ではエネルギー効率と信頼性に優れる車輪方式が主流です

  • スリップ制御の重要性: 柔らかい砂地ではスリップと沈下の正のフィードバックによりスタックのリスクがあります。スリップ率を監視し制御することで、走行性能を大幅に改善できます

本記事で扱った走行力学の知識は、ローバーの自律ナビゲーションを理解する上で不可欠な基盤です。ローバーが「どのくらい安全に走れるか」を判断するには、地形の認識だけでなく、土壌とのインタラクション(走行力学)を考慮する必要があるからです。

次のステップとして、以下の記事も参考にしてください。