【问题标题】:Calculate an intercept to an orbit for a given speed.计算给定速度下轨道的截距。
【发布时间】:2018-08-15 04:05:01
【问题描述】:

背景: 尝试编写一个“FTL”旅行不受重力影响且加速度是即时的游戏。

在给定行星的开普勒轨道和飞船当前位置及其最大 FTL 速度的情况下,我如何计算行星的位置。 (以 m/s 为单位)

我可以在给定的 DateTime 内获得行星的位置,但我正在努力弄清楚如何计算行星的位置,以及将船送到哪里,而不是围绕轨道追逐行星。

【问题讨论】:

    标签: math orbital-mechanics


    【解决方案1】:

    我会迭代...

    1. 计算行星与飞船当前位置的距离

      如果目标是静止的(不移动),您可以从中计算出您的船需要多长时间才能到达目标。这次我们打电话t

    2. 计算actual_time+t 中的行星位置并计算t 的新位置

      记住最后一个t,我们称之为t0。然后以与 #1 中相同的方式计算新的t,但计算t 之后的行星位置。

    3. 循环 #2

      如果fabs(t-t0)<accuracy 则停止。

    这个迭代解决方案应该更接近于每次迭代的发现 t,除非你的行星移动得太快和/或船真的太远或太慢(初始 t 是重要的部分,甚至比热带行星更大年)。在这种情况下,您通常会先跳入星系,然后再跳入行星(就像在原始精英中一样)。

    对于晦涩难懂的行星运动(例如太小的轨道周期),您将需要不同的方法,但要意识到这种情况意味着行星非常靠近恒星或非常重的系统中心质量(如黑洞)......

    在具有恒定 FTL 速度的代码中,它看起来像这样:

    vec3 pp,ps=vec3(?,?,?); // planet and ship positions
    double t,t0,time;
    
    time=actual_time(); t=0.0;
    for (int i=0;i<100;i++) // just avoiding infinite loop in case t/planet_orbit_period>=~0.5
     {
     t0=t;
     pp = planet_position(time+t);
     t=Length(pp-ps)/ship_FTL_speed;
     if (fabs(t-t0)*ship_FTL_speed<=ship_safe_FTL_distance) break;
     }
    

    这应该很快收敛(就像 5-10 次迭代应该足够了)现在 t 应该保持旅行所需的时间,pp 应该保持你的船应该前往的位置。但是,如果i&gt;=100 没有找到解决方案,那么您首先需要更靠近系统,或者使用更快的 FTL 或使用不同的方法,但这不应该在恒星系统 FTL 中常见,因为旅行时间应该少得多那么目标轨道周期...

    顺便说一句。您可能对此感兴趣:

    [Edit1] 比 FTL 翻译驱动器慢

    我对它进行了一些教导并稍微改变了算法。首先,它以某个步长(每个周期 100 个点)检查整个行星周期的所有位置,并记住最接近飞船的时间,无论在旅行期间经过的行星周期如何。然后简单地以越来越小的角度步长“递归”检查最佳位置。结果预览:

    以及更新的源代码(完整的 VCL 应用程序代码,因此只需使用/移植您需要的内容,而忽略其余部分)

    //$$---- Form CPP ----
    //---------------------------------------------------------------------------
    #include <vcl.h>
    #include <math.h>
    #pragma hdrstop
    #include "win_main.h"
    #include "GLSL_math.h"  // just for vec3
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TMain *Main;
    //---------------------------------------------------------------------------
    // constants
    const double deg=M_PI/180.0;
    const double t_day=60.0*60.0*24.0;
    // view
    double view_x0=0.0;
    double view_y0=0.0;
    double zoom=1.0;
    // simulation
    double sim_t=0.0,sim_dt=0.01*t_day;
    //---------------------------------------------------------------------------
    void toscr(double &x,double &y)
        {
        x*=zoom; x+=view_x0;
        y*=zoom; y+=view_y0;
        }
    //---------------------------------------------------------------------------
    class planet                // Kepler body simplified to 2D axis aligned. For fully 3D orbit add mising orbital parameters and equations
        {
    public:
        // input parameters
        double a,b,t0,T;        // major axis,minor axis, time where M=E=0.0 deg, orbital period
        // computet parameters
        double c1,c2,e;
    
        void ld(double _a,double _b,double _t0,double _T)
            {
            // copy input orbital parameters
            a=_a;
            b=_b;
            t0=_t0;
            T=_T;
            // prepare orbital constants
            e=1.0-((b*b)/(a*a));                // eccentricity
            if (e>=1.0) e=0;                    // wrong e
            c1=sqrt((1.0+e)/(1.0-e));           // some helper constants computation
            c2=a*(1-e*e);
            //b=a*sqrt(1.0-e);
            }
        vec3 position(double t) // actual position relative to center mass of the system
            {
            int q;
            vec3 p;
            double E,V,r,M;
            // compute mean orbital position M [rad] from time t
            M=(t-t0)/T;
            M-=floor(M);
            M*=2.0*M_PI;
            // compute real orbital position E [rad] from M
            for (E=M,q=0;q<20;q++) E=M+e*sin(E);// Kepler's equation
            // heliocentric ellipse
            V=2.0*atan(c1*tan(E/2.0));
            r=c2/(1.0+e*cos(V));
            p.x=r*cos(V);
            p.y=r*sin(V);
            p.z=0.0;
            return p;
            }
        void draw_orbit(TCanvas *scr)
            {
            int i;
            double ang,x,y,r,V,E;
            x=a; y=0; toscr(x,y);
            for (i=2,E=0.0;i;E+=3.6*deg)
                {
                if (E>=2.0*M_PI) { E=0.0; i=0; }
                V=2.0*atan(c1*tan(E/2.0));
                r=c2/(1.0+e*cos(V));
                x=r*cos(V);
                y=r*sin(V);
                toscr(x,y);
                if (i==2){ scr->MoveTo(x,y); i=1; }
                 else      scr->LineTo(x,y);
                }
            }
        };
    //---------------------------------------------------------------------------
    class ship                          // Space ship with translation propulsion
        {
    public:
        vec3 pos,dir;                   // position and translation direction
        double spd,tim;                 // translation speed and time to translate or 0.0 if no translation
        ship() { pos=vec3(0.0,0.0,0.0); dir=pos; spd=0.0; tim=0.0; }
        void update(double dt)          // simulate dt time step has passed
            {
            if (tim<=0.0) return;
            if (dt>tim) { dt=tim; tim=0.0; }
             else                 tim-=dt;
            pos+=spd*dt*dir;
            }
        void intercept(planet &pl)      // set course for planet pl intercept
            {
            if (spd<=0.0) { tim=0.0; return; }
            const double d=1000000.0;   // safe distance to target
    /*
            // [Iteration]
            int i;
            vec3 p;
            double t0;
            for (tim=0.0,i=0;i<100;i++)
                {
                t0=tim;
                p=pl.position(sim_t+tim);
                tim=length(p-pos)/spd;
                if (fabs(tim-t0)*spd<=d) break;
                }
            dir=normalize(p-pos);
    */
            // [search]
            vec3 p;
            int i;
            double tt,t,dt,a0,a1,T;
            // find orbital position with min error (coarse)
            for (a1=-1.0,t=0.0,dt=0.01*pl.T;t<pl.T;t+=dt)
                {
                p=pl.position(sim_t+t);                     // try time t
                tt=length(p-pos)/spd;
                a0=tt-t; if (a0<0.0) continue;              // ignore overshoots
                a0/=pl.T;                                   // remove full periods from the difference
                a0-=floor(a0);
                a0*=pl.T;
                if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; }   // remember best option
                }
            // find orbital position with min error (fine)
            for (i=0;i<3;i++)                               // recursive increase of accuracy
             for (a1=-1.0,t=tim-dt,T=tim+dt,dt*=0.1;t<T;t+=dt)
                {
                p=pl.position(sim_t+t);                     // try time t
                tt=length(p-pos)/spd;
                a0=tt-t; if (a0<0.0) continue;              // ignore overshoots
                a0/=pl.T;                                   // remove full periods from the difference
                a0-=floor(a0);
                a0*=pl.T;
                if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; }   // remember best option
                }
            // direction
            p=pl.position(sim_t+tim);
            dir=normalize(p-pos);
            }
        };
    //---------------------------------------------------------------------------
    planet pl;
    ship sh;
    //---------------------------------------------------------------------------
    void TMain::draw()
        {
        if (!_redraw) return;
        double x,y,r=3;
        vec3 p;
    
        // clear buffer
        bmp->Canvas->Brush->Color=clBlack;
        bmp->Canvas->FillRect(TRect(0,0,xs,ys));
    
        // Star
        bmp->Canvas->Pen->Color=clYellow;
        bmp->Canvas->Brush->Color=clYellow;
        x=0; y=0; toscr(x,y);
        bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
        // planet
        bmp->Canvas->Pen->Color=clDkGray;
        pl.draw_orbit(bmp->Canvas);
        bmp->Canvas->Pen->Color=clAqua;
        bmp->Canvas->Brush->Color=clAqua;
        p=pl.position(sim_t);
        x=p.x; y=p.y; toscr(x,y);
        bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
        // ship
        bmp->Canvas->Pen->Color=clRed;
        bmp->Canvas->Brush->Color=clRed;
        p=sh.pos;
        x=p.x; y=p.y; toscr(x,y);
        bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
    
        // render backbuffer
        Main->Canvas->Draw(0,0,bmp);
        _redraw=false;
        }
    //---------------------------------------------------------------------------
    __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
        {
        pl.ld(1000000000.0,350000000.0,0.0,50.0*t_day);
        sh.pos=vec3(-3500000000.0,-800000000.0,0.0);
        sh.spd=500.0;   // [m/s]
        sh.intercept(pl);
    
        bmp=new Graphics::TBitmap;
        bmp->HandleType=bmDIB;
        bmp->PixelFormat=pf32bit;
        pyx=NULL;
        _redraw=true;
        }
    //---------------------------------------------------------------------------
    void __fastcall TMain::FormDestroy(TObject *Sender)
        {
        if (pyx) delete[] pyx;
        delete bmp;
        }
    //---------------------------------------------------------------------------
    void __fastcall TMain::FormResize(TObject *Sender)
        {
        xs=ClientWidth;  xs2=xs>>1;
        ys=ClientHeight; ys2=ys>>1;
        bmp->Width=xs;
        bmp->Height=ys;
        if (pyx) delete[] pyx;
        pyx=new int*[ys];
        for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
        _redraw=true;
    
        view_x0=xs-(xs>>3);
        view_y0=ys2;
        zoom=double(xs2)/(2.5*pl.a);
    
        // draw(); Sleep(5000);
        }
    //---------------------------------------------------------------------------
    void __fastcall TMain::FormPaint(TObject *Sender)
        {
        _redraw=true;
        }
    //---------------------------------------------------------------------------
    void __fastcall TMain::tim_redrawTimer(TObject *Sender)
        {
        for (int i=0;i<10;i++)
            {
            sh.update(sim_dt);
            sim_t+=sim_dt;
            if (sh.tim<=0.0) sim_dt=0.0; // stop simulation when jump done
            }
        if (sim_dt>0.0) _redraw=true;
        draw();
        }
    //---------------------------------------------------------------------------
    

    重要的东西在两个类中planet,ship 然后sim_t 是实际模拟时间,sim_dt 是模拟时间步长。在这种情况下,模拟在船舶到达目的地后停止。 ship::tim 是在ship::intercept() 中计算的剩余行驶时间以及预设速度的方向。应该在每个模拟时间步上调用更新...

    【讨论】:

    • 这看起来像我想要的,我已经完成了一半,但这很好,很快就会尝试。还想检查是否没有我不知道的非迭代计算,我猜不是。
    • @se5a 它可能存在,但它很可能是一个可怕的微分方程(或那些系统),并且由于开普勒是超越的,所以你需要使用泰勒级数,一段时间后你会堆积比简单的 2 个嵌套 for 循环要多得多,每个循环有 ~5 个(迭代)和 ~20 个(Kepler)循环......
    • 我将把它标记为已接受的答案,但是如何处理旅行时间超过目标轨道周期的情况?另外,你的链接很有趣,里面有一些我正在收藏的好信息。
    • @se5a 在这种情况下首先跳到行星/系统附近,如果你离得更近,那么最初的t 会少得多。如果行星轨道速度非常高接近您的 FTL 速度的奇异系统,您需要以不同的方式解决这个问题......例如,您可以跳到轨道并沿轨道以相反的方式移动,直到遇到行星,但是此类案例是假设性的,因为 99.999% 的案例将通过他的首选解决方案。
    • 更多我的速度可能远低于c。在早期技术。 (此时我正试图避免/推迟处理用于船舶旅行的牛顿轨道力学,并且仍然没有为船舶确定良好的速度范围游戏玩法,所以也许 FTL 是一个糟糕的措辞选择,也许“非牛顿驱动”会更好)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多