Python ボールの投げ上げ(斜方投射)シミュレーション

ボールを斜めに投げ上げたときの運動をグラフに描画する

はじめに

 「高等学校 情報Ⅰ(数研出版)」の教科書にボールの投げ上げのシミュレーションをするプログラムの例が記載されていました。そのプログラムでは,ボールの位置を(x, y)の座標として画面に表示していましたが,グラフ描画のライブラリ(matplotlib)を用いてグラフを出力したいと思います。
 
 
(2022-10-14 追記)
 教科書のプログラム例を変更する場合は以下のようになります。(赤字が追加箇所)


(2022-10-14 追記ここまで)

座標出力からグラフデータ作成への変更

 座標計算の方法を教科書の論理モデルから変更し,物理基礎の「重力による運動」で学習する斜方投射の式で例を示します。

座標を文字で画面出力するプログラム

import math

kmh = 60                # 初速度 km/h
v0 = kmh*1000/3600      # 初速度 m/s
deg = 45                # 投げ上げ角度
rad = math.radians(deg) # ラジアンに変換

g = 9.8                 # 重力加速度
t = 0                   # 経過時間 s
while True:
    x = v0*math.cos(rad)*t
    y = v0*math.sin(rad)*t - g/2*(t**2)
    print("(",x,",",y,")")
    if y < 0:
        break
    t = t + 0.1

実行結果

( 0.0 , 0.0 )
( 1.1785113019775795 , 1.1295113019775795 )
( 2.357022603955159 , 2.1610226039551588 )
 :中略
( 28.284271247461916 , 0.06027124746189472 )
( 29.462782549439495 , -1.1622174505605294 )

 初速度 60km/h,初速度の水平面に対する角度が45°のとき,空気抵抗を無視した計算です。コード中の「t**2」はtの2乗(t*tと同じ)です。
 結果を見るとx座標が29mまで行きました。スポーツテストのハンドボール投げで時速60キロで投げたときに30m近い記録になるのでしょう。(y座標のマイナスは肩の高さの分と考えておきます)

グラフに描画するコードの追加

 プログラムにグラフ描画の命令を追加します。(コード例の★印の次の行)

import math
# ★1 ライブラリの追加
import matplotlib.pyplot as plt

kmh = 60                # 初速度 km/h
v0 = kmh*1000/3600      # 初速度 m/s
deg = 45                # 投げ上げ角度
rad = math.radians(deg) # ラジアンに変換

g = 9.8                 # 重力加速度
t = 0                   # 経過時間 s
while True:
    x = v0*math.cos(rad)*t
    y = v0*math.sin(rad)*t - g/2*(t**2)
    print("(",x,",",y,")")
    # ★2 グラフにデータ追加
    plt.plot(x,y,"bo")
    if y < 0:
        break
    t = t + 0.1

# ★3 グラフを表示
plt.show()

実行結果

  • ★1 「import matplotlib.pyplot as plt」は使いたいモジュール(matplotlib の pyplot)を plt という識別子でプログラムに記述するという宣言です。モジュールがロードされるので処理に時間がかかるかもしれません。また,ここでエラーが出る場合はMatplotlibがインストールできているか確認してください。(Python グラフ描画のプログラム実行でエラー - 勉強ボックス管理者ブログ
  • ★2 「plt.plot(x,y,"bo")」でxとyをプロットするデータに追加しています。"bo"はマーカーの色と形の指定(blue circle markers)です。
  • ★3 「plt.show()」はグラフを表示させる命令です。


マーカーの色と形の指定の例(matplotlib.pyplot.plotリファレンスから一部抜粋)

b blue .
g green , ピクセル
r red o
c cyan ^ 三角
m magenta s 四角
y yellow *
k black d 小ダイヤ
w white p 五角形



 x軸とy軸の大きさが合っていないため,45度の角度になっていないことが気になる人は,グラフ表示前にy軸の表示範囲設定とアスペクト比を調整する命令を追加してみてください。

# ★3 グラフを表示
plt.ylim(-2,20)
plt.gca().set_aspect("equal", adjustable="box")
plt.show()

アスペクト比を調整した例





※ 以下は参考までにグラフ表示でできることの紹介

参考 線でつないだグラフ

xy座標を配列データで渡すと放物線を表示できます。

import math
import matplotlib.pyplot as plt

kmh = 60                # 初速度 km/h
v0 = kmh*1000/3600      # 初速度 m/s
deg = 45                # 投げ上げ角度
rad = math.radians(deg) # ラジアンに変換

g = 9.8                 # 重力加速度

xdata = []              # データ用配列
ydata = []
t = 0                   # 経過時間 s
while True:
    x = v0*math.cos(rad)*t
    y = v0*math.sin(rad)*t - g/2*(t**2)
    print("(",x,",",y,")")
    xdata.append(x)     # 配列にデータ追加
    ydata.append(y)
    if y < 0:
        break
    t = t + 0.1

# グラフにデータ追加
plt.plot(xdata,ydata,color="red",marker="o")
# グラフを表示
plt.show()

実行結果

参考 投げ上げる角度を変えてみる

 グラフ用のデータ作成部分を関数にして,投げ上げる角度を変えて複数回実行してみました。
 pyplot の scatter() を利用して散布図にしています。

import matplotlib.pyplot as plt
import math

plt.rcParams["font.family"] = "Yu Gothic"

g = 9.8     # 重力加速度

# グラフデータ作成関数
#   kmh:初速度km/h deg:角度 mycol:色 mark:マーカー
def myplot(kmh,deg,mycol,mark):
    v0 = kmh*1000/3600  # 初速度 m/s
    rad = math.radians(deg)
    xdata = []
    ydata = []
    t = 0       # 時間
    while True:
        x = v0*math.cos(rad)*t
        y = v0*math.sin(rad)*t - 0.5*g*(t**2)
        #print(x,y)
        xdata.append(x)
        ydata.append(y)
        if y < 0:
            break
        t = t + 0.1

    # 凡例用の文字列を作成
    lbl_str = "初速度"+str(kmh)+"km/h "+ str(deg)+"度"
    # 散布図のデータを設定
    plt.scatter(xdata,ydata,color=mycol,label=lbl_str,marker=mark)
    return

# メイン処理
# 関数呼び出し
kmh = 60    # 初速度 km/h
myplot(kmh,90,"gray",".")
myplot(kmh,70,"orange","p")
myplot(kmh,60,"blue","*")
myplot(kmh,45,"red","o")
myplot(kmh,35,"purple","s")

# グラフ設定
plt.axhline(0, color="gray", zorder=0.5)
plt.title("斜方投射(空気抵抗なし)")
plt.xlabel("飛距離(m)")
plt.ylabel("高さ(m)")
plt.legend()    # 凡例表示
plt.gca().set_aspect("equal", adjustable="box") #アスペクト比調整

# グラフ表示
plt.show()

実行結果

 

関連記事

kinutani.hateblo.jp