2013-03-21 6 views
5

के लिए एंड्रॉइड पर असंगत अभिविन्यास सेंसर मान मुझे अच्छा अभिविन्यास सेंसर रीडिंग प्राप्त करने में परेशानी हो रही है। सेंसर रीडिंग अविश्वसनीय लगती थी, इसलिए मैंने दो कोड मुफ्त सेंसर परीक्षण ऐप्स (Sensor Tester (Dicotomica) और Sensor Monitoring (R's Software)) के खिलाफ अपना कोड परीक्षण किया। मैंने पाया कि मेरे रीडिंग अक्सर सेंसर परीक्षण ऐप्स के साथ सहमत होते हैं, कभी-कभी एजीमुथ/यॉ के लिए मान, और रोल 40 डिग्री तक भिन्न होते हैं, हालांकि पिच पढ़ने पर ज्यादातर सहमत होते हैं। दो मुफ्त ऐप्स हमेशा एक-दूसरे से सहमत होते थे।एंड्रॉइड पर एज़िमथ/यॉ और रोल

मैंने अपना कोड एक छोटी एंड्रॉइड गतिविधि में डाल दिया और एक ही असंगतता प्राप्त की। कोड निम्नानुसार है:

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

} 

मैं गैलेक्सी नोट 2 का उपयोग कर जेली बीन 4.1.1 चला रहा हूं। क्या कोई मुझे बता सकता है कि मैं क्या गलत कर रहा हूं?

अपडेट 24-मार्च-2013: अधिक जानकारी। (1) मैंने मैनिफेस्ट में पोर्ट्रेट और लैंडस्केप के बीच स्विच अक्षम कर दिए हैं, इसलिए GetWindowManager()। GetDefaultDisplay()। GetRotation() हमेशा शून्य होता है। इसलिए मुझे नहीं लगता कि remapCoordSystem यहां मदद करेगा, क्योंकि यह axes को स्विच करने के लिए है, जबकि जो त्रुटियां मैं देख रहा हूं वे बड़ी त्रुटियां नहीं हैं, वे बहुत अधिक सूक्ष्म हैं। (2) मैंने सटीकता संवेदनशीलता की जांच की है, और असंगतता तब होती है जब दोनों सेंसर उच्च सटीकता का दावा करते हैं।

मैं देख रहा विसंगतियों का एक उदाहरण के रूप में, जब उपरोक्त कोड मुझे (एजीमुथ, पिच, रोल) = (235, -52, -11) देता है तो दो फ्री ऐप्स समान मान दिखाते हैं। लेकिन जब मैं देखता हूं (278, -58, -52) ऐप्स दिखाते हैं (256, -58, -26), तो अजीमुथ और रोल दोनों में इतने बड़े अंतर, हालांकि पिच ठीक लगता है।

उत्तर

4

मुझे लगता है कि डिवाइस फ्लैट नहीं होने पर आपके ओरिएंटेशन कोण को परिभाषित करने का सबसे अच्छा तरीका है एक उचित उचित कोणीय समन्वय प्रणाली का उपयोग करना है कि मानक यूलर कोण जिन्हें आप SensorManager.getOrientation(...) से प्राप्त करते हैं। मैं सुझाव देता हूं कि मैं here on math.stackexchange.com का वर्णन करता हूं। मैंने कुछ कोड भी लगाया है जो इसे an answer here में लागू करता है। अजीमुथ की एक अच्छी परिभाषा के अलावा, इसमें पिच कोण की बेहतर परिभाषा भी होती है, जो इस पद्धति क्षैतिज विमान से घूर्णन के रूप में परिभाषित करती है, भले ही घूर्णन किस धुरी के साथ होता है।

आप पहले पैराग्राफ में दिए गए दो लिंक से पूर्ण विवरण प्राप्त कर सकते हैं। हालांकि, संक्षेप में, आपके रोटेशन मैट्रिक्स R सेन्सोरमेनगर से .getRotationMatrix (...) है

Definition of rotation matrix R

जहां (ई एक्स, ई y, ई z), (एन एक्स, एन y, एन z) और (जी एक्स, जी वाई, जी z) वेक्टर पूर्व पूर्व, उत्तर और गुरुत्वाकर्षण की दिशा में इंगित करते हैं। तब दिगंश कोण जो आप चाहते द्वारा

Definition of azimuth angle phi

+0

यह केवल अजीमुथ को -90 से 90 डिग्री तक देता है। उत्तर से आगे बढ़ते समय मैं 0 से 35 9 डिग्री तक अज़ीमुथ कैसे प्राप्त कर सकता हूं। – Master

+2

@ हूसीयर अच्छा सवाल है, क्योंकि जैसा कि आप कहते हैं, सामान्य व्यस्त टेंगेंट क्षेत्र -90 से 9 0 डिग्री में संख्या देता है। जवाब Atan2 (http://en.wikipedia.org/wiki/Atan2) का उपयोग करना है जो कि मैं उपयोग की जाने वाली दोनों भाषाओं में उपलब्ध हूं (अर्थात् सी # और जावा)। – Stochastically

+0

धन्यवाद। अगर यह काम करता है तो मैं इसका इस्तेमाल करने की कोशिश करूंगा। – Master

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

धन्यवाद SensorManager.class में दिए गए स्रोत कोड है, लेकिन मैं इसे अपने समस्या का हल नहीं लगता। मैंने नीचे अपने मूल प्रश्न में कुछ और जानकारी जोड़ दी है। साथ ही, मुझे नहीं लगता कि एक फ़िल्टर इस समस्या को हल करेगा क्योंकि ये त्रुटियां लगातार हैं। एक फ़िल्टर अस्थायी खराब रीडिंग को हल करने में मदद करेगा। वैसे भी धन्यवाद, क्योंकि आपके लिंक सेंसर के मतलब के बारे में और अधिक समझने में मेरी सहायता के लिए उपयोगी थे। – gisking

+0

मैंने अपना जवाब संपादित किया। –

+0

आप सही हैं कि फ़ोन सपाट होने पर सब कुछ ठीक है, और जब यह सीधे है तो यह गलत है। मैंने एंड्रॉइड सेंसर.TYPE_GRAVITY का उपयोग करना शुरू कर दिया है, और मुझे लगता है कि इसका मतलब है कि मुझे फ़िल्टरिंग के बारे में चिंता करने की आवश्यकता नहीं है। मेरे लिए एकमात्र रहस्य यह है कि जब फ्री ऐप फोन पर सही होता है तो मेरा ऐप असफल होने पर फ्री ऐप एजीमुथ, पिच, रोल को परिभाषित करने में सक्षम होता है। मैं इसके जवाब के लिए बहुत आभारी हूं। – gisking

2

पर अपना उत्तर पढ़ सकते हैं, मैं इस पहेली का हिस्सा जवाब दे सकता हूं। जब डिवाइस एक टेबल पर फ्लैट लगा रहा है, तो सबकुछ ठीक होगा, लेकिन जब यह सीधे हो और पिच प्लस या घटाकर 90 डिग्री हो तो आपको Gimbal lock का सामना करना पड़ेगा। जब ऐसा होता है, तो एजीमुथ और रोल अपरिभाषित होते हैं। उस स्थिति में अजीमुथ और रोल का मतलब एक ही बात है, और यदि आप गणित को देखते हैं तो आप देखेंगे कि या तो अज़ुमिथ + रोल परिभाषित किया गया है (जब पिच = +90 डिग्री) या अजीमुथ-रोल परिभाषित किया जाता है (जब पिच = -90 डिग्री), लेकिन उनमें से दो को स्वयं परिभाषित नहीं किया गया है। आपके द्वारा दिए गए उदाहरण में, ध्यान दें कि योग एज़िमथ + रोल लगभग आपके ऐप और अन्य ऐप्स के बीच समान है।

इसके अलावा, आप एंड्रॉइड सेंसर.TYPE_GRAVITY का उपयोग कर सकते हैं ताकि आपको एक्सेलेरोमीटर रीडिंग पर कम पास फ़िल्टर का उपयोग करने की आवश्यकता न हो। सेंसर। TYPE_GRAVITY एक सेंसर है जिसे एक्सेलेरोमीटर सेंसर से त्वरण के प्रभावों को आजमाने और समाप्त करने के लिए अन्य सेंसर से गणना की जाती है। हालांकि, अगर आप सेंसर की शुरूआत से पहले अपने ऐप को एंड्रॉइड के पुराने संस्करणों पर चलाना चाहते हैं। TYPE_GRAVITY तो आपको स्वयं को कार्य करने की आवश्यकता होगी।

0

प्रसंभात्य getOrientation(), उपयोग के लिए कहा दिया जाता है:

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

हालांकि, अगर आप Android के कार्यान्वयन को देखो, यह वास्तव में

है
azimuth = atan2(Ey, Ny) 

यह आपको -180 और 180 की सीमा में मूल्य भी देगा।

नीचे उत्तर के लिए

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; 
} 
संबंधित मुद्दे