web-dev-qa-db-fra.com

Lissage des données d'un capteur

J'ai un capteur 3D qui mesure les données v (x, y, z). J'utilise uniquement les données x et y. Lisser seulement x et y suffirait.

Si j'utilise un journal pour afficher les données, il me montre quelque chose comme ceci: .__ (heure) 0.1 ... (journal des données) x = 1.1234566667 (Heure) 0.2 ... (journal des données) x = 1.1245655666 (Heure) 0.3 ... (journal des données) x = 1.2344445555

Eh bien, les données sont plus exactes en réalité, mais je veux lisser la valeur entre 1.1234 et 1.2344, car pour moi, c’est la même chose, je peux utiliser des entiers pour ne montrer que "x = 1", mais j’ai également besoin des décimales , Je dois montrer ici une sorte de valeur "lissée". 

Quelqu'un a une idée? Je programme en c # mais toutes les fonctions ne fonctionnent pas, je dois donc créer ma propre fonction. 

23
Mworks

Le plus simple est de faire une moyenne mobile de vos données. En d’autres termes, conserver un tableau de lectures de données de capteurs et les faire la moyenne. Quelque chose comme ça (pseudocode):

  data_X = [0,0,0,0,0];

  function read_X () {
      data_X.delete_first_element();
      data_X.Push(get_sensor_data_X());
      return average(data_X);
   }

Cela fait un compromis. Plus le tableau que vous utilisez est grand, plus le résultat sera lisse, mais plus le décalage entre le résultat et la lecture sera important. Par exemple:

                           /\_/\
                        /\/     \_/\
  Sensor reading:  __/\/            \/\
                                       \/\  _/\___________
                                          \/
                              _
                           __/ \_
                       ___/      \__
  Small array:     ___/             \_/\_       _
                                         \   __/ \________
                                          \_/

                                 ____
                              __/    \__
                           __/           \__
  Large array:     _______/                 \__      __
                                               \_   /  \__
                                                 \_/


(forgive my ASCII-ART but I'm hoping it's good enough for illustration).

Si vous voulez une réponse rapide mais un bon lissage de toute façon, vous utiliserez une moyenne pondérée du tableau. Il s’agit essentiellement du traitement du signal numérique (avec DSP majuscule) qui, contrairement à son nom, est plus étroitement lié à la conception analogique. Voici un court article sur Wikipédia (avec de bons liens externes que vous devriez lire si vous voulez emprunter cette voie): http://en.wikipedia.org/wiki/Digital_filter

Voici un code de SO sur un filtre passe-bas qui peut répondre à vos besoins: Logiciel de filtre passe-bas? . Notez que, dans le code de cette réponse, il utilise un tableau de taille 4 (ou ordre 4 dans la terminologie du traitement du signal, car ces filtres sont appelés filtres de quatrième ordre), ils peuvent en fait être modélisés par une équation polynomiale du quatrième ordre: ax ^ 4 + bx ^ 3 + cx ^ 2 + dx).

61
slebetman

Je suis donc venu ici pour tenter de résoudre le même problème (lissage des capteurs dans Android) et voici ce que je propose:

/*
 * time smoothing constant for low-pass filter
 * 0 ≤ α ≤ 1 ; a smaller value basically means more smoothing
 * See: http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
 */
static final float ALPHA = 0.2f;

protected float[] accelVals;

public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
        accelVals = lowPass( event.values, accelVals );

    // use smoothed accelVals here; see this link for a simple compass example:
    // http://www.codingforandroid.com/2011/01/using-orientation-sensors-simple.html
}

/**
 * @see http://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
 * @see http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
 */
protected float[] lowPass( float[] input, float[] output ) {
    if ( output == null ) return input;

    for ( int i=0; i<input.length; i++ ) {
        output[i] = output[i] + ALPHA * (input[i] - output[i]);
    }
    return output;
}

Merci @slebetman de m'avoir orienté vers le lien Wikipedia, qui, après un peu de lecture, m'a amené à l'algorithme de l'article sur les filtres passe-bas de Wikipédia. Je ne jurerai pas que j'ai le meilleur algorithme (ou même le droit!) Mais des preuves anecdotiques semblent indiquer qu'il fait l'affaire.

22
thom_nic

Il existe de nombreuses façons de lisser les données du capteur, de quel type de capteur il s’agit et de l’analogie qui conviendra.

  1. Filtre passe-haut [HPF] et filtres passe-bas [LPF] - comme indiqué dans la réponse sélectionnée.
  2. Algorithme de moyenne mobile -MAA 
  3. Gaely's Algorithmm [Meilleure version pour MAA]
  4. Transformation de Fourier rapide -FFT

Code:

Filtre passe-haut HPF

private float[] highPass(float x, float y, float z) {
    float[] filteredValues = new float[3];
    gravity[0] = ALPHA * gravity[0] + (1 – ALPHA) * x;
    gravity[1] = ALPHA * gravity[1] + (1 – ALPHA) * y;
    gravity[2] = ALPHA * gravity[2] + (1 – ALPHA) * z;
    filteredValues[0] = x – gravity[0];
    filteredValues[1] = y – gravity[1];
    filteredValues[2] = z – gravity[2];
    return filteredValues;   
    }

Filtre passe-bas LPF

private float[] lowPass(float x, float y, float z) {
    float[] filteredValues = new float[3];
    filteredValues[0] = x * a + filteredValues[0] * (1.0f – a);
    filteredValues[1] = y * a + filteredValues[1] * (1.0f – a);
    filteredValues[2] = z * a + filteredValues[2] * (1.0f – a);
    return filteredValues;
    }

MAA-Moyenne Mobile

     private final int SMOOTH_FACTOR_MAA = 2;//increase for better results   but hits cpu bad

     public ArrayList<Float> processWithMovingAverageGravity(ArrayList<Float> list, ArrayList<Float> gList) {
            int listSize = list.size();//input list
            int iterations = listSize / SMOOTH_FACTOR_MAA;
            if (!AppUtility.isNullOrEmpty(gList)) {
                gList.clear();
            }
            for (int i = 0, node = 0; i < iterations; i++) {
                float num = 0;
                for (int k = node; k < node + SMOOTH_FACTOR_MAA; k++) {
                    num = num + list.get(k);
                }
                node = node + SMOOTH_FACTOR_MAA;
                num = num / SMOOTH_FACTOR_MAA;
                gList.add(num);//out put list
            }
            return gList;
        }
3
Jayant Arora

Voici un exemple basé sur la logique de la section MotionEvents du guide Event Handling pour iOS.

float ALPHA = 0.1;

protected float[] lowPass( float[] input, float[] output ) {
    if ( output == null ) return input;

    for ( int i=0; i<input.length; i++ ) {
        output[i] = (input[i] * ALPHA) + (ouptut[i] * (1.0 - ALPHA));
    }
    return output;
}
1
rockfakie

Vous cherchez une vieille question ici, mais si vous êtes en mode .NET, vous pouvez utiliser le RX pour le faire à votre place. 

Par exemple, en utilisant RX avec WebClient.DownloadFileAsync pour calculer une vitesse de téléchargement "lissée":

double interval = 2.0; // 2 seconds
long bytesReceivedSplit = 0;

WebClient wc = new WebClient();
var downloadProgress = Observable.FromEventPattern<
    DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
    h => wc.DownloadProgressChanged += h,
    h => wc.DownloadProgressChanged -= h)
    .Select(x => x.EventArgs);

downloadProgress.Sample(TimeSpan.FromSeconds(interval)).Subscribe(x =>
    {
        Console.WriteLine((x.BytesReceived - bytesReceivedSplit) / interval);
        bytesReceivedSplit = x.BytesReceived;
    });

Uri source = new Uri("http://someaddress.com/somefile.Zip");
wc.DownloadFileAsync(source, @"C:\temp\somefile.Zip");

Évidemment, plus l'intervalle est long, plus le lissage sera important, mais plus vous devrez attendre longtemps pour une première lecture.

0
CatBusStop