地面から高さ $h$ の地点から空気抵抗なしの斜方投射を行うと,初速度の大きさを一定とした場合,水平方向の到達距離が最大となるのは打ち上げ角度(仰角)が何度のときかを Python の SymPy を使って(かつ SciPy や NumPy を使わずに)求める。
update: 2025.01.31
ライブラリの import
from sympy.abc import *
from sympy import *
# SymPy Plotting Backends (SPB) でグラフを描く
from spb import *
# グラフを SVG で Notebook にインライン表示
%config InlineBackend.figure_formats = ['svg']
init_printing()
運動方程式の解
初期条件を $t = 0$ で
$$x(0) = 0, \quad y(0) = h, \quad v_x(0) = v_0 \cos\theta, \quad v_y(0) = v_0 \sin \theta$$
としたときの解は
\begin{eqnarray}
x(t) &=& v_0 \cos\theta\cdot t \\
y(t) &=& h + v_0 \sin\theta\cdot t -\frac{1}{2} g t^2
\end{eqnarray}
無次元化された斜方投射の解
この系に特徴的な時間 $\displaystyle \tau \equiv \frac{v_0}{g}$ および長さ $\displaystyle \ell \equiv v_0 \tau = \frac{v_0^2}{g}$ で解を無次元化すると,
\begin{eqnarray}
T &\equiv& \frac{t}{\tau} \\
H &\equiv& \frac{h}{\ell} \\
X &\equiv& \frac{x}{\ell} = \cos\theta\cdot T \\
Y&\equiv& \frac{y}{\ell} = H+ \sin\theta\cdot T -\frac{1}{2} T^2 \\
\end{eqnarray}
詳細は以下のページ
無次元化された解 $X(T, \theta), Y(T, \theta)$ を関数として定義します。
def X(T, theta):
return cos(theta) * T
var('H', positive = True)
def Y(T, theta, H):
return H + sin(theta)*T - T**2/2
無次元化された滞空時間と水平到達距離
滞空時間 $T_1 \ (>0)$ は以下の式を解いて求められる。
$$Y(T_1, \theta, H) = 0, \quad\Rightarrow\quad T_1 = T_1(\theta, H)$$
var('T1')
solve(Y(T1, theta, H), T1)
$T_1 > 0$ であるから,以下のように関数として定義します。
def T1(theta, H):
return sin(theta) + sqrt(2*H + sin(theta)**2)
T1(theta, H)
滞空時間 $T_1(\theta)$ の間に水平方向に進む距離を水平到達距離 $L(\theta)$ は
def L(theta, H):
return X(T1(theta, H), theta)
L(theta, H)
$H = 0$ の場合の軌道
まずは,とりあえずのグラフ。$\theta = 45^{\circ}$ での斜方投射の軌道をグラフにしてみる。SymPy で媒介変数表示の曲線のグラフを描く方法は授業でやりました。以下を参考に。
# θ = 45° のグラフ
th = 45
theta = rad(th)
H = 0
graphics(
line_parametric_2d(
X(T, theta), Y(T, theta, H), (T, 0, T1(theta, H)))
);
以下のようなオプションを設定して,もう少し体裁を整えます。
- $\theta$ の値を凡例に
- カラーマップは不要
use_cm = False
(デフォルトでFalse
にしてほしいところ) - 座標軸のラベルとグラフのタイトル
- 横軸縦軸の表示範囲
- 縦軸横軸のアスペクト比
# θ = 45° のグラフ
th = 45
theta = rad(th)
H = 0
graphics(
line_parametric_2d(
X(T, theta), Y(T, theta, H), (T, 0, T1(theta, H)),
# 凡例に θ の値を入れる
label = 'θ = %2d°' % th,
use_cm = False),
# タイトルに H の値を入れる
title = 'H = %.1f の場合の斜方投射' % H,
xlabel = 'X', ylabel = 'Y',
xlim = (0, 1.1), ylim = (0, 0.5),
aspect = 'equal',
legend = True
);
$\theta$ の値を $43^{\circ}$ から $47^{\circ}$ まで $1^{\circ}$ 刻みで変えて,複数の曲線を一度に描く例。
なお,細かいことですが,グラフの最高点は $\theta$ の値が大きいときですので,先に $47^{\circ}$ から描きはじめて,$1^{\circ}$ ずつ減らしてみます。
# th を変えて line_parametric_2d() を
# 何行もコピペするのは大変なので,
# 関数を定義してみる
def lp2d(th):
theta = rad(th)
return line_parametric_2d(
X(T, theta), Y(T, theta, H), (T, 0, T1(theta, H)),
# 凡例に θ の値を入れる
label = 'θ = %2d°' % th,
use_cm = False)
H = 0
graphics(
lp2d(47), lp2d(46), lp2d(45), lp2d(44), lp2d(43),
# タイトルに H の値を入れる
title = 'H = %.1f の場合の斜方投射' % H,
xlabel = 'X', ylabel = 'Y',
xlim = (0, 1.1), ylim = (0, 0.5),
aspect = 'equal',
legend = True
);
最大到達距離となる角度 $\theta$ を調べるために,着地点付近を拡大表示します。
H = 0
graphics(
lp2d(47), lp2d(46), lp2d(45), lp2d(44), lp2d(43),
# タイトルに H の値を入れる
title = 'H = %.1f の場合の斜方投射' % H,
xlabel = 'X', ylabel = 'Y',
# 着地点付近を拡大表示
xlim = (0.99, 1.001), ylim = (0, 0.01),
legend = True
);
上のグラフから,$H=0$ の場合には確かに $\theta=45^{\circ}$ の時に最大到達距離になっていることがわかります。
$H = 0.2$ の場合の軌道
今度は,$H=0.2$ の場合の軌道をグラフにしてみます。
H = 0.2
graphics(
lp2d(47), lp2d(46), lp2d(45), lp2d(44), lp2d(43),
lp2d(42), lp2d(41), lp2d(40), lp2d(39), lp2d(38),
# タイトルに H の値を入れる
title = 'H = %.1f の場合の斜方投射' % H,
xlabel = 'X', ylabel = 'Y',
xlim = (0, 1.4), ylim = (0, 0.7),
aspect = 'equal',
legend = True
);
最大到達距離となる角度 $\theta$ を調べるために,着地点付近を拡大表示します。
H = 0.2
graphics(
lp2d(47), lp2d(46), lp2d(45), lp2d(44), lp2d(43),
lp2d(42), lp2d(41), lp2d(40), lp2d(39), lp2d(38),
# タイトルに H の値を入れる
title = 'H = %.1f の場合の斜方投射' % H,
xlabel = 'X', ylabel = 'Y',
# 着地点付近を拡大表示
xlim = (1.15, 1.19), ylim = (0, 0.02),
legend = True
);
水平到達距離が最大となるのは $\theta=45^{\circ}$ のときではない!ことがわかるだろう。
$H = 0.2$ の場合の水平到達距離 $L$
定量的に調べるために,水平到達距離 L(th, H)
をグラフにしてみる。
var('th') # 変数 th の宣言・初期化(前に使っているので)
H = 0.2
graphics(
line(L(rad(th), H), (th, 38, 47)),
title = "H = %.1f の場合の打ち出し角度と水平到達距離" % H,
xlabel = "打ち出し角度 (°)",
ylabel = "規格化された水平到達距離",
);
上のグラフをみると,$H=0.2$ の場合に水平到達距離が最大となるのは $\theta=45^{\circ}$ のときではなく… どのくらい? $40^{\circ}$ のあたり?
L が最大となる角度 $\theta_{\rm m}$ を数値的に求める nsolve()
$\displaystyle \frac{dL(\theta)}{d\theta} = 0$ となる角度 $\theta_{\rm m}$ を数値的に求めてみる。
var('theta H', positive = True) # 変数の初期化(数値を入れてたので)
def dL(theta, H):
return diff(L(theta, H), theta)
dL(theta, H)
方程式の数値解を求める。授業でやりました。以下を参照:
# 40°をラジアンへ換算して小数点表示に
th1 = N(rad(40))
H = 0.2
# theta = th1 の付近で方程式を数値解を探す
thetam = nsolve(dL(theta, H), theta, th1)
print('H = %.1f のとき' % H)
# 得られた数値解を度に換算
thm = N(deg(thetam))
print(' 最大水平到達距離となる角度は %.5f°' % thm)
Lm = L(thetam, H)
print(' そのときの最大水平到達距離は %.5f' % Lm)
print()
○練習:$H$ を変えて最大水平到達距離を求める
高さ $H$ からの斜方投射の水平到達距離が最大となる打ち出し角度と,そのときの最大水平到達距離を次の $H$ について求めよ。
$H = 0, 0.2, 0.4, 0.6, 0.8, 1.0$
Hs = [0.2*i for i in range(6)]
Hs
for H in Hs:
thetam = nsolve(dL(theta, H), theta, th1)
print('H = %.1f のとき' % H)
# 得られた数値解を度に換算
thm = N(deg(thetam))
print(' 最大水平到達距離となる角度は %.5f°' % thm)
Lm = L(thetam, H)
print(' そのときの最大水平到達距離は %.5f' % Lm)
print()
○練習:高さ $H$ と 打ち出し角度 $\theta_{\rm m}$ のグラフ
高さ $H$ を横軸に,そのときの水平到達距離が最大となる打ち出し角度 $\theta_{\rm m}$ を縦軸にしたグラフを描け。
Hs = [0.2*i for i in range(6)]
thms = []
Lms = []
for H in Hs:
thetam = nsolve(dL(theta, H), theta, th1)
thm = N(deg(thetam))
thms += [thm]
Lm = L(thetam, H)
Lms += [Lm]
graphics(
list_2d(Hs, thms, scatter = True),
title = '水平到達距離が最大となる打ち出し角度',
xlabel = '規格化された高さ H',
ylabel = '打ち出し角度 (°)',
ylim = (29, 46)
);
○練習:高さ $H$ と最大水平到達距離 $L_{\rm m}$ のグラフ
高さ $H$ を横軸に,そのときの最大水平到達距離 $L_{\rm m} \equiv L(\theta_{\rm m}, H)$ を縦軸にしたグラフを描け。
graphics(
list_2d(Hs, Lms, scatter = True),
title = '最大水平到達距離',
xlabel = '規格化された高さ H',
ylabel = '規格化された最大水平到達距離'
);
○練習:水平到達距離を求める
$h = 5\,\mbox{(m)}$ の高さから速さ $v_0 = 10\,\mbox{(m/s)}$ で斜方投射したときの最大水平到達距離は何 $\mbox{m}$ か。またそのときの打出し角度は何度か。
ヒント:
$h = 5\,\mbox{(m)}$ は規格化された高さ $H$ ではいくらか,また規格化された最大到達距離 $L_{\rm m}$ を次元をもった量に直すといくらか,ということ。
\begin{eqnarray}
H &=& \frac{h}{\ell} = \frac{g h}{v_0^2} \\
x &=& \ell X = \frac{v_0^2}{g} X \\
\therefore\ \ x_{\rm m} &=& \frac{v_0^2}{g} L_{\rm m}
\end{eqnarray}
重力加速度 $g = 9.80665 \,(\mbox{m}/\mbox{s}^2)$
h = 5
v0 = 10
g = 9.80665
# 規格化された高さ H
H = h * g/v0**2
thetam = nsolve(dL(theta, H), theta, N(rad(40)))
xm = v0**2/g * L(thetam, H)
print('高さ h = %2d m のときの最大水平到達距離は' % h, end=' ')
print('%9.6f m' % xm)
print('そのときの打ち出し角度は %9.6f°' % N(deg(thetam)))
最大水平到達距離となる角度を解析的に求める
解法1:最大水平到達距離となる $\sin\theta_{\rm m}$ を解析的に求める
水平到達距離 $L(\theta)$ を $s \equiv \sin \theta$ を使って表してみます。
var('H', positive = True) # 数値を入れてたので初期化
display(L(theta, H))
var('s', positive = True) # s > 0 のみを考える
def Ls(s):
return sqrt(1-s**2)*(s + sqrt(2*H + s**2))
Ls(s)
dLs
$\displaystyle \equiv\frac{d L(s)}{d s}$
dLs = diff(Ls(s), s)
dLs = dLs.simplify()
dLs
dLs = 0
を s
について解く。
sols = solve(dLs, s)
sols
ということで,最大水平到達距離となる $s_{\rm m} = \sin\theta_{\rm m} = $ sols[0]
は
\begin{eqnarray}
s_{\rm m} = \sin\theta_{\rm m} &=& \frac{1}{\sqrt{2 (1 + H)}} \\
\therefore\ \ \theta_{\rm m} &=& \arcsin\left(\frac{1}{\sqrt{2 (1 + H)}}\right)
\end{eqnarray}
最大水平到達距離
そのときの最大水平到達距離 $L_{\rm m}$ は…
sm = sols[0]
Ls(sm).factor()
簡単化してくれないなぁ…
解法2:最大水平到達距離となる $\tan\theta_{\rm m}$ を解析的に求める
水平到達距離 $L(\theta)$ を $\tan \theta$ を使って表してみます。
var('H', positive = True)
L(theta, H)
人力で整理してみよう。
\begin{eqnarray}
L(\theta) &=&\left(\sqrt{2 H + \sin^{2}{\left(\theta \right)}} + \sin{\left(\theta \right)}\right) \cos{\left(\theta \right)} \\
&=&
\left(\sqrt{\frac{2H}{\cos^2\theta}+ \frac{\sin^2\theta}{\cos^2\theta}} + \frac{\sin\theta}{\cos\theta}
\right) \cos^2\theta \\
&=& \left(\sqrt{2H \left(1+\tan^2\theta\right)+ \tan^2\theta} + \tan\theta
\right) \frac{1}{1+\tan^2\theta} \\
&=& \left(\sqrt{2H \left(1+t^2\right)+ t^2} + t
\right) \frac{1}{1+t^2} \\
&=& \frac{t + \sqrt{2H + t^2 (1 + 2H)}}{1+t^2} \\
t &\equiv& \tan\theta
\end{eqnarray}
水平到達距離を変数 $t \equiv \tan\theta$ の関数として定義します。
var('t H', positive = True) # positive = True がキモ
def Lt(t):
return (t+sqrt((1+2*H)*t**2 + 2*H))/(1 + t**2)
Lt(t)
$\displaystyle \frac{d L_{t}}{d t} = 0$ となる $t$ を求めます。
dLt = diff(Lt(t), t)
sols = solve(dLt, t)
sols
ということで,最大水平到達距離となる $t_{\rm m} = \tan\theta_{\rm m} = $ sols[0]
は
\begin{eqnarray}
t_{\rm m} = \tan\theta_{\rm m} &=& \frac{1}{\sqrt{1 + 2 H}} \\
\therefore\ \ \theta_{\rm m} &=& \arctan\left(\frac{1}{\sqrt{1 + 2 H}}\right)
\end{eqnarray}
最大水平到達距離
$\displaystyle t = t_{\rm m} \equiv \frac{1}{\sqrt{1+2H}}$ のとき極値(最大値)を持つことがわかり,そのときの最大水平到達距離 $L_{\rm m} \equiv L(t_{\rm m}, H)$ は…
tm = sols[0]
Lt(tm).simplify()
解法3:陰関数定理を使って最大水平到達距離を求める
あらためて,($H$ は変数ではなくパラメータとして)
無次元化された解 $X(T, \theta), Y(T, \theta)$ の定義を書き出すと…
\begin{eqnarray}
X(T, \theta) &\equiv& \cos\theta\cdot T \\
Y(T, \theta)&\equiv& H+ \sin\theta\cdot T -\frac{1}{2} T^2 \\
\end{eqnarray}
var('H', positive = True)
def X(T, theta):
return cos(theta) * T
def Y(T, theta):
return H + sin(theta)*T - T**2/2
滞空時間 $T_1$
滞空時間 $T_1\ (> 0)$ は以下の式を満たす。
$$Y(T_1, \theta) = 0$$
これから,$T_1$ は $\theta$ の陰関数とみることができる。(必ずしも陽関数として解く必要はない。)
$$T_1 = T_1(\theta)$$
水平到達距離 $L(\theta,)$ は,滞空時間 $T_1$ の間に水平方向に進む距離であるから
$$L(\theta) \equiv X(T_1(\theta), \theta)$$
であるから,$L(\theta)$ が最大となる角度 $\theta$ を求める問題は,
以下の連立方程式を $\theta$ について解く問題である。
\begin{eqnarray}
Y(T_1(\theta), \theta) &=& 0 \tag{1}\\
\frac{d}{d\theta} L(\theta) &=& \frac{d}{d\theta} X(T_1(\theta), \theta) \\
&=&
\frac{\partial X}{\partial T_1} \frac{d T_1}{d\theta} + \frac{\partial X}{\partial \theta} = 0
\tag{2}
\end{eqnarray}
(2) 式で必要となる $\displaystyle \frac{d T_1}{d\theta}$ については,以下のように (1) 式の微分をとって,陰関数定理から求めることができる。
\begin{eqnarray}
Y(T_1(\theta), \theta) &=& 0 \\
\therefore\ \ \frac{dY}{d\theta} &=& \frac{\partial Y}{\partial T_1} \frac{d T_1}{d\theta} +
\frac{\partial Y}{\partial \theta} = 0 \\
\therefore\ \ \frac{d T_1}{d\theta} &=& -\frac{\frac{\partial Y}{\partial \theta}}{\frac{\partial Y}{\partial T_1}}
\end{eqnarray}
これを SymPy でやると…
# T1 を theta の(陰)関数として宣言
T1 = Function('T1')(theta)
dY = diff(Y(T1, theta), theta)
sols = solve(dY, diff(T1, theta))
sols
# 解は 1 個だけなので
dT1 = sols[0]
Eq(Derivative(T1, theta), dT1)
水平到達距離 $L$
滞空時間 $T_1(\theta)$ の間に水平方向に進む距離は
$$L(\theta) = X(T_1(\theta), \theta)$$
def L(theta):
return X(T1, theta)
L(theta)
$L$ が最大となる角度を求める
$\displaystyle \frac{dL}{d\theta} = 0$ となる角度を求める。陰関数定理の結果を .subs()
で代入して…
dL = diff(L(theta), theta).subs(diff(T1, theta), dT1)
dL = simplify(dL)
display(dL)
Y(T1, theta)
$L$ が最大となる角度は,連立方程式
\begin{eqnarray}
Y(T_1(\theta), \theta) &=& H + T_1(\theta)\,\sin\theta -\frac{1}{2} T^2_1(\theta)= 0 \\
\frac{d}{d\theta} L(\theta) &=& \frac{\left(-T_{1}{\left(\theta \right)} \sin{\left(\theta \right)} + 1\right) T_{1}{\left(\theta \right)}}{T_{1}{\left(\theta \right)} -\sin{\left(\theta \right)}}=0
\end{eqnarray}
を,($\theta$ そのものではなく)$\sin\theta$ と $T_1(\theta)$ について解いて求めることができる。
$\displaystyle 0 < \theta \leq \frac{\pi}{2}$ であるから $\sin\theta > 0$ である。
$ \sin\theta \Rightarrow s$ と置き換えて変数 $s$ に対して $s > 0$ という条件をつけて解いてみる。
var('s', positive = True)
sols2 = solve([Y(T1, theta).subs(sin(theta), s),
dL.subs(sin(theta), s)], [s, T1])
sols2
最大水平到達距離となる $\sin\theta_{\rm m}$ とそのときの滞空時間 $T_1$ は,
$s_m = \sin\theta_m = $ sols2[0][0]
,$T_1 = $ sols2[0][1]
であり,
\begin{eqnarray}
s_m = \sin\theta_{\rm m} &=& \frac{1}{\sqrt{2 (1+H)}} \\
\therefore\ \ \theta_{\rm m} &=& \arcsin \left( \frac{1}{\sqrt{2 (1+H)}}\right) \\
T_1 &=& \sqrt{2 (1 + H)}
\end{eqnarray}
thetam = asin(sols2[0][0])
display(thetam)
T1m = sols2[0][1]
display(T1m)
そのときの最大水平到達距離 $L_{\rm m} = X(T_1(\theta_{\rm m}), \theta_{\rm m})$ は…
X(T1m, thetam).simplify()