5

我无法获得良好的方向传感器读数。传感器读数似乎不可靠,因此我针对两个免费的传感器测试应用程序(Sensor Tester (Dicotomica)Sensor Monitoring (R's Software))测试了我的代码。我发现虽然我的读数通常与传感器测试应用程序一致,但有时方位角/偏航角和滚动的值相差高达 40 度,尽管俯仰读数大多一致。这两个免费应用程序似乎总是相互一致。

我将我的代码放入一个很小的 ​​Android 活动中并得到了同样的不一致。代码如下:

public class MainActivity extends Activity implements  SensorEventListener {

    private SensorManager mSensorManager;
    private float[] AccelerometerValues;
    private float[] MagneticFieldValues;
    private float[] RotationMatrix;
    private long nextRefreshTime;           // used to ensure dump to LogCat occurs no more than 4 times a second
    private DecimalFormat df;               // used for dumping sensors to LogCat

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSensorManager = (SensorManager)getSystemService(android.content.Context.SENSOR_SERVICE);
        Sensor SensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, SensorAccelerometer, SensorManager.SENSOR_DELAY_UI);  
        Sensor SensorMagField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        mSensorManager.registerListener(this, SensorMagField, SensorManager.SENSOR_DELAY_UI);
        AccelerometerValues = new float[3];
        MagneticFieldValues = new float[3];
        RotationMatrix = new float[9];
        nextRefreshTime = 0;
        df = new DecimalFormat("#.00");
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
            System.arraycopy(event.values, 0, AccelerometerValues, 0, AccelerometerValues.length);
        else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
                System.arraycopy(event.values, 0, MagneticFieldValues, 0, MagneticFieldValues.length);

        if (AccelerometerValues != null && MagneticFieldValues != null) {
            if(SensorManager.getRotationMatrix(RotationMatrix, null, AccelerometerValues, MagneticFieldValues)) {
                float[] OrientationValues = new float[3];
                SensorManager.getOrientation(RotationMatrix, OrientationValues);

                // chance conventions to match sample apps
                if (OrientationValues[0] < 0) OrientationValues[0] += 2*(float)Math.PI;
                OrientationValues[2] *= -1;

                // dump to logcat 4 times a second
                long currentTimeMillis = System.currentTimeMillis();
                if (currentTimeMillis > nextRefreshTime) {
                    nextRefreshTime = currentTimeMillis+250;
                    Log.i("Sensors",    // arrange output so that numbers line up in columns :-)
                            "(" + AngleToStr(OrientationValues[0]) + "," + AngleToStr(OrientationValues[1]) + "," + AngleToStr(OrientationValues[2])
                            + ") ("+FloatToStr(AccelerometerValues[0]) + "," + FloatToStr(AccelerometerValues[1]) + "," + FloatToStr(AccelerometerValues[2])
                            + ") ("+FloatToStr(MagneticFieldValues[0]) + "," + FloatToStr(MagneticFieldValues[1]) + "," + FloatToStr(MagneticFieldValues[2])+")");
                }               
            }
        }               
    }

    private String AngleToStr(double AngleInRadians) {
        String Str = "   "+Integer.toString((int)Math.toDegrees(AngleInRadians));
        return Str.substring(Str.length() - 3);
    }
    private String FloatToStr(float flt) {
        String Str = "      "+df.format(flt);
        return Str.substring(Str.length() - 6);
    }   

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSensorManager.unregisterListener(this);
    }

    @Override
    public void onAccuracyChanged(Sensor arg0, int arg1) { }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

}

我正在使用运行 Jelly Bean 4.1.1 的 Galaxy Note 2。谁能告诉我我做错了什么?

2013 年 3 月 24 日更新:更多信息。(1) 我在清单中禁用了纵向和横向之间的切换,因此 getWindowManager().getDefaultDisplay().getRotation() 始终为零。因此,我认为 remapCoordSystem 在这里没有帮助,因为那是用于切换轴,而我看到的错误并不是大错误,它们更加微妙。(2)我检查了精度灵敏度,当两个传感器都声称具有高精度时,就会出现不一致的情况。

作为我看到的不一致的示例,当上面的代码给我 (azimuth,pitch,roll) = (235,-52,-11) 时,这两个免费应用程序显示相似的值。但是当我看到 (278, -58, -52) 应用程序显示 (256, -58, -26) 时,方位角和滚动的差异如此之大,尽管俯仰似乎还可以。

4

4 回答 4

6

我认为当设备不平坦时定义方向角的最佳方法是使用更合适的角坐标系,该坐标系是您从中获得的标准欧拉角SensorManager.getOrientation(...)。我建议我在 math.stackexchange.com 上描述的那个。我还在此处的答案中放了一些实现它的代码。除了方位角的良好定义外,它还对俯仰角有更好的定义,该方法将其定义为水平面外的旋转,而不管旋转发生在哪个轴上。

您可以从我在第一段中给出的两个链接中获得完整的详细信息。但是,总而言之,来自 SensorManager.getRotationMatrix(...)的旋转矩阵R是

旋转矩阵R的定义

其中 (E x , E y , E z ), (N x , N y , N z ) 和 (G x , G y , G z ) 是指向正东、正北和重力方向的向量。然后你想要的方位角由下式给出

方位角phi的定义

于 2013-05-07T11:36:50.340 回答
5

这完全取决于设备的方向。如果设备不是的并且有一些旋转,即不是完全纵向横向,那么方位角是错误的。在这种情况下,您必须先打电话remapCoordSystem再打电话。getOrientation您还应该至少过滤加速度计值。有关更多详细信息,请参阅我在将磁场 X、Y、Z 值从设备转换为全局参考框架
中的 回答 有关平坦度的含义,请参阅如何使用 Android 中的加速度计测量手机在 XY 平面中的倾斜度

您应该按如下方式测试您的应用程序:

  1. 将您的设备平放并启动您的应用程序并将您的价值与其他应用程序进行比较。给每个读数大约 30 秒,并且在此步骤中不要移动设备。
    我对这一步的猜测:您的值应该与其他应用程序的值相差不大,但一开始它可能不如其他应用程序稳定。

  2. 在您的应用程序运行时,将您的设备旋转到一个新位置,等待该值稳定(不必是相同的值,但变化大约为 1 或 2 度)。在同一位置将您的价值与其他应用程序的价值进行比较。重复相同的操作,但在旋转时运行其他应用程序。
    我对这一步的猜测:您的稳定值应该与其他应用程序的值相差不大,但您的值需要更长的时间才能变得稳定。此外,当停在新位置时,您的应用程序的前几个值和稳定值之间的差异比另一个更大。

  3. 现在将以下代码放在注释之前 //dump 到 logcat 并像为方向那样记录它
    float rotation = (float) Math.atan2(RotationMatrix[6], RotationMatrix[7]);
    将您的设备直立放置在盒子中,使其无法旋转。确保上面给出的旋转值尽可能接近 0(它会跳跃一点)。
    现在重复步骤 1 和 2,设备直立。
    我的猜测是,您的结果将与我对上述步骤 1 和 2 的猜测相同。

  4. 将您的设备倾斜直立放置在盒子中,使其无法进一步旋转。确保上面给出的旋转值尽可能接近 25 或 -25(它会跳跃一点)。
    现在重复步骤 1,将设备倾斜直立。
    我的猜测是您的结果将与其他应用程序完全不同。
    您可以使用不同的旋转值重复步骤 4。

如果我的猜测是正确的回来,我会给你解释。同时,您可以在磁场、旋转矩阵和全局坐标中阅读我的答案

于 2013-03-21T00:20:04.923 回答
3

我可以回答这个难题的一部分。当设备平放在桌子上时一切都很好,但是当它直立并且俯仰为正负 90 度时,您会遇到云台锁定。发生这种情况时,方位角和滚动是未定义的。在这种情况下,方位角和滚动意味着同一件事,如果您查看数学,您会发现定义了方位角+滚动(当俯仰 = +90 度时)或方位角滚动(当俯仰 = -90 度时) ),但他们两个不是自己定义的。在您给出的示例中,请注意您的应用程序和其他应用程序之间的方位角+滚动总和大致相同。

此外,您可以使用 android Sensor.TYPE_GRAVITY,这样您就不需要在加速度计读数上使用低通滤波器。Sensor.TYPE_GRAVITY 是一个传感器,它是从其他传感器计算出来的,以尝试消除加速度计传感器的加速度影响。但是,如果您希望您的应用在引入 Sensor.TYPE_GRAVITY 之前在旧版本的 Android 上运行,那么您需要自己完成这项工作。

于 2013-03-26T00:12:38.177 回答
0

@Stochastically 说对于 getOrientation(),使用:

azimuth = atan2((Ey-Nx), (Ex-Ny))

但是,如果你看 Android 的实现,它实际上是

azimuth = atan2(Ey, Ny)

这也将为您提供 -180 和 180 范围内的值。

下面是 SensorManager.class 中给出的源代码

public static float[] getOrientation(float[] R, float values[]) {
    /*
     * 4x4 (length=16) case:
     *   /  R[ 0]   R[ 1]   R[ 2]   0  \
     *   |  R[ 4]   R[ 5]   R[ 6]   0  |
     *   |  R[ 8]   R[ 9]   R[10]   0  |
     *   \      0       0       0   1  /
     *
     * 3x3 (length=9) case:
     *   /  R[ 0]   R[ 1]   R[ 2]  \
     *   |  R[ 3]   R[ 4]   R[ 5]  |
     *   \  R[ 6]   R[ 7]   R[ 8]  /
     *
     */
    if (R.length == 9) {
        values[0] = (float)Math.atan2(R[1], R[4]);
        values[1] = (float)Math.asin(-R[7]);
        values[2] = (float)Math.atan2(-R[6], R[8]);
    } else {
        values[0] = (float)Math.atan2(R[1], R[5]);
        values[1] = (float)Math.asin(-R[9]);
        values[2] = (float)Math.atan2(-R[8], R[10]);
    }
    return values;
}
于 2015-02-21T01:37:13.283 回答