14

Im creare un'applicazione in cui ho bisogno di posizionare un ImageView a seconda dell'Orientamento del dispositivo. Io uso ai valori di un campo magnetico e accelerometro sensori per calcolare l'orientamento del dispositivo conOrientamento bussola Android inaffidabile (filtro passa basso)

SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues) 
SensorManager.getOrientation(rotationMatrix, values); 
double degrees = Math.toDegrees(values[0]); 

mio problema è che il posizionamento del ImageView è molto sensibile alle variazioni di orientamento. Rendere la vista di immagini costantemente saltando sullo schermo. (perché i gradi cambiano)

Ho letto che questo può essere dovuto al fatto che il mio dispositivo è vicino a cose che possono influenzare le letture del campo magnetico. Ma questa non è l'unica ragione per cui sembra.

Ho provato a scaricare alcune applicazioni e ho trovato che "3D compass" e "Compass" rimangono estremamente stabili nelle sue letture (quando si imposta il filtro rumore), vorrei lo stesso comportamento nella mia applicazione.

Ho letto che posso modificare il "rumore" delle mie letture aggiungendo un "Filtro passa basso", ma non ho idea di come implementarlo (a causa della mia mancanza di matematica).

Im sperando che qualcuno possa aiutarmi a creare una lettura più stabile sul mio dispositivo, dove un piccolo movimento verso il dispositivo non influenzerà l'orientamento corrente. In questo momento faccio un piccolo

if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() } 

Per filtrare un po 'nervosa del rumore. Ma la sua non funziona molto bene :)

+0

Ho cancellato la mia risposta "ripetuta", ma ho pensato che avrebbe davvero aiutato le persone perché conosco il dolore di attraversare questo enigma del rumore del sensore su Android. Comunque, per le persone in futuro, ecco la soluzione che funziona per me -> http://stackoverflow.com/a/39276888/1262089 –

risposta

29

Anche se non ho utilizzato la bussola su Android, l'elaborazione di base mostrata di seguito (in JavaScript) funzionerà probabilmente per voi.

Si basa sul filtro passa basso sul accelerometro che è raccomandato dal Windows Phone team con modifiche per soddisfare una bussola (comportamento ciclico ogni 360").

I assumono la lettura della bussola è in gradi, un galleggiante tra 0-360 e l'output dovrebbe essere simile.

si vuole realizzare 2 cose nel filtro:

  1. Se il cambiamento è piccolo, per evitare Gitter, girare a poco a poco a questa direzione.
  2. Se la modifica è grande, per evitare ritardi, girare immediatamente in quella direzione (e può essere annullata se si desidera che la bussola si muova solo in modo uniforme).

Per questo avremo 2 costanti:

  1. Il galleggiante easing che definisce come lisciare il movimento sarà (1 c'è levigante e 0 non è mai l'aggiornamento, il mio valore di default è 0.5). Lo chiameremo SmoothFactorCompass.
  2. La soglia in cui la distanza è abbastanza grande da girare immediatamente (0 è sempre salta, 360 non salta mai, il mio valore predefinito è 30). Lo chiameremo SmoothThresholdCompass.

Abbiamo una variabile salvata attraverso le chiamate, un galleggiante chiamato oldCompass ed è il risultato dell'algoritmo.

Così il defenition variabile è:

var SmoothFactorCompass = 0.5; 
var SmoothThresholdCompass = 30.0; 
var oldCompass = 0.0; 

e la funzione riceve newCompass, e restituisce oldCompass come risultato.

if (Math.abs(newCompass - oldCompass) < 180) { 
    if (Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) { 
     oldCompass = newCompass; 
    } 
    else { 
     oldCompass = oldCompass + SmoothFactorCompass * (newCompass - oldCompass); 
    } 
} 
else { 
    if (360.0 - Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) { 
     oldCompass = newCompass; 
    } 
    else { 
     if (oldCompass > newCompass) { 
      oldCompass = (oldCompass + SmoothFactorCompass * ((360 + newCompass - oldCompass) % 360) + 360) % 360; 
     } 
     else { 
      oldCompass = (oldCompass - SmoothFactorCompass * ((360 - newCompass + oldCompass) % 360) + 360) % 360; 
     } 
    } 
} 

vedo che il problema è stato aperto 5 mesi fa e probabilmente non è rilevante più, ma sono sicuro che altri programmatori potrebbero trovare utile.

Oded Elyada.

+2

Questo mi dà risultati strani quando il 'oldCompass' è circa 0 e il' newCompass 'è 359. –

3

Un filtro passa basso (LPF) blocca i segnali di cambio veloce e
consente solo modifiche lente nei segnali. Ciò significa che qualsiasi piccola variazione improvvisa di verrà ignorata.

Il modo standard per implementare questo nel software consiste nel prendere una media corrente
degli ultimi N campioni e riportare tale valore. Inizia con N a partire da 3 e
continua ad aumentare N fino a quando non trovi una risposta uniforme nella tua app.

Ricordare che quanto più alto si ottiene N, più lentamente la risposta del sistema.

4

Tenete a mente che, per esempio, la media di 350 e 10 non è 180. La mia soluzione:

filtro
int difference = 0; 
for(int i= 1;i <numberOfAngles;i++){ 
    difference += ((angles[i]- angles[0] + 180 + 360) % 360) - 180; 
} 
averageAngle = (360 + angles[0] + (difference/numberOfAngles)) % 360; 
18

Questo passa-basso funziona per gli angoli in radianti. Usa la funzione aggiungi per ogni lettura della bussola, quindi chiama media per ottenere la media.

public class AngleLowpassFilter { 

    private final int LENGTH = 10; 

    private float sumSin, sumCos; 

    private ArrayDeque<Float> queue = new ArrayDeque<Float>(); 

    public void add(float radians){ 

     sumSin += (float) Math.sin(radians); 

     sumCos += (float) Math.cos(radians); 

     queue.add(radians); 

     if(queue.size() > LENGTH){ 

      float old = queue.poll(); 

      sumSin -= Math.sin(old); 

      sumCos -= Math.cos(old); 
     } 
    } 

    public float average(){ 

     int size = queue.size(); 

     return (float) Math.atan2(sumSin/size, sumCos/size); 
    } 
} 

Usa Math.toDegrees() o Math.toRadians() per convertire.

+1

Puoi spiegare perché hai bisogno di tutte quelle funzioni trigonometriche qui? – felixd

+4

Ciao Felix, hai bisogno del trig perché una semplice media numerica di un insieme di angoli non rappresenta sempre il centro. Diciamo che i tuoi angoli in gradi erano [350,10], quindi la tua media sarebbe (350 + 10)/2 = 180 (dove il mezzo è in realtà 0). ** Grazie anche per la tua modifica. ** – SharkAlley

+0

Ok, sì, ha senso.E in qualche modo i trigos risolvono questo problema. :) E ho ragione che 'average()' restituisce l'angolo in gradi? – felixd