Bài tập: mô phỏng cơ học quỹ đạo 2D (python)


12

Chỉ cần từ chối một chút trước đó: Tôi chưa bao giờ nghiên cứu thiên văn học hay bất kỳ ngành khoa học chính xác nào cho vấn đề đó (thậm chí không phải CNTT), vì vậy tôi đang cố gắng lấp đầy khoảng trống này bằng cách tự học. Thiên văn học là một trong những lĩnh vực đã thu hút sự chú ý của tôi và ý tưởng tự học của tôi là hướng đến phương pháp ứng dụng. Vì vậy, đi thẳng vào vấn đề - đây là mô hình mô phỏng quỹ đạo mà tôi tình cờ làm việc khi có thời gian / tâm trạng. Mục tiêu chính của tôi là tạo ra hệ mặt trời hoàn chỉnh trong chuyển động và khả năng lên kế hoạch phóng tàu vũ trụ tới các hành tinh khác.

Bạn có thể tự do chọn dự án này bất cứ lúc nào và vui chơi thử nghiệm!

cập nhật !!! (Ngày 10 tháng 11)

  • vận tốc bây giờ là deltaV thích hợp và cho chuyển động bổ sung bây giờ tính toán vectơ tổng của vận tốc
  • bạn có thể đặt bao nhiêu vật thể tĩnh tùy thích, vào mỗi lần đối tượng đơn vị trong chuyển động kiểm tra các vectơ trọng lực từ tất cả các nguồn (và kiểm tra va chạm)
  • cải thiện đáng kể hiệu suất tính toán
  • một bản sửa lỗi cho tài khoản mod tương tác trong matplotlib. Có vẻ như đây là tùy chọn mặc định chỉ dành cho ipython. Python3 thông thường yêu cầu tuyên bố đó rõ ràng.

Về cơ bản, giờ đây có thể "phóng" một tàu vũ trụ từ bề mặt Trái đất và thực hiện một nhiệm vụ lên Mặt trăng bằng cách điều chỉnh vectơ deltaV thông qua giveMotion (). Dòng tiếp theo đang cố gắng thực hiện biến thời gian toàn cầu để cho phép chuyển động đồng thời, ví dụ Mặt trăng quay quanh Trái đất trong khi tàu vũ trụ thử vận ​​động hỗ trợ trọng lực.

Bình luận và đề xuất cải tiến luôn được chào đón!

Thực hiện trong Python3 với thư viện matplotlib

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)

Làm thế nào nó hoạt động

Tất cả sôi sục xuống hai điều:

  1. Tạo đối tượng như Earth = Object(name="Earth", position=(50.0, 50.0), radius=6.371, mass=5.972e24)với các tham số vị trí trên lưới (1 đơn vị lưới là 1000km theo mặc định nhưng điều này cũng có thể thay đổi), bán kính theo đơn vị lưới và khối lượng tính bằng kg.
  2. Cung cấp cho đối tượng một số deltaV như Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)rõ ràng nó cần Craft = Object(...)phải được tạo ở vị trí đầu tiên như đã đề cập ở điểm trước. Các tham số ở đây tính deltaVbằng m / s (lưu ý rằng hiện tại gia tốc là tức thời), motionDirectionlà hướng của deltaV tính theo độ (từ vị trí hiện tại tưởng tượng vòng tròn 360 độ quanh đối tượng, vì vậy hướng là một điểm trên vòng tròn đó) và cuối cùng timelà tham số là bao nhiêu giây sau khi quỹ đạo đẩy deltaV của đối tượng sẽ được theo dõi. Sau đó giveMotion()bắt đầu từ vị trí cuối cùng của trước đó giveMotion().

Câu hỏi:

  1. Đây có phải là một thuật toán hợp lệ để tính toán quỹ đạo?
  2. Những cải tiến rõ ràng sẽ được thực hiện là gì?
  3. Tôi đã xem xét biến "timeScale" sẽ tối ưu hóa các tính toán, vì có thể không cần thiết phải tính toán lại các vectơ và vị trí cho mỗi giây. Bất kỳ suy nghĩ về cách nó nên được thực hiện hoặc nói chung là một ý tưởng tốt? (mất độ chính xác so với hiệu suất được cải thiện)

Về cơ bản mục tiêu của tôi là bắt đầu một cuộc thảo luận về chủ đề này và xem nó dẫn đến đâu. Và, nếu có thể, hãy học (hoặc thậm chí tốt hơn - dạy) một cái gì đó mới và thú vị.

Hãy thử nghiệm!

Hãy thử sử dụng:

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)

Với hai lần đốt - một lần tiến lên quỹ đạo Trái đất và một lần lùi ở quỹ đạo Mặt trăng, tôi đã đạt được quỹ đạo Mặt trăng ổn định. Là những gần với giá trị lý thuyết dự kiến?

Bài tập được đề xuất: Hãy thử trong 3 lần đốt - quỹ đạo Trái đất ổn định từ bề mặt Trái đất, đốt trước để tiếp cận Mặt trăng, đốt ngược để ổn định quỹ đạo quanh Mặt trăng. Sau đó cố gắng giảm thiểu deltaV.

Lưu ý: Tôi dự định cập nhật mã với các bình luận mở rộng cho những người không quen với cú pháp python3.


Một ý tưởng rất tốt cho việc tự giáo dục! Có thể tóm tắt các công thức của bạn cho những người không quen với cú pháp Python không?

Chắc chắn, tôi đoán. Tôi sẽ đưa ra nhiều nhận xét sâu rộng hơn trong mã cho những ai muốn chọn nó và tóm tắt logic chung trong chính câu hỏi.
gian nhà nước

Ra khỏi đỉnh đầu của tôi: xem xét sử dụng một vectơ cho vận tốc thay vì xử lý tốc độ và hướng khác nhau. Nơi bạn nói "F = m * v" có nghĩa là "F = m * a"? Bạn đang cho rằng Trái đất không di chuyển vì nó nặng hơn nhiều so với tiểu hành tinh? Hãy xem xét việc xem github.com/barrycarter/bcapps/blob/master/bc-grav-sim.pl
barrycarter

Bạn có thể đưa ra chuyển động cho bất kỳ đối tượng nào, kể cả Trái đất. Đối với mục đích thử nghiệm, tôi chỉ bao gồm đối tượng -> Quan hệ trái đất trong vòng lặp chính. Nó có thể dễ dàng được chuyển đổi rằng mọi đối tượng liên quan đến tất cả các đối tượng khác được tạo ra. Và mọi đối tượng có thể có vector chuyển động riêng của họ. Lý do tại sao tôi không làm điều đó - tính toán rất chậm ngay cả đối với 1 đối tượng. Tôi hy vọng rằng các đơn vị thời gian mở rộng sẽ giúp rất nhiều nhưng tôi vẫn không chắc làm thế nào để làm điều đó đúng.
gian nhà nước

1
ĐỒNG Ý. Một suy nghĩ: thực hiện mô phỏng cho hai đối tượng thực (ví dụ: Trái đất / Mặt trăng hoặc Trái đất / Mặt trời) và so sánh kết quả của bạn với ssd.jpl.nasa.gov/?horizons cho chính xác? Nó sẽ không hoàn hảo vì nhiễu loạn bởi các nguồn khác, nhưng nó sẽ cho bạn một số ý tưởng về độ chính xác?
barrycarter

Câu trả lời:


11

m1,m2

F=ma
a

F21=Gm1m2|r21|3r21

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

r21=(x1x2y1y2).

|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.

Cùng với các vị trí và vận tốc ban đầu, hệ thống phương trình vi phân thông thường (ODE) này bao gồm một vấn đề giá trị ban đầu. Cách tiếp cận thông thường là viết hệ thống này dưới dạng hệ phương trình bậc nhất gồm 8 phương trình và áp dụng phương pháp Runge-Kutta hoặc multistep để giải chúng.

Nếu bạn áp dụng một cái gì đó đơn giản như Euler tiến hoặc Euler lùi, bạn sẽ thấy Trái đất xoắn ốc ra vô cực hoặc hướng về phía mặt trời, nhưng đó là ảnh hưởng của các lỗi số. Nếu bạn sử dụng một phương pháp chính xác hơn, như phương pháp Runge-Kutta bậc 4 cổ điển, bạn sẽ thấy rằng nó ở gần một quỹ đạo thực sự trong một thời gian nhưng cuối cùng vẫn đi đến vô tận. Cách tiếp cận đúng là sử dụng phương pháp đối xứng, phương pháp này sẽ giữ Trái đất đi đúng quỹ đạo - mặc dù pha của nó vẫn bị tắt do lỗi số.

Đối với bài toán 2 thân, có thể lấy được một hệ thống đơn giản hơn bằng cách dựa vào hệ tọa độ của bạn xung quanh tâm khối lượng. Nhưng tôi nghĩ rằng công thức trên rõ ràng hơn nếu điều này là mới đối với bạn.


Điều này sẽ mất một thời gian để tiêu hóa.
gian tiểu bang

Vẫn tiêu hóa. Quá nhiều từ chưa biết đối với tôi nhưng bằng cách nào đó tôi cảm thấy rằng đến một lúc nào đó tôi sẽ đến đó. Bây giờ thuật toán của riêng tôi đủ tốt để mọi thứ chỉ đơn giản hoạt động. Nhưng khi tôi sẽ cắm vào chuyển động đồng thời - tôi sẽ bị buộc phải tìm đến tài liệu và đọc về các thuật toán thích hợp. Cho rằng những hạn chế của phần cứng hiện đại lỏng lẻo hơn nhiều, tôi có thể đủ khả năng để đánh lừa các phương trình đơn giản. Sợ không lâu mặc dù.
gian tiểu bang

Thật vậy, các phương pháp đối xứng cho đến nay là chính xác nhất nhưng tôi nghĩ rằng rất khó để một người không có nền tảng về khoa học thực hiện chúng. Thay vào đó, bạn có thể sử dụng phương pháp Euler rất đơn giản cùng với hiệu chỉnh Feynman. Tôi không nghĩ rằng bạn cần một cái gì đó phức tạp hơn thế cho mục đích tự giáo dục.
chrispap
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.