2013-03-21 33 views
5

我無法獲得良好的方向傳感器讀數。傳感器讀數似乎不可靠,所以我測試了兩個免費的傳感器測試應用程序(Sensor Tester (Dicotomica)Sensor Monitoring (R's Software))我的代碼。我發現雖然我的閱讀經常與傳感器測試應用程序達成一致,但偶爾會有方位角/偏航值和滾動值相差達40度,儘管俯仰讀數大部分是一致的。這兩個免費應用程序似乎總是彼此認同。Android在方位角/偏航和滾轉方向上傳感器值不一致

我把我的代碼放到一個小的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; 
    } 

} 

我正在使用Galaxy Note 2運行Jelly Bean 4.1.1。誰能告訴我我做錯了什麼?

更新24-Mar-2013:更多信息。 (1)我已禁用清單中縱向和橫向之間的切換,因此getWindowManager()。getDefaultDisplay()。getRotation()始終爲零。因此,我不認爲remapCoordSystem在這裏會有所幫助,因爲這是爲了切換座標軸,而我所看到的錯誤並不是很大的錯誤,它們更加微妙。 (2)我檢查了精確度的靈敏度,當兩個傳感器都聲稱具有高精度時會出現不一致。

作爲我看到的不一致的一個例子,當上面的代碼給我(方位角,俯仰角,滾動角)=(235,-52,-11)時,兩個免費應用程序顯示相似的值。但是當我看到(278,-58,-52)應用程序顯示(256,-58,-26)時,方位角和滾動角度差異很大,儘管俯仰看起來不錯。

回答

4

我認爲當設備不平坦時定義方位角度的最好方法是使用更合適的角座標系統,即從SensorManager.getOrientation(...)獲得的標準歐拉角。我建議我描述一個here on math.stackexchange.com。我也已經把一些代碼實現在an answer here中。除了方位角的良好定義之外,它還具有對俯仰角的更好定義,該方法定義爲旋轉離開水平面,而不管旋轉發生在哪個軸上。

您可以從我在第一段中給出的兩個鏈接中獲得完整詳細信息。但是,總之,您的旋轉矩陣R來自SensorManager.getRotationMatrix(...)是

Definition of rotation matrix R

其中(E X,E ÿ,E Ž),(N X,N ÿ,N Ž)和(G X,G y,G z)是指向正東,北和向重力方向的矢量。然後你想要的方位角由

Definition of azimuth angle phi

+0

它只從-90度到90度提供方位角。從北方移動時,如何獲得0到359度範圍內的方位角。 – Master 2014-03-22 17:13:07

+2

@Hoosier很好的問題,因爲正如你所說,通常的反正切返回該地區的數字-90到90度。答案是使用Atan2(http://en.wikipedia.org/wiki/Atan2),它使用我使用的兩種語言(即C#和Java)。 – Stochastically 2014-03-23 07:47:09

+0

謝謝。如果它有效,我會嘗試使用它。 – Master 2014-03-23 08:08:28

5

這一切都取決於設備的方向。如果設備不並有一些旋轉即不太肖像景觀,則方位是錯誤的。在致電getOrientation之前,您必須致電remapCoordSystem。您還應該至少過濾加速度計的值。
更多細節請參閱我的答案在Convert magnetic field X, Y, Z values from device into global reference frame
而對於平整度的含義見How to measure the tilt of the phone in XY plane using accelerometer in Android

您應該測試您的應用程序如下:

  1. 把你的設備平坦,開始你的應用程序,並比較你與其他應用程序的價值。每次讀取約30秒鐘,在此步驟中不要移動設備。
    我對這一步的猜測:你的價值應該與其他應用的價值差別不大,但它在開始時可能不如其他應用穩定。

  2. 當您的應用程序正在運行時,將您的設備旋轉到新的位置,等到該值變穩定(不必是相同的值,但變化約爲1或2度)。在相同位置將您的值與其他應用程序值進行比較。重複相同的操作,但其他應用程序在旋轉時運行。
    我對這一步的猜測是:你的穩定值應該與其他應用程序的值差別不大,但你的價值變得穩定需要更長的時間。另外,當停在新位置時,前幾個值和穩定值之間的差異對於您的應用來說要大於其他值。

  3. 現在把下面的代碼之前的註釋//轉儲到logcat的和記錄它,你的方向做
    浮動旋轉=(浮動)Math.atan2(RotationMatrix [6],RotationMatrix [7]);
    將您的設備直立放置在一個盒子中,使其不能旋轉。確保上面給出的旋轉值儘可能接近0(它會稍微跳躍一點)。
    立即重複步驟1和2。
    我的猜測是你的結果與我上面第1步和第2步的猜測是一樣的。

  4. 將您的設備放置在一個盒子中傾斜的直立位置,以便它不能進一步旋轉。確保上面給出的旋轉值儘可能接近25或-25(它會稍微跳躍一點)。
    現在重複步驟1,使設備傾斜直立。
    我的猜測是你的結果與其他應用程序會有很大的不同。
    您可以用不同的旋轉值重複步驟4。

如果我的猜測是正確的回來,我會給你解釋。與此同時你可以閱讀我的答案Magnetic Fields, Rotation Matrix And global coordinates

+0

由於源代碼,但我不認爲它解決了我的問題。我在底部添加了一些有關我的原始問題的信息。另外,我不認爲過濾器可以解決這個問題,因爲這些錯誤是持久的。過濾器可以幫助解決間歇性不良的讀數。無論如何,因爲你的鏈接對於幫助我更多地瞭解傳感器的含義非常有用。 – gisking 2013-03-24 11:58:12

+0

我編輯了我的答案。 – 2013-03-24 23:55:33

+0

你是對的,當手機是平的時候一切正常,直立時是錯誤的。我已經開始使用android Sensor.TYPE_GRAVITY,我認爲這意味着我不需要擔心過濾。現在我唯一的謎團是免費應用程序如何在手機豎直時我的應用程序失敗時一致地定義方位角,俯仰角和滾動角。我非常感謝這個答案。 – gisking 2013-03-26 09:16:20

2

我可以回答這個難題的一部分。當設備平放在桌子上時,一切都會很好,但是當它豎直放置並且音高正負90度時,你會遇到Gimbal lock。發生這種情況時,方位角和橫滾角是不確定的。在這種情況下,方位角和滾轉方向意味着同樣的事情,如果你仔細觀察數學,你會發現azumith + roll被定義(當pitch = +90度時)或者方位角roll被定義(當pitch = -90度),但是他們兩個沒有自己定義。在您給出的示例中,請注意您的應用和其他應用之間的總和方位角+滾動大致相同。

此外,您可以使用android Sensor.TYPE_GRAVITY,以便您不需要在加速計讀數上使用低通濾波器。 Sensor.TYPE_GRAVITY是從其他傳感器計算出的傳感器,用於嘗試消除來自加速度傳感器的加速度影響。但是,如果您希望您的應用在引入Sensor.TYPE_GRAVITY之前在舊版Android上運行,那麼您需要自己完成這項工作。

0

隨機所說的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; 
} 
相關問題