紙蜻蜓的受風面積與紙蜻蜓落地時間的關係 #2 [可視化使用VPython]

作品的網址在這裡:(https://www.glowscript.org/#/user/DCtime/folder/MyPrograms/program/MyFirstVPython

一開始,我是使用 Python + Matplotlib 畫出有空氣阻力落體的x-t圖和v-t圖。( Python + Matplotlib 設計流程請看 https://dctimelearninghowtocode.blogspot.com/2021/01/python-matplotlib.html )老師看完我的專案後,提議可以使用VPython將得到的數據用3D模型"直接看到"東西在移動,不只以圖表呈現。因此我就開始製作這個專案。

一、學習VPython

學習國英數,課本、習作、講義、學校授課配合政策很輕易的成為我們學生的學習資源,但學校沒有自動提供VPython的學習資源,因此學習VPython的第一步,就是尋找可使用的學習資源。老師在推薦VPython,有提到可以去Google搜尋 "VPython石明豐教授" 關鍵字。沿著這個線索,找到了一個NTU做的網站(https://tcjd71.wixsite.com/vpython),裡面有許多教學文章提供下載。再搭配一些Youtube的資源,和先前學的一些Python的知識,不到一天就把我需要的知識全部學完了。

VPython是一個數據可視化的一個工具,簡單來說,就是一個3D動畫製作工具,而非一個物理模擬工具。VPython只提供圖形渲染的工具,移動只能透過直接設定位置的方式,而不是製造環境去使物件動起來。

二、製作物理動畫

正式使用VPython前,要先設定VPython的開發環境。我試著用終端機下載VPython,但她總是跳出一些奇怪的錯誤訊息,可能是因為我的設備太老舊,導致有不相容的問題。為了解決這個問題,我將錯誤碼貼上Google,希望可以找到解決方法。就在此時,我找到了GlowScript,一個可在線上開發的VPython編輯器。雖然沒有像Pycharm那麼好用,但至少解決環境的問題。

(圖一:GlowScript 主頁面, 資料來源:螢幕截圖)

接下來,就是產生出物體有空氣阻力之落體之相關數據(速度與位移)。在上一篇的文章,我們已經完成這個步驟,Ctrl+C + Ctrl+V,這步驟就完成了。( Python + Matplotlib 設計流程請看 https://dctimelearninghowtocode.blogspot.com/2021/01/python-matplotlib.html )

有了數據,接著使用VPython將冷冰冰的數據轉化成3D動畫。在此模擬中,我做兩個長方體,一個受風面積大,另一個受風面積小。這樣就可以比較兩者的掉落情形。

(圖二:一個長方體掉落,資料來源:螢幕截圖)

但是只有兩個物體在掉落,只能看出誰掉得比較快,無法看出掉落的速度等等。因此,要為這兩個長方體畫出 x-t 圖和 v-t 圖,才能精確地看出掉落的過程。

另外,為了確定兩個物體的運動方式是正確的,在此再製作一個圓球,代表自由落體,照理來講,如果有空氣阻力的物體掉落的速度比自由落體快,那就代表此程式有問題,需要修正。

(圖三:完成的專案,資料來源:螢幕截圖)

經過多次的修正,以上就是最後完成的樣子。

從這個動畫可以看到,受風面積越大,最終掉落速度越小,掉落的距離越短。而且有受到空氣阻力的物體,最後會呈現一個趨近等速的狀態,自由落體則是以等加速度落下。


以下為此專案的程式碼:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
GlowScript 3.0 VPython

# ---animation settings---
time_for_a_loop = 1.0 / 30.0;
max_frame = 10000;
time = 0

# ---environment settings---
drag_coefficient = 0.4
air_density = 1.225
gravity = 9.8

# ---box1 status---
length_1 = 2
width_1 = 2
height_1 = 1

mass_1 = 10
speed_1 = 0
position_1 = vector(0, 0, 0)

# ---box2 status---
length_2 = 1
width_2 = 1
height_2 = 1

mass_2 = 10
speed_2 = 0
position_2 = vector(5, 0, 0)

# ---free-fall ball status---
mass_3 = 10
speed_3 = 0
position_3 =vector(10, 0, 0)

# --- small def---
def drag_force(current_speed, affected_area, drag_coefficient = 0.4, air_density = 1.225):
    '''cal the air drag force'''
    #print("drag_force:", drag_coefficient * air_density * affected_area * current_speed * current_speed / 2)
    try:
        return drag_coefficient * air_density * affected_area * current_speed * current_speed / 2
    except ZeroDivisionError:
        return 0
    
def gravity_force(mass, gravity = 9.8):
    '''cal the gravity affected on the item'''
    #print("gravity_force:", mass * gravity)
    return mass * gravity

def final_acceleration(gravity_force, drag_force, mass):
    '''cal the acceleration with g force and drag force and mass effected'''
    #print("final_acceleration:", (gravity_force - drag_force) / mass)
    return (gravity_force - drag_force) / mass

def delta_time_move(acceleration, min_time, current_speed):
    '''cal the delta time movement'''
    #print("delta_time_move:", current_speed * min_time + acceleration * min_time * min_time / 2)
    return current_speed * min_time + acceleration * min_time * min_time / 2

def delta_time_speed(acceleration, min_time, current_speed):
    '''cal the delta time speed (final speed)'''
    #print("delta_time_speed:", current_speed + acceleration * min_time)
    return current_speed + acceleration * min_time

# --- main def ---
def cal_speed(mass, gravity, current_speed, affected_area, drag_coefficient, air_density, min_time):
    return delta_time_speed(final_acceleration(gravity_force(mass, gravity), drag_force(current_speed, affected_area, drag_coefficient, air_density), mass), min_time, current_speed)

def cal_position(now_position, mass, gravity, current_speed, affected_area, drag_coefficient, air_density, min_time):
    change_vector = vector(0, -1 * delta_time_move(final_acceleration(gravity_force(mass, gravity), drag_force(current_speed, affected_area, drag_coefficient, air_density), mass), min_time, current_speed), 0)
    return now_position + change_vector
    
# make boxes
box1 = box(pos=position_1, length=length_1, width=width_1, height=height_1, make_trail=True, trail_type="points", color=color.red)
box2 = box(pos=position_2, length=length_2, width=width_2, height=height_2, make_trail=True, trail_type="points", color=color.blue)
sphere3 = sphere(pos=position_3, color=color.yellow, make_trail=True, trail_type="points", color=color.yellow)

# make speed graph window
speed_graph = graph(xtitle="Time", ytitle="speed")
speed_curve_1 = gcurve(color=color.red, label="Area = " + str(width_1 * length_1))
speed_curve_2 = gcurve(color=color.blue, label="Area = " + str(width_2 * length_2))
speed_curve_3 = gcurve(color=color.yellow, label="Free-fall")

# make position graph window
pos_graph = graph(xtitle="Time", ytitle="position")
pos_curve_1 = gcurve(color=color.red, label="Area = " + str(width_1 * length_1))
pos_curve_2 = gcurve(color=color.blue, label="Area = " + str(width_2 * length_2))
pos_curve_3 = gcurve(color=color.yellow, label="Free-fall")

for i in range(max_frame):
    rate(1.0 / time_for_a_loop)
    
    # cal time
    time = time + time_for_a_loop
    
    # cal speed
    speed_1 = cal_speed(mass_1, gravity, speed_1, length_1 * width_1, drag_coefficient, air_density, time_for_a_loop)
    speed_2 = cal_speed(mass_2, gravity, speed_2, length_2 * width_2, drag_coefficient, air_density, time_for_a_loop)
    speed_3 = cal_speed(mass_3, gravity, speed_3, 0, drag_coefficient, air_density, time_for_a_loop)
    speed_curve_1.plot(time, speed_1)
    speed_curve_2.plot(time, speed_2)
    speed_curve_3.plot(time, speed_3)
    
    # cal pos
    box1.pos = cal_position(box1.pos, mass_1, gravity, speed_1, length_1 * width_1, drag_coefficient, air_density, time_for_a_loop)
    box2.pos = cal_position(box2.pos, mass_2, gravity, speed_2, length_2 * width_2, drag_coefficient, air_density, time_for_a_loop)
    sphere3.pos = cal_position(sphere3.pos, mass_3, gravity, speed_3, 0, drag_coefficient, air_density, time_for_a_loop)
    pos_curve_1.plot(time, box1.pos.y * -1.0)
    pos_curve_2.plot(time, box2.pos.y * -1.0)
    pos_curve_3.plot(time, sphere3.pos.y * -1.0)
    


三、用途

回到最初做得那受風面積與落地時間之關係的實驗:我們預測受風面積越大,落地時間越久,並從數據得知紙蜻蜓的受風面積與落地時間呈現正相關,但不是正比或一次函數的關係,此數據符合我們之預測。但我們試著去使用公式去解釋為何他會有此行為時,因數學程度不足,所以使用此程式去模擬此公式的現象:用VPython去吸引同學目光,藉由程式模擬,在不使用高深數學的狀況下使同學信服。最後,因為一些原因,並沒有將此專案搬上講臺,有些可惜。

四、製作的過程,我學到...

(一)電腦是計算機

小時候接觸電腦時,我一直認為電腦只是個遊樂器材,一個無限遊戲的儲藏庫。但隨著我電腦知識的累積,知道電腦的歷史,開始了解電腦是一個高級計算機,一個可以用撰寫程式或圖形介面使其計算出我要的答案。玩遊戲也是藉由其快速的計算達成我們的目的(螢幕投射出來,使我們獲得成就感和使我們開心的畫面)。

(二)電腦可應用在科學研究裡

最近所上的探究與實作課程,使我們可以體驗和學習科學研究的方法。電腦可以在提出預測時使用,也可以用來分析數據,彙整成圖表,更可以在實驗完成,建立模型後,利用程式去跑,比對預測是否符合。


















留言

這個網誌中的熱門文章

Zerojudge 基礎題庫a004 文文的求婚 (Python)

紙蜻蜓的受風面積與紙蜻蜓落地時間的關係 #1 [實驗歷程與Python Matplotlib]

Zerojudge 基礎題庫a013 羅馬數字 (Python)