Alıştırma: 2D yörünge mekaniği simülasyonu (python)


12

Önceden küçük bir feragatname: Astronomi veya bu konuda herhangi bir kesin bilimi (BT bile değil) hiç çalışmadım, bu yüzden bu boşluğu kendi kendine eğitim ile doldurmaya çalışıyorum. Astronomi dikkatimi çeken alanlardan biri ve kendi kendine eğitim fikrim uygulamalı yaklaşıma yöneliyor. Yani, doğrudan - bu, zaman / ruh hali olduğunda rasgele üzerinde çalıştığım yörünge simülasyon modeli. Benim asıl amacım hareket halinde tam bir güneş sistemi oluşturmak ve diğer gezegenlere uzay aracı fırlatma planlamak için yeteneği.

Bu projeyi istediğiniz zaman alabilir ve eğlenceli deneyler yapabilirsiniz!

Güncelleme!!! (Nov10)

  • hız artık uygun deltaV ve ek hareket vermek artık hızın toplam vektörünü hesaplıyor
  • hareket halindeki birim nesnenin tüm kaynaklardan yerçekimi vektörlerini kontrol ettiğinde (ve çarpışma olup olmadığını kontrol ettiğinizde) istediğiniz kadar statik nesne yerleştirebilirsiniz.
  • hesaplamaların performansını büyük ölçüde geliştirdi
  • matplotlib'deki etkileşimli modun hesabına yönelik bir düzeltme. Bu sadece ipython için varsayılan seçenek gibi görünüyor. Düzenli python3 bu ifadeyi açıkça gerektirir.

Temel olarak, Dünya'nın yüzeyinden bir uzay aracını "başlatmak" ve giveMotion () aracılığıyla deltaV vektör düzeltmeleri yaparak Ay'a bir görev çizmek mümkündür. Sırada, uzay aracı yerçekimi destek manevrasını denerken, eş zamanlı hareket sağlamak için küresel zaman değişkenini uygulamaya çalışıyor.

İyileştirmeler için yorum ve öneriler her zaman bekleriz!

Python3'te matplotlib kütüphanesi ile yapıldı

import matplotlib.pyplot as plt
import math
plt.ion()

G = 6.673e-11  # gravity constant
gridArea = [0, 200, 0, 200]  # margins of the coordinate grid
gridScale = 1000000  # 1 unit of grid equals 1000000m or 1000km

plt.clf()  # clear plot area
plt.axis(gridArea)  # create new coordinate grid
plt.grid(b="on")  # place grid

class Object:
    _instances = []
    def __init__(self, name, position, radius, mass):
        self.name = name
        self.position = position
        self.radius = radius  # in grid values
        self.mass = mass
        self.placeObject()
        self.velocity = 0
        Object._instances.append(self)

    def placeObject(self):
        drawObject = plt.Circle(self.position, radius=self.radius, fill=False, color="black")
        plt.gca().add_patch(drawObject)
        plt.show()

    def giveMotion(self, deltaV, motionDirection, time):
        if self.velocity != 0:
            x_comp = math.sin(math.radians(self.motionDirection))*self.velocity
            y_comp = math.cos(math.radians(self.motionDirection))*self.velocity
            x_comp += math.sin(math.radians(motionDirection))*deltaV
            y_comp += math.cos(math.radians(motionDirection))*deltaV
            self.velocity = math.sqrt((x_comp**2)+(y_comp**2))

            if x_comp > 0 and y_comp > 0:  # calculate degrees depending on the coordinate quadrant
                self.motionDirection = math.degrees(math.asin(abs(x_comp)/self.velocity))  # update motion direction
            elif x_comp > 0 and y_comp < 0:
                self.motionDirection = math.degrees(math.asin(abs(y_comp)/self.velocity)) + 90
            elif x_comp < 0 and y_comp < 0:
                self.motionDirection = math.degrees(math.asin(abs(x_comp)/self.velocity)) + 180
            else:
                self.motionDirection = math.degrees(math.asin(abs(y_comp)/self.velocity)) + 270

        else:
            self.velocity = self.velocity + deltaV  # in m/s
            self.motionDirection = motionDirection  # degrees
        self.time = time  # in seconds
        self.vectorUpdate()

    def vectorUpdate(self):
        self.placeObject()
        data = []

        for t in range(self.time):
            motionForce = self.mass * self.velocity  # F = m * v
            x_net = 0
            y_net = 0
            for x in [y for y in Object._instances if y is not self]:
                distance = math.sqrt(((self.position[0]-x.position[0])**2) +
                             (self.position[1]-x.position[1])**2)
                gravityForce = G*(self.mass * x.mass)/((distance*gridScale)**2)

                x_pos = self.position[0] - x.position[0]
                y_pos = self.position[1] - x.position[1]

                if x_pos <= 0 and y_pos > 0:  # calculate degrees depending on the coordinate quadrant
                    gravityDirection = math.degrees(math.asin(abs(y_pos)/distance))+90

                elif x_pos > 0 and y_pos >= 0:
                    gravityDirection = math.degrees(math.asin(abs(x_pos)/distance))+180

                elif x_pos >= 0 and y_pos < 0:
                    gravityDirection = math.degrees(math.asin(abs(y_pos)/distance))+270

                else:
                    gravityDirection = math.degrees(math.asin(abs(x_pos)/distance))

                x_gF = gravityForce * math.sin(math.radians(gravityDirection))  # x component of vector
                y_gF = gravityForce * math.cos(math.radians(gravityDirection))  # y component of vector

                x_net += x_gF
                y_net += y_gF

            x_mF = motionForce * math.sin(math.radians(self.motionDirection))
            y_mF = motionForce * math.cos(math.radians(self.motionDirection))
            x_net += x_mF
            y_net += y_mF
            netForce = math.sqrt((x_net**2)+(y_net**2))

            if x_net > 0 and y_net > 0:  # calculate degrees depending on the coordinate quadrant
                self.motionDirection = math.degrees(math.asin(abs(x_net)/netForce))  # update motion direction
            elif x_net > 0 and y_net < 0:
                self.motionDirection = math.degrees(math.asin(abs(y_net)/netForce)) + 90
            elif x_net < 0 and y_net < 0:
                self.motionDirection = math.degrees(math.asin(abs(x_net)/netForce)) + 180
            else:
                self.motionDirection = math.degrees(math.asin(abs(y_net)/netForce)) + 270

            self.velocity = netForce/self.mass  # update velocity
            traveled = self.velocity/gridScale  # grid distance traveled per 1 sec
            self.position = (self.position[0] + math.sin(math.radians(self.motionDirection))*traveled,
                             self.position[1] + math.cos(math.radians(self.motionDirection))*traveled)  # update pos
            data.append([self.position[0], self.position[1]])

            collision = 0
            for x in [y for y in Object._instances if y is not self]:
                if (self.position[0] - x.position[0])**2 + (self.position[1] - x.position[1])**2 <= x.radius**2:
                    collision = 1
                    break
            if collision != 0:
                print("Collision!")
                break

        plt.plot([x[0] for x in data], [x[1] for x in data])

Earth = Object(name="Earth", position=(50.0, 50.0), radius=6.371, mass=5.972e24)
Moon = Object(name="Moon", position=(100.0, 100.0), radius=1.737, mass = 7.347e22)  # position not to real scale
Craft = Object(name="SpaceCraft", position=(49.0, 40.0), radius=1, mass=1.0e4)

Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)
Craft.giveMotion(deltaV=2000.0, motionDirection=90, time=60000)
plt.show(block=True)

Nasıl çalışır

Her şey iki şeye kadar kaynar:

  1. Earth = Object(name="Earth", position=(50.0, 50.0), radius=6.371, mass=5.972e24)Izgara üzerindeki konum parametreleriyle (1 birim ızgara varsayılan olarak 1000km'dir, ancak bu da değiştirilebilir), ızgara birimlerinde yarıçap ve kg cinsinden kütle gibi nesne oluşturma .
  2. Nesneye Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)açık bir şekilde deltaV vermek gibi Craft = Object(...), ilk noktada daha önce belirtildiği gibi yaratılması gerekir . Buradaki parametreler deltaVm / s cinsindendir (şimdilik ivmenin anlık olduğunu unutmayın), motionDirectiondeltaV'ın derece cinsinden yönüdür (mevcut konumdan nesnenin etrafında 360 derece daire hayal edin, bu nedenle yön o dairenin üzerindeki bir noktadır) ve son olarak parametre timekaç saniye sonra nesnenin deltaV itme yörünge izlenecektir. Sonraki öncekinin giveMotion()son konumundan başlayarak giveMotion().

Sorular:

  1. Bu yörüngeleri hesaplamak için geçerli bir algoritma mı?
  2. Yapılacak belirgin iyileştirmeler nelerdir?
  3. Her saniye için vektörleri ve konumları yeniden hesaplamak gerekmeyebileceğinden, hesaplamaları optimize edecek "timeScale" değişkenini göz önünde bulunduruyorum. Nasıl uygulanması gerektiğine dair herhangi bir düşünce ya da genellikle iyi bir fikir mi? (gelişmiş performansa karşı doğruluk kaybı)

Temel olarak amacım konuyla ilgili bir tartışma başlatmak ve nereye götürdüğünü görmek. Ve mümkünse, yeni ve ilginç bir şey öğrenin (veya daha iyisi - öğretin).

Denemekten çekinmeyin!

Kullanmayı deneyin:

Earth = Object(name="Earth", position=(50.0, 100.0), radius=6.371, mass=5.972e24)
Moon = Object(name="Moon", position=(434.0, 100.0), radius=1.737, mass = 7.347e22)
Craft = Object(name="SpaceCraft", position=(43.0, 100.0), radius=1, mass=1.0e4)

Craft.giveMotion(deltaV=10575.0, motionDirection=180, time=322000)
Craft.giveMotion(deltaV=400.0, motionDirection=180, time=50000)

İki yanıkla - Dünya yörüngesinde bir ilerleme ve Ay yörüngesinde bir retrograd, kararlı Ay yörüngesine ulaştım. Bunlar teorik olarak beklenen değerlere yakın mı?

Önerilen egzersiz: 3 yanıkta deneyin - Dünya yüzeyinden kararlı Dünya yörüngesi, Ay'a ulaşmak için yanmayı ilerletin, Ay çevresindeki yörüngeyi stabilize etmek için yanmayı retrograd yapın. Ardından deltaV'ı en aza indirmeye çalışın.

Not: Kodu python3 sözdizimine aşina olmayanlar için kapsamlı yorumlarla güncellemeyi planlıyorum.


Kendi kendine eğitim için çok iyi bir fikir! Python sözdizimine aşina olmayanlarımız için formüllerinizi özetlemek mümkün müdür?

Tabii, sanırım. Kodda onu almak ve sorunun kendisindeki genel mantığı özetlemek isteyenler için daha kapsamlı yorumlar yapacağım.
statespace

Başımın üstünden: hız ve yönü farklı işlemek yerine hız için bir vektör kullanmayı düşünün. Nerede "F = m * v" demek istersiniz "F = m * a"? Dünya'nın hareket etmediğini varsayıyorsunuz çünkü asteroitten çok daha ağır mı? Github.com/barrycarter/bcapps/blob/master/bc-grav-sim.pl
barrycarter

Dünya da dahil olmak üzere herhangi bir nesneye hareket verebilirsiniz. Test amacıyla ana döngüye sadece nesne -> Dünya ilişkisi ekledim. Her nesnenin, oluşturulan tüm diğer nesnelerle ilgili olduğu kolayca dönüştürülebilir. Ve her nesnenin kendi hareket vektörü olabilir. Neden yapmadım - 1 nesne için bile çok yavaş hesaplamalar. Umarım ölçekleme birimleri çok yardımcı olur ama hala nasıl doğru emin değilim.
Statespace

1
TAMAM. Bir düşünce: iki gerçek nesne için simülasyon yapın (örneğin, Dünya / Ay veya Dünya / Güneş) ve sonuçlarınızı doğruluk için ssd.jpl.nasa.gov/?horizons ile karşılaştırın ? Diğer kaynaklardan kaynaklanan bozulmalar nedeniyle mükemmel olmayacak, ancak size doğruluk hakkında bir fikir verecek mi?
barrycarter

Yanıtlar:


11

m1,m2

F=ma
a

F21=Gm1m2|r21|3r21

r21F12=F21r12=r21(x1,y1)(x2,y2)

r21=(x1x2y1y2).

ve

|r|=(x1x2)2+(y1y2)2.
a=F/m

x1(t)=Gm2(x2x1)|r|3y1(t)=Gm2(y2y1)|r|3x2(t)=Gm1(x1x2)|r|3y2(t)=Gm1(y1y2)|r|3.

Başlangıç ​​pozisyonları ve hızları ile birlikte, bu adi diferansiyel denklemler sistemi (ODE'ler) bir başlangıç ​​değeri problemi içerir. Genel yaklaşım, bunu 8 denklemin birinci dereceden bir sistemi olarak yazmak ve çözmek için bir Runge-Kutta veya çok aşamalı yöntem uygulamaktır.

İleri Euler veya geri Euler gibi basit bir şey uygularsanız, Dünya'nın sırasıyla sonsuzluğa veya güneşe doğru sarıldığını göreceksiniz, ancak bu sayısal hataların bir etkisidir. Klasik 4. dereceden Runge-Kutta yöntemi gibi daha doğru bir yöntem kullanırsanız, bir süre gerçek bir yörüngeye yakın olduğunu, ancak yine de sonsuza kadar gittiğini göreceksiniz. Doğru yaklaşım, dünyayı doğru yörüngede tutacak olan sezgisel bir yöntem kullanmaktır - ancak sayısal hatalar nedeniyle fazı hala kapalı olacaktır.

2 cisim problemi için koordinat sisteminizi kütle merkezi etrafında toplayarak daha basit bir sistem elde etmek mümkündür. Ama bence yukarıdaki formülasyon bu sizin için yeniyse daha açık.


Sindirimi biraz zaman alacaktır.
statespace

Hala sindiriliyor. Benim için çok fazla bilinmeyen kelime var ama her nasılsa bir noktada oraya varacağımı hissediyorum. Şimdilik kendi algoritmam işler için yeterli. Fakat eşzamanlı hareketi eklediğimde - literatüre girmeye ve uygun algoritmaları okumaya zorlanacağım. Modern donanım sınırlamalarının çok daha gevşek olduğu göz önüne alındığında, basit denklemlerle dalga geçmeyi göze alabilirim. Uzun süre korkmuyor.
devlet alanı

Aslında, sezgisel yöntemler açık ara en doğru olanıdır, ancak bilimde geçmişi olmayan birinin bunları uygulamak zor olduğunu düşünüyorum. Bunun yerine Feynman düzeltmesiyle birlikte çok basit Euler yöntemini kullanabilirsiniz. Kendi kendine eğitim için bundan daha karmaşık bir şeye ihtiyacınız olduğunu düşünmüyorum.
chrispap
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.