6

假设您有所有 3 个维度的加速度读数,即 X、Y 和 Z。您如何使用手机向左或向右倾斜的读数推断?读数每 20 毫秒生成一次。

我实际上想要从读数中推断倾斜的逻辑。倾斜需要平滑。

4

4 回答 4

23

可以以不同的方式检测倾斜。您可以考虑 1 轴、2 轴或 3 轴。取决于你想要它的准确程度,以及你对数学打架的感觉。

如果你只使用一个轴,那就很简单了。认为手机是完全水平的,然后像这样移动它:

在此处输入图像描述

只使用一个轴,比如说轴 x 就足够了,因为您可以准确地检测到该轴位置的变化,因为即使是任何微小的运动都会对轴产生变化。但是,如果您的应用程序只读取该轴,并且用户的手机几乎是垂直的,那么即使将手机旋转一个很大的角度,x 轴的差异也会非常小。无论如何,对于只需要粗分辨率的应用,可以使用单轴。

参考基本三角学,重力矢量在 x 轴上的投影产生的输出加速度等于加速度计 x 轴与地平线之间夹角的正弦值。这意味着有了轴的值(这些是加速度值),您可以计算设备所在的角度。

在此处输入图像描述

这意味着传感器给您的值是 = 到 9,8 * 角度的正弦值,因此通过数学运算可以得到实际角度。

但别担心,你甚至不必这样做。由于这些值或多或少是成比例的,如下表所示,您可以直接使用传感器的值,而不必太在意角度代表什么,如果您不需要它非常准确,由于该值的变化意味着角度的比例变化,因此通过一些测试,您会发现变化应该有多大才能与您相关。

在此处输入图像描述

所以,如果你在一段时间内取值,并相互比较,你可以算出旋转有多大。为了这,

  1. 你只考虑一个轴。这将是 X 轴。
  2. 编写一个函数来获取一个函数调用和下一个函数调用之间该轴的传感器值的差异
  3. 确定最大时间和最小传感器差异,您将考虑有效运动(例如,大旋转是好的,但前提是它足够快,而快速运动只有在角度差异足够大时才是好的)
  4. 如果您检测到满足这些条件的两个测量值,您会记下已完成的半倾斜(例如布尔值),然后再次开始测量,但现在,新的参考值是被认为是半倾斜的值。
  5. 如果最后的差异是正的,现在你需要一个负的差异,如果最后的差异是负的,现在你需要一个正的差异;这就是,回来。因此,开始将新参考值与来自传感器的新值进行比较,看看是否完成了您在第 3 点中的决定。
  6. 如果你找到一个有效值(完成值差和时间条件),你就有了倾向。但是如果你没有得到一个好的值并且时间被消耗,你重置一切:让你的参考值是最后一个,重置计时器,将 half-tilt-done 布尔值重置为 false,并继续测量。

我希望这对你来说已经足够好了。当然,您可以找到一些库或代码片段来帮助您解决这个问题,但我认为,正如您所说,了解从读数中推断倾斜的逻辑是件好事

图片取自本文,如果您想提高精度并考虑倾斜 2 o 3 轴,我建议您阅读。

于 2013-12-09T11:44:29.447 回答
0

commonsware Sensor Monitor 应用程序在这方面做得很好。它将传感器读数转换为每个传感器读数的 X、Y、Z 值,因此很容易从那里确定设备的移动方式。

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

另一个值得注意的项目(来自Commonsware book):

有四个标准延迟周期,在 SensorManager 类中定义为常量:

  1. SENSOR_DELAY_NORMAL,这是大多数应用程序用于广泛更改的方法,例如检测屏幕从纵向旋转到横向
  2. SENSOR_DELAY_UI,适用于您希望根据传感器读数持续更新 UI 的非游戏情况
  3. SENSOR_DELAY_GAME,比 SENSOR_DELAY_UI 更快(延迟更少),以尝试驱动更高的帧速率
  4. SENSOR_DELAY_FASTEST,它是传感器读数的“消防软管”,没有延迟
于 2013-12-10T18:38:49.593 回答
0

您可以使用加速度计和磁场传感器来完成此操作。您可以在 OnSensorChanged 方法中调用此方法来检测手机是否向上倾斜。这目前仅适用于水平握持手机的情况。查看实际的博客文章以获得更完整的解决方案。

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;
}
于 2015-03-22T11:03:14.883 回答
-1

这是你要找的吗?

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];
    }
}
于 2013-12-11T01:10:51.607 回答