2013-03-21 22 views
5

Tôi gặp sự cố khi đọc chỉ số cảm biến định hướng tốt. Các chỉ số cảm biến dường như không đáng tin cậy, vì vậy tôi đã kiểm tra mã của mình dựa trên hai ứng dụng thử nghiệm cảm biến miễn phí (Sensor Tester (Dicotomica)Sensor Monitoring (R's Software)). Tôi nhận thấy rằng trong khi các bài đọc của tôi thường đồng ý với các ứng dụng thử nghiệm cảm biến, đôi khi các giá trị cho góc phương vị/yaw và cuộn khác nhau lên đến 40 độ, mặc dù mức độ đọc phần lớn được đồng ý. Hai ứng dụng miễn phí dường như luôn đồng ý với nhau.Giá trị cảm biến định hướng không nhất quán trên Android cho góc phương vị/yaw và cuộn

Tôi đặt mã của mình vào một hoạt động Android nhỏ và có cùng sự mâu thuẫn. Mã như sau:

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; 
    } 

} 

Tôi đang sử dụng Galaxy Note 2 chạy Jelly Bean 4.1.1. Bất cứ ai có thể cho tôi biết những gì tôi đang làm sai?

Cập nhật 24 tháng 3 năm 2013: Thông tin thêm. (1) Tôi đã vô hiệu hóa chuyển đổi giữa chân dung và phong cảnh trong tệp kê khai, do đó getWindowManager(). GetDefaultDisplay(). GetRotation() luôn bằng 0. Do đó tôi không nghĩ rằng remapCoordSystem sẽ giúp đỡ ở đây, bởi vì đó là để chuyển đổi trục, trong khi các lỗi mà tôi nhìn thấy không phải là lỗi lớn, chúng tinh tế hơn nhiều. (2) Tôi đã kiểm tra độ nhạy chính xác, và sự mâu thuẫn xảy ra khi cả hai cảm biến đều có độ chính xác cao.

Ví dụ về những mâu thuẫn mà tôi thấy, khi mã ở trên cho tôi (góc phương vị, độ dốc, cuộn) = (235, -52, -11) thì hai ứng dụng miễn phí hiển thị các giá trị tương tự. Nhưng khi tôi nhìn thấy (278, -58, -52) các ứng dụng hiển thị (256, -58, -26), sự khác biệt lớn như vậy trong cả Azimuth và cuộn, mặc dù sân có vẻ OK.

Trả lời

4

Tôi nghĩ cách tốt nhất để xác định góc định hướng của bạn khi thiết bị không phẳng là sử dụng hệ thống phối hợp góc thích hợp hơn mà góc Euler tiêu chuẩn mà bạn nhận được từ SensorManager.getOrientation(...). Tôi đề nghị cái mà tôi mô tả here on math.stackexchange.com. Tôi cũng đã đặt một số mã thực hiện nó trong an answer here. Ngoài một định nghĩa tốt về góc phương vị, nó cũng có định nghĩa tốt hơn về góc sân, mà phương pháp này định nghĩa là xoay vòng ra khỏi mặt phẳng ngang bất kể trục quay xảy ra dọc theo.

Bạn có thể lấy chi tiết đầy đủ từ hai liên kết mà tôi đã đưa ra trong đoạn đầu tiên. Tuy nhiên, tóm lại, ma trận xoay của bạn R từ SensorManager.getRotationMatrix (...) Là

Definition of rotation matrix R

nơi (E x, E y, E z), (N x, N y, N z) và (G x, G y, G z) là các vectơ trỏ do Đông, Bắc và theo hướng Trọng lực. Sau đó, góc phương vị mà bạn muốn được đưa ra bởi

Definition of azimuth angle phi

+0

Nó chỉ cho Azimuth từ -90 đến 90 độ. Làm thế nào tôi có thể nhận được Azimuth trong khoảng 0 đến 359 độ khi di chuyển từ miền Bắc. – Master

+2

@Hoosier câu hỏi tốt, bởi vì như bạn nói, ốp ngược nghịch đảo bình thường trả về số trong khu vực -90 đến 90 độ. Câu trả lời là sử dụng Atan2 (http://en.wikipedia.org/wiki/Atan2) có sẵn trong cả hai ngôn ngữ tôi sử dụng (cụ thể là C# và Java). – Stochastically

+0

Cảm ơn. Tôi sẽ cố gắng sử dụng nó nếu nó hoạt động. – Master

5

Tất cả phụ thuộc vào hướng hướng của thiết bị. Nếu thiết bị không phải là phẳng và có một số xoay tức là không hoàn toàn Portrait hoặc Cảnh thì azimuth là sai. Bạn phải gọi số remapCoordSystem trước khi gọi số getOrientation trong trường hợp này. Bạn cũng nên lọc ít nhất gia tốc giá trị.
Để cụ thể hơn xem câu trả lời của tôi tại Convert magnetic field X, Y, Z values from device into global reference frame
Và đối với ý nghĩa của độ phẳng thấy How to measure the tilt of the phone in XY plane using accelerometer in Android

Bạn nên kiểm tra ứng dụng của bạn như sau:

  1. Lay thiết bị của bạn bằng phẳng và bắt đầu ứng dụng của bạn và so sánh của bạn giá trị với các ứng dụng khác. Cho mỗi lần đọc khoảng 30 giây và không di chuyển thiết bị trong bước này.
    Dự đoán của tôi cho bước này: Giá trị của bạn sẽ khác ít so với các giá trị ứng dụng khác nhưng có thể ít ổn định hơn so với các ứng dụng khác.

  2. Trong khi ứng dụng của bạn đang chạy, hãy xoay thiết bị của bạn đến vị trí mới, đợi cho đến khi giá trị trở thành ổn định (không phải cùng giá trị, nhưng biến thể là khoảng 1 hoặc 2 độ). Trong cùng một vị trí, hãy so sánh giá trị của bạn với các giá trị ứng dụng khác. Lặp lại điều tương tự nhưng với các ứng dụng khác đang chạy khi xoay.
    Dự đoán của tôi cho bước này: giá trị ổn định của bạn sẽ khác ít so với các giá trị ứng dụng khác nhưng sẽ mất nhiều thời gian hơn để giá trị của bạn trở nên ổn định. Ngoài ra, khi dừng ở vị trí mới, sự khác biệt giữa một vài giá trị đầu tiên và giá trị ổn định lớn hơn cho ứng dụng của bạn so với giá trị còn lại.

  3. Bây giờ đặt mã sau đây trước khi comment // đổ vào logcat và đăng nhập nó như bạn làm cho định hướng
    xoay float = (float) Math.atan2 (RotationMatrix [6], RotationMatrix [7]);
    Đặt thiết bị của bạn ở vị trí thẳng đứng trong một hộp để thiết bị không thể xoay. Hãy chắc chắn rằng giá trị của vòng quay được đưa ra ở trên là gần 0 càng tốt (nó sẽ nhảy một chút).
    Lặp lại bước 1 và 2 ngay bây giờ với thiết bị thẳng đứng.
    Đoán của tôi là kết quả của bạn sẽ giống như dự đoán của tôi cho các bước 1 và 2 ở trên.

  4. Đặt thiết bị của bạn ở vị trí thẳng đứng nghiêng trong hộp để không xoay được nữa. Hãy chắc chắn rằng giá trị của vòng quay được đưa ra ở trên là gần 25 hoặc -25 càng tốt (nó sẽ nhảy một chút).
    Lặp lại bước 1 ngay bây giờ với thiết bị nghiêng thẳng đứng.
    Đoán của tôi là kết quả của bạn sẽ khác với các ứng dụng khác.
    Bạn có thể lặp lại bước 4 với giá trị xoay khác nhau.

Nếu dự đoán của tôi đúng, tôi sẽ giải thích cho bạn. Trong thời gian chờ đợi, bạn có thể đọc câu trả lời của tôi tại Magnetic Fields, Rotation Matrix And global coordinates

+0

Cảm ơn bạn đã trả lời, nhưng tôi không nghĩ rằng nó giải quyết vấn đề của tôi. Tôi đã thêm một số thông tin khác vào câu hỏi ban đầu của tôi ở dưới cùng. Ngoài ra, tôi không nghĩ rằng một bộ lọc sẽ giải quyết vấn đề này bởi vì những lỗi này là dai dẳng. Một bộ lọc sẽ giúp giải quyết các bài đọc không liên tục. Tuy nhiên, nhờ các liên kết của bạn hữu ích để giúp tôi hiểu thêm về ý nghĩa của cảm biến. – gisking

+0

Tôi đã chỉnh sửa câu trả lời của mình. –

+0

Bạn nói đúng là mọi thứ đều ổn khi điện thoại phẳng, và nó sai khi đứng thẳng. Tôi đã bắt đầu sử dụng Sensor.TYPE_GRAVITY android và tôi nghĩ điều đó có nghĩa là tôi không cần phải lo lắng về việc lọc. Bí ẩn duy nhất với tôi bây giờ là làm thế nào các ứng dụng miễn phí có thể định nghĩa góc phương vị, độ dốc, cuộn liên tục khi ứng dụng của tôi không thành công khi điện thoại ở chế độ thẳng đứng. Tôi rất biết ơn vì đã trả lời câu hỏi này. – gisking

2

Tôi có thể trả lời một phần câu đố này. Mọi thứ sẽ ổn khi thiết bị được đặt phẳng trên bàn, nhưng khi nó thẳng đứng và sân là cộng hoặc trừ 90 độ thì bạn sẽ gặp phải Gimbal lock. Khi điều này xảy ra, góc phương vị và cuộn không xác định. Trong tình huống đó góc phương vị và cuộn có nghĩa giống nhau, và nếu bạn nhìn vào các phép toán, bạn sẽ thấy rằng azumith + roll được xác định (khi pitch = +90 độ) hoặc góc phương vị được xác định (khi pitch = -90 độ), nhưng hai trong số đó không được định nghĩa riêng. Trong ví dụ bạn đã đưa ra, hãy lưu ý rằng tổng số phương vị + cuộn xấp xỉ giữa ứng dụng của bạn và các ứng dụng khác.

Ngoài ra, bạn có thể sử dụng Android Sensor.TYPE_GRAVITY để bạn không cần sử dụng bộ lọc thông thấp trên các chỉ số gia tốc kế. Sensor.TYPE_GRAVITY là một cảm biến được tính toán từ các cảm biến khác để thử và loại bỏ các hiệu ứng gia tốc từ cảm biến gia tốc kế. Tuy nhiên, nếu bạn muốn ứng dụng của mình chạy trên các phiên bản Android cũ trước khi giới thiệu Sensor.TYPE_GRAVITY thì bạn sẽ cần tự mình thực hiện công việc.

0

Stochastically nói cho getOrientation(), sử dụng:

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

Tuy nhiên, nếu bạn nhìn vào thực hiện của Android, nó thực sự là

azimuth = atan2(Ey, Ny) 

Điều này cũng sẽ cung cấp cho bạn giá trị trong khoảng -180 và 180.

Dưới đây là mã nguồn được đưa ra trong 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; 
} 
Các vấn đề liên quan