什么算法可以让我模拟行星物理?

时间:2010-02-02 08:09:50

标签: algorithm simulation physics astronomy

我有兴趣做一个“太阳系”模拟器,它可以让我模拟行星和恒星的旋转和重力。

我希望能够说,模拟我们的太阳系,并以不同的速度模拟它(即,观察地球和其他行星在太阳,几年等周围绕太阳旋转)。我希望能够添加行星并改变行星质量等,以了解它将如何影响系统。

有没有人有任何资源可以指出我正确的方向来编写这种模拟器?

是否有为此目的而设计的现有物理引擎?

12 个答案:

答案 0 :(得分:9)

一切都是here,一般来说,是Jean Meeus写的所有内容。

alt text

答案 1 :(得分:8)

您需要了解并了解Newton's Law of Universal GravitationKepler's Laws of Planetary Motion。这两个很简单,我相信你已经听过他们,如果不是在高中学习的话。最后,如果您希望模拟器尽可能准确,您应该熟悉n-Body problem

你应该从简单开始。尝试制作一个Sun对象和围绕它的Earth对象。这应该会给你一个非常可靠的开始,并且很容易从那里扩展。行星物体看起来像:

Class Planet {
  float x;
  float y;
  float z; // If you want to work in 3D
  double velocity;
  int mass;
}

请记住F = MA,其余的只是无聊的数学:P

答案 2 :(得分:6)

这是一个关于N体问题的精彩教程。

http://www.artcompsci.org/#msa

它是用Ruby编写的,但很容易映射到其他语言等。它涵盖了一些常见的集成方法;前向欧拉,越级和厄米特。

答案 3 :(得分:4)

您可能需要查看免费空间模拟器Celestia。我相信你可以用它来创造虚构的太阳系,它是open source

答案 4 :(得分:2)

你需要实现的是适当的微分方程(Keplers定律)和使用Runge-Kutta。 (至少这对我有用,但可能有更好的方法)

网上有很多这样的模拟器。

这是一个简单的实现在500行的c代码。 (montion algorhitm要少得多) http://astro.berkeley.edu/~dperley/programs/ssms.html

同时检查:
http://en.wikipedia.org/wiki/Kepler_problem
http://en.wikipedia.org/wiki/Two-body_problem
http://en.wikipedia.org/wiki/N-body_problem

答案 5 :(得分:2)

在物理学中,这被称为N-Body Problem。它之所以出名,是因为对于有三个以上行星的系统,你不能手动解决这个问题。幸运的是,您可以非常轻松地使用计算机获得近似解决方案。

可以找到一篇关于从头开始编写此代码的好文章here

但是,我觉得一句警告在这里很重要。您可能无法获得预期的结果。如果你想看看如何:

  1. 行星的质量会影响它绕太阳的轨道速度,很酷。你会看到的。
  2. 不同的行星相互作用,你会感到沮丧。
  3. 问题在于此。

    是的,现代天文学家关心的是土星的质量如何改变地球围绕太阳的轨道。但这是一个非常小的影响。如果你要绘制太阳周围行星的路径,那么太阳系中还有其他行星就没那么重要了。太阳太大了,它会淹没所有其他引力。唯一的例外是:

    1. 如果你的行星有非常椭圆的轨道。这将导致行星可能更加靠近,因此它们会相互作用更多。
    2. 如果你的行星与太阳几乎完全相同。他们会互动更多。
    3. 如果你让你的行星变得如此狡猾,它们会与太阳竞争外太阳系的重力。
    4. 要清楚,是的,你将能够计算出行星之间的一些相互作用。但是,如果你创造一个真实的太阳系,那么这些相互作用对于肉眼来说并不重要。

      尝试一下,然后找出来!

答案 6 :(得分:1)

查看nMod,一个用C ++编写并使用OpenGL的n-body建模工具包。它有一个非常好的太阳能系统模型,它附带它,它应该很容易修改。此外,他对一般的n体仿真有很好的维基。创建这个的同一个人也在创建一个名为Moody的新程序,但它看起来并不是那么远。

另外,如果你要用不止一些物体进行n体模拟,你应该真正看一下fast multipole method(也称为快速多极算法)。它可以减少从O(N ^ 2)到O(N)的计算次数,从而真正加快仿真速度。根据这篇文章的作者,它也是top ten most successful algorithms of the 20th century之一。

答案 7 :(得分:1)

模拟行星物理的算法。

以下是我的Android应用中Keppler部件的实现。主要部分在我的网站上,您可以下载整个来源:http://www.barrythomas.co.uk/keppler.html

这是我在下一步拍摄地球的方法。在轨道中的位置。想象一下这样的步骤,比如绕着一个圆圈,一次一个圆圈,在一个与你想要追踪的行星具有相同周期的圆圈上。在这个方法之外,我使用全局double作为步数计数器 - 称为dTime,它包含多个旋转度。

传递给该方法的关键参数是,dEccentricty,dScalar(缩放系数,因此轨道全部适合显示),dYear(地球年代轨道的持续时间)和定向轨道,使近日点处于表盘上的正确位置,可以说是dLongPeri - 近日点的经度。

<强> drawPlanet:

public void drawPlanet (double dEccentricity, double dScalar, double dYear, Canvas canvas, Paint paint, 
            String sName, Bitmap bmp, double dLongPeri)
{
        double dE, dr, dv, dSatX, dSatY, dSatXCorrected, dSatYCorrected;
        float fX, fY;
        int iSunXOffset = getWidth() / 2;
        int iSunYOffset = getHeight() / 2;

        // get the value of E from the angle travelled in this 'tick'

        dE = getE (dTime * (1 / dYear), dEccentricity);

        // get r: the length of 'radius' vector

        dr = getRfromE (dE, dEccentricity, dScalar);

        // calculate v - the true anomaly

        dv = 2 * Math.atan (
                Math.sqrt((1 + dEccentricity) / (1 - dEccentricity))
                *
                Math.tan(dE / 2)
                ); 

        // get X and Y coords based on the origin

        dSatX = dr / Math.sin(Math.PI / 2) * Math.sin(dv);
        dSatY = Math.sin((Math.PI / 2) - dv) * (dSatX / Math.sin(dv));

        // now correct for Longitude of Perihelion for this planet

        dSatXCorrected = dSatX * (float)Math.cos (Math.toRadians(dLongPeri)) - 
            dSatY * (float)Math.sin(Math.toRadians(dLongPeri));
        dSatYCorrected = dSatX * (float)Math.sin (Math.toRadians(dLongPeri)) + 
            dSatY * (float)Math.cos(Math.toRadians(dLongPeri));

        // offset the origin to nearer the centre of the display

        fX = (float)dSatXCorrected + (float)iSunXOffset;
        fY = (float)dSatYCorrected + (float)iSunYOffset;

        if (bDrawOrbits)
            {
            // draw the path of the orbit travelled
            paint.setColor(Color.WHITE);
            paint.setStyle(Paint.Style.STROKE);
            paint.setAntiAlias(true);

            // get the size of the rect which encloses the elliptical orbit

            dE = getE (0.0, dEccentricity);
            dr = getRfromE (dE, dEccentricity, dScalar);
            rectOval.bottom = (float)dr;
            dE = getE (180.0, dEccentricity);
            dr = getRfromE (dE, dEccentricity, dScalar);
            rectOval.top = (float)(0 - dr);

            // calculate minor axis from major axis and eccentricity
            // http://www.1728.org/ellipse.htm

            double dMajor = rectOval.bottom - rectOval.top;
            double dMinor = Math.sqrt(1 - (dEccentricity * dEccentricity)) * dMajor;

            rectOval.left = 0 - (float)(dMinor / 2);
            rectOval.right = (float)(dMinor / 2);

            rectOval.left += (float)iSunXOffset;
            rectOval.right += (float)iSunXOffset;
            rectOval.top += (float)iSunYOffset;
            rectOval.bottom += (float)iSunYOffset;

            // now correct for Longitude of Perihelion for this orbit's path

            canvas.save();
                canvas.rotate((float)dLongPeri, (float)iSunXOffset, (float)iSunYOffset);
                canvas.drawOval(rectOval, paint);
            canvas.restore();
            }

        int iBitmapHeight = bmp.getHeight();

        canvas.drawBitmap(bmp, fX - (iBitmapHeight / 2), fY - (iBitmapHeight / 2), null);

        // draw planet label

        myPaint.setColor(Color.WHITE);
        paint.setTextSize(30);
        canvas.drawText(sName, fX+20, fY-20, paint);
}

上述方法调用另外两种方法,它们提供E(平均异常)的值和r,即在行星末端找到的向量的长度。

<强> GETE:

public double getE (double dTime, double dEccentricity)
    {
    // we are passed the degree count in degrees (duh) 
    // and the eccentricity value
    // the method returns E

    double dM1, dD, dE0, dE = 0; // return value E = the mean anomaly
    double dM; // local value of M in radians

    dM = Math.toRadians (dTime);

    int iSign = 1;

    if (dM > 0) iSign = 1; else iSign = -1;

    dM = Math.abs(dM) / (2 * Math.PI); // Meeus, p 206, line 110
    dM = (dM - (long)dM) * (2 * Math.PI) * iSign; // line 120
    if (dM < 0)
        dM = dM + (2 * Math.PI); // line 130
    iSign = 1;
    if (dM > Math.PI) iSign = -1; // line 150
    if (dM > Math.PI) dM = 2 * Math.PI - dM; // line 160

    dE0 = Math.PI / 2; // line 170
    dD = Math.PI / 4; // line 170

    for (int i = 0; i < 33; i++) // line 180 
        {
        dM1 = dE0 - dEccentricity * Math.sin(dE0); // line 190
        dE0 = dE0 + dD * Math.signum((float)(dM - dM1));
        dD = dD / 2; 
        }

    dE = dE0 * iSign;

    return dE;
    }

<强> getRfromE:

public double getRfromE (double dE, double dEccentricty, double dScalar)
{
    return Math.min(getWidth(), getHeight()) / 2 * dScalar * (1 - (dEccentricty * Math.cos(dE)));
}

答案 8 :(得分:1)

它看起来非常难,需要强大的物理知识,但事实上它很容易,你只需要知道2个公式和对矢量的基本理解:

行星1和行星2之间的吸引力(或重力),质量m1和m2以及它们之间的距离d:Fg = G * m1 * m2 / d ^ 2; Fg = m * a。 G是常数,通过替换随机值来找到它,以便加速&#34; a&#34;不会太小而且不会太大&#34; 0.01&#34;或&#34; 0.1&#34;。

如果你的总矢量力在那个时刻作用于当前行星,你可以找到即时加速度a =(总力)/(当前行星的质量)。如果您有当前的加速度和当前速度和当前位置,您可以找到新的速度和新位置

如果你想看起来真实,你可以使用以下supereasy algorythm(伪代码):

int n; // # of planets
Vector2D planetPosition[n]; 
Vector2D planetVelocity[n]; // initially set by (0, 0)
double planetMass[n];

while (true){
    for (int i = 0; i < n; i++){
        Vector2D totalForce = (0, 0); // acting on planet i
        for (int j = 0; j < n; j++){
            if (j == i)
                continue; // force between some planet and itself is 0
            Fg = G * planetMass[i] * planetMass[j] / distance(i, j) ^ 2;
        // Fg is a scalar value representing magnitude of force acting
        // between planet[i] and planet[j]
        // vectorFg is a vector form of force Fg
        // (planetPosition[j] - planetPosition[i]) is a vector value
        // (planetPosition[j]-planetPosition[i])/(planetPosition[j]-plantetPosition[i]).magnitude() is a
        // unit vector with direction from planet[i] to planet[j]
            vectorFg = Fg * (planetPosition[j] - planetPosition[i]) / 
                  (planetPosition[j] - planetPosition[i]).magnitude();
            totalForce += vectorFg;
        }
        Vector2D acceleration = totalForce / planetMass[i];
        planetVelocity[i] += acceleration;
    }

    // it is important to separate two for's, if you want to know why ask in the comments
    for (int i = 0; i < n; i++)
        planetPosition[i] += planetVelocity[i];

    sleep 17 ms;
    draw planets;
}

答案 9 :(得分:0)

如果您正在模拟物理,我强烈推荐Box2D 这是一个很棒的物理模拟器,通过物理模拟真的可以减少你需要的锅炉板数量。

答案 10 :(得分:-1)

Bate,Muller和White的

Fundamentals of Astrodynamics仍然需要在母校为本科航空航天工程师阅读。这往往涵盖了地球轨道上的物体的轨道力学......但这可能是您需要开始理解的物理和数学水平。

+1 @Stefano Borini建议“Jean Meeus撰写的所有内容。”

答案 11 :(得分:-4)

Dear Friend here is the graphics code that simulate solar system

Kindly refer through it

/*Arpana*/

#include<stdio.h>
#include<graphics.h>
#include<conio.h>
#include<math.h>
#include<dos.h>
void main()
{
int i=0,j=260,k=30,l=150,m=90;
int n=230,o=10,p=280,q=220;
float pi=3.1424,a,b,c,d,e,f,g,h,z;
int gd=DETECT,gm;
initgraph(&gd,&gm,"c:\tc\bgi");
outtextxy(0,10,"SOLAR SYSTEM-Appu");
outtextxy(500,10,"press any key...");
circle(320,240,20);               /* sun */
setfillstyle(1,4);
floodfill(320,240,15);
outtextxy(310,237,"sun");
circle(260,240,8);
setfillstyle(1,2);
floodfill(258,240,15);
floodfill(262,240,15);
outtextxy(240,220,"mercury");
circle(320,300,12);
setfillstyle(1,1);
floodfill(320,298,15);
floodfill(320,302,15);
outtextxy(335,300,"venus");
circle(320,160,10);
setfillstyle(1,5);
floodfill(320,161,15);
floodfill(320,159,15);
outtextxy(332,150, "earth");
circle(453,300,11);
setfillstyle(1,6);
floodfill(445,300,15);
floodfill(448,309,15);
outtextxy(458,280,"mars");
circle(520,240,14);
setfillstyle(1,7);
floodfill(519,240,15);
floodfill(521,240,15);
outtextxy(500,257,"jupiter");
circle(169,122,12);
setfillstyle(1,12);
floodfill(159,125,15);
floodfill(175,125,15);
outtextxy(130,137,"saturn");
circle(320,420,9);
setfillstyle(1,13);
floodfill(320,417,15);
floodfill(320,423,15);
outtextxy(310,400,"urenus");
circle(40,240,9);
setfillstyle(1,10);
floodfill(38,240,15);
floodfill(42,240,15);
outtextxy(25,220,"neptune");
circle(150,420,7);
setfillstyle(1,14);
floodfill(150,419,15);
floodfill(149,422,15);
outtextxy(120,430,"pluto");
getch();
while(!kbhit())             /*animation*/
{
a=(pi/180)*i;
b=(pi/180)*j;
c=(pi/180)*k;
d=(pi/180)*l;
e=(pi/180)*m;
f=(pi/180)*n;
g=(pi/180)*o;
h=(pi/180)*p;
z=(pi/180)*q;
cleardevice();
circle(320,240,20);
setfillstyle(1,4);
floodfill(320,240,15);
outtextxy(310,237,"sun");

circle(320+60*sin(a),240-35*cos(a),8);
setfillstyle(1,2);
pieslice(320+60*sin(a),240-35*cos(a),0,360,8);
circle(320+100*sin(b),240-60*cos(b),12);
setfillstyle(1,1);
pieslice(320+100*sin(b),240-60*cos(b),0,360,12);
circle(320+130*sin(c),240-80*cos(c),10);
setfillstyle(1,5);
pieslice(320+130*sin(c),240-80*cos(c),0,360,10);
circle(320+170*sin(d),240-100*cos(d),11);
setfillstyle(1,6);
pieslice(320+170*sin(d),240-100*cos(d),0,360,11);
circle(320+200*sin(e),240-130*cos(e),14);
setfillstyle(1,7);
pieslice(320+200*sin(e),240-130*cos(e),0,360,14);
circle(320+230*sin(f),240-155*cos(f),12);
setfillstyle(1,12);
pieslice(320+230*sin(f),240-155*cos(f),0,360,12);
circle(320+260*sin(g),240-180*cos(g),9);
setfillstyle(1,13);
pieslice(320+260*sin(g),240-180*cos(g),0,360,9);
circle(320+280*sin(h),240-200*cos(h),9);
setfillstyle(1,10);
pieslice(320+280*sin(h),240-200*cos(h),0,360,9);
circle(320+300*sin(z),240-220*cos(z),7);
setfillstyle(1,14);
pieslice(320+300*sin(z),240-220*cos(z),0,360,7);
delay(20);
i++;
j++;
k++;
l++;
m++;
n++;
o++;
p++;
q+=2;
}
getch();
}