Преобразование элементов орбиты в декартовы векторы состояния

Обновлено 2016-12-12

Я пишу программу орбитальной механики в Unity . Я преобразовываю положение и скорость объекта на орбите в элементы орбиты, а затем преобразую элементы орбиты обратно в декартовы векторы положения, чтобы я мог построить всю орбиту. Я следил за уравнениями по этим двум ссылкам:

Декартовы векторы состояния в кеплеровы элементы

Кеплеровы элементы в декартовых векторах состояния

Уравнения, которым я следую для расчета векторов состояния, предполагают, что эталонная плоскость — это XY. В Unity эталонной плоскостью является XZ, а Y — «направление вверх». Итак, что я сделал, так это вычел 90 градусов из наклонения, чтобы орбита в плоскости XZ была с нулевым наклонением, что в итоге и стало причиной моих проблем. Поэтому я удалил эту модификацию, и теперь большинство моих орбит отображаются правильно для орбит, начинающихся в перицентре. Но орбиты, начинающиеся в апоапсисе, обратные. Смотрите изображения ниже:

Вот правильно нарисованная орбита. У меня есть небольшой космический корабль на орбите, и он начинается с оси -X. Я нарисовал неполную орбиту, чтобы увидеть начальную точку орбиты, которая, как вы видите, начинается на оси -X.

Здесь я изменил начальное состояние корабля, чтобы он стартовал в апоапсисе. Как видите, начальная точка орбиты смещена на 180 градусов и лежит на оси +X, а не на оси -X. На самом деле орбита всегда рисуется, начиная с перицентра. Я пытаюсь сделать так, чтобы первая точка орбиты была позицией корабля в момент времени, когда я вызываю функцию ConvertToCartesian.

Вот мой код:

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

Чтобы нарисовать линию орбиты, я просто вызываю функцию ConvertToCartesian в цикле с увеличением значения t, чтобы получить массив векторов положения.

Значения: M = 3,3e16, GM ~= 2,2e6, r = (-50000, 0,0) Для круговой орбиты v = (0,0, sqrt(GM/50000))

Дополнительный вопрос: поскольку уравнения предполагают базовую плоскость XY, как я могу изменить ее, чтобы она соответствовала базовой плоскости XZ, которую я использую в своей программе?

Обновлять

Я обновил метод ConvertToCartesian, чтобы он принимал в качестве входных данных истинную аномалию (nu), вычисленную функцией ConvertToKeplerElements. Затем он вычисляет эксцентрическую аномалию (E), а затем вычисляет среднюю аномалию (Mt). Когда ConvertToCartesian вызывается в последующих циклах, Mt обновляется с соответствующим временным шагом. Затем я снова вычисляю E из обновленного Mt, затем вычисляю nu из обновленного E. Это решает большинство случаев. Проблема в том, что иногда nu вычисляется на 180 градусов от того места, где оно должно быть, что делает начальную точку отображаемой орбиты отклоненной на 180 градусов.

В моей функции ConvertToKeplerElements, согласно математике, nu = 2pi - nu, если rv < 0. Это происходит, например, когда я задаю начальные векторы r, v так, что корабль стартует не в перицентре и не в апоапсисе, а где-то между ними. Я знаю, что это правильно, но когда я вычисляю nu->E->nu, этот переворот на 180 градусов теряется при вычислении.

Вот соответствующие части обновленного метода 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)); 

Вот плохой результат, который я получаю:введите описание изображения здесь

Истинная аномалия в начале орбиты должна быть 1,5pi (~4,71), но при расчете по E она составляет 0,5pi (~1,57)

Можете ли вы включить дополнительную информацию? Хотя бы один вектор состояния ( р ,   р ˙ ) и грамм М который вы используете, и снимок экрана с окончательной «неправильной» орбитой, которая получается. Может быть, я могу попытаться воспроизвести проблему, и, возможно, кто-то еще узнает ее. Вы должны опубликовать некоторый код - с заявлением типа " Я уверен, что проблема заключается в том, что я неправильно использую математику; с точки зрения кода ..." показ вашего кода был бы очевидным шагом, если вы просите помощи в исправлении Это.
Если вы можете предоставить примеры значений, я могу поместить их в используемое мной орбитальное программное обеспечение, чтобы мы могли выяснить, какая из ваших функций дает разные результаты.
Я нашел лучший способ сформулировать свой вопрос после попытки понять, что происходит... должен ли я создать новую тему или отредактировать свой существующий вопрос?
@echl Однако, если он отличается, так что требует другого ответа, то вы определенно можете опубликовать новый отдельно, упомянуть и связать этот вопрос и объяснить, почему он новый и другой и требует другого типа ответов.
@uhoh Хорошо, спасибо. Я не забуду сделать это для будущих вопросов. Извините, я новичок в размещении здесь.
Нет простите!" Кстати, это один из «хороших» сайтов обмена стеками. Если вы измените слишком много, кто-то может просто откатить ваши правки назад. Я думаю, что грубое руководство состоит в том, чтобы не стесняться улучшать свой вопрос, если это помогает, пока не начнут появляться ответы. Затем поместите вас в ответчики, действуйте соответствующим образом.

Ответы (4)

Вы можете найти лучшие инструкции здесь:

Декартовский в кеплеровский (PDF)

Кеплеровский в декартовый (DOC)

Я закодировал все шаги, указанные в этих документах, чтобы проверить, могу ли я найти вашу ошибку, но, похоже, это работает. Ниже я приведу свой код (на Python) и, возможно, если вы сравните его со своим, вы сможете найти свою ошибку.

Запустив код, я получил следующий вывод:

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

Учитывая, что р в метрах и р ˙ в метрах в секунду эти погрешности вполне допустимы.

OBS: Если вы хотите преобразиться р а также р ˙ в Earth Centered and Fixed есть дополнительное преобразование, описанное в документе!

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)

Редактировать: я смог построить их на основе данных JPL Horizon, и с небольшим редактированием они действительно точны.

Edit2: произошла ошибка в реализации # 9 в cart_2_kep. Он никогда не выведет все возможные углы. Использование альтернативной формулы atan2исправляет это.

Edit3: Уравнение V_z исправлено с "-" на "+"

Очень полезный ответ (и да, для python)! Я хотел провести тест-драйв, но мой OpenOffice не мог отобразить уравнения в документе Kep to Cart. Однако однократная загрузка в документы Google позволяет повторно загружать в форматы .odtи .pdfформаты, и уравнения, кажется, там просто прекрасны.
Хотите попробовать другой вопрос ?
Как вы рассчитали EA в kep2cart? Я не вижу его нигде там.
EA вычисляется на 11 шаге cart_2_kep, затем передается в качестве аргумента в kep_2_cart.
Мне не удалось здесь подтвердить результаты выводом JPL Horizons, например, для параметров орбиты Земли в сравнении с векторами. Было бы здорово, если бы вы могли сравнить свои результаты с этим и добавить пример.
Ссылки мертвы, есть ли зеркала для pdf?
Обе ссылки, к сожалению, мертвы. Может кто-нибудь отразить их?
Не могли бы вы обновить ссылки? Те мертвы. Спасибо
Я считаю, что это тот же кеплариан в декартов (DOC), на который ссылается выше: crisp.nus.edu.sg/~liew/phys/notes/orbits/kep2cart_2002.doc
Когда р г знак равно в г знак равно 0 , час Икс знак равно час у знак равно 0 . Это делает omega_LANNaN и, следовательно, latNaN. Как это решить?

Я не уверен - может быть, я что-то упускаю, но ваше изображение, кажется, показывает левосторонние декартовы координаты. Это может привести к тому, что полная орбита будет выглядеть правильно (особенно если нет стрелок, показывающих направление движения), но неполная орбита действительно будет выглядеть неправильно, потому что она подчеркнет направление движения (график части вектора скорости на графике). вектор состояния также может показать это!)

Вот ваше изображение - если бы я использовал правило правой руки, мой большой палец, кажется, указывает на ваше отрицательное направление z.

введите описание изображения здесь

Это то, что у меня есть в Beldner, и я готов поспорить, что AutoCad и другие программы для трехмерного проектирования выглядят одинаково:

введите описание изображения здесь

выше: Скриншот из 3D-окна Blender .

Вот случайные изображения правой декартовой координаты. Они выглядят так же, как координаты Blender.

введите описание изображения здесь

выше: Из Википедии .

введите описание изображения здесь

выше: Из Википедии .

И это показывает разницу между правшами и левшами:

введите описание изображения здесь

выше: декартова координатная ручность отсюда

введите описание изображения здесь

выше: декартова координатная рукоятка отсюда

Ах да, в этом вы правы... но почему-то мои орбиты все еще идут в правильном направлении. Прямо сейчас кажется, что орбита всегда рисуется, начиная с перицентра орбиты; Я не уверен, связано ли это с леворукостью системы координат Unity или с тем, как я вычисляю векторы состояния. Может ли проблема заключаться в моем коде, где я вычисляю среднюю аномалию Mt и эксцентрическую аномалию E? Для t=0 Mt=0 -> E = 0 Следовательно, rc = a * (1 - e * Mathf.Cos(E)) = минимальное значение. Следовательно, в момент времени t0 вектор положения всегда является перицентром.
Это не ответ, это просто очень длинное замечание о том, что Unity действительно использует левостороннюю систему координат.
@AaronFranke, вы могли заметить, что вопроса тоже нет. Заголовок «Преобразование орбитальных элементов в декартовы векторы состояния» без пунктуации, и единственный вопросительный знак в любом месте сообщения ОП — это «дополнительный вопрос». ответы были опубликованы, чтобы помочь им решить технические проблемы, которые у них были. Это немного необычно, и в большинстве случаев люди с проблемами программирования никогда не получают полезного ответа, или они никогда не отвечают на комментарии и просто исчезают. Этот был намного больше продуктивнее среднего.

Как в вашем исходном коде, так и в вашем обновлении ваш код корневого приближения не использует тот факт, что E находится в диапазоне от 0 до 360 градусов (от 0 до 2pi).

Изменение вычисления E на это может быть достаточным, если это остающаяся проблема:

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

Прежде всего, спасибо за то, что показали свою работу и результаты, которые так ясно иллюстрируют ваш вопрос!

Два предложения внизу проливают свет на это? Лайти отредактировал из Википедии элементы кеплеровской орбиты:


Основные два элемента, определяющие форму и размер эллипса:

  1. Эксцентриситет ( e ) — форма эллипса...
  2. Большая полуось ( а ) - сумма расстояний перицентра и апоцентра, деленная на два.

Два элемента определяют ориентацию орбитальной плоскости, в которую встроен эллипс:

  1. Наклон ( i ) — вертикальный наклон эллипса относительно базовой плоскости.
  2. Долгота восходящего узла ( ω или Ω ) — ориентирует восходящий узел по горизонтали по отношению к начальной точке системы отсчета.

И наконец:

  1. Аргумент перицентра ( ω ) ориентация эллипса в плоскости орбиты, измеренная от восходящего узла до перицентра

  2. Истинная аномалия ( ν , θ или f ) в эпоху ( М 0 определяет положение в определенное время ( также известное как «эпоха» ).


проверьте свои единицы:

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

Если 60это unitelss, то deltaTимеет единицы времени в квадрате. Это дает Mtединицы времени. Тогда это означает:

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

дает Eединицы времени, и вещи, которые входят в триггерные функции, вроде Sin()должны быть безразмерными.

примечание; например, использование 60для преобразования секунд в минуты, которое будет называться секундами в минуту , не имеет единиц измерения.

попробуй разные настоящие аномалии ( t0и)

Настоящая аномалия (#6) вызывается t0в вашей программе — вы пробовали просто передавать разные значения? Если вы передаете разные значения между 0 и периодом T, вы должны заставить орбиту начинаться в разных местах. т 0 знак равно 0,5 Т должно начинаться как апоапсис.

Я могу произвольно изменить начальную точку орбиты, но проблема заключается в том, что она должна соответствовать положению корабля в момент времени t=t0, что не всегда соответствует действительности. Я отредактировал свой пост, чтобы показать некоторые изменения, которые я сделал.
@echl Хорошо, я думаю, теперь я понимаю, что ты имеешь в виду. Я работаю над этим (ваш код переведен на python (у меня аллергия на фигурные скобки)) и скоро свяжусь с вами. Чтобы перепроверить, я думаю, вам нужна функция, которая берет физическую начальную точку и вычисляет правильную т 0 чтобы когда все начиналось вовремя т знак равно 0 орбита начинается с этой начальной точки. И, возможно, могут быть или не быть некоторые проблемы с переворачиванием nu = 2pi - nu.
Это точно правильно! Я, возможно, сузился до того места, где я вычисляю E из nu (напоминаю, «конвейер» вычислений: r,v->nu->E->Mt->Mt с опережением (t - t0)->E- >nu->r,v). Пробовал два уравнения из [Википедии , в разделе "От истинной аномалии". Вычисление E из CosE =... работает для некоторых орбит, а вычисление E из TanE = ... работает для некоторых других орбит.