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

Я использую Unity3D и пишу на C#.

  • Масса Земли: 5,972e+22
  • Скорость Земли: [0,0,0]
  • Земной радиус: 63,71
  • Масса МКС: 4194,55
  • Начальная скорость МКС: [76.50, 0, 0]
  • Высота МКС: 4
  • Гравитационная постоянная: 6,67408e-15

Все значения являются реальными значениями / 100, так как я уменьшаю масштаб. (например, гравитационная постоянная была 6,67408e-11/11 = 6,67408e-15.

Вот код...

void Gravity(Gravity body) {
    float dSquared = (body.transform.position - transform.position).sqrMagnitude;
    float force = (body.G * shipMass * body.planetMass) / dSquared;
    Vector3 forceDirection = (body.transform.position - transform.position).normalized;
    Vector3 forceVector = (forceDirection * force);
    shipVelocity += forceVector * Time.deltaTime;

    transform.position += shipVelocity * Time.deltaTime;
}

Чтобы сделать эту работу, мне нужно установить гравитационную постоянную на: 1,59508e-21.

Вот изображение орбиты, розовая линия — пройденный, а не будущий путь (показанные расчетные элементы орбиты, скорее всего, неверны):

Вот изображение орбиты после ускорения времени:

Редактировать 1:

Используя эту формулу для ускорения и первую часть ответа Рассела Борогова, я смог выйти на круговую орбиту, используя эти значения:

  • Масса Земли: 5,972e+22
  • Скорость Земли: [0,0,0]
  • Земной радиус: 63,71
  • Масса МКС: 4194,55
  • Начальная скорость МКС: [0,0765, 0, 0]
  • Высота МКС: 4
  • Гравитационная постоянная: 6,67408e-24 (смещено на -15, затем на +2 порядка)

и этот код:

void Gravity(Gravity body) {
    float dSquared = (body.transform.position - transform.position).sqrMagnitude;
    float M = body.planetMass;
    Vector3 forceDirection = (body.transform.position - transform.position).normalized;
    float g = G * M / dSquared;
    Vector3 forceVector = (forceDirection * g);
    shipVelocity += forceVector * Time.deltaTime;
}

Редактировать 2: добавлены расчеты гравитации с использованием скрипта-аккумулятора Рассела Борогова ниже...

void Update() {
    Gravity(orbitee);
    ApplyForce();
}

void ApplyForce() {
    transform.position += shipVelocity * Time.deltaTime;
    transform.Rotate(transform.forward * shipTorque * Time.deltaTime);
}

void Gravity(Gravity body) {
    accumulator += Time.deltaTime;
    while (accumulator > 0.0f) {
        float dSquared = (body.transform.position - transform.position).sqrMagnitude;
        float M = body.planetMass;
        Vector3 forceDirection = (body.transform.position - transform.position).normalized;
        float g = G * M / dSquared;
        Vector3 forceVector = (forceDirection * g);
        shipVelocity += forceVector * simtime;
        accumulator -= simtime;
    }
}

Редактировать 3: Обновлен скрипт орбитальных элементов (вероятно, не на 100% правильно)

    //Mass of satellite
    float m1 = shipMass;

    //Mass of planet
    float m2 = orbiting.planetMass;
    float M = m2;

    //Relative Position Vector
    Vector3 r = FindRelativePosition(orbitee.transform.position, transform);

    //Relative Velocity Vector
    Vector3 v = shipCurVel;
    //#!# add relative to planet velocity

    //Specific Angular Momentum
    Vector3 h = Vector3.Cross(r, v);

    //Standard Gravitational Parameter
    float µ = G * (m1 + m2);
    //float µ = G * M;

    //Eccentricity Vector
    Vector3 evec = (Vector3.Cross(v, h) / µ) - (r / Vector3.Magnitude(r));

    //Eccentricity
    float e = Vector3.Magnitude(evec);

    //Vector to Ascending Node #!#
    Vector3 n = Vector3.Cross(new Vector3(0, 0, 1), h);

    //True Anomaly
    float t = Mathf.Acos((Vector3.Dot(evec, r)) / (e * Vector3.Magnitude(r)));
    if (Vector3.Dot(r, v) < 0)
        t = (2 * Mathf.PI) - t;

    //Eccentric Anomaly
    float E = 2 * Mathf.Atan(Mathf.Tan(t / 2) / Mathf.Sqrt((1 + e) / (1 - e)));

    //Longitude of Ascending Node (2D)
    float Ω = 0;

    //Inclination (2D)
    float i = 0;

    //Argument of Periapsis
    float ω = Mathf.Atan2(evec.y, evec.x);
    float ωdegrees = ω * (180 / Mathf.PI);
    //float ω = Mathf.Acos((Vector3.Dot(n, evec)) / (e * Vector3.Magnitude(n)));

    //Mean Anomaly 
    float MA = E - (e * Mathf.Sin(E));

    //Semi-Major Axis
    float a = 1 / ((2 / Vector3.Magnitude(r)) - (Mathf.Pow(Vector3.Magnitude(v), 2) / µ));

    //Apoapsis
    float ap = a * (1 + e);

    //Periapsis
    float pe = a * (1 - e);

    //Orbital Period
    float T = (2 * Mathf.PI) * Mathf.Sqrt(Mathf.Pow(a, 3) / µ)/60;

Последнее редактирование: исправлена ​​проблема, связанная с неправильным эксцентриситетом — неправильное вычисление относительного положения.

Вы используете массу корабля для вычисления силы, но не делите на массу, когда применяете ее как ускорение (быстрая проверка, 1,595 (мантисса вашего G) x 4194 (масса корабля) дает около 6700 (соответствие мантиссы правильному G) так что проблема скорее всего в этом) Как правило, проще пропустить вычислительную мощность и просто вычислить ускорение, которое не зависит от массы корабля. И снова вы путаете 100 м и 100 км.
Я попробую и доложу. Значит, скорость должна быть 0,0765 вместо 76,5?
Вероятно, это нормально, если вы получите правильное количество нулей в G.
Изменяется ли G в зависимости от масштаба или это нечто, что должно быть постоянным? Пришлось сделать его существенно меньшим по сравнению с изменением всех остальных параметров.
Конечно, это так. Уточнил в ответе.

Ответы (1)

Большая G равна 6,67408e-11 м 3 кг -1 сек - 2 .

Таким образом, если вы равномерно используете 100-километровые единицы вместо m единиц, это будет разница в 5 величин, поэтому G должна измениться на 15 величин, потому что линейный размер возводится в куб.

Единицы массы - 100 кг, G использует -1 из них, поэтому G идет в другую сторону на 2 величины.

Я понятия не имею, что ты делаешь с секундами. Если секунды — это секунды (а для совершения оборота по орбите требуется 90 минут), то G не меняется. Если вы используете какой-то другой коэффициент преобразования, удвойте количество нулей, на которые вы сдвигаете секунды, чтобы изменить G.

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

float accumulator = 0.0f;
float simtime = 0.001f;

void Update( void )
{
    accumulator += Time.deltaTime;
    while (accumulator > 0.0f)
    {
         SimulateGravityForTime( simtime );
         accumulator -= simtime;
    }
}

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


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

+1 за отличный ответ, +1 за включение небольшого кода, -1 за помощь и подстрекательство творческих и индивидуализированных физических единиц без малейшего предостережения о том, что может пойти не так в будущем. :) Даже дюймы против сантиметров чуть не убили Хаббла.
Я думаю, что в обсуждениях, которые у меня были с OP (в комментариях к этому и предыдущему вопросу), я ясно дал понять, что важно уделять внимание единицам. Тем не менее, я добавлю немного о разделении модели представления.
Хорошо - я начну работать над тем, чтобы изменить свой голос на +2, которого заслуживает ваш ответ. Это включает в себя целочисленное переполнение и что-то о забивании строк, а затем некоторые тахионы, но я еще не там. (юмор)
@RussellBorogove Я закончил гравитацию и использовал здесь ваш скрипт аккумулятора . Когда я конвертирую T в минуты, вы видите, что здесь он соответствует 90-метровому орбитальному периоду . Я отредактировал свой пост, включив в него скрипт орбитальных элементов. Что-то не так с моими орбитальными элементами? Я уверен, что AP и PE неполные или неправильные, поскольку мне нужно изменить их по радиусу, чтобы получить близкие числа, или по величине r, чтобы получить точные числа в момент времени t: 0.
@RussellBorogove следует отметить, что я преобразовал ω в градусы в выводе на втором изображении, когда обычно это в радианах.
Что ж, эксцентриситет должен быть около нуля (круговой), а не около 1 (выход). Насчет остальных не уверен.
Мне придется разобраться с этим, так как эксцентриситет используется во многих других вычислениях.
Я не уверен в своем расчете E, так как приведенные здесь обозначения смущают меня, я не знаю, должен ли я использовать: Mathf.Atan2(Mathf.Tan(t / 2), Mathf.Sqrt((1 + e) / (1 - e)))или2 * Mathf.Atan(Mathf.Tan(t / 2) / Mathf.Sqrt((1 + e) / (1 - e)))
@RussellBorogove Думаю, я, скорее всего, столкнулся с проблемой масштабирования единиц измерения с моим µ
Если я применяю v = v / 100или v /= 100после нахождения скорости v , AP выходит правильным, так что где-то все еще что-то не так на порядок?
2in в библиотеках Atan2компьютерной математики относится к двум аргументам, а не к умножению на 2. Один аргумент Atanне может определить разницу между входными данными ( + / + ) и ( - / - ), что важно в некоторых триггерных приложениях. не знаю, является ли это одним из них. Второй из ваших вариантов выглядит правильно, для первого нужен файл 2 *. Если ваши результаты являются нормальными для половины орбиты и безумными для другой половины, вам нужно Atan2.