天天看點

使用Matplotlib模拟Python中的三維太陽系

作者:Python阿傑

程式設計的一個用途是通過模拟來幫助我們了解真實世界。這一技術被應用于科學、金融和許多其他定量領域。隻要控制現實世界屬性的“規則”是已知的,你就可以編寫一個計算機程式來探索你遵循這些規則所得到的結果。在本文中,您将用Python模拟三維太陽系使用流行的可視化庫Matplotlib

在這篇文章,你将能夠用Python建立你自己的3D太陽系,你可以用你想要的多少太陽和行星。下面是一個簡單的太陽系的一個例子,它有一個太陽和兩個行星:

你還可以打開動畫地闆上的二維投影,更好地展示太陽系的三維本質。下面是同樣的太陽系模拟,包括2D投影:

下面是這篇文章的概要,以便您知道接下來會發生什麼:

淺談兩個物體之間的引力你需要用它來模拟Python中的三維太陽系。

簡介三維矢量 .

太陽系和軌道天體類别的定義在裡面,比如太陽和行星。你将用一步一步的方法編寫這些類,并用一個簡單的太陽系來測試它們。

增加選項顯示2D投影軌道物體的三維模拟。這個2D投影有助于在3D中可視化運動。

建立一個雙星系統 .

在本文中,您将使用面向對象的程式設計和Matplotlib。如果您希望閱讀更多關于任何一個主題的内容,您可以閱讀:

面向對象程式設計

使用Matplotlib的Python資料可視化基礎

讓我們從使用Matplotlib在Python中模拟一個3D太陽系開始。

太陽系中的太陽、行星和其他天體都是運動中的天體,它們互相吸引。引力在任何兩個物體之間施加。

如果這兩個對象有大量M_1和M_2是距離r然後,你可以用以下公式計算它們之間的引力:

常數G是一個引力常數。您将看到如何在模拟的版本中忽略這個常量,在本文中,您将使用任意機關的品質和距離,而不是kg和m。

一旦你知道了兩個物體之間的引力,你就可以計算出加速度。a每個物體都是由于這種引力而經曆的,使用以下公式:

使用這個加速度,你可以調整運動物體的速度。當速度發生變化時,速度和方向都會發生變化。

當用Python模拟一個三維太陽系時,你需要用三維空間來表示太陽系。是以,這個3D空間中的每個點都可以用三個數字來表示,x -, y-和z-坐标。例如,如果你想把太陽放在太陽系的中心,你可以将太陽的位置表示為(0, 0, 0) .

您還需要在3D空間中表示向量。矢量具有大小和方向。你需要像速度、加速度和力這樣的量的矢量,因為這些量都有一個方向和一個震級。

在本文中,我将不詳細讨論向量代數。相反,我将陳述您需要的任何結果。你可以讀到更多關于向量與向量代數如果你願意的話。

為了在代碼中更容易地處理向量,您可以建立一個類來處理它們。編寫這個類将作為對類和面向對象程式設計的快速重新整理。你可以讀到用Python進行面向對象的程式設計如果你覺得你需要一個更徹底的解釋。雖然您也可以建立一個類來處理3D空間中的點,但這并不是必要的,在本文中我也不會建立一個類。

如果您熟悉向量和面向對象程式設計,可以跳過本節,隻需在定義Vector班級。

建立一個名為vectors.py中,您将定義Vector班級。您将使用此腳本定義類并對其進行測試。然後,可以删除最後的測試代碼,隻需在這個腳本中保留類定義:

這個__init__()方法的Vector類有三個參數,表示每個軸上的值。每個參數的預設值為0表示該軸的原點。雖然我們不喜歡在Python中使用單個字母名稱,x , y,和z是恰當的,因為它們代表了數學中常用的笛卡爾坐标系的術語。

您還定義了兩個Dunder方法來将對象表示為一個字元串:

__repr__()傳回用于顯示類名的程式員的輸出。輸出__repr__()可用于重新建立對象。

__str__()傳回對象的字元串表示形式的非程式員版本。在這種情況下,它傳回數學中常用的表示來表示向量,使用機關向量i , j,和k .

在代碼段的末尾,您可以更多地了解這兩種類型的字元串表示之間的差異。Python編碼書第9章 .

測試代碼塊的輸出如下:

在Python項目中的這個3D太陽系中,如果Vector類是可索引的,以便您可以使用[]帶有索引以提取其中一個值的符号。使用目前形式的類,如果添加print(test[0])在您的腳本中,您将得到一個TypeError說Vector對象不可訂閱。您可以通過向類定義中添加另一個Dud方法來修複這個問題:

通過定義__getitem__(),你做了Vector可索引的類。向量中的第一項是x的價值。y的價值。z。任何其他索引都會引發錯誤。測試代碼塊的輸出如下:

test[0]傳回向量中的第一個項,x .

可以定義類的對象的加法和減法。__add__()和__sub__()DunderMethod.這些方法将使您能夠使用+和-執行這些操作的符号。如果沒有這些Dud方法,則使用+和-提出TypeError .

若要添加或減去兩個向量,可以分别添加或減去向量的每個元素:

雙管齊下__add__()和__sub__()傳回另一個Vector對象,每個元素等于兩個原始向量中相應元素的加減。輸出如下:

對于乘法和除法,您也可以這樣做,盡管在處理向量時,這些操作需要更多的注意。

在處理向量時,不能僅僅引用“乘法”,因為有不同類型的“乘法”。在這個項目中,你隻需要标量乘法。标量乘法是指向量與标量相乘(标量有一個數量級,但沒有方向)。但是,在本小節中,您還将定義點積兩個向量。你想用*運算符,既适用于标量乘法,也适用于點積。是以,可以定義__mul__()DunderMethod:

使用*運算符将取決于第二個操作數,即*符号,是标量或向量。如果由參數表示的第二個操作數other,是類型的Vector,計算了點積。但是,如果other是類型的int或float,傳回的結果是一個新的Vector,按比例調整。

以上代碼的輸出如下:

如果您想要标量乘法,則需要标量乘法。後這個*象征。如果您試圖運作該語句3*Vector(3, 5, 9)相反,TypeError将被提高,因為Vector類不是用于使用的有效操作數。*帶有類型的對象int .

兩個向量是分不開的。但是,可以将向量除以标量。您可以使用/運算符Vector如果定義__truediv__()DunderMethod:

産出如下:

如果你有一個向量(x,y,z),您可以找到它的震級使用表達式(x^2+y^2+z^2)。你也可以規範化向量。歸一化給出一個方向相同但大小為1。您可以通過将向量的每個元素除以矢量的大小來計算歸一化向量。

可以定義兩個新方法來完成Vector班級:

測試代碼提供了以下輸出:

第三個輸出給出了歸一化向量的大小,表明它的大小是1 .

根據使用的IDE或其他工具,在分割時可能會收到警告self.x , self.y,和self.z,如在__truediv__()和normalize()。您不需要擔心這個問題,但是如果您想要修複它,可以通過更改__init__()簽署下列任何一項:

這兩個選項都讓IDE知道參數應該是浮動的。在第二個選項中,您使用類型暗示來實作。

您現在可以删除此腳本末尾的測試代碼,以便您在vectors.py是類的定義。

現在,你可以開始研究Python中的3D太陽系了。您将建立兩個主要類:

你将使用Matplotlib來建立和可視化太陽系。您可以在終端中使用以下内容來安裝Matplotlib:

這個Axes3DMatplotlib中的物體将“托管”太陽系。如果您使用過Matplotlib,并且主要使用了2D繪圖,那麼您将使用(有意或不知情的)Axes對象。Axes3D的3D等效Axes,顧名思義!

現在是開始編寫和測試這些類的時候了。您可以建立兩個新檔案:

接下來,您将開始處理SolarSystem班級。

您将在整個項目中使用任意單元。這意味着,與其用米作為距離,而用公斤作為品質,你将使用沒有機關的數量。參數size用于定義包含太陽系的立方體的大小:

定義SolarSystem類的__init__()方法,其中包含參數。size。您還定義了bodies屬性。這個屬性是一個空清單,當你稍後建立它們時,它将包含太陽系内的所有天體。這個add_body()方法可以用來将軌道天體添加到太陽系中。

下一步是介紹Matplotlib。屬性建立圖形和一組軸。subplots()在matplotlib.pyplot :

你打電話plt.subplots(),它傳回一個圖形和一組軸。傳回的值配置設定給屬性。fig和ax。你打電話plt.subplots()有以下論點:

前兩個論點是1和1若要在圖形中建立單個軸集,請執行以下操作。

這個subplot_kw參數有一個字典作為它的參數,它将投影設定為3d。這意味着建立的軸是Axes3D對象。

figsize設定包含Axes3D對象。

您還可以調用該方法。tight_layout()。這是Figure類在Matplotlib中。此方法減少了圖形邊緣的邊距。

到目前為止,您可以在控制台/REPL中嘗試代碼:

這給出了一組空的三維軸的圖形:

使用Matplotlib模拟Python中的三維太陽系

您将使用size參數設定此多元資料集的大小。你會回到SolarSystem稍後上課。目前,您可以将您的注意力轉向定義SolarSystemBody班級。

您可以開始建立SolarSystemBody類及其__init__()方法。我正在截斷SolarSystem下面代碼中的類定義用于顯示。在此代碼塊和以後的代碼塊中,包含# ...指出您之前編寫的未顯示的代碼:

中的參數。__init__()方法是:

solar_system使你能夠将天體連接配接到太陽系。這個論點應該是類型的。SolarSystem .

mass定義身體品質的整數或浮點數。在這個項目中,你将使用任意的機關,是以你不需要使用“真實”品質的恒星和行星。

position是三維空間中定義身體位置的點。這是一個包含x -, y-和z-點坐标。預設值是起源。

velocity定義身體的速度。由于運動物體的速度有大小和方向,是以它必須是一個矢量。盡管執行個體化SolarSystemBody元組,則可以将元組轉換為Vector将其指派給屬性時。self.velocity .

你也叫add_body()方法中定義的SolarSystem類将這個天體添加到太陽系中。稍後,您将向__init__()方法。

中定義另一個方法。SolarSystemBody用其目前的位置和速度移動物體:

這個move()方法重新定義position屬性的velocity屬性。我們已經讨論過你是如何用任意機關來計算距離和品質的。你也在使用任意的時間機關。每個‘時間機關’将是循環的一個疊代,您将使用它來運作模拟。是以,move()将身體按一次疊代所需的數量移動,這是一個時間機關。

你們已經建立了Matplotlib結構,它将容納太陽系及其所有天體。現在,您可以添加一個draw()方法SolarSystemBody若要在Matplotlib圖上顯示主體,請執行以下操作。您可以通過繪制一個标記來完成這一任務。

在這樣做之前,您需要在SolarSystemBody若要控制将繪制的标記的顔色和大小以表示身體,請執行以下操作:

類屬性min_display_size和display_log_base設定參數,以确定您将在3D圖上顯示的标記的大小。您設定了一個最小的大小,以便您顯示的标記不太小,即使對于小的身體也是如此。您将使用對數标度将品質轉換為标記大小,并将此對數的基值設定為另一個類屬性。

這個display_size屬性中的執行個體屬性。__init__()方法在計算的标記大小和所設定的最小标記大小之間進行選擇。為了在這個項目中确定身體的顯示大小,你要使用它的品質。

您還可以添加colour屬性__init__(),暫時預設為黑色。

要測試這些新添加的内容,可以在控制台/REPL中嘗試以下内容:

第一次呼叫body.draw()在原點繪制物體,因為你使用的是太陽系天體的預設位置。打電話給body.move()用一個“時間機關”所需的數量移動身體。因為身體的速度是(1, 1, 1),身體将沿着三個軸中的每一個移動一個機關。第二次呼叫body.draw()在第二個位置畫太陽系天體。請注意,當您這樣做時,軸将自動重新排列。您很快就會在主代碼中處理這個問題。

您可以傳回到SolarSystem通過給太陽系及其天體添加兩種新的方法,将其分類和連接配接起來:update_all()和draw_all() :

這個update_all()方法穿過太陽系中的每一個物體,移動并畫出每一個物體。這個draw_all()方法使用太陽系的大小設定三軸的限制,并通過pause()功能。此方法還清除軸,為下一個繪圖做好準備。

您可以開始建構一個簡單的太陽系,并通過建立一個名為simple_solar_system.py :

運作此腳本時,您将看到一個黑體從情節的中心移動:

您可以更改三維圖形的透視圖,這樣您就可以直接沿着其中一個軸檢視3D軸。可以通過将視圖的方位和仰角設定為0在……裡面SolarSystem.__init__() :

跑動simple_solar_system.py現在給出以下觀點:

這個x-軸現在垂直于你的螢幕。因為你在2D顯示器上顯示一個3D視圖,是以你總是有一個方向與你用來顯示圖形的2D平面垂直。這一限制使得很難區分物體何時沿該軸運動。你可以通過改變身體的速度simple_solar_system.py到(1, 0, 0)并再次運作腳本。身體似乎是靜止的,因為它隻是沿着軸移動,從你的螢幕出來!

您可以通過根據它的不同更改标記的大小來改進三維可視化。x-協調。靠近您的對象看起來更大,而更遠的對象看起來更小。您可以對draw()方法中的SolarSystemBody班級:

self.position[0]表示身體的位置。x-軸,即垂直于螢幕的軸。因子30除以是一個任意因素,您可以使用它來控制您希望這種效果有多強。

在本教程的後面,您還将添加另一個功能,将有助于可視化的三維運動的恒星和行星。

你有一個太陽系,裡面有可以移動的物體。到目前為止,如果您隻有一個身體,那麼代碼可以正常工作。但那不是一個非常有趣的太陽系!如果你有兩個或兩個以上的物體,它們就會通過互相的引力互相作用。

在這篇文章的開頭,我簡要回顧了你需要處理兩個物體之間的引力的實體。由于在這個項目中使用的是任意機關,是以可以忽略引力常數G簡單地計算出由于兩個物體之間的重力而産生的力,如:

一旦你知道了兩個物體之間的力,因為F=ma,您可以計算出每個對象必須使用的加速度:

一旦你知道加速度,你就可以改變物體的速度。

您可以添加兩個新方法,一個在SolarSystemBody另一個在SolarSystem,計算出任何兩個物體之間的力和加速度,并穿過太陽系中的所有物體,并計算它們之間的互相作用。

第一種方法計算出兩個物體之間的引力,計算每個物體的加速度,并改變兩個物體的速度。如果您願意,可以将這些任務分為三種方法,但在本例中,我将将這些任務放在SolarSystemBody :

accelerate_due_to_gravity()對類型的對象調用。SolarSystemBody需要另一個SolarSystemBody身體作為一種争論。參數self和other代表兩個身體互相作用。此方法的步驟如下:

用兩個物體的位置來确定兩個物體之間的距離。您将其表示為向量,因為它的大小和方向都很重要。你提取x -, y-和z-position屬性使用解包裝運算符。*并将這些轉換為類型的對象。Vector你之前定義的。因為您定義了__sub__()DUD方法Vector類,您可以從另一個向量中減去一個向量,以獲得它們之間的距離作為另一個向量。

還可以使用get_magnitude()方法Vector班級。

接下來,你用上面總結的方程計算出兩個物體之間的力的大小。

然而,力有方向,也有震級。是以,您需要将其表示為向量。力的方向與連接配接兩個物體的矢量方向相同。首先對距離向量進行歸一化,得到力向量。這種歸一化給出了一個機關向量,其方向與連接配接這兩個物體的向量相同,但其大小為1。然後,把機關向量乘以力的大小。在這種情況下,您使用的是向量的标量乘法,這是在包含以下内容時定義的__mul__()在Vector班級。

對于這兩個物體中的每一個,你用上面所示的方程來計算加速度。force是一個向量。是以,當你除以body.mass,您使用的是您定義的标量除法,包括__truediv__()在Vector班級。acceleration傳回的對象是Vector.__truediv__(),這也是Vector對象。

最後,用加速度來增加速度。該方法計算出與一個時間機關相關的值,在本仿真中是控制仿真的循環疊代所需的時間。二.reverse參數確定對第二個物體施加相反的加速度,因為這兩個物體是互相拉伸的。這個*操作員再次調用Vector.__mul__()并導緻标量乘法。

現在你可以計算出任何兩個天體之間的互相作用,你可以計算出太陽系中所有天體之間的互相作用。你可以把你的注意力轉移到SolarSystem類的類:

這個calculate_all_body_interactions()方法貫穿太陽系的所有天體。每個天體與太陽系中的其他天體互相作用:

你用的是self.bodies以滿足天體在循環過程中被從太陽系中移除的可能性。在你在本文中所寫的版本中,你不會從太陽系中移除任何物體。但是,如果您進一步擴充這個項目,您可能需要在将來這樣做。

為了確定您的代碼不會兩次計算同一兩個主體之間的互動,您隻需要計算出一個實體與清單中跟随它的那些主體之間的互動。這就是為什麼你要用切片idx + 1:在第二個for循環。

最後一行調用accelerate_due_to_gravity()對于第一主體,并包括第二主體作為方法的論證。

現在,您已經準備好建立一個簡單的太陽系,并測試您到目前為止編寫的代碼。

在這個項目中,您将關注建立兩種類型的天體之一:太陽和行星。您可以為這些機建構立兩個類。新類繼承自SolarSystemBody :

這個Sun類的預設品質為10,000個機關,并将顔色設定為黃色。使用字元串'yellow',這是Matplotlib中的有效顔色。

在Planet類建立一個itertools.cycle對象有三種顔色。在這種情況下,這三種顔色是紅色、綠色和藍色。你可以使用你想要的任何RGB顔色,也可以使用任意數量的顔色。在這個類中,使用帶有RGB值的元組來定義顔色,而不是使用顔色名稱的字元串。這也是在Matplotlib中定義顔色的有效方法。使用next()每當你建立一個新的行星時。

您還将預設品質設定為10個單元。

現在,你可以建立一個太陽系,其中一個太陽和兩個行星在simple_solar_system.py :

在這個腳本中,您建立了一個太陽和兩個行星。你把太陽和行星配置設定給變量sun和planets,但這并不是嚴格要求的,因為Sun和Planet對象被建立,它們被添加到solar_system你不需要直接引用它們。

你用一個while循環來運作模拟。循環在每次疊代中執行三個操作。運作此腳本時,将獲得以下動畫:

它起作用了,算是吧。你可以看到太陽錨定在這個太陽系的中心,行星受到太陽引力的影響。除了行星在包含你電腦螢幕的平面上的運動(這些是y-和z--軸),你也可以看到行星越來越大,因為它們也在x-軸,垂直于螢幕。

然而,你可能已經注意到行星的一些奇怪的行為。當它們被安排在太陽後面時,行星仍然被展示在太陽的前面。這不是數學上的問題--如果你跟蹤行星的位置,你會發現x-坐标顯示,它們實際上是在太陽後面,正如你所預料的那樣。

這個問題來自Matplotlib在繪圖中繪制對象的方式。Matplotlib按繪制對象的順序将對象按層繪制。因為你在行星之前創造了太陽,Sun對象放在第一位solar_system.bodies并作為底層繪制。您可以通過在行星之後建立太陽來驗證這一事實,在這種情況下,您将看到行星總是出現在太陽後面。

你會希望Matplotlib按照正确的順序繪制太陽系的天體,從最前的那些天體開始。要實作這一點,您可以對SolarSystem.bodies的值為基礎的清單。x-協調每次重新整理3D圖形的時間。下面是如何在update_all()方法SolarSystem :

使用List方法sort帶着key參數來定義要用于排序清單的規則。這個lambda函數設定此規則。在本例中,您使用的值是position[0]表示x-協調。是以,每次你打電話update_all()在模拟中while循環中,根據其沿x-軸心。

運作simple_solar_system.py現在的腳本如下:

現在,你可以想象行星的軌道,就像它們圍繞太陽運作一樣。不斷變化的大小顯示了它們的x-位置,當行星在太陽後面時,它們被隐藏在視線之外!

最後,你也可以移除軸線和網格,這樣你在模拟中看到的就是太陽和行星。可以通過添加對Matplotlib的調用來做到這一點。axis()方法SolarSystem.draw_all() :

模拟現在看起來是這樣的:

使用Matplotlib對Python中的一個三維太陽系進行的模拟現在已經完成。在下一節中,您将添加一個功能,允許您檢視XY-模拟底部的飛機。這有助于可視化太陽系中物體的三維動力學。

在Python的三維太陽系模拟中,為了幫助可視化身體的運動,您可以在動畫的“地闆”上添加一個2D投影。這個2D投影将顯示物體在XY-飛機。要實作這一點,您需要将另一個繪圖添加到顯示動畫的相同軸上,并且隻需在x-和y-坐标。你可以錨定z-與圖形底部協調,使2D投影顯示在動畫的地闆上。

您可以首先将一個新參數添加到__init__()方法的SolarSystem班級:

新參數projection_2d,預設為False,将允許您在兩個可視化選項之間切換。如果projection_2d是False動畫将隻顯示身體在3D中移動,沒有軸和網格,就像你最後看到的結果一樣。

讓我們開始做一些改變projection_2d是True :

您所做的更改如下:

在……裡面SolarSystem.__init__(),則3d視圖設定為view_init(0, 0)當2D投影被關閉時,就像以前一樣。但是,當打開2D投影選項以允許底部平面可見時,高程将更改為10。

在……裡面SolarSystem.draw_all(),隻有當沒有2D投影時,才會關閉網格和軸。當啟用2D投影時,将顯示軸和網格。但是,由于這三個軸上的數字是任意的,是以不需要用空格代替勾号。

在……裡面SolarSystemBody.draw(),則添加第二個情節。projection_2d是True。前兩個論點plot()身體是x-和y-職位。但是,而不是使用z位置作為第三個參數,您使用的最小值是z它表示包含三個軸的立方體的“地闆”。然後,繪制一個灰色标記,大小為動畫中主要标記的一半。

您還需要在simple_solar_system.py打開2D投影:

模拟現在看起來如下:

的二維投影XY-平面使它更容易跟随軌道物體的路徑。

我們将用Python完成另一個三維太陽系的模拟。您将使用已經定義的類來模拟雙星系統。建立一個名為binary_star_system.py創造兩個太陽和兩個行星:

對這個雙星系統的模拟如下:

或者,您可以在建立SolarSystem目的:

此版本提供了以下結果:

這個雙星系統是不穩定的,兩顆行星很快就被兩個太陽抛出了這個系統!

如果您願意,您可以擴充類定義,以檢測兩個天體之間的碰撞,如果行星與太陽碰撞,則将其移除。這個項目的更簡單的2D版本,其中在2D中模拟軌道行星,包括此功能。如果您想将它添加到這個項目中,您可以檢視它是如何在這個簡單的項目中實作的。

本文中使用的代碼的最終版本也是此GitHub回購提供 .

現在,您可以使用Matplotlib在Python中模拟一個3D太陽系。在本文中,您已經學習了如何使用向量和Matplotlib的圖形功能将對象放置在3D空間中。您可以了解更多有關如何使用Matplotlib的内容,包括使用animations在Matplotlib中的子子產品,在本章中使用Matplotlib的Python資料可視化基礎Python編碼書。

這就完成了兩個部分的軌道行星系列。在這個系列的第一篇文章中,您隻考慮了2D場景,并使用了turtle子產品建立圖形動畫。。在您剛剛完成的第二篇文章中,您使用Matplotlib檢視了Python中的一個3D太陽系,用于動畫的圖形表示。

現在輪到你嘗試創造簡單而更複雜的太陽系了。你能創造一個穩定的雙星系統嗎?

我希望你喜歡用Matplotlib在Python中模拟一個3D太陽系。現在,您已經準備好嘗試建立您自己的真實世界過程模拟。

原文:Https://thepythoncodingbook.com/2021/12/11/simulating-3d-solar-system-python-matplotlib/

繼續閱讀