多脚ロボットの歩行制御と宇宙応用 — 足で歩く惑星探査ロボット

月面の細かいレゴリス(砂)に覆われた斜面を想像してみてください。車輪型のローバーがスリップして登れない急勾配でも、6本の脚を持つロボットなら、一歩ずつ確実に足場を確保しながら登っていけます。あるいは、月の溶岩洞(溶岩チューブ)の入口に散乱する岩塊の上を、まるでヤギが山を登るように足を選んで進むことも可能です。車輪やクローラでは到達できない地形へのアクセス — これが脚型ロボットに宇宙探査で注目が集まる最大の理由です。

多脚ロボットの歩行制御は、古典力学・制御工学・生物学が交差する魅力的な領域です。この技術を学ぶと、以下のような応用と理解が広がります。

  • 惑星探査: 月面や火星の不整地・溶岩洞・崖面など、車輪型では到達不能な地形での探査が可能になります
  • 災害対応ロボット: 瓦礫の上を歩く災害救助ロボット(Boston Dynamics の Spot 等)の歩行制御は、宇宙応用と同じ理論基盤の上に成り立っています
  • バイオメカニクス: 昆虫や四足動物の歩行パターンを数理モデル化する手法は、生物学と工学の橋渡しとなります
  • ロボット工学全般: ZMP(ゼロモーメントポイント)や CPG(中枢パターン生成器)は、ヒューマノイドから産業用歩行ロボットまで広く使われる汎用的な概念です

本記事の内容

  • 車輪型と脚型の比較 — なぜ脚が必要か
  • 多脚ロボットの運動学と力学モデル
  • ZMP(ゼロモーメントポイント)による安定性評価
  • CPG(中枢パターン生成器)による歩容パターン生成
  • 代表的な歩容パターン(ウォーク・トロット・ギャロップ)
  • 低重力環境での歩行特性の変化
  • 宇宙応用の実例(SpaceBok・ANYmal・ATHLETE・RoboSimian)
  • 月面溶岩洞探査への展望
  • Pythonによるシミュレーション実装

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

車輪型と脚型の比較 — なぜ「脚」が必要なのか

車輪型ローバーの限界

惑星探査における車輪型ローバーは目覚ましい実績を残してきました。NASAの Curiosity は火星のゲールクレーター内を 30 km 以上走破し、Perseverance は古代の河川デルタを調査しています。しかし、車輪型には本質的な制約があります。

第一の問題は不整地走破性です。車輪は平坦な地面では効率的ですが、岩の高さが車輪の直径を超えると乗り越えることができません。Curiosity の車輪直径は約 50 cm ですが、火星の地表には 1 m を超える岩塊が散在しています。実際、Curiosity の車輪は火星の鋭い岩で損傷を受けており、車輪の脆弱性は深刻な運用上の課題となっています。

第二の問題は軟弱地盤でのスリップです。2009年、NASAの Spirit ローバーは軟弱な砂地にはまり込み、脱出不能となって事実上のミッション終了を迎えました。車輪が砂に沈み込むと、接地面積が増えても剪断力が不足し、推進力を得られなくなります。

第三の問題は急傾斜地での走行です。車輪型ローバーの限界傾斜角は一般に 20〜30 度程度です。月面や火星の溶岩洞入口、クレーター壁、崖面など、科学的に興味深い地形の多くがこの限界を超えています。

脚型ロボットの優位性

脚型ロボットは、これらの制約を根本的に異なるメカニズムで解決します。

離散的な接地点の選択が最大の優位性です。脚型ロボットは、足を置く場所を一つひとつ選択できます。岩塊の間に足を差し込んだり、安定した岩の上にだけ足を置いたりすることが可能です。これは、車輪型が連続的な地面との接触を必要とするのと根本的に異なります。

姿勢の独立制御も重要です。脚型ロボットは、地形に合わせて各脚の長さを独立に変えることで、胴体を水平に保ちながら傾斜地を歩くことができます。車輪型では地面の傾斜がそのまま車体の傾きになりますが、脚型では脚の伸縮で地形の凹凸を吸収できます。

高い障害物の乗り越えも可能です。脚型ロボットは、胴体の高さまでの障害物を脚を持ち上げることで乗り越えられます。車輪型の限界(車輪直径程度)と比較すると、はるかに大きな障害物に対応できます。

もちろん脚型にも欠点があります。機構の複雑さ、制御の難しさ、エネルギー効率の悪さなどです。車輪型はエネルギー効率の点で脚型を大きく上回ります。平坦な地面では車輪が最適解であり、脚はあくまで「車輪では行けない場所」のためのソリューションです。

以下の表に両者の特性を整理します。

特性 車輪型 脚型
平地の移動効率 非常に高い 低い
不整地走破性 低い 非常に高い
急傾斜走行 限界20〜30° 45°以上可能
障害物乗り越え 車輪径まで 脚長まで
機構の複雑さ 低い 高い
制御の難しさ 容易 難しい
耐スリップ性 低い 高い
ペイロード比 高い 低い

ここまでで、脚型ロボットがなぜ必要かがわかりました。では、脚型ロボットが歩くとはどういうことか — その力学の基礎をモデル化していきましょう。

多脚ロボットの力学モデル — 歩行の物理を理解する

脚の運動学

脚型ロボットの「脚」は、複数の関節(ジョイント)で構成されるシリアルリンク機構です。最もシンプルなモデルは、2つの回転関節を持つ平面2リンク脚です。

2リンク脚を例に考えましょう。肩関節(股関節)の角度を $\theta_1$、膝関節の角度を $\theta_2$、上腿の長さを $l_1$、下腿の長さを $l_2$ とします。足先の位置 $(x, y)$ は順運動学で次のように求まります。

$$ \begin{equation} x = l_1 \cos\theta_1 + l_2 \cos(\theta_1 + \theta_2) \end{equation} $$

$$ \begin{equation} y = l_1 \sin\theta_1 + l_2 \sin(\theta_1 + \theta_2) \end{equation} $$

この式は、肩から膝までのベクトルと、膝から足先までのベクトルを加えたものです。幾何学的に言えば、2本のリンクを繋いだ先端の位置を三角関数で表しているだけです。

逆に、足先の目標位置 $(x_d, y_d)$ から関節角度を求める逆運動学も必要です。幾何学的に解くと、余弦定理から $\theta_2$ が求まります。

まず足先までの距離 $r$ を計算します。

$$ r^2 = x_d^2 + y_d^2 $$

余弦定理を適用すると、膝関節角度 $\theta_2$ は次のようになります。

$$ \begin{equation} \cos\theta_2 = \frac{r^2 – l_1^2 – l_2^2}{2 l_1 l_2} \end{equation} $$

$\theta_2$ が求まったら、$\theta_1$ は逆正接を使って次のように得られます。

$$ \begin{equation} \theta_1 = \text{atan2}(y_d,\, x_d) – \text{atan2}(l_2 \sin\theta_2,\, l_1 + l_2 \cos\theta_2) \end{equation} $$

ここで atan2 は4象限の逆正接関数です。この逆運動学により、「足先をこの位置に置きたい」という指令を関節角度の指令に変換できます。

ヤコビアンと足先の速度

脚の運動学を微分すると、関節角速度と足先速度の関係が得られます。この関係を記述するのがヤコビアン $\bm{J}$ です。

$$ \begin{equation} \dot{\bm{x}} = \bm{J}(\bm{\theta}) \, \dot{\bm{\theta}} \end{equation} $$

2リンク脚の場合、ヤコビアンは $2 \times 2$ の行列です。

$$ \bm{J} = \begin{pmatrix} -l_1 \sin\theta_1 – l_2 \sin(\theta_1+\theta_2) & -l_2 \sin(\theta_1+\theta_2) \\ l_1 \cos\theta_1 + l_2 \cos(\theta_1+\theta_2) & l_2 \cos(\theta_1+\theta_2) \end{pmatrix} $$

この行列の各要素を読み解きましょう。$(1,1)$ 成分は「肩関節を回すと足先が $x$ 方向にどれだけ動くか」、$(1,2)$ 成分は「膝関節を回すと足先が $x$ 方向にどれだけ動くか」を表しています。

ヤコビアンが重要なのは、足先に加えたい力を関節トルクに変換するためでもあります。仮想仕事の原理から、足先に力 $\bm{f}$ を発生させるために必要な関節トルク $\bm{\tau}$ は次のようになります。

$$ \begin{equation} \bm{\tau} = \bm{J}^T \bm{f} \end{equation} $$

この関係は、脚の力制御において中心的な役割を果たします。歩行中にどれだけの関節トルクが必要かを見積もるには、この式が不可欠です。

多脚ロボットの全身力学

多脚ロボット全体の動力学を考えましょう。胴体(ボディ)の質量を $M$、重心の位置を $\bm{r}_G$、脚 $i$ の足先が地面に加える接地力を $\bm{f}_i$ とします。$n$ 本の脚が接地しているとき、ニュートンの運動方程式は次のようになります。

$$ \begin{equation} M \ddot{\bm{r}}_G = \sum_{i=1}^{n} \bm{f}_i + M \bm{g} \end{equation} $$

ここで $\bm{g}$ は重力加速度ベクトルです。静的な歩行(加速度がほぼゼロの低速歩行)では、左辺がゼロとなるため、接地力の合計が重力と釣り合うという単純な関係になります。

$$ \sum_{i=1}^{n} \bm{f}_i = -M \bm{g} $$

角運動量に関しても同様に、重心まわりのモーメントの釣り合いが成り立ちます。

$$ \begin{equation} \bm{I} \dot{\bm{\omega}} + \bm{\omega} \times (\bm{I} \bm{\omega}) = \sum_{i=1}^{n} (\bm{p}_i – \bm{r}_G) \times \bm{f}_i \end{equation} $$

ここで $\bm{I}$ は慣性テンソル、$\bm{\omega}$ は角速度、$\bm{p}_i$ は $i$ 番目の足先の位置です。

こうした力学の基本方程式が揃ったところで、次に考えるべきは「ロボットが転ばずに歩けるかどうか」の判定基準です。これを定量的に扱うのが、ZMP(ゼロモーメントポイント)の概念です。

ZMP — 転倒しないための安定性指標

ZMPの直感的理解

人間が片足で立っているとき、足の裏のどこかに「体重が集中する点」があります。この点が足の裏の範囲内にある限り、バランスを保てます。もしこの点が足の裏の端に達すると、つま先立ちになったり、かかとが浮いたりして、やがて転倒します。

ZMP(Zero Moment Point)は、この「体重が集中する点」を数学的に一般化したものです。正確に言えば、地面に作用するモーメントの水平成分がゼロになる点です。1968年にユーゴスラビア(現セルビア)の研究者ブコブラトヴィッチ(Vukobratović)が提唱したこの概念は、以来50年以上にわたって二足歩行・多脚歩行ロボットの安定性判定の標準的な指標として使われ続けています。

ZMPの数学的定義

ZMPを厳密に定義しましょう。ロボットの総質量を $M$、重心の位置を $\bm{r}_G = (x_G, y_G, z_G)$、重心の加速度を $\ddot{\bm{r}}_G$、重力加速度を $g$ とします。ZMPの位置 $(x_{ZMP}, y_{ZMP})$ は、地面と接触する平面($z = 0$ とします)上の点として次のように定義されます。

まず、地面上の任意の点 $P$ まわりのモーメントを考えます。ZMPとは、この点 $P$ まわりのモーメントの水平成分($x$ 成分と $y$ 成分)がゼロになるような点のことです。

この条件を展開すると、$x$ 方向については次のように求まります。

全モーメントの $y$ 軸まわり成分をゼロとおきます。

$$ M(\ddot{z}_G + g) \cdot x_{ZMP} = M(g + \ddot{z}_G) \cdot x_G – M \ddot{x}_G \cdot z_G – \dot{L}_y $$

ここで $\dot{L}_y$ は角運動量の $y$ 成分の時間変化率です。両辺を $M(g + \ddot{z}_G)$ で割ると次のようになります。

$$ \begin{equation} x_{ZMP} = x_G – \frac{M \ddot{x}_G \cdot z_G + \dot{L}_y}{M(\ddot{z}_G + g)} \end{equation} $$

同様に $y$ 方向については次のようになります。

$$ \begin{equation} y_{ZMP} = y_G – \frac{M \ddot{y}_G \cdot z_G – \dot{L}_x}{M(\ddot{z}_G + g)} \end{equation} $$

多くの実用的な場面では、角運動量の変化 $\dot{L}_x, \dot{L}_y$ は小さく、さらに重心の上下加速度 $\ddot{z}_G$ も $g$ に比べて小さいと近似できます。この場合、ZMPの式はより簡潔になります。

$$ \begin{equation} x_{ZMP} \approx x_G – \frac{\ddot{x}_G}{g} z_G \end{equation} $$

$$ \begin{equation} y_{ZMP} \approx y_G – \frac{\ddot{y}_G}{g} z_G \end{equation} $$

この簡略化された式は直感的にも解釈しやすいです。ZMPの位置は、重心の真下($x_G, y_G$)から、水平加速度に応じてずれます。ロボットが右方向に加速していれば、ZMPは左にずれる — これは、車の中でブレーキをかけると体が前に傾くのと同じ原理です。

安定性判定と支持多角形

ZMPの威力は、安定性判定において発揮されます。多脚ロボットが接地している全ての足先の位置を頂点として構成される凸多角形を支持多角形(Support Polygon)と呼びます。

安定性の判定基準は明確です。

  • ZMP が支持多角形の内部にある → ロボットは安定(転倒しない)
  • ZMP が支持多角形の辺上にある → 限界安定(その辺を軸に回転し始める寸前)
  • ZMP が支持多角形の外部に出る → 不安定(転倒する)

4脚ロボットが全脚接地している場合、支持多角形は4本の足先を頂点とする四角形です。1本の脚を持ち上げて遊脚にすると、支持多角形は残り3本の足先で構成される三角形に縮小します。支持多角形が小さくなるほど安定余裕が減り、歩行中の姿勢制御がシビアになります。

安定余裕(Stability Margin)は、ZMPから支持多角形の最も近い辺までの距離として定義されます。

$$ \begin{equation} d_{\text{margin}} = \min_i \, d(ZMP, \, e_i) \end{equation} $$

ここで $e_i$ は支持多角形の第 $i$ 辺です。$d_{\text{margin}} > 0$ であれば安定、大きいほど安定余裕が大きいことを意味します。

ZMPに基づく安定歩行の基本戦略は、「歩行中のあらゆる瞬間で ZMP が支持多角形の内部にあるよう、脚の動きと胴体の運動を計画する」ということに集約されます。

では、Pythonで ZMP の計算と支持多角形の可視化を実装してみましょう。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection

# 4脚ロボットの足先位置(上から見た2D座標)
feet_positions = np.array([
    [0.3, 0.2],    # 左前脚
    [0.3, -0.2],   # 右前脚
    [-0.3, 0.2],   # 左後脚
    [-0.3, -0.2],  # 右後脚
])

# 支持多角形を計算(凸包)
from scipy.spatial import ConvexHull

def compute_support_polygon(feet):
    """接地している足先位置から支持多角形を計算"""
    if len(feet) < 3:
        return None
    hull = ConvexHull(feet)
    return feet[hull.vertices]

def compute_zmp(x_G, y_G, z_G, ddx_G, ddy_G, g=9.81):
    """簡略化ZMPの計算"""
    x_zmp = x_G - (ddx_G / g) * z_G
    y_zmp = y_G - (ddy_G / g) * z_G
    return x_zmp, y_zmp

def point_in_polygon(point, polygon):
    """点が凸多角形の内部にあるかを判定"""
    n = len(polygon)
    for i in range(n):
        edge = polygon[(i + 1) % n] - polygon[i]
        to_point = point - polygon[i]
        cross = edge[0] * to_point[1] - edge[1] * to_point[0]
        if cross < 0:
            return False
    return True

def stability_margin(zmp, polygon):
    """ZMPから支持多角形の最近辺までの距離"""
    n = len(polygon)
    min_dist = float('inf')
    for i in range(n):
        p1 = polygon[i]
        p2 = polygon[(i + 1) % n]
        edge = p2 - p1
        edge_len = np.linalg.norm(edge)
        edge_unit = edge / edge_len
        to_zmp = zmp - p1
        proj = np.dot(to_zmp, edge_unit)
        proj = np.clip(proj, 0, edge_len)
        closest = p1 + proj * edge_unit
        dist = np.linalg.norm(zmp - closest)
        min_dist = min(min_dist, dist)
    return min_dist

# --- シナリオ1: 全脚接地(静止) ---
z_G = 0.4  # 重心高さ
sp_full = compute_support_polygon(feet_positions)
zmp_static = compute_zmp(0.0, 0.0, z_G, 0.0, 0.0)

# --- シナリオ2: 全脚接地(加速中) ---
zmp_accel = compute_zmp(0.0, 0.0, z_G, 2.0, 1.0)

# --- シナリオ3: 右前脚を持ち上げ(遊脚) ---
feet_3leg = feet_positions[[0, 2, 3]]  # 左前・左後・右後
sp_3leg = compute_support_polygon(feet_3leg)
zmp_3leg = compute_zmp(-0.05, 0.02, z_G, 0.0, 0.0)

fig, axes = plt.subplots(1, 3, figsize=(15, 5))

for ax, title, sp, zmp, feet in [
    (axes[0], "4-leg support (static)", sp_full, zmp_static, feet_positions),
    (axes[1], "4-leg support (accelerating)", sp_full, zmp_accel, feet_positions),
    (axes[2], "3-leg support (RF leg swing)", sp_3leg, zmp_3leg, feet_3leg),
]:
    poly = Polygon(sp, closed=True, alpha=0.3, color='cyan', edgecolor='cyan', linewidth=2)
    ax.add_patch(poly)
    ax.scatter(feet[:, 0], feet[:, 1], s=100, c='white', edgecolors='cyan', zorder=5, label='Foot')
    ax.scatter(*zmp, s=150, c='red', marker='x', linewidths=3, zorder=6, label='ZMP')

    margin = stability_margin(np.array(zmp), sp)
    is_stable = point_in_polygon(np.array(zmp), sp)

    ax.set_title(f"{title}\nMargin={margin:.3f}m {'(Stable)' if is_stable else '(UNSTABLE)'}", fontsize=10)
    ax.set_xlim(-0.5, 0.5)
    ax.set_ylim(-0.4, 0.4)
    ax.set_aspect('equal')
    ax.grid(True, alpha=0.3)
    ax.set_xlabel('x [m]')
    ax.set_ylabel('y [m]')
    ax.legend(fontsize=8)

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

print(f"シナリオ1 (静止): ZMP=({zmp_static[0]:.3f}, {zmp_static[1]:.3f}), margin={stability_margin(np.array(zmp_static), sp_full):.3f} m")
print(f"シナリオ2 (加速): ZMP=({zmp_accel[0]:.3f}, {zmp_accel[1]:.3f}), margin={stability_margin(np.array(zmp_accel), sp_full):.3f} m")
print(f"シナリオ3 (3脚): ZMP=({zmp_3leg[0]:.3f}, {zmp_3leg[1]:.3f}), margin={stability_margin(np.array(zmp_3leg), sp_3leg):.3f} m")

このコードでは、3つのシナリオで ZMP と支持多角形の関係を可視化しています。シナリオ1(静止)では ZMP は重心の真下にあり、四角形の支持多角形の中心付近にあるため安定余裕が大きくなります。シナリオ2(加速中)では水平加速度の影響で ZMP が支持多角形内でずれ、安定余裕が減少します。シナリオ3(1脚遊脚)では支持多角形が三角形に縮小し、安定余裕がさらに小さくなります。このように、歩行中は脚を持ち上げるたびに支持多角形が変化し、ZMP を常にその内部に収めるよう制御する必要があることが視覚的に理解できます。

ZMPは「転倒しないか」を判定する強力な指標ですが、具体的にどのような順序で脚を動かすか — つまり歩容パターンをどう生成するか — は、ZMPだけでは決まりません。生物から着想を得た歩容生成メカニズムであるCPGを次に見ていきましょう。

CPG — 生物に学ぶリズミカルな歩容生成

CPGの直感的理解

馬が走るとき、4本の脚を動かす順序とタイミングは精密に制御されています。驚くべきことに、このリズミカルな脚の動きは脳の高次中枢からの詳細な指令なしに、脊髄内の神経回路が自律的に生成していることがわかっています。1911年にブラウン(T. Graham Brown)が脊髄損傷した猫でもリズミカルな脚の動きが現れることを発見して以来、この神経回路はCPG(Central Pattern Generator: 中枢パターン生成器)と呼ばれてきました。

CPGの本質を一言で言えば、「感覚フィードバックがなくても自律的にリズムを生み出す神経回路」です。もちろん実際の動物では感覚フィードバック(足が地面に触れた情報など)が歩容を修正しますが、基本的なリズムはCPG単独で生成されます。

ロボット工学では、この生物学的なメカニズムを数学モデルで模倣し、歩行パターンを生成します。CPGモデルの利点は次の通りです。

  1. パラメータが少ない — 歩行速度や歩容の切り替えを少数のパラメータで制御できる
  2. 外乱に対するロバスト性 — 結合振動子の同期特性により、外乱からの回復が自然に行われる
  3. 感覚フィードバックとの統合が容易 — 環境からのフィードバックでリズムを変調できる
  4. 軌道計画不要 — 脚の軌道を事前に詳細に計画する必要がない

結合振動子モデル

CPGの最も基本的な数学モデルは、結合振動子(Coupled Oscillators)です。各脚に1つの振動子を割り当て、振動子間の結合によって脚同士の位相関係(タイミング)を決定します。

各振動子 $i$ の状態を位相 $\phi_i$ で表すとしましょう。$N$ 個の振動子($N$ 本の脚に対応)の位相ダイナミクスは、蔵本(Kuramoto)モデルを拡張した以下の形で記述されます。

$$ \begin{equation} \dot{\phi}_i = \omega_i + \sum_{j=1}^{N} w_{ij} \sin(\phi_j – \phi_i – \psi_{ij}) \end{equation} $$

各記号の意味を確認しましょう。$\omega_i$ は振動子 $i$ の固有角振動数で、脚の振りの基本的な速さを決めます。$w_{ij}$ は振動子 $i$ と $j$ の間の結合強度で、大きいほど互いの影響が強くなります。$\psi_{ij}$ は目標位相差で、脚 $i$ と脚 $j$ のタイミングのずれを指定します。

この方程式が表していることを直感的に言えば、「各脚は自分のペースで動こうとするが($\omega_i$ の項)、他の脚との結合によって互いにタイミングを合わせようとする($\sin$ の項)」ということです。結合項の $\sin$ 関数は、実際の位相差 $(\phi_j – \phi_i)$ と目標位相差 $\psi_{ij}$ のずれに比例した「修正力」を生み出します。

位相から脚の運動への変換

CPGが生成するのは「各脚の位相」であり、実際の脚の運動はこの位相から変換して得ます。最もシンプルな変換は、位相 $\phi_i$ に基づいて脚の遊脚(空中)と立脚(接地)を切り替えるものです。

デューティファクター $\beta$ を導入します。これは1周期のうち脚が地面に接触している割合です。

$$ \text{脚の状態} = \begin{cases} \text{立脚(stance)} & 0 \leq (\phi_i \mod 2\pi) < 2\pi\beta \\ \text{遊脚(swing)} & 2\pi\beta \leq (\phi_i \mod 2\pi) < 2\pi \end{cases} $$

デューティファクター $\beta$ は歩容の性質を決める重要なパラメータです。$\beta > 0.5$ であれば各脚は半周期以上を接地に使っており、低速で安定した歩行に対応します。$\beta < 0.5$ は高速走行で、空中にいる時間が長くなります。$\beta = 0.5$ が歩行と走行の境界です。

遊脚中の足先軌道は、典型的には半楕円(または台形)の軌道で設計します。足先の $x$ 方向(進行方向)の位置は三角関数で滑らかに変化させ、$z$ 方向(高さ)は地面からの持ち上げ高さを指定します。

ここまでCPGの基本的な仕組みを見てきました。CPGの真の威力は、目標位相差 $\psi_{ij}$ を変えるだけで様々な歩容パターンを切り替えられる点にあります。次のセクションでは、代表的な歩容パターンとその特性を見ていきましょう。

歩容パターン — ウォーク・トロット・ギャロップ

歩容とは何か

歩容(Gait)とは、歩行中の各脚の接地・離地のタイミングパターンのことです。同じ構造のロボットでも、歩容を切り替えることで低速安定歩行から高速走行まで様々な運動を実現できます。自然界では、馬が速度に応じて常歩(ウォーク)→ 速歩(トロット)→ 駈歩(キャンター)→ 襲歩(ギャロップ)と歩容を切り替えることが知られています。

4脚ロボットの場合、脚を左前(LF)、右前(RF)、左後(LH)、右後(RH)と名付け、各脚の位相差で歩容を定義します。LFの位相を基準($\phi_{LF} = 0$)として、他の脚の位相差を $\psi$ で指定します。

代表的な歩容パターン

ウォーク(Walk / 常歩)

ウォークは最も低速で安定した歩容です。4本の脚を1本ずつ順番に持ち上げ、常に3本以上が接地しています。

位相差の設定は次の通りです。

位相差
LF $0$
RH $\pi/2$
RF $\pi$
LH $3\pi/2$

デューティファクターは $\beta \geq 0.75$ で、各脚が全周期の75%以上を接地に費やします。常に少なくとも3脚が接地しているため、支持多角形は常に三角形以上であり、ZMP ベースの安定歩行が容易に実現できます。

トロット(Trot / 速歩)

トロットは中速度の歩容で、対角線上の2本の脚を同時に動かします。馬の速歩と同じパターンです。

位相差
LF $0$
RH $0$
RF $\pi$
LH $\pi$

LFとRH(左前・右後)が同時に遊脚、次にRFとLH(右前・左後)が同時に遊脚です。デューティファクターは $\beta \approx 0.5$ 前後です。支持多角形は対角線上の2点を結ぶ線分に退化するため、厳密には ZMP ベースの静的安定性は成り立ちません。しかし、動的なバランスにより安定した移動が可能です。四脚ロボットの多くはトロットを主要歩容として採用しています。

ギャロップ(Gallop / 襲歩)

ギャロップは最高速の歩容です。前脚2本と後脚2本がほぼ同時に動き、全脚が地面から離れる空中相(flight phase)が存在します。

位相差
LF $0$
RF $\delta$(小さな位相差)
LH $\pi$
RH $\pi + \delta$

ここで $\delta$ は小さな値($\pi/8$ 程度)で、同側の前後脚のわずかなタイミングのずれを表します。デューティファクターは $\beta < 0.5$ で、空中相を含む動的な走行です。エネルギー効率は高いですが、衝撃が大きく制御も難しいため、現時点では宇宙応用よりも地上の高速走行ロボットで主に研究されています。

プロンク(Pronk / 跳躍歩容)

全4脚を同時に動かす特殊な歩容で、バウンディングとも呼ばれます。全脚が同位相($\psi = 0$)です。実用的ではありませんが、低重力環境での効率的な移動手段として研究対象になっています。ETHチューリッヒのSpaceBokはこの歩容を月面での跳躍移動に活用することを目指しています。

歩容の数理的統一

ここまで見てきた歩容パターンは、すべてCPGモデルの位相差パラメータ $\psi_{ij}$ の設定で統一的に記述できます。これをPythonで実装して確認しましょう。

import numpy as np
import matplotlib.pyplot as plt

def simulate_cpg(gait_phases, omega=2*np.pi, coupling=5.0, beta=0.6,
                 dt=0.001, T=5.0):
    """
    CPG結合振動子モデルのシミュレーション

    Parameters
    ----------
    gait_phases : dict
        各脚の目標位相差 {'LF': 0, 'RF': pi, ...}
    omega : float
        固有角振動数
    coupling : float
        結合強度
    beta : float
        デューティファクター
    dt : float
        時間刻み
    T : float
        シミュレーション時間
    """
    leg_names = list(gait_phases.keys())
    N = len(leg_names)
    target_phases = np.array([gait_phases[name] for name in leg_names])

    # 初期位相(少しランダムにずらす)
    phi = np.random.uniform(0, 2*np.pi, N)
    t_array = np.arange(0, T, dt)
    phi_history = np.zeros((len(t_array), N))

    for k, t in enumerate(t_array):
        phi_history[k] = phi.copy()
        dphi = np.zeros(N)
        for i in range(N):
            dphi[i] = omega
            for j in range(N):
                if i != j:
                    # 目標位相差: target_phases[j] - target_phases[i]
                    psi_ij = target_phases[j] - target_phases[i]
                    dphi[i] += coupling * np.sin(phi[j] - phi[i] - psi_ij)
        phi += dphi * dt
    return t_array, phi_history, leg_names, beta

# 歩容パターンの定義
gaits = {
    'Walk': {'LF': 0, 'RH': np.pi/2, 'RF': np.pi, 'LH': 3*np.pi/2},
    'Trot': {'LF': 0, 'RH': 0, 'RF': np.pi, 'LH': np.pi},
    'Gallop': {'LF': 0, 'RF': np.pi/8, 'LH': np.pi, 'RH': np.pi + np.pi/8},
    'Pronk': {'LF': 0, 'RF': 0, 'LH': 0, 'RH': 0},
}
betas = {'Walk': 0.75, 'Trot': 0.5, 'Gallop': 0.35, 'Pronk': 0.4}

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

for ax, (gait_name, phases) in zip(axes, gaits.items()):
    beta = betas[gait_name]
    t, phi_hist, names, _ = simulate_cpg(phases, beta=beta, T=4.0)

    # 接地/遊脚のタイムチャート
    for i, name in enumerate(names):
        phase_mod = phi_hist[:, i] % (2 * np.pi)
        stance = phase_mod < 2 * np.pi * beta
        # 接地区間を塗りつぶし
        ax.fill_between(t, i - 0.4, i + 0.4, where=stance,
                        color='cyan', alpha=0.7)
        ax.fill_between(t, i - 0.4, i + 0.4, where=~stance,
                        color='gray', alpha=0.2)

    ax.set_yticks(range(len(names)))
    ax.set_yticklabels(names)
    ax.set_title(f'{gait_name} (β={beta})', fontsize=12, fontweight='bold')
    ax.set_xlim(1.5, 4.0)  # 収束後を表示
    ax.set_xlabel('Time [s]')
    ax.grid(True, axis='x', alpha=0.3)

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

このタイムチャートから、各歩容の特徴が明確に読み取れます。ウォーク(Walk)では、シアン(接地)の帯が各脚で$1/4$周期ずつずれており、常に3本以上が接地しています。トロット(Trot)では、LF-RH と RF-LH のペアが交互に接地・遊脚を繰り返し、対角線同期の構造が視覚的に確認できます。ギャロップ(Gallop)では接地時間が短くなり($\beta = 0.35$)、全脚が空中にある瞬間(空中相)が存在します。プロンク(Pronk)では全脚が同時に接地・同時に離地する最もシンプルなパターンが現れています。CPGモデルの結合によって、ランダムな初期位相から目標の歩容パターンに自然に収束している点も注目に値します。

歩容パターンの基本が理解できたところで、次は各脚がどのような軌道を描いて動くか — 足先の軌道生成について詳しく見ていきましょう。

足先軌道生成 — 遊脚はどう動くか

立脚と遊脚の軌道

歩行中の各脚は、立脚相(Stance Phase)遊脚相(Swing Phase)を交互に繰り返します。

立脚相では、足先は地面に固定されており、胴体が前進することで相対的に後方へ移動します。胴体の速度を $v_{\text{body}}$ とすると、胴体座標系から見た足先の $x$ 方向速度は $-v_{\text{body}}$ です。

遊脚相では、足先を地面から持ち上げて前方に運び、次の接地点に着地させます。この軌道は障害物を避けつつ滑らかに動くよう設計する必要があります。

半楕円軌道モデル

遊脚中の足先軌道として最も広く使われるのが、半楕円軌道です。遊脚の開始点から終了点(ストライド長 $s$ だけ前方)まで、最大持ち上げ高さ $h$ の半楕円を描きます。

遊脚相の正規化時間を $\tau \in [0, 1]$ として、足先の軌道は次のように表されます。

進行方向($x$ 軸)の位置は、後方から前方へ線形に移動します。

$$ \begin{equation} x(\tau) = x_{\text{start}} + s \cdot \tau \end{equation} $$

高さ方向($z$ 軸)は、半楕円(正弦曲線)で滑らかに持ち上がり、着地します。

$$ \begin{equation} z(\tau) = h \sin(\pi \tau) \end{equation} $$

この $\sin$ 関数を使った理由は、$\tau = 0$ と $\tau = 1$ で $z = 0$(地面)となり、$\tau = 0.5$ で最大高さ $h$ に達する滑らかな曲線が得られるからです。さらに、接線が離地・着地時に水平になるため、衝撃が小さくなります。

ベジェ曲線による高度な軌道

より柔軟な軌道設計にはベジェ曲線(Bézier Curve)が使われます。$n$ 次ベジェ曲線は $n+1$ 個の制御点 $\bm{P}_0, \bm{P}_1, \ldots, \bm{P}_n$ で定義されます。

$$ \begin{equation} \bm{B}(\tau) = \sum_{k=0}^{n} \binom{n}{k} (1-\tau)^{n-k} \tau^k \bm{P}_k, \quad \tau \in [0, 1] \end{equation} $$

5次ベジェ曲線(6つの制御点)を使うと、離地・着地の位置だけでなく速度と加速度も指定でき、「つまずかないように高く持ち上げてから素早く着地する」といった細かい軌道設計が可能になります。

以下のコードで、半楕円軌道とベジェ曲線軌道を比較してみましょう。

import numpy as np
import matplotlib.pyplot as plt
from scipy.special import comb

def swing_trajectory_ellipse(tau, stride, height):
    """半楕円遊脚軌道"""
    x = -stride/2 + stride * tau
    z = height * np.sin(np.pi * tau)
    return x, z

def bezier_curve(tau, control_points):
    """n次ベジェ曲線"""
    n = len(control_points) - 1
    point = np.zeros(2)
    for k in range(n + 1):
        coeff = comb(n, k) * (1 - tau)**(n - k) * tau**k
        point += coeff * control_points[k]
    return point[0], point[1]

def swing_trajectory_bezier(tau, stride, height):
    """5次ベジェ曲線遊脚軌道"""
    control_points = np.array([
        [-stride/2, 0.0],       # P0: 離地点
        [-stride/2, height],    # P1: 急速に持ち上げ
        [-stride/4, height*1.2],# P2: 最高点付近
        [stride/4, height*1.2], # P3: 最高点付近
        [stride/2, height*0.3], # P4: 降下開始
        [stride/2, 0.0],        # P5: 着地点
    ])
    return bezier_curve(tau, control_points)

# 軌道の比較プロット
tau_vals = np.linspace(0, 1, 200)
stride = 0.2  # ストライド長 [m]
height = 0.08  # 持ち上げ高さ [m]

x_ell = np.zeros(len(tau_vals))
z_ell = np.zeros(len(tau_vals))
x_bez = np.zeros(len(tau_vals))
z_bez = np.zeros(len(tau_vals))

for i, tau in enumerate(tau_vals):
    x_ell[i], z_ell[i] = swing_trajectory_ellipse(tau, stride, height)
    x_bez[i], z_bez[i] = swing_trajectory_bezier(tau, stride, height)

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

# 軌道の形状比較
axes[0].plot(x_ell, z_ell, 'c-', linewidth=2, label='Semi-ellipse')
axes[0].plot(x_bez, z_bez, 'm--', linewidth=2, label='Bézier (5th order)')
axes[0].fill_between(np.linspace(-0.15, 0.15, 50), -0.01, 0, color='gray', alpha=0.3)
axes[0].set_xlabel('x [m] (Forward)')
axes[0].set_ylabel('z [m] (Height)')
axes[0].set_title('Swing Foot Trajectory Comparison')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[0].set_aspect('equal')
axes[0].set_ylim(-0.02, 0.12)

# 高さの時間変化
axes[1].plot(tau_vals, z_ell, 'c-', linewidth=2, label='Semi-ellipse')
axes[1].plot(tau_vals, z_bez, 'm--', linewidth=2, label='Bézier (5th order)')
axes[1].set_xlabel('Normalized phase τ')
axes[1].set_ylabel('z [m] (Height)')
axes[1].set_title('Foot Height vs Phase')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

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

左のグラフから、半楕円軌道は対称な弧を描くのに対し、ベジェ曲線軌道は離地時に素早く持ち上がり、着地前にやや緩やかに降下する非対称な形状になっていることがわかります。右のグラフ(高さの時間変化)を見ると、ベジェ曲線は遊脚相の前半で急速に最大高さに達し、後半をゆっくり降下する特性を持っています。この非対称性は、障害物をすばやく乗り越えてから慎重に着地するという実用的な要求に適しています。実際の四脚ロボット(MIT Cheetah 3、ANYmal 等)ではベジェ曲線やスプライン曲線が広く使われています。

足先軌道の設計方法がわかったところで、次は宇宙応用で最も重要な問題 — 低重力環境で歩行がどう変わるか — を考えましょう。

低重力環境での歩行 — 月面・火星で脚型ロボットはどう歩くか

重力が歩行に与える影響

地球上の歩行制御は、$g = 9.81 \, \text{m/s}^2$ の重力加速度を前提として設計されています。月面($g_{\text{Moon}} = 1.62 \, \text{m/s}^2$、地球の約 1/6)や火星表面($g_{\text{Mars}} = 3.72 \, \text{m/s}^2$、地球の約 3/8)では、歩行の力学特性が大きく変化します。

まず、フルード数(Froude Number)を導入しましょう。歩行のダイナミクスを支配する無次元数です。

$$ \begin{equation} Fr = \frac{v^2}{g \, l} \end{equation} $$

ここで $v$ は歩行速度、$g$ は重力加速度、$l$ は脚の長さ(あるいは股関節の高さ)です。フルード数の物理的意味は、歩行における慣性力と重力の比です。

生物の歩行研究から、以下の経験則が知られています。

  • $Fr < 0.5$ : ウォーク(歩行)— 少なくとも1脚は常に接地
  • $Fr \approx 0.5$ : 歩容の遷移点(ウォーク → トロット)
  • $Fr > 0.5$ : ランニング歩容(トロット・ギャロップ)— 空中相を含む

重力が小さくなると、同じ速度 $v$ と脚長 $l$ に対してフルード数が大きくなります。つまり、低重力環境では低速でも $Fr > 0.5$ に達しやすく、歩行から走行への遷移がより低速で起こるということです。

月面での歩行速度の上限を見積もりましょう。ウォークの条件 $Fr < 0.5$ から次のようになります。

$$ v < \sqrt{0.5 \cdot g \cdot l} $$

脚長 $l = 0.5 \, \text{m}$ のロボットを例にとると、各天体での最大歩行速度は次のようになります。

地球では $v < \sqrt{0.5 \times 9.81 \times 0.5} = 1.57 \, \text{m/s}$ です。

月面では $v < \sqrt{0.5 \times 1.62 \times 0.5} = 0.64 \, \text{m/s}$ です。

火星では $v < \sqrt{0.5 \times 3.72 \times 0.5} = 0.96 \, \text{m/s}$ です。

月面ではわずか $0.64 \, \text{m/s}$(時速約2.3 km)で歩行からランニングに遷移してしまいます。アポロ宇宙飛行士が月面で「ぴょんぴょん跳ねるように」移動していたのは、まさにこの物理的制約の表れです。低重力下では通常の歩行よりも跳躍の方が効率的になるのです。

ZMPへの影響

低重力環境では ZMP の振る舞いも大きく変化します。ZMP の簡略式を再掲します。

$$ x_{ZMP} \approx x_G – \frac{\ddot{x}_G}{g} z_G $$

重力加速度 $g$ が小さくなると、分母が小さくなるため、同じ水平加速度 $\ddot{x}_G$ に対して ZMP のずれが大きくなります。これは、低重力環境ではわずかな加速度でも ZMP が支持多角形から外れやすいことを意味します。

定量的に評価しましょう。地球での水平加速度 $\ddot{x}_G = 1 \, \text{m/s}^2$、重心高さ $z_G = 0.4 \, \text{m}$ のとき、ZMPのずれは次のようになります。

地球: $\Delta x = \frac{1.0}{9.81} \times 0.4 = 0.041 \, \text{m}$

月面: $\Delta x = \frac{1.0}{1.62} \times 0.4 = 0.247 \, \text{m}$

月面では同じ加速度で ZMP が約6倍もずれます。支持多角形のサイズが同じなら、安定余裕が大幅に減少します。この問題に対処するには、(1) 低速歩行に限定する、(2) 重心を下げる($z_G$ を小さく)、(3) 足の接地幅を広げる(支持多角形を大きく)、(4) 跳躍歩容に切り替える、といった戦略が考えられます。

接地力と牽引力

低重力環境では、ロボットの自重が小さいため、足先が地面に押し付ける力(法線力 $N$)も小さくなります。摩擦力の上限はクーロンの摩擦法則で $f_{\max} = \mu N$ ですから、法線力の減少は利用可能な牽引力の低下に直結します。

$$ \begin{equation} f_{\max} = \mu \cdot \frac{M g_{\text{local}}}{n_{\text{contact}}} \end{equation} $$

ここで $\mu$ は摩擦係数、$M$ はロボットの質量、$g_{\text{local}}$ は現地の重力加速度、$n_{\text{contact}}$ は接地脚の数です。

月面レゴリスの摩擦係数は $\mu \approx 0.4 \sim 0.8$ 程度とされていますが、粒子が細かく低密度なため、足先が沈み込んでめり込む現象(テラメカニクスの問題)も考慮する必要があります。

Pythonで低重力環境での歩行パラメータの変化を計算・可視化してみましょう。

import numpy as np
import matplotlib.pyplot as plt

# 天体パラメータ
bodies = {
    'Earth': {'g': 9.81, 'color': 'deepskyblue'},
    'Mars': {'g': 3.72, 'color': 'orangered'},
    'Moon': {'g': 1.62, 'color': 'silver'},
    'Titan': {'g': 1.35, 'color': 'gold'},
    'Europa': {'g': 1.31, 'color': 'lightyellow'},
}

leg_length = 0.5  # 脚長 [m]
z_G = 0.4         # 重心高さ [m]
robot_mass = 50   # ロボット質量 [kg]
mu = 0.6          # 摩擦係数
n_contact = 3     # 接地脚数

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

# (1) フルード数と速度の関係
ax = axes[0, 0]
v_range = np.linspace(0, 3, 200)
for name, params in bodies.items():
    Fr = v_range**2 / (params['g'] * leg_length)
    ax.plot(v_range, Fr, color=params['color'], linewidth=2, label=name)
ax.axhline(y=0.5, color='red', linestyle='--', alpha=0.7, label='Walk/Run transition')
ax.set_xlabel('Walking speed v [m/s]')
ax.set_ylabel('Froude number Fr')
ax.set_title('Froude Number vs Walking Speed')
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 3)

# (2) 最大歩行速度の比較
ax = axes[0, 1]
names = list(bodies.keys())
g_vals = [bodies[n]['g'] for n in names]
v_max = [np.sqrt(0.5 * g * leg_length) for g in g_vals]
colors = [bodies[n]['color'] for n in names]
bars = ax.bar(names, v_max, color=colors, edgecolor='white', linewidth=1.5)
for bar, v in zip(bars, v_max):
    ax.text(bar.get_x() + bar.get_width()/2., bar.get_height() + 0.02,
            f'{v:.2f}', ha='center', fontsize=10)
ax.set_ylabel('Max walk speed [m/s]')
ax.set_title(f'Maximum Walking Speed (l={leg_length}m)')
ax.grid(True, axis='y', alpha=0.3)

# (3) ZMPずれの比較
ax = axes[1, 0]
accel_range = np.linspace(0, 3, 200)
for name, params in bodies.items():
    zmp_shift = (accel_range / params['g']) * z_G
    ax.plot(accel_range, zmp_shift * 100, color=params['color'], linewidth=2, label=name)
ax.axhline(y=15, color='red', linestyle='--', alpha=0.5, label='Typical margin (15cm)')
ax.set_xlabel('Horizontal acceleration [m/s²]')
ax.set_ylabel('ZMP shift [cm]')
ax.set_title('ZMP Shift vs Horizontal Acceleration')
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

# (4) 最大牽引力の比較
ax = axes[1, 1]
f_max = [mu * robot_mass * g / n_contact for g in g_vals]
bars = ax.bar(names, f_max, color=colors, edgecolor='white', linewidth=1.5)
for bar, f in zip(bars, f_max):
    ax.text(bar.get_x() + bar.get_width()/2., bar.get_height() + 1,
            f'{f:.1f}N', ha='center', fontsize=10)
ax.set_ylabel('Max traction force per leg [N]')
ax.set_title(f'Maximum Traction Force (M={robot_mass}kg, μ={mu})')
ax.grid(True, axis='y', alpha=0.3)

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

4つのグラフから、低重力環境での歩行の困難さが定量的に読み取れます。左上のフルード数グラフでは、月面(銀色)やタイタン(金色)では低速でもフルード数が急速に増加し、歩行可能な速度域が地球に比べて大幅に狭いことがわかります。右上の棒グラフでは、月面での最大歩行速度が地球の約40%にとどまることが明確です。左下のZMPずれグラフでは、月面で同じ加速度に対して ZMP が地球の約6倍もずれることが示されており、安定歩行の制御がいかに難しいかを物語っています。右下の牽引力グラフからは、月面での脚あたり最大牽引力が地球の約1/6であり、急な推進や制動が困難であることがわかります。

これらの分析から、低重力環境では「静的安定性を重視した低速ウォーク」か「跳躍を活用したダイナミック歩容」の二極化した戦略が有効であることが示唆されます。では実際に、こうした課題にどのようなロボットが挑んでいるのかを見ていきましょう。

宇宙応用の実例 — 月面・惑星を歩くロボットたち

SpaceBok — 月面で跳ねる四脚ロボット

SpaceBokは、ETHチューリッヒのRobotic Systems Lab(RSL)が開発した四脚ロボットで、低重力環境での動的移動を主要テーマとしています。名前は南アフリカのスプリングボック(跳躍するアンテロープ)に由来しています。

SpaceBokの最も革新的な点は、プロンク歩容(全脚同時跳躍)を低重力環境で積極的に活用することを提案していることです。前述のように、低重力では通常の歩行の速度域が狭くなりますが、逆に跳躍に必要なエネルギーは小さくなります。月面重力(1/6 g)では、地球と同じ脚の蹴り出し速度で6倍の高さまで跳べるのです。

SpaceBokの主な仕様は以下の通りです。

パラメータ
質量 約 25 kg
脚数 4
脚の自由度 各2 DoF(股関節 + 膝関節)
歩容 プロンク(跳躍)+ スタティックウォーク
対象環境 月面(1/6 g)
開発元 ETH Zürich RSL

ESAが保有する模擬低重力環境での実験では、SpaceBokは$1/6 \, g$のシミュレーション環境で最大 2 m の跳躍高度を達成しています。跳躍歩容は「空中での姿勢制御」というさらなる課題を生みますが、SpaceBokはリアクションホイールを胴体に搭載することで空中での姿勢安定化を実現しています。これは宇宙機の姿勢制御技術(リアクションホイール)を脚型ロボットに取り込んだ興味深い事例です。

ANYmal — 産業用から宇宙へ

ANYmalもETHチューリッヒ RSL 発の四脚ロボットで、同大学発のスタートアップ ANYbotics 社が商用化しています。各脚は3自由度(股関節アブダクション・股関節フレクション・膝関節)を持ち、高い不整地走破性を誇ります。

ANYmalの特徴は、強化学習ベースの歩行制御を実用レベルで実現している点です。従来のモデルベース制御(ZMP、逆運動学等)に加え、深層強化学習によって未知の地形に適応する歩行ポリシーをシミュレーションで学習し、実機に転移(Sim-to-Real Transfer)する手法を確立しています。

宇宙応用の文脈では、ESAの研究プロジェクトにおいて月面探査への適用が検討されています。ANYmalをベースに、放射線耐性の向上、真空環境での熱管理、低重力歩行アルゴリズムの開発が進められています。

ATHLETE — NASAの巨大6脚ロボット

ATHLETE(All-Terrain Hex-Limbed Extra-Terrestrial Explorer)は、NASAジェット推進研究所(JPL)が開発した巨大な6脚ロボットです。各脚の先端に車輪が付いているという、脚型と車輪型のハイブリッド設計が最大の特徴です。

ATHLETEのコンセプトは明快です。平坦な地面では車輪で効率よく走行し、岩場や急斜面では脚型歩行に切り替えるという、両方のメリットを享受する設計です。各脚は6自由度を持ち、先端には車輪の他にドリルやグリッパーなどのツールを取り付けることもできます。

パラメータ
質量 約 850 kg(フルスケール版)
脚数 6
脚の自由度 各6 DoF
脚先端 車輪 + ツール交換機構
スパン 約 4.5 m
対象環境 月面
開発元 NASA JPL

ATHLETEの6脚構成には力学的な利点があります。6脚のうち1本を遊脚にしても残り5脚で支持多角形を構成でき、4脚ロボットよりも大きな安定余裕を確保できます。さらに、対称な6角形配置により、どの方向への移動でも均等な安定性が得られます。

RoboSimian — 災害対応から宇宙応用へ

RoboSimian(ロボシミアン)は、NASA JPLが開発した4脚ロボットです。2015年のDARPA Robotics Challenge(災害対応ロボットの競技会)に参加し、上位に入賞しました。名前の通り、サル(Simian)のような体型で、4本の脚はそれぞれ腕としても機能する独特の設計です。

RoboSimianの設計哲学は「慎重で確実な移動」です。他の四脚ロボットが動的歩行(トロットなど)を得意とするのに対し、RoboSimianは静的安定歩行を基本とし、常に3脚以上で支持された状態を維持します。各脚/腕は7自由度を持ち、先端のグリッパーで手すりや岩を掴むことも可能です。

宇宙応用の観点では、RoboSimianの設計は溶岩洞内部の探査に適しています。崩壊した岩の上を慎重に歩き、壁面を掴んで降下し、科学機器を設置するといった多彩なタスクが想定されています。

比較表

ロボット 脚数 DoF/脚 質量 特徴 開発元
SpaceBok 4 2 25 kg 跳躍歩容 + リアクションホイール ETH Zürich
ANYmal 4 3 30 kg 強化学習ベース歩行 ETH Zürich / ANYbotics
ATHLETE 6 6 850 kg 車輪-脚ハイブリッド NASA JPL
RoboSimian 4 7 100 kg 脚=腕の4肢設計 NASA JPL

これらのロボットが実際に活躍する舞台として、いま最も注目されているのが月面の溶岩洞探査です。

月面溶岩洞探査 — 脚型ロボットの究極の活躍の場

溶岩洞とは

月面の溶岩洞(Lava Tube)は、かつて月の内部から溶岩が流れ出たときに形成されたトンネル状の地下空洞です。溶岩が流れる際、表面が先に冷えて固まり、内部の溶岩が流れ去った後にトンネルが残ります。地球上にも同様の構造がありますが、月面の溶岩洞は月の低重力(1/6 g)のおかげで地球のものよりはるかに大規模とされています。

2009年、日本の月探査機「かぐや」が月面に巨大な縦穴(ピット)を発見しました。直径約 65 m、深さ約 80〜90 m と推定されるこの穴は、地下の溶岩洞の天井が崩壊して形成されたスカイライトと考えられています。その後、複数の同様の縦穴が発見されており、月面の地下に広大なトンネルネットワークが存在する可能性が指摘されています。

溶岩洞が宇宙探査で重要な理由

月面溶岩洞は、将来の月面基地の候補地として高い注目を集めています。その理由を整理しましょう。

放射線遮蔽: 月面には地球のような磁場や大気がなく、太陽フレアや銀河宇宙線が直接降り注ぎます。溶岩洞の岩盤(厚さ数十メートル以上)は天然の放射線シールドとして機能し、人間の長期滞在を可能にします。

微小隕石からの保護: 月面では微小隕石が秒速数十 km で衝突します。地下の溶岩洞は完全に保護された環境を提供します。

温度安定性: 月面の表面温度は昼夜で約 -173°C 〜 +127°C と 300 度の温度差がありますが、地下数メートルでは温度がほぼ一定(約 -20°C 前後)に安定します。

大空間: 月面の溶岩洞は幅数百メートル、高さ数十メートル、長さ数キロメートル以上に達する可能性があり、大規模な基地建設に十分な空間を提供できます。

なぜ脚型ロボットが必要か

溶岩洞の探査は、車輪型ローバーにとって極めて困難な環境です。

第一に、縦穴への降下が必要です。スカイライト(天井の穴)から内部に入るには、80 m 以上の垂直降下が求められます。車輪では不可能ですが、脚型ロボット(特に RoboSimian のようなグリッパー付き)であれば壁面を掴みながら降下できる可能性があります。あるいは、テザー(ロープ)で懸垂降下した後に脚で歩行する方式も考えられます。

第二に、崩壊地形の走破です。溶岩洞の内部は、天井の部分的な崩壊で生じた岩塊が散乱している可能性が高いです。不規則な形状の岩の上を歩くには、離散的な足場の選択が可能な脚型ロボットが適しています。

第三に、傾斜面の走行です。溶岩洞の床面は必ずしも平坦ではなく、溶岩が流れた跡の傾斜や段差が存在します。脚型ロボットは急傾斜面でも姿勢を保ちながら歩行できます。

第四に、暗闇での自律行動です。溶岩洞の内部はスカイライト直下を除いて完全な暗闇です。LiDAR やステレオカメラを用いた SLAM(自己位置推定と環境地図作成の同時実行)を行いながら自律的に歩行する能力が求められます。

想定されるミッションシナリオ

月面溶岩洞探査の典型的なシナリオを考えてみましょう。

  1. 着陸: ランダーがスカイライト近傍の平坦地に着陸する
  2. 地表移動: 脚型ロボットがランダーから降りて、スカイライトの縁まで歩行する
  3. 降下: テザーで縦穴に降下する。脚は壁面への接触でダンピングと姿勢制御に使用する
  4. 内部探査: 溶岩洞内部を脚型歩行で移動し、LiDARでマッピングしながら科学データ(温度、放射線量、岩石サンプル等)を収集する
  5. 帰還: テザーを使って縦穴を登り、ランダーに帰還する。あるいはデータのみを無線中継で地表に送る

このシナリオにおいて、脚型ロボットの歩行制御に特に求められるのは、(1) 未知の不整地形を LiDAR で認識して安全な足場を選択するフットホールド・プランニング、(2) 低重力(1/6 g)での ZMP 管理と安定歩行、(3) 暗闘環境での SLAM を用いた自律航法、(4) 通信途絶時の完全自律行動、です。

では最後に、これまでの理論を統合したシミュレーションを Python で実装しましょう。4脚ロボットの CPG 歩行シミュレーションを地球・月面・火星の各環境で実行し、低重力が歩行に与える影響を定量的に確認します。

Pythonで実装する — 4脚ロボットのCPG歩行シミュレーション

シミュレーションの概要

これから実装するシミュレーションでは、以下の要素を統合します。

  1. CPG結合振動子による歩容パターン(トロット)の生成
  2. 半楕円軌道による遊脚の軌道生成
  3. ZMPの計算と安定性評価
  4. 地球・月面・火星の3つの重力環境での比較

まず、CPGと足先軌道生成の部分を実装します。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon as MplPolygon
from scipy.spatial import ConvexHull

class QuadrupedCPGWalker:
    """4脚ロボットのCPG歩行シミュレーション"""

    def __init__(self, g=9.81, body_mass=50.0, body_length=0.6,
                 body_width=0.4, leg_length=0.5, stride=0.15,
                 swing_height=0.06):
        # 環境パラメータ
        self.g = g
        # ロボットパラメータ
        self.body_mass = body_mass
        self.body_length = body_length
        self.body_width = body_width
        self.leg_length = leg_length
        self.stride = stride
        self.swing_height = swing_height
        # 股関節位置(ボディ座標系)
        self.hip_offsets = np.array([
            [ body_length/2,  body_width/2],  # LF
            [ body_length/2, -body_width/2],  # RF
            [-body_length/2,  body_width/2],  # LH
            [-body_length/2, -body_width/2],  # RH
        ])
        # CPGパラメータ(トロット歩容)
        self.target_phases = np.array([0, np.pi, np.pi, 0])  # LF, RF, LH, RH
        self.omega = 2 * np.pi * 1.0  # 1 Hz
        self.coupling = 8.0
        self.beta = 0.6  # デューティファクター

    def simulate(self, T=6.0, dt=0.002, walk_speed=0.3):
        """歩行シミュレーションを実行"""
        steps = int(T / dt)
        # CPG位相
        phi = np.array([0.0, np.pi + 0.1, np.pi - 0.1, 0.2])
        # 記録用配列
        t_hist = np.zeros(steps)
        phi_hist = np.zeros((steps, 4))
        body_x_hist = np.zeros(steps)
        foot_pos_hist = np.zeros((steps, 4, 2))  # 各脚のxy位置
        zmp_hist = np.zeros((steps, 2))
        margin_hist = np.zeros(steps)
        stance_hist = np.zeros((steps, 4), dtype=bool)

        body_x = 0.0
        body_vx = walk_speed
        prev_vx = walk_speed

        for k in range(steps):
            t = k * dt
            t_hist[k] = t
            phi_hist[k] = phi.copy()
            body_x_hist[k] = body_x

            # CPGの更新
            dphi = np.zeros(4)
            for i in range(4):
                dphi[i] = self.omega
                for j in range(4):
                    if i != j:
                        psi_ij = self.target_phases[j] - self.target_phases[i]
                        dphi[i] += self.coupling * np.sin(phi[j] - phi[i] - psi_ij)
            phi += dphi * dt

            # 各脚の状態と位置
            foot_positions = np.zeros((4, 2))
            stance = np.zeros(4, dtype=bool)

            for i in range(4):
                phase_mod = phi[i] % (2 * np.pi)
                is_stance = phase_mod < 2 * np.pi * self.beta
                stance[i] = is_stance

                hip_x = body_x + self.hip_offsets[i, 0]
                hip_y = self.hip_offsets[i, 1]

                if is_stance:
                    # 立脚: 足先は地面に固定(ボディ座標系では後退)
                    stance_progress = phase_mod / (2 * np.pi * self.beta)
                    foot_x = hip_x + self.stride/2 - self.stride * stance_progress
                    foot_positions[i] = [foot_x, hip_y]
                else:
                    # 遊脚: 半楕円軌道
                    swing_progress = (phase_mod - 2*np.pi*self.beta) / (2*np.pi*(1-self.beta))
                    foot_x = hip_x - self.stride/2 + self.stride * swing_progress
                    foot_positions[i] = [foot_x, hip_y]

            foot_pos_hist[k] = foot_positions
            stance_hist[k] = stance

            # ZMP計算(簡略版)
            # ボディの加速度を推定(速度の数値微分)
            body_ax = 0.0  # 等速歩行を仮定
            body_ay = 0.0
            z_G = self.leg_length

            zmp_x = body_x - (body_ax / self.g) * z_G
            zmp_y = 0.0 - (body_ay / self.g) * z_G
            zmp_hist[k] = [zmp_x, zmp_y]

            # 安定余裕の計算
            stance_feet = foot_positions[stance]
            if len(stance_feet) >= 3:
                try:
                    hull = ConvexHull(stance_feet)
                    polygon = stance_feet[hull.vertices]
                    margin = self._stability_margin(np.array([zmp_x, zmp_y]), polygon)
                except:
                    margin = 0.0
            elif len(stance_feet) == 2:
                # 2脚支持: ZMPから線分への距離
                margin = self._point_to_segment_dist(
                    np.array([zmp_x, zmp_y]), stance_feet[0], stance_feet[1])
                margin = -margin  # 線分上にはZMPは乗らないので負
            else:
                margin = -1.0
            margin_hist[k] = margin

            # ボディ位置の更新
            body_x += body_vx * dt

        return {
            't': t_hist, 'phi': phi_hist, 'body_x': body_x_hist,
            'foot_pos': foot_pos_hist, 'zmp': zmp_hist,
            'margin': margin_hist, 'stance': stance_hist,
        }

    def _stability_margin(self, zmp, polygon):
        """ZMPから支持多角形の最近辺までの距離"""
        n = len(polygon)
        min_dist = float('inf')
        for i in range(n):
            p1 = polygon[i]
            p2 = polygon[(i + 1) % n]
            dist = self._point_to_segment_dist(zmp, p1, p2)
            min_dist = min(min_dist, dist)
        # 内部判定
        inside = True
        for i in range(n):
            edge = polygon[(i+1)%n] - polygon[i]
            to_p = zmp - polygon[i]
            cross = edge[0]*to_p[1] - edge[1]*to_p[0]
            if cross < 0:
                inside = False
                break
        return min_dist if inside else -min_dist

    def _point_to_segment_dist(self, p, a, b):
        """点pから線分abへの距離"""
        ab = b - a
        ap = p - a
        t = np.dot(ap, ab) / (np.dot(ab, ab) + 1e-10)
        t = np.clip(t, 0, 1)
        closest = a + t * ab
        return np.linalg.norm(p - closest)

次に、このクラスを使って3つの重力環境での歩行を比較します。

import numpy as np
import matplotlib.pyplot as plt

# 3つの重力環境でシミュレーション
environments = {
    'Earth (g=9.81)': 9.81,
    'Mars (g=3.72)': 3.72,
    'Moon (g=1.62)': 1.62,
}
colors = {'Earth (g=9.81)': 'deepskyblue', 'Mars (g=3.72)': 'orangered', 'Moon (g=1.62)': 'silver'}

results = {}
for name, g in environments.items():
    walker = QuadrupedCPGWalker(g=g, stride=0.15, swing_height=0.06)
    results[name] = walker.simulate(T=5.0, dt=0.002, walk_speed=0.3)

fig, axes = plt.subplots(3, 1, figsize=(14, 12))

# (1) 安定余裕の時間変化
ax = axes[0]
for name, res in results.items():
    # 移動平均でスムージング
    window = 50
    margin_smooth = np.convolve(res['margin'], np.ones(window)/window, mode='valid')
    t_smooth = res['t'][:len(margin_smooth)]
    ax.plot(t_smooth, margin_smooth * 100, color=colors[name], linewidth=1.5,
            label=name, alpha=0.8)
ax.axhline(y=0, color='red', linestyle='--', alpha=0.5, label='Stability boundary')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Stability margin [cm]')
ax.set_title('Stability Margin Comparison (Trot Gait, v=0.3 m/s)')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_xlim(1.5, 5.0)

# (2) 歩容タイムチャート(地球)
ax = axes[1]
res_earth = results['Earth (g=9.81)']
leg_names = ['LF', 'RF', 'LH', 'RH']
for i, name in enumerate(leg_names):
    stance = res_earth['stance'][:, i]
    ax.fill_between(res_earth['t'], i - 0.35, i + 0.35,
                    where=stance, color='cyan', alpha=0.7)
    ax.fill_between(res_earth['t'], i - 0.35, i + 0.35,
                    where=~stance, color='gray', alpha=0.2)
ax.set_yticks(range(4))
ax.set_yticklabels(leg_names)
ax.set_xlabel('Time [s]')
ax.set_title('Gait Timing Chart (Earth, Trot)')
ax.set_xlim(1.5, 4.0)
ax.grid(True, axis='x', alpha=0.3)

# (3) ZMP軌跡の比較(上面図)
ax = axes[2]
for name, res in results.items():
    # 定常状態のZMP
    start_idx = int(1.5 / 0.002)
    zmp = res['zmp'][start_idx:]
    body_x = res['body_x'][start_idx:]
    # ZMPのボディ座標系での位置
    zmp_body_x = zmp[:, 0] - body_x
    ax.scatter(zmp_body_x[::10], zmp[::10, 1] * 100, s=3, alpha=0.5,
              color=colors[name], label=name)

# 股関節位置を表示
hip_x = [0.3, 0.3, -0.3, -0.3]
hip_y = [20, -20, 20, -20]
ax.scatter(hip_x, hip_y, s=100, c='white', edgecolors='cyan', zorder=5, marker='s')
for hx, hy, ln in zip(hip_x, hip_y, leg_names):
    ax.annotate(ln, (hx, hy), textcoords="offset points", xytext=(10, 5), fontsize=9)

ax.set_xlabel('x (body frame) [m]')
ax.set_ylabel('y [cm]')
ax.set_title('ZMP Position in Body Frame')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_aspect('equal')

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

# 統計量の出力
print("=== 安定余裕の統計(定常状態, t > 1.5s) ===")
for name, res in results.items():
    start_idx = int(1.5 / 0.002)
    margin = res['margin'][start_idx:]
    print(f"{name}: mean={np.mean(margin)*100:.1f} cm, "
          f"min={np.min(margin)*100:.1f} cm, "
          f"std={np.std(margin)*100:.1f} cm")

このシミュレーション結果から、重力環境が歩行安定性に与える影響が明確に読み取れます。上段のグラフでは、地球(青線)の安定余裕が最も大きく安定した値を維持しているのに対し、月面(銀線)では安定余裕が小さく、ゼロに近づく瞬間が頻繁に現れています。火星(赤線)はその中間です。中段のタイムチャートからは、CPGによるトロット歩容がきれいに同期していることが確認できます — LF-RH と RF-LH のペアが交互に遊脚・立脚を繰り返しています。下段の ZMP 軌跡では、月面でのZMPの分散が大きく、支持多角形の辺付近まで ZMP が揺動する様子が可視化されています。このシミュレーションは「月面での歩行制御には地球よりもはるかに高い精度が要求される」ことを定量的に示しています。

フルード数と歩容遷移のシミュレーション

最後に、各重力環境での歩容遷移速度を計算し、実用的な歩行速度域を可視化しましょう。

import numpy as np
import matplotlib.pyplot as plt

# パラメータ
leg_lengths = np.linspace(0.2, 1.0, 50)  # 脚長 [m]
g_earth = 9.81
g_mars = 3.72
g_moon = 1.62

# 各重力環境での歩容遷移速度
v_walk_max_earth = np.sqrt(0.5 * g_earth * leg_lengths)
v_walk_max_mars = np.sqrt(0.5 * g_mars * leg_lengths)
v_walk_max_moon = np.sqrt(0.5 * g_moon * leg_lengths)

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

# (1) 脚長 vs 最大歩行速度
ax = axes[0]
ax.plot(leg_lengths, v_walk_max_earth, 'deepskyblue', linewidth=2, label='Earth')
ax.plot(leg_lengths, v_walk_max_mars, 'orangered', linewidth=2, label='Mars')
ax.plot(leg_lengths, v_walk_max_moon, 'silver', linewidth=2, label='Moon')
ax.fill_between(leg_lengths, 0, v_walk_max_moon, alpha=0.1, color='silver')
ax.fill_between(leg_lengths, v_walk_max_moon, v_walk_max_mars, alpha=0.1, color='orangered')
ax.fill_between(leg_lengths, v_walk_max_mars, v_walk_max_earth, alpha=0.1, color='deepskyblue')

# 代表的ロボットの脚長を注釈
robots = {'SpaceBok': 0.35, 'ANYmal': 0.5, 'ATHLETE': 0.8}
for rname, rl in robots.items():
    ax.axvline(x=rl, color='white', linestyle=':', alpha=0.5)
    ax.annotate(rname, (rl, ax.get_ylim()[1]*0.9 if ax.get_ylim()[1] > 0 else 2.0),
                fontsize=9, ha='center', color='white',
                bbox=dict(boxstyle='round,pad=0.3', facecolor='gray', alpha=0.7))

ax.set_xlabel('Leg length [m]')
ax.set_ylabel('Max walking speed (Fr=0.5) [m/s]')
ax.set_title('Walk-to-Run Transition Speed vs Leg Length')
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)

# (2) 各ロボット×各天体の実用歩行速度域
ax = axes[1]
robot_data = {
    'SpaceBok\n(l=0.35m)': 0.35,
    'ANYmal\n(l=0.50m)': 0.50,
    'ATHLETE\n(l=0.80m)': 0.80,
}
envs = [('Earth', g_earth, 'deepskyblue'),
        ('Mars', g_mars, 'orangered'),
        ('Moon', g_moon, 'silver')]

x_pos = np.arange(len(robot_data))
width = 0.25

for idx, (env_name, g_val, color) in enumerate(envs):
    v_maxs = [np.sqrt(0.5 * g_val * ll) for ll in robot_data.values()]
    bars = ax.bar(x_pos + idx*width, v_maxs, width, color=color,
                  edgecolor='white', linewidth=1, label=env_name)
    for bar, v in zip(bars, v_maxs):
        ax.text(bar.get_x() + bar.get_width()/2., bar.get_height() + 0.02,
                f'{v:.2f}', ha='center', fontsize=8)

ax.set_xticks(x_pos + width)
ax.set_xticklabels(robot_data.keys(), fontsize=9)
ax.set_ylabel('Max walking speed [m/s]')
ax.set_title('Walking Speed Envelope by Robot × Environment')
ax.legend()
ax.grid(True, axis='y', alpha=0.3)

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

# 数値サマリ
print("=== 歩容遷移速度 (Fr=0.5) ===")
for rname, rl in robots.items():
    print(f"\n{rname} (leg={rl}m):")
    for env_name, g_val, _ in envs:
        v = np.sqrt(0.5 * g_val * rl)
        print(f"  {env_name}: {v:.2f} m/s ({v*3.6:.1f} km/h)")

左のグラフからは、脚の長さと重力加速度の両方が歩容遷移速度に影響することが明確に読み取れます。塗りつぶされた領域は各天体でウォーク歩容が使える速度域を示しており、月面(銀色の領域)では歩行可能な速度域が非常に狭いことがわかります。右の棒グラフは、具体的なロボット(SpaceBok・ANYmal・ATHLETE)と天体の組み合わせごとの最大歩行速度を示しています。ATHLETEは脚が長い(0.8 m)ため月面でも比較的高い歩行速度を確保できますが、SpaceBokは脚が短い(0.35 m)ため月面では $0.53 \, \text{m/s}$(時速約 1.9 km)が歩行の限界です。SpaceBokが跳躍歩容を採用する設計判断は、この物理的制約からも合理的であることがわかります。

今後の技術的展望

多脚ロボットの宇宙応用は、まだ研究段階にあり、実際に他天体の表面を歩いた脚型ロボットはまだ存在しません。しかし、技術的な収束点はいくつか見えてきています。

強化学習の深化: ANYmalで実証されたSim-to-Real Transfer(シミュレーションで学習した歩行ポリシーを実機に転移する手法)は、低重力環境のシミュレータで学習した歩行を実機に適用できる可能性を開きます。月面の重力環境は地球上で完全に再現することが困難ですが、シミュレーション上では任意の重力を設定できます。

車輪-脚ハイブリッド: ATHLETEのコンセプトは、平坦地での効率性と不整地での走破性を両立させる現実的なアプローチです。今後は、センサ情報に基づいて車輪モードと脚モードを自律的に切り替えるインテリジェントな制御が重要になるでしょう。

群ロボット探査: 1台の大型ロボットではなく、複数の小型脚型ロボットが協調して探査する群ロボット戦略も有望です。1台が故障しても他のロボットがミッションを継続でき、複数地点を同時に調査できるという冗長性と効率性の利点があります。

テラメカニクスとの統合: 月面レゴリスや火星のレゴリスに足が沈み込む現象を正確にモデル化するテラメカニクス(地盤力学)との統合が、実用的な歩行制御には不可欠です。足先の形状設計やスタンス戦略の最適化には、地盤との相互作用の正確な理解が必要です。

まとめ

本記事では、多脚ロボットの歩行制御の基礎から宇宙応用までを体系的に解説しました。

  • 脚型ロボットは離散的な接地点の選択ができるため、車輪型では到達不能な不整地・急傾斜地・岩場での移動に優れています
  • ZMP(ゼロモーメントポイント)は、ロボットの転倒安定性を評価する標準的な指標であり、支持多角形の内部にZMPが存在することが安定歩行の条件です
  • CPG(中枢パターン生成器)は、生物の脊髄神経回路を数理モデル化した歩容生成手法であり、位相差パラメータの設定だけでウォーク・トロット・ギャロップなどの歩容を統一的に生成できます
  • 低重力環境では、フルード数の増大により歩行可能な速度域が縮小し、ZMPのずれが増大し、牽引力が低下するなど、歩行制御の難易度が大幅に上がります
  • SpaceBok・ANYmal・ATHLETE・RoboSimianといった実在のロボットが、それぞれ異なるアプローチで宇宙探査への適用を目指しています
  • 月面溶岩洞探査は、脚型ロボットの優位性が最も活きる応用先であり、縦穴降下・崩壊地形走破・暗闇での自律行動が求められます

歩行制御は、古典力学(ニュートンの運動法則、ZMP)、生物学(CPG)、現代の機械学習(強化学習によるSim-to-Real Transfer)が融合するダイナミックな研究分野です。惑星探査ロボットが初めて他天体で「歩く」日は、そう遠くないかもしれません。

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