约束俯仰,横摆和横摇

问题描述 投票:1回答:2

我有一个表示为四元数的旋转,正在尝试限制俯仰,偏航和横滚轴。我尝试过这样做:

public struct Orientation
{
    public Vector3 up, forward;

    public Orientation(Vector3 up, Vector3 forward)
    {
        this.up = up;
        this.forward = forward;
    }
}

public static Orientation[] orientations = new Orientation[3]
{
    new Orientation(Vector3.right, Vector3.up),
    new Orientation(Vector3.up, Vector3.forward),
    new Orientation(Vector3.forward, Vector3.right)
};

public enum Axis
{
    Pitch,
    Yaw,
    Roll
};

private Vector3 ConstrainAxis(Vector3 vector, Axis axis, float from, float to)
{
    Orientation orientation = orientations[(int)axis];

    float theta = (to - from) * 0.5F;

    Vector3 cons = Quaternion.AngleAxis(from + theta, orientation.up) * orientation.forward;
    Vector3 proj = Vector3.ProjectOnPlane(vector, orientation.up);

    return ConstrainVector(cons.normalized, proj.normalized, theta);
}

private Vector3 ConstrainVector(Vector3 from, Vector3 to, float angle)
{
    float theta = Mathf.Abs(angle / Vector3.Angle(from, to));

    if(theta < 1.0F)
    {
        return Vector3.Slerp(from, to, theta);
    }

    return to;
}

这仅是限制欧拉角表示的各个分量的一种过于复杂的方式,而欧拉角表示的各个分量都受到一个奇怪的抖动问题(与万向锁定有关?)。

约束这些轴的最佳方法是什么?

vector 3d constraints quaternions
2个回答
2
投票

对于联合约束,通常的做法是使用“摆动扭曲”参数化。将电流旋转表示为四元数的“摆动扭曲”,它们是良好的分解http://www.alinenormoyle.com/weblog/?p=726

并且可以使用四元数来约束“摆动”和“扭转”。

如果我们想将摆动限制在+ -30度,伪代码看起来像

Quaternion swing;
const double maxMagnitude = sin( 0.5 * toRad(30) );
const double maxMagnitudeW = sqrt(1.0 - maxMagnitude*maxMagnitude );
if(swing.vec().normSqr() > maxMagnitude*maxMagnitude)
{
  swing.vec() = swing.vec().normalized() * maxMagnitude;
  swing.w() = maxMagnitudeW;
}

0
投票

添加到minorlogic的答案对于保存targetQuat的W分量的符号很重要。这是扭曲约束的three.js实现。似乎还有一些我没有检查过的奇点:http://www.allenchou.net/2018/05/game-math-swing-twist-interpolation-sterp/

const HEAD_YAW_MAX = 40
const MAX_MAGNITUDE = Math.sin(0.5 * THREE.Math.degToRad(HEAD_YAW_MAX));
const MAX_MAGNITUDE_W = Math.sqrt(1.0 - MAX_MAGNITUDE * MAX_MAGNITUDE);
const MAX_MAG_POW_2 = MAX_MAGNITUDE * MAX_MAGNITUDE;

in update function

const qT = this.headBone.quaternion;
v1.set(qT.x, qT.y, qT.z); //todo check singularity: rotation by 180
v1.projectOnVector(this.headBone.up); //up is direction around twist
// v1.set(0, qT.y, 0); //project on y axis
q1.set(v1.x, v1.y, v1.z, qT.w); //twist
q1.normalize();
q3.copy(q1).conjugate();
q2.multiplyQuaternions(qT, q3); //swing
q2.normalize();
v1.set(q1.x, q1.y, q1.z);
if (v1.lengthSq() > MAX_MAG_POW_2) {
   v1.setLength(MAX_MAGNITUDE);
   const sign = qT.w < 0 ? -1 : 1;
   q1.set(v1.x, v1.y, v1.z, sign * MAX_MAGNITUDE_W);
   this.headBone.quaternion.multiplyQuaternions(q2, q1); //swing * twist
}

摆动扭曲参数化算法的来源:Component of a quaternion rotation around an axis

© www.soinside.com 2019 - 2024. All rights reserved.