レッスン7:時間を一歩ずつ進める
Pythonで 「回転するリングの中のボール」 を作りながら、次のことを学びます:
- シミュレーション:while ループの中の小さな時間ステップ
- numpy の配列としてのベクトル
- 内積と反射
- np.arctan2 による角度と、その折り返し
- ループを抜ける break
まず見てみる
クリックするとYouTubeから再生されます。
"""A ball under gravity in a ring with a gap that spins; the bounces are elastic and without friction.
Run: python spinning-ring.py (needs: pip install numpy matplotlib)
"""
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("dark_background")
R, ball, g = 1.0, 0.05, 9.81 # ring radius (m), ball radius, gravity
gap, spin = np.radians(18), 2 * np.pi / 4 # an 18 degree gap, one turn every 4 seconds
p, v = np.array([0.3, 0.2]), np.array([1.8, 0.7])
dt, t, track = 0.0005, 0.0, []
while t < 60:
v[1] -= g * dt
p = p + v * dt
t += dt
track.append(p.copy())
r = np.linalg.norm(p)
if r > R - ball: # the ball touches the wall, or is at the gap
angle = np.arctan2(p[1], p[0])
centre = -np.pi / 2 + spin * t # where the gap is now
off = abs((angle - centre + np.pi) % (2 * np.pi) - np.pi)
if off < gap / 2 - ball / R: # all of the ball fits through: out
break
normal = p / r
v = v - 2 * np.dot(v, normal) * normal # the bounce: the spin plays no part in it
p = normal * (R - ball)
track = np.array(track)
print(f"escaped after {t:.1f} s" if t < 60 else "still inside after 60 s")
plt.figure(figsize=(6, 6))
plt.plot(track[:, 0], track[:, 1], color="#5eead4", linewidth=0.4)
plt.gca().add_patch(plt.Circle((0, 0), R, fill=False, color="white"))
plt.axis("equal")
plt.axis("off")
plt.show()
# Try: spin = 0 and centre = -np.pi / 2 (a still gap at the bottom): the bounces stay the same
このコンピューターにPythonが入っていませんか?ノートブックなら、同じスクリプト、練習問題、答えを、ブラウザ上のGoogle Colabで実行できます。ノートブックは英語です。
1行ずつ
v[1] -= g * dt重力です。ステップごとに、縦方向の速さが g·dt だけ減ります。
p = p + v * dtボールは、速度に時間ステップを掛けたぶんだけ動きます。このいちばん単純な進め方を、オイラー法と呼びます。
if r > R - ball:ボールの中心がリングからボールの半径ひとつぶんの距離に来たとき、ボールの端が壁に届きます。
off = abs((angle - centre + np.pi) % (2 * np.pi) - np.pi)ボールとすき間の真ん中とのあいだの角度を、−π … π の範囲に折り返したものです。こうすると359°は1°離れているとみなされます。
v = v - 2 * np.dot(v, normal) * normal反射です。速度のうち法線に沿った成分を、2回ぶん取り除きます。リングの回転はここに入ってきません。
p = normal * (R - ball)ボールを壁の上に戻します。こうすれば、どのステップでもボールが外に出たままになることはありません。
やってみよう
単位質量あたりのエネルギー ½·|v|² + g·y を確かめてみましょう。最初と最後に表示します。同じままですか?
ヒント
np.dot(v, v) は |v|² です。
答え
ほぼ同じです。実行のあいだに約2 %、3.83から3.75へ減ります。単純なステップと、壁の上へ押し戻す処理で、少しずつ失われるのです。
dt を半分にしてみましょう。エネルギーと、ボールが抜け出す時刻はどうなりますか?
ヒント
dt = 0.00025。
答え
エネルギーの減り方は2 %ではなく約1 %になります。ボールはやはり約3.9秒後に抜け出します。
跳ね返りの行で spin をまったく使わないのはなぜですか?
ヒント
壁の1点は、どちらへ動きますか?
答え
摩擦がなければ、壁は法線に沿ってしか押せません。そして回転する壁は接線に沿って、つまり法線と直角に動くからです。
クイズ
フラッシュカード
表を読み、裏に何があるかを自分で言ってみてから、カードを開いてください。
オイラー法のステップ
位置 += 速度 · dt;速度 += 加速度 · dt。
反射
v − 2·(v · n)·n。n は壁の単位法線ベクトル。
np.arctan2(y, x)
点 (x, y) の角度。−πからπまで。
break
今いるループを、すぐに抜けます。
エネルギーの確認
摩擦がなければ、½·|v|² + g·y はほぼ一定に保たれるはずです。シミュレーションのテストになります。