注解

此笔记本可在此处下载: Badminton.ipynb

数值分析导论:羽毛球的建模

介绍

羽毛球是最快的球类运动,尽管外表看起来很美。在这个例子中,我们将对羽毛球的飞行进行建模,并对其轨迹进行数值模拟。为此,我们将使用一个常微分方程解算器来追踪加速度回到轨迹。

import IPython
IPython.display.YouTubeVideo("wy5UraBy5co")

模型羽毛球飞行

羽毛球可以看作是一个准时的群众。它的加速度受牛顿第二定律支配:

\[m\vec a(m/r)=-mg\vec y-\frac 1 2 \rho v^2 a c x\vec t\]

进一步阅读:

模拟输入:

%matplotlib notebook
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import integrate
import ipywidgets as ipw
v0  = 493. / 3.6 # Initial velocity [m/s]
A   = 4.e-3      # Shuttlecock cross area [m**2]
cx  = .62        # Drag coefficient []
m   = 4.e-2      # Shuttlecock mass [kg]
rho = 1.225      # Air density [kg/m**3]
g   = 9.81       # Gravity [m/s**2]

数值模拟

def derivative(X, t):
    """
    Target ODE: Newton's second law
    """
    x, y, vx, vy = X
    v = (vx**2 + vy**2)**.5
    Tx, Ty = vx / v, vy / v
    ax = -.5 * rho * v**2 * A * cx * Tx / m
    ay = -.5 * rho * v**2 * A * cx * Ty / m - g
    return np.array([vx, vy, ax, ay])


x0, y0 = 0., 0.
theta0 = 45.
X0 = [x0, y0, v0* np.cos(np.radians(theta0)), v0* np.sin(np.radians(theta0))]
t = np.linspace(0., 10., 200)
sol = integrate.odeint(derivative, X0, t)
out = pd.DataFrame(sol, columns = ["x", "y", "vx", "vy"])
out.head()
x y vx vy
0 0.000000 0.000000 96.834345 96.834345
1 4.323421 4.311938 76.793616 76.351638
2 7.831625 7.788303 63.660684 62.843454
3 10.785471 10.692467 54.394327 53.238940
4 13.337946 13.178880 47.510084 46.039129
plt.figure()
plt.plot(out.x, out.y)
plt.grid()
plt.ylim(0., 50.)
plt.xlabel("Position, $x$")
plt.ylabel("Position, $y$")
plt.show()
<IPython.core.display.Javascript object>
thetas = [0., 10.,15., 20., 30., 45., 60., 80., 85.]
plt.figure()

for theta0 in thetas:
    x0, y0 = 0., 3.
    X0 = [x0, y0, v0* np.cos(np.radians(theta0)), v0* np.sin(np.radians(theta0))]
    t = np.linspace(0., 10., 1000)
    sol = integrate.odeint(derivative, X0, t)
    out = pd.DataFrame(sol, columns = ["x", "y", "vx", "vy"])
    out["t"] = t
    plt.plot(out.x, out.y, label = r"$\theta_0 = $" + "{0}".format(theta0))
plt.legend()
plt.grid()
plt.ylim(0., 50.)
plt.xlabel("Position, $x$")
plt.ylabel("Position, $y$")
plt.show()
<IPython.core.display.Javascript object>

作为初始角度函数的范围 \(\theta_0\)

%%time
thetas = np.linspace(-180., 180., 300)
xmax = np.zeros_like(thetas)


for i in range(len(thetas)):
    theta0 = thetas[i]
    x0, y0 = 0., 3.
    X0 = [x0, y0, v0* np.sin(np.radians(theta0)), -v0* np.cos(np.radians(theta0))]
    t = np.linspace(0., 10., 10000)
    sol = integrate.odeint(derivative, X0, t)
    out = pd.DataFrame(sol, columns = ["x", "y", "vx", "vy"])
    xmax[i] = out[out.y < 0.].iloc[0].x
CPU times: user 1.17 s, sys: 0 ns, total: 1.17 s
Wall time: 1.24 s
plt.figure()
plt.plot(thetas, xmax)
plt.grid()
plt.xlabel(r"Start angle $\theta_0$")
plt.ylabel(r"Range $x_m$")
plt.show()
<IPython.core.display.Javascript object>

更具互动性的解决方案

N = 100




x = np.zeros(N)
y = x.copy()


plt.figure()
line, = plt.plot(x, y, "r-", label = "trajectory")
plt.grid()
plt.xlim(0., 80.)
plt.ylim(0., 80.)
plt.xlabel("Position, $x$ [m]")
plt.ylabel("Position, $y$ [m]")
plt.legend()

@ipw.interact(theta0 = (0.,90., 1.), v0 = (10., 500., 10))
def simulate(theta0 = 45., v0 = 490.):
    v0 /= 3.6
    x0, y0 = 0., 0.
    X0 = [x0, y0, v0* np.cos(np.radians(theta0)), v0* np.sin(np.radians(theta0))]
    t = np.linspace(0., 10, 200)
    sol = integrate.odeint(derivative, X0, t)
    line.set_xdata(sol[:, 0])
    line.set_ydata(sol[:, 1])
<IPython.core.display.Javascript object>
interactive(children=(FloatSlider(value=45.0, description='theta0', max=90.0, step=1.0), FloatSlider(value=490…