28

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

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

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

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

4

12 回答 12

9

这是这里的一切,总的来说,是让·米乌斯写的一切。

替代文字

于 2010-02-02T08:15:01.710 回答
8

您需要了解和理解牛顿万有引力定律开普勒行星运动定律。这两个很简单,如果不是在高中学习的话,我相信你已经听说过它们。最后,如果您希望您的模拟器尽可能准确,您应该熟悉n-Body 问题

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

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

只要记住这一点F = MA,其余的只是无聊的数学:P

于 2010-02-02T08:19:03.373 回答
6

这是一个关于一般 N 体问题的很棒的教程。

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

它是使用 Ruby 编写的,但很容易映射到其他语言等。它涵盖了一些常见的集成方法;Forward-Euler、Leapfrog 和 Hermite。

于 2010-02-02T21:18:00.943 回答
4

您可能想看看Celestia,一个自由空间模拟器。我相信你可以用它来创建虚构的太阳系,而且它是开源的。

于 2010-02-02T08:33:24.417 回答
2

您需要实现的只是适当的微分方程(开普勒定律)并使用 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

于 2010-02-02T16:37:03.180 回答
2

在物理学中,这被称为N 体问题。它之所以出名,是因为对于具有三个以上行星的系统,您无法手动解决此问题。幸运的是,您可以很容易地通过计算机获得近似解。

可以在这里找到一篇关于从头开始编写此代码的好论文。

但是,我觉得这里的警告很重要。你可能不会得到你期望的结果。如果你想看看如何:

  1. 行星的质量会影响其围绕太阳的轨道速度,很酷。你会看到的。
  2. 不同的行星相互作用,你会很沮丧。

问题是这样的。

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

  1. 如果你的行星有非常椭圆的轨道。这将导致行星之间的距离更近,因此它们之间的相互作用更多。
  2. 如果您的行星与太阳的距离几乎完全相同。他们会更多地互动。
  3. 如果你让你的行星大得如此可笑,它们会在外太阳系中与太阳竞争引力。

需要明确的是,是的,您将能够计算行星之间的一些相互作用。但是,不,如果您创建一个逼真的太阳系,这些相互作用对肉眼来说并不重要。

尝试一下,然后找出答案!

于 2012-10-04T22:20:09.133 回答
1

模拟行星物理的算法。

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

这是我在轨道的“下一个”位置绘制行星的方法。想象一下这些步骤,比如绕一个圆圈,一次一个度数,在一个与你试图追踪的行星具有相同周期的圆圈上。在此方法之外,我使用全局双精度作为计步器 - 称为 dTime,其中包含多个旋转度数。

传递给该方法的关键参数是,dEccentricty,dScalar(一个比例因子,因此轨道都适合显示器),dYear(轨道的持续时间,以地球年为单位)和定向轨道,以便近日点在正确的位置在表盘上,可以说,dLongPeri - 近日点经度。

绘制星球:

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 的值,r 是在其末端找到行星的向量的长度。

获取:

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;
    }

从E获取:

public double getRfromE (double dE, double dEccentricty, double dScalar)
{
    return Math.min(getWidth(), getHeight()) / 2 * dScalar * (1 - (dEccentricty * Math.cos(dE)));
}
于 2014-07-29T08:55:33.447 回答
1

查看nMod,这是一个用 C++ 编写并使用 OpenGL 的 n 体建模工具包。它附带一个人口众多的太阳系模型,应该很容易修改。此外,他有一个很好的关于 n 体模拟的 wiki。创建此程序的同一个人也在制作一个名为Moody的新程序,但它似乎并没有那么远。

此外,如果您要对多个对象进行 n 体模拟,您应该真正了解快速多极子方法(也称为快速多极子算法)。它可以将计算次数从 O(N^2) 减少到 O(N),从而真正加快模拟速度。据本文作者介绍,它也是20 世纪十大最成功的算法之一

于 2010-02-06T06:35:10.310 回答
1

看起来很难,需要很强的物理知识,但实际上很容易,你只需要知道2个公式和对向量的基本了解:

行星 1 和行星 2 之间的引力(或引力),质量为 m1 和 m2,它们之间的距离为 d:Fg = G*m1*m2/d^2;Fg = m*a。G 是一个常数,通过代入随机值来找到它,这样加速度“a”就不会太小,也不会太大,大约为“0.01”或“0.1”。

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

如果您想看起来真实,可以使用以下超简单算法(伪代码):

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;
}
于 2015-07-14T22:50:23.047 回答
0

Bate、Muller 和 White的《天体动力学基础》仍然是我母校航空航天工程师的必读内容。这往往涵盖地球轨道上物体的轨道力学......但这可能是您开始理解所需的物理和数学水平。

+1 @Stefano Borini 对“Jean Meeus 所写的一切”的建议。

于 2010-02-22T19:34:00.363 回答
0

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

于 2010-02-02T18:58:08.443 回答
-5
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();
}
于 2014-03-21T06:24:13.310 回答