2012-02-26 3 views
3

Я работаю над наклонным приложением для Android. У меня проблема с портретом & пейзажным режимом. Когда шаг = 90 градусов (телефон на конце) и даже немного до того, как значение рулона сойдет с ума, когда физическое изменение рулона не изменилось. Я не смог найти решение этой проблемы. Если кто-то может указать мне в правильном направлении, это будет оценено.Android Pitch and Roll Issue

Вот краткий код, поэтому вы знаете, что это не ошибка акселерометра.

final SensorEventListener mEventListener = new SensorEventListener(){ 
    public void onAccuracyChanged(Sensor sensor, int accuracy) {} 
public void onSensorChanged(SensorEvent event) { 
    setListners(sensorManager, mEventListener); 

     SensorManager.getRotationMatrix(mRotationMatrix, null, mValuesAccel, mValuesMagnet); 
    SensorManager.getOrientation(mRotationMatrix, mValuesOrientation); 


     synchronized (this) { 

      switch (event.sensor.getType()){ 
       case Sensor.TYPE_ACCELEROMETER: 

        System.arraycopy(event.values, 0, mValuesAccel, 0, 3); 

        long actualTime = System.currentTimeMillis(); 

        //Sensitivity delay 
        if (actualTime - lastUpdate < 250) { 
         return; 
         } 
        else { 
         sysAzimuth = (int)Math.toDegrees(mValuesOrientation[0]); 
         sysPitch = (int)Math.toDegrees(mValuesOrientation[1]); 
         sysRoll = (int)Math.toDegrees(mValuesOrientation[2]); 

         //invert direction with -1 
         pitch = (sysPitch - pitchCal)*-1; 
         roll = (sysRoll - rollCal); 
         azimuth = sysAzimuth; 

        lastUpdate = actualTime; 
        } 

ответ

8

Я нашел то, что искал, Вращающиеся матрицы.

Я использовал углы Эйлера (рулон, шаг, рыскание) для высоты тона и рулона. Когда телефон находится на конце 90 градусов, точки x и z одинаковы, а телефон сходит с ума, что является фундаментальным недостатком с углами Эйлера.

мне нужно, чтобы получить тангаж и крен градусов, используя вращательные Матрицы через getRotationMatrix

Здесь для всех;)

XML:

<?xml version="1.0" encoding="utf-8"?> 
<!-- This file is res/layout/main.xml --> 
<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" > 
<Button android:id="@+id/update" android:text="Update Values" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:onClick="doUpdate" /> 
<Button android:id="@+id/show" android:text="Show Me!" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:onClick="doShow" android:layout_toRightOf="@id/update" /> 
<TextView android:id="@+id/preferred" android:textSize="20sp" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_below="@id/update" /> 
<TextView android:id="@+id/orientation" android:textSize="20sp" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_below="@id/preferred" /> 
</RelativeLayout> 

Код:

package YOURPACKAGE; 



import android.app.Activity; 
import android.content.Intent; 
import android.hardware.Sensor; 
import android.hardware.SensorEvent; 
import android.hardware.SensorEventListener; 
import android.hardware.SensorManager; 
import android.net.Uri; 
import android.os.Build; 
import android.os.Bundle; 
import android.view.View; 
import android.view.WindowManager; 
import android.widget.TextView; 


public class YOURCLASS extends Activity implements SensorEventListener { 
private static final String TAG = "VirtualJax"; 
private SensorManager mgr; 
private Sensor accel; 
private Sensor compass; 
private Sensor orient; 
private TextView preferred; 
private TextView orientation; 
private boolean ready = false; 
private float[] accelValues = new float[3]; 
private float[] compassValues = new float[3]; 
private float[] inR = new float[9]; 
private float[] inclineMatrix = new float[9]; 
private float[] orientationValues = new float[3]; 
private float[] prefValues = new float[3]; 
private float mAzimuth; 
private double mInclination; 
private int counter; 
private int mRotation; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    preferred = (TextView)findViewById(R.id.preferred); 
    orientation = (TextView)findViewById(R.id.orientation); 
    mgr = (SensorManager) this.getSystemService(SENSOR_SERVICE); 
    accel = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 
    compass = mgr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 
    orient = mgr.getDefaultSensor(Sensor.TYPE_ORIENTATION); 
    WindowManager window = (WindowManager) this.getSystemService(WINDOW_SERVICE); 
    int apiLevel = Integer.parseInt(Build.VERSION.SDK); 
    if(apiLevel <8) { 
     mRotation = window.getDefaultDisplay().getOrientation(); 
    } 
    else { 
     mRotation = window.getDefaultDisplay().getRotation(); 
    } 
} 

@Override 
protected void onResume() { 
    mgr.registerListener(this, accel, SensorManager.SENSOR_DELAY_GAME); 
    mgr.registerListener(this, compass, SensorManager.SENSOR_DELAY_GAME); 
    mgr.registerListener(this, orient, SensorManager.SENSOR_DELAY_GAME); 
    super.onResume(); 
} 

@Override 
protected void onPause() { 
    mgr.unregisterListener(this, accel); 
    mgr.unregisterListener(this, compass); 
    mgr.unregisterListener(this, orient); 
    super.onPause(); 
} 

public void onAccuracyChanged(Sensor sensor, int accuracy) { 
    // ignore 
} 

public void onSensorChanged(SensorEvent event) { 
    // Need to get both accelerometer and compass 
    // before we can determine our orientationValues 
    switch(event.sensor.getType()) { 
     case Sensor.TYPE_ACCELEROMETER: 
      for(int i=0; i<3; i++) { 
       accelValues[i] = event.values[i]; 
      } 
      if(compassValues[0] != 0) 
       ready = true; 
      break; 
     case Sensor.TYPE_MAGNETIC_FIELD: 
      for(int i=0; i<3; i++) { 
       compassValues[i] = event.values[i]; 
      } 
      if(accelValues[2] != 0) 
       ready = true; 
      break; 
     case Sensor.TYPE_ORIENTATION: 
      for(int i=0; i<3; i++) { 
       orientationValues[i] = event.values[i]; 
      } 
      break; 
    } 

    if(!ready) 
     return; 
    if(SensorManager.getRotationMatrix(inR, inclineMatrix, accelValues, compassValues)) { 
     // got a good rotation matrix 
     SensorManager.getOrientation(inR, prefValues); 
     mInclination = SensorManager.getInclination(inclineMatrix); 
     // Display every 10th value 
     if(counter++ % 10 == 0) { 
      doUpdate(null); 
      counter = 1; 
     } 

    } 
} 

public void doUpdate(View view) { 
    if(!ready) 
     return; 
    mAzimuth = (float) Math.toDegrees(prefValues[0]); 
    if(mAzimuth < 0) { 
     mAzimuth += 360.0f; 
    } 
    String msg = String.format(
      "Preferred:\nazimuth (Z): %7.3f \npitch (X): %7.3f\nroll (Y): %7.3f", 
      mAzimuth, Math.toDegrees(prefValues[1]), 
      Math.toDegrees(prefValues[2])); 
    preferred.setText(msg); 
    msg = String.format(
      "Orientation Sensor:\nazimuth (Z): %7.3f\npitch (X): %7.3f\nroll (Y): %7.3f", 
      orientationValues[0], 
      orientationValues[1], 
      orientationValues[2]); 
    orientation.setText(msg); 
    preferred.invalidate(); 
    orientation.invalidate(); 
} 

public void doShow(View view) { 
    // google.streetview:cbll=30.32454,-81.6584&cbp=1,yaw,,pitch,1.0 
    // yaw = degrees clockwise from North 
    // For yaw we can use either mAzimuth or orientationValues[0]. 
    // 
    // pitch = degrees up or down. -90 is looking straight up, 
    // +90 is looking straight down 
    // except that pitch doesn't work properly 
    Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse(
      "google.streetview:cbll=30.32454,-81.6584&cbp=1," + 
        Math.round(orientationValues[0]) + ",,0,1.0" 
    )); 
    startActivity(intent); 
    return; 
} 
+0

У меня такая же проблема, хотя я также использую вращательные матрицы. Я использую getRotationMatrixFromVector(), но вместо getRotationMatrix(), потому что я использую TYPE_ROTATION_VECTOR. Существуют ли различия между этими двумя методами? Второй вопрос заключается в том, что это у вас код if (mAzimuth <0) { mAzimuth + = 360.0f; }? – Nazerke

+0

Невозможно прокомментировать вектор, но mAzimuth - это заголовок компаса, он не обеспечивает его чистым показанием 0-360, вам нужно преобразовать значения neg обратно в 360, плюс множество других настроек, если вы хотите правильно рассчитать. Вы могли бы подумать, что можете просто попросить устройство для заголовка, ролика и компаса и вернуть его, никоим образом, вы не поймете это, крупный PITA для того, что должно было быть простым. – user1234051

+0

Что это за другие хитрости? Что вы делаете с mInclination? Это часть исправления? Я также использую getRotationMatrixFromVector() и вижу, что азимут меняется с шагом, чего я не хочу, и карты Google не работают в режиме компаса. – Flyview

2

Я бы не использовать углы Эйлера (крен, тангаж, рыскание). Это довольно сильно придает устойчивость вашему приложению, как вы уже заметили.

См. Здесь, почему и что делать: Strange behavior with android orientation sensor.

+0

Спасибо, я проверил почту, я четко понимаю, почему ангелы Эйлера являются плохой выбор, но я не знаком с матрицами вращения и не могу найти никакой хорошей информации об их реализации в Android. Любые другие ссылки? – user1234051

+0

Вам не нужно его реализовывать, он реализуется SensorManager. Я бы начал с [SensorManager.getRotationMatrix] (http://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix). – Ali

+0

Спасибо, я посмотрел на это. К сожалению, я не очень хорошо разбираюсь в теоретических примерах Google. Я размышляю над тем, как смотреть на рабочий код, новичок в концепции матриц вращения, а также Android. Прямо сейчас на подъеме поднимается гора. – user1234051

0

В результате экспериментов я обнаружил, что при переключении с режима «Портрет в альбом» Ваша матрица вращения не меняется, но вы должны изменить его вручную, чтобы использовать с OpenGL правильно

copyMat(mRotationMatrixP, mRotationMatrix); 

// permute and negate columns 0, 1 
mRotationMatrixP[0] = -mRotationMatrix[1]; 
mRotationMatrixP[4] = -mRotationMatrix[5]; 
mRotationMatrixP[8] = -mRotationMatrix[9]; 

// permute 1, 0 
mRotationMatrixP[1] = mRotationMatrix[0]; 
mRotationMatrixP[5] = mRotationMatrix[4]; 
mRotationMatrixP[9] = mRotationMatrix[8]; 

Также я надеюсь, что вы приобретете матрицы поворота правильно на первом месте:

public void onSensorChanged(SensorEvent event) { 
    if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { 
     SensorManager.getRotationMatrixFromVector(
       mRotationMatrix , event.values); 
     SensorManager.getOrientation (mRotationMatrix, values); 
0

Что вы описываете, называется gimbal lock. При шаге +/- 90, рыскание - (+) рулон полностью не определено. Около отметки +/- 90, малый шум/погрешность в отношении может вызвать большие колебания в рыскании и рулоне по отдельности, даже если в фактической ориентации нет больших изменений. Вот большая рецензия на рыскания, тангажа рулоне (и как Тэй не реализованы хорошо на многих платформах):

http://www.sensorplatforms.com/understanding-orientation-conventions-mobile-platforms/

Смежные вопросы