การออกกำลังกาย: การจำลองกลไกการโคจร 2D (หลาม)


12

ขอสงวนสิทธิ์เพียงเล็กน้อยก่อน: ฉันไม่เคยศึกษาดาราศาสตร์หรือวิทยาศาสตร์ที่แน่นอนสำหรับเรื่องนั้น (ไม่ใช่แม้แต่เรื่องไอที) ดังนั้นฉันจึงพยายามเติมช่องว่างนี้ด้วยการศึกษาด้วยตนเอง ดาราศาสตร์เป็นหนึ่งในพื้นที่ที่ได้รับความสนใจจากฉันและความคิดของฉันเกี่ยวกับการศึกษาด้วยตนเองนั้นมุ่งไปที่วิธีการประยุกต์ ตรงประเด็น - นี่คือแบบจำลองการโคจรที่ฉันตั้งใจทำงานเมื่อฉันมีเวลา / อารมณ์ เป้าหมายหลักของฉันคือการสร้างระบบสุริยะที่สมบูรณ์ในการเคลื่อนที่และความสามารถในการวางแผนการเปิดตัวยานอวกาศไปยังดาวเคราะห์ดวงอื่น

คุณมีอิสระที่จะเลือกโครงการนี้ได้ทุกเมื่อและสนุกไปกับการทดลอง!

ปรับปรุง !!! (Nov10)

  • velocity เป็น deltaV ที่เหมาะสมและให้การเคลื่อนไหวเพิ่มเติมจะคำนวณผลรวมเวกเตอร์ของ velocity
  • คุณสามารถวางวัตถุแบบคงที่ได้มากเท่าที่คุณต้องการในทุก ๆ ครั้งที่วัตถุในการตรวจสอบการเคลื่อนไหวสำหรับเวกเตอร์แรงโน้มถ่วงจากทุกแหล่ง (และตรวจสอบการชนกัน)
  • ปรับปรุงประสิทธิภาพการคำนวณอย่างมาก
  • การแก้ไขเพื่อบัญชีสำหรับ mod แบบโต้ตอบใน matplotlib ดูเหมือนว่านี่เป็นตัวเลือกเริ่มต้นสำหรับ ipython เท่านั้น python3 ปกติต้องการคำสั่งนั้นอย่างชัดเจน

โดยทั่วไปตอนนี้เป็นไปได้ที่จะ "ส่ง" ยานอวกาศจากพื้นผิวโลกและวางแผนภารกิจสู่ดวงจันทร์ด้วยการแก้ไขเวกเตอร์ deltaV ผ่านทาง giveMotion () ในแถวถัดไปกำลังพยายามที่จะใช้ตัวแปรเวลาทั่วโลกเพื่อเปิดใช้งานการเคลื่อนไหวพร้อมกันเช่นดวงจันทร์โคจรรอบโลกในขณะที่ยานอวกาศพยายามใช้แรงโน้มถ่วงช่วยในการจัดทำ

ความคิดเห็นและข้อเสนอแนะสำหรับการปรับปรุงยินดีต้อนรับเสมอ!

ทำใน Python3 พร้อมกับ matplotlib library

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)

มันทำงานอย่างไร

ทุกอย่างเดือดลงไปสองสิ่ง:

  1. การสร้างวัตถุเช่นเดียวEarth = Object(name="Earth", position=(50.0, 50.0), radius=6.371, mass=5.972e24)กับพารามิเตอร์ของตำแหน่งบนกริด (1 หน่วยของกริดคือ 1,000 กม. โดยค่าเริ่มต้น แต่สามารถเปลี่ยนแปลงได้เช่นกัน) รัศมีในหน่วยกริดและมวลหน่วยเป็นกิโลกรัม
  2. การให้วัตถุ deltaV บางตัวเช่นCraft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)เห็นได้ชัดว่าจำเป็นต้องCraft = Object(...)สร้างขึ้นในตำแหน่งแรกตามที่กล่าวไว้ในจุดก่อนหน้า พารามิเตอร์ที่นี่มีหน่วยเป็นdeltaVm / s (โปรดทราบว่าตอนนี้การเร่งความเร็วเป็นแบบทันที) motionDirectionคือทิศทางของ deltaV เป็นองศา (จากตำแหน่งปัจจุบันจินตนาการถึงวงกลม 360 องศารอบวัตถุดังนั้นทิศทางคือจุดหนึ่งในวงกลมนั้น) และพารามิเตอร์สุดท้ายtimeคือกี่วินาที หลังจากวิถีการเคลื่อนที่แบบกดของ deltaV ของวัตถุจะถูกตรวจสอบ giveMotion()เริ่มต้นครั้งต่อไปจากตำแหน่งสุดท้ายของหน้าที่giveMotion()แล้ว

คำถาม:

  1. นี่เป็นอัลกอริทึมที่ถูกต้องในการคำนวณวงโคจรหรือไม่?
  2. การปรับปรุงที่ชัดเจนที่จะทำคืออะไร?
  3. ฉันกำลังพิจารณาตัวแปร "timeScale" ที่จะเพิ่มประสิทธิภาพการคำนวณเนื่องจากอาจไม่จำเป็นต้องคำนวณเวกเตอร์และตำแหน่งใหม่ทุกวินาที มีความคิดเห็นเกี่ยวกับวิธีการนำไปปฏิบัติหรือเป็นความคิดที่ดีหรือไม่? (การสูญเสียความถูกต้องเทียบกับการปรับปรุงประสิทธิภาพ)

โดยทั่วไปเป้าหมายของฉันคือการเริ่มต้นการอภิปรายในหัวข้อและดูว่ามันนำไปสู่ และถ้าเป็นไปได้เรียนรู้ (หรือดีกว่า - สอน) สิ่งใหม่และน่าสนใจ

ทดลองใช้ฟรี!

ลองใช้:

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)

ด้วยการเบิร์นสองครั้ง - หนึ่งเกรดที่วงโคจรของโลกและหนึ่ง retrograde ที่วงโคจรของดวงจันทร์ฉันบรรลุวงโคจรของดวงจันทร์ที่มั่นคง สิ่งเหล่านี้ใกล้เคียงกับค่าที่คาดหวังทางทฤษฎีหรือไม่?

การออกกำลังกายที่แนะนำ: ลองทำใน 3 เบิร์น - วงโคจรโลกที่เสถียรจากพื้นผิวโลก, โปรเกรดการเผาไหม้ไปถึงดวงจันทร์, การเบิร์นเรเกรดถอยหลังเข้าคลองเพื่อทำให้วงโคจรรอบ ๆ ดวงจันทร์คงที่ จากนั้นลองย่อ deltaV ให้เล็กที่สุด

หมายเหตุ: ฉันวางแผนที่จะอัปเดตโค้ดด้วยความคิดเห็นที่ครอบคลุมสำหรับผู้ที่ไม่คุ้นเคยกับไวยากรณ์ python3


เป็นความคิดที่ดีมากสำหรับการให้การศึกษาด้วยตนเอง! เป็นไปได้ไหมที่จะสรุปสูตรของคุณสำหรับพวกเราที่ไม่คุ้นเคยกับ Python syntax?

แน่นอนฉันเดา ฉันจะทำให้ความคิดเห็นที่ครอบคลุมมากขึ้นในรหัสสำหรับผู้ที่ต้องการหยิบขึ้นมาและสรุปตรรกะทั่วไปในคำถามนั้น
Statespace

จากด้านบนของหัวของฉัน: พิจารณาใช้เวกเตอร์สำหรับความเร็วแทนที่จะรักษาความเร็วและทิศทางแตกต่างกัน คุณพูดว่า "F = m * v" คุณหมายถึง "F = m * a" หรือไม่ คุณกำลังสมมติว่าโลกไม่ขยับเพราะมันหนักกว่าดาวเคราะห์น้อยมาก? ลองพิจารณาที่github.com/barrycarter/bcapps/blob/master/bc-grav-sim.pl
barrycarter

คุณสามารถให้การเคลื่อนไหวกับวัตถุใด ๆ รวมถึงโลก สำหรับวัตถุประสงค์ในการทดสอบฉันรวมเฉพาะวัตถุ -> ความสัมพันธ์ของ Earth ในลูปหลัก สามารถแปลงได้อย่างง่ายดายว่าทุกวัตถุเกี่ยวข้องกับวัตถุอื่น ๆ ทั้งหมดที่สร้างขึ้น และทุกวัตถุสามารถมีเวคเตอร์การเคลื่อนไหวของตัวเองได้ เหตุผลที่ฉันไม่ได้ทำมัน - การคำนวณช้ามากแม้กับวัตถุ 1 ชิ้น ฉันหวังว่าการขยายเวลาจะช่วยได้มาก แต่ก็ยังไม่แน่ใจว่าจะทำอย่างไร
Statespace

1
ตกลง. ความคิด: ทำการจำลองสำหรับวัตถุจริงสองชิ้น (เช่น Earth / Moon หรือ Earth / Sun) และเปรียบเทียบผลลัพธ์ของคุณกับssd.jpl.nasa.gov/?horizonsเพื่อความแม่นยำ มันจะไม่สมบูรณ์แบบเพราะการก่อกวนโดยแหล่งอื่น ๆ แต่มันจะทำให้คุณมีความแม่นยำ
barrycarter

คำตอบ:


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.

เมื่อรวมกับตำแหน่งและความเร็วเริ่มต้นระบบของสมการเชิงอนุพันธ์สามัญ (ODE) นี้ประกอบด้วยปัญหาค่าเริ่มต้น วิธีการปกติคือการเขียนสิ่งนี้เป็นระบบลำดับแรกของ 8 สมการและใช้วิธี Runge-Kutta หรือ multistep เพื่อแก้ปัญหา

หากคุณใช้สิ่งที่เรียบง่ายเช่นไปข้างหน้าออยเลอร์หรือออยเลอร์ด้านหลังคุณจะเห็นโลกหมุนวนออกไปไม่สิ้นสุดหรือเข้าหาดวงอาทิตย์ตามลำดับ แต่นั่นเป็นผลของข้อผิดพลาดเชิงตัวเลข หากคุณใช้วิธีที่แม่นยำยิ่งขึ้นเช่นวิธี Runge-Kutta แบบคลาสสิกลำดับที่ 4 คุณจะพบว่ามันอยู่ใกล้กับวงโคจรที่แท้จริงมาระยะหนึ่ง แต่ในที่สุดก็ยังคงไม่สิ้นสุด วิธีการที่เหมาะสมคือการใช้วิธี symplectic ซึ่งจะทำให้โลกอยู่ในวงโคจรที่ถูกต้อง - แม้ว่าเฟสของมันจะยังคงปิดอยู่เนื่องจากข้อผิดพลาดเชิงตัวเลข

สำหรับปัญหา 2 ตัวนั้นเป็นไปได้ที่จะได้ระบบที่ง่ายขึ้นโดยใช้ระบบพิกัดรอบศูนย์กลางมวล แต่ฉันคิดว่าสูตรด้านบนมีความชัดเจนมากขึ้นถ้านี่เป็นเรื่องใหม่สำหรับคุณ


ขั้นตอนนี้จะใช้เวลาในการย่อย
Statespace

ยังคงย่อยอาหาร มีคำที่ไม่รู้จักมากเกินไปสำหรับฉัน แต่อย่างใดฉันก็รู้สึกว่าในบางจุดฉันจะไปที่นั่น ตอนนี้อัลกอริทึมของฉันเองดีพอที่จะทำให้การทำงานง่ายขึ้น แต่เมื่อฉันจะเสียบการเคลื่อนไหวพร้อมกัน - ฉันจะถูกบังคับให้ไปที่วรรณกรรมและอ่านเกี่ยวกับอัลกอริทึมที่เหมาะสม เนื่องจากข้อ จำกัด ของฮาร์ดแวร์ที่ทันสมัยมีความคลาดเคลื่อนมากฉันสามารถที่จะหลอกด้วยสมการง่ายๆ กลัวไม่นานแม้ว่า
Statespace

อันที่จริงวิธีการ symplectic นั้นถูกต้องที่สุด แต่ฉันคิดว่ามันเป็นเรื่องยากสำหรับคนที่ไม่มีพื้นฐานทางวิทยาศาสตร์ในการใช้พวกเขา แต่คุณสามารถใช้วิธี Euler ง่าย ๆ พร้อมกับการแก้ไข Feynman ฉันไม่คิดว่าคุณต้องการอะไรที่ซับซ้อนกว่านั้นเพื่อจุดประสงค์ในการศึกษาด้วยตนเอง
chrispap
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.