# 行星運動 > 作者:王一哲 > 日期:2018/4/18 <br /> 克卜勒行星運動定侓(Kepler’s laws of planetary motion)共有以下3條: 1. 第一定律(軌道定律):所有行星繞太陽公轉的穩定軌道為橢圓形,太陽在其中一個焦點上。 2. 第二定律(等面積速率定律):行星與太陽連線於單位時間內掃過的面積相等。若太陽與行星連線為 $r$、行星速度為 $v$、$r$ 與 $v$ 的夾角為 $\theta$,則行星與太陽連線於單位時間內掃過的面積為 $$ \frac{\Delta A}{\Delta t} = \frac{1}{2}rv \sin \theta = 定值 $$ 3. 第三定律(週期定律):所有繞太陽公轉的行星,公轉週期 $T$ 與平均軌道半徑 $a$ 的關係為 $$ \frac{a^3}{T^2} = 定值 $$ <br /> 我們知道第一定律是因為太陽與恆星之間只有重力作用,依照萬有引力定律可以證明只有橢圓是穩定軌道。第二定律則是因為重力通過太陽,所以行星相對於太陽的角動量守恆,因此行星與太陽連線於單位時間內掃過的面積相等。第三定律則可以用有引力定律當作向心力推導出來。如果配合太陽系星球的真實數據,應該可以將這三個定律畫出來。 <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/8xTWd1U.png"> <div style="text-align:center">用 ejs 自製的行星運動定律動畫</div> <br /> ## 程式 12-1.行星運動, 自訂星球速度、距離, 可更改萬有引力定律中 r 的次方([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/12.%E8%A1%8C%E6%98%9F%E9%81%8B%E5%8B%95/12-1_Kepler_1st.py)) ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/12-1Kepler1st)) ```python= """ VPython教學: 12-1.行星運動, 自訂星球速度、距離, 可更改萬有引力定律中 r 的次方 Ver. 1: 2018/2/25 Ver. 2: 2018/3/1 Ver. 3: 2019/9/8 作者: 王一哲 """ from vpython import * """ 1. 參數設定, 設定變數及初始值 """ G = 6.67408E-11 # 重力常數 size = 1E10 # 星球半徑, 放大約1000倍, 否則會看不見 sun_m = 1988500E24 # 太陽質量 d = 1.5E11 # 地球平均軌道半徑為 1.5E11 m v0 = 29290 # 地球於遠日點公轉速率為 29290 m/s theta = radians(90) # 速度與 +x 軸夾角, 用 radians 將單位換為 rad, 預設為 90 度 n = 2 # 萬有引力定律中 r 的次方 t = 0 # 時間 dt = 60*60 # 時間間隔 """ 2. 畫面設定 """ scene = canvas(title="Planetary Motion", width=600, height=600, x=0, y=0, background=color.black) # 產生太陽 sun, 行星 planet sun = sphere(pos=vec(0, 0, 0), radius=size, m=sun_m, color=color.orange, emissive=True) planet = sphere(pos=vec(d, 0, 0), radius=size, texture=textures.earth, make_trail=True, trail_color=color.blue, trail_radius=0.1*size, v=vec(v0*cos(theta), v0*sin(theta), 0)) lamp = local_light(pos=vec(0,0,0), color=color.white) # 產生表示速度、加速度用的箭頭 arrow_v = arrow(pos=planet.pos, axis=vec(0, 0, 0), shaftwidth=0.3*size, color=color.green) arrow_a = arrow(pos=planet.pos, axis=vec(0, 0, 0), shaftwidth=0.3*size, color=color.magenta) # 產生繪圖視窗 gdr = graph(title="r-t plot", width=600, height=450, x=0, y=600, xtitle="<i>t</i> (s)", ytitle="<i>r</i> (m)") gdv = graph(title="v-t plot", width=600, height=450, x=0, y=1050, xtitle="<i>t</i> (s)", ytitle="<i>v</i> (m/s)") gda = graph(title="a-t plot", width=600, height=450, x=0, y=1500, xtitle="<i>t</i> (s)", ytitle="<i>a</i> (m/s<sup>2</sup>") rt = gcurve(graph=gdr, color=color.blue) vt = gcurve(graph=gdv, color=color.green) at = gcurve(graph=gda, color=color.magenta) """ 3. 星球運動部分 """ while True: rate(60*24*2) # 更新行星加速度、速度、位置 planet.a = -G*sun.m / planet.pos.mag**n * planet.pos.norm() planet.v += planet.a*dt planet.pos += planet.v*dt # 更新代表速度、加速度的箭頭位置、方向、長度 arrow_v.pos = planet.pos arrow_a.pos = planet.pos arrow_v.axis = planet.v*2E6 # 乘以 2E6 放大箭頭, 否則會看不見 arrow_a.axis = planet.a*1E13 # 乘以 1E13 放大箭頭, 否則會看不見 # 畫出 r-t, v-t, a-t 圖 rt.plot(pos=(t, planet.pos.mag)) vt.plot(pos=(t, planet.v.mag)) at.plot(pos=(t, planet.a.mag)) # 更新時間 t += dt ``` <br /> ### 參數設定 在此設定變數為 G、size、sun_m、d、v0、theta、n、t、dt,用途已寫在該行的註解當中。其中 E 代表乘以 10 的幾次方,例如 1E3 = 1000,也可以用 e 代替。由於我希望盡量使用真實數據,但是當距離為真實數據時,星球半徑就必須放大,否則會看不到星球。 <br /> ### 畫面設定 1. 產生太陽時最後一個選項為 **emissive=True** ,這個選項的預設值為 False,若設定為 True 則太陽會發光。 2. 產生地球時設定材質 **texture=textures.earth** ,這是因為 VPython 有內建地球的貼圖,不需要自己找圖片。 但由於地球物件沒有設定顏色,軌跡的顏色預設為白色,若想改藍色就要加上 **trail_color=color.blue** 。若要改變軌跡的半徑為 0.1*size 則要加上 **trail_radius=0.1 \* size** 。 3. 若要使太陽發光,在舊版的 VPython 中寫法為 ```python scene.lights = [local_light(pos=vec(0, 0, 0), color=color.white)] ``` 但是在 VPython 7 已經不支援這樣的寫法,改為在太陽的位置上放置一個光源 ```python lamp = local_light(pos=vec(0,0,0), color=color.white) ``` 4. 產生表示速度、加速度的箭頭。 5. 開啟繪圖視窗,畫星球與太陽的距離、行星速度、加速度與時間關係圖。 <br /> ### 物體運動 1. 計算行星的加速度 ```python planet.a = - G*sun.m / planet.pos.mag**n * planet.pos.norm() ``` 為了要能夠改變萬有引力定律當中距離的次方,因此不使用 **planet.pos.mag2** 計算行星與太陽連線距離的平方,改用 **planet.pos.mag\*\*n** 計算行星與太陽連線距離的 n 次方。接著更新行星的速度、位置。 2. 更新代表速度、加速度的箭頭位置、方向、長度,由於行星與太陽間的距離很大,要箭頭放大才能看到。 3. 最後畫 r - t、v - t、a - t 圖、更新時間。 <br /> ### 模擬結果 由於我是用地球的遠日距、位於遠日點時的速度代入程式中,因此預設的角度為90度,可以將角度改為其它的值,例如120度,地球的軌道就會變成很扁的橢圓形。也可以試著將 n 從 2 改成其它數值,只要不是 2 就沒有辦法形成穩定軌道。 <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/skIubkQ.png"> <div style="text-align:center">角度為 120 度的模擬結果</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/bsrVdIA.png"> <div style="text-align:center">n = 2.015 的模擬結果</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/vJHpZkR.png"> <div style="text-align:center">n = 2.02</div> <br /> ## 程式 12-2.行星運動, 用 dictionary 儲存星球資料([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/12.%E8%A1%8C%E6%98%9F%E9%81%8B%E5%8B%95/12-2_Kepler.py)) ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/12-2Kepler)) ```python= """ VPython教學: 12-2.行星運動, 用 dictionary 儲存星球資料 Ver. 1: 2018/2/25 Ver. 2: 2019/9/8 作者: 王一哲 """ from vpython import * """ 1. 參數設定, 設定變數及初始值, 太陽及行星半徑、質量、遠日距、遠日點速率, 資料來源 """ # 用 dictionary 儲存星球資料, 半徑 radius, 質量 mass, 遠日距 d_at_aphelion, 於遠日點的速率 v_at_aphelion radius = {"Mercury": 2439700, "Venus": 6051800, "Earth": 6371000, "Mars": 3389500, "Sun": 696392000} mass = {"Mercury": 0.33011E24, "Venus": 4.8675E24, "Earth": 5.9723E24, "Mars": 0.64171E24, "Sun": 1988500E24} d_at_aphelion = {"Mercury": 6982E7, "Venus": 10894E7, "Earth": 15210E7, "Mars": 24923E7} v_at_aphelion = {"Mercury": 38860, "Venus": 34790, "Earth": 29290, "Mars": 21970} G = 6.67408E-11 # 重力常數 eps = 10000 # 精準度 t = 0 # 時間 dt = 60*60 # 時間間隔 """ 2. 畫面設定 """ scene = canvas(title="Planetary Motion", width=600, height=600, x=0, y=0, background=color.black) # 產生太陽 sun, 地球 earth 及火星 mars sun = sphere(pos=vec(0,0,0), radius=radius["Sun"]*20, m=mass["Sun"], color=color.orange, emissive=True) earth = sphere(pos=vec(d_at_aphelion["Earth"], 0, 0), radius=radius["Earth"]*2E3, m=mass["Earth"], texture=textures.earth, make_trail=True, trail_color=color.blue, retain=365, v=vec(0, v_at_aphelion["Earth"], 0)) mars = sphere(pos=vec(d_at_aphelion["Mars"], 0, 0), radius=radius["Mars"]*2E3, m=mass["Mars"], color=color.red, make_trail=True, retain=365, v=vec(0, v_at_aphelion["Mars"], 0)) lamp = local_light(pos=vec(0, 0, 0), color=color.white) """ 3. 星球運動部分 """ while True: rate(60*24) # 更新行星加速度、速度、位置 earth.a = -G*sun.m / earth.pos.mag2 * earth.pos.norm() earth.v += earth.a*dt earth.pos += earth.v*dt mars.a = -G*sun.m / mars.pos.mag2 * mars.pos.norm() mars.v += mars.a*dt mars.pos += mars.v*dt # 判斷行星是否回到遠日點, 若回到出發點則顯示經過的時間 if abs(earth.pos.x - d_at_aphelion["Earth"]) <= eps: print("t_Earth =", t) if abs(mars.pos.x - d_at_aphelion["Mars"]) <= eps: print("t_Mars =", t) # 更新時間 t += dt ``` <br /> ### 參數設定 1. 字典(dictionary)是 Python 特殊的儲存資料格式,假設定義一個名稱為 data 的字典格式資料,語法為 ```python data = {"key1": value1, "key2": value2, ...} ``` 引號的部分單引號或雙引號皆可,只要有成對使用即可。每個 key 會對應到一筆資料,資料可以是任意的格式,但是每個 key 都要不一樣,不能重複。如果想要呼叫 data 中 key2 對應的資料,語法為 ```python data["key2"] ``` 以程式中的資料為例 ```python radius = {"Mercury": 2439700, "Venus": 6051800, "Earth": 6371000, "Mars": 3389500, "Sun": 696392000} ``` 若輸入 radius["Sun"] ,則系統會輸出 696392000。 2. 在此設定變數還有 G、eps、t、dt,用途已寫在該行的註解當中。 <br /> ### 畫面設定 畫面設定的部分與程式 12-1 非常相似,但是在產生星球物件時有點不一樣,之前是直接輸入對應的變數名稱,在此則改用儲存資料用的字典 + 名稱的方式來處理,例如設定地球位於遠日點時是使用 **pos = vec(d_at_aphelion["Earth"], 0, 0)** ,這樣的好處在於產生每個星球時所用的程式碼幾乎一模一樣,只是修改了星球的名字,就能自動找到對應的資料。 <br /> ### 物體運動 物體運動的部分與程式 12-1 非常相似,由於只畫出兩個行星,只要將計算加速度、更新速度、位移的程式碼針對兩個行星各寫一次即可。另外再用 if 判斷行星是否回到出發點附近,用來計算週期。雖然這樣的寫法不算精準,但至少還堪用。 <br /> ### 模擬結果 由模擬程式得到的地球公轉週期為 31550400 s,約為 365.17 天,與回歸年 365.2421990741 天相差不到 0.02%。由模擬程式得到的火星公轉週期為 59335200 s,為 686.75 天,約為 1.88 年,火星的回歸週期實際值為 686.973 天,相差約 0.03%。由地球、火星的模擬結果來看,這個程式應該相當準確。 <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/9f81GIB.png"> <div style="text-align:center">程式 12-2 畫面截圖</div> <br /> ## 程式 12-3.行星運動, 用 dictionary 儲存星球資料, 用 for 迴圈產生行星([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/12.%E8%A1%8C%E6%98%9F%E9%81%8B%E5%8B%95/12-3_Kepler_for.py)) ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/12-3Keplerfor)) ```python= """ VPython教學: 12-3.行星運動, 用 dictionary 儲存星球資料, 用 for 迴圈產生行星 Ver. 1: 2018/2/26 Ver. 2: 2019/9/8 作者: 王一哲 """ from vpython import * """ 1. 參數設定, 設定變數及初始值, 太陽及行星半徑、質量、遠日距、遠日點速率, 資料來源 """ # 用 dictionary 儲存星球資料, 半徑 radius, 質量 mass, 遠日距 d_at_aphelion, 於遠日點的速率 v_at_aphelion radius = {"Mercury": 2439700, "Venus": 6051800, "Earth": 6371000, "Mars": 3389500, "Sun": 696392000} mass = {"Mercury": 0.33011E24, "Venus": 4.8675E24, "Earth": 5.9723E24, "Mars": 0.64171E24, "Sun": 1988500E24} material = {"Mercury": color.cyan, "Venus": color.yellow, "Earth": color.blue, "Mars": color.red, "Sun": color.orange} d_at_aphelion = {"Mercury": 6982E7, "Venus": 10894E7, "Earth": 15210E7, "Mars": 24923E7} v_at_aphelion = {"Mercury": 38860, "Venus": 34790, "Earth": 29290, "Mars": 21970} G = 6.67408E-11 # 重力常數 eps = 10000 # 精準度 t = 0 # 時間 dt = 60*60 # 時間間隔 """ 2. 畫面設定 """ scene = canvas(title="Planetary Motion", width=600, height=600, x=0, y=0, background=color.black) # 產生太陽 sun sun = sphere(pos=vec(0, 0, 0), radius=radius["Sun"]*20, m=mass["Sun"], color=color.orange, emissive=True) # 用 for 迴圈產生水星、金星、地球、火星 names = ["Mercury", "Venus", "Earth", "Mars"] planets = [] for name in names: planets.append(sphere(pos=vec(d_at_aphelion[name], 0, 0), radius=radius[name]*2E3, m=mass[name], color=material[name], make_trail=True, retain = 365, v=vec(0, v_at_aphelion[name], 0))) lamp = local_light(pos=vec(0, 0, 0), color=color.white) """ 3. 星球運動部分 """ while True: rate(60*24) # 用 for 迴圈自動跑完所有行星的資料 for planet in planets: planet.a = -G*sun.m / planet.pos.mag2 * planet.pos.norm() planet.v += planet.a*dt planet.pos += planet.v*dt # 更新時間 t += dt ``` <br /> ### 程式設計部分 這是以程式 12-2 為基礎衍生出來的偷懶寫法,因為對每個行星而言,產生行星與計算加速度、更新速度、位置的程式碼幾乎一模一樣,但如果總共有4個行星的話,同樣的程式碼要寫4次,這樣實在太麻煩了,如果使用 for 迴圈就能少寫 3/4 的程式碼。 1. 產生行星時,先把要產生的行星名稱存到串列中 ```python names = ["Mercury", "Venus", "Earth", "Mars"] ``` 第31 ~ 35行,開啟一個名為 planets 的空白串列,再用 for 迴圈將 names 當中的行星名稱取出來指定給變數 name,在使用 sphere 產生球體時丟到儲存資料的字典當中作為 key 值,就可以讀取對應的資料。接下來再用 **planets.append()** 將括號中的物件加到串列 planets 最後面。Python 當中有一個特殊的寫法,可以在一行程式碼中使用 for 迴圈産生串列,所以第31 ~ 35行可以改寫為 ```python planets = [sphere(pos=vec(d_at_aphelion[name], 0, 0), radius=radius[name]*2E3, m=mass[name], color=material[name], make_trail=True, retain = 365, v=vec(0, v_at_aphelion[name], 0)) for name in names] ``` <br /> 2. 計算行星的加速度、更新速度、位置。用 for 迴圈將 planets 當中的行星物件取出來指定給變數 planet。用 ```python planet.a = - G*sun.m / planet.pos.mag2 * planet.pos.norm() ``` 計算加速度,再用 ```python planet.v += planet.a*dt ``` 更新速度,最後用 ```python planet.pos += planet.v*dt ``` 更新位置。 <br /> ### 模擬結果 我們很成功地使用 for 迴圈減少程式碼,並且畫出水星、金星、地球、火星繞太陽公轉的動畫,如果只想畫出其中幾個行星,只要修改串列 names 即可。 <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/Oc5mhe6.png"> <div style="text-align:center">程式 12-3 畫面截圖</div> <br /> ## 程式 12-4.行星運動, 用 dictionary 儲存星球資料, 用 class 產生行星([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/12.%E8%A1%8C%E6%98%9F%E9%81%8B%E5%8B%95/12-3_Kepler_for.py)) ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/12-4Keplerclass)) ```python= """ VPython教學: 12-4.行星運動, 用 dictionary 儲存星球資料, 用 class 產生行星 Ver. 1: 2018/2/26 Ver. 2: 2018/10/25 改為不用繼承的 class Ver. 3: 2019/9/8 作者: 王一哲 """ from vpython import * """ 1. 參數設定, 設定變數及初始值, 太陽及行星半徑、質量、遠日距、遠日點速率, 資料來源 """ # 用 dictionary 儲存星球資料, 半徑 radius, 質量 mass, 遠日距 d_at_aphelion, 於遠日點的速率 v_at_aphelion radius = {"Mercury": 2439700, "Venus": 6051800, "Earth": 6371000, "Mars": 3389500, "Sun": 696392000} mass = {"Mercury": 0.33011E24, "Venus": 4.8675E24, "Earth": 5.9723E24, "Mars": 0.64171E24, "Sun": 1988500E24} material = {"Mercury": color.cyan, "Venus": color.yellow, "Earth": color.blue, "Mars": color.red, "Sun": color.orange} d_at_aphelion = {"Mercury": 6982E7, "Venus": 10894E7, "Earth": 15210E7, "Mars": 24923E7} v_at_aphelion = {"Mercury": 38860, "Venus": 34790, "Earth": 29290, "Mars": 21970} G = 6.67408E-11 # 重力常數 t = 0 # 時間 dt = 60*60 # 時間間隔 """ 2. 產生行星類別 """ # 使用繼承的 class 寫法 ''' class Planet(sphere): sun_m = mass["Sun"] def a(self): return - G*self.sun_m / self.pos.mag2 * self.pos.norm() ''' # 不用繼承的 class 寫法 class Planet: def __init__(self, pos, radius, mass, color, v): self.pos = pos self.radius = radius self.mass = mass self.color = color self.v = v self.a = 0 self.planet = sphere(pos=self.pos, radius=self.radius, mass=self.mass, color=self.color, make_trail=True, retain=365, v=self.v) def update(self, dt): self.dt = dt self.a = -G*mass["Sun"] / self.planet.pos.mag2 * self.planet.pos.norm() self.v += self.a * self.dt self.planet.pos += self.v * self.dt """ 3. 畫面設定 """ scene = canvas(title="Planetary Motion", width=600, height=600, x=0, y=0, background=color.black) sun = sphere(pos=vec(0,0,0), radius=radius["Sun"]*20, color=color.orange, emissive=True) lamp = local_light(pos=vec(0, 0, 0), color=color.white) # 用 for 迴圈產生水星、金星、地球、火星 names = ["Mercury", "Venus", "Earth", "Mars"] planets = [] for name in names: planets.append(Planet(pos=vec(d_at_aphelion[name], 0, 0), radius=radius[name]*2E3, mass=mass[name], color=material[name], v=vec(0, v_at_aphelion[name], 0))) # 第58 ~ 62行另一個寫法 #planets = [Planet(pos=vec(d_at_aphelion[name], 0, 0), radius=radius[name]*2E3, mass=mass[name], # color=material[name], v=vec(0, v_at_aphelion[name], 0)) for name in names] """ 4. 星球運動部分 """ while True: rate(60*24) # 用 for 迴圈自動跑完所有行星的資料 for planet in planets: planet.update(dt) # 更新時間 t += dt ``` <br /> ### 程式設計部分 這是以程式 12-3 為基礎,再加上 **類別 (class)** 的寫法。如果想要在 Python 程式中產生許多相似的物件,而且我們還想要在這些物件中自訂函式,就可以使用 class 來達成目的,通常 class 的名稱開頭為大寫字母。class 的語法有兩種,如果繼承其它的 class 則語法為: ```python class 名稱(來源): def 函式1(參數): return 回傳值1 def 函式2(參數): return 回傳值2 ``` <br /> 例如程式 12-4 中自訂的 class 程式碼 ```python class Planet(sphere): sun_m = mass["Sun"] def a(self): return - G*self.sun_m / self.pos.mag2 * self.pos.norm() ``` <br /> class 名稱為 Planet,這個類別是由 sphere 衍生出來的,繼承了 sphere 所有的屬性,因此使用 Planet 產生的物件可以定義 pos、radius……等屬性。除此之外,還自行定義了函式 a,用來計算並回傳此行星物件的加速度。 <br /> 如果不使用繼承,則 class 的語法為 ```python class 名稱: def __init__(self, 參數1, 參數2, ...): self.參數1 = 參數1 self.參數2 = 參數2 ... def 函式1(參數): return 回傳值1 # 若無回傳值則可省略此行 def 函式2(參數): return 回傳值2 # 若無回傳值則可省略此行 ``` <br /> 例如程式 12-4 中自訂的 class 程式碼 ```python class Planet: def __init__(self, pos, radius, mass, color, v): self.pos = pos self.radius = radius self.mass = mass self.color = color self.v = v self.a = 0 self.planet = sphere(pos=self.pos, radius=self.radius, mass=self.mass, color=self.color, make_trail=True, retain=365, v=self.v) def update(self, dt): self.dt = dt self.a = -G*mass["Sun"] / self.planet.pos.mag2 * self.planet.pos.norm() self.v += self.a * self.dt self.planet.pos += self.v * self.dt ``` <br /> 使用 Planet 建立物件時,需要輸入 pos、radius、mass、color、v 等5個參數,並將參數值代入自訂函式 \_\_init\_\_,産生一個名為 planet 的 sphere 物件。使用另一個自訂函式 update 時,需要輸入參數 dt,函式可以計算 sphere 物件的加速度、速度,更新 planet 的位置。關於 class 更詳細的說明請參閱這篇文章:〈[自訂類別 Class](https://hackmd.io/@yizhewang/r1XyZogto)〉。 <br /> ### 模擬結果 結果與程式 12-3 完全相同。 <br /> ## 程式 12-5.克卜勒第三行星運動定律([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/12.%E8%A1%8C%E6%98%9F%E9%81%8B%E5%8B%95/12-5_Kepler_3rd.py)) ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/12-5Kepler3rd)) ```python= """ VPython教學: 12-5.克卜勒第三行星運動定律 Ver. 1: 2018/3/1 Ver. 2: 2018/4/17 Ver. 3: 3019/9/8 作者: 王一哲 """ from vpython import * """ 1. 參數設定, 設定變數及初始值, 太陽及行星半徑、質量、遠日距、遠日點速率, 資料來源 太陽 https://nssdc.gsfc.nasa.gov/planetary/factsheet/sunfact.html 地球 https://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html """ size = 2E10 # 星球半徑, 放大約2000倍, 否則會看不見 sun_m = 1988500E24 # 太陽質量 d = 1.5E11 # 地球平均軌道半徑為 1.5E11 m v0 = 29780 # 地球公轉平均速率為 29780 m/s eps = 10000 # 計算週期用的精準度 n = 4 # 自訂行星 planet 軌道半徑為 n*d ec = 0.0 # 自訂行星軌道離心率(eccentricity), 若 n != 1 時設為 0 G = 6.67408E-11 # 重力常數 t = 0 # 時間 dt = 60*60 # 時間間隔 """ 2. 畫面設定 (1) 用 canvas 物件作為顯示動畫用的視窗 http://www.glowscript.org/docs/VPythonDocs/canvas.html (2) 用 sphere 物件產生星球 http://www.glowscript.org/docs/VPythonDocs/sphere.html (3) 星球的半徑要手動調整比例, 否則會看不到星球 """ scene = canvas(title="Kepler's Third Law of Planetary Motion", width=600, height=600, x=0, y=0, background=color.black) # 產生太陽 sun, 地球 earth 及自訂行星 planet sun = sphere(pos=vec(0,0,0), radius=size, m=sun_m, color=color.orange, emissive=True) earth = sphere(pos=vec(d, 0, 0), radius=size, texture=textures.earth, make_trail=True, trail_color=color.blue, retain=365, v=vec(0, v0, 0)) planet = sphere(pos=vec(n*(1+ec)*d, 0, 0), radius=size, color=color.red, make_trail=True, retain=365*n, v=vec(0, v0/sqrt(n)*sqrt((1-ec)/(1+ec)), 0)) line = cylinder(pos=vec(0, 0, 0), axis=vec(n*(1+ec)*d, 0, 0), radius=0.3*size, color=color.yellow) # 原來的寫法為 scene.lights = [local_light(pos = vec(0,0,0), color = color.white)] # 在 VPython 7 中 canvas.lights 無法設定為 local_light, 只能另外在太陽處放置另一個光源 lamp lamp = local_light(pos=vec(0,0,0), color=color.white) """ 3. 星球運動部分 """ while True: rate(60*24) # 計算行星加速度、更新速度、位置 earth.a = -G*sun.m / earth.pos.mag2 * earth.pos.norm() earth.v += earth.a*dt earth.pos += earth.v*dt planet.a = -G*sun.m / planet.pos.mag2 * planet.pos.norm() planet.v += planet.a*dt planet.pos += planet.v*dt # 判斷行星是否回到出發點 if abs(earth.pos.x - d) <= eps: print("t_Earth =", t) if abs(planet.pos.x - n*(1+ec)*d) <= eps: print("t_planet =", t) # 更新時間 t += dt ``` <br /> ### 程式設計部分 這是以程式 12-1 為基礎,除了地球以外再加上一個自訂行星,用來說明克卜勒行三行星運動定律。假設自訂行星的平均軌道半徑為 **n * d**,離心率為 **ec**,則遠日距為 **n * (1 + ec) * d**。若地球於遠日點時的速率為 **v0**,則自訂行星於遠日點時的速率為 **v0 / sqrt(n) * sqrt((1 - ec) / (1 + ec))**。剩下的部分幾乎沒有改變。 地球的速度推導過程如下: 假設太陽的質量為 \\( M \\),地球的質量為 \\( m \\),地球的平均軌道半徑為 \\( d \\),地球地球的速率為 \\( v_0 \\),由太陽對地球的重力當作向心力可得 $$ \frac{GMm}{d^2} = m \cdot \frac{v_0^2}{d} ~\Rightarrow~ v_0 = \sqrt{\frac{GM}{d}} $$ <br /> 自訂行星位於遠日點的速率推導過程如下: 假設太陽的質量為 \\( M \\),行星的質量為 \\( m \\),行星的平均軌道半徑為 $a = nd$,行星位於近日點的速率為 \\( v_1 \\) ,位於遠日點的速率為 \\( v_2 \\),由角動量守恆可得 $$ L = (1-e)amv_1 = (1+e)amv_2 ~\Rightarrow~ v_1 = \frac{1+e}{1-e}v_2 $$ 由力學能守恆可得 $$ \frac{1}{2}mv_1^2 + \left[ -\frac{GMm}{(1-e)a} \right ] = \frac{1}{2}mv_2^2 + \left[ -\frac{GMm}{(1+e)a} \right ] $$ 將 $v_1$、$v_2$ 的關係代入上式 $$ \left(\frac{1+e}{1-e} \right )^2 v_2^2 -\frac{2GM}{(1-e)a} = v_2^2 -\frac{2GM}{(1+e)a} $$ $$ \left[\left(\frac{1+e}{1-e} \right )^2 - 1 \right ] v_2^2 = \left( \frac{1}{1-e} - \frac{1}{1+e} \right) \frac{2GM}{a} $$ $$ \frac{4ev_2^2}{(1-e)^2} = \frac{4eGM}{(1-e)(1+e)a} $$ $$ v_2 = \sqrt{\frac{1+e}{1-e} \cdot \frac{GM}{a}} = \sqrt{\frac{1+e}{1-e} \cdot \frac{GM}{nd}} = \sqrt{\frac{1+e}{(1-e)n}} v_0 $$ <br /> ### 模擬結果 1. 假設 n = 4,則自訂行星的週期為 8 年。 2. 假設 n = 1,調整 0 < ec < 1,則自訂行星的週期為 1 年。 <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/WWfvhLD.png"> <div style="text-align:center">n = 4、ec = 0,自訂行星的週期為 8 年</div> <br /> <img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://i.imgur.com/VCXKG4Q.png"> <div style="text-align:center">n = 1、ec = 0.5,自訂行星的週期為 1 年</div> <br /> ## 結語 用 VPython 可以模擬出各種不同的狀況,例如修改萬有引力定律的型式,或是用實際數據代入模型中看看是否符合實際情形,同學們可以試著將程式修改成自己想要的樣子。 <br /> ## 太陽系天體資料來源 1. **太陽** https://nssdc.gsfc.nasa.gov/planetary/factsheet/sunfact.html 2. **水星** https://nssdc.gsfc.nasa.gov/planetary/factsheet/mercuryfact.html 3. **金星** https://nssdc.gsfc.nasa.gov/planetary/factsheet/venusfact.html 4. **地球** https://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html 5. **火星** https://nssdc.gsfc.nasa.gov/planetary/factsheet/marsfact.html <br /> ## VPython官方說明書 1. **canvas**: http://www.glowscript.org/docs/VPythonDocs/canvas.html 2. **sphere**: http://www.glowscript.org/docs/VPythonDocs/sphere.html 3. **cylinder**: http://www.glowscript.org/docs/VPythonDocs/cylinder.html 4. **graph**: http://www.glowscript.org/docs/VPythonDocs/graph.html --- ###### tags:`VPython`