Подтвердить что ты не робот

Векторное направление для гравитации по круговой орбите

В настоящее время я работаю над проектом на С#, где я играю с планетарной гравитацией, о которой я знаю, является хардкорной темой, которая подходит к ней, но мне нравятся проблемы. Я читал законы Ньютонов и законы Кеплера, но я не могу понять, как получить правильное направление гравитации.

В моем примере у меня всего 2 тела. Спутник и планета. Это должно упростить его, поэтому я могу понять это, но мой план состоит в том, чтобы иметь несколько объектов, которые динамически влияют друг на друга, и, надеюсь, в конечном итоге получится несколько реалистичная многоуровневая система.

Когда у вас есть орбита, тогда у спутника есть гравитационная сила, и это, конечно, в направлении планеты, но это направление не является постоянным. Чтобы лучше объяснить мою проблему, я попытаюсь использовать пример:

скажем, у нас есть спутник, движущийся со скоростью 50 м/с и ускоряющийся к планете со скоростью 10 м/с/с в радиусе 100 м. (все теоретические числа). Если мы скажем, что частота кадров равна 1, то через одну секунду объект будет на 50 единиц вперед и 10 единиц вниз.

Поскольку спутник перемещает несколько единиц в кадре и около 50% от радиуса, гравитационный курс сильно сдвинулся во время этого кадра, но приложенная сила только "вниз". это создает большую погрешность, особенно если объект перемещает большой процент от радиуса.

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

Как бы это исправить?

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

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

Просто для справки, это функция, которую я имею прямо сейчас для перемещения:

foreach (ExtTerBody OtherObject in UniverseController.CurrentUniverse.ExterTerBodies.Where(x => x != this))
{
    double massOther = OtherObject.Mass;

    double R = Vector2Math.Distance(Position, OtherObject.Position);

    double V = (massOther) / Math.Pow(R,2) * UniverseController.DeltaTime;

    Vector2 NonNormTwo = (OtherObject.Position - Position).Normalized() * V;

    Vector2 NonNormDir = Velocity + NonNormTwo;
    Velocity = NonNormDir;

    Position += Velocity * Time.DeltaTime;
}

Если я плохо сформулировал себя, попросите меня перефразировать части - английский не мой родной язык, и конкретные темы могут быть трудно сформулировать, когда вы не знаете правильных технических терминов.:)

У меня есть подозрение, что это описано в втором законе кплеров, но если это так, то я не уверен, как его использовать, так как я не понимаю его законов в полной мере.

Спасибо за ваше время - это много значит!

(также, если кто-нибудь видит несколько ошибок в моей функции, то, пожалуйста, укажите их!)

4b9b3361

Ответ 1

Итак, я нашел решение, оно может быть не самым умным, но оно работает, и это довольно пришло в голову после прочтения как ответа Эрика, так и чтения комментария, сделанного marcus, можно сказать, что это комбинация двух

Это новый код:

foreach (ExtTerBody OtherObject in UniverseController.CurrentUniverse.ExterTerBodies.Where(x =>  x != this))
{
double massOther = OtherObject.Mass;

double R = Vector2Math.Distance(Position, OtherObject.Position);

double V = (massOther) / Math.Pow(R,2) * Time.DeltaTime;

float VRmod = (float)Math.Round(V/(R*0.001), 0, MidpointRounding.AwayFromZero);
if(V > R*0.01f)
{
    for (int x = 0; x < VRmod; x++)
    {
        EulerMovement(OtherObject, Time.DeltaTime / VRmod);
    }
}
else
    EulerMovement(OtherObject, Time.DeltaTime);

}

public void EulerMovement(ExtTerBody OtherObject, float deltaTime)
    {

            double massOther = OtherObject.Mass;

            double R = Vector2Math.Distance(Position, OtherObject.Position);

            double V = (massOther) / Math.Pow(R, 2) * deltaTime;

            Vector2 NonNormTwo = (OtherObject.Position - Position).Normalized() * V;

            Vector2 NonNormDir = Velocity + NonNormTwo;
            Velocity = NonNormDir;



            //Debug.WriteLine("Velocity=" + Velocity);
            Position += Velocity * deltaTime;
    }

Чтобы объяснить это:

Я пришел к выводу, что если проблема в том, что у спутника слишком много скорости в одном кадре, то почему бы не разделить его на несколько кадров? Таким образом, это то, что "оно" делает сейчас.

Когда скорость спутника составляет более 1% от текущего радиуса, он разделяет расчет на несколько укусов, делая его более точным.. Это приведет к снижению частоты кадров при работе с большими скоростями, но это нормально с проект вроде этого.

Различные решения по-прежнему очень приветствуются. Я могу настроить триггерные суммы, но самое главное, что это работает, тогда я могу беспокоиться о том, чтобы сделать его более гладким!

Спасибо всем, кто посмотрел, и всем, кто помог сам найти вывод!:) Удивительно, что люди могут так помочь!

Ответ 2

В настоящее время я работаю над проектом на С#, где я играю с планетарной гравитацией

Это интересный способ одновременно изучить методы моделирования, программирования и физики.

Одна вещь, которую я не могу понять, - это получить правильное направление гравитации.

Я предполагаю, что вы не пытаетесь моделировать релятивистскую гравитацию. Земля не находится на орбите вокруг Солнца, Земля находится на орбите вокруг, где солнце было восемь минут назад. Исправить из-за того, что гравитация не мгновенная, может быть затруднительной. (ОБНОВЛЕНИЕ: В соответствии с комментарием это неверно. Что я знаю, я прекратил заниматься физикой после второй год ньютоновской динамики и имею только самое неопределенное понимание тензорного исчисления.)

На этом раннем этапе вы сделаете все возможное, чтобы предположить, что гравитационная сила мгновенно и что планеты - это точки со всей их массой в центре. Вектор гравитационной силы представляет собой прямую линию от одной точки к другой.

Скажем, у нас есть спутник, движущийся со скоростью 50 м/с... Если мы скажем, что частота кадров составляет один кадр в секунду, то через одну секунду объект будет 50 единиц вправо и 10 единиц вниз.

Позвольте сделать это более ясным. Сила равна массовому ускорению. Вы определяете силу между телами. Вы знаете их массы, так что теперь вы знаете ускорение каждого тела. Каждое тело имеет положение и скорость. Ускорение изменяет скорость. Скорость изменяет положение. Поэтому, если частица начинает с скорости 50 м/с влево и 0 м/с вниз, а затем вы применяете усилие, которое ускоряет ее на 10 м/с/с, тогда мы сможем разработать изменение на скорость, а затем переход к позиции. Как вы заметили, в конце этой секунды положение и скорость будут изменяться на огромное количество по сравнению с их существующими величинами.

Поскольку спутник перемещает несколько единиц в кадре и около 50% от радиуса, гравитационный курс сильно сдвинулся во время этого кадра, но приложенная сила только "вниз". это создает большую погрешность, особенно если объект перемещает большой процент от радиуса.

Правильно. Проблема в том, что частота кадров чрезвычайно слишком мала, чтобы правильно моделировать взаимодействие, которое вы описываете. Вам нужно запустить симуляцию, чтобы вы смотрели десятые, сотые или тысячи тысяч секунд, если объекты быстро меняли направление. Размер шага времени обычно называется "дельта t" симуляции, а ваш слишком большой.

Для планетных тел то, что вы сейчас делаете, похоже на попытку моделировать Землю, моделируя ее положение каждые несколько месяцев и предполагая, что она движется по прямой линии в то же время. Вы должны фактически имитировать свою позицию каждые несколько минут, а не каждые несколько месяцев.

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

Вы могли бы это сделать, но было бы проще просто уменьшить "дельта t" для вычисления. Тогда разница между направлениями в начале и конце кадра намного меньше.

После того, как вы разобрались, есть еще несколько методов, которые вы можете использовать. Например, вы можете обнаружить, когда позиция слишком сильно изменяется между кадрами и возвращается назад, и повторите вычисления с меньшим шагом времени. Если позиции практически не меняются, увеличьте шаг времени.

После того, как вы отсортированы, есть много более совершенных методов, которые вы можете использовать в физическом моделировании, но я бы начал с того, что вначале началось базовое время, сделавшее шаг вперед. Более продвинутые методы существенно отличаются от вашей идеи "сделать более разумную интерполяцию изменений по временному шагу" - вы на правильном пути здесь, но вы должны идти до того, как вы запустите.

Ответ 3

Я начну с техники, которая почти такая же простая, как интеграция Euler-Cromer, которую вы использовали, но заметно более точная. Это техника чехарды. Идея очень проста: положение и скорость сохраняются на расстоянии половины временного шага друг от друга.

Начальное состояние имеет положение и скорость в момент времени t 0. Чтобы получить смещение на половину шага, вам понадобится специальный случай для самого первого шага, где скорость продвигается на половину шага, используя ускорение в начале интервала, а затем положение продвигается полным шагом. После этого первого специального случая код работает так же, как ваш интегратор Euler-Cromer.

В псевдокоде алгоритм выглядит как

void calculate_accel (orbiting_body_collection, central_body) {
    foreach (orbiting_body : orbiting_body_collection) {
        delta_pos = central_body.pos - orbiting_body.pos;
        orbiting_body.acc =
            (central_body.mu / pow(delta_pos.magnitude(),3)) * delta_pos;
    }
}

void leapfrog_step (orbiting_body_collection, central_body, delta_t) {
    static bool initialized = false;
    calculate_accel (orbiting_body_collection, central_body);
    if (! initialized) {
        initialized = true;
        foreach orbiting_body {
            orbiting_body.vel += orbiting_body.acc*delta_t/2.0;
            orbiting_body.pos += orbiting_body.vel*delta_t;
        }
    }
    else {
        foreach orbiting_body {
            orbiting_body.vel += orbiting_body.acc*delta_t;
            orbiting_body.pos += orbiting_body.vel*delta_t;
        }
    }
}

Обратите внимание, что я добавил ускорение как поле каждого орбитального тела. Это был временный шаг, чтобы сохранить алгоритм, похожий на ваш. Отметим также, что я переместил вычисление ускорения на свою собственную функцию. Это не временный шаг. Это первый важный шаг к продвижению еще более совершенных методов интеграции.

Следующий важный шаг - отменить это временное добавление ускорения. Ускорения должным образом принадлежат интегратору, а не телу. С другой стороны, расчет ускорений принадлежит проблемному пространству, а не интегратору. Возможно, вы захотите добавить релятивистские поправки, или давление солнечной радиации, или планету к гравитационным взаимодействиям планеты. Интегратор не должен знать, что происходит в этих ускорениях. Функция calculate_accels представляет собой черный ящик, вызываемый интегратором.

У разных интеграторов есть очень разные понятия, когда нужно вычислять ускорения. Некоторые хранят историю недавних ускорений, для некоторых требуется дополнительное рабочее пространство для вычисления среднего ускорения. Некоторые делают то же самое со скоростями (сохраняют историю, имеют некоторую рабочую область скорости). Некоторые более продвинутые методы интеграции используют целый ряд методов, переходя от одного к другому, чтобы обеспечить наилучший баланс между точностью и потреблением ЦП. Если вы хотите имитировать солнечную систему, вам нужен чрезвычайно точный интегратор. (И вам нужно перемещаться далеко, далеко от поплавков. Даже удваивается недостаточно для высокоточной интеграции солнечной системы. С поплавками не слишком много идет мимо RK4 и, возможно, даже не прыгает.)

Правильное разделение того, что принадлежит кому (интегратор по сравнению с проблемным пространством) позволяет уточнить область проблем (добавить относительность и т.д.) и позволяет легко переключать методы интеграции, чтобы вы могли оценить один метод по сравнению с другим.