Как рассчитать отрицательное ускорение?
Я использую прокрутку в сенсорном интерфейсе, но в данный момент я слишком устал, чтобы сосредоточиться на какой-то тривиальной части математики:
y (distance/velocity)
|********
| ******
| ****
| ***
| ***
| **
| **
| *
| *
-------------------------------- x (time)
е (х)->?
Предполагается, что пользовательский интерфейс позволяет пользователю перетаскивать и "бросать" представление в любом направлении и удерживать его прокруткой в течение некоторого времени даже после того, как он уберет палец с экрана. Это своего рода импульс, который зависит от того, насколько быстро пользователь перетаскивал, прежде чем он убрал палец.
Таким образом, у меня есть начальная скорость (v0), и каждые 20 мс я прокручиваю на величину относительно текущей скорости. С каждой итерацией прокрутки я немного понижаю скорость, пока она не падает ниже порогового значения, когда я ее останавливаю. Это просто не выглядит правильно, когда я уменьшаю его на фиксированную величину (линейную), поэтому мне нужно смоделировать отрицательное ускорение, но не могу найти приличную простую формулу, как рассчитать величину, на которую мне нужно снизить скорость в каждой итерации.
Обновить:
Спасибо за ваши ответы, но мне все еще не удалось получить удовлетворительную функцию из обратной связи. Вероятно, я не описал желаемое решение достаточно хорошо, поэтому я попытаюсь привести пример из реальной жизни, который должен проиллюстрировать, какой вид расчета я бы хотел сделать:
Предположим, что по определенной улице едет определенная машина, и водитель нажимает на тормоза до максимума, пока машина не останавливается. Водитель делает это с одним и тем же автомобилем на одной и той же улице несколько раз, но начинает тормозить с разными скоростями. Пока машина замедляется, я хочу иметь возможность рассчитать скорость, которую она будет иметь ровно через одну секунду, основываясь только на текущей скорости. Для этого расчета не должно иметь значения, на какой скорости ехал автомобиль, когда водитель начал тормозить, так как все факторы окружающей среды остаются неизменными. Конечно, в формуле будут некоторые константы, но когда автомобиль опускается до 30 м / с, он будет проходить то же расстояние в следующую секунду, независимо от того, ехал он 100 или 50 м / с, когда водитель начал тормозить, Таким образом, время, прошедшее с момента появления перерывов, также не будет параметром функции. Замедление с определенной скоростью всегда будет одинаковым.
Как вы вычисляете скорость на одну секунду позже в такой ситуации, предполагая некоторые произвольные постоянные для замедления, массы, трения или чего-либо еще и игнорируя такие усложняющие воздействия, как сопротивление воздуха? Я только после кинетической энергии и ее рассеивания из-за трения от поломки машины.
Обновление 2 Теперь я вижу, что ускорение автомобиля будет линейным, и это на самом деле не то, что я искал. Я уберу это и попробую новые предложения завтра. Спасибо за ваш вклад до сих пор.
15 ответов
[Краткий ответ (при условии C
синтаксис)]
double v(double old_v, double dt) {
t = t_for(old_v);
new_t = t - dt;
return (new_t <= 0)?0:v_for(t);
}
double t_for(double v)
а также double v_for(double t)
являются возвращаемыми значениями от двунаправленного отображения v-to-t (функция в математическом смысле), которое является произвольным с ограничением, что оно является монотонным и определено для v >=0
(и, следовательно, имеет точку, в которой v=0
). Примером является:
double v_for(double t) { return pow(t, k); }
double t_for(double v) { return pow(v, 1.0/k); }
где имея:
k>1
дает замедление, уменьшающееся по модулю с течением времени.k<1
дает замедление, увеличивающееся по модулю с течением времени.k=1
дает постоянное замедление.
[Более длинный (с обоснованием и графиками)]
Таким образом, цель заключается в следующем:
Чтобы найти функцию
v(t+dt)=f(v(t),dt)
который принимает текущее значение скоростиv
и дельта времениdt
и возвращает скорость в данный моментt+dt
(не требует указанияt
посколькуv(t)
уже известен и поставляется в качестве параметра иdt
просто дельта времени). Другими словами, задача состоит в том, чтобы реализовать рутинуdouble next_v(curr_v, dt);
с конкретными свойствами (см. ниже).[Обратите внимание] У рассматриваемой функции есть полезное (и желаемое) свойство возвращать один и тот же результат независимо от "истории" предыдущих изменений скорости. Это означает, что, например, если ряд последовательных скоростей равен [100, 50, 10, 0] (для начальной скорости
v0=100
), любая другая последовательность, большая, чем эта, будет иметь такой же "хвост": [150, 100, 50, 10, 0] (для начальной скоростиv0=150
) и т. д. Другими словами, независимо от начальной скорости, все графики скорости-времени будут фактически копиями друг друга, просто смещенными вдоль оси времени, каждый на свое собственное значение (см. график ниже, обратите внимание на части графиков). между линиямиt=0.0
а такжеt=2.0
идентичны).Кроме того, ускорение
w(t)=dv(t)/dt
должна быть убывающей функцией времениt
(с целью визуально приятного и "интуитивного" поведения движущегося объекта GUI, который мы здесь моделируем).
Предлагаемая идея:
Сначала вы выбираете монотонную функцию скорости с желаемыми свойствами (в вашем случае она постепенно уменьшает ускорение, хотя, как показывает пример ниже, легче использовать "ускоренные"). Эта функция также не должна иметь верхней границы, чтобы вы могли использовать ее для любых больших значений скорости. Кроме того, он должен иметь точку, где скорость равна нулю. Вот некоторые примеры:
v(t) = k*t
(не совсем ваш случай, так как замедлениеk
здесь постоянно),v=sqrt(-t)
(это нормально, определяется на интервалеt <= 0
).Затем, для любой заданной скорости, вы найдете точку с этим значением скорости на графике вышеупомянутой функции (будет точка, поскольку функция не связана, и только одна, поскольку она монотонна), продвигайтесь по дельте времени к меньшей скорости значения, таким образом, приобретая следующий. Итерация постепенно (и неизбежно) приведет вас к точке, где скорость равна нулю.
Это в основном все, даже нет необходимости создавать какую-то "окончательную" формулу, зависимости от значения времени или начальных (не текущих) скоростей исчезают, и программирование становится действительно простым.
Для двух простых случаев этот маленький скрипт на Python создает графики ниже (начальные скорости были 1.0
в 10.0
), и, как вы можете видеть, с любой заданной скорости "уровня" и "вниз" графики "ведут себя" одинаково, что имеет смысл, потому что независимо от того, с какой скоростью вы начинаете замедляться (замедляться), вы "движетесь" "по той же кривой ОТНОСИТЕЛЬНО к точке, где скорость равна (становится) ноль:
import numpy
import pylab
import math
class VelocityCurve(object):
"""
An interface for the velocity 'curve'.
Must represent a _monotonically_ _growing_
(i.e. with one-to-one correspondence
between argument and value) function
(think of a deceleration reverse-played)
Must be defined for all larger-than-zero 'v' and 't'
"""
def v(self, t):
raise NotImplementedError
def t(self, v):
raise NotImplementedError
class VelocityValues(object):
def __init__(self, v0, velocity_curve):
assert v0 >= 0
assert velocity_curve
self._v = v0
self._vc = velocity_curve
def next_v(self, dt):
t = self._vc.t(self._v)
new_t = t - dt
if new_t <= 0:
self._v = 0
else:
self._v = self._vc.v(new_t)
return self._v
class LinearVelocityCurve(VelocityCurve):
def __init__(self, k):
"""k is for 'v(t)=k*t'"""
super(LinearVelocityCurve, self).__init__()
self._k = k
def v(self, t):
assert t >= 0
return self._k*t
def t(self, v):
assert v >= 0
return v/self._k
class RootVelocityCurve(VelocityCurve):
def __init__(self, k):
"""k is for 'v(t)=t^(1/k)'"""
super(RootVelocityCurve, self).__init__()
self._k = k
def v(self, t):
assert t >= 0
return math.pow(t, 1.0/self._k)
def t(self, v):
assert v >= 0
return math.pow(v, self._k)
def plot_v_arr(v0, velocity_curve, dt):
vel = VelocityValues(v0, velocity_curve)
v_list = [v0]
while True:
v = vel.next_v(dt)
v_list.append(v)
if v <= 0:
break
v_arr = numpy.array(list(v_list))
t_arr = numpy.array(xrange(len(v_list)))*dt
pylab.plot(t_arr, v_arr)
dt = 0.1
for v0 in range(1, 11):
plot_v_arr(v0, LinearVelocityCurve(1), dt)
for v0 in range(1, 11):
plot_v_arr(v0, RootVelocityCurve(2), dt)
pylab.xlabel('Time ')
pylab.ylabel('Velocity')
pylab.grid(True)
pylab.show()
Это дает следующие графики (линейные графики для линейного замедления (т. Е. Постоянное замедление), "пышные" - для случая "квадратный корень" (см. Код выше)):
Также, пожалуйста, имейте в виду, что для запуска вышеописанного скрипта необходимо установить pylab, numpy и friends (но только для части прорисовки классы "core" ни от чего не зависят и, конечно, могут использоваться самостоятельно).
PS При таком подходе можно действительно "построить" (например, расширить различные функции для разных t
интервалы или даже сглаживание рисованной (записанной) "эргономичной" кривой) "перетаскивание", которое ему нравится:)
После прочтения комментариев я хотел бы изменить свой ответ: умножьте скорость на k < 1, например, k = 0,955, чтобы она экспоненциально уменьшалась.
Объяснение (с графиками и настраиваемым уравнением!) Следует...
Я интерпретирую график в вашем первоначальном вопросе как показывающий, что скорость остается около начального значения, а затем быстро уменьшается. Но если вы представите, что книга скользит по столу, она быстро уходит от вас, затем замедляется, а затем останавливается. я согласен с @Chris Farmer
что подходящая модель - это сила сопротивления, пропорциональная скорости. Я собираюсь взять эту модель и получить ответ, который я предложил выше. Я заранее прошу прощения за длину этого. Я уверен, что кто-то лучше в математике может значительно упростить это. Кроме того, я помещаю ссылки на графики напрямую, в ссылках есть некоторые символы, которые парсеру SO не нравятся. URL исправлены сейчас.
Я собираюсь использовать следующие определения:
x -> time
a(x) -> acceleration as a function of time
v(x) -> velocity as a function of time
y(x) -> position as a function of time
u -> constant coefficient of drag
colon : denotes proportionality
Мы знаем, что сила, вызванная сопротивлением, пропорциональна скорости. Мы также знаем, что сила пропорциональна ускорению.
a(x) : -u v(x) (Eqn. 1)
Знак минус гарантирует, что ускорение будет противоположно текущему направлению движения.
Мы знаем, что скорость - это интегрированное ускорение.
v(x) : integral( -u v(x) dx ) (Eqn. 2)
Это означает, что скорость пропорциональна ее собственному интегралу. Мы знаем это e^x
удовлетворяет этому условию. Итак, мы предполагаем, что
v(x) : e^(-u x) (Eqn. 3)
Коэффициент аэродинамического сопротивления в экспоненте таков, что при решении интеграла в уравнении. 2 u
отменяет, чтобы вернуться к уравнению. 3.
Теперь нам нужно выяснить значение u
, Как @BlueRaja
указал, e^x
никогда не равняется нулю, независимо от х. Но он приближается к нулю для достаточно отрицательного х. Давайте рассмотрим 1% нашей первоначальной скорости как "остановленный" (ваше представление о пороге), и предположим, что мы хотим остановиться в течение x = 2 секунд (вы можете настроить это позже). Тогда нам нужно решить
e^(-2u) = 0.01 (Eqn. 4)
что приводит нас к расчету
u = -ln(0.01)/2 ~= 2.3 (Eqn. 5)
Давайте график v(x)
,
Похоже, он экспоненциально затухает до небольшого значения за 2 секунды. Все идет нормально.
Мы не обязательно хотим рассчитывать экспоненты в нашем графическом интерфейсе. Мы знаем, что мы можем легко преобразовать экспоненциальные базы,
e^(-u x) = (e^-u)^x (Eqn. 6)
Мы также не хотим отслеживать время в секундах. Мы знаем, что у нас частота обновления 20 мс, поэтому давайте определим временную метку n
со скоростью тиков 50 тиков / сек
n = 50 x (Eqn. 7)
Подставляя значение u из уравнения. 5 в уравнение 6, в сочетании с уравнением. 7 и подставляя в уравнение. 3, мы получаем
v(n) : k^n, k = e^(ln(0.01)/2/50) ~= 0.955 (Eqn. 8)
Давайте нарисуем это с нашей новой осью X в временных точках.
Опять же, наша функция скорости пропорциональна чему-то, что уменьшается до 1% за желаемое количество итераций и следует модели "выбегания под влиянием трения". Теперь мы можем умножить нашу начальную скорость v0
по формуле 8, чтобы получить фактическую скорость в любой момент времени n:
v(n) = v0 k^n (Eqn. 9)
Обратите внимание, что при реализации нет необходимости отслеживать v0! Мы можем конвертировать закрытую форму v0 * k^n
на рекурсию, чтобы получить окончательный ответ
v(n+1) = v(n)*k (Eqn. 10)
Этот ответ удовлетворяет вашему ограничению, не заботясь о начальной скорости - следующую скорость всегда можно рассчитать, используя только текущую скорость.
Стоит проверить, чтобы поведение позиции имело смысл. Положение после такой скоростной модели
y(n) = y0 + sum(0..n)(v(n)) (Eqn. 11)
Сумма в уравнении 11 легко решается, используя форму уравнения 9. Используя индексную переменную p:
sum(p = 0..n-1)(v0 k^p) = v0 (1-k^n)/(1-k) (Eqn. 12)
Итак, мы имеем
y(n) = y0 + v0 (1-k^n)/(1-k) (Eqn. 13)
Давайте построим это с y0 = 0
а также v0 = 1
,
Таким образом, мы видим быстрое движение от источника, следуя по грациозному берегу до остановки. Я считаю, что этот график является более точным изображением скольжения, чем ваш исходный график.
В общем, вы можете настроить k
используя уравнение
k = e^(ln(Threshold)/Time/Tickrate) (Eqn. 14)
where:
Threshold is the fraction of starting velocity at which static friction kicks in
Time is the time in seconds at which the speed should drop to Threshold
Tickrate is your discrete sampling interval
(Благодаря @poke
для демонстрации использования Wolfram Alpha для сюжетов - это очень мило.)
СТАРЫЙ ОТВЕТ
Умножьте скорость на k < 1, например, k = 0,98, чтобы она экспоненциально уменьшалась.
Пока машина замедляется, я хочу иметь возможность рассчитать скорость, которую она будет иметь ровно через одну секунду, основываясь только на текущей скорости.
Это было бы определение ускорения. Например, если ускорение было a = -9 meters/sec/sec
и скорость сейчас 20 meters/sec
через 1 секунду скорость будет 11 meters/sec
,
Другими словами, изменение скорости Δv
между сейчас и t
секунд с этого момента (при условии постоянного ускорения) будет
Δv = a*t
Это означает, что (классическая физика) уравнение для скорости в любое время t
с учетом начальной скорости при t=0
(эта скорость называется v0)
v(t) = v
0+ a*t
Используя то, чему вы научитесь в первые две недели занятий по исчислению, вы также можете получить уравнение для x(t)
(расстояние автомобиля во времени t
) из приведенного выше уравнения; это даст вам
x(t) = x
0+ v
0*t + 0.5*a*t
2
(также возможно получить это без исчисления, см. здесь)
Наконец, если вы делаете это для игры, а не для симуляции физики (то есть вам не нужны точно точные результаты), вам нужно просто изменить положение и скорость каждого кадра, а не пересчитывать положение каждого кадра. Чтобы сделать это, вам нужно будет делать следующее каждый кадр, предполагая, что скорость (и ускорение) измеряется в пикселях в секунду (-per-second):
velocity_new = velocity_old + acceleration/frames_per_second
position_new = position_old + velocity_old/frames_per_second
Похоже, вы ищете замедление, которое увеличивается со временем.
Попробуйте вычислить
Delta_v = -(A*t + B)
, выбирая разумные константы A и B, которые вам подходят.
т это общее время до этой точки.
Измените свою скорость, добавив Delta_v
,
Это в основном соответствует линейному отрицательному ускорению.
Вы можете в основном выбрать любую функцию, которая увеличивается со временем (скажем, f(t))
и вычислить
Delta_v = -f(t)
Подходящий выбор для f (t) даст желаемый эффект.
Некоторые примеры, которые вы могли бы использовать:
f(t) = At + B.
f(t) = A*exp(Bt)
Конечно, вам придется немного поиграться и попытаться определить правильные константы.
Если вы хотите увеличить замедление, как вы сказали в своем комментарии к ответу mtrw, и вы НЕ очень разборчивы в физическом реализме, приведенное ниже уравнение может быть тем, что вы ищете:
V(t+dt) = V (t) - K1 + K2 x V (t)
V (t) = текущая скорость V(t+dt) = скорость при следующем приращении K1 и K2 - это константы, которые вы калибруете. Просто убедитесь, что (K2 x Vmax) Если это все еще не кажется правильным, попробуйте V(t+dt) = V(t) - K1 + K2 x f(V(t)) где f (x) - монотонно возрастающая функция, которую вы выбираете, может быть квадратный или квадратный корень в зависимости от того, где вы хотите получить чувство. Просто убедитесь, что (K2 x f(V(t))) (монотонно возрастающая функция означает, что f (x) всегда увеличивается при увеличении x)
Я тоже добавлю мысль. Похоже, вы не хотите постоянного (отрицательного) ускорения. Это привело бы к уравнению как:
v(t) = v(0) + a*t,
где a
отрицательное ускорение, t
это время, и v(t)
скорость во времени t
, Это дает вам:
v(t2) - v(t1) = a(t2-t1),
и это означает, что для данного Δt разность скоростей равна Δt, константе.
То, что вы можете искать, это термин "трение", который зависит от текущей скорости. Согласно этому допущению скорость изменения скорости пропорциональна текущей скорости:
d v(t) / d t = -b*v(t).
Решить вышесказанное легко, и вы получите: v (t) = v (0) e−bt.
Интегрируя это уравнение, мы получаем x (t) = v (0) (1 − e−bt) / b, где x - позиция. График положения1 для v(0) = 1, b = 0.1 выглядит как то, что вы могли бы использовать. Возможно, вам захочется поиграть со значениями b и добавить в уравнение коэффициент масштабирования.
1 http://www.wolframalpha.com/input/?i=plot+%281+-+1+e^%28-0.1+x%29+%29+%2F+0.1+for+x+%3D+0+to+100
Вы можете просто уменьшить скорость на постоянную величину в каждой итерации. Пример: вы начинаете со скорости 50, на следующей итерации - 40, затем 30, 20, 10, стоп. Это будет представлять собой постоянное "трение", не зависящее от скорости, и это на самом деле очень близко к реальности (см. Трение в Википедии).
Если вам не нравится появление этого, вам нужно сделать трение зависимым от скорости. Я бы предположил, что линейные отношения friction = base-friction + (coefficient * velocity)
с довольно малым коэффициентом будет достаточно.
Нелинейное изменение скорости означает, что ускорение не является постоянным. Непостоянное ускорение означает, что система находится под влиянием рывка. Возьмите все уравнения ускорения и добавьте "(1/6) jt3". Зафиксируйте a и дайте j a небольшое отрицательное значение, пока v не достигнет 0.
Я попробовал это, который работает (в Ruby). Не уверен, что математика - это звук, но результат выглядит правильно, а это означает, что вы двигаетесь быстрее по направлению к центру:
velocity=100;
(100.downto(0)).each { |distance_from_black_hole | velocity=velocity+9.8/distance_from_black_hole; puts velocity; }
y(x) = y0 - a * e ^ ( k * x )
где y0
является постоянной начала, и a
а также k
факторы.
ускорение = (сила / масса) скорость = (ускорение * время) (сила от пальца пользователя) = ускорение / масса = скорость / время
- придать массе вид (настраивать, пока все не покажется разумным, и позволить пользователю настраивать)
- определить новую силу (перетащить)
- придать новой силе (перетаскивать) величину (настроить до разумного, позволить пользователю настроить ее)
- применить новую силу к объекту и смотреть, как он замедляется
Ускорение является производной первого порядка по скорости и производной второго порядка по расстоянию. Ваш график выглядит как парабола второго порядка, что-то вроде Ck*x^2 для некоторых констант C и k. Если у действительно расстояние, вам нужно =-2k, если у скорость, вам нужно =-2kx. В любом случае скорость v(x) = V0 + a(x)*x. (Где х на самом деле время. Я следую вашему соглашению и не использую т.)
Немного непрограммируемой дискуссии о примере автомобиля.
Во-первых, я предполагаю, что водитель не может заставить тормоза блокироваться на скорости.
Первое (или, может быть, второе или третье), которое учат большинство новых водителей, заключается в том, что естественная тенденция при торможении - удерживать педаль тормоза в фиксированном положении. Результатом является внезапный крен вперед, когда машина медленно движется к остановке. Это происходит потому, что тормоза переходят от динамического трения, где сила торможения пропорциональна тормозному давлению, к статическому трению, где сила торможения восстанавливает передний импульс автомобиля. этот внезапный скачок ускорения неприятен, и новый водитель учится нажимать педаль в самом конце торможения, чтобы остановиться.
Такое поведение маскирует другую особенность, но это можно заметить во время обычного ускорения в автомобиле с механической коробкой передач. при ускорении (или замедлении), если водитель внезапно отключает передачу, все пассажиры внезапно кренится вперед. Что на самом деле происходит, так это то, что ускоряющая сила, прижимающая их к спинкам сидений, внезапно исчезает, и они возвращаются в нейтральное положение сидя. Более удобный способ - постепенно включать сцепление, чтобы постепенно снижать движущую силу двигателя.
В обоих случаях более эстетичный стиль вождения предполагает сглаживание ускорения, устранение внезапных скачков. Это в основном другой способ говорить о непрерывной второй производной. Почти любое движение с этим свойством покажется естественным.
Вы можете отслеживать скорость и уменьшать ее на долю скорости каждый раз. Я считаю, что это очень хорошо имитирует трение.
Я бы уменьшил скорость как что-то вроде v=v*0,9. Тогда у меня была бы скорость, которая считается остановленной скоростью. Таким образом, объект в конце концов остановится и не будет продолжать использовать ресурсы как движущиеся. так что-то вроде для (v= начальная скорость;v<1,0;v*=0,9) { x+=v; }