设备方向使用四元数

9
我已经编写了一个JS SDK,监听移动设备的旋转,并提供3个输入:
α:角度范围在0到360度之间
β:角度范围在-180到180度之间
γ:角度范围在-90到90度之间。
关于设备旋转的文档请参考此处
我尝试使用欧拉角确定设备的方向,但遇到了万向节锁定效应,当设备朝上时,计算会失控。这促使我使用四元数,它不受万向锁定效应的影响。
我发现这个js库可以将α、β和γ转换为四元数,因此对于以下值:
α:81.7324
β:74.8036
γ:-84.3221
我得到了ZXY 顺序的四元数:
w : 0.7120695154301472
x : 0.6893688637611577
y : -0.10864439143062626
z : 0.07696733776346154
代码:
var rad = Math.PI / 180;
window.addEventListener("deviceorientation", function(ev) {

  // Update the rotation object
  var q = Quaternion.fromEuler(ev.alpha * rad, ev.beta * rad, ev.gamma * rad, 'ZXY');

  // Set the CSS style to the element you want to rotate
  elm.style.transform = "matrix3d(" + q.conjugate().toMatrix4() + ")";

}, true);

使用从四元数反射正确设备方向得出的4D CSS矩阵来可视化设备方向(演示,使用移动设备):
这里输入图片描述 使用欧拉角和开发者工具进行错误可视化(演示,使用移动设备):
这里输入图片描述 我想编写一个方法,输入α、β和γ,并输出设备是否处于以下任一方向之一:
- 竖屏 - 倒立竖屏 - 左横屏 - 右横屏 - 屏幕朝上 - 屏幕朝下
将每个方向定义为围绕相关轴的±45°范围。
我应该采取什么方法?
1个回答

4
假设您已经成功将欧拉角转换为单位四元数,下面是一种简单的方法来确定设备的方向:
1. 取一个指向正上方(即沿着 +z 轴)的世界空间向量,并使用四元数(或其共轭)将其旋转到设备坐标系中。(请注意,您也可以直接使用欧拉角、旋转矩阵或任何其他表示设备旋转的方式来对向量进行变换。)
2. 取得变换后的向量,并找到绝对值最大的分量。这将告诉您设备的哪个轴最靠近垂直,分量值的符号告诉您它是指向上还是向下。
具体而言:
- 如果设备的 x 轴最垂直,则设备处于横屏模式; - 如果设备的 y 轴最垂直,则设备处于竖屏模式; - 如果设备的 z 轴最垂直,则设备的屏幕朝上或朝下。
这是一个简单的JS演示,至少在Chrome上应该可以工作 - 但设备方向API似乎在Stack Snippets中根本无法工作。 :( 要查看实时演示,请尝试此CodePen

const orientations = [
  ['landscape left', 'landscape right'],  // device x axis points up/down
  ['portrait', 'portrait upside down'],   // device y axis points up/down
  ['display up', 'display down'],         // device z axis points up/down
];

const rad = Math.PI / 180;

function onOrientationChange (ev) {
  const q = Quaternion.fromEuler(ev.alpha * rad, ev.beta * rad, ev.gamma * rad, 'ZXY');

  // transform an upward-pointing vector to device coordinates
  const vec = q.conjugate().rotateVector([0, 0, 1]);

  // find the axis with the largest absolute value
  const [value, axis] = vec.reduce((acc, cur, idx) => (Math.abs(cur) < Math.abs(acc[0]) ? acc : [cur, idx]), [0, 0]);

  const orientation = orientations[axis][1 * (value < 0)];

  document.querySelector('#angles').textContent = `alpha = ${ev.alpha.toFixed(1)}°, beta = ${ev.beta.toFixed(1)}°, gamma = ${ev.gamma.toFixed(1)}°`;
  document.querySelector('#vec').textContent = `vec = ${vec.map(a => a.toFixed(3))}, dominant axis = ${axis}, value = ${value.toFixed(3)}`;
  document.querySelector('#orientation').textContent = `orientation = ${orientation}`;
}

onOrientationChange({ alpha: 0, beta: 0, gamma: 0 });
window.addEventListener("deviceorientation", onOrientationChange, true);
<script src="https://cdn.jsdelivr.net/npm/quaternion@1.1.0/quaternion.min.js"></script>
<div id="angles"></div>
<div id="vec"></div>
<div id="orientation"></div>

请注意,设备定向API提供的欧拉角度数的符号和范围在不同浏览器之间存在明显的不一致性,可能会导致其他浏览器计算错误的符号。您可能需要进行一些浏览器嗅探来解决此问题,或使用类似gyronorm.js的封装库。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接