# 使用 For 迴圈計算水平抛射資料 > 作者:王一哲 > 日期:2018/3/28 <br /> 在前一篇文章〈[水平抛射](https://hackmd.io/s/Hy4UoZfMm)〉中,我們寫了一個模擬水平抛射運動的程式,再手動修高度 h 計算飛行時間 t 及水平射程 R。但是手動修改是很麻煩的,如果能多寫一個 for 迴圈,就能讓程式幫我們自動代入不同的 h 並將資料存成文字檔。以下共有兩個程式: - 程式5-4:水平抛射, 改變h, 記錄R ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/5-4projectileh)) - 程式5-5:由程式5-4修改, 用for迴圈改變h, 記錄t、R <br /> ## 程式 5-4:水平抛射, 改變h, 記錄R ([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/05.%E6%B0%B4%E5%B9%B3%E6%8A%9B%E5%B0%84/5-4_projectile_h.py)) ```python= """ VPython教學: 5-4.水平抛射, 改變h, 記錄R Ver. 1: 2018/2/19 Ver. 2: 2019/9/6 作者: 王一哲 """ from vpython import * """ 1. 參數設定, 設定變數及初始值 """ size = 1 # 小球半徑 v0 = 5 # 小球水平初速 h = 15 # 小球離地高度5, 10, 15, 20, 25, 30, 35, 40, 45, 50 L = 50 # 地板長度 g = 9.8 # 重力加速度 9.8 m/s^2 t = 0 # 時間 dt = 0.001 # 時間間隔 """ 2. 畫面設定 """ scene = canvas(title="Projectile", width=800, height=600, x=0, y=0, center=vec(0, h/2, 0), background=vec(0, 0.6, 0.6)) floor = box(pos=vec(0, -size, 0), size=vec(L, 0.01, 10), texture=textures.metal) ball = sphere(pos=vec(-L/2, h, 0), radius=size, texture=textures.wood, make_trail=True, v=vec(v0, 0, 0), a=vec(0, -g, 0)) """ 3. 物體運動部分, 小球觸地時停止 """ while ball.pos.y - floor.pos.y > size + 0.5*floor.height: rate(1000) ball.v += ball.a*dt ball.pos += ball.v*dt t += dt print(t, ball.pos.x + L/2) ``` <br /> 這個程式是程式 5-1 的簡化版,由於我只想算出飛行時間 t 及水平射程 R,小球碰到地板後就可以停止動畫,因此 while 迴圈中設定的條件為 ```python ball.pos.y - floor.pos.y > size + 0.5*floor.height ``` 手動修改 h,分別以 5、10、15、……50代入,再將計算得到的 t 和 R 手動輸入 SciDAVis 並作圖,這個作法雖然可行,但是相當費時。 <br /> ### 數據處理原則 通常我們只用 XY 散布圖,而且只畫數據點,不會將數據點連線,畫完數據點之後再加上擬合直線式曲線。如果應變數與自變數的關係圖為曲線,還要想辦法化為直線,最後的成果一定要用線性擬合,還要解釋最接近直線的斜率、y軸截距的物理意義,要求相當嚴謹。使用 SciDAVis 畫 XY 散布圖的方法請參考另一篇文章〈[SciDAVis繪圖:XY散布圖](https://hackmd.io/s/HkNHsKFzN)〉。 先畫 t - h 關係圖,由於圖形看起來像是二次曲線,就試著用多項式擬合,最高次方為二次,結果為 $$ y = 0.656 + 0.070 x - 0.00058 x^2$$ 但這畢竟是猜測的結果,不一定正確。比較好的作法是取 \\( \log t \\) 、\\( \log h \\),畫 \\( \log t - \log h \\) 關係圖,畫出來的圖為斜直線,最接近直線為 $$ y = 0.500 x - 0.345 $$ 由斜率可以猜測 \\( t \propto \sqrt h \\)。理論上 $$ h = \frac{1}{2}gt^2 ~\Rightarrow~ \log h = \log \left( \frac{1}{2}gt^2 \right) = \log \left( \frac{1}{2}g \right) + \log t^2 = 2 \log t + c$$ $$ \log t = 0.5 \log h + c' $$ 為了驗證這個猜測,新增一欄數據計算 \\( \sqrt h \\),畫 \\( t - \sqrt h \\) 關係圖,畫出來的圖為斜直線,最接近直線為 $$ y = 0.45178 x - 0.00034 $$ 理論上 $$ h = \frac{1}{2}gt^2 ~\Rightarrow~ t = \sqrt{\frac{2h}{g}} ~\Rightarrow~ slope = \sqrt{\frac{2}{g}} \approx 0.45175 $$ 理論值與模擬得到的值很接近,可以確定程式應該沒有問題。 <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://i.imgur.com/SkGwcNE.png"> <div style="text-align:center">於 SciDAVis 手動輸入數據</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/U1zNT9t.jpg"> <div style="text-align:center">t - h 關係圖</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://i.imgur.com/Emntfzv.jpg"> <div style="text-align:center">log(t) - log(h) 關係圖</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://i.imgur.com/xR0iB99.jpg"> <div style="text-align:center">t - h<sup>1/2</sup> 關係圖</div> <br /><br /> ## 程式 5-5:水平抛射, 用for迴圈改變h, 記錄t、R ([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/05.%E6%B0%B4%E5%B9%B3%E6%8A%9B%E5%B0%84/5-5_projectile_loop.py)) ```python= """ VPython教學: 5-5.水平抛射, 用for迴圈改變h, 記錄t、R Ver. 1: 2018/3/21 Ver. 2: 2018/6/13 加上用 matplotlib.pyplot 繪圖的部分 Ver. 3: 2019/10/10 改用 with 開啟檔案, 上課用的版本 作者: 王一哲 """ from vpython import * import matplotlib.pyplot as plt """ 1. 參數設定, 設定變數及初始值 """ size = 1 # 小球半徑 v0 = 5 # 小球水平初速 L = 50 # 地板長度 g = 9.8 # 重力加速度 9.8 m/s^2 dt = 0.001 # 時間間隔 data_y, data_x = [], [] # 儲存繪圖資料的串列 """ 2. 畫面設定 """ scene = canvas(title="Projectile", width=800, height=600, x=0, y=0, center=vec(0, 5, 0), background=vec(0, 0.6, 0.6)) floor = box(pos=vec(0, -size, 0), size=vec(L, 0.01, 10), texture=textures.metal) # 開啟檔案 5-5_data.csv, 屬性為寫入, 先寫入欄位的標題 with open("5-5_data.csv", "w", encoding="UTF-8") as file: file.write("h(m), t(s), R(m)\n") """ 3. 主程式 """ def main(h): t = 0 ball = sphere(pos=vec(-L/2, h, 0), radius=size, texture=textures.wood, make_trail=True, v=vec(v0, 0, 0), a=vec(0, -g, 0)) while ball.pos.y - floor.pos.y > size + 0.5*floor.height: rate(1000) ball.v += ball.a*dt ball.pos += ball.v*dt t += dt return t, ball.pos.x + L/2 """ 4. 用for迴圈改變h, 計算t、R, 寫入檔案 """ for h in range(5, 51, 1): t, r = main(h) with open("5-5_data.csv", "a", encoding="UTF-8") as file: file.write(str(h) + "," + str(t) + "," + str(r) + "\n") data_y.append(t) data_x.append(h) # 用 matplotlib.pyplot 繪圖 plt.figure(figsize=(8, 6), dpi=100) # 設定圖片尺寸 plt.xlabel(r'$h ~\mathrm{(m)}$', fontsize=16) # 設定坐標軸標籤 plt.ylabel(r'$t ~\mathrm{(s)}$', fontsize=16) plt.xticks(fontsize=12) # 設定坐標軸數字格式 plt.yticks(fontsize=12) plt.grid(color='red', linestyle='--', linewidth=1) # 設定格線顏色、種類、寬度 plt.plot(data_x, data_y, marker='o', markerfacecolor='blue', markersize=8, color='skyblue', linewidth=3) # 繪圖並設定線條顏色、寬度 plt.savefig('t-h_plot.svg') # 儲存圖片 plt.savefig('t-h_plot.png') plt.show() # 顯示圖片 ``` <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://i.imgur.com/OyULerj.png"> <div style="text-align:center">程式 5-5 模擬結果畫面截圖</div> <br /> **程式 5-5 當中運用了串列 (list) 及自訂函式 (def) 兩個新的物件,詳細的說明請參考另外兩篇講義〈[串列](https://hackmd.io/@yizhewang/H1rSL1W8K)〉、〈[自訂函式](https://hackmd.io/@yizhewang/S1awsJ-IY)〉** ,以下先說明與程式 5-4 的不同之處。 1. 為了將資料儲存到文字檔中,需要加上以下的程式碼: ```python with open("5-5_data.csv", "w", encoding="UTF-8") as file file.write("h(m), t(s), R(m)\n") ``` 第一行程式碼用來開啟名稱為 5-5_data.csv 的文字檔,如果檔案已存在則覆蓋檔案內容,文字編碼為 UTF-8。第二行程式碼會在 5-5_data.csv 中寫入文字 h(m), t(s), R(m) 並換行。 2. 第 34 ~ 42 行,用 def 自訂名為 main 的函式,輸入變數 h 數值,自動跑完小球落下的過程,回傳落下需要的時間 t 以及水平射程 ball.pos.x + L/2 。如果要讓動畫不要執行地太快,要在 while 迴圈當中加上 rate(1000)。 3. 為了讓程式自動改變 h 的數值並代入後續的程式碼中運算,需要用到 for 迴圈。 ```python for h in range(5, 51, 1): t, r = main(h) with open("5-5_data.csv", "a", encoding="UTF-8") as file: file.write(str(h) + "," + str(t) + "," + str(r) + "\n") data_y.append(t) data_x.append(h) ``` h 的數值由 5 開始代入,每次增加 1,直到數量為 50 為止。將每個小球的飛行時間及水平射程回傳為 t 及 r,再用 ```python file.write(str(h) + "," + str(t) + "," + str(r) + "\n") ``` 將 h、t、r 轉為字串、用逗號分隔、寫入檔案。 4. 由於是用 with 開啟檔案,不需要加上 file.close() 關閉檔案。 5. 儲存到文字檔中的資料格式如下([取得檔案](https://raw.githubusercontent.com/YiZheWangTw/VPythonTutorial/master/05.%E6%B0%B4%E5%B9%B3%E6%8A%9B%E5%B0%84/data.csv)): ```python h(m), t(s), R(m) 5,1.0099999999999996,5.049999999998995 6,1.105999999999989,5.5299999999989 7,1.1949999999999792,5.974999999998811 8,1.2769999999999702,6.38499999999873 9,1.3549999999999616,6.774999999998652 10,1.4279999999999535,7.1399999999985795 ``` 6. 第 55 ~ 64 行的程式碼是利用 matplotlib 套件繪圖,如果想要用其它軟體讀取 5-5_data.csv 的資料並繪圖,可以刪除這幾行。 7. 由於 [GlowScript](https://www.glowscript.org/#/) 網站上不能將資料儲存成文字檔,也不支援 matplotlib,可以使用以下的程式碼,將不支援的部分刪掉,並用 print 輸出需要的資料。程式碼第1行 **Web VPython 3.2** 是在網站上建立程式時自動産生的,會自動採用最新的版本,不需要手動輸入,也不需要再寫 **from vpython import \***,這是 [GlowScript 動畫連結](https://www.glowscript.org/#/user/yizhe/folder/Public/program/5-5projectliLoop)。 ```python= Web VPython 3.2 """ 1. 參數設定, 設定變數及初始值 """ size = 1 # 小球半徑 v0 = 5 # 小球水平初速 L = 50 # 地板長度 g = 9.8 # 重力加速度 9.8 m/s^2 dt = 0.001 # 時間間隔 """ 2. 畫面設定 """ scene = canvas(title="Projectile", width=800, height=600, x=0, y=0, center=vec(0, 5, 0), background=vec(0, 0.6, 0.6)) floor = box(pos=vec(0, -size, 0), size=vec(L, 0.01, 10), texture=textures.metal) """ 3. 主程式 """ def main(h): t = 0 ball = sphere(pos=vec(-L/2, h, 0), radius=size, texture=textures.wood, make_trail=True, v=vec(v0, 0, 0), a=vec(0, -g, 0)) while ball.pos.y - floor.pos.y > size + 0.5*floor.height: rate(1000) ball.v += ball.a*dt ball.pos += ball.v*dt t += dt return t, ball.pos.x + L/2 """ 4. 用for迴圈改變h, 計算t、R, 寫入檔案 """ for h in range(5, 51, 1): t, r = main(h) print("{}, {}, {}".format(h, t, r)) ``` <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/sHfjAis.png"> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/jjsIYXs.png"> <div style="text-align:center">於 GlowScript 網站執行的結果</div> <br /> <br /> ### 數據處理原則 仿照程式 5-4 的方法,先畫 t - h 關係圖,多項式擬合的結果為 $$ y = 0.728 + 0.074 x - 0.0005 x^2 $$ 再畫 \\( \log t - \log h \\) 關係圖,畫出來的圖為斜直線,最接近直線為 $$ y = 0.500 x - 0.345 $$ 由斜率可以猜測 \\( t \propto \sqrt h \\)。接著新增一欄數據計算 \\( \sqrt h \\),畫 \\( t - \sqrt h \\) 關係圖,畫出來的圖為斜直線,最接近直線為 $$ y = 0.45179 x - 0.00043 $$ 斜率的理論值為 0.45175,理論值與模擬得到的值很接近,可以確定程式應該沒有問題。 <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/oQp10Ta.jpg"> <div style="text-align:center">t - h 關係圖</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://i.imgur.com/1TTOupe.jpg"> <div style="text-align:center">log(t) - log(h) 關係圖</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://i.imgur.com/0xWTrqt.jpg"> <div style="text-align:center">t - h<sup>1/2</sup> 關係圖</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/BnQsnlH.png"> <div style="text-align:center">使用 Matplotlib 繪製的 t - h 關係圖</div> <br /><br /> ## 結語 本文是以改變 h 為例,可以試著用相同的方法改變其它的變數值,例如改變空氣阻力的係數,找出飛行時間與空氣阻力係數的關係。而且用 for 迴圈自動改變數值,再將運算結果存入文字檔中,比手動改變數值所需要的時間少很多,這樣才能發揮寫程式的好處。 <br /><br /> ## VPython官方說明書 1. **canvas**: http://www.glowscript.org/docs/VPythonDocs/canvas.html 2. **box**: http://www.glowscript.org/docs/VPythonDocs/box.html 3. **sphere**: http://www.glowscript.org/docs/VPythonDocs/sphere.html 4. **texture**: http://www.glowscript.org/docs/VPythonDocs/textures.html <br /> --- ###### tags: `VPython`