연습 : 2D 궤도 역학 시뮬레이션 (파이썬)


12

사전에 약간의 면책 조항을 보았습니다. 나는 천문학이나 그 문제에 대한 정확한 과학 (IT조차도)을 연구 한 적이 없으므로 자체 교육을 통해이 격차를 메우려 고합니다. 천문학은 저의주의를 끌었던 분야 중 하나이며 자기 교육에 대한 나의 생각은 응용 접근법에 대한 머리입니다. 요컨대, 시간 / 분위기가있을 때 우연히 작업하는 궤도 시뮬레이션 모델입니다. 저의 주요 목표는 모션으로 완벽한 태양계를 만들고 다른 행성에 우주선 발사를 계획하는 능력입니다.

언제든지이 프로젝트를 자유롭게 선택하고 즐겁게 실험 할 수 있습니다!

최신 정보!!! (11 월)

  • 속도는 이제 적절한 deltaV이며 추가 모션을 제공하면 속도의 합 벡터가 계산됩니다.
  • 모션에서 모든 소스의 중력 벡터를 검사하고 충돌을 검사 할 때마다 단위 오브젝트에 원하는 수의 정적 오브젝트를 배치 할 수 있습니다.
  • 계산 성능을 크게 향상
  • matplotlib에서 대화식 모드를 설명하기위한 수정. 이것이 ipython의 기본 옵션 인 것 같습니다. 일반 python3에는 해당 명령문이 명시 적으로 필요합니다.

기본적으로 이제 지구 표면에서 우주선을 "발사"하고 giveMotion ()을 통해 deltaV 벡터 보정을 수행하여 달에 대한 임무를 그릴 수 있습니다. 다음으로 우주선이 중력 지원 기동을 시도하는 동안 달 궤도를 도는 지구와 같은 동시 모션을 가능하게하는 전역 시간 변수를 구현하려고합니다.

개선에 대한 의견과 제안은 언제나 환영합니다!

matplotlib 라이브러리를 사용하여 Python3에서 완료

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 개의 그리드 단위는 1000km이지만 변경 될 수 있음), 그리드 단위의 반경 및 kg 단위의 질량 과 같은 객체 생성 .
  2. 객체에 deltaV를 제공하는 Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)것은 분명히 Craft = Object(...)이전 시점에서 언급 한 것처럼 처음부터 생성 해야 합니다. 여기의 매개 변수는 deltaVm / s입니다 (현재 가속은 순간적입니다), motionDirectiondeltaV의 방향은 도입니다 (현재 위치에서 물체 주위의 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)

지구 궤도에서 1 회, 달 궤도에서 1 회 후진으로 2 회의 화상으로 안정적인 달 궤도를 달성했습니다. 이것들은 이론적으로 기대되는 값에 가깝습니까?

제안 된 운동 : 3 번의 화상을 입으십시오-지구 표면에서 안정된 지구 궤도, 달에 도달하기 위해 점진적인 화상, 달 주위의 궤도를 안정시키기 위해 역행 화상. 그런 다음 deltaV를 최소화하십시오.

참고 : python3 구문에 익숙하지 않은 사람들을 위해 광범위한 주석으로 코드를 업데이트 할 계획입니다.


자기 교육에 대한 아주 좋은 아이디어! 파이썬 문법에 익숙하지 않은 사람들을 위해 공식을 요약 할 수 있습니까?

물론입니다. 코드를 선택하고 질문 자체의 일반적인 논리를 요약하려는 사람들을 위해 코드에서 더 광범위한 주석을 작성합니다.
statespace

내 머리 꼭대기에서 : 속도와 방향을 다르게 취급하는 대신 속도에 벡터를 사용하는 것을 고려하십시오. "F = m * v"라고 말하는 곳은 "F = m * a"를 의미합니까? 당신은 지구가 소행성보다 훨씬 무거워 움직이지 않는다고 가정합니까? 보고 고려 github.com/barrycarter/bcapps/blob/master/bc-grav-sim.pl
barrycarter

지구를 포함한 모든 물체에 움직임을 줄 수 있습니다. 테스트 목적으로 메인 루프에는 객체-> 지구 관계 만 포함했습니다. 모든 객체가 생성 된 다른 모든 객체와 관련이 있음을 쉽게 변환 할 수 있습니다. 그리고 모든 객체는 자체 모션 벡터를 가질 수 있습니다. 내가하지 않은 이유-1 개체에 대해서도 계산 속도가 느립니다. 시간 단위를 조정하면 많은 도움이되기를 바랍니다.하지만 여전히 올바른 방법을 모르겠습니다.
statespace

1
확인. 생각 : 두 개의 실제 객체 (예 : Earth / Moon 또는 Earth / Sun)에 대한 시뮬레이션을 수행하고 결과를 ssd.jpl.nasa.gov/?horizons 와 비교하여 정확도 를 비교 합니까? 다른 출처의 동요 때문에 완벽하지는 않지만 정확성에 대한 아이디어를 줄 수 있습니까?
barrycarter

답변:


11

관련된 두 바디의 질량이 합니다. 뉴턴의 두 번째 법칙 로 시작 . 여기서 는 가속입니다. 1 번 몸체에서 2 번 몸체의 중력은m1,m2

F=ma
a

F21=Gm1m2|r21|3r21

여기서 은 문제가되는 두 몸체의 상대 위치 벡터입니다. 본문 2의 본문 1에 대한 힘은 이후 입니다. 위치를 및 로 표시하면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 등식의 1 차 시스템으로 작성하고 Runge-Kutta 또는 다단계 방법을 적용하여 해결합니다.

전방 오일러 또는 후방 오일러와 같은 간단한 것을 적용하면 지구가 각각 무한대로 또는 태양을 향해 나아지는 것을 볼 수 있지만 이는 수치 오류의 영향입니다. 고전적인 4 차 Runge-Kutta 방법과 같이보다 정확한 방법을 사용하면 한동안 실제 궤도에 가깝게 유지되지만 결국에는 무한대로 진행됩니다. 올바른 접근 방법은 상징적 인 방법을 사용하여 지구를 올바른 궤도에 유지하는 것입니다. 수치 오류로 인해 위상이 여전히 꺼져 있습니다.

2 체 문제의 경우, 질량 중심을 중심으로 좌표계를 기반으로하여보다 간단한 시스템을 도출 할 수 있습니다. 그러나 이것이 새로운 것이면 위의 공식이 더 명확하다고 생각합니다.


이 과정을 소화하는 데 시간이 걸립니다.
statespace

여전히 소화 중입니다. 나에게 알려지지 않은 단어가 너무 많지만 어느 시점에서 나는 그곳에 갈 것이라고 생각한다. 현재로서는 내 알고리즘이 간단하게 작동하기에 충분합니다. 그러나 동시에 모션을 연결하면 문학에 가서 적절한 알고리즘에 대해 읽게됩니다. 현대 하드웨어의 한계가 훨씬 느슨하다는 점을 감안할 때 간단한 방정식으로 속일 수 있습니다. 그래도 오랫동안 두려워하지 마십시오.
statespace

실제로 상징적 인 방법은 훨씬 정확하지만 과학에 대한 지식이없는 사람이 구현하는 것이 어렵다고 생각합니다. 대신 Feynman 보정과 함께 매우 간단한 Euler 방법을 사용할 수 있습니다. 자기 교육 목적보다 복잡한 것이 필요하다고 생각하지 않습니다.
chrispap
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.