2-е планирование траектории космического корабля с физикой

Я реализую 2D игру с поставками в пространстве.

Чтобы сделать это, я использую LÖVE, который переносит Box2D с Lua. Но я полагаю, что на мой вопрос может ответить любой с большим пониманием физики, чем я - таким образом, псевдо код принят как ответ.

Моя проблема состоит в том, что я не знаю, как переместить мои космические корабли правильно в 2D поддерживающий физику мир. Более конкретно:

Поставка массы m расположен в исходном положении {x, y}. Это имеет начальный вектор скорости {vx, vy} (может быть {0,0}).

Цель является точкой в {xo,yo}. Поставка должна достигнуть цели, имеющей скорость {vxo, vyo} (или около него), после самой короткой траектории.

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

function Ship:update(dt)
  m = self:getMass()
  x,y = self:getPosition()
  vx,vy = self:getLinearVelocity()
  xo,yo = self:getTargetPosition()
  vxo,vyo = self:getTargetVelocity()
  thrust = self:getThrust()

  if(???)
    angle = ???
    self:applyImpulse(math.sin(angle)*thrust, math.cos(angle)*thrust))
  end
end

Первое ??? есть ли, чтобы указать, что в некоторых случаях (я предполагаю) было бы лучше к "не послать импульсы" и оставить поставку "дрейфом". Второе ??? часть состоит о том, как вычислить импульсный угол на данный dt.

Мы находимся в пространстве, таким образом, мы можем проигнорировать вещи как трение о воздух.

Хотя это было бы очень хорошо, я не ищу кого-то для кодирования этого для меня; я поместил код там, таким образом, моя проблема ясно понята.

То, в чем я нуждаюсь, является стратегией - способ напасть на это. Я знаю некоторую базовую физику, но я не эксперт. Например, эта проблема имеет имя? Такая вещь.

Большое спасибо.

Править: Бета предоставила действительную стратегию этого, и судите, любезно реализовал его непосредственно в LÖVE, в комментариях.

EDIT2: После большего количества поиска с помощью Google я также нашел openSteer. Это находится на C++, но это делает то, что я симулировал. Это, вероятно, будет полезно любому достигающему этого вопроса.

12
задан Judge Maygarden 28 September 2010 в 19:09
поделиться

6 ответов

Это называется планированием движения, и оно не является тривиальным.

Вот простой способ получить неоптимальную траекторию:

  1. Остановитесь. Приложите тягу, противоположную направлению скорости, пока скорость не станет нулевой.
  2. Рассчитайте последний этап, который будет противоположен первому - постоянная тяга со старта, которая приведет корабль к x0 и v0. Начальная точка будет находиться на расстоянии |v0|^2/(2*тяга) от x0.
  3. Доберитесь до этой стартовой точки (а затем сделайте последний рывок). Добраться из одной точки стояния в другую очень просто: тянитесь к ней, пока не пройдете половину пути, затем тянитесь назад, пока не остановитесь.

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

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

EDIT: Вот точное решение более простой задачи.

Предположим, что вместо тяги, которую мы можем направить в любом направлении, у нас есть четыре фиксированных движителя, направленных в направлениях {+X, +Y, -X, -Y}. В любой момент времени мы будем стрелять не более чем по одному из +/-X и не более чем по одному из +/-Y (нет смысла стрелять по +x и -X одновременно). Итак, теперь задачи X и Y независимы (в исходной задаче их нет, потому что тяга должна быть разделена между X и Y). Теперь мы должны решить одномерную задачу - и применить ее дважды.

Оказывается, наилучшая траектория включает в себя движение в одном направлении, затем в другом, и больше не возвращается в первое. (Каботажное движение полезно только в том случае, если решение задачи по другой оси займет больше времени, чем по вашей, так что у вас есть время, которое можно убить). Сначала решите проблему скорости: предположим (WLOG), что ваша целевая скорость больше вашей начальной скорости. Для достижения целевой скорости вам потребуется период тяги (+) длительностью

T = (Vf - Vi)/a

(Я использую Vf: конечная скорость, Vi: начальная скорость, a: величина тяги.)

Мы заметили, что если это все, что мы делаем, местоположение не будет правильным. Фактическое конечное положение будет

X = (Vi + Vf)T/2

Поэтому мы должны добавить поправку

D = Xf - X = Xf -(Vi+Vf)T/2

Теперь, чтобы положение получилось правильным, мы добавим период тяги в одном направлении до этого, и такой же период в противоположном направлении после. Это оставит конечную скорость неизменной, но даст нам некоторое смещение. Если длительность этого первого периода (и третьего) равна t, то смещение, которое мы получим в результате, будет равно

d = +/-(at^2 + atT)

Величина +/- зависит от того, на что мы делаем упор: на +, затем на -, или на -, затем на +. Предположим, что это +. Решаем квадратик:

t = (-aT + sqrt(a^2 T^2 + 4 a D))/2a

И готово.

9
ответ дан 2 December 2019 в 21:23
поделиться

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

-- shortest path minus initial velocity
dx,dy = x0 - x - vx, y0 - y - vy

-- normalize the direction vector
magnitude = sqrt(dx*dx + dy*dy)
dx,dy = dx/magnitude, dy/mangitude

-- apply the thrust in the direction we just calculated
self:applyImpulse(thrust*dx, thrust*dy)

Обратите внимание, что это не принимает во внимание скорость цели, потому что это становится чрезвычайно сложным.

У меня есть очень маленький Lua-модуль для обработки 2D-векторов в этом бункере для вставки . Добро пожаловать. Приведенный выше код сократится до:

d = destination - position - velocity
d:normalize()
d = d * thrust
self:applyImpulse(d.x, d.y)
1
ответ дан 2 December 2019 в 21:23
поделиться

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

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

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

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

(РЕДАКТИРОВАТЬ: также, я забыл упомянуть, это потребует некоторой корректировки, чтобы иметь дело со сценарием, когда вы сильно смещаетесь в перпендикулярном направлении, но не сильно в параллельном направлении. Важный урок здесь - взять компоненты , который дает вам полезные числа, на которых можно принять решение.)

0
ответ дан 2 December 2019 в 21:23
поделиться

В отсутствие дополнительной информации мы можем предположить, что на космический корабль действуют 3 силы, которые в конечном итоге определяют его траекторию:

  • " импульсы ": [управляемая пользователем / программой] сила.
    Пользователь (или программа), похоже, полностью контролирует это, т.е. он контролирует направление импульса и его тягу (вероятно, в диапазоне от 0 до максимального значения)
  • некоторая внешняя сила : назовите это гравитацией, как угодно ...
    Такая сила может быть вызвана несколькими источниками, но нас просто интересует результирующая объединенная сила: в данный момент времени и в данном пространстве эта внешняя сила действует на корабль с заданной силой и направлением. Пользователь / программа не может их контролировать.
  • инерция : это связано с текущей скоростью и направлением корабля. Эта сила обычно заставляет корабль продолжать движение в текущем направлении с текущей скоростью. Могут быть и другие параметры [космической эры], управляющие инерцией, но в целом они пропорциональны как скорости, так и массе корабля (интуитивно понятно, что будет легче остановить корабль, если его текущая скорость меньше и / или если его масса меньше)

Очевидно, пользователь / программа контролирует (в определенных пределах) только первую силу.
Из вопроса неясно, является ли данная проблема:

  • [Проблема A ], чтобы написать программу, которая обнаруживает динамику системы (и / или адаптируется к изменениям этой динамики).
    или ..
  • [Проблема B], чтобы предложить модель-формулу-, которую можно использовать для вычисления объединенной силы, в конечном итоге приложенной к кораблю: «взвешенная» сумма импульса, управляемого пользователем, и двух других сил, управляемых системой / физикой.

Последний вопрос, проблема B, более легко и лаконично объясняется, поэтому давайте предложим следующую модель:

Constant Parameters:
  ExternalForceX   = strength of the external force in the X direction
  ExternalForceY   = id. Y direction
  MassOfShip       = coeficient controlling 
Variable Parameters:
  ImpulseAngle     = direction of impulse
  ImpulseThrust    = force of thrust
Formula:
  Vx[new] = (cos(ImpulseAngle) * ImpulseThrust) + ExternalForceX  + (MassOfShip * Vx[current])
  Vy[new] = (sin(ImpulseAngle) * ImpulseThrust) + ExternalForceY  + (MassOfShip * Vy[current])

Обратите внимание, что вышеупомянутая модель предполагает постоянную Внешнюю силу (постоянную как с точки зрения ее силы, так и направления); то есть: сродни гравитационному полю, относительно удаленному от отображаемой области (точно так же, как, скажем, гравитация Земли, рассматриваемая в пределах футбольного поля). Если масштаб отображаемой области велик по сравнению с источником (источниками) внешних сил, средний член приведенных выше формул должен быть изменен, чтобы включить: тригонометрический фактор, основанный на углу между центром источника и током. положение и / или [обратно] пропорциональный коэффициент, основанный на расстоянии между центром источника и текущим положением.
Точно так же предполагается, что масса Корабля остается постоянной, это вполне может быть переменной, основанной, скажем, на массе пустого Корабля, к которой вес топлива удаляется / добавляется в процессе игры. прогрессирует.

Теперь ... Все вышеизложенное предполагает, что динамикой системы управляет разработчик игры: по сути, выбирая набор значений для упомянутого параметра и, возможно, добавляя немного сложности в математику формулы (а также обеспечение надлежащего масштабирования, чтобы в целом «удерживать» корабль в пределах области отображения).

Что, если бы вместо этого динамика системы была легко запрограммирована в игру (и предполагалась, что она скрыта / случайна), и наша задача состоит в том, чтобы написать программу, которая будет постепенно определять направление и значение тяги импульсов для движения корабль к намеченному пункту назначения таким образом, чтобы его скорость у цели была как можно ближе к getTargetVelocity ()? Это «Проблема А».

Этот тип проблемы может быть решен с помощью ПИД-регулятора . В двух словах, такой контроллер «решает», какое действие (в случае этой игры = какой угол импульса и количество тяги применить) на основе трех взвешенных факторов, в общих чертах определенных ниже:

  • насколько далеко мы находимся от текущих значений от «заданной точки»: это P = Пропорциональная часть ПИД
  • , насколько быстро мы приближаемся к «уставке»: это D = Производная часть ПИД
  • , как долго и сколько мы были вдали от «уставки»: это I = Интегральная часть ПИД-регулятора

Менее сложный контроллер может, например, использовать только пропорциональный коэффициент. Это может привести к колебаниям, иногда с большой амплитудой по обе стороны от заданного значения («Я на X единиц от того места, где я должен быть: позвольте мне дернуть руль и нажать на газ»). Такое превышение уставки сдерживается производным фактором («Да, я все еще не там, где должен быть, но прогресс, которого я добился с момента последней проверки, очень велик: лучше немного притормозить») . Наконец, интегральная часть принимает во внимание тот факт, что при прочих равных в отношении комбинированной пропорциональной и производной частей, меньшее или большее действие будет уместным в зависимости от того, были ли мы «сбились с пути» в течение длительного времени или нет. и из-за большого количества отклонений, которые мы были все это время (например. «В последнее время мы отслеживаем довольно близко к тому месту, где должны быть, нет смысла делать необдуманные шаги»)

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

4
ответ дан 2 December 2019 в 21:23
поделиться

Вы выгружаете топливо? Если это так, ваша масса со временем изменится.

Тяга - это реактивная сила.Это скорость изменения массы, умноженная на скорость выхлопа относительно космического корабля.

Есть ли у вас внешние силы? Если вы это сделаете, они должны быть включены в ваш импульсный расчет.

Предположим, что это магический толчок без выброса массы и без внешних сил.

Импульс имеет единицы количества движения. Это интеграл силы во времени.

Во-первых, вам нужно точно выяснить, что API называет «толчком» и «импульсом». Если вы подаете ему тягу, умноженную на скаляр (число), тогда applyImpulse должен сделать что-то еще с вашим вводом, чтобы иметь возможность использовать его в качестве импульса, потому что единицы не совпадают.

Предполагая, что ваша «тяга» - это сила, вы умножаете эту тягу на временной интервал (1/30 секунды), чтобы получить импульс, и разбиваете компоненты.

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

0
ответ дан 2 December 2019 в 21:23
поделиться

Ваш угол - это обратная касательная к противоположному / смежному

Итак, angle = InvTan (VY / VX)

Не уверен, о чем вы говорите относительно желания дрейфовать ??

-1
ответ дан 2 December 2019 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: