Алгоритм управления ускорением до достижения позиции - PullRequest
9 голосов
/ 24 февраля 2011

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

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

Примечания:

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

Ответы [ 4 ]

26 голосов
/ 24 февраля 2011

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

Концептуально вы прикрепляете точку к целевой точке с помощью пружины или куска упругости.Пружина демпфируется, чтобы вы не подпрыгивали.Вы можете контролировать скорость реакции системы, изменяя константу, называемую SpringConstant.Это, по сути, то, насколько прочный кусок упругости.

В основном вы прикладываете две силы к позиции, а затем интегрируете это с течением времени.Первая сила, которая применяется пружиной, Fs = SpringConstant * DistanceToTarget.Второе - это демпфирующая сила, Fd = -CurrentVelocity * 2 * sqrt (SpringConstant).

CurrentVelocity является частью состояния системы и может быть инициализирована в ноль.

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

Мы добавим это к фактическому положению точки.

В коде C ++:

float CriticallyDampedSpring( float a_Target,
                              float a_Current,
                              float & a_Velocity,
                              float a_TimeStep )
{
    float currentToTarget = a_Target - a_Current;
    float springForce = currentToTarget * SPRING_CONSTANT;
    float dampingForce = -a_Velocity * 2 * sqrt( SPRING_CONSTANT );
    float force = springForce + dampingForce;
    a_Velocity += force * a_TimeStep;
    float displacement = a_Velocity * a_TimeStep;
    return a_Current + displacement;
}

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

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

Я надеюсь, что это полезно, удачи:)

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

1 голос
/ 24 февраля 2011

Не совсем понятно, что именно вы ищете, но я собираюсь предположить следующее:

1) Существует некоторое максимальное ускорение;2) Вы хотите, чтобы объект прекратил движение, когда достигнет пункта назначения;3) В отличие от скорости, вам не требуется, чтобы ускорение было непрерывным.

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

УравнениеВы хотите v_f ^ 2 = v_i ^ 2 + 2 ad, где v_f = 0 - конечная скорость, v_i - начальная (текущая) скорость, а d - расстояние до пункта назначения (когда вы переключаетесь с ускорения A на ускорение -A - то есть от ускорения до замедления; здесь я предполагаю, что d положительно).

Решение: d = v_i ^ 2 / (2A) - это расстояние.(Негативы отменяются).Если текущее оставшееся расстояние больше, чем d, увеличьте скорость как можно быстрее.В противном случае начните замедляться.

Допустим, вы обновляете позицию объекта каждые t_step секунд.Тогда:

new_position = old_position + old_velocity * t_step + (1/2) a (t_step) ^ 2

new_velocity = old_velocity + a * t_step.

Если пункт назначениянаходится между new_position и old_position (т. е. объект достиг пункта назначения между обновлениями), просто установите new_position = destination.

0 голосов
/ 24 февраля 2011

Я бы сделал что-то вроде ответа Алекса Дима для планирования траектории, но с ограничениями по силе и скорости:

В псевдокоде:

xtarget:  target position
vtarget:  target velocity*
x: object position
v: object velocity
dt: timestep

F = Ki * (xtarget-x) + Kp * (vtarget-v);
F = clipMagnitude(F, Fmax);
v = v + F * dt;
v = clipMagnitude(v, vmax);
x = x + v * dt;

clipMagnitude(y, ymax):
   r = magnitude(y) / ymax
   if (r <= 1)
       return y;
   else
       return y * (1/r);

где Ki и Kp - постоянные настройки, Fmax и vmax - максимальная сила и скорость. Это должно работать в 1-D, 2-D или 3-D ситуациях (величина (y) = abs (y) в 1-м, иначе используйте векторную величину).

0 голосов
/ 24 февраля 2011

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

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

Плагин jquery.easing имеет множество функций замедления, на которые вы можете посмотреть:

http://gsgd.co.uk/sandbox/jquery/easing/

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...