练习:2D轨道力学模拟(Python)


12

事先只是一点免责声明:我从来没有研究过天文学或任何与此相关的精确科学(甚至没有研究过IT),所以我试图通过自我教育来填补这一空白。天文学是引起我注意的领域之一,而我的自我教育理念是应用方法。因此,直截了当-这是我有时间/心情时随便正在研究的轨道仿真模型。我的主要目标是创建一个完整的运动中的太阳系,并具有计划将航天器发射到其他行星的能力。

您都可以随时选择这个项目,并享受有趣的实验!

更新!!!(11月10日)

  • 速度现在是适当的deltaV,并且通过给予附加运动来计算速度的和向量
  • 您可以在每个运动的单位对象上放置任意数量的静态对象,以检查所有来源的重力矢量(并检查碰撞)
  • 大大提高了计算性能
  • 在matplotlib中解决交互式mod的问题。看起来这是仅适用于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个单位为1000 km,但是也可以更改),以网格为单位的半径和以kg为单位的质量。
  2. 给对象一些deltaV,例如,Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)显然需要Craft = Object(...)首先创建,如前所述。此处的参数deltaV以m / s为单位(请注意,现在加速度是瞬时的),motionDirection是以度为单位的deltaV的方向(从当前位置想象物体周围360度的圆,因此方向是该圆上的一个点),最后参数time是多少秒之后,将监视对象的delV推入轨迹。随后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)

经过两次燃烧-我在地球轨道上进行了一次前进,而在月球轨道上进行了一次倒退,我获得了稳定的月球轨道。这些接近理论上的期望值吗?

建议的运动:尝试3次燃烧-从地球表面稳定地球轨道,前进燃烧以到达月球,逆行燃烧以稳定绕月球的轨道。然后尝试最小化deltaV。

注意:我计划为不熟悉python3语法的人使用大量注释来更新代码。


自我教育的好主意!是否可以为不熟悉Python语法的我们的人总结您的公式?

我觉得是的。我将在代码中为想要选择它的人做更广泛的评论,并总结问题本身的一般逻辑。
Statespace 2014年

让我烦恼的是:考虑对速度使用向量,而不是不同地对待速度和方向。当您说“ F = m * v”时,您是说“ F = m * a”吗?您以为地球不动是因为它比小行星重得多?考虑查看github.com/barrycarter/bcapps/blob/master/bc-grav-sim.pl
barrycarter 2014年

您可以使任何物体运动,包括地球。出于测试目的,我在主循环中仅包含对象->地球关系。每个对象都与创建的所有其他对象相关,可以很容易地进行转换。每个对象都可以有自己的运动矢量。我没有这样做的原因-即使是1个对象,计算速度也很慢。我希望缩放时间单位可以提供很多帮助,但是我仍然不确定如何正确地做到这一点。
Statespace 2014年

1
好。一个想法:是否对两个真实物体(例如,地球/月球或地球/太阳)进行了仿真,并将您的结果与ssd.jpl.nasa.gov/?horizo​​ns进行了准确性比较?由于其他来源的干扰,它并不是完美的,但是它会让您对准确性有所了解吗?
barrycarter 2014年

Answers:


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或多步方法求解它们。

如果应用简单的东西,例如前向欧拉或后向欧拉,您将分别看到地球旋转成无限远或朝着太阳旋转,但这是数值误差的影响。如果您使用更精确的方法,例如经典的四阶Runge-Kutta方法,您会发现它会在一段时间内接近真实轨道,但最终仍会变为无穷大。正确的方法是使用辛方法,它将使地球保持在正确的轨道上-尽管由于数值误差其相位仍然会偏离。

对于2体问题,可以通过将坐标系围绕重心为基础来推导更简单的系统。但是我认为,如果您不熟悉上述内容,那么上面的表述会更清楚。


这将需要一些时间来消化。
Statespace 2014年

仍在消化。对我来说,太多未知的单词,但不知何故,我觉得在某个时候我会到达那里。现在,我自己的算法足以使事情简单地工作。但是,当我插入同步运动时-我将不得不阅读文献并阅读适当的算法。鉴于现代硬件的局限性要宽松得多,因此我可以愚弄简单的方程式。怕不长久。
statespace 2014年

确实,辛方法是迄今为止最准确的方法,但我认为对于没有科学背景的人来说,实施它们是困难的。相反,您可以使用非常简单的Euler方法以及Feynman校正。我认为您不需要比自我教育更复杂的东西。
chrispap 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.