2013-03-06 1 views
12

Скажем, у меня есть Bezier curveB(u), если приращение u параметра с постоянной скоростью не получить скорость перемещения Костанта вдоль кривой, так как соотношение между u параметром и точкой полученная оценка кривой не является линейной.Безье кубических кривых: перемещение с равномерным ускорением

Я прочитал и внедрил article Дэвида Эберли. В нем объясняется, как двигаться с постоянной скоростью по параметрической кривой.

Предположим, что у меня есть функция F(t), который принимает в качестве входных данных значение времени t и функция скорости sigma, которая возвращает значение скорости в момент времени t, можно получить скорость перемещения Костанта вдоль кривой, изменяя параметр т с постоянной скоростью : B(F(t))

ядро ​​статьи я использую следующие функции:

float umin, umax; // The curve parameter interval [umin,umax]. 
Point Y (float u); // The position Y(u), umin <= u <= umax. 
Point DY (float u); // The derivative dY(u)/du, umin <= u <= umax. 
float LengthDY (float u) { return Length(DY(u)); } 
float ArcLength (float t) { return Integral(umin,u,LengthDY()); } 
float L = ArcLength(umax); // The total length of the curve. 
float tmin, tmax; // The user-specified time interval [tmin,tmax] 
float Sigma (float t); // The user-specified speed at time t. 

float GetU (float t) // tmin <= t <= tmax 
{ 
    float h = (t - tmin)/n; // step size, `n' is application-specified 
    float u = umin; // initial condition 
    t = tmin; // initial condition 
    for (int i = 1; i <= n; i++) 
    { 
    // The divisions here might be a problem if the divisors are 
    // nearly zero. 
    float k1 = h*Sigma(t)/LengthDY(u); 
    float k2 = h*Sigma(t + h/2)/LengthDY(u + k1/2); 
    float k3 = h*Sigma(t + h/2)/LengthDY(u + k2/2); 
    float k4 = h*Sigma(t + h)/LengthDY(u + k3); 
    t += h; 
    u += (k1 + 2*(k2 + k3) + k4)/6; 
    } 
    return u; 
} 

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

Вот пример прямой кривой Безье, где P0 и P1 - контрольные точки, T0 T1 - касательная. это определяется кривая:

[x,y,z]= B(u) =(1–u)3P0 + 3(1–u)2uT0 + 3(1–u)u2T1 + u3P2 

enter image description here

Допустим, я хочу знать позицию вдоль кривой во время t = 3. Если я постоянная скорость:

float sigma(float t) 
{ 
    return 1f; 
} 

и следующие данные:

V0 = 1; 
V1 = 1; 
t0 = 0; 
L = 10; 

Я могу аналитически рассчитать положение:

px = v0 * t = 1 * 3 = 3 

Если я решаю то же самое уравнение, используя мой Безье сплайн и выше приведенный выше алгоритм с n =5. Я получаю:

px = 3.002595; 

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

Теперь, если я попытаюсь определить сигму как функцию равномерного ускорения, я получаю плохие результаты. Рассмотрим следующие данные:

V0 = 1; 
V1 = 2; 
t0 = 0; 
L = 10; 

можно вычислить время частица достигнет Р1 с использованием линейных уравнений движения:

L = 0.5 * (V0 + V1) * t1 => 
t1 = 2 * L/(V1 + V0) = 2 * 10/3 = 6.6666666 

Имея t можно рассчитать ускорение:

a = (V1 - V0)/(t1 - t0) = (2 - 1)/6.6666666 = 0.15 

У меня есть все данные для определения моей сигма-функции:

float sigma (float t) 
{ 
    float speed = V0 + a * t; 
} 

Если я решить эту проблему аналитически я ожидаю следующую скорость частицы после времени t =3:

Vx = V0 + a * t = 1 + 0.15 * 3 = 1.45 

и позиция будет:

px = 0.5 * (V0 + Vx) * t = 0.5 * (1 + 1.45) * 3 = 3.675 

Но если рассчитать его с alorithm выше, результаты позиции:

px = 4.358587 

, что совсем другое fr о, чего я ожидаю.

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

У вас есть предложения? Что мне не хватает? Кто-нибудь может сказать мне, что я делаю неправильно?


EDIT: Я пытаюсь с кривой 3D Безье. Определяется следующим образом:

public Vector3 Bezier(float t) 
{ 
    float a = 1f - t; 
    float a_2 = a * a; 
    float a_3 = a_2 *a; 

    float t_2 = t * t; 

    Vector3 point = (P0 * a_3) + (3f * a_2 * t * T0) + (3f * a * t_2 * T1) + t_2 * t * P1 ; 

    return point; 
} 

и производное:

public Vector3 Derivative(float t) 
{ 
    float a = 1f - t; 
    float a_2 = a * a; 
    float t_2 = t * t; 
    float t6 = 6f*t; 

    Vector3 der = -3f * a_2 * P0 + 3f * a_2 * T0 - t6 * a * T0 - 3f* t_2 * T1 + t6 * a * T1 + 3f * t_2 * P1; 

    return der; 
} 
+0

и что алгоритм дает вам для t = 6.6666 ...? Это значение 10, то есть L, или другое? – lmsteffan

ответ

1

Я думаю, что n=5 просто не дает достаточной точности для задачи под рукой. Тот факт, что это нормально для случая с постоянной скоростью, не означает, что это также относится к случаю с постоянным ускорением. К сожалению, вам придется определить себе компромисс, который дает значение для n, которое соответствует вашим потребностям и ресурсам.

Во всяком случае, если вы действительно имеете постоянную скорость параметризацию X (и (т)), которая работает с достаточной точностью, то вы можете переименовать этот параметр «время» t «космический» (расстояние) Параметр s, так что у вас действительно есть X (ы), и вам нужно только подключить s (t), который вам нужен: X (s (t)). В вашем случае (постоянное ускорение) s (t) = s + u t + a t /2, где u и a легко определяются из ваших входных данных.

1

Я думаю, что у вас просто есть опечатка где-то в функциях, не показанных в вашей реализации, Y и DY. Я попробовал одномерную кривую с P0 = 0, T0 = 1, T1 = 9, P1 = 10 и получил 3.6963165 с n = 5, что улучшилось до 3.675044 при n = 30 и 3.6750002 с n = 100.

Если ваша реализация двумерна, попробуйте с P0 = (0, 0), T0 = (1, 0), T1 = (9, 0) и P1 = (10, 0). Затем повторите попытку с P0 = (0, 0), T0 = (0, 1), T1 = (0, 9) и P1 = (0, 10).

Если вы используете C, помните, что оператор^НЕ означает экспоненту. Вы должны использовать pow (u, 3) или u * u * u, чтобы получить куб u.

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

i=1 
    h=0.6 
    t=0.0 
    u=0.0 
    LengthDY(u)=3.0 
    sigma(t)=1.0 
    k1=0.2 
    sigma(t+h/2)=1.045 
    LengthDY(u+k1/2)=6.78 
    k2=0.09247787 
    LengthDY(u+k2/2)=4.8522377 
    k3=0.12921873 
    sigma(t+h)=1.09 
    LengthDY(u+k3)=7.7258916 
    k4=0.08465043 
    t_new=0.6 
    u_new=0.12134061 
i=2 
    h=0.6 
    t=0.6 
    u=0.12134061 
    LengthDY(u)=7.4779167 
    sigma(t)=1.09 
    k1=0.08745752 
    sigma(t+h/2)=1.135 
    LengthDY(u+k1/2)=8.788503 
    k2=0.0774876 
    LengthDY(u+k2/2)=8.64721 
    k3=0.078753725 
    sigma(t+h)=1.1800001 
    LengthDY(u+k3)=9.722377 
    k4=0.07282171 
    t_new=1.2 
    u_new=0.20013426 
i=3 
    h=0.6 
    t=1.2 
    u=0.20013426 
    LengthDY(u)=9.723383 
    sigma(t)=1.1800001 
    k1=0.072814174 
    sigma(t+h/2)=1.225 
    LengthDY(u+k1/2)=10.584761 
    k2=0.069439456 
    LengthDY(u+k2/2)=10.547299 
    k3=0.069686085 
    sigma(t+h)=1.27 
    LengthDY(u+k3)=11.274727 
    k4=0.06758479 
    t_new=1.8000001 
    u_new=0.26990926 
i=4 
    h=0.6 
    t=1.8000001 
    u=0.26990926 
    LengthDY(u)=11.276448 
    sigma(t)=1.27 
    k1=0.06757447 
    sigma(t+h/2)=1.315 
    LengthDY(u+k1/2)=11.881528 
    k2=0.06640561 
    LengthDY(u+k2/2)=11.871877 
    k3=0.066459596 
    sigma(t+h)=1.36 
    LengthDY(u+k3)=12.375444 
    k4=0.06593703 
    t_new=2.4 
    u_new=0.3364496 
i=5 
    h=0.6 
    t=2.4 
    u=0.3364496 
    LengthDY(u)=12.376553 
    sigma(t)=1.36 
    k1=0.06593113 
    sigma(t+h/2)=1.405 
    LengthDY(u+k1/2)=12.7838 
    k2=0.06594283 
    LengthDY(u+k2/2)=12.783864 
    k3=0.0659425 
    sigma(t+h)=1.45 
    LengthDY(u+k3)=13.0998535 
    k4=0.06641296 
    t_new=3.0 
    u_new=0.4024687 

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

+0

спасибо за данные. Я постараюсь сделать еще несколько экспериментов. На самом деле я использую кривые Безье. Я отредактирую сообщение с кодом. – Heisenbug

Смежные вопросы