如何在Android中使用加速度计测量手机在XY平面上的倾斜度
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11175599/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to measure the tilt of the phone in XY plane using accelerometer in Android
提问by udiboy1209
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.
我尝试使用 SensorEvent.values 中的 Z 轴数据,但它没有检测到我的手机在 XY 平面中的旋转,即。绕Z轴。
I am using this as a reference for the co-ordinate axes. Is it correct?
我将其用作坐标轴的参考。这是正确的吗?
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.
PS:我的手机方向是横向。
回答by Hoan Nguyen
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.
本质上,这里有两种情况:设备平放和不平放。这里的平面是指设备屏幕表面与世界xy平面的夹角(我称之为倾角)小于25度或大于155度。想想手机平放或从桌子上稍微倾斜一点。
First you need to normalize the accelerometer vector.
That is if gis the vector returns by the accelerometer sensor event values. In code
首先,您需要对加速度计矢量进行归一化。
也就是说,如果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
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.
现在旋转 = 0 表示设备处于正常位置。对于大多数手机来说,这是没有任何倾斜的肖像,对于平板电脑来说可能是风景。因此,如果您如上图所示握住手机并开始旋转,旋转将发生变化,当手机处于横向时,旋转将是 90 度或 -90 度,具体取决于旋转方向。
回答by Dan
The accelerometer is sufficient for checking if the phone is flat as Hoan very nicely demonstrated.
加速度计足以检查手机是否平坦,正如 Hoan 很好地展示的那样。
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
然后要检查手机是否平整,您可以简单地将tilt
和pitch
值与容差值进行比较。例如
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.
值得注意的是,应用程序的方向不会影响俯仰、倾斜和方位角的值。
回答by Dayan
Working off of the perfect response from @Dan
从@Dan 的完美回应中解脱出来
He missed a very slight bit of information that @davy307 pointed out.
他错过了@davy307 指出的一点点信息。
When initializing the mAccelerometer, you must define it as Sensor.TYPE_ROTATION_VECTORotherwise, it will not have the 3rd rotation vector and throw an ArrayIndexOutOfBounds exception.
初始化 mAccelerometer 时,必须将其定义为Sensor.TYPE_ROTATION_VECTOR否则,它将没有第三个旋转向量并抛出 ArrayIndexOutOfBounds 异常。
mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
Otherwise, this is a perfect solution... Appreciated!
否则,这是一个完美的解决方案......赞赏!