Неверное положение при движении по орбите в Unity

Я работаю над модулем орбитальной физики уже несколько месяцев и достиг барьера. У меня есть два метода: один для преобразования элементов Кеплера в планетарные координаты, а другой — для определения элементов Кеплера из векторов состояния R и V . Метод PositionFromOrbit()распространяет тело по орбите, но оно начинает смещаться со своего начального места на орбите. Затем, через один период, он начинает прыгать с точки на точку на орбите. Вот пример:введите описание изображения здесь

Синий куб — ​​это текущая позиция объекта перед выполнением, а красный контур — расчетная позиция объекта в соответствии с его кеплеровскими элементами. Я вижу, что контур находится точно над апоцентром, но я не знаю, как и почему. Вот основные фрагменты, начиная с преобразования векторов состояния в элементы Кеплера:

public static KeplerOrbit OrbitFromState(Vector3Decimal R, Vector3Decimal V, decimal mu)
        {
            Vector3Decimal h = Vector3Decimal.Cross(R, V);
            Vector3Decimal n = Vector3Decimal.Cross(Vector3Decimal.forward, h);
            Vector3Decimal eVec = ((V.magnitude * V.magnitude - mu / R.magnitude) * R - Vector3Decimal.Dot(R, V) * V) / mu; // eccentricity vector
            decimal eMag = eVec.magnitude;
            decimal E = V.magnitude * V.magnitude / 2 - mu / R.magnitude;
            decimal a;
            decimal p;
            if(eVec.magnitude != 1)
            {
                a = -mu / (2 * E);
                p = a * (1 - eMag * eMag);
            }
            else
            {
                a = 0;
                p = h.magnitude * h.magnitude / mu;
            }
            decimal inc = ACos(h.z / h.magnitude);

            decimal O;
            if(inc == 0 || inc == Pi)
            {
                O = 0;
            }
            else
            {
                O = ACos(n.x / n.magnitude);
            }

            if(n.y < 0)
            {
                O = 2 * Pi - O;
            }

            decimal w;
            if (eVec.magnitude == 0)
            {
                w = 0;
            }
            else
            {
                if (inc == 0 || inc == Pi)
                {
                    w = ATan2(eVec.y, eVec.x);
                }
                else
                {
                    w = ACos(Vector3Decimal.Dot(n, eVec) / (n.magnitude * eVec.magnitude));
                }
            }
            if(eVec.z < 0)
            {
                w = 2 * Pi - w;
            }

            decimal nu;
            if(eVec.magnitude == 0)
            {
                if(inc == 0 || inc == Pi)
                {
                    nu = ACos(R.z / R.magnitude);
                }
                else
                {
                    nu = ACos(Vector3Decimal.Dot(n, R) / (n.magnitude * R.magnitude));
                }
            }
            else
            {
                nu = ACos(Vector3Decimal.Dot(eVec, R) / (eVec.magnitude * R.magnitude));
                if(Vector3Decimal.Dot(R,V) < 0)
                {
                    nu = 2 * Pi - nu;
                }
            }

            KeplerOrbit ret = new KeplerOrbit(E, a, eVec.magnitude, inc, O, w, nu);
            return ret;

        }

И вот метод, чтобы найти его положение:

public static Vector3Decimal PositionFromOrbit(KeplerOrbit orbit, decimal t, decimal mu)
        {
            decimal a = orbit.semi_major_axis;
            decimal e = orbit.eccentricity;
            decimal i = orbit.inclination;
            decimal O = orbit.longitude_of_ascent;
            decimal w = orbit.argument_of_periapsis;
            //decimal T = Period(orbit.semi_major_axis, mu);
            decimal t0 = TimeAtEccentricAnomaly(a, e, 0, mu);
            decimal Mt;
            if(t == t0)
            {
                t = t0;
                Mt = 0;
            }
            else
            {
                decimal deltaT = (t - t0);
                Mt = deltaT * Sqrt(mu / (a * a * a));
            }

            decimal E = Mt;
            decimal F = E - e * Sin(E) - Mt;
            int j = 0;
            int maxIter = 30;
            decimal delta = 0.000001m;
            while(Abs(F) > delta && j < maxIter)
            {
                //E = (E - F / (1 - e * Cos(E))) % (Pi * 2);
                E = (E - F / (1 - e * Cos(E))); // this is the line that fixed jittering.
                F = E - e * Sin(E) - Mt;
                j++;
            }

            decimal nu = 2 * ATan2(Sqrt(1 + e) * Sin(E / 2), Sqrt(1 - e) * Cos(E / 2));
            decimal rc = a * (1 - e * Cos(E));

            Vector3Decimal o = new Vector3Decimal(rc * Cos(nu), rc * Sin(nu), 0);
            Vector3Decimal odot = new Vector3Decimal(Sin(E), Sqrt(1 - e * e) * Cos(E), 0);
            odot = odot * (Sqrt(mu * a) / rc);
            decimal rx, ry, rz;
            rx = o.x; ry = o.y; rz = o.z;

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

            Vector3Decimal r = new Vector3Decimal(rx, ry, rz);
            return r;

        }

Может ли кто-нибудь определить, где «я» или методы не справляются со своей задачей?

Мои начальные векторы: R = 15, -0,07, 3,36 V = -0,39, 5,14, -0,04

У меня была эта проблема в течение нескольких недель, и было бы большим прорывом, если бы она была исправлена. Я пытался добавить/вычесть аргумент перицентра из истинной аномалии, как нуб, но это сделало орбиту неточной. Я попытался связать положение со временем в перицентре, но это тоже не сработало. Я использовал три других метода, которые работали не так хорошо, как текущий.

Спасибо, один и все!

Редактировать: Обнаружен еще один симптом проблемы или другой: После плавного витка объект начинает прыгать по своей орбите.после плавной орбиты объект начинает прыгать по своей орбите. Он перемещается от перицентра к апоапсису и обратно, прежде чем начинает дрожать.

Редактировать: дрожание было решено благодаря notovny, который рекомендовал удалить % (2 * Pi)при решении эксцентрической аномалии.

Насколько эксцентрична и наклонна ваша тестовая орбита? Укажите параметры, используемые для инициализации тестовой орбиты, показанной на рисунке. Сталкиваетесь ли вы с теми же проблемами при тестировании на сильно наклоненной орбите с большим эксцентриситетом?
Единственное, что бросается в глаза: если Е эксцентрическая аномалия, и ж это Аргумент Периапсиса, вы не должны складывать их вместе; Эксцентрическая аномалия совершенно не зависит от аргумента перицентра.
@AJN Тестовая орбита была очень слегка наклонена, но я только что проверил орбиту с наклонением около 40 градусов и одну, очень близкую к 90 градусам, и обе продолжали иметь одни и те же симптомы с незаметной разницей.
@notovny упс, взял эту строчку и проверил еще раз... к сожалению, без разницы.
Другая вещь, которая кажется странной, заключается в том, что попытка исправить Е к диапазону ( 0 , 2 π ] с оператором модуля внутри вашего цикла метода Ньютона, вероятно, плохая идея, поскольку он берет гладкую функцию и добавляет в нее периодический разрыв, чтобы Ньютон-Рафсон спотыкался каждый раз, когда орбита завершается. Я бы, наверное, удалил % (Pi * 2)полностью. Также поддерживаю запрос AJN на набор параметров, которые вызывают нежелательное поведение, а также ссылки на ссылку, которую вы использовали для создания этих уравнений.
@notovny Ахах! Удаление `% (Pi * 2)` устранило сумасшедшее дрожание и прыжки. @AJN Моими начальными параметрами являются векторы состояния, R = 15, -0,07, 3,36 и V = -0,39, 5,14, -0,04. Объект все еще смещается из исходного положения, но я надеюсь, что после того, как дрожание прекратилось.
Основной источник методов находится здесь: space.stackexchange.com/questions/19322/…
Удаление предыдущего комментария: похоже, что OrbitFromStateметод сохраняет истинную аномалию в значении Epoch как nu. Тем не менее, все еще нужно видеть KeplerOrbitобъект полностью и то, что TimeAtEccentricAnomalyметод делает для диагностики; Если я сделаю вероятные предположения, похоже, что ваш текущий метод PositionFromOrbitдаст правильные ответы только в том случае, если ваши векторы радиального расстояния Rи скорости Vотносятся к времени t = 0 в и и указывают на перицентр. OrbitFromStateR

Ответы (1)

Если вам нужно точное распространение орбиты, вы должны хранить орбиту в декартовой форме, потому что это несингулярное представление орбиты. Это также самая простая форма для учета дополнительных сил на космическом корабле, потому что вам нужно только интегрировать Ф "=" м а и применить его к декартовому состоянию, не пытаясь выяснить, как данная сила изменяет эксцентриситет, большую полуось и т. д.

Для преобразования из кеплеровского в декартово представление алгоритм не так прост (если вы хотите учесть все возможные орбиты). Вот ссылка на точную функцию в исходном коде GMAT C++ (148 строк C++), а вот «перевод» в Nyx (80 строк в Rust, что мне кажется более понятным). Чтобы подтвердить правильность этого перевода, вот ссылка на проверку этой функции в Nyx .

Я поясняю, что эти функции предназначены для распространения по рельсовым орбитам, с которыми мало кто будет взаимодействовать. Я создаю методы с высокой точностью, но буду копировать их для точности с плавающей запятой. Смысл этого преобразования в том, чтобы иметь возможность хранить орбиту и не ссылаться на нее в течение длительного времени, пока на нее не потребуется ссылаться.
Кроме того, они будут использоваться для звездных орбит вокруг галактического центра, поэтому точность весьма необходима.
Я не уверен, что понимаю ваш комментарий. Что вы подразумеваете под "рельсовыми" орбитами? Кроме того, хранение орбит в декартовом формате — это метод, который обеспечивает высокую точность хранения без каких-либо математических ошибок (чего нельзя сказать о наборе кеплеровских орбитальных элементов).
Ах, я должен объяснить. На рельсах ( on_railsэто booleanу меня на каждом астрофизическом теле) орбиты не подвержены возмущению и могут распространяться в течение длительных периодов времени или временных шагов без ошибок, что позволяет вам игнорировать их, а также строить маневры и предсказывать события, а также как отображение LOD (уровень детализации). Тела вне рельсов будут использовать физику Unity и будут подвержены возмущениям. Я использую плавающее начало, чтобы держать игрока на низкой скорости в сцене.
Все эти вещи очень важны, так как я пытаюсь генерировать звезды внутри галактики в масштабе (не генерируя каждую звезду, только поля плотности и вероятности), поэтому я создал тип данных и храню элементы Кепа Vector3Decimalв decimal. Я нашел инструмент, который мне нужен для работы, у меня просто проблемы с его реализацией. Многие из этих методов и типов данных будут иметь floatформы и быть асинхронными, чтобы облегчить работу процессора. Я надеюсь в этом есть смысл.