Pusula dahil Android telefon yönüne genel bakış


107

Bir süredir kafamı Android yönlendirme sensörlerinin etrafından dolaşmaya çalışıyorum. Anladığımı sanıyordum. Sonra yapmadığımı anladım. Şimdi düşünüyorum da (umarım) tekrar daha iyi hissettim ama yine de% 100 değilim. Bu konudaki düzensiz anlayışımı açıklamaya çalışacağım ve umarım, eğer bölümlerde yanılsam veya boşlukları doldurursam insanlar beni düzeltebilecekler.

0 derece boylamda (ana meridyen) ve 0 derece enlemde (ekvator) durduğumu hayal ediyorum. Bu konum aslında Afrika kıyılarının açıklarında denizde ama benimle birlikte. Telefonumu yüzümün önünde tutuyorum, böylece telefonun altı ayaklarıma işaret ediyor; Kuzeye bakıyorum (Greenwich'e bakıyorum) bu nedenle telefonun sağ tarafı Doğu, Afrika'yı gösteriyor. Bu oryantasyonda (aşağıdaki diyagrama göre) X ekseni Doğuya, Z ekseni Güneye ve Y ekseni gökyüzüne işaret ediyor.

Artık telefondaki sensörler, bu durumda cihazın yönünü (konumunu değil) belirlemenize izin veriyor. Bu kısım her zaman kafamı karıştırdı, çünkü muhtemelen bir şeyin sadece işe yaradığını kabul etmeden önce nasıl çalıştığını anlamak istediğim için. Görünüşe göre telefon yönünü iki farklı tekniğin bir kombinasyonunu kullanarak yapıyor.

Buna geçmeden önce, yukarıda bahsedilen yönde duran 0 derece enlem ve boylamdaki o hayali kara parçasının üzerinde durduğunuzu hayal edin. Ayrıca gözünüzün bağlı olduğunu ve ayakkabılarınızın bir oyun alanı döner kavşağına sabitlendiğini hayal edin. Biri sizi arkaya iterse, öne doğru (Kuzeye doğru) düşersiniz ve düşüşünüzü engellemek için iki elinizi de uzatırsınız. Benzer şekilde, birisi sizi sol omzuna sokarsa, sağ elinize düşersiniz. İç kulağınızda "yerçekimi sensörleri" (youtube klipsi) vardır ; bu, öne / arkaya mı düşüyor, sola / sağa mı düşüyor veya düşüyor (veya yukarı !!). Bu nedenle insanlar, telefonla aynı X ve Z eksenleri etrafında hizalama ve dönüşü algılayabilir.

Şimdi birisinin sizi şimdi Doğu'ya bakacak şekilde döner kavşakta 90 derece döndürdüğünü hayal edin. Y ekseni etrafında döndürülüyorsunuz. Bu eksen farklı çünkü biyolojik olarak tespit edemiyoruz. Açının belli bir miktarda olduğunu biliyoruz, ancak gezegenin manyetik Kuzey kutbuna göre yönü bilmiyoruz. Bunun yerine harici bir alet kullanmalıyız ... manyetik bir pusula. Bu, hangi yöne baktığımızı belirlememizi sağlar. Aynısı telefonumuz için de geçerlidir.

Artık telefonun 3 eksenli ivmeölçeri de var. Bende HAYIR vargerçekte nasıl çalıştıklarına dair bir fikriniz var ama benim görselleştirme şeklim, yerçekimini gökyüzünden düşen sabit ve tekdüze 'yağmur' olarak hayal etmek ve yukarıdaki şekildeki eksenleri, içinden geçen yağmur miktarını algılayabilen tüpler olarak hayal etmektir. Telefon dik tutulduğunda tüm yağmur Y 'tüpünden' akacaktır. Telefon, ekranı gökyüzüne bakacak şekilde kademeli olarak döndürülürse, Y'den geçen yağmur miktarı sıfıra düşerken, Z'den geçen hacim, maksimum yağmur miktarı akana kadar sürekli olarak artacaktır. Benzer şekilde, telefonu yan tarafına yatırırsak, X tüpü sonunda maksimum miktarda yağmuru toplayacaktır. Bu nedenle telefonun yönüne bağlı olarak 3 borudan akan yağmur miktarını ölçerek yönünü hesaplayabilirsiniz.

Telefon ayrıca normal bir pusula gibi davranan elektronik bir pusulaya sahiptir - "sanal iğnesi" manyetik kuzeyi gösterir. Android, bu iki sensörden gelen bilgileri birleştirir, böylece bir SensorEventtanesi TYPE_ORIENTATIONoluşturulduğunda values[3]dizinin
değerleri [0] vardır: Azimut - (manyetik kuzeyin doğusunu gösteren pusula)
değerleri [1]: Pitch, x ekseni etrafında dönüş (telefon öne veya arkaya eğilerek)
değerleri [2]: Döndürme, y ekseni etrafında dönme (telefonun sol veya sağ tarafına eğilmesi)

Bu yüzden bence (yani bilmiyorum) Android'in üçüncü ivmeölçeri okumak yerine azimutu (pusula yönü) vermesinin nedeni pusula yönünün sadece daha kullanışlı olmasıdır. Onlar şimdi sensör bu tür kullanımdan kaldırıldı neden sizin için sistemli bir dinleyici kayıt gerekmez görünüyor emin değilim SensorEventÇeşidi s TYPE_MAGNETIC_FIELD. Olayın value[]dizisinin, daha sonra SensorManger.getRotationMatrix(..)yönteme iletilecek bir rotasyon matrisi (aşağıya bakın) elde etmek için SensorManager.getOrientation(..)yönteme aktarılması gerekir . Android ekibinin neden kullanımdan kaldırıldığını bilen var mı Sensor.TYPE_ORIENTATION? Verimlilik meselesi mi? Benzer bir soruya yapılan yorumlardan birinde ima edilen şey budur, ancak yine de farklı türde bir dinleyici kaydetmeniz gerekir.geliştirme / örnekler / Compass / src / com / example / android / compass / CompassActivity.java örneği.

Şimdi rotasyon matrisinden bahsetmek istiyorum. (En çok emin olmadığım yer burası) Yani yukarıda Android belgelerinden üç rakam var, onlara A, B ve C diyeceğiz.

A = SensorManger.getRotationMatrix (..) yöntemi şekli ve Dünyanın koordinat sistemini temsil eder

B = SensorEvent API tarafından kullanılan koordinat sistemi.

C = SensorManager.getOrientation (..) yöntem şekli

Anladığım kadarıyla, benim anladığım kadarıyla, A, gezegendeki konumların isteğe bağlı (yükseklik) bir (enlem, boylam) çifti olarak verildiğini varsaydığım "dünyanın koordinat sistemini" temsil ediyor. X "doğuya giden " koordinat, Y "kuzeye giden" koordinattır. Z gökyüzünü gösterir ve rakımı temsil eder.

Telefonların koordinat sistemi şekil B'de sabittir. Y ekseni her zaman üste işaret eder. Rotasyon matrisi telefon tarafından sürekli olarak hesaplanıyor ve ikisi arasında haritalamaya izin veriyor. Öyleyse, rotasyon matrisinin B'nin koordinat sistemini C'ye dönüştürdüğünü düşünmekte haklı mıyım? Dolayısıyla, SensorManager.getOrientation(..)yöntemi çağırdığınızda values[], şekil C'ye karşılık gelen değerlere sahip diziyi kullanırsınız.Telefon gökyüzüne doğrultulduğunda, döndürme matrisi kimlik matrisidir (matris matematiksel olarak 1'in eşdeğeri), yani cihaz hizalandığında eşleştirme gerekli değildir. dünyanın koordinat sistemi ile.

Tamam. Sanırım şimdi dursam iyi olur. Daha önce de söylediğim gibi, umarım insanlar bana nerede batırdığımı veya insanlara yardım ettiğimi söyler (veya insanların kafaları daha da karışır!)


25
Bu soruyu gerçekten beğendim. Cevap veremem ama hoşuma gitti.
Octavian A. Damiean

4
Tim, hiç cevap aldın mı? Ben de aynı şekilde kafamı kaşıyordum. Bu, şimdiye kadar gördüğüm en kötü belgelenmiş API'lerden biridir.
Pierre-Luc Paour

Gerçekten korkmuyorum. Devam etmeliydim. Bir gün bu konuya döneceğim.
Tim

1
Burada da neredeyse aynı soru mu var? Ve Tepki, çözüm de. Yapılan kodum kamu Github at.

Merak ettiğim aynı şey, bir android cihaza bir pusula uyguladım ve internetten yardım aldıysam düzgün çalışıyor, iyi çalışıyor ama kafa karıştırıcı olan şey ... Varsayalım cihazım yer yüzünde bana doğru ve kuzeyi gösteriyor şimdi cep telefonumu kaldırıyorum ve dikey olarak başımın üzerine koyuyorum ve yüz hala bana dönük. Öncelikle iğne yönünü ve nedenini değiştirmelidir. Düşündüğüm gibi, yönümü değiştirmediğimden değil, uygulamamda ve indirdiğim diğer tüm uygulamalarda değişiyor. Biri nedenini açıklayabilir mi?
Syed Raza Mehdi

Yanıtlar:


26

Bir Ekran Dönüşü Başka Bir Makaleyi Hak Ediyor makalesine göz atmak isteyebilirsiniz . Rotasyon matrisine neden ihtiyaç duyduğunuzu açıklıyor.

Özetle, telefonun sensörleri, cihaz döndürüldüğünde bile her zaman aynı koordinat sistemini kullanır.

Tek bir yöne kilitlenmemiş uygulamalarda, cihazı döndürdüğünüzde ekran koordinat sistemi değişir. Böylece, cihaz varsayılan görünüm modundan döndürüldüğünde, sensör koordinat sistemi artık ekran koordinat sistemi ile aynı değildir. Bu durumda rotasyon matrisi, A'yı C'ye dönüştürmek için kullanılır (B her zaman sabit kalır).

İşte size nasıl kullanılabileceğini gösteren bir kod parçacığı.

SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);

// Register this class as a listener for the accelerometer sensor
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                    SensorManager.SENSOR_DELAY_NORMAL);
// ...and the orientation sensor
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                    SensorManager.SENSOR_DELAY_NORMAL);

//...
// The following code inside a class implementing a SensorEventListener
// ...

float[] inR = new float[16];
float[] I = new float[16];
float[] gravity = new float[3];
float[] geomag = new float[3];
float[] orientVals = new float[3];

double azimuth = 0;
double pitch = 0;
double roll = 0;

public void onSensorChanged(SensorEvent sensorEvent) {
    // If the sensor data is unreliable return
    if (sensorEvent.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE)
        return;

    // Gets the value of the sensor that has been changed
    switch (sensorEvent.sensor.getType()) {  
        case Sensor.TYPE_ACCELEROMETER:
            gravity = sensorEvent.values.clone();
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            geomag = sensorEvent.values.clone();
            break;
    }

    // If gravity and geomag have values then find rotation matrix
    if (gravity != null && geomag != null) {

        // checks that the rotation matrix is found
        boolean success = SensorManager.getRotationMatrix(inR, I,
                                                          gravity, geomag);
        if (success) {
            SensorManager.getOrientation(inR, orientVals);
            azimuth = Math.toDegrees(orientVals[0]);
            pitch = Math.toDegrees(orientVals[1]);
            roll = Math.toDegrees(orientVals[2]);
        }
    }
}

4
sadece azimut, eğim ve yuvarlanmanın, kullanımdan kaldırılan OrientationSensor'dan gelenle aynı OLMADIĞINI belirtin. orientation[0] = orientation[0] >= 0 ? orientation[0]: orientation[0] + 360;azimutu normalleştirecek ve if (orientation[1] <= -90) { orientation[1] += (-2*(90+orientation[1])); } else if(orientation[1] >= 90){ orientation[1] += (2*(90 - orientation[1])); }perdeyi normalleştirecek
Rafael T

@RafaelT ve roll'u normalleştirmek? Yoksa bu mantıklı değil mi?
Matthias

@RafaelT: Azimut normalleştirmenizin etkisi var gibi görünüyor: değerler [-180,180] 'den [0, 360]' a gidiyor. Ama aldığım perde değerleri zaten [-90,90] 'dır, bu yüzden önerdiğiniz normalizasyonun hiçbir etkisi yok.
Matthias

Kontrol ettikten sonra (yerçekimi! = Null && geomag! = Null), tableti nasıl hareket ettirirsem de geomag değerinin her zaman 0 olması ne anlama geliyor? Geomag sensörü olmayan bir tablet olabilir mi?
İleri

3

Yuvarlanma, yerçekiminin bir fonksiyonudur, 90 derecelik bir yuvarlanma, tüm yerçekimini x kaydına koyar.

Pitch aynıdır, 90 derecelik bir artış, yerçekiminin tüm bileşenini y yazmacına koyar.

Yaw / Heading / azimut'un yerçekimi üzerinde hiçbir etkisi yoktur, DAİMA yer çekimine dik açıdadır, bu nedenle yerçekimiyle hangi yöne bakarsanız bakın ölçülemez olacaktır.

Bu yüzden değerlendirmek için bir pusulaya ihtiyacınız var, belki bu mantıklı olabilir?



0

Bu sorunu yaşıyordum, bu yüzden farklı yönlerde neler olduğunu planladım. Cihaz yatay bir şekilde monte edilmişse, örneğin bir arabaya monte edilmişse, pusuladan gelen 'dereceler' 0-275'ten (saat yönünde) 269'un (batı ile kuzey arasında) üzerinde çalışıyor gibi görünür, -90'dan 0'a kadar geriye doğru sayılır, o zaman 0'dan 269'a ilerleyen. 270, -90 olur

Hâlâ manzarada ama cihaz sırt üstü yatarken sensörüm 0-360 veriyor. ve portre modunda 0-360 hem sırtüstü hem de dikey olarak ayakta duruyor.

Umarım birine yardımcı olur

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.