Matplotlibでグラフを作図-04 バネの伸縮の動きをグラフで表現する

Matplotlibでグラフを作図-04 バネの伸縮の動きをグラフで表現する

モモノキ&ナノネと一緒にMatplotlibを使ってグラフの作図方法を学習していきます。

モモノキ&ナノネと一緒にMatplotlibでバネの伸縮をアニメーションで実行してみよう




Matplotlibでバネ(もどき)を作図して単振動の動きをアニメーションで表現

ナノネ、今回はMatplotlibを使って伸縮するバネを作図してみよう。

びよーんびよーんする渦巻バネ?

そうだよ。でも、バネを厳密に描くのは難しいから簡略化したギザギザでバネを表現してみよう。

ただのギザギザだったら、Matplotlibでプロットできそう。

ナノネ、横に寝かせたバネ(もどき)を1個グラフにプロットしてみて。あとでアニメーションの元図に使うからバネの大きさは変数化しておいてね。

In [1]:
# Jupyter Notebook上でインタラクティブに表示
%matplotlib nbagg 

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
In [2]:
BANE_div = 50 # バネのギザギザ数
BANE_length = 5 # 伸縮中間のバネ長さ:x方向
BANE_diameater = 1 # バネの径(山):y方向

x = np.linspace(0, BANE_length, BANE_div)

y = np.zeros(BANE_div)
y[::2] = BANE_diameater # ギザギザの山部

fig, ax = plt.subplots(figsize=(8,3))
fig.patch.set_alpha(1.0)
ax.set_xlim(0, BANE_length*2)
ax.set_ylim(0, BANE_diameater*1.5)
plt.plot(x, y, alpha=0.7)
Out[2]:
[<matplotlib.lines.Line2D at 0x204f5605a58>]

こんなんでいい?ただのギザギザのグラフでバネに見えないけど。

OK、大丈夫だよ。アニメーションで伸縮させればバネっぽく見えるはずだよ。

モモノキ、伸縮のびよーんびよーんはどうやってやるの?

最初に描いたバネの右端(長さ)を伸縮の中心にして、バネが伸びた状態から縮んだ状態を連続的に作図できれば伸縮を表現できそうだね。

たとえば、bane_phaseという変数を一つ用意して-1が最大に縮んだとき、+1が最大に伸びたときと定義する。そうすると下のグラフように任意の状態のバネの長さを計算で作図することができるよ。

In [3]:
BANE_div = 50 # バネのギザギザ数
BANE_length = 5 # 伸縮中間のバネ長さ:x方向
BANE_diameater = 1 # バネの径(山):y方向
BANE_raito = 0.7 # バネの伸縮度合い

y = np.zeros(BANE_div)
y[::2] = BANE_diameater # ギザギザの山部

def get_add_length(bane_phase):
    return BANE_length * BANE_raito * bane_phase

# バネの伸縮パータン(作図確認用)
bane_phases = np.array([0, 0.5, 1, 0.5, 0, -0.5, -1, -0.5, 0]) # -1 ~ +1(縮み最大、伸び最大)

# グラフ描画
fig = plt.figure(figsize=(6, 10))

for i, bane_phase in enumerate(bane_phases):

    add_length = get_add_length(bane_phase)
    x = np.linspace(0, BANE_length + add_length, BANE_div)

    fig.add_subplot(len(bane_phases), 1, i+1)
    plt.title('bane phase={}'.format(bane_phase), fontsize=10)
    plt.xlim(0, BANE_length*2)
    plt.ylim(0, BANE_diameater*1.5)
    plt.tick_params(labelsize=8)
    plt.plot(x, y, alpha=0.7)

plt.tight_layout()

なるほど、イメージできたかも。このグラフを連続的にアニメーション表示すれば、バネのびよーんびよーんが実現できるね。

y軸の値はいつも一緒だから、x軸のデータだけ更新すればよさそう。

単振動のバネの動きは正弦波で表現できるから、コードは前に作った正弦波アニメーションと同じように書けばうまく動くはずだよ。

コピペで少しコードを変更すればできそう。FuncAnimationを使ってコードを書いてみる。

In [4]:
# Jupyter Notebook上でインタラクティブに表示
%matplotlib nbagg 

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

BANE_div = 40 # バネのギザギザ数
BANE_length = 5 # 伸縮中間のバネ長さ:x方向
BANE_diameater = 1 # バネの径(山):y方向
BANE_raito = 0.7 # バネの伸縮度合い

y = np.zeros(BANE_div)
y[::2] = BANE_diameater # ギザギザの山部

# バネ長さの増減量
def get_add_length(bane_phase):
    return BANE_length * BANE_raito * bane_phase

# グラフオブジェクト作成
fig, ax = plt.subplots(figsize=(8,3))
fig.patch.set_alpha(1.0) # figure不透明

ax.set_xlim(0, BANE_length*2)
ax.set_ylim(0, BANE_diameater*1.5)

add_length = get_add_length(0)
x = np.linspace(0, BANE_length + add_length, BANE_div)
bane_plot, = ax.plot(x, y, linewidth=2, c="blue", alpha=0.7) # バネをプロット(左辺の末カンマはunpack)

# アニメーション初期化
def init():
    # initialize :nothing
    return fig,

# アニメーション更新関数
def animate(i):
    bane_phase = np.sin(2 * np.pi * (i / frames)) # -1 ~ 1(正弦波でバネの伸び縮みに対応)
    add_length = get_add_length(bane_phase) # バネ長さ増減量の計算 
    x = np.linspace(0, BANE_length + add_length, BANE_div) # x軸データを再計算
    bane_plot.set_xdata(x) # バネのx軸データを再設定
    return fig,

frames = 36 # アニメーションのフレーム数

# アニメーション実行
ani = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=frames, interval=100, blit=True)

# plt.show()

バネ(もどき)できた!たしかに動かすとバネっぽく見える。

In [5]:
# gifファイルに出力
ani.save("move_bane.gif", writer='imagemagick') # gifファイル出力にImageMagick利用

gif画像はこんな感じ。びよーんびよーん動いてる。

バネ(もどき)うまくいったね!

今回は単振動だけど、きちんと計算すればバネ減衰も表現できそうだよ。

アニメーションを作る理屈は一緒だから、いろいろな作図に応用できそうだ。

またね!







スポンサーリンク

0 件のコメント :

コメントを投稿