【问题标题】:Device orientation using Quaternion使用四元数的设备方向
【发布时间】:2019-11-08 04:48:11
【问题描述】:

我编写了一个监听移动设备旋转的 JS SDK,提供 3 个输入:

α : 角度可以在 0 到 360 度之间
β : -180 到 180 度之间的角度
γ : -90 到 90 度之间的角度

Documentation for device rotation

我曾尝试使用欧拉角来确定设备方向,但遇到了gimbal lock effect,当设备指向上方时计算会爆炸。这导致我使用Quaternion,它不受云台锁定效果的影响。

我发现 this js library 将 α、β 和 γ 转换为四元数,因此对于以下值:

α : 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 矩阵可视化设备方向反映了正确的设备方向 (DEMO, use mobile):


错误使用 Euler Angels 和开发人员工具 (DEMO, use mobile) 进行可视化:


我想编写一个方法来获取 α、β 和 γ 并在设备处于以下方向之一时输出:

  • 肖像
  • 倒立的肖像
  • 横向左侧
  • 横向右侧
  • 显示出来
  • 向下显示

将每个方向定义为围绕相关轴的 +- 45° 范围。

我应该采取什么方法?

【问题讨论】:

    标签: javascript android ios mobile sensors


    【解决方案1】:

    鉴于您已经设法将欧拉角转换为单位四元数,这里有一个简单的方法来确定设备的方向:

    1. 获取一个指向正上方的世界空间向量(即沿 +z 轴)并使用四元数(或其共轭)将其旋转到设备坐标中。 (请注意,您也可以直接使用欧拉角,或使用旋转矩阵,或使用可应用于变换矢量的设备旋转的任何其他表示来执行此操作。)

    2. 取变换后的向量,找出绝对值最大的分量。这将告诉您设备的哪个轴最接近垂直方向,组件值的符号会告诉您它是向上还是向下。

    特别是:

    • 如果设备 x 轴最垂直,则设备处于横向;
    • 如果设备y轴最垂直,则设备处于纵向;
    • 如果设备 z 轴最垂直,则设备的屏幕朝上或朝下。

    这是一个简单的 JS 演示,它至少应该可以在 Chrome 上运行——或者它应该可以运行,只是设备方向 API 似乎根本无法在 Stack Snippets 中运行。 :( 对于现场演示,请尝试this CodePen instead

    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>

    请注意apparently 设备方向 API 提供的欧拉角的符号和范围在浏览器之间存在一些不一致,可能导致在其他浏览器上计算错误的符号。你可能需要做一些浏览器嗅探来解决这个问题,或者使用像 gyronorm.js 这样的包装库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-26
      • 2012-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-24
      相关资源
      最近更新 更多