テレオペレーションと通信遅延補償 — 地上から宇宙ロボットを操る

国際宇宙ステーション(ISS)の外壁に亀裂が見つかったとしましょう。船外活動(EVA)は宇宙飛行士にとって危険が伴うため、地上からロボットアームを遠隔操縦して修理できれば理想的です。ところが、オペレータがジョイスティックを倒してから、宇宙でロボットが実際に動くまでには数百ミリ秒の遅れが生じます。月面なら約1.3秒、火星ともなると最大24分もの遅延です。この「通信遅延」は、まるで氷の上でブレーキを踏むようなもので、操作と結果のずれが蓄積し、ロボットが暴走したり振動したりする危険をはらんでいます。

こうした通信遅延のもとで、ロボットを安全かつ正確に操るための技術が テレオペレーション(遠隔操縦)の遅延補償 です。この技術を理解すると、以下のような幅広い応用が見えてきます。

  • 宇宙ステーションの保守・修理: 地上から、あるいは船内からロボットアームを使って船外の機器を操作する
  • 月面・火星探査: 探査ローバーや掘削ロボットを地球から遠隔操縦する
  • 深海探査・遠隔手術: 通信遅延が問題となるのは宇宙だけではなく、海底ケーブル経由の深海ロボットや、遠隔地からの外科手術ロボットにも同じ理論が適用される
  • 災害現場の遠隔ロボット操作: 原子力施設の事故対応など、人が立ち入れない環境でのロボット活用

本記事の内容

  • テレオペレーションの基本概念と、なぜ完全自律が難しい場面があるのか
  • 通信遅延が制御ループに与える影響の定量的理解
  • マスタースレーブ型バイラテラル制御の数学的定式化
  • 遅延下でシステムが不安定になるメカニズム
  • パッシビティベースの遅延補償(Scattering Transform / Wave Variable)
  • 予測表示(Predictive Display)の原理
  • 共有自律(Shared Autonomy)の考え方
  • Pythonによる遅延ありバイラテラル制御シミュレーション

前提知識

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

  • 制御工学の基礎(フィードバック制御、伝達関数の概念)
  • 力学の基本(ニュートンの運動方程式)
  • 線形システムの安定性(極の概念)

テレオペレーションとは何か

人間がロボットを「手の延長」として使う

テレオペレーションとは、離れた場所にあるロボット(スレーブ)を、手元のデバイス(マスター)を介して人間が操作する技術です。日常的な例で言えば、ゲームのコントローラーでキャラクターを動かす感覚に近いでしょう。ただし、ゲームとの決定的な違いは、力の情報が双方向にやり取りされる という点です。

たとえば、宇宙ロボットが衛星のボルトを締めている場面を考えてみましょう。オペレータが手元のマスターデバイスを操作すると、その位置・力の情報がスレーブロボットに送られ、ロボットがボルトを回します。同時に、ボルトがきつくなってきたときの 反力(手応え) がスレーブからマスターに返され、オペレータの手に伝わります。このように位置と力の情報が行き来する制御を バイラテラル制御(bilateral control)と呼びます。

なぜ完全自律ではダメなのか

「通信遅延が問題なら、ロボットを完全に自律化すればいいのでは?」という疑問は自然です。しかし、現実には完全自律が難しい場面が数多くあります。

非構造化環境での判断: 宇宙空間での修理作業では、断熱材の剥がれ方や配線の絡まり具合など、事前に想定できない状況が次々と発生します。こうした場面では、人間の柔軟な判断力が不可欠です。

責任の所在: 数十億円の衛星に対する作業では、最終的な判断を人間が行う必要があります。完全自律のロボットが誤動作した場合のリスクは甚大です。

技術的限界: 現在のAIは、未知の物体の把持や複雑な組み立て作業において、人間の器用さには遠く及びません。特に、触覚フィードバックを使った繊細な力加減の調整は、人間の直感的な能力に大きく依存しています。

そのため、実際の宇宙ミッションでは、人間の判断力とロボットの精密さを組み合わせた テレオペレーション が現実的な選択肢となります。しかし、テレオペレーションには避けて通れない課題があります。それが 通信遅延 の問題です。

通信遅延の問題

宇宙における遅延の大きさ

電磁波は光速(約 $3 \times 10^8$ m/s)で伝搬しますが、宇宙の距離スケールでは無視できない遅延が生じます。片道の通信遅延 $T_d$ は、距離 $d$ に対して次のように計算されます。

$$ T_d = \frac{d}{c} $$

ここで $c$ は光速です。代表的な通信遅延を整理すると、次のようになります。

通信経路 片道距離 片道遅延 往復遅延(RTT)
地上 → LEO衛星 400 km 1.3 ms 2.7 ms
地上 → 静止軌道(GEO)経由 約 72,000 km(往復) — 約 500 ms
地球 → 月 384,400 km 1.28 s 2.56 s
地球 → 火星(最近時) 5,500万 km 3.0 min 6.1 min
地球 → 火星(最遠時) 4億 km 22.2 min 44.4 min

LEO(低軌道)のISS操作であっても、通信経路が地上局 → GEO中継衛星 → ISS と経由する場合、往復遅延は500 ms以上になることがあります。これは人間が「遅い」と感じ始める閾値(約200〜300 ms)を超えています。

遅延がオペレータに与える影響

人間がリアルタイムで制御できる遅延の限界は、タスクの種類によって異なります。

  • 追従操作(トラッキング): 往復遅延が約300 msを超えると、操作精度が急激に低下する
  • 接触作業(挿入、締結など): 往復遅延が100 msを超えると、力の過大適用や接触不安定が生じやすくなる
  • 自由空間移動: 比較的遅延に強いが、1秒を超えると「動かして止めて確認」というムーブ・アンド・ウェイト戦略を強いられる

火星探査のような数分〜数十分の遅延下では、リアルタイム操縦はもはや不可能であり、根本的に異なるアプローチが必要になります。

では、テレオペレーションシステムの基本的な制御構造はどのようになっているのでしょうか。まず遅延がない理想的な場合の マスタースレーブ制御 の枠組みを理解し、そこに遅延を導入したときに何が起こるかを見ていきましょう。

マスタースレーブ制御の基本

1自由度のマスタースレーブモデル

最もシンプルな設定として、マスターとスレーブがそれぞれ1自由度の質量-ダンパ系であるモデルを考えます。日常的なイメージで言えば、マスター側は「ジョイスティック」、スレーブ側は「ロボットアーム」を、それぞれバネとダンパで支えられた質点で単純化したものです。

マスター側の運動方程式は次のようになります。

$$ M_m \ddot{x}_m + B_m \dot{x}_m = f_h + f_{cm} $$

ここで、$M_m$ はマスターの質量、$B_m$ はダンパ係数、$x_m$ はマスターの位置、$f_h$ は人間(オペレータ)が加える力、$f_{cm}$ はコントローラがマスターに加える制御力です。

同様に、スレーブ側の運動方程式は次のとおりです。

$$ M_s \ddot{x}_s + B_s \dot{x}_s = f_{cs} + f_e $$

ここで、$M_s$ はスレーブの質量、$B_s$ はダンパ係数、$x_s$ はスレーブの位置、$f_{cs}$ はコントローラがスレーブに加える制御力、$f_e$ は環境からスレーブに加わる力(接触力など)です。

位置-力型バイラテラル制御

バイラテラル制御にはさまざまなアーキテクチャがありますが、ここでは直感的に理解しやすい 位置-力型(PF型) を取り上げます。

PF型バイラテラル制御の基本的な考え方は次のとおりです。

  • スレーブ側: マスターの位置 $x_m$ を追従する(位置制御)
  • マスター側: スレーブが環境から受ける力 $f_e$ を再現する(力制御)

これを数式で書くと、スレーブ側の制御力は位置誤差に基づく PD 制御で構成されます。

$$ f_{cs} = K_p (x_m – x_s) + K_d (\dot{x}_m – \dot{x}_s) $$

ここで $K_p$ は比例ゲイン、$K_d$ は微分ゲインです。スレーブはマスター位置に追従しようとします。

マスター側の制御力は、スレーブが環境から受けた力のスケーリングです。

$$ f_{cm} = -\alpha f_e $$

$\alpha$ は力のスケーリング係数で、通常は $\alpha = 1$(等倍フィードバック)とします。符号が負なのは、環境がスレーブを押し返す方向の力を、マスター側でオペレータに「手応え」として伝えるためです。

理想的なテレオペレーション — 透明性

理想的なバイラテラル制御では、オペレータがまるで自分の手で直接作業しているかのように感じられることを目指します。これを 透明性(transparency) と呼びます。数学的には、マスター側から見たインピーダンスが環境のインピーダンス $Z_e$ と一致する状態です。

$$ Z_{\text{transmitted}} = Z_e $$

透明性が完全に実現されると、マスターの位置はスレーブの位置と一致し($x_m = x_s$)、オペレータが感じる力は環境の反力と一致します($f_h = -f_e$)。これはいわば、オペレータの手がそのまま宇宙空間に「伸びている」ような感覚です。

しかし、この理想は通信遅延がない場合にのみ近似的に達成可能です。遅延が入ると、状況は一変します。

ここまでで、バイラテラル制御の基本的な枠組みを理解しました。次に、通信路に遅延が入ったとき、このフィードバックループがなぜ不安定になるのかを数学的に分析していきます。

遅延下での安定性問題

遅延をモデルに組み込む

通信遅延がある場合、マスターからスレーブに送られる信号は、時間 $T$ だけ遅れて届きます。逆に、スレーブからマスターへの信号も同様に遅延します。これを数式に反映すると、スレーブ側の制御則は次のように変わります。

$$ f_{cs}(t) = K_p \bigl(x_m(t – T) – x_s(t)\bigr) + K_d \bigl(\dot{x}_m(t – T) – \dot{x}_s(t)\bigr) $$

$x_m(t – T)$ は「$T$ 秒前のマスター位置」です。スレーブはマスターの 過去の 位置を追いかけることになります。

同様に、マスター側の制御力も遅延します。

$$ f_{cm}(t) = -\alpha f_e(t – T) $$

オペレータが感じる力は、$T$ 秒前にスレーブが環境から受けた力です。

なぜ遅延でフィードバックが不安定になるのか

遅延が不安定性を引き起こすメカニズムを、直感的に理解しましょう。

シャワーの温度調節を思い浮かべてください。蛇口をひねってからお湯が出るまでに時間差がある古いシャワーでは、次のようなことが起きます。

  1. 水が冷たいので、お湯側に大きくひねる
  2. しばらく待っても冷たいままなので、さらにひねる
  3. 突然、熱湯が出てくる
  4. 慌てて水側にひねる
  5. しかしまだ熱いので、さらに水側にひねる
  6. 今度は急に冷水になる

これが 遅延による不安定振動 です。フィードバック信号が現在の状態ではなく過去の状態に基づいているため、修正が過大になり、振動が増幅していきます。

安定性の数学的分析

遅延系の安定性をより厳密に分析するために、ラプラス領域で考えましょう。遅延要素のラプラス変換は $e^{-sT}$ で表されます。

スレーブ側の閉ループ伝達関数を簡略化のために1次系で考えます。スレーブの運動方程式をラプラス変換し、制御則を代入すると次のようになります。

$$ (M_s s^2 + B_s s) X_s(s) = (K_p + K_d s) \bigl(e^{-sT} X_m(s) – X_s(s)\bigr) + F_e(s) $$

$X_m(s)$ を入力、$X_s(s)$ を出力として整理すると、特性方程式は次のようになります。

$$ M_s s^2 + (B_s + K_d) s + K_p + (K_p + K_d s) e^{-sT} \cdot (\text{terms}) = 0 $$

遅延項 $e^{-sT}$ のせいで、特性方程式は 超越方程式 となり、有限個ではなく無限個の根を持ちます。遅延がゼロ($T = 0$)のとき安定であっても、$T$ が増加するとこれらの根の一部が虚軸を超えて右半平面に移動し、システムが不安定になります。

ここで重要なのは、単に制御ゲインを下げれば安定化できるという単純な話ではないということです。ゲインを下げれば確かに安定性は改善しますが、同時に追従性能(透明性)が悪化します。遅延が大きい場合、安定性と性能のトレードオフは非常に厳しくなり、実用的な制御が困難になります。

エネルギーの観点からの理解

もう一つの重要な観点は、エネルギーの保存です。遅延のないシステムでは、マスターからスレーブに送られるエネルギーと、スレーブからマスターに返されるエネルギーが各瞬間でバランスしています。

しかし遅延があると、送信されたエネルギーが受信側に到着するまでのタイムラグの間に、エネルギーの帳尻が合わなくなる 瞬間が生じます。具体的には、通信路がエネルギーを「生成」してしまう場合があるのです。物理的にはあり得ないこのエネルギー生成が、システムの振動を増幅させる原因となります。

この「エネルギーが保存されているか」という視点が、次に説明する パッシビティ の概念につながります。パッシビティベースのアプローチは、通信路がエネルギーを生成しないことを保証することで、任意の遅延に対して安定性を保証する画期的な方法です。

パッシビティベースの遅延補償

パッシビティとは何か

パッシビティ(受動性)は、電気回路の抵抗を思い浮かべると理解しやすい概念です。抵抗は電気エネルギーを消費することはあっても、エネルギーを生み出すことはありません。このように、外部から入力されたエネルギー以上のエネルギーを出力しないシステム をパッシブ(受動的)なシステムと言います。

数学的には、入力 $u(t)$ と出力 $y(t)$ を持つシステムが パッシブ であるとは、ある非負の関数 $V(t) \geq 0$(蓄積エネルギー)が存在して、次の不等式が任意の $t \geq 0$ で成り立つことです。

$$ V(t) – V(0) \leq \int_0^t u(\tau) y(\tau) \, d\tau $$

左辺は蓄積エネルギーの変化、右辺はシステムに入力されたエネルギーの累積です。この不等式は「蓄積エネルギーの増加は、入力されたエネルギーを超えない」ことを意味しています。つまり、パッシブなシステムはエネルギーを生成しません。

パッシビティの強力な性質は、パッシブなサブシステム同士を接続した系もパッシブである ということです。したがって、通信路をパッシブに保てれば、パッシブなマスター(人間 + マスターデバイス)とパッシブなスレーブ(ロボット + 環境)をつないだ全体系もパッシブ、すなわち安定であることが保証されます。

問題: 遅延のある通信路はパッシブか?

ここで核心的な問題に到達します。遅延のある通信路はパッシブでしょうか?

マスター側から通信路に入力されるパワー(単位時間あたりのエネルギー)は $P_{\text{in}}(t) = f_m(t) \dot{x}_m(t)$ です。スレーブ側で通信路から出力されるパワーは $P_{\text{out}}(t) = f_m(t – T) \dot{x}_m(t – T)$ です。

遅延だけの通信路では、入力と出力が時間的にずれるため、ある時刻 $t$ において $P_{\text{out}}(t) > P_{\text{in}}(t)$ となることがあり得ます。つまり、純粋な遅延通信路はパッシブであることが保証されません。

Anderson と Spong は1989年の画期的な論文で、この問題に対する解決策を提示しました。それが Scattering Transform(散乱変換)です。

Scattering Transform(散乱変換)

Scattering Transform のアイデアは、電気回路やマイクロ波工学の 伝送線路理論 から借りてきたものです。伝送線路では、電圧と電流をそのまま送る代わりに、進行波(forward wave)と 反射波(backward wave)に分解して送ります。こうすることで、線路の特性インピーダンスに依存せず、エネルギーの散逸が保証されます。

具体的に定式化しましょう。マスター側で、位置に関連する速度 $\dot{x}_m$ と力 $f_m$ から、散乱変数(wave variable)$u_m$ と $v_m$ を次のように定義します。

$$ u_m = \frac{1}{\sqrt{2b}} (f_m + b \dot{x}_m) $$

$$ v_m = \frac{1}{\sqrt{2b}} (f_m – b \dot{x}_m) $$

ここで $b > 0$ は 特性インピーダンス と呼ばれるパラメータで、設計者が自由に選べます。

同様にスレーブ側でも散乱変数を定義します。

$$ u_s = \frac{1}{\sqrt{2b}} (f_s + b \dot{x}_s) $$

$$ v_s = \frac{1}{\sqrt{2b}} (f_s – b \dot{x}_s) $$

通信路の構成

通信路では、以下のように散乱変数をやり取りします。

  • マスター → スレーブ方向: $u_m(t)$ を送信し、スレーブ側で $u_s(t) = u_m(t – T)$ として受信
  • スレーブ → マスター方向: $v_s(t)$ を送信し、マスター側で $v_m(t) = v_s(t – T)$ として受信

つまり、力と速度を直接送る代わりに、それらを散乱変数に変換してから送り、受信側で逆変換するわけです。

パッシビティの証明

この構成がなぜパッシブになるのかを確認しましょう。マスター側で通信路に入力されるパワーは、散乱変数を使うと次のように表現できます。

$u_m$ と $v_m$ の定義式から力と速度を逆に解くと、次のようになります。

$$ f_m = \frac{\sqrt{2b}}{2}(u_m + v_m), \quad \dot{x}_m = \frac{1}{\sqrt{2b}} \cdot \frac{u_m – v_m}{1} $$

通信路に入力されるパワーを計算すると、次の関係が得られます。

$$ P_{\text{in}}(t) = f_m(t) \dot{x}_m(t) = \frac{1}{2}\bigl(u_m^2(t) – v_m^2(t)\bigr) $$

同様に、スレーブ側で通信路から出力されるパワーは次のようになります。

$$ P_{\text{out}}(t) = f_s(t) \dot{x}_s(t) = \frac{1}{2}\bigl(u_s^2(t) – v_s^2(t)\bigr) $$

ここで通信路の関係 $u_s(t) = u_m(t-T)$、$v_m(t) = v_s(t-T)$ を使って、通信路に蓄積されるエネルギーを計算します。

通信路が時刻 $0$ から $t$ までに蓄積するエネルギー $E_{\text{line}}(t)$ は、入力されたエネルギーと出力されたエネルギーの差です。

$$ E_{\text{line}}(t) = \int_0^t P_{\text{in}}(\tau) \, d\tau – \int_0^t P_{\text{out}}(\tau) \, d\tau $$

$u_m$ 成分について考えると、入力側では $\frac{1}{2}\int_0^t u_m^2(\tau) d\tau$ のエネルギーが入り、出力側では $\frac{1}{2}\int_0^t u_m^2(\tau – T) d\tau = \frac{1}{2}\int_{-T}^{t-T} u_m^2(\tau) d\tau$ のエネルギーが出ます。初期条件が零ならば $u_m(\tau) = 0$ for $\tau < 0$ なので、差分は次のようになります。

$$ \frac{1}{2}\int_{t-T}^{t} u_m^2(\tau) \, d\tau \geq 0 $$

$v_s$ 成分についても同様に非負となります。したがって、$E_{\text{line}}(t) \geq 0$ が常に成り立ち、通信路はエネルギーを生成しない、すなわちパッシブです。

これが Scattering Transform の核心です: 力と速度を散乱変数に変換してから通信することで、任意の一定遅延 $T > 0$ に対して通信路のパッシビティが保証され、システム全体の安定性が確保されます。

実用上の課題

Scattering Transform は理論的にはエレガントですが、実用上いくつかの課題があります。

定常偏差の問題: Wave Variable による通信では、定常状態でマスターとスレーブの位置が完全に一致しないことがあります。特性インピーダンス $b$ を通じてエネルギーが散逸するため、定常状態でも位置のドリフトが生じます。

特性インピーダンスの選択: パラメータ $b$ の選択には、安定性と性能のトレードオフがあります。$b$ が大きいと安定性は増しますが、追従が鈍くなります。$b$ が小さいと追従性は良くなりますが、安定性のマージンが減少します。

時変遅延への対応: インターネットのようにパケット遅延が変動する環境では、基本的な Scattering Transform だけではパッシビティが保証されません。Niemeyer と Slotine(2004)が提案した改良版では、受信側でパケットの到着タイミングを監視し、エネルギーの帳尻を合わせる補正項を追加しています。

これらの課題はあるものの、パッシビティベースのアプローチは遅延補償の理論的基盤として極めて重要です。次に、パッシビティとは異なるアプローチで遅延の問題に対処する 予測表示 の手法を見ていきましょう。

予測表示(Predictive Display)

オペレータに「未来」を見せる

パッシビティベースの手法が通信路の安定性を制御理論の枠組みで保証するのに対し、予測表示(Predictive Display) はオペレータ側のインタフェースを改善することで遅延の影響を軽減するアプローチです。

基本的なアイデアは非常にシンプルです。オペレータの画面に映すのは、$T$ 秒前のスレーブの実際の映像ではなく、スレーブの 現在の状態を予測したCGモデル です。つまり、オペレータが操作した結果がリアルタイムで予測CGに反映されるため、遅延を感じずに操作できます。

これは、カーナビの自車位置表示を思い浮かべるとわかりやすいでしょう。GPS信号には遅延がありますが、ナビは車の速度と方向から現在位置を予測して表示するため、トンネルに入っても位置表示がしばらく追従します。予測表示も同じ原理です。

予測表示の数学的構造

予測表示システムは、スレーブの環境モデルを使って次のように動作します。

ステップ1: オペレータがマスターを操作し、コマンド $x_m(t)$ を生成する

ステップ2: スレーブの環境モデル $\hat{G}_s$ を用いて、コマンドに対するスレーブの応答を予測する

$$ \hat{x}_s(t) = \hat{G}_s\bigl(x_m(t)\bigr) $$

ステップ3: 予測位置 $\hat{x}_s(t)$ を用いたCGをオペレータの画面にリアルタイムで表示する

ステップ4: 一方で、スレーブから $T$ 秒遅れて返ってくる実際の状態 $x_s(t-T)$ を使って、モデル予測を補正する

$$ \hat{x}_s(t) \leftarrow \hat{x}_s(t) + K_{\text{corr}} \bigl(x_s(t – T) – \hat{x}_s(t – T)\bigr) $$

$K_{\text{corr}}$ は補正ゲインです。$T$ 秒前の予測値と実測値の差をフィードバックすることで、モデルの誤差を徐々に修正していきます。

Smith 予測器との関連

制御理論に詳しい方は、予測表示が Smith 予測器 と似た構造であることに気づくかもしれません。Smith 予測器は、プロセスのモデルを使って遅延の影響をコントローラのループから取り除く手法です。

Smith 予測器では、プラント(制御対象)のモデル $\hat{G}(s)$ と遅延モデル $e^{-sT}$ を用いて、コントローラが見る誤差信号から遅延の影響を除去します。

$$ e(s) = R(s) – \hat{G}(s) X_m(s) – \bigl(Y(s) – \hat{G}(s) e^{-sT} X_m(s)\bigr) $$

ここで $R(s)$ は目標値、$Y(s)$ は遅延を含むプラント出力です。モデルが正確であれば($\hat{G}(s) = G(s)$)、コントローラは遅延のないプラントに対して設計されたものがそのまま使えます。

予測表示は、この考え方をオペレータのフィードバックループに適用したものと言えます。

予測表示の限界

予測表示には本質的な限界があります。

モデル精度への依存: 環境モデルが不正確だと、予測と現実のずれが大きくなります。未知の環境(デブリの形状、壊れた部品の状態など)ではモデルの構築自体が困難です。

接触予測の困難さ: ロボットが物体に接触する瞬間の予測は特に難しく、接触力のモデルには不確実性が大きくなります。

視覚と触覚の不一致: 予測CGの映像と、遅延して返ってくる実際の力覚フィードバックにズレが生じ、オペレータに違和感を与えることがあります。

これらの限界を克服するために、近年注目されているのが 共有自律 の概念です。人間がすべてを操作するのではなく、ロボット側にもある程度の自律性を持たせることで、遅延の影響を根本的に低減しようという考え方です。

共有自律(Shared Autonomy)

完全遠隔操縦と完全自律の間

テレオペレーションの歴史を振り返ると、大きく2つの極端なアプローチがありました。

  • 完全遠隔操縦: 人間がすべての動きを指示する。遅延の影響をもろに受ける
  • 完全自律: ロボットが自分で判断する。未知の環境への適応力に限界がある

共有自律(Shared Autonomy) は、この2つの間のスペクトラムに位置する手法です。人間は 高レベルの意図や目標 を指示し、ロボットが 低レベルの実行 を自律的に行います。

共有自律のレベル

共有自律の度合いは、通信遅延の大きさに応じて調整するのが合理的です。

レベル1: 直接制御 + 安全制約(遅延 < 500 ms)

人間がほぼリアルタイムで操作しますが、ロボット側に衝突回避などの安全制約が組み込まれています。オペレータが壁に向かってロボットを動かしても、ロボットが自動的に減速・停止します。

レベル2: 指令ベース操作(遅延 500 ms〜数秒)

人間が「あの物体を掴め」「10 cm 右に動け」といった高レベルの指令を出し、ロボットが軌道計画と実行を自律的に行います。力加減や衝突回避はロボット側で処理します。

レベル3: 監督制御(遅延 数秒〜数分)

人間は作業の全体計画を立て、ロボットに一連のタスクを指示します。ロボットは個々のタスクを自律的に遂行し、完了報告や異常検知の結果を人間に返します。月面探査がこのレベルに該当します。

レベル4: 高度自律 + 例外処理(遅延 数十分以上)

ロボットはほぼ完全に自律的に動作し、人間は例外的な状況(想定外の障害物、判断が必要な分岐点)でのみ介入します。火星探査ローバーの運用が近い形態です。

共有自律の数学的定式化

共有自律を定式化する一つの方法として、ブレンディング アプローチがあります。ロボットの制御入力 $u$ を、人間の指令 $u_h$ とロボットの自律制御 $u_a$ のブレンドとして表現します。

$$ u(t) = \alpha(t) u_h(t) + \bigl(1 – \alpha(t)\bigr) u_a(t) $$

ここで $\alpha(t) \in [0, 1]$ は 仲裁関数(arbitration function) であり、人間の制御の割合を決めます。$\alpha = 1$ は完全遠隔操縦、$\alpha = 0$ は完全自律に対応します。

仲裁関数 $\alpha(t)$ の設計は、以下の要因を考慮して動的に調整されます。

  • 通信遅延の大きさ: 遅延が大きいほど $\alpha$ を小さくし、自律度を上げる
  • タスクの複雑さ: 精密な操作が必要な場面では $\alpha$ を大きくし、人間の介入度を上げる
  • 環境の不確実性: モデルの信頼度が低い場合は $\alpha$ を大きくし、人間の判断に委ねる
  • 人間の操作品質: オペレータの疲労や操作エラーが検出された場合は $\alpha$ を小さくする

意図推定

共有自律を効果的に機能させるためには、人間の 意図推定(intent estimation) が重要です。オペレータがジョイスティックを右に倒したとき、それが「右の物体を掴みたい」のか「右方向に移動したい」のかを、ロボット側で推定する必要があります。

ベイズ推定を用いた意図推定では、可能な目標集合 $\{g_1, g_2, \dots, g_N\}$ に対して、現在までの操作履歴 $\xi_{0:t}$ が与えられたときの目標の事後確率を計算します。

$$ P(g_i \mid \xi_{0:t}) = \frac{P(\xi_{0:t} \mid g_i) P(g_i)}{\sum_{j=1}^N P(\xi_{0:t} \mid g_j) P(g_j)} $$

尤度 $P(\xi_{0:t} \mid g_i)$ は、目標 $g_i$ に向かう最適軌道からの乖離度で評価されることが多く、ボルツマン分布型のモデルが使われます。

$$ P(\xi_{0:t} \mid g_i) \propto \exp\bigl(-\beta \, C(\xi_{0:t}, g_i)\bigr) $$

$C(\xi_{0:t}, g_i)$ は操作軌跡と目標 $g_i$ に対する最適軌道とのコスト差、$\beta$ は人間の合理性パラメータです。$\beta \to \infty$ は完全に合理的なオペレータ、$\beta \to 0$ はランダムな操作に対応します。

共有自律は、遅延が大きい環境でのテレオペレーションを実用的なものにする鍵となる技術です。ここまでで、テレオペレーションの理論的基盤を一通り学びました。それでは、これらの概念をPythonシミュレーションで実際に確かめてみましょう。

Pythonによるシミュレーション

シミュレーション1: 遅延なしと遅延ありのバイラテラル制御

まず、遅延がない場合と遅延がある場合で、マスタースレーブ制御の振る舞いがどう変わるかを比較します。ここでは1自由度の質量-ダンパ系を用い、オペレータがステップ状の力を加える場面をシミュレーションします。

import numpy as np
import matplotlib.pyplot as plt

# --- パラメータ設定 ---
dt = 0.001          # タイムステップ [s]
T_end = 5.0         # シミュレーション時間 [s]
t = np.arange(0, T_end, dt)
N = len(t)

# マスター・スレーブの物理パラメータ
Mm, Bm = 1.0, 2.0   # マスター: 質量 [kg], ダンパ係数 [Ns/m]
Ms, Bs = 1.0, 2.0   # スレーブ: 質量 [kg], ダンパ係数 [Ns/m]

# PD制御ゲイン
Kp, Kd = 50.0, 20.0

# 人間の入力力(ステップ入力)
f_h = np.zeros(N)
f_h[int(0.5/dt):] = 5.0  # t=0.5s からステップ力

# 環境は自由空間(接触なし)
f_e = np.zeros(N)

def simulate_bilateral(delay_sec, label):
    """遅延ありバイラテラル制御のシミュレーション"""
    delay_steps = int(delay_sec / dt)

    # 状態変数の初期化
    xm, vm = np.zeros(N), np.zeros(N)  # マスター位置・速度
    xs, vs = np.zeros(N), np.zeros(N)  # スレーブ位置・速度

    for i in range(1, N):
        # 遅延を考慮したインデックス
        j_m = max(0, i - delay_steps)  # マスター情報の遅延到着
        j_s = max(0, i - delay_steps)  # スレーブ情報の遅延到着

        # スレーブ側制御力: 遅延したマスター位置を追従
        f_cs = Kp * (xm[j_m] - xs[i-1]) + Kd * (vm[j_m] - vs[i-1])

        # マスター側制御力: 遅延したスレーブの環境力を反映
        f_cm = -f_e[j_s]

        # マスターの運動方程式
        am = (f_h[i] + f_cm - Bm * vm[i-1]) / Mm
        vm[i] = vm[i-1] + am * dt
        xm[i] = xm[i-1] + vm[i] * dt

        # スレーブの運動方程式
        a_s = (f_cs + f_e[i] - Bs * vs[i-1]) / Ms
        vs[i] = vs[i-1] + a_s * dt
        xs[i] = xs[i-1] + vs[i] * dt

    return xm, xs, label

# 3つの遅延条件でシミュレーション
results = []
for delay, lbl in [(0.0, "遅延なし (0 ms)"),
                    (0.1, "遅延 100 ms (LEO経由)"),
                    (0.5, "遅延 500 ms (GEO経由)")]:
    xm, xs, label = simulate_bilateral(delay, lbl)
    results.append((xm, xs, label))

# --- 可視化 ---
fig, axes = plt.subplots(3, 1, figsize=(10, 10), sharex=True)

for idx, (xm, xs, label) in enumerate(results):
    axes[idx].plot(t, xm, 'b-', linewidth=1.5, label='Master $x_m$')
    axes[idx].plot(t, xs, 'r--', linewidth=1.5, label='Slave $x_s$')
    axes[idx].set_ylabel('Position [m]')
    axes[idx].set_title(label, fontsize=12)
    axes[idx].legend(loc='lower right')
    axes[idx].grid(True, alpha=0.3)

axes[-1].set_xlabel('Time [s]')
plt.tight_layout()
plt.savefig('bilateral_delay_comparison.png', dpi=150, bbox_inches='tight')
plt.show()

上のシミュレーション結果から、いくつかの重要な特徴が読み取れます。

  1. 遅延なし(0 ms)の場合: スレーブはマスターにほぼ即座に追従します。PDゲインが適切に設定されているため、位置の収束は滑らかで振動もありません。これが理想的な透明性です。

  2. 遅延100 ms(LEO経由)の場合: スレーブの応答に明確な遅れが見えますが、系は安定しています。ただし、追従の遅れはオペレータにとって違和感を生じさせ、特に精密作業では問題になります。

  3. 遅延500 ms(GEO経由)の場合: 遅延がさらに大きくなると、スレーブの応答の遅れが顕著になります。高いPDゲインを使うと振動が生じやすくなるため、ゲインの調整が必要になります。

シミュレーション2: 遅延による不安定性の実演

次に、制御ゲインを大きくしたときに遅延が不安定性を引き起こす様子を観察します。

import numpy as np
import matplotlib.pyplot as plt

dt = 0.001
T_end = 5.0
t = np.arange(0, T_end, dt)
N = len(t)

Mm, Bm = 1.0, 2.0
Ms, Bs = 1.0, 2.0

# 人間の入力(短いパルス)
f_h = np.zeros(N)
f_h[int(0.5/dt):int(0.7/dt)] = 5.0

delay_sec = 0.3  # 300 ms
delay_steps = int(delay_sec / dt)

def simulate_with_gain(Kp_val, Kd_val, label):
    """指定ゲインでシミュレーション"""
    xm, vm = np.zeros(N), np.zeros(N)
    xs, vs = np.zeros(N), np.zeros(N)

    for i in range(1, N):
        j = max(0, i - delay_steps)

        f_cs = Kp_val * (xm[j] - xs[i-1]) + Kd_val * (vm[j] - vs[i-1])
        f_cm = 0.0  # 自由空間

        am = (f_h[i] + f_cm - Bm * vm[i-1]) / Mm
        vm[i] = vm[i-1] + am * dt
        xm[i] = xm[i-1] + vm[i] * dt

        a_s = (f_cs - Bs * vs[i-1]) / Ms
        vs[i] = vs[i-1] + a_s * dt
        xs[i] = xs[i-1] + vs[i] * dt

    return xm, xs, label

# 異なるゲインでシミュレーション
gain_cases = [
    (30, 10, "低ゲイン ($K_p=30, K_d=10$) — 安定"),
    (100, 30, "中ゲイン ($K_p=100, K_d=30$) — 振動的"),
    (300, 50, "高ゲイン ($K_p=300, K_d=50$) — 不安定"),
]

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

for idx, (kp, kd, label) in enumerate(gain_cases):
    xm, xs, lbl = simulate_with_gain(kp, kd, label)
    axes[idx].plot(t, xm, 'b-', linewidth=1.5, label='Master $x_m$')
    axes[idx].plot(t, xs, 'r--', linewidth=1.5, label='Slave $x_s$')
    axes[idx].set_ylabel('Position [m]')
    axes[idx].set_title(f"{label} (delay=300ms)", fontsize=11)
    axes[idx].legend(loc='upper right')
    axes[idx].grid(True, alpha=0.3)

axes[-1].set_xlabel('Time [s]')
plt.suptitle('Effect of Control Gain on Stability under Time Delay',
             fontsize=13, y=1.01)
plt.tight_layout()
plt.savefig('delay_instability_demo.png', dpi=150, bbox_inches='tight')
plt.show()

この結果は、遅延下でのゲイン設計の難しさを端的に示しています。

  1. 低ゲインの場合: 系は安定していますが、スレーブの追従が遅く、位置偏差が大きくなります。オペレータにとっては「鈍い」操作感になります。

  2. 中ゲインの場合: 追従速度は改善されますが、振動的な応答が現れます。これは遅延によるフィードバック信号の位相遅れが、ゲインの増加によって増幅された結果です。

  3. 高ゲインの場合: 発振が生じ、位置が振動し続けるか発散します。遅延がなければ十分安定なゲインであっても、遅延が存在すると不安定になることが確認できます。

シミュレーション3: Wave Variable による遅延補償

いよいよ、Scattering Transform(Wave Variable)による遅延補償の効果を確認します。先ほどの不安定なケースに Wave Variable を導入し、安定性が回復する様子を観察しましょう。

import numpy as np
import matplotlib.pyplot as plt

dt = 0.001
T_end = 6.0
t = np.arange(0, T_end, dt)
N = len(t)

Mm, Bm = 1.0, 2.0
Ms, Bs = 1.0, 2.0
Kp, Kd = 100.0, 30.0

# 人間の入力(ステップ)
f_h = np.zeros(N)
f_h[int(0.5/dt):] = 5.0

delay_sec = 0.5   # 500 ms 遅延
delay_steps = int(delay_sec / dt)
b_wave = 5.0      # 特性インピーダンス

def simulate_standard(label):
    """通常のバイラテラル制御(遅延あり)"""
    xm, vm = np.zeros(N), np.zeros(N)
    xs, vs = np.zeros(N), np.zeros(N)

    for i in range(1, N):
        j = max(0, i - delay_steps)

        f_cs = Kp * (xm[j] - xs[i-1]) + Kd * (vm[j] - vs[i-1])
        f_cm = 0.0

        am = (f_h[i] + f_cm - Bm * vm[i-1]) / Mm
        vm[i] = vm[i-1] + am * dt
        xm[i] = xm[i-1] + vm[i] * dt

        a_s = (f_cs - Bs * vs[i-1]) / Ms
        vs[i] = vs[i-1] + a_s * dt
        xs[i] = xs[i-1] + vs[i] * dt

    return xm, xs, label

def simulate_wave_variable(label):
    """Wave Variableによる遅延補償"""
    xm, vm_arr = np.zeros(N), np.zeros(N)
    xs, vs_arr = np.zeros(N), np.zeros(N)

    # Wave variableのバッファ
    um_buf = np.zeros(N)  # マスター→スレーブの波動変数
    vs_wave_buf = np.zeros(N)  # スレーブ→マスターの波動変数

    for i in range(1, N):
        # --- マスター側 ---
        # スレーブから遅延して届く波動変数
        j_s = max(0, i - delay_steps)
        v_received = vs_wave_buf[j_s]

        # 受信した波動変数から力を復元
        # v = (f - b*v_dot) / sqrt(2b)  => f = sqrt(2b)*v + b*v_dot
        # 簡易的に: f_from_slave = sqrt(2b)*v_received - b*vm_arr[i-1]
        f_cm = np.sqrt(2 * b_wave) * v_received - b_wave * vm_arr[i-1]

        # マスターの運動方程式
        am = (f_h[i] + f_cm - Bm * vm_arr[i-1]) / Mm
        vm_arr[i] = vm_arr[i-1] + am * dt
        xm[i] = xm[i-1] + vm_arr[i] * dt

        # マスター側から送信する波動変数
        # u = (f + b*v_dot) / sqrt(2b)
        f_m_out = -f_cm  # マスターが通信路に入力する力
        um_buf[i] = (f_m_out + b_wave * vm_arr[i]) / np.sqrt(2 * b_wave)

        # --- スレーブ側 ---
        # マスターから遅延して届く波動変数
        j_m = max(0, i - delay_steps)
        u_received = um_buf[j_m]

        # 受信した波動変数から力を復元
        f_cs = np.sqrt(2 * b_wave) * u_received - b_wave * vs_arr[i-1]

        # スレーブの運動方程式
        a_s = (f_cs - Bs * vs_arr[i-1]) / Ms
        vs_arr[i] = vs_arr[i-1] + a_s * dt
        xs[i] = xs[i-1] + vs_arr[i] * dt

        # スレーブ側から送信する波動変数
        f_s_out = -f_cs
        vs_wave_buf[i] = (f_s_out + b_wave * vs_arr[i]) / np.sqrt(2 * b_wave)

    return xm, xs, label

# シミュレーション実行
xm_std, xs_std, lbl_std = simulate_standard("通常制御 (遅延500ms)")
xm_wv, xs_wv, lbl_wv = simulate_wave_variable("Wave Variable (遅延500ms)")

# --- 可視化 ---
fig, axes = plt.subplots(2, 1, figsize=(10, 8), sharex=True)

axes[0].plot(t, xm_std, 'b-', linewidth=1.5, label='Master $x_m$')
axes[0].plot(t, xs_std, 'r--', linewidth=1.5, label='Slave $x_s$')
axes[0].set_ylabel('Position [m]')
axes[0].set_title(lbl_std, fontsize=12)
axes[0].legend(loc='lower right')
axes[0].grid(True, alpha=0.3)

axes[1].plot(t, xm_wv, 'b-', linewidth=1.5, label='Master $x_m$')
axes[1].plot(t, xs_wv, 'r--', linewidth=1.5, label='Slave $x_s$')
axes[1].set_ylabel('Position [m]')
axes[1].set_title(lbl_wv, fontsize=12)
axes[1].legend(loc='lower right')
axes[1].grid(True, alpha=0.3)

axes[-1].set_xlabel('Time [s]')
plt.tight_layout()
plt.savefig('wave_variable_comparison.png', dpi=150, bbox_inches='tight')
plt.show()

このシミュレーション結果を比較すると、Wave Variable の効果が明確に見て取れます。

  1. 通常制御(上段): 500 msの遅延下で高めのゲインを使っているため、スレーブの応答に振動や不安定な挙動が見られます。マスターとスレーブの位置が乖離し、制御性能が著しく劣化しています。

  2. Wave Variable(下段): 同じ遅延条件でありながら、系は安定しています。スレーブはマスターの動きに追従しますが、追従にはある程度のダンピング(鈍さ)が伴います。これは特性インピーダンス $b$ によるエネルギー散逸の副作用であり、安定性を保証する代償です。

  3. 定常偏差: Wave Variable では、定常状態でマスターとスレーブの位置にわずかな差(定常偏差)が残ることがあります。これは先述の実用上の課題の一つです。

シミュレーション4: 共有自律のブレンディング

最後に、共有自律のブレンディングアプローチをシミュレーションします。人間のコマンドとロボットの自律制御をブレンドし、遅延の影響を軽減する様子を確認しましょう。

import numpy as np
import matplotlib.pyplot as plt

dt = 0.001
T_end = 8.0
t = np.arange(0, T_end, dt)
N = len(t)

# ターゲット位置(ロボットが到達すべき目標)
x_target = 2.0

# 人間のコマンド(ノイズを含むステップ + サイン波の揺らぎ)
np.random.seed(42)
human_cmd = np.zeros(N)
for i in range(N):
    if t[i] > 0.5:
        # 人間は目標に向かうが、揺らぎがある
        human_cmd[i] = x_target + 0.3 * np.sin(2 * np.pi * 0.5 * t[i]) \
                       + 0.1 * np.random.randn()

# ロボットの自律制御(目標への滑らかな軌道)
def autonomous_trajectory(t_val, x_current, x_goal):
    """指数関数的に目標に接近する自律制御"""
    tau = 1.5  # 時定数
    return x_goal * (1 - np.exp(-(t_val - 0.5) / tau)) if t_val > 0.5 else 0.0

delay_sec = 1.0  # 1秒遅延(月面想定)
delay_steps = int(delay_sec / dt)

def simulate_shared_autonomy(alpha_val, label):
    """共有自律シミュレーション"""
    x = np.zeros(N)
    v = np.zeros(N)
    M, B = 1.0, 3.0
    K_ctrl = 30.0

    for i in range(1, N):
        # 遅延した人間のコマンド
        j = max(0, i - delay_steps)
        u_human = K_ctrl * (human_cmd[j] - x[i-1])

        # ロボットの自律制御(遅延なし、ローカル)
        x_auto = autonomous_trajectory(t[i], x[i-1], x_target)
        u_auto = K_ctrl * (x_auto - x[i-1])

        # ブレンディング
        u = alpha_val * u_human + (1 - alpha_val) * u_auto

        a = (u - B * v[i-1]) / M
        v[i] = v[i-1] + a * dt
        x[i] = x[i-1] + v[i] * dt

    return x, label

# 異なるブレンド比でシミュレーション
cases = [
    (1.0, "完全遠隔操縦 ($\\alpha=1.0$)"),
    (0.5, "共有自律 ($\\alpha=0.5$)"),
    (0.2, "高自律 ($\\alpha=0.2$)"),
    (0.0, "完全自律 ($\\alpha=0.0$)"),
]

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

for idx, (alpha, label) in enumerate(cases):
    x_result, lbl = simulate_shared_autonomy(alpha, label)
    axes[idx].plot(t, x_result, 'b-', linewidth=1.5, label='Robot position')
    axes[idx].axhline(y=x_target, color='g', linestyle=':', alpha=0.7,
                      label=f'Target ({x_target} m)')
    axes[idx].set_ylabel('Position [m]')
    axes[idx].set_title(f"{label} — delay=1.0s", fontsize=11)
    axes[idx].legend(loc='lower right', fontsize=9)
    axes[idx].grid(True, alpha=0.3)
    axes[idx].set_ylim(-0.5, 3.5)

axes[-1].set_xlabel('Time [s]')
plt.tight_layout()
plt.savefig('shared_autonomy_blending.png', dpi=150, bbox_inches='tight')
plt.show()

この結果は、共有自律のブレンディングがもたらす効果を明瞭に示しています。

  1. 完全遠隔操縦($\alpha = 1.0$): 1秒の遅延を受けた人間のコマンドがそのまま使われるため、応答が遅く、人間のコマンドに含まれる揺らぎやノイズもそのまま現れます。ロボットの動きは不安定で、目標に滑らかに到達できません。

  2. 共有自律($\alpha = 0.5$): 人間の意図(目標方向への移動)とロボットの滑らかな自律制御がブレンドされ、遅延の影響が半減します。ノイズも低減され、より実用的な動きになっています。

  3. 高自律($\alpha = 0.2$): ロボットの自律制御が支配的になり、目標への接近は滑らかです。人間の影響はわずかに残りますが、遅延やノイズの影響はほとんど見られません。

  4. 完全自律($\alpha = 0.0$): 遅延の影響を全く受けず、指数関数的に目標に接近します。ただし、環境に未知の変化があった場合、人間の介入ができないリスクがあります。

シミュレーション5: 遅延と通信路エネルギーの可視化

最後に、通信路のエネルギー収支を可視化し、パッシビティの概念を直感的に理解するためのシミュレーションを行います。

import numpy as np
import matplotlib.pyplot as plt

dt = 0.001
T_end = 5.0
t = np.arange(0, T_end, dt)
N = len(t)

Mm, Bm = 1.0, 2.0
Ms, Bs = 1.0, 2.0
Kp, Kd = 60.0, 15.0

# 人間の入力(正弦波 — 連続的な操作を模擬)
f_h = 3.0 * np.sin(2 * np.pi * 0.8 * t)
f_h[:int(0.3/dt)] = 0.0

delay_sec = 0.3
delay_steps = int(delay_sec / dt)

def simulate_energy_standard():
    """通常制御のエネルギー収支"""
    xm, vm = np.zeros(N), np.zeros(N)
    xs, vs = np.zeros(N), np.zeros(N)
    E_in = np.zeros(N)   # 通信路への入力エネルギー(累積)
    E_out = np.zeros(N)  # 通信路からの出力エネルギー(累積)

    for i in range(1, N):
        j = max(0, i - delay_steps)

        f_cs = Kp * (xm[j] - xs[i-1]) + Kd * (vm[j] - vs[i-1])
        f_cm = 0.0

        am = (f_h[i] + f_cm - Bm * vm[i-1]) / Mm
        vm[i] = vm[i-1] + am * dt
        xm[i] = xm[i-1] + vm[i] * dt

        a_s = (f_cs - Bs * vs[i-1]) / Ms
        vs[i] = vs[i-1] + a_s * dt
        xs[i] = xs[i-1] + vs[i] * dt

        # エネルギー計算
        # マスターが通信路に送るパワー: 制御力 × マスター速度
        f_to_channel = Kp * (xm[i] - xs[i]) + Kd * (vm[i] - vs[i])
        P_in = f_to_channel * vm[i]
        E_in[i] = E_in[i-1] + P_in * dt

        # スレーブが通信路から受けるパワー
        P_out = f_cs * vs[i]
        E_out[i] = E_out[i-1] + P_out * dt

    return xm, xs, E_in, E_out

def simulate_energy_wave():
    """Wave Variable制御のエネルギー収支"""
    xm, vm_arr = np.zeros(N), np.zeros(N)
    xs, vs_arr = np.zeros(N), np.zeros(N)
    b = 5.0

    um_buf = np.zeros(N)
    vs_wave_buf = np.zeros(N)
    E_in = np.zeros(N)
    E_out = np.zeros(N)

    for i in range(1, N):
        j_s = max(0, i - delay_steps)
        v_received = vs_wave_buf[j_s]
        f_cm = np.sqrt(2 * b) * v_received - b * vm_arr[i-1]

        am = (f_h[i] + f_cm - Bm * vm_arr[i-1]) / Mm
        vm_arr[i] = vm_arr[i-1] + am * dt
        xm[i] = xm[i-1] + vm_arr[i] * dt

        f_m_out = -f_cm
        um_buf[i] = (f_m_out + b * vm_arr[i]) / np.sqrt(2 * b)

        j_m = max(0, i - delay_steps)
        u_received = um_buf[j_m]
        f_cs = np.sqrt(2 * b) * u_received - b * vs_arr[i-1]

        a_s = (f_cs - Bs * vs_arr[i-1]) / Ms
        vs_arr[i] = vs_arr[i-1] + a_s * dt
        xs[i] = xs[i-1] + vs_arr[i] * dt

        f_s_out = -f_cs
        vs_wave_buf[i] = (f_s_out + b * vs_arr[i]) / np.sqrt(2 * b)

        # Wave variable のエネルギー: (u^2 - v^2)/2
        E_in[i] = E_in[i-1] + 0.5 * um_buf[i]**2 * dt
        E_out[i] = E_out[i-1] + 0.5 * u_received**2 * dt

    return xm, xs, E_in, E_out

xm_s, xs_s, Ein_s, Eout_s = simulate_energy_standard()
xm_w, xs_w, Ein_w, Eout_w = simulate_energy_wave()

# --- 可視化 ---
fig, axes = plt.subplots(2, 2, figsize=(12, 8))

# 位置の比較
axes[0, 0].plot(t, xm_s, 'b-', linewidth=1.2, label='Master')
axes[0, 0].plot(t, xs_s, 'r--', linewidth=1.2, label='Slave')
axes[0, 0].set_title('Standard Control — Position', fontsize=11)
axes[0, 0].set_ylabel('Position [m]')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)

axes[0, 1].plot(t, xm_w, 'b-', linewidth=1.2, label='Master')
axes[0, 1].plot(t, xs_w, 'r--', linewidth=1.2, label='Slave')
axes[0, 1].set_title('Wave Variable — Position', fontsize=11)
axes[0, 1].set_ylabel('Position [m]')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)

# エネルギー収支の比較
axes[1, 0].plot(t, Ein_s, 'b-', linewidth=1.5, label='$E_{in}$ (to channel)')
axes[1, 0].plot(t, Eout_s, 'r--', linewidth=1.5, label='$E_{out}$ (from channel)')
E_stored_s = Ein_s - Eout_s
axes[1, 0].fill_between(t, 0, E_stored_s,
                         where=(E_stored_s < 0), alpha=0.3, color='red',
                         label='Energy generated (unstable)')
axes[1, 0].set_title('Standard Control — Channel Energy', fontsize=11)
axes[1, 0].set_xlabel('Time [s]')
axes[1, 0].set_ylabel('Energy [J]')
axes[1, 0].legend(fontsize=8)
axes[1, 0].grid(True, alpha=0.3)

axes[1, 1].plot(t, Ein_w, 'b-', linewidth=1.5, label='$E_{in}$ (to channel)')
axes[1, 1].plot(t, Eout_w, 'r--', linewidth=1.5, label='$E_{out}$ (from channel)')
E_stored_w = Ein_w - Eout_w
axes[1, 1].fill_between(t, 0, E_stored_w,
                         where=(E_stored_w >= 0), alpha=0.3, color='green',
                         label='Energy stored (passive)')
axes[1, 1].set_title('Wave Variable — Channel Energy', fontsize=11)
axes[1, 1].set_xlabel('Time [s]')
axes[1, 1].set_ylabel('Energy [J]')
axes[1, 1].legend(fontsize=8)
axes[1, 1].grid(True, alpha=0.3)

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

このエネルギー収支の可視化は、パッシビティの意味を直感的に理解するうえで非常に有用です。

  1. 通常制御(左下): 通信路に入力されたエネルギー $E_{\text{in}}$ よりも出力されたエネルギー $E_{\text{out}}$ が大きくなる瞬間(赤い領域)があります。これは通信路がエネルギーを「生成」していることを意味し、パッシビティが破れています。このエネルギー生成がシステムの振動を増幅させる原因です。

  2. Wave Variable(右下): 通信路に蓄積されるエネルギー($E_{\text{in}} – E_{\text{out}}$、緑の領域)は常に非負です。つまり、通信路はエネルギーを消費するのみで、決して生成しません。これがパッシビティの保証であり、任意の遅延に対してシステムの安定性が保たれる理由です。

まとめ

本記事では、宇宙ロボットのテレオペレーションにおける通信遅延の問題と、その補償手法について体系的に解説しました。

  • テレオペレーションの必要性: 完全自律が難しい非構造化環境での作業、責任の所在、技術的限界から、人間とロボットの協調が不可欠である
  • 通信遅延の深刻さ: LEOで数百ms、月で1.3秒、火星で最大24分の遅延があり、フィードバック制御の安定性を根本から脅かす
  • マスタースレーブ制御: 位置-力型バイラテラル制御の基本構造と、理想的な透明性の概念を定式化した
  • 遅延下の不安定性: 遅延がフィードバックループの位相を狂わせ、ゲインが高いほど不安定になるメカニズムを確認した
  • パッシビティベースの遅延補償: Scattering Transform(Wave Variable)により、通信路のパッシビティを保証し、任意の遅延に対する安定性を実現できる
  • 予測表示: スレーブの環境モデルを用いて「未来」をオペレータに見せることで、操作感の遅延を解消する
  • 共有自律: 人間の高レベル判断とロボットの低レベル自律制御をブレンドし、遅延の影響を根本的に低減する

テレオペレーションの遅延補償は、単なる制御理論の問題にとどまらず、人間工学、通信工学、人工知能が交差する学際的な分野です。今後、月面基地の建設や火星探査ミッションが現実化するにつれて、これらの技術はますます重要になるでしょう。

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