我一直试图用pygame制作代码来模拟简单的引力。目前,只有一个物体(HOM)绕太阳运行。然而,由于我不知道的原因,每当我运行代码时,HOM在开始时在轨道上绕太阳运行,但是当它到达时从太阳加速 离开 距垂直方向约135度。
有谁知道为什么会发生这种情况以及如何解决这个问题?我一直在打印一些变量来尝试解决问题,但到目前为止还没有运气。
代码:
import pygame,sys,time
from math import *
screen=pygame.display.set_mode((800,600))
G = 5
class Object: #Just an object, like a moon or planet
def __init__(self,mass,init_cds,init_vel,orbit_obj='Sun'):
self.mass = mass
self.cds = init_cds
self.velocity = init_vel
self.accel = [0,0]
self.angle = 0
self.orb_obj = orbit_obj
def display(self):
int_cds = (round(self.cds[0]),round(self.cds[1]))#Stores its co-ordinates as floats, has to convert to integers for draw function
pygame.draw.circle(screen,(255,0,0),int_cds,10)
def calc_gravity(self):
if self.orb_obj == 'Sun':
c_x,c_y = 400,300
c_mass = 10000
else:
c_x,c_y = self.orb_obj.cds
c_mass = self.orb_obj.mass
d_x = self.cds[0]-c_x
d_y = self.cds[1]-c_y
dist = sqrt(d_x**2+d_y**2) #Find direct distance
angle = atan(d_x/d_y) #Find angle
print(d_x,d_y)
print(dist,degrees(angle))
if dist == 0:
acc = 0
else:
acc = G*c_mass/(dist**2) #F=G(Mm)/r^2, a=F/m -> a=GM/r^2
print(acc)
acc_x = acc*sin(angle) #Convert acceleration from magnitude+angle -> x and y components
acc_y = acc*cos(angle)
self.accel = [acc_x,acc_y]
print(self.accel)
self.velocity = [self.velocity[0]+self.accel[0],self.velocity[1]+self.accel[1]] #Add acceleration to velocity
print(self.velocity)
self.cds = (self.cds[0]+self.velocity[0],self.cds[1]+self.velocity[1]) #Change co-ordinates by velocity
print(self.cds)
print('-------------------') #For seperating each run of the function when printing variables
HOM = Object(1000000,(400,100),[10,0]) #The problem planet
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
pygame.draw.circle(screen,(255,255,0),(400,300),25)
HOM.display()
HOM.calc_gravity()
clock.tick(30)
pygame.display.flip()
答案 0 :(得分:1)
你的主要问题与这一行有关:
angle = atan(d_x/d_y) #Find angle
atan
函数计算角度的能力非常有限,因为它无法分辨您在分区中组合的坐标的符号。例如,它会为atan(1/1)
和atan(-1/-1)
提供相同的结果,因为两个分区都计算相同的斜率(1
)。
相反,您应该使用atan2
,并分别传递坐标。因为这会让代码看到两个坐标,所以每次都可以在圆的右象限中选择一个角度。
但是还有更好的解决方法。不是计算角度然后立即将其转换回单位向量(通过调用sin
和cos
),为什么不直接计算单位向量?你已经有了原始矢量的长度!而不是:
acc_x = acc*sin(angle) #Convert acceleration from magnitude+angle -> x and y components
acc_y = acc*cos(angle)
使用:
acc_x = acc * d_x / distance
acc_y = acc * d_y / distance
d_x / distance
和d_y / distance
值与您之前获得的sin
和cos
值相同(对于他们正常工作时的角度),但是不需要三角学。你可以完全摆脱我引用的那条线!
请注意,可能需要反转您计算d_x
和d_y
的方式,以便获得从轨道物体指向物体的矢量它绕着轨道运行(而不是指向另一个方向,从轨道中心朝向轨道物体)。我不确定我是否正确地阅读了你的代码,但它看起来像你现在已经反过来了。这意味着,在您当前代码按预期方式工作的情况下,您实际上从atan
获得了错误的结果,并且不良行为(飞向无处)是代码正常工作(来自数学观点)。或者,您可以将acc
计算为负数,而不是正数。
正如一些评论者所提到的,您可能还有其他与您选择积分算法相关的问题,但这些错误不会像加速角度的主要问题那么大。当您在较长时间段内运行模拟时,它们会突然出现(并尝试使用更大的时间步长来使模拟更快)。你当前的算法对于一两个轨道是足够好的,但是如果你正在模拟几十个或几百个轨道,你就会开始看到错误累积,所以你应该选择一个更好的积分器。