コンテンツにスキップ

04. 深さ制限付き最速降下

最速降下の坂は、深く下って速度を稼ぐほど有利でした。では、ある深さより下には坂を作れないとしたらどうでしょうか。 床に沿って進む区間が現れ、道筋の形が変わります。

項目内容
状態水平位置 xx、深さ yy、速さ vv
制御坂の向き θ\theta(真下から測った角度)
目的到着時刻 tft_f を最小にする
区間1区間、40ノード、終了時刻は自由
新しく学ぶこと状態の上下限を経路制約として使うこと、制約に沿って進む区間(境界弧)が現れること

例題03 と同じく、摩擦のない坂を玉が重力だけで転がります。今回は次の2点が違います。

  • 終点を1点に決めます。 水平に 1 m、深さ 0.2 m の点です。例題03では深さを自由にしていました。
  • 床があります。 深さ 0.3 m より下には坂を作れません。

床がなければ、最速の道は終点より深いところまで下ってから、終点へ向かって上り返します。深く下るほど速くなれるからです。 その最も深い点が床より下にあると、そのままでは通れません。床が答えをどう変えるかを調べます。

この例では重力加速度を地球の値 9.81 m/s² にしています。

深さ yy は下向きを正、角度 θ\theta は真下から測ります。θ=0\theta = 0 が真下、90∘90^\circ が水平、90∘90^\circ より大きいと上り坂です。

x˙=vsin⁡θy˙=vcos⁡θv˙=gcos⁡θ\begin{aligned} \dot{x} &= v \sin\theta \\[2pt] \dot{y} &= v \cos\theta \\[2pt] \dot{v} &= g \cos\theta \end{aligned}

例題03と同じ式です。上り坂(cos⁡θ<0\cos\theta < 0)では3行目が負になり、玉は減速します。 ここでもエネルギー保存により、速さは深さだけで決まります(v=2gyv = \sqrt{2 g y})。

x(0)=0,y(0)=0,v(0)=0x(tf)=1,y(tf)=0.2\begin{aligned} x(0) &= 0, & y(0) &= 0, & v(0) &= 0 \\[2pt] x(t_f) &= 1, & y(t_f) &= 0.2 \end{aligned}

終点の速さは決めません。エネルギー保存から、終点の深さが決まれば速さも 2⋅9.81⋅0.2≈1.98\sqrt{2 \cdot 9.81 \cdot 0.2} \approx 1.98 m/s に決まります。

0≤ y≤0.3v≥00≤ θ≤π0.01≤ tf≤5\begin{aligned} 0 \le\ & y \le 0.3 \\[2pt] & v \ge 0 \\[2pt] 0 \le\ & \theta \le \pi \\[2pt] 0.01 \le\ & t_f \le 5 \end{aligned}

1行目の上限 y≤0.3y \le 0.3 がこの例の主役の床です。この例では、どれも変数の上下限として与えています。 状態の上下限はすべてのノードで課されるので、「途中のどの時刻でも床より下へ行かない」という経路制約として働きます。

v≥0v \ge 0、0≤θ≤π0 \le \theta \le \pi(左へ戻らない)、終了時刻の範囲は、計算が変な方向へ迷い込まないための補助的な制約です。

J=tfJ = t_f
記号意味値
gg重力加速度 PARAMETERS["gravity"]9.81 m/s²
ll終点の水平距離 PARAMETERS["distance"]1 m
yfy_f終点の深さ PARAMETERS["final_depth"]0.2 m
ymax⁡y_{\max}床の深さ PARAMETERS["max_depth"]0.3 m

時刻は 0 から 0.7 s までとし、その割合を s=t/0.7s = t / 0.7(0 から 1)とします。

x=l sy=yf s+0.2sin⁡(πs)v=2gy\begin{aligned} x &= l\, s \\[2pt] y &= y_f\, s + 0.2 \sin(\pi s) \\[2pt] v &= \sqrt{2 g y} \end{aligned}

水平位置は一定の速さで進み、深さは途中で一度ふくらんでから終点の深さへ戻る形です。 速さはエネルギー保存に合わせています。向き θ\theta は 0 から 2 rad(約 115°)へ時刻に比例して増やします。

玉の通る道筋。縦軸は深さで、下ほど深くなります。灰色の領域が床より深い通れない範囲です。点は40個のノードでの値、青の線はそれらを通る多項式、黒の破線は床がない場合の最速降下曲線です。
玉の通る道筋。縦軸は深さで、下ほど深くなります。灰色の領域が床より深い通れない範囲です。点は40個のノードでの値、青の線はそれらを通る多項式、黒の破線は床がない場合の最速降下曲線です。
深さ・速さ・向き(制御)の時間履歴。点はノードでの値、線は多項式、赤の点線は深さの上限 0.3 m です。向きは度で表示しています。
深さ・速さ・向き(制御)の時間履歴。点はノードでの値、線は多項式、赤の点線は深さの上限 0.3 m です。向きは度で表示しています。
玉が転がる様子。左は道筋と床、右は深さ・速さ・向きの時間履歴で、縦線が現在の時刻です。
  • 到着時刻は 0.6242 s です。床がない場合の 0.6206 s より、わずかに遅くなります。
  • 床がなければ深さ 0.358 m まで下りますが、床があると最大深さは 0.300 m に抑えられます。
  • 道筋は「下る → 床に沿って水平に進む → 上る」の3つの部分に分かれます。床の上では向きが 90°(水平)で一定、速さも一定です。
  • 終点では向きが 125° の上り坂で、速さは 1.981 m/s です。

床がないときの最速の道は、床より深いところを通ります。床があるとそこへは行けないので、 行ける範囲で最も深いところ、つまり床の上をできるだけ長く通るのが次善の策になります。 床の上では速さが 2gymax⁡=2⋅9.81⋅0.3≈2.43\sqrt{2 g y_{\max}} = \sqrt{2 \cdot 9.81 \cdot 0.3} \approx 2.43 m/s で、これが途中で出せる最大の速さです。

このように、制約が等号で成り立ったまま(y=ymax⁡y = y_{\max})進む部分を境界弧といいます。 境界弧の上では制御も制約で決まり、深さを保つために向きは水平(θ=90∘\theta = 90^\circ)に固定されます。 境界弧の前後は、床のない問題と同じくサイクロイドの一部で、床に接するように入り、接するように出ていきます。

床があると、床がない場合の最も深い(最も速い)部分を通れません。ただ、その部分は道のりのごく一部で、 床の上でも速さは 2.43 m/s と十分に大きいので、到着時刻の差は1 % 未満にとどまります。

例題03では終点の深さを自由にしたので、終点でちょうど水平になりました。この例では終点の深さを 0.2 m に固定したので、 その条件はなくなります。床から 0.2 m まで上り返す必要があり、終点では上り坂になっています。

境界弧の始まりと終わりでは、向き θ\theta の変化の仕方が急に変わります(傾きが折れ曲がる)。 1区間の多項式ではこの折れ目を正確に表せないので、向きのグラフでは境界弧の前後が少し丸まっています。 上限はノードでだけ課されるので、ノードの間を通る多項式は床をごくわずかに(10−510^{-5} m 程度)超えることがあります。 ノードを増やすと折れ目はよりはっきりします。

  • PARAMETERS["max_depth"] を 0.4 にする。床が床なしの最大深さ(約 0.358 m)より深くなるので床に触れず、到着時刻は床なしの値 約 0.6206 s に一致します。
  • PARAMETERS["max_depth"] を 0.25 にする。床に沿う区間が長くなり、到着時刻は約 0.636 s に延びます。
  • main() の solve() を solve(nodes=20) にしてノード数を半分にし、向きのグラフの折れ目付近の形を40ノードの場合と比べる。
ターミナルウィンドウ
uv run python examples/04_constrained_descent.py

examples/04_constrained_descent.py を GitHub で見る