中級 · 回転するリングの中のボール

レッスン7:時間を一歩ずつ進める

Pythonで 「回転するリングの中のボール」 を作りながら、次のことを学びます:

  • シミュレーション:while ループの中の小さな時間ステップ
  • numpy の配列としてのベクトル
  • 内積と反射
  • np.arctan2 による角度と、その折り返し
  • ループを抜ける break

まず見てみる

クリックするとYouTubeから再生されます。

spinning-ring.py
"""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行ずつ

  1. v[1] -= g * dt

    重力です。ステップごとに、縦方向の速さが g·dt だけ減ります。

  2. p = p + v * dt

    ボールは、速度に時間ステップを掛けたぶんだけ動きます。このいちばん単純な進め方を、オイラー法と呼びます。

  3. if r > R - ball:

    ボールの中心がリングからボールの半径ひとつぶんの距離に来たとき、ボールの端が壁に届きます。

  4. off = abs((angle - centre + np.pi) % (2 * np.pi) - np.pi)

    ボールとすき間の真ん中とのあいだの角度を、−π … π の範囲に折り返したものです。こうすると359°は1°離れているとみなされます。

  5. v = v - 2 * np.dot(v, normal) * normal

    反射です。速度のうち法線に沿った成分を、2回ぶん取り除きます。リングの回転はここに入ってきません。

  6. p = normal * (R - ball)

    ボールを壁の上に戻します。こうすれば、どのステップでもボールが外に出たままになることはありません。

やってみよう

  1. 単位質量あたりのエネルギー ½·|v|² + g·y を確かめてみましょう。最初と最後に表示します。同じままですか?

    ヒント

    np.dot(v, v) は |v|² です。

    答え

    ほぼ同じです。実行のあいだに約2 %、3.83から3.75へ減ります。単純なステップと、壁の上へ押し戻す処理で、少しずつ失われるのです。

  2. dt を半分にしてみましょう。エネルギーと、ボールが抜け出す時刻はどうなりますか?

    ヒント

    dt = 0.00025。

    答え

    エネルギーの減り方は2 %ではなく約1 %になります。ボールはやはり約3.9秒後に抜け出します。

  3. 跳ね返りの行で spin をまったく使わないのはなぜですか?

    ヒント

    壁の1点は、どちらへ動きますか?

    答え

    摩擦がなければ、壁は法線に沿ってしか押せません。そして回転する壁は接線に沿って、つまり法線と直角に動くからです。

クイズ

1. np.dot(v, normal) は何を測りますか?
2. while ループの中で break は何をしますか?
3. np.arctan2(y, x) は、どの範囲の値を返しますか?
4. 時間ステップを小さくすると、シミュレーションがよくなるのはなぜですか?
5. リングの回転で何が変わりますか?

フラッシュカード

表を読み、裏に何があるかを自分で言ってみてから、カードを開いてください。

  • オイラー法のステップ

    位置 += 速度 · dt;速度 += 加速度 · dt。

  • 反射

    v − 2·(v · n)·n。n は壁の単位法線ベクトル。

  • np.arctan2(y, x)

    点 (x, y) の角度。−πからπまで。

  • break

    今いるループを、すぐに抜けます。

  • エネルギーの確認

    摩擦がなければ、½·|v|² + g·y はほぼ一定に保たれるはずです。シミュレーションのテストになります。

このフラッシュカードを別のページで →