负加速度恒加速度运动
Constant acceleration movement with minus acceleration
我在Unity上制作运动过程。
我想做一个将object移动到指定位置的过程,但是如题所示,我希望object不仅移动,而且到达预定位置速度衰减的距离。
如果加速度是负数,我处理不好。
具体来说,我想在初始速度为10时到达一个位置而不回头,如图gif所示。
我用恒加速度运动公式中的“s=v0t+1/2at^2”求出加速度“a”,但这似乎还不够。
如果你能帮助我,我将不胜感激。
public class Test : MonoBehaviour
{
public float t;
public float initSpd;
public Transform t1, t2;
IEnumerator Start()
{
var p = t1.position;
while (true) {
t1.position = p;
var v0t = initSpd * t;
var distance = Vector2.Distance(t1.position, t2.position);
var direction = (t2.position - t1.position).normalized;
var a = (2 * (distance - v0t)) / (t * t);
var v = initSpd;
// update
yield return Utils.Coroutine.WhileForSeconds(t, () =>
{
t1.Translate(direction * v * Time.deltaTime);
v += a * Time.deltaTime;
});
}
}
}
跨度=3,初始速度=0
跨度=3,初始速度=3
跨度=3,初始速度=10
您计算 a
的数学是正确的,但您提出问题的方式无法令您满意。
正如你所说,在加速度恒定的情况下,位置是时间的二次函数,因此描述为
s = b t^2 + c t + d
对于一些常量 b, c, d
。 d
的值已经被初始位置固定。 c
的值已经被初始速度固定。只剩下一个自由参数,当你求解s(finalTime) = goalPosition
时,你无法控制得到的抛物线是否超过目标。
可以增加多项式的次数,但总会有一些初速度过大导致超调。
本质上,你有一个最优控制/轨迹优化问题,类似于
minimize: integral(acceleration(t)^2) from t = 0 to T
subject to: x(0) given
v(0) given
x(T) given
x(t) <= x(T) for all t in [0, T] (assuming x(0) < x(T))
正如你所说的问题,没有优化 objective,但你需要一个成本或对加速度的约束,否则你可以获得无限加速度的解决方案(例如,在第一个真的很难加速时间步长,然后以恒定速度滑行到目标。
不平等使事情变得复杂。如果你想要一个连续时间的解析解,那么 Pontryagin 的原则将是解决它的一把锤子,但可能有更简单的技巧。如果将时间离散化并让加速度分段恒定,那么它就是一个简单的凸优化问题。
如果您愿意放宽“永不超调”和“恰好在此时到达那里”的限制,那么非常简单的解决方案将更好地扩展到外部力量等更复杂的场景,即使用反馈控制像PD控制器一样的规律:
a = kp * vectorToGoal - kd * velocityVector,
其中 kp
和 kd
是手动调整的正常数,并且该表达式在每一帧中都会重新计算。您可以调整 kp
和 kd
以最大限度地减少典型场景中的过冲。
或者,您可以像大多数游戏一样 - 允许速度瞬间改变:)
我找到了通过分两步改变速度实现的理想行为。
IEnumerator Start()
{
var p = t1.position;
while (true)
{
t1.position = p;
var direction = (t2.position - t1.position).normalized;
var distance = Vector2.Distance(t1.position, t2.position);
var v0 = initSpd;
var M = distance;
var T = duration;
var tm = M / v0;
var vm = v0 / T * tm;
var accel1 = (vm - v0) / (tm - 0);
var accel2 = (0 - vm) / (T - tm);
Debug.Log($"vo={v0}, M={M}, T={T}, tm={tm}, vm={vm}, accel1={accel1}, accel2={accel2}");
var v = initSpd;
var stime = Time.time;
var hist = 0f;
// update
yield return Utils.Coroutine.WhileForSeconds(T, () =>
{
t1.Translate(direction * v * Time.deltaTime);
hist += v * Time.deltaTime;
if (Time.time - stime <= tm)
v += accel1 * Time.deltaTime;
else
v += accel2 * Time.deltaTime;
});
Debug.Log($"elapsed={Time.time - stime}, moved distance={hist}, v={v}");
}
}
我在Unity上制作运动过程。
我想做一个将object移动到指定位置的过程,但是如题所示,我希望object不仅移动,而且到达预定位置速度衰减的距离。
如果加速度是负数,我处理不好。 具体来说,我想在初始速度为10时到达一个位置而不回头,如图gif所示。
我用恒加速度运动公式中的“s=v0t+1/2at^2”求出加速度“a”,但这似乎还不够。
如果你能帮助我,我将不胜感激。
public class Test : MonoBehaviour
{
public float t;
public float initSpd;
public Transform t1, t2;
IEnumerator Start()
{
var p = t1.position;
while (true) {
t1.position = p;
var v0t = initSpd * t;
var distance = Vector2.Distance(t1.position, t2.position);
var direction = (t2.position - t1.position).normalized;
var a = (2 * (distance - v0t)) / (t * t);
var v = initSpd;
// update
yield return Utils.Coroutine.WhileForSeconds(t, () =>
{
t1.Translate(direction * v * Time.deltaTime);
v += a * Time.deltaTime;
});
}
}
}
跨度=3,初始速度=0
您计算 a
的数学是正确的,但您提出问题的方式无法令您满意。
正如你所说,在加速度恒定的情况下,位置是时间的二次函数,因此描述为
s = b t^2 + c t + d
对于一些常量 b, c, d
。 d
的值已经被初始位置固定。 c
的值已经被初始速度固定。只剩下一个自由参数,当你求解s(finalTime) = goalPosition
时,你无法控制得到的抛物线是否超过目标。
可以增加多项式的次数,但总会有一些初速度过大导致超调。
本质上,你有一个最优控制/轨迹优化问题,类似于
minimize: integral(acceleration(t)^2) from t = 0 to T
subject to: x(0) given
v(0) given
x(T) given
x(t) <= x(T) for all t in [0, T] (assuming x(0) < x(T))
正如你所说的问题,没有优化 objective,但你需要一个成本或对加速度的约束,否则你可以获得无限加速度的解决方案(例如,在第一个真的很难加速时间步长,然后以恒定速度滑行到目标。
不平等使事情变得复杂。如果你想要一个连续时间的解析解,那么 Pontryagin 的原则将是解决它的一把锤子,但可能有更简单的技巧。如果将时间离散化并让加速度分段恒定,那么它就是一个简单的凸优化问题。
如果您愿意放宽“永不超调”和“恰好在此时到达那里”的限制,那么非常简单的解决方案将更好地扩展到外部力量等更复杂的场景,即使用反馈控制像PD控制器一样的规律:
a = kp * vectorToGoal - kd * velocityVector,
其中 kp
和 kd
是手动调整的正常数,并且该表达式在每一帧中都会重新计算。您可以调整 kp
和 kd
以最大限度地减少典型场景中的过冲。
或者,您可以像大多数游戏一样 - 允许速度瞬间改变:)
我找到了通过分两步改变速度实现的理想行为。
IEnumerator Start()
{
var p = t1.position;
while (true)
{
t1.position = p;
var direction = (t2.position - t1.position).normalized;
var distance = Vector2.Distance(t1.position, t2.position);
var v0 = initSpd;
var M = distance;
var T = duration;
var tm = M / v0;
var vm = v0 / T * tm;
var accel1 = (vm - v0) / (tm - 0);
var accel2 = (0 - vm) / (T - tm);
Debug.Log($"vo={v0}, M={M}, T={T}, tm={tm}, vm={vm}, accel1={accel1}, accel2={accel2}");
var v = initSpd;
var stime = Time.time;
var hist = 0f;
// update
yield return Utils.Coroutine.WhileForSeconds(T, () =>
{
t1.Translate(direction * v * Time.deltaTime);
hist += v * Time.deltaTime;
if (Time.time - stime <= tm)
v += accel1 * Time.deltaTime;
else
v += accel2 * Time.deltaTime;
});
Debug.Log($"elapsed={Time.time - stime}, moved distance={hist}, v={v}");
}
}