简体   繁体   English

如何检测安装有加速度计的Android设备的左右倾斜?

[英]How to detect left and right tilt of an android device mounted with an accelerometer?

Lets say you have the acceleration readings in all the 3 dimensions ie X, Y and Z. How do you infer using the readings the phone was tilted left or right? 假设您在所有3个维度(即X,Y和Z)中都有加速度读数。您如何使用读数来推断手机是向左还是向右倾斜? The readings get generated every 20ms. 读数每20ms生成一次。

I actually want the logic of inferring the tilt from the readings. 我实际上想要从读数推断出倾斜的逻辑。 The tilt needs to be smooth. 倾斜需要平稳。

A tilt can be detected in a sort of diferent ways. 可以通过某种不同的方式检测倾斜。 You can take into account 1 axis, 2 axis, or the 3 axis. 您可以考虑1轴,2轴或3轴。 Depending on how accurate you want it, and how much you feel like fighting with maths. 取决于你想要它的准确程度,以及你对数学的斗争感觉。

If you use only one axis, it is quite simple. 如果只使用一个轴,则非常简单。 Think the mobile is completely horizontal, and you move it like this: 认为移动设备是完全水平的,你可以像这样移动它:

在此输入图像描述

using just one axis, lets say, axis x, will be enough, since you can detect accurately a change in that axis position, since even any small movement will do a change in the axis. 使用一个轴,比如x轴,就足够了,因为你可以准确地检测到该轴位置的变化,因为即使任何小的运动都会对轴进行改变。 But, if your application is only reading that axis, and the user has the phone almost vertical, the difference in x axis will be really small even rotating the phone a big angle. 但是,如果您的应用程序仅读取该轴,并且用户使手机几乎垂直,那么即使将手机旋转大角度,x轴的差异也会非常小。 Anyways,for applications that only need coarse resolution, a single-axis can be used. 无论如何,对于仅需要粗分辨率的应用,可以使用单轴。

Referring to basic trigonometry, the projection of the gravity vector on the x-axis produces an output acceleration equal to the sine of the angle between the accelerometer x-axis and the horizon . 参考基本三角学, 重力矢量在x轴上的投影产生的输出加速度等于加速度计x轴和地平线之间的角度的正弦 This means that having the values of an axis (those are acceleration values) you can calculate the angle in which the device is. 这意味着具有轴的值(这些是加速度值),您可以计算设备的角度。

在此输入图像描述

this means that the value given to you by the sensor, is = to 9,8 * sine of the angle, so doing the maths you can get the actual angle. 这意味着传感器给你的值是=到角度的9,8 *正弦,所以做数学就可以得到实际的角度。

But don't worry, you don't even have to do this. 但不要担心,你甚至不必这样做。 Since the values are more or less proportional, as you can see in the table below, you can work directly with the value of the sensor, without taking much care of what angle represents, if you don't need it to be much accurate, since a change in that value means a proportional change in the angle, so with a few test, you will find out how big should be the change in order to be relevant to you. 由于这些值或多或少是成比例的,如下表所示,您可以直接使用传感器的值,而不必过多关注角度代表什么,如果您不需要它准确得多,因为该值的变化意味着角度的比例变化,所以通过一些测试,您将发现变化应该有多大才能与您相关。

在此输入图像描述

So, if you take the value over the time, and compare to each other, you can figure out how big the rotation was. 因此,如果你在一段时间内取值,并相互比较,你就可以计算出旋转的大小。 For this, 为了这,

  1. you consider just one axis. 你只考虑一个轴。 this will be axis X. 这将是X轴。
  2. write a function to get the difference in the sensor value for that axis between one function call, and the next 编写一个函数来获得一个函数调用和下一个函数调用之间该轴的传感器值的差异
  3. Decide a maximum time and a minimum sensor difference, that you will consider a valid movement (eg a big rotation is good but only if it is fast enough, and a fast movement is good only if the difference in the angle is big enough) 确定最大时间和最小传感器差异,您将考虑有效运动(例如,大旋转是好的,但只有当它足够快时,只有当角度的差异足够大时,快速运动才是好的)
  4. if you detect two measurements that accomplish those conditions, you take note of half tilt done (in a boolean for instance), and start measuring again, but now, the new reference value is the value that was considered half tilt. 如果您检测到两个完成这些条件的测量,您会注意到半倾斜(例如,在布尔值中),并再次开始测量,但现在,新参考值是被认为是半倾斜的值。
  5. if the last difference was positive, now you need a negative difference, and if the last difference was negative, now you need a positive difference; 如果最后一个差异为正,那么现在你需要一个负差异,如果最后一个差异是负数,那么现在你需要一个正差异; this is, coming back. 这就是回来。 so start taking values comparing the new reference value with the new values coming from the sensor, and see if one accomplish what you decided in point 3. 因此,开始将新参考值与来自传感器的新值进行比较,并查看是否完成了您在第3点中的决定。
  6. if you find a valid value (accomplishing value difference and time conditions ), you have a tilt. 如果找到有效值(完成值差和时间条件),则表示您有倾斜。 But if you dont get a good value and the time is consumed, you reset everything: let your reference value be the last one, reset the timers, reset the half-tilt-done boolean to false, and keep measuring. 但是如果你没有得到一个好的值并消耗时间,你就重置一切:让你的参考值成为最后一个,重置定时器,将half-tilt-done布尔值重置为false,并继续测量。

I hope this is good enough for you. 我希望这对你来说已经足够了。 For sure you can find some libraries or code snippets to help you out with this, but i think is good, as you say, to know the logic of inferring the tilt from the readings 当然,您可以找到一些库或代码片段来帮助您解决这个问题,但我认为,如果您知道从读数中推断出倾斜的逻辑,我认为这很好。

The pictures was taken from this article , wich i recomend to read if you want to improve the accuracy and consider 2 o 3 axis for the tilt 这些照片是从这篇文章中拍摄的,如果你想要提高精度并考虑2 o 3轴倾斜,我建议阅读

The commonsware Sensor Monitor app does a pretty good job with this. commonsware Sensor Monitor应用程序可以很好地完成这项工作。 It converts the sensor readouts to X, Y, Z values on each sensor reading, so it's pretty easy from there to determine which way the device is moving. 它将传感器读数转换为每个传感器读数上的X,Y,Z值,因此可以很容易地确定设备的移动方式。

https://github.com/commonsguy/cw-omnibus/tree/master/Sensor/Monitor https://github.com/commonsguy/cw-omnibus/tree/master/Sensor/Monitor

Another item worth noting (from the Commonsware book ): 值得注意的另一个项目(来自Commonsware书 ):

There are four standard delay periods, defined as constants on the SensorManager class: 有四个标准延迟周期,在SensorManager类中定义为常量:

  1. SENSOR_DELAY_NORMAL, which is what most apps would use for broad changes, such as detecting a screen rotating from portrait to landscape SENSOR_DELAY_NORMAL,这是大多数应用程序用于广泛更改的内容,例如检测从纵向旋转到横向的屏幕
  2. SENSOR_DELAY_UI, for non-game cases where you want to update the UI continuously based upon sensor readings SENSOR_DELAY_UI,适用于您希望根据传感器读数连续更新UI的非游戏案例
  3. SENSOR_DELAY_GAME, which is faster (less delay) than SENSOR_DELAY_UI, to try to drive a higher frame rate SENSOR_DELAY_GAME比SENSOR_DELAY_UI更快(延迟更少),试图提高帧速率
  4. SENSOR_DELAY_FASTEST, which is the “firehose” of sensor readings, without delay SENSOR_DELAY_FASTEST,这是传感器读数的“消防”,没有延迟

You can use the accelerometer and magnetic field sensor to accomplish this. 您可以使用加速度计和磁场传感器来实现此目的。 You can call this method in your OnSensorChanged method to detect if the phone was tilt upwards. 您可以在OnSensorChanged方法中调用此方法来检测手机是否向上倾斜。 This currently only works if the phone is held horizontally. 目前仅在手机水平握持时才有效。 Check the actual blog post for a more complete solution. 查看实际的博客文章,获取更完整的解决方案。

http://www.ahotbrew.com/how-to-detect-forward-and-backward-tilt/ http://www.ahotbrew.com/how-to-detect-forward-and-backward-tilt/

public boolean isTiltUpward()
{
    if (mGravity != null && mGeomagnetic != null) 
    {
          float R[] = new float[9];
          float I[] = new float[9];

          boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);

          if (success) 
          {
            float orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);               

            /*
             * If the roll is positive, you're in reverse landscape (landscape right), and if the roll is negative you're in landscape (landscape left)
             * 
             * Similarly, you can use the pitch to differentiate between portrait and reverse portrait. 
             * If the pitch is positive, you're in reverse portrait, and if the pitch is negative you're in portrait.
             * 
             * orientation -> azimut, pitch and roll
             * 
             *
             */

            pitch = orientation[1];
            roll = orientation[2];              

            inclineGravity = mGravity.clone();

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

            // Normalize the accelerometer vector
            inclineGravity[0] = (float) (inclineGravity[0] / norm_Of_g);
            inclineGravity[1] = (float) (inclineGravity[1] / norm_Of_g);
            inclineGravity[2] = (float) (inclineGravity[2] / norm_Of_g);

            //Checks if device is flat on ground or not
            int inclination = (int) Math.round(Math.toDegrees(Math.acos(inclineGravity[2])));                     

            /*
             *   Float obj1 = new Float("10.2");
             *   Float obj2 = new Float("10.20");
             *   int retval =  obj1.compareTo(obj2);
             *   
             *   if(retval > 0) {
             *      System.out.println("obj1 is greater than obj2");
             *   }
             *   else if(retval < 0) {
             *      System.out.println("obj1 is less than obj2");
             *   }
             *   else {
             *      System.out.println("obj1 is equal to obj2");
             *   }
             */
            Float objPitch = new Float(pitch);
            Float objZero = new Float(0.0);
            Float objZeroPointTwo = new Float(0.2);
            Float objZeroPointTwoNegative = new Float(-0.2);

            int objPitchZeroResult = objPitch.compareTo(objZero);
            int objPitchZeroPointTwoResult = objZeroPointTwo.compareTo(objPitch);
            int objPitchZeroPointTwoNegativeResult = objPitch.compareTo(objZeroPointTwoNegative);

            if (roll < 0 && ((objPitchZeroResult > 0 && objPitchZeroPointTwoResult > 0) || (objPitchZeroResult < 0 &&  objPitchZeroPointTwoNegativeResult > 0)) && (inclination > 30 && inclination < 40))
            {
                return true;
            }
            else
            {
                return false;
            }               
        }
    }

    return false;
}

Is this what you're looking for? 这是你在找什么?

public class AccelerometerHandler implements SensorEventListener
{
    float accelX;
    float accelY;
    float accelZ;

    public AccelerometerHandler(Context paramContext)
    {
        SensorManager localSensorManager = (SensorManager)paramContext.getSystemService("sensor");

        if (localSensorManager.getSensorList(1).size() != 0)
            localSensorManager.registerListener(this, (Sensor)localSensorManager.getSensorList(1).get(0), 1);
    }

    public float getAccelX()
    {
        return this.accelX;
    }

    public float getAccelY()
    {
        return this.accelY;
    }

    public float getAccelZ()
    {
        return this.accelZ;
    }

    public void onAccuracyChanged(Sensor paramSensor, int paramInt)
    {
    }

    public void onSensorChanged(SensorEvent paramSensorEvent)
    {
        this.accelX = paramSensorEvent.values[0];
        this.accelY = paramSensorEvent.values[1];
        this.accelZ = paramSensorEvent.values[2];
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM