【问题标题】:Android getOrientation Azimuth gets polluted when phone is tilted当手机倾斜时,Android getOrientation 方位角会被污染
【发布时间】:2013-07-31 19:30:19
【问题描述】:

我遇到了一个非常烦人的问题,即 AR 视图就像指南针一样。所以当我竖着拿着手机时(让屏幕指向我的脸),然后我打电话给remapCoordinateSystem,当竖着它时,音高为0。然后方位角(罗盘功能)是完美的,但是一旦我倾斜手机,方位角就会被破坏,如果我向前弯曲,方位角会增加,如果我向后弯曲,它会减小。

我使用 2 个传感器来获取读数,Sensor.TYPE_MAGNETIC_FIELD 和 Sensor.TYPE_GRAVITY。

我使用了一个非常基础的低通滤波器,它使用 alpha 常量实现,并直接用于传感器的读取值。

这是我的代码:

float[] rotationMatrix = new float[9];
SensorManager.getRotationMatrix(rotationMatrix, null, gravitymeterValues,
    magnetometerValues);

float[] remappedRotationMatrix = new float[9];

SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X,
    SensorManager.AXIS_Z, remappedRotationMatrix);

float results[] = new float[3];
SensorManager.getOrientation(remappedRotationMatrix, results);

float azimuth = (float) (results[0] * 180 / Math.PI);
if (azimuth < 0) {
    azimuth += 360;
}

float pitch = (float) (results[1] * 180 / Math.PI);
float roll = (float) (results[2] * 180 / Math.PI);

如您所见,这里没有魔法。当gravitymeterValues和magnetometerValues都可以使用时,我调用这段代码。

我的问题是,当我倾斜手机时,如何防止方位角变得疯狂?

我在 Google Play 商店中查看了一个免费的应用程序,Compass,它并没有解决这个问题,但我希望有一个解决方案。

我有两个解决方案:

  1. 让 AR 视图只在非常受限的俯仰角度下工作,现在我有类似 pitch &gt;= -5 &amp;&amp; pitch &lt;= 30 的东西。如果这未完成,则会向用户显示一个屏幕,要求他/她将手机旋转到纵向。

  2. 以某种方式使用俯仰角来抑制方位角,虽然这似乎是一个非常适合特定设备的解决方案,但我当然愿意接受建议。

我还可以补充一点,我已经花了几个小时寻找一个体面的解决方案,但我没有找到任何比 2) 更好的解决方案。

提前致谢!

【问题讨论】:

  • 你用什么低通滤波器?使用 TYPE_GRAVITY 时无需过滤。
  • @HoanNguyen 我需要低通Sensor.TYPE_MAGNETIC_FIELD吗?现在我低通Sensor.TYPE_MAGNETIC_FIELD 和Sensor.TYPE_GRAVITY。
  • 不,使用低通滤波器是为了去除 x 和 y 方向的加速度。 getRotationMatrix 要求第三个参数大约是仅在 z 方向上的加速度。如果使用 TYPE_GRAVItY 就不需要低通滤波,但是很多设备没有 TYPE_GRAVITY,所以对于没有 TYPE_GRAVITY 的设备,你需要使用 TYPE_ACCELEROMETER 和低通滤波。
  • 好的,谢谢。但是当我倾斜手机时,我的方位角值仍然很差,知道如何解决这个问题吗?
  • 我保存了历史记录并平均了结果。我前后弯曲都没有问题,当然方位角可以改变,但不超过 2 或 3 度。

标签: android math augmented-reality compass-geolocation


【解决方案1】:

完整代码见https://github.com/hoananguyen/dsensor
保持历史和平均值,我不知道俯仰和滚动的正确解释,所以以下代码仅用于方位角。

班级成员

private List<float[]> mRotHist = new ArrayList<float[]>();
private int mRotHistIndex;
// Change the value so that the azimuth is stable and fit your requirement
private int mHistoryMaxLength = 40;
float[] mGravity;
float[] mMagnetic;
float[] mRotationMatrix = new float[9];
// the direction of the back camera, only valid if the device is tilted up by
// at least 25 degrees.
private float mFacing = Float.NAN;

public static final float TWENTY_FIVE_DEGREE_IN_RADIAN = 0.436332313f;
public static final float ONE_FIFTY_FIVE_DEGREE_IN_RADIAN = 2.7052603f;

onSensorChanged

@Override
public void onSensorChanged(SensorEvent event)
{
     if (event.sensor.getType() == Sensor.TYPE_GRAVITY)
     {
         mGravity = event.values.clone();
     }
     else
     {
        mMagnetic = event.values.clone();
     }

     if (mGravity != null && mMagnetic != null)
     {
          if (SensorManager.getRotationMatrix(mRotationMatrix, null, mGravity, mMagnetic))
          {
              // inclination is the degree of tilt by the device independent of orientation (portrait or landscape)
              // if less than 25 or more than 155 degrees the device is considered lying flat
              float inclination = (float) Math.acos(mRotationMatrix[8]);
              if (inclination < TWENTY_FIVE_DEGREE_IN_RADIAN 
                      || inclination > ONE_FIFTY_FIVE_DEGREE_IN_RADIAN)
              {
                  // mFacing is undefined, so we need to clear the history
                  clearRotHist();
                  mFacing = Float.NaN;
              }
              else
              {
                  setRotHist();
                  // mFacing = azimuth is in radian
                  mFacing = findFacing(); 
              }
          }
     }
}

private void clearRotHist()
{
    if (DEBUG) {Log.d(TAG, "clearRotHist()");}
    mRotHist.clear();
    mRotHistIndex = 0;
}

private void setRotHist()
{
    if (DEBUG) {Log.d(TAG, "setRotHist()");}
    float[] hist = mRotationMatrix.clone();
    if (mRotHist.size() == mHistoryMaxLength)
    {
        mRotHist.remove(mRotHistIndex);
    }   
    mRotHist.add(mRotHistIndex++, hist);
    mRotHistIndex %= mHistoryMaxLength;
}

private float findFacing()
{
    if (DEBUG) {Log.d(TAG, "findFacing()");}
    float[] averageRotHist = average(mRotHist);
    return (float) Math.atan2(-averageRotHist[2], -averageRotHist[5]);
}

public float[] average(List<float[]> values)
{
    float[] result = new float[9];
    for (float[] value : values)
    {
        for (int i = 0; i < 9; i++)
        {
            result[i] += value[i];
        }
    }

    for (int i = 0; i < 9; i++)
    {
        result[i] = result[i] / values.size();
    }

    return result;
}

【讨论】:

  • 好东西!现在我的方位角好多了,现在唯一的问题是快速移动它会跳很多,但我想你必须忍受这一点。非常感谢您的代码。还有一个问题:您的传感器的更新率是多少? UI/游戏/哪一个?谢谢
  • 您是否在指南针应用程序中使用此代码?我使用 UI 并且它非常频繁,顺便说一下,我的代码效果很好!
  • 我正在尝试编写一个基于位置的 AR 库。我没有完成它,因为我想包括高度,并在测试 GPS 返回的高度时发现非常不准确。
  • 好的,我会用普通 vs UI 进行实验,感谢您的帮助,非常感谢!
  • @HoanNguyen 你能说明如何为Sensor.TYPE_ROTATION_VECTOR 和SensorManager.getRotationMatrixFromVector() 而不是SensorManager.getRotationMatrix()? 执行此操作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-13
相关资源
最近更新 更多