【问题标题】:Get quaternion from Android gyroscope?从 Android 陀螺仪获取四元数?
【发布时间】:2013-09-03 08:15:10
【问题描述】:

The official development documentation 建议通过以下方式从 3D 旋转速率向量 (wx, wy, wz) 中获取四元数。

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
  // This timestep's delta rotation to be multiplied by the current rotation
  // after computing it from the gyro sample data.
  if (timestamp != 0) {
    final float dT = (event.timestamp - timestamp) * NS2S;
    // Axis of the rotation sample, not normalized yet.
    float axisX = event.values[0];
    float axisY = event.values[1];
    float axisZ = event.values[2];

    // Calculate the angular speed of the sample
    float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

    // Normalize the rotation vector if it's big enough to get the axis
    // (that is, EPSILON should represent your maximum allowable margin of error)
    if (omegaMagnitude > EPSILON) {
      axisX /= omegaMagnitude;
      axisY /= omegaMagnitude;
      axisZ /= omegaMagnitude;
    }

    // Integrate around this axis with the angular speed by the timestep
    // in order to get a delta rotation from this sample over the timestep
    // We will convert this axis-angle representation of the delta rotation
    // into a quaternion before turning it into the rotation matrix.
    float thetaOverTwo = omegaMagnitude * dT / 2.0f;
    float sinThetaOverTwo = sin(thetaOverTwo);
    float cosThetaOverTwo = cos(thetaOverTwo);
    deltaRotationVector[0] = sinThetaOverTwo * axisX;
    deltaRotationVector[1] = sinThetaOverTwo * axisY;
    deltaRotationVector[2] = sinThetaOverTwo * axisZ;
    deltaRotationVector[3] = cosThetaOverTwo;
  }
  timestamp = event.timestamp;
  float[] deltaRotationMatrix = new float[9];
  SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
   }
}

我的问题是:

这与加速度情况完全不同,其中使用加速度ALONG 3 轴计算合成加速度是有意义的。

我真的很困惑,为什么也可以用 AROUND 3 个轴的子旋转率来计算合成的旋转率。这对我来说没有意义。

为什么这种方法 - 找到 复合 旋转速率幅度 - 甚至可以工作?

【问题讨论】:

    标签: android quaternions gyroscope


    【解决方案1】:

    由于您的标题与您的问题不完全相符,因此我正在尝试尽可能多地回答。

    陀螺仪不提供绝对方向(如ROTATION_VECTOR),而只提供围绕它们构建为“旋转”的那些轴的旋转速度。这是由于gyroscope 的设计和构造。想象一下下面的结构。金色的东西正在旋转,由于laws of physics 它不想改变它的旋转。现在您可以旋转框架并测量这些旋转。

    现在,如果您想从陀螺仪获得“当前旋转状态”,您必须从初始旋转开始,称之为q0,并不断添加陀螺仪正在测量的那些微小的旋转差异它的轴:q1 = q0 + gyro0, q2 = q1 + gyro1, ...

    换句话说:陀螺仪为您提供了它围绕三个构造轴旋转的差异,因此您不是在组成绝对值,而是组成小的增量。

    现在这是非常笼统的,还有几个问题没有得到解答:

    1. 我从哪里获得初始位置?答:看看旋转矢量传感器 - 您可以使用从那里获得的四元数作为初始化
    2. 如何“求和”q 和陀螺仪?

    取决于当前的旋转表示:如果您使用旋转矩阵,则应按照 cmets 中的建议进行简单的矩阵乘法(请注意,此矩阵乘法实现效率不高!):

    /**
     * Performs naiv n^3 matrix multiplication and returns C = A * B
     * 
     * @param A Matrix in the array form (e.g. 3x3 => 9 values)
     * @param B Matrix in the array form (e.g. 3x3 => 9 values)
     * @return A * B
     */
    public float[] naivMatrixMultiply(float[] B, float[] A) {
        int mA, nA, mB, nB;
        mA = nA = (int) Math.sqrt(A.length);
        mB = nB = (int) Math.sqrt(B.length);
    
        if (nA != mB)
            throw new RuntimeException("Illegal matrix dimensions.");
    
        float[] C = new float[mA * nB];
        for (int i = 0; i < mA; i++)
            for (int j = 0; j < nB; j++)
                for (int k = 0; k < nA; k++)
                    C[i + nA * j] += (A[i + nA * k] * B[k + nB * j]);
        return C;
    }
    

    要使用这种方法,假设mRotationMatrix 持有当前状态,这两行代码就可以完成这项工作:

    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    mRotationMatrix = naivMatrixMultiply(mRotationMatrix, deltaRotationMatrix);
    // Apply rotation matrix in OpenGL
    gl.glMultMatrixf(mRotationMatrix, 0);
    

    如果您选择使用四元数,请再次想象mQuaternion 包含当前状态:

    // Perform Quaternion multiplication
    mQuaternion.multiplyByQuat(deltaRotationVector);
    // Apply Quaternion in OpenGL
    gl.glRotatef((float) (2.0f * Math.acos(mQuaternion.getW()) * 180.0f / Math.PI),mQuaternion.getX(),mQuaternion.getY(), mQuaternion.getZ());
    

    描述了四元数乘法here - equation (23)。确保正确应用乘法,因为它不可交换!

    如果您只想了解设备的旋转(我假设这是您最终想要的),我强烈推荐 ROTATION_VECTOR-Sensor。另一方面,陀螺仪在测量旋转速度方面非常精确,并且具有非常好的动态响应,但会受到漂移的影响,并且不会为您提供绝对方向(指向磁北或根据重力)。

    更新:如果您想查看完整示例,可以从https://bitbucket.org/apacha/sensor-fusion-demo 下载简单的demo-app 的源代码。

    【讨论】:

    • 云台陀螺仪的图片具有误导性,因为该陀螺仪确实可以测量方向。如果您沿云台轴匀速旋转,则对应的云台轴将以相同的速度旋转。如果你读出万向节角度,那将是欧拉角,受制于有时被称为“万向节锁定”的奇点,正是因为这张图片。手机中的 MEMS 陀螺仪看起来不像这样,它们确实可以测量角速度。
    • 如何在android中进行四元数乘法?
    • 给出了方程的链接。你也可以在bitbucket.org/apacha/sensor-fusion-demo/src/…查看我的实现
    【解决方案2】:

    对我来说很有意义。加速度传感器通常通过在对被测轴施加力时产生一些可测量的量变化来工作。例如。如果重力在测量该轴的传感器上向下拉,它会更好地导电。所以现在你可以知道重力或某个方向的加速度有多大的拉力。很简单。

    同时,陀螺仪是旋转的东西(好吧,或者像调整过的跳水板一样在一条直线上来回弹跳)。陀螺正在旋转,现在你在旋转,陀螺看起来会根据你旋转的方向旋转得更快或更慢。或者,如果您尝试移动它,它会抵抗并尝试继续前进。所以你只是从测量中得到一个旋转变化。然后你必须通过整合一段时间内的所有变化来找出变化的力量。

    通常这些东西都不是一个传感器。它们通常是 3 个不同的传感器,它们彼此垂直排列,并测量不同的轴。有时所有传感器都在同一个芯片上,但它们仍然是芯片上不同的东西,分别测量。

    【讨论】:

    • 感谢您抽空回答,但似乎没有回答我的问题...
    • 问题是你的问题是为什么陀螺仪不像线性力传感器那样工作。答案是它们不是一回事。陀螺仪旋转(或用水平力振动,远离作为 hack 测量的振动)。这就是为什么所有陀螺仪测量都基于旋转而不是沿轴的线性力。整个评论试图向您解释每件事是什么以及它们有何不同。为什么你认为你可以测量除了陀螺旋转以外的任何东西?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-16
    • 1970-01-01
    • 1970-01-01
    • 2014-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多