如何在Android中使用加速度计测量手机在XY平面上的倾斜

如何在Android中使用加速度计测量手机在XY平面上的倾斜,android,accelerometer,plane,tilt,Android,Accelerometer,Plane,Tilt,我尝试使用SensorEvent.values中的Z轴数据,但它没有检测到我的手机在XY平面上的旋转,即绕Z轴旋转 我使用它作为坐标轴的参考。对吗 如何使用加速计值测量该运动 这些游戏做一些类似的事情:,涂鸦跳跃 PS:我的手机方向是横向的。基本上,这里有两种情况:设备是平放的,而不是平放的。这里的平面表示设备屏幕表面与世界xy平面之间的角度(我称之为倾斜度)小于25度或大于155度。想象一下,手机平放或从桌子上稍稍向上倾斜 首先,您需要规范化加速度计矢量。 也就是说,如果g是加速度计传感器事

我尝试使用SensorEvent.values中的Z轴数据,但它没有检测到我的手机在XY平面上的旋转,即绕Z轴旋转

我使用它作为坐标轴的参考。对吗

如何使用加速计值测量该运动

这些游戏做一些类似的事情:,涂鸦跳跃


PS:我的手机方向是横向的。

基本上,这里有两种情况:设备是平放的,而不是平放的。这里的平面表示设备屏幕表面与世界xy平面之间的角度(我称之为倾斜度)小于25度或大于155度。想象一下,手机平放或从桌子上稍稍向上倾斜

首先,您需要规范化加速度计矢量。
也就是说,如果g是加速度计传感器事件值返回的向量。编码

float[] g = new float[3]; 
g = event.values.clone();

double norm_Of_g = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);

// Normalize the accelerometer vector
g[0] = g[0] / norm_Of_g
g[1] = g[1] / norm_Of_g
g[2] = g[2] / norm_Of_g
那么倾斜度可以计算为

int inclination = (int) Math.round(Math.toDegrees(Math.acos(g[2])));
因此

if (inclination < 25 || inclination > 155)
{
    // device is flat
}
else
{
    // device is not flat
}

现在旋转=0表示设备处于正常位置。对于大多数手机来说,这是一幅没有任何倾斜的肖像画,对于平板电脑来说,这可能是一幅风景画。因此,如果您如上图所示握住手机并开始旋转,则旋转将发生变化,当手机处于横向时,旋转将为90或-90,具体取决于旋转方向。

加速计足以检查手机是否如Hoan所演示的那样平坦

对于来到这里的任何人来说,不仅要检查手机是否平坦,还要检查手机的旋转情况,这可以通过以下方式实现

这样做的好处是,您可以检查手机是否以任何特定的旋转方式握住


值得注意的是,应用程序的方向不会影响俯仰、倾斜和方位角的值。

利用@Dan的完美响应

他遗漏了@davy307指出的一点非常微小的信息

初始化mAccelerometer时,必须将其定义为传感器。键入旋转向量,否则,它将没有第三个旋转向量,并引发ArrayIndexOutOfBounds异常

mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

否则,这是一个完美的解决方案。。。谢谢

太棒了!通常人们建议使用Sensor.TYPE_MAGNETIC_FIELD来完成这项任务,但是您简化了解决方案;顺便说一下,加速计是安卓系统上唯一保证存在的传感器。我能问一下“acc向量的标准化”背后的数学原理吗?为什么要使用atan(g[1]/g[0])(或atan(y/x))来获取度数@霍恩Nguyen@AndreaBaccega我忘记了为什么我需要正常化,或者根本没有必要,只是我需要为其他事情做一些事情。atan(y/x)只是简单的三角计算。要查找旋转,首先需要将重力投影到xy平面。现在,如果没有旋转,这个投影向量将有坐标(0,1)(假设标准化)。如果设备旋转,该向量相同,但坐标发生变化,且该向量与设备y坐标之间的角度仅为tan(y/x)。@AndreaBaccega据我所知,需要标准化将-1到1范围内的值降低,这是
Math.acos()的可接受范围,对于超出此范围的数字,返回NAN。如果有人感兴趣,这里有一些链接:有人能告诉我们,当“设备处于平放位置”时,如何找到角度吗?我想使用“类型磁场”吗?“磁场类型”在棒棒糖(moto e)中不起作用。怎么做?为什么倾斜在-90到90之间?如果我需要查找/tilt vs\tilt呢?当我试图编译它时,它失败了,在g[3]上有一个ArrayIndexOutOfBounds,因为长度是3。。。我提出的解决方案是将90(垂直向上)到-90(垂直向下)的值设为0,这意味着手机处于水平位置,这将取代
sinT=(g[1]-g[2]*g[0])的
sinT
方程。toDouble()
private double pitch, tilt, azimuth;

@Override
public void onSensorChanged(SensorEvent event) {
    //Get Rotation Vector Sensor Values
    double[] g = convertFloatsToDoubles(event.values.clone());

    //Normalise
    double norm = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2] + g[3] * g[3]);
    g[0] /= norm;
    g[1] /= norm;
    g[2] /= norm;
    g[3] /= norm;

    //Set values to commonly known quaternion letter representatives
    double x = g[0];
    double y = g[1];
    double z = g[2];
    double w = g[3];

    //Calculate Pitch in degrees (-180 to 180)
    double sinP = 2.0 * (w * x + y * z);
    double cosP = 1.0 - 2.0 * (x * x + y * y);
    pitch = Math.atan2(sinP, cosP) * (180 / Math.PI);

    //Calculate Tilt in degrees (-90 to 90)
    double sinT = 2.0 * (w * y - z * x);
    if (Math.abs(sinT) >= 1)
        tilt = Math.copySign(Math.PI / 2, sinT) * (180 / Math.PI);
    else
        tilt = Math.asin(sinT) * (180 / Math.PI);

    //Calculate Azimuth in degrees (0 to 360; 0 = North, 90 = East, 180 = South, 270 = West)
    double sinA = 2.0 * (w * z + x * y);
    double cosA = 1.0 - 2.0 * (y * y + z * z);
    azimuth = Math.atan2(sinA, cosA) * (180 / Math.PI);
}

private double[] convertFloatsToDoubles(float[] input)
{
    if (input == null)
        return null;

    double[] output = new double[input.length];

    for (int i = 0; i < input.length; i++)
        output[i] = input[i];

    return output;
}
public boolean flatEnough(double degreeTolerance) {
    return tilt <= degreeTolerance && tilt >= -degreeTolerance && pitch <= degreeTolerance && pitch >= -degreeTolerance;
}
mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);