How to measure the tilt of the phone in XY plane using accelerometer in Android

和自甴很熟 提交于 2019-11-26 02:31:28

问题


I tried to use the Z axis data from SensorEvent.values, but it doesn\'t detect rotation of my phone in the XY plane, ie. around the Z-axis.

I am using this as a reference for the co-ordinate axes. Is it correct?

\"axes\"

How do I measure that motion using accelerometer values?

These games do something similar: Extreme Skater, Doodle Jump.

PS: my phone orientation will be landscape.


回答1:


Essentially, there is 2 cases here: the device is laying flat and not flat. Flat here means the angle between the surface of the device screen and the world xy plane (I call it the inclination) is less than 25 degree or larger than 155 degree. Think of the phone lying flat or tilt up just a little bit from a table.

First you need to normalize the accelerometer vector.
That is if g is the vector returns by the accelerometer sensor event values. In code

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

Then the inclination can be calculated as

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

Thus

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

For the case of laying flat, you have to use a compass to see how much the device is rotating from the starting position.

For the case of not flat, the rotation (tilt) is calculated as follow

int rotation = (int) Math.round(Math.toDegrees(Math.atan2(g[0], g[1])));

Now rotation = 0 means the device is in normal position. That is portrait without any tilt for most phone and probably landscape for tablet. So if you hold a phone as in your picture above and start rotating, the rotation will change and when the phone is in landscape the rotation will be 90 or -90 depends on the direction of rotation.




回答2:


The accelerometer is sufficient for checking if the phone is flat as Hoan very nicely demonstrated.

For anyone who arrives here looking to not only check if the phone flat, but what the rotation of the phone is, it can be achieved through the Rotation Vector Motion Sensor.

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;
}

Then to check if the phone is flat you can simply compare the tilt and pitch values with a tolerance values. For example

public boolean flatEnough(double degreeTolerance) {
    return tilt <= degreeTolerance && tilt >= -degreeTolerance && pitch <= degreeTolerance && pitch >= -degreeTolerance;
}

The advantage to doing it this way is you can check if the phone is being held in any specific rotation.

It is worth noting that the app's orientation will not affect the values of pitch, tilt, and azimuth.




回答3:


Working off of the perfect response from @Dan

He missed a very slight bit of information that @davy307 pointed out.

When initializing the mAccelerometer, you must define it as Sensor.TYPE_ROTATION_VECTOR otherwise, it will not have the 3rd rotation vector and throw an ArrayIndexOutOfBounds exception.

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

Otherwise, this is a perfect solution... Appreciated!



来源:https://stackoverflow.com/questions/11175599/how-to-measure-the-tilt-of-the-phone-in-xy-plane-using-accelerometer-in-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!