我正在使用2自由度(俯仰和转动)的运动模拟器。我正在从游戏中读取变换矩阵,我需要获得角度并发送到硬件来驱动电机。 由于欧拉角有奇点,我不能真正使用它们。它的行为如下:
什么时候应该这样:
我准备了在线示例以更好地展示问题:
// Get euler angles from model matrix
var mat = model.matrix;
mat.transpose();
var e = new THREE.Euler();
e.setFromRotationMatrix(mat, 'XZY');
var v = e.toVector3();
var pitch = -v.z;
var roll = -v.x;
http://jsfiddle.net/qajro0ny/3/
据我了解,这里有两个问题。
我已经阅读了关于万向节锁,甚至实现了euler过滤器,但这没有按预期工作。 关于万向节锁的大多数建议是使用四元数,但我不能用四元数驱动物理运动(或者我可以吗?)。
轴顺序在这里并不重要,因为改变它只会将奇点从一个轴移动到另一个轴。
我需要以其他方式处理这个问题。
我尝试用矩阵乘以轴向量,然后使用十字和点积来获得角度,但这也失败了。我认为还应该进行轴重投,以实现这一目标,但我无法弄明白。但有些事情告诉我,这是正确的方法。它是这样的:http://jsfiddle.net/qajro0ny/53/
然后我提出了不同的想法。我知道以前的位置,所以可能会做以下事情:
所以我试过了......它奏效了!在任何方向上都没有奇点,在俯仰,滚转和偏航方面完美的360度旋转。完美的解决方案!除了......它不是。帧没有同步,所以经过一段时间的角度远离他们应该是什么。我一直在考虑某种同步机制,但我认为这不是正确的方法。
看起来像这样:http://jsfiddle.net/qajro0ny/52/
同样的逻辑,但直接与矩阵:http://jsfiddle.net/qajro0ny/54/
我搜索了网页的高低,我已经阅读了几十篇论文和其他问题/帖子,我简直无法相信没有什么能真正适用于我的案例。
我可能不理解或遗漏某些东西,所以这里是我发现和尝试的一切:
链接:http://pastebin.com/3G0dYLvu
代码:http://pastebin.com/PiZKwE2t(我把它们放在一起所以它很混乱)
我一定是错过了什么,或者我是从错误的角度看这个。
答案 0 :(得分:7)
如果您知道矩阵只包含两个旋转(按给定顺序T = RZ * RX
),那么您可以执行以下操作:
局部x轴不受第二次旋转的影响。因此,您只能使用局部x轴计算第一个角度。然后,您可以从矩阵中移除此旋转,并从其他两个轴中的任何一个计算剩余角度:
function calculateAngles() {
var mat = model.matrix;
//calculates the angle from the local x-axis
var pitch = Math.atan2(mat.elements[1], mat.elements[0]);
//this matrix is used to remove the first rotation
var invPitch = new THREE.Matrix4();
invPitch.makeRotationZ(-pitch);
//this matrix will only contain the roll rotation
// rollRot = RZ^-1 * T
// = RZ^-1 * RZ * RX
// = RX
var rollRot = new THREE.Matrix4();
rollRot.multiplyMatrices(invPitch, mat);
//calculate the remaining angle from the local y-axis
var roll = Math.atan2(rollRot.elements[6], rollRot.elements[5]);
updateSimAngles(pitch, roll);
}
这当然只有在给定矩阵符合要求时才有效。它不得包含第三个旋转。否则,您可能需要找到非线性最小二乘解。
答案 1 :(得分:2)
关于使用旋转增量的想法实际上听起来很有希望。
我不太清楚你对“帧没有同步”的意思。我可以想象你使用四元数的计算可能不是100%精确(你使用浮点吗?)这会导致小的错误堆积起来并最终导致异步移动。
关键是你应该使用单位四元数来表示旋转。您可以在理论模型中执行此操作,但如果您使用4个浮点数表示四元数,则在大多数情况下,四元数将不是单位四元数,但只是非常接近(对于某些小的,它们的范数为1+e
- 可能是负面的 - 价值e
)。这是可以的,因为你不会注意到这些小的差异,但是如果你在你的四元数上投入大量的操作(你通过不断地模拟你的模型并计算增量),那么这些小的错误就会堆积起来。因此,您需要不断重新规范化四元数以使它们尽可能接近单位四元数,以便您的计算 - 尤其是转换回欧拉角 - 保持(几乎)精确。
答案 2 :(得分:2)
我在http://jsfiddle.net/qajro0ny/58/加了我的两分钱,基本上应用了几年前我偶然发现的http://blogs.msdn.com/b/mikepelton/archive/2004/10/29/249501.aspx的作品。它基本归结为
var a = THREE.Math.clamp(-mat.elements[6], -1, 1);
var xPitch = Math.asin(a);
var yYaw = 0;
var zRoll = 0;
if (Math.cos(xPitch) > 0.0001)
{
zRoll = -Math.atan2(mat.elements[4], mat.elements[5]);
yYaw = -Math.atan2(mat.elements[2], mat.elements[10]);
}
else
{
zRoll = -Math.atan2(-mat.elements[1], mat.elements[0]);
}
我注意到,在DirectX左手坐标系中使用的假设映射偏航,俯仰,滚动到轴(y,x,z)与使用OpenGL / WebGL时的不同,所以也许这个需要是终于整理出来了。
我希望这会有所帮助。
答案 3 :(得分:1)
我想你实际上可以用四元数驱动物理马达。请记住,四元数表示旋转轴(x,y,z)和角度。 我想你有三个电机(每个轴),旋转速度可以缩放。现在,通过缩放这三个电机的转速,您可以设置特定的旋转轴,并通过仔细测量时间,您可以设置特定的旋转角度。它应该比转换为欧拉角度增量更容易。