Conversión de elementos orbitales en vectores de estado cartesianos

Actualizado 2016-12-12

Estoy escribiendo un programa de mecánica orbital en Unity . Convierto la posición y la velocidad de un objeto en órbita en elementos orbitales, luego convierto los elementos orbitales nuevamente en vectores de posición cartesianos para poder trazar la órbita completa. Seguí las ecuaciones en estos dos enlaces:

Vectores de estado cartesiano a elementos keplerianos

Elementos keplerianos a vectores de estado cartesianos

Las ecuaciones que estoy siguiendo para calcular los vectores de estado suponen que el plano de referencia es XY. En Unity, el plano de referencia es XZ, siendo Y la "dirección hacia arriba". Entonces, lo que hice fue restar 90 grados de la inclinación, de modo que una órbita en el plano XZ tendría una inclinación cero, lo que terminó siendo la causa de mis problemas. Así que eliminé esa modificación y ahora la mayoría de mis órbitas se muestran correctamente, para las órbitas que comienzan en el periapsis. Pero las órbitas que comienzan en la apoapsis están invertidas. Vea las imágenes a continuación:

Aquí hay una órbita correctamente dibujada. Tengo una pequeña nave espacial en órbita y comienza en el eje -X. Dibujé una órbita incompleta para poder ver el punto de inicio de la órbita, que puede ver que comienza en el eje -X.

Aquí cambié el estado inicial de la nave para que comience en apoapsis. Como puede ver, el punto de partida de la órbita está a 180 grados, y se encuentra en el eje +X en lugar del eje -X. De hecho, la órbita siempre se dibuja a partir del periápside. Lo que estoy tratando de hacer es que el primer punto de la órbita sea la posición de la nave en el momento en que llamo a la función ConvertToCartesian

Aquí está mi código:

Vector3 ConvertToCartesian(float e, float a, float i, float O, float w, float t, float t0){
    float mu = G * M;
    float Mt; //Mean anomaly at time t
    float T = 2 * Mathf.PI * Mathf.Sqrt(a * a * a / mu); //Orbital period
    if (t == t0) {
        t = t0;
        Mt = 0; 
    } else {
        float deltaT = (T/60) * (t - t0); //divide time increments into 1/60th of the orbital period
        Mt = deltaT * Mathf.Sqrt (mu / Mathf.Pow (a, 3));
    }
    //Calculate eccentric anomaly using Newton's method
    float E = Mt;
    float F = E - e * Mathf.Sin (E) - Mt;
    int j = 0, maxIter = 30;
    float delta = 0.000001f;
    while (Mathf.Abs(F) > delta && j < maxIter) {
        E = E - F / (1 - e * Mathf.Cos (E));
        F = E - e * Mathf.Sin (E) - Mt;
        j++;
    }
    float nu = 2 * Mathf.Atan2 (Mathf.Sqrt (1 + e) * Mathf.Sin (E / 2), Mathf.Sqrt (1 - e) * Mathf.Cos (E / 2)); //True anomaly

    float rc = a * (1 - e * Mathf.Cos(E)); //Distance to central body

    Vector3 o = new Vector3(rc * Mathf.Cos(nu), rc * Mathf.Sin(nu), 0); 
                
    Vector3 odot = new Vector3 (Mathf.Sin (E), Mathf.Sqrt (1 - e * e) * Mathf.Cos (E), 0); //Velocity vector in the orbital frame
    odot = (Mathf.Sqrt (mu * a) / rc) * odot;

    float rx, ry, rz;
    rx = o.x; ry = o.y; rz = o.z;

    rx = ( o.x * (Mathf.Cos (w) * Mathf.Cos (O) - Mathf.Sin (w) * Mathf.Cos (i) * Mathf.Sin (O)) -
            o.y * (Mathf.Sin (w) * Mathf.Cos (O) + Mathf.Cos (w) * Mathf.Cos (i) * Mathf.Sin (O)));
    ry = (o.x * (Mathf.Cos (w) * Mathf.Sin (O) + Mathf.Sin (w) * Mathf.Cos (i) * Mathf.Cos (O)) +
        o.y * (Mathf.Cos (w) * Mathf.Cos (i) * Mathf.Cos (O) - Mathf.Sin (w) * Mathf.Sin (O)));
    rz = (o.x * (Mathf.Sin (w) * Mathf.Sin (i)) + o.y * (Mathf.Cos (w) * Mathf.Sin (i)));

    Vector3 r = new Vector3(rx, ry, rz); //Position vector

    return r / objectScale;

}


/* Convert a body's cartesian state vectors (position, r, and velocity, v) into Kepler elements
 */
void ConvertToKeplerElements(Vector3 r, Vector3 v){
    float mu = G * M;
    Vector3 h = Vector3.Cross (r, v); //Orbital momentum
    Vector3 n = Vector3.Cross (Vector3.forward, h); 
    eVector = ((v.magnitude * v.magnitude - mu / r.magnitude) * r - Vector3.Dot(r, v) * v) / mu; //Eccentricity
    float emag = eVector.magnitude;
    float E = v.magnitude * v.magnitude / 2 - mu / r.magnitude; //Specific mechanical energy
    if (eVector.magnitude != 1) {
        a = -mu / (2 * E); //Semi major axis
        float p = a * (1 - emag * emag);
    } else {
        //a = infinity
        a = 0;
        float p = h.magnitude * h.magnitude / mu;
    }

    inc = Mathf.Acos (h.z / h.magnitude); //Inclination

    //Longitude of ascending node (LAN), angle of body to node vector
    if (inc == 0 || inc == Mathf.PI) { 
        O = 0; //For an equatorial orbit (i = 0), LAN is undefined. By convention, set to 0. 
    } else {
        O = Mathf.Acos (n.x / n.magnitude); 
    }
    if (n.y < 0) {
        O = 2 * Mathf.PI - O;
    }

    //Argument of periapsis
    if (eVector.magnitude == 0) {
        w = 0; //For a circular orbit, by convention place w at ascending node (w = 0)
    } else {
        if (inc == 0 || inc == Mathf.PI) {
            w = Mathf.Atan2 (eVector.y, eVector.x);
        } else {
            w = Mathf.Acos (Vector3.Dot (n, eVector) / (n.magnitude * eVector.magnitude)); 
        }
    }
    if (eVector.z < 0) {
        w = 2 * Mathf.PI - w;
    }

    //True anomaly
    if (eVector.magnitude == 0) {
        if (inc == 0 || inc == Mathf.PI) {
            nu = Mathf.Acos (r.z/ r.magnitude); //For a circular non-inclined orbit, nu is angle of body from the x axis
            //(True Longitude)
        } else {
            //For a circular inclined orbit, nu is the angle between ascending node position vectors, (Argument of Latitude)
            nu = Mathf.Acos (Vector3.Dot (n, r) / (n.magnitude * r.magnitude));
        }
    } else {
        nu = Mathf.Acos (Vector3.Dot (eVector, r) / (eVector.magnitude * r.magnitude)); //True anomaly is angle between eccentricity and position vectors
        if (Vector3.Dot (r, v) < 0) {
            nu = 2 * Mathf.PI - nu;
        }
    }
}

Para dibujar la línea de la órbita, simplemente llamo a la función ConvertToCartesian en un ciclo con valores crecientes de t para obtener una matriz de vectores de posición.

Valores: M = 3.3e16, GM ~= 2.2e6, r = (-50000, 0,0) Para una órbita circular, v = (0,0, sqrt(GM/50000))

Pregunta de seguimiento: dado que las ecuaciones asumen el plano de referencia XY, ¿cómo puedo cambiarlo para que se ajuste al plano de referencia XZ que estoy usando en mi programa?

Actualizar

Actualicé el método ConvertToCartesian, de modo que toma la anomalía verdadera (nu) calculada a partir de la función ConvertToKeplerElements como entrada. Luego calcula la anomalía excéntrica (E), luego calcula la anomalía media (Mt) a partir de eso. Cuando se llama a ConvertToCartesian en bucles posteriores, Mt se actualiza con el paso de tiempo relevante. Luego calculo E nuevamente a partir del Mt actualizado, luego calculo nu a partir de la E actualizada. Esto resuelve la mayoría de los casos. El problema es que, a veces, nu se calcula a 180 grados de donde debería estar, lo que hace que el punto de inicio de la órbita mostrada esté a 180 grados de distancia.

En mi función ConvertToKeplerElements, de acuerdo con las matemáticas, nu = 2pi - nu si rv < 0. Esto sucede, por ejemplo, cuando configuro los vectores r, v iniciales de modo que la nave no comience ni en el periapsis ni en el apoapsis, sino en algún punto intermedio. Sé que esto es correcto, pero cuando calculo nu->E->nu, ese giro de 180 grados se pierde en el cálculo.

Aquí están las partes relevantes del método actualizado ConvertToCartesian.

        float mu = G * M;
    float E = Mathf.Acos((e + Mathf.Cos(nu)) / (1 + e * Mathf.Cos(nu)));
    float Mt = E - e * Mathf.Sin (E);

    //Mean anomaly at time t
    float T = 2 * Mathf.PI * Mathf.Sqrt(a * a * a / mu); //Orbital period
    float deltaT = (T/60) * (t - t0); //divide time increments into 1/60th of the orbital period
    Mt = Mt + deltaT * Mathf.Sqrt (mu / Mathf.Pow (a, 3));

    //Calculate eccentric anomaly using Newton's method
    float F = E - e * Mathf.Sin (E) - Mt;
    int j = 0, maxIter = 30;
    float delta = 0.000001f;
    while (Mathf.Abs(F) > delta && j < maxIter) {
        E = E - F / (1 - e * Mathf.Cos (E));
        F = E - e * Mathf.Sin (E) - Mt;
        j++;
    }
        
    //True anomaly
    nu = 2 * Mathf.Atan2 (Mathf.Sqrt (1 + e) * Mathf.Sin (E / 2), Mathf.Sqrt (1 - e) * Mathf.Cos (E / 2)); 

Aquí está el mal resultado que estoy obteniendo:ingrese la descripción de la imagen aquí

La verdadera anomalía al comienzo de la órbita debería ser 1,5pi (~4,71), pero al calcularla desde E es 0,5pi (~1,57)

¿Puedes incluir algo más de información? Al menos un vector de estado ( r ,   r ˙ ) y el GRAMO METRO está utilizando y una captura de pantalla de la órbita "incorrecta" final que resulta. Tal vez pueda intentar reproducir el problema y tal vez alguien más lo reconozca. Debe publicar un código, con una declaración como " Estoy seguro de que el problema radica en que no estoy usando las matemáticas correctamente; en cuanto al código ...", mostrar su código sería un paso obvio si está pidiendo ayuda para solucionarlo. eso.
Si puede proporcionar valores de ejemplo, puedo colocarlos en el software orbital que uso, por lo tanto, podemos averiguar cuál de sus funciones está produciendo los diferentes resultados.
He encontrado una mejor manera de enmarcar mi pregunta después de tratar de entender lo que está pasando... ¿debería crear un nuevo hilo o editar mi pregunta existente?
@echl Sin embargo, si es distinta, por lo que requiere una respuesta diferente, definitivamente puede publicar una nueva por separado, mencionar y vincular esta pregunta y explicar por qué es nueva y diferente y necesita un tipo diferente de respuestas.
@uhoh Está bien, gracias. Recordaré hacerlo para futuras preguntas. Lo siento, soy nuevo en esto de publicar aquí.
¡No lo siento!" Por cierto, este es uno de los sitios de intercambio de pilas "agradables". Si cambia demasiado, alguien puede simplemente retroceder en sus ediciones. Creo que una pauta aproximada es sentirse libre de mejorar su pregunta si ayuda, hasta que comiencen a aparecer las respuestas. Entonces ponte i los respondedores proceden en consecuencia.

Respuestas (4)

Puedes encontrar mejores instrucciones aquí:

De cartesiano a kepleriano (PDF)

Kepleriano a Cartesiano (DOC)

Codifiqué todos los pasos dados en estos documentos para verificar si pude encontrar su error, pero parece funcionar. Daré mi código a continuación (en Python) y quizás si lo compara con el suyo puede encontrar su error.

Ejecutando el código obtuve el siguiente resultado:

[ -9.31322575e-10  -1.09686156e-20   4.12053763e-05]
[  2.80478539e-13   9.09494702e-13  -8.42143002e-19]

Teniendo en cuenta que r esta en metros y r ˙ está en metros por segundo estos errores son bastante aceptables.

OBS: Si quieres transformar r y r ˙ a Earth Centered and Fixed, ¡hay una transformación adicional descrita en el documento!

from astropy.constants import G, M_earth, R_earth
from astropy import units as u
import numpy as np

mu = G.value*M_earth.value
Re = R_earth.value

#Test vectors
r_test = np.array([Re + 600.0*1000, 0, 50])
v_test = np.array([0, 6.5 * 1000, 0])
t = 0


def cart_2_kep(r_vec,v_vec):
    #1
    h_bar = np.cross(r_vec,v_vec)
    h = np.linalg.norm(h_bar)
    #2
    r = np.linalg.norm(r_vec)
    v = np.linalg.norm(v_vec)
    #3
    E = 0.5*(v**2) - mu/r
    #4
    a = -mu/(2*E)
    #5
    e = np.sqrt(1 - (h**2)/(a*mu))
    #6
    i = np.arccos(h_bar[2]/h)
    #7
    omega_LAN = np.arctan2(h_bar[0],-h_bar[1])
    #8
    #beware of division by zero here
    lat = np.arctan2(np.divide(r_vec[2],(np.sin(i))),\
    (r_vec[0]*np.cos(omega_LAN) + r_vec[1]*np.sin(omega_LAN)))
    #9
    p = a*(1-e**2)
    nu = np.arctan2(np.sqrt(p/mu) * np.dot(r_vec,v_vec), p-r)
    #10
    omega_AP = lat - nu
    #11
    EA = 2*np.arctan(np.sqrt((1-e)/(1+e)) * np.tan(nu/2))
    #12
    n = np.sqrt(mu/(a**3))
    T = t - (1/n)*(EA - e*np.sin(EA))

    return a,e,i,omega_AP,omega_LAN,T, EA

def kep_2_cart(a,e,i,omega_AP,omega_LAN,T, EA):

    #1
    n = np.sqrt(mu/(a**3))
    M = n*(t - T)
    #2
    MA = EA - e*np.sin(EA)
    #3
    #
    # ERROR WAS HERE
   #nu = 2*np.arctan(np.sqrt((1-e)/(1+e)) * np.tan(EA/2))
    nu = 2*np.arctan(np.sqrt((1+e)/(1-e)) * np.tan(EA/2))
    #4
    r = a*(1 - e*np.cos(EA))
    #5
    h = np.sqrt(mu*a * (1 - e**2))
    #6
    Om = omega_LAN
    w =  omega_AP

    X = r*(np.cos(Om)*np.cos(w+nu) - np.sin(Om)*np.sin(w+nu)*np.cos(i))
    Y = r*(np.sin(Om)*np.cos(w+nu) + np.cos(Om)*np.sin(w+nu)*np.cos(i))
    Z = r*(np.sin(i)*np.sin(w+nu))

    #7
    p = a*(1-e**2)

    V_X = (X*h*e/(r*p))*np.sin(nu) - (h/r)*(np.cos(Om)*np.sin(w+nu) + \
    np.sin(Om)*np.cos(w+nu)*np.cos(i))
    V_Y = (Y*h*e/(r*p))*np.sin(nu) - (h/r)*(np.sin(Om)*np.sin(w+nu) - \
    np.cos(Om)*np.cos(w+nu)*np.cos(i))
    V_Z = (Z*h*e/(r*p))*np.sin(nu) + (h/r)*(np.cos(w+nu)*np.sin(i))

    return [X,Y,Z],[V_X,V_Y,V_Z]

a,e,i,omega_AP,omega_LAN,T, EA = cart_2_kep(r_test,v_test)
r_test2, v_test2 = kep_2_cart(a,e,i,omega_AP,omega_LAN,T, EA)


print(r_test2 - r_test)
print(v_test2 - v_test)

Editar: Pude trazar estos contra los datos de Horizon de JPL y con la pequeña edición son realmente precisos.

Edit2: Hubo un error en la implementación de #9 en cart_2_kep. Nunca daría salida a todos los ángulos posibles. La fórmula alternativa usando atan2soluciona esto.

Edit3: Ecuación de V_z corregida, de un "-" a un "+"

¡Respuesta muy útil (y yay para python)! Quería probarlo, pero mi OpenOffice no podía mostrar las ecuaciones en el documento Kep to Cart. Sin embargo, cargar a Google Docs una vez permite volver a descargar en .odty .pdfformatos y las ecuaciones parecen estar bien allí.
¿ Quieres probar con otra pregunta ?
¿Cómo calculó EA en kep2cart? No lo veo por ningún lado allí.
EA se calcula en el paso 11 de cart_2_kep, luego se pasa como argumento a kep_2_cart
No pude corroborar los resultados aquí con la salida de JPL Horizons, por ejemplo, para los parámetros orbitales de la Tierra frente a los vectores. Sería genial si pudiera comparar sus resultados con eso y agregar un ejemplo.
Los enlaces están muertos, ¿hay algunos sitios espejo para los pdf?
Lamentablemente, ambos enlaces están muertos. ¿Alguien puede reflejarlos?
¿Podrías por favor actualizar los enlaces? Esos están muertos. Gracias
Creo que este es el mismo Keplarian to Cartesian (DOC) al que se hace referencia anteriormente: crisp.nus.edu.sg/~liew/phys/notes/orbits/kep2cart_2002.doc
Cuando r z = v z = 0 , h X = h y = 0 . Esto hace omega_LANNaN y por lo tanto latNaN. ¿Cómo se solucionará esto?

No estoy seguro, tal vez me estoy perdiendo algo, pero su imagen parece mostrar coordenadas cartesianas para zurdos. Esto podría hacer que una órbita completa se vea bien (especialmente si no hay flechas que muestren la dirección del movimiento), pero una órbita incompleta se vería mal porque resaltaría la dirección del movimiento (una gráfica de la parte del vector de velocidad del ¡el vector de estado también podría mostrar esto!)

Aquí está su imagen: si utilicé la regla de la mano derecha, mi pulgar parece apuntar en su dirección negativa z.

ingrese la descripción de la imagen aquí

Esto es lo que tengo en Beldner, y apuesto a que AutoCad y otros programas de ingeniería 3D se parecen:

ingrese la descripción de la imagen aquí

arriba: Captura de pantalla de la ventana 3D de Blender .

Aquí hay imágenes aleatorias de las coordenadas cartesianas de la derecha. Se parecen a las coordenadas de Blender.

ingrese la descripción de la imagen aquí

arriba: De Wikipedia .

ingrese la descripción de la imagen aquí

arriba: De Wikipedia .

Y esto muestra la diferencia entre diestros y zurdos:

ingrese la descripción de la imagen aquí

arriba: la lateralidad de las coordenadas cartesianas desde aquí

ingrese la descripción de la imagen aquí

arriba: la lateralidad de las coordenadas cartesianas desde aquí

Ah, sí, tienes razón en eso... pero de alguna manera mis órbitas todavía van en la dirección correcta. En este momento, parece que la órbita siempre se dibuja a partir del periápside de la órbita; No estoy seguro de si eso tiene que ver con el carácter zurdo del sistema de coordenadas de Unity o con la forma en que calculo los vectores de estado. ¿Podría el problema estar en mi código donde calculo la anomalía media Mt y la anomalía excéntrica E? Para t=0, Mt=0 -> E = 0 Por lo tanto, rc = a * (1 - e * Mathf.Cos(E)) = valor mínimo Por lo tanto, en el tiempo t0, el vector de posición es siempre el periápside.
Esta no es una respuesta, es solo una observación muy larga de que Unity sí usa un sistema de coordenadas para zurdos.
@AaronFranke, es posible que note que tampoco hay una pregunta. El título es "Conversión de elementos orbitales en vectores de estado cartesianos" sin puntuación, y el único signo de interrogación en cualquier parte de la publicación del OP es una "pregunta de seguimiento". En este caso, el OP estaba teniendo problemas de varias maneras, y cuatro se han publicado respuestas para ayudarlos a abordar los problemas técnicos que estaban teniendo. Es un poco poco convencional, y la mayoría de las veces las personas con problemas de programación nunca obtienen una respuesta útil, o nunca responden a los comentarios y simplemente desaparecen. Esta fue mucho más productiva que la media.

Tanto en su código original como en su actualización, su código de aproximación raíz no aprovecha el hecho de que E está entre 0 y 360 grados (0 y 2pi).

Cambiar el cálculo de E a esto puede ser suficiente si este es el problema restante:

  E = (E - F / (1 - e * Mathf.Cos(E))) % (Mathf.PI * 2);

En primer lugar felicitaciones por mostrar tu trabajo. y los resultados que ilustran su pregunta tan claramente!

¿Las dos sugerencias en la parte inferior arrojan alguna luz sobre esto? Ligeramente editado de Wikipedia , los elementos orbitales keplerianos son:


Los dos elementos principales que definen la forma y el tamaño de la elipse:

  1. Excentricidad ( e )—forma de la elipse...
  2. Semieje mayor ( a ): la suma de las distancias del periapsis y la apoapsis dividida por dos.

Dos elementos definen la orientación del plano orbital en el que se incrusta la elipse:

  1. Inclinación ( i ): inclinación vertical de la elipse con respecto al plano de referencia
  2. Longitud del nodo ascendente ( ω o Ω ): orienta horizontalmente el nodo ascendente con respecto al punto vernal del marco de referencia

Y finalmente:

  1. Argumento del periapsis ( ω ) la orientación de la elipse en el plano orbital, medida desde el nodo ascendente hasta el periapsis

  2. Anomalía verdadera ( ν , θ o f ) en la época ( METRO 0 define la posición en un momento específico ( también conocido como la "época" ).


Consulta tus unidades:

float deltaT = (T/60) * (t - t0); //divide time increments into 1/60th of the orbital period
Mt = deltaT * Mathf.Sqrt (mu / Mathf.Pow (a, 3));

Si no tiene unidades 60, entonces deltaTtiene unidades de tiempo al cuadrado. Eso da Mtunidades de tiempo. Entonces eso significa:

float E = Mt;
float F = E - e * Mathf.Sin (E) - Mt;

da Eunidades de tiempo, y las cosas que van dentro de las funciones trigonométricas como Sin()si no tuvieran unidades.

Nota; el uso de 60para convertir segundos a minutos, por ejemplo, que se llamaría segundos por minuto, no tiene unidades.

probar diferentes anomalías verdaderas (t0 's )

La verdadera anomalía (# 6) se llama t0en su programa. ¿Ha intentado simplemente pasar valores diferentes? Si pasa diferentes valores entre 0 y el período T, debería obtener la órbita para comenzar en diferentes lugares. t 0 = 0.5 T debe comenzar como apoapsis.

Puedo cambiar el punto de partida de la órbita arbitrariamente, pero el problema que tengo es que debe coincidir con la posición de la nave en t=t0, lo cual no siempre sucede. Edité mi publicación para mostrar algunas modificaciones que hice.
@echl OK, creo que ahora entiendo lo que quieres decir. Estoy trabajando en ello (tu código traducido a python (soy alérgico a las llaves)) y te responderé pronto. Para verificar, creo que desea una función que tome un punto de partida físico y calcule el correcto t 0 para que cuando todo empiece a la hora t = 0 la órbita comienza desde ese punto de partida. Y tal vez podría haber o no algunos problemas con nu = 2pi - nu flipping.
¡Eso es exactamente correcto! Posiblemente me he reducido a donde calculo E desde nu (para recapitular, el cálculo "tubería" es: r,v->nu->E->Mt->Mt avanzado por (t - t0)->E- >nu->r,v). Probé dos ecuaciones de [Wikipedia , en la sección "De la verdadera anomalía". Calcular E a partir de CosE =... funciona para algunas órbitas, y calcular E a partir de TanE = ... funciona para algunas otras órbitas.