중급 · 회전하는 고리 속 공

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. 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

    반사예요. 속도에서 법선 방향 성분을 두 번 빼요. 고리의 회전은 이 식에 들어가지 않아요.

  6. p = normal * (R - ball)

    공을 벽 위로 되돌려 놓아서, 어떤 단계에서도 공이 벽 밖에 남지 않게 해요.

직접 해 보기

  1. 단위 질량당 에너지 ½·|v|² + g·y를 확인해 보세요. 처음과 끝에 출력해 보세요. 그대로 유지되나요?

    힌트

    np.dot(v, v)는 |v|²예요.

    정답

    거의 그래요. 실행하는 동안 3.83에서 3.75로 약 2 % 줄어요. 단순한 걸음과 벽 위로 되돌려 놓는 처리에서 조금씩 잃어요.

  2. dt를 절반으로 줄여 보세요. 에너지와 공이 빠져나가는 순간은 어떻게 될까요?

    힌트

    dt = 0.00025.

    정답

    이제 에너지가 2 % 대신 약 1 % 줄어요. 공은 여전히 약 3.9초 뒤에 빠져나가요.

  3. 튕김을 계산하는 줄은 왜 spin을 전혀 쓰지 않을까요?

    힌트

    벽 위의 한 점은 어느 방향으로 움직이나요?

    정답

    마찰이 없으면 벽은 법선 방향으로만 밀 수 있는데, 회전하는 벽은 그와 직각인 접선 방향으로 움직여요.

퀴즈

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는 거의 일정해야 해요: 시뮬레이션을 시험하는 방법.

이 플래시카드만 따로 보기 →