コンテンツにスキップ

01. 最小操作量の移動

直線の上にある点を、決まった距離だけ動かします。急げば早く着きますが、そのぶん大きな操作が必要です。 ゆっくり動かせば操作は小さくて済みますが、時間がかかります。この二つの釣り合いがいちばん良い動かし方はどれでしょうか。

項目内容
状態位置 xx
制御速度 uu
目的所要時間と、速度の2乗の積分の和を最小にする
区間1区間、8ノード、開始時刻 5 は固定、終了時刻は自由
新しく学ぶこと最適制御問題の組み立て方(運動方程式・境界条件・目的関数)、終端コストと積分コストの組み合わせ

一本の直線の上を動く点を考えます。点の位置を xx、点を動かす速さを uu とします。 私たちは各時刻に uu を自由に選べます。uu が「自分で決められる入力」、つまり制御です。 位置 xx は uu を選んだ結果として決まる量で、状態と呼びます(用語は最適制御問題とはで詳しく説明しています)。

点を x=0x = 0 から x=2x = 2 まで運びたいとします。良い運び方の基準として、次の二つを同時に考えます。

  • 早く着きたい。 所要時間が短いほど良い。
  • 操作を小さくしたい。 速さ uu が大きいほどエネルギーを使うとみなし、u2u^2 を時間で積み上げた量が小さいほど良い。

単位は特に決めない無次元量です。物理的な意味よりも、最適制御問題の形を知ることを目的とした例です。

x˙=u\dot{x} = u

x˙\dot{x} は位置の時間微分 dx/dtdx/dt、つまり位置の変化の速さです。この式は「位置は選んだ速さ uu で変わる」ことを表しています。 運動方程式は、制御を選んだとき状態がどう変わるかを決める規則で、最適化の途中でも必ず守られます。

x(t0)=0,x(tf)=2x(t_0) = 0, \qquad x(t_f) = 2

t0t_0 は開始時刻、tft_f は終了時刻です。この例では開始時刻を t0=5t_0 = 5 に固定しています。 時刻が 0 から始まる必要はないことを示すためで、答えの所要時間 tf−t0t_f - t_0 は開始時刻によりません。 終了時刻 tft_f は固定せず、答えの一部として決めます。

0≤u≤35.1≤tf≤15\begin{aligned} 0 &\le u \le 3 \\[2pt] 5.1 &\le t_f \le 15 \end{aligned}

速さは 0 以上 3 以下、終了時刻は 5.1 から 15 の間とします。所要時間でいえば 0.1 から 10 の範囲です。 後で見るとおり、最適解ではどちらの制約にも届かず、答えを決めているのは目的関数の釣り合いです。

J=(tf−t0)⏟所要時間+∫t0tfu2 dt⏟操作の大きさJ = \underbrace{(t_f - t_0)}_{\text{所要時間}} + \underbrace{\int_{t_0}^{t_f} u^2\, dt}_{\text{操作の大きさ}}

第1項は終わったときの時刻だけで決まる終端コスト、第2項は途中の操作を時間で積み上げる積分コストです。 u2u^2 は uu の正負によらず、大きな操作ほど急に大きくなります。 二つの項は向きが逆で、所要時間を縮めると速く動く必要があるので第2項が増えます。

記号意味値
dd移動距離(コードの target)2
t0t_0開始時刻5
umax⁡u_{\max}速さの上限3

最適化の内部では、時刻を 3、位置を 2 を単位とする大きさにそろえて計算しています (単位をそろえる)。結果は元の単位で取り出せます。

最適化は、答えに近い「たたき台」から出発して少しずつ改良していきます。この例では

  • 時刻:5 から 8 まで(所要時間 3)
  • 位置:0 から 2 まで時刻に比例して増える直線
  • 速さ:一定値 2/32/3(3 の時間で 2 進む速さ)

を初期推定とします。

位置と速度(制御)の時間履歴。点は8個のノードでの値、青の線はそれらを通る多項式、黒の破線は解析解、赤の点線は速度の上限 3 です。
位置と速度(制御)の時間履歴。点は8個のノードでの値、青の線はそれらを通る多項式、黒の破線は解析解、赤の点線は速度の上限 3 です。
点が動く様子。左は直線上の点の位置、右は位置と速度の時間履歴で、縦線が現在の時刻を示します。
  • 所要時間は 2.0000、目的関数の値は 4.0000 です。
  • 速度は最初から最後まで 1.0000 で一定です。位置は時刻に比例して増えます。
  • 速度は上限 3 よりずっと小さく、制約はどこでも効いていません。

この問題は手計算で解けます。数値解と比べてみましょう。

所要時間を決めたときの最良の動かし方

Section titled “所要時間を決めたときの最良の動かし方”

まず所要時間を T=tf−t0T = t_f - t_0 に固定して考えます。距離 dd を進むので、速さの平均は d/Td/T です。 平均が同じなら、速さを途中で変えるよりずっと一定にした方が u2u^2 の積分は小さくなります。 速いときの u2u^2 の増え方が、遅いときの減り方より大きいからです(2乗の平均は平均の2乗以上)。 したがって u=d/Tu = d/T で一定に動くのが最良で、積分コストは

∫t0tfu2 dt=(dT)2T=d2T\int_{t_0}^{t_f} u^2\, dt = \left(\frac{d}{T}\right)^2 T = \frac{d^2}{T}

になります。

目的関数は所要時間 TT だけの関数になります。

J(T)=T+d2TJ(T) = T + \frac{d^2}{T}

TT を大きくすると第1項が増え、小さくすると第2項が増えます。dJ/dT=1−d2/T2=0dJ/dT = 1 - d^2/T^2 = 0 から

T=d=2u=d/T=1J=2d=4\begin{aligned} T &= d = 2 \\[2pt] u &= d/T = 1 \\[2pt] J &= 2d = 4 \end{aligned}

が得られます。数値解の所要時間 2.0000、速度 1、目的関数 4.0000 はこれと一致しています。

興味深いのは、最適な速さが距離によらず常に 1 になることです。この目的関数では「時間 1 の価値」と 「速さ 1 で動き続ける操作の負担」がちょうど釣り合うのが速さ 1 だからです。距離が変わると、所要時間だけが変わります。

OpenGoddard は、時間を8個の点(ノード)に分け、各ノードでの位置と速度、それに終了時刻を未知数として、 「運動方程式・境界条件・制約を満たしつつ JJ を最小にする」有限個の数の最適化問題として解きます (直接法と間接法、擬スペクトル法)。 解が直線と定数なので、8ノードでも誤差なく表せます。

  • main() の target = 2.0 を 1 や 5 に変え、所要時間が距離と同じ値になり、速度は 1 のまま変わらないことを確かめる。
  • target を 20 にする。制約がなければ所要時間 20 が最良ですが、終了時刻の上限 15(所要時間 10)を超えてしまいます。 所要時間は上限の 10 に張り付き、速度は一定の 2、目的関数は 10+202/10=5010 + 20^2/10 = 50 になることを確かめる。制約が答えを決める例です。
  • Problem([5, 8], [8], ...) の 8 を 3 や 20 に変えても、答えがほとんど変わらないことを確かめる。
ターミナルウィンドウ
uv run python examples/01_minimum_energy_transfer.py

examples/01_minimum_energy_transfer.py を GitHub で見る