You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

模拟中月球为何向地球螺旋靠近?求问题排查与建议

地月引力模拟:月球螺旋靠近问题的排查与修正

问题概述

我正在编写一个模拟地月间引力相互作用的半真实程序,但运行时月球持续向地球螺旋靠近,无法维持稳定轨道。以下是我的代码:

from math import sin,cos,sqrt,atan2,pi
import pygame
pygame.init()

class Planet:
    dt = 1/100
    G = 6.67428e-11 #G constant
    scale = 1/(1409466.667) #1 m = 1/1409466.667 pixels
    def __init__(self,x=0,y=0,radius=0,color=(0,0,0),mass=0,vx=0,vy=0):
        self.x = x #x-coordinate pygame-window
        self.y = y #y-coordinate pygame-window
        self.radius = radius
        self.color = color
        self.mass = mass
        self.vx = vx #velocity in the x axis
        self.vy = vy #velocity in the y axis
        
    def draw(self,screen):
        pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius)
    
    def orbit(self,trace):
        pygame.draw.rect(trace, self.color, (self.x, self.y, 2, 2))
        
    def update_vel(self,Fnx,Fny):
        ax = Fnx/self.mass #Calculates acceleration in x- and y-axis for body 1.
        ay = Fny/self.mass
        self.vx -= ((ax * Planet.dt)/Planet.scale)
        self.vy -= ((ay * Planet.dt)/Planet.scale)
        self.update_pos()
     
    def update_pos(self):
        self.x += ((self.vx * Planet.dt)) #changes position considering each body's velocity.
        self.y += ((self.vy * Planet.dt))
        
    def move(self,body):
        dx = (self.x - body.x) #Calculates difference in x- and y-axis between the bodies
        dy = (self.y - body.y)
        r = (sqrt((dy**2)+(dx**2))) #Calculates the distance between the bodies
        angle = atan2(dy, dx) #Calculates the angle between the bodies with atan2!
        if r < self.radius: #Checks if the distance between the bodies is less than the radius of the bodies. Uses then Gauss gravitational law to calculate force.
            F = 4/3 * pi * r
            Fx = cos(angle) * F
            Fy = sin(angle) * F
        else:  
            F = (Planet.G*self.mass*body.mass)/((r/Planet.scale)**2) #Newtons gravitational formula.
            Fx = cos(angle) * F
            Fy = sin(angle) * F
        return Fx,Fy

def motion():
    for i in range(0,len(bodies)):
        Fnx = 0 #net force
        Fny = 0
        for j in range(0,len(bodies)):
            if bodies[i] != bodies[j]:
                Fnx += (bodies[i].move(bodies[j]))[0]
                Fny += (bodies[i].move(bodies[j]))[1]
            elif bodies[i] == bodies[j]:
                continue
        bodies[i].update_vel(Fnx,Fny)
        bodies[i].draw(screen)
        bodies[i].orbit(trace)
        Fnx,Fny=0,0 

screen = pygame.display.set_mode([900,650]) #width - height
trace = pygame.Surface((900, 650))
pygame.display.set_caption("Moon simulation")
FPS = 150 #how quickly/frames per second our game should update.

earth = Planet(450,325,30,(0,0,255),5.97219*10**(24),-24.947719394204714/2) #450= xpos,325=ypos,30=radius
luna = Planet(450,(575/11),10,(128,128,128),7.349*10**(22),1023)
bodies = [earth,luna]

running = True
clock = pygame.time.Clock()

while running: #if user clicks close window
    clock.tick(FPS)    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            
    screen.fill((0,0,0))
    pygame.Surface.blit(screen, trace, (0, 0))
    motion()

    pygame.display.flip() 

pygame.quit()

我计划在修复地月系统后扩展为三体系统,欢迎各类建议与指导。


问题根源分析

1. 引力方向完全错误

move方法中,dx = self.x - body.x计算的是当前行星到目标行星的反向距离,导致引力方向与实际相反——本该吸引的力变成了排斥或错误的加速力,直接破坏轨道平衡。

2. 速度缩放逻辑颠倒

update_vel方法中,将真实加速度转换为像素速度时用了除法而非乘法:

self.vx -= ((ax * Planet.dt)/Planet.scale)

根据scale = 1/(1409466.667)的定义(1米对应scale像素),真实速度变化量(m/s)需乘以scale才能转换为像素/秒,除法会导致速度变化被错误放大,轨道迅速衰减。

3. 积分顺序引入累积误差

当前代码计算单个行星受力后立即更新其位置,后续行星的受力计算会基于已改变的位置,每帧都引入误差,长期累积后轨道必然崩溃。

4. 近距离引力公式错误

当行星距离小于半径时,使用的F = 4/3 * pi * r完全不符合高斯引力定律,正确的内部引力公式应为F = G*self.mass*body.mass*r/(R³)(R为行星半径),当前公式会导致近距离力计算完全失效。

5. 初始速度不符合物理规律

月球的初始速度1023未基于真实地月距离和引力公式计算,地球的初始速度也未匹配系统质心运动,导致整体动量不守恒,轨道天然不稳定。


修正方案

1. 修正引力方向

修改move方法的距离计算,让方向指向目标行星:

def move(self,body):
    dx = body.x - self.x  # 改为目标行星减当前行星,得到指向目标的方向
    dy = body.y - self.y
    r = sqrt(dy**2 + dx**2)
    angle = atan2(dy, dx)
    # 修正碰撞判断为两行星半径之和
    if r < self.radius + body.radius:
        # 替换为正确的内部引力公式
        r_m = r / Planet.scale
        R_m = self.radius / Planet.scale
        F = (Planet.G * self.mass * body.mass * r_m) / (R_m**3)
    else:
        r_m = r / Planet.scale  # 转换为真实距离(米)
        F = (Planet.G * self.mass * body.mass) / (r_m**2)
    Fx = cos(angle) * F
    Fy = sin(angle) * F
    return Fx,Fy

2. 修复速度缩放逻辑

调整update_vel的速度更新公式,修正符号和缩放方式:

def update_vel(self,Fnx,Fny):
    ax = Fnx / self.mass
    ay = Fny / self.mass
    # 真实加速度转速度变化(m/s),再转换为像素/秒,方向与引力一致用加号
    self.vx += ax * Planet.dt * Planet.scale
    self.vy += ay * Planet.dt * Planet.scale
    # 移除此处的update_pos调用,统一后续更新

3. 改进积分顺序

修改motion函数,先批量计算所有行星的合力,再统一更新速度和位置,避免顺序误差:

def motion():
    # 第一步:批量计算所有行星的合力
    forces = []
    for i in range(len(bodies)):
        Fnx = 0
        Fny = 0
        for j in range(len(bodies)):
            if i != j:
                fx, fy = bodies[i].move(bodies[j])
                Fnx += fx
                Fny += fy
        forces.append((Fnx, Fny))
    
    # 第二步:统一更新所有行星的速度
    for i in range(len(bodies)):
        bodies[i].update_vel(*forces[i])
    
    # 第三步:统一更新位置并绘制轨迹
    for body in bodies:
        body.update_pos()
        body.draw(screen)
        body.orbit(trace)

4. 设置符合物理规律的初始速度

基于真实地月距离计算月球的环绕速度,同时将地球初始速度设为0(或匹配质心运动):

# 地月真实距离(3.84e8米)转换为像素
moon_distance_m = 3.84e8
moon_distance_px = moon_distance_m * Planet.scale
# 月球环绕速度(m/s):sqrt(G*M_earth / r)
moon_v_m = sqrt(Planet.G * earth.mass / moon_distance_m)
# 转换为像素/秒
moon_v_px = moon_v_m * Planet.scale

earth = Planet(450, 325, 30, (0,0,255), 5.97219e24, 0, 0)
# 月球初始位置在地球正上方,速度沿x轴横向环绕
luna = Planet(450, 325 - moon_distance_px, 10, (128,128,128), 7.349e22, moon_v_px, 0)
bodies = [earth, luna]

扩展三体系统的建议

  • 替换稳定的积分算法:欧拉法误差累积快,建议改用Verlet积分或Runge-Kutta(RK4)法,大幅提升长期模拟稳定性。
  • 优化受力计算:N体系统中,i对j的力与j对i的力大小相等方向相反,可只计算一次并复用,减少一半计算量。
  • 切换到质心参考系:将所有行星的位置和速度转换到系统质心参考系,避免整体漂移,让模拟更直观稳定。
  • 完善碰撞处理:若需处理碰撞,需实现动量守恒和能量损失计算,而非简单的内部引力公式。
  • 引入近似算法:当行星数量增多时,可使用Barnes-Hut算法降低计算复杂度,提升模拟效率。

内容的提问来源于stack exchange,提问作者Hale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 14:48:13