web-dev-qa-db-fra.com

comment calculer le nombre de pas exact du pied en utilisant l'accéléromètre sous Android?

Je développe des applications comme le podomètre Runtastic en utilisant le algorithme mais je n’obtiens aucune similitude entre les résultats.

mon code est le suivant:

public void onSensorChanged(SensorEvent event) 
{
        Sensor sensor = event.sensor; 
        synchronized (this)
 {
            if (sensor.getType() == Sensor.TYPE_ORIENTATION) {}
            else {
            int j = (sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? 1 : 0;
                if (j == 1) {
                    float vSum = 0;
                    for (int i=0 ; i<3 ; i++) {
                        final float v = mYOffset + event.values[i] * mScale[j];
                        vSum += v;

                    }
                    int k = 0;
                    float v = vSum / 3;
                    //Log.e("data", "data"+v);

                    float direction = (v > mLastValues[k] ? 1 : (v < mLastValues[k] ? -1 : 0));
                    if (direction == - mLastDirections[k]) {
                        // Direction changed
                        int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
                        mLastExtremes[extType][k] = mLastValues[k];
                        float diff = Math.abs(mLastExtremes[extType][k] - mLastExtremes[1 - extType][k]);

                        if (diff > mLimit) {

                            boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k]*2/3);
                            boolean isPreviousLargeEnough = mLastDiff[k] > (diff/3);
                            boolean isNotContra = (mLastMatch != 1 - extType);

                            if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough && isNotContra) {

                                for (StepListener stepListener : mStepListeners) {
                                    stepListener.onStep();
                                }
                                mLastMatch = extType;
                            }
                            else {
                                Log.i(TAG, "no step");
                                mLastMatch = -1;
                            }
                        }
                        mLastDiff[k] = diff;
                    }
                    mLastDirections[k] = direction;
                    mLastValues[k] = v;
                }
            }
        }
    }

pour enregistrer des capteurs:

mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mSensor = mSensorManager.getDefaultSensor(
                Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(mStepDetector,mSensor,SensorManager.SENSOR_DELAY_NORMAL);

dans l'algorithme, j'ai différents niveaux de sensibilité en tant que vide public

setSensitivity(float sensitivity) {
        mLimit = sensitivity; // 1.97  2.96  4.44  6.66  10.00  15.00  22.50  33.75  50.62
    }

sur différents niveaux de sensibilité, mon résultat est:

sensitivity   rantastic pedometer  my app
10.00           3870                 5500
11.00           3000                 4000
11.15           3765                 4576
13.00           2000                 890
11.30           754                  986

Je ne reçois aucun modèle approprié pour correspondre à l'exigence. Selon mon analyse, cette application utilise Sensor.TYPE_MAGNETIC_FIELD pour le calcul des pas s'il vous plaît laissez-moi savoir un algorithme afin que je puisse répondre à l'exigence.

42
user3056585

La première chose à faire est de choisir un algorithme. Autant que je sache, il existe approximativement trois façons de détecter des étapes à l'aide d'accéléromètres décrites dans la littérature:

  1. Utilisez le théorème de Pythagore pour calculer la magnitude du vecteur d’accélération de chaque échantillon à partir de l’accéléromètre. Filtre passe-bas du signal d'amplitude pour supprimer le bruit haute fréquence, puis rechercher des pics et des creux dans le signal filtré. Vous devrez peut-être ajouter des exigences supplémentaires pour supprimer les faux positifs. C’est de loin le moyen le plus simple de détecter les pas, c’est aussi le moyen utilisé par la plupart, sinon tous les podomètres ordinaires du type de ceux que vous pouvez acheter dans un magasin de sport.

  2. Utilisez "comme dans (1) de Pythagore, puis transmettez le signal à travers une FFT et comparez la sortie de la FFT avec les sorties connues de la marche. Cela nécessite que vous ayez accès à une assez grande quantité de données de formation.

  3. Introduisez les données de l'accéléromètre dans un algorithme utilisant une technique d'apprentissage automatique appropriée, par exemple un réseau de neurones ou une transformation en ondelettes numérique. Vous pouvez bien sûr inclure d'autres capteurs dans cette approche. Cela nécessite également que vous ayez accès à une assez grande quantité de données de formation.

Une fois que vous avez choisi un algorithme, vous voudrez probablement utiliser quelque chose comme Matlab ou SciPy pour tester votre algorithme sur votre ordinateur à l'aide des enregistrements que vous avez effectués sur les téléphones Android. Dump des données de l'accéléromètre sur un cvs fichier sur votre téléphone, enregistrez le nombre d’étapes qu’il représente, copiez le fichier sur votre ordinateur et exécutez votre algorithme sur les données pour voir si le nombre de pas est correct, afin de détecter les problèmes liés à l’algorithme et de les corriger. leur.

Si cela vous semble difficile, le meilleur moyen d'accéder à une détection de pas correcte est probablement d'attendre que davantage de téléphones soient livrés avec le compteur de pas intégré activé par KitKat.

19
Rasmusob

https://github.com/bagilevi/Android-pedometer

j'espère que cela pourrait être utile

18
madhu kotagiri

J'utilise la détection de pas dans mon instrument de marche. J'obtiens de bons résultats de détection de pas. J'utilise achartengine pour tracer les données de l'accéléromètre. Jetez un oeil ici . Ce que je fais:

  1. Analyse du vecteur de magnitude pour le capteur accéléromètre.
  2. Définition d'un niveau modifiable . Quand le signal de l'accéléromètre est au dessus, je le compte comme un pas.
  3. Réglage du temps d'inactivité (pour la détection de pas) après premier franchissement du seuil.

Le point 3. est calculé:

  • réglage arbitraire du tempo maximum de notre marche (par exemple, 120bpm)
  • si 60bpm - 1000msec par étape, alors 120bpm - 500msec par étape
  • l’accéléromètre transmet les données à une fréquence donnée (SENSOR_DELAY_NORMAL, SENSOR_DELAY_GAME, etc.). Lorsque DELAY_GAME: T ~ = 20ms (cela est inclus dans Android)
  • n - échantillons à omettre (après avoir dépassé le seuil)
  • n = 500msec/T
  • n = 500/20 = 25 (beaucoup d’entre eux. Vous pouvez ajuster cette valeur).
  • après cela, le seuil devient actif .

Regardez cette image: My application

5

Ceci est ma réalisation. Il a été écrit il y a environ 1,5 à 2 ans. Et je ne me souviens vraiment pas de tout ce que j'ai écrit. Mais cela a fonctionné. Et cela a bien fonctionné pour mes besoins.

Je sais que c'est vraiment une grosse classe (certaines méthodes sont supprimées), mais peut-être que ça sera utile. Sinon, je vais juste enlever cette réponse ...

public class StepDetector implements SensorEventListener
{
    public static final int MAX_BUFFER_SIZE = 5;

    private static final int Y_DATA_COUNT = 4;
    private static final double MIN_GRAVITY = 2;
    private static final double MAX_GRAVITY = 1200;

    public void onSensorChanged(final SensorEvent sensorEvent)
    {
        final float[] values = sensorEvent.values;
        final Sensor sensor = sensorEvent.sensor;

        if (sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
        {
            magneticDetector(values, sensorEvent.timestamp / (500 * 10 ^ 6l));
        }
        if (sensor.getType() == Sensor.TYPE_ACCELEROMETER)
        {
            accelDetector(values, sensorEvent.timestamp / (500 * 10 ^ 6l));
        }
    }

    private ArrayList<float[]> mAccelDataBuffer = new ArrayList<float[]>();
    private ArrayList<Long> mMagneticFireData = new ArrayList<Long>();
    private Long mLastStepTime = null;
    private ArrayList<Pair> mAccelFireData = new ArrayList<Pair>();

    private void accelDetector(float[] detectedValues, long timeStamp)
    {
        float[] currentValues = new float[3];
        for (int i = 0; i < currentValues.length; ++i)
        {
            currentValues[i] = detectedValues[i];
        }
        mAccelDataBuffer.add(currentValues);
        if (mAccelDataBuffer.size() > StepDetector.MAX_BUFFER_SIZE)
        {
            double avgGravity = 0;
            for (float[] values : mAccelDataBuffer)
            {
                avgGravity += Math.abs(Math.sqrt(
                        values[0] * values[0] + values[1] * values[1] + values[2] * values[2]) -    SensorManager.STANDARD_GRAVITY);
            }
            avgGravity /= mAccelDataBuffer.size();

            if (avgGravity >= MIN_GRAVITY && avgGravity < MAX_GRAVITY)
            {
                mAccelFireData.add(new Pair(timeStamp, true));
            }
            else
            {
                mAccelFireData.add(new Pair(timeStamp, false));
            }

            if (mAccelFireData.size() >= Y_DATA_COUNT)
            {
                checkData(mAccelFireData, timeStamp);

                mAccelFireData.remove(0);
            }

            mAccelDataBuffer.clear();
        }
    }

    private void checkData(ArrayList<Pair> accelFireData, long timeStamp)
    {
        boolean stepAlreadyDetected = false;

        Iterator<Pair> iterator = accelFireData.iterator();
        while (iterator.hasNext() && !stepAlreadyDetected)
        {
            stepAlreadyDetected = iterator.next().first.equals(mLastStepTime);
        }
        if (!stepAlreadyDetected)
        {
            int firstPosition = Collections.binarySearch(mMagneticFireData, accelFireData.get(0).first);
            int secondPosition = Collections
                .binarySearch(mMagneticFireData, accelFireData.get(accelFireData.size() - 1).first - 1);

            if (firstPosition > 0 || secondPosition > 0 || firstPosition != secondPosition)
            {
                if (firstPosition < 0)
                {
                    firstPosition = -firstPosition - 1;
                }
                if (firstPosition < mMagneticFireData.size() && firstPosition > 0)
                {
                    mMagneticFireData = new ArrayList<Long>(
                           mMagneticFireData.subList(firstPosition - 1, mMagneticFireData.size()));
                }

                iterator = accelFireData.iterator();
                while (iterator.hasNext())
                {
                    if (iterator.next().second)
                    {
                        mLastStepTime = timeStamp;
                        accelFireData.remove(accelFireData.size() - 1);
                        accelFireData.add(new Pair(timeStamp, false));
                        onStep();
                        break;
                    }
                }
            }
        }
    }

    private float mLastDirections;
    private float mLastValues;
    private float mLastExtremes[] = new float[2];
    private Integer mLastType;
    private ArrayList<Float> mMagneticDataBuffer = new ArrayList<Float>();

    private void magneticDetector(float[] values, long timeStamp)
    {
        mMagneticDataBuffer.add(values[2]);

        if (mMagneticDataBuffer.size() > StepDetector.MAX_BUFFER_SIZE)
        {
            float avg = 0;

            for (int i = 0; i < mMagneticDataBuffer.size(); ++i)
            {
                avg += mMagneticDataBuffer.get(i);
            }

            avg /= mMagneticDataBuffer.size();

            float direction = (avg > mLastValues ? 1 : (avg < mLastValues ? -1 : 0));
            if (direction == -mLastDirections)
            {
                // Direction changed
                int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
                mLastExtremes[extType] = mLastValues;
                float diff = Math.abs(mLastExtremes[extType] - mLastExtremes[1 - extType]);

                if (diff > 8 && (null == mLastType || mLastType != extType))
                {
                    mLastType = extType;

                    mMagneticFireData.add(timeStamp);
                }
            }
            mLastDirections = direction;
            mLastValues = avg;

            mMagneticDataBuffer.clear();
        }
    }

    public static class Pair implements Serializable
    {
        Long first;
        boolean second;

        public Pair(long first, boolean second)
        {
            this.first = first;
            this.second = second;
        }

        @Override
        public boolean equals(Object o)
        {
            if (o instanceof Pair)
            {
                return first.equals(((Pair) o).first);
            }
            return false;
        }
    }
}
2
mig35

Une différence principale que j'ai remarquée entre votre implémentation et le code dans le projet grepcode est la façon dont vous enregistrez l'écouteur.

Votre code:

mSensorManager.registerListener(mStepDetector,
                                mSensor,
                                SensorManager.SENSOR_DELAY_NORMAL);

Leur code:

mSensorManager.registerListener(mStepDetector,
                                mSensor,
                                SensorManager.SENSOR_DELAY_FASTEST);

C'est une grande différence. SENSOR_DELAY_NORMAL est destiné aux changements d’orientation et n’est donc pas si rapide (vous avez déjà remarqué qu’il faut un certain temps entre la rotation de l’appareil et la rotation réelle de l’appareil? C’est une fonctionnalité qui ne nécessite pas forcément une serait probablement assez ennuyant même). Le taux auquel vous obtenez des mises à jour n'est pas si élevé).

D'autre part, SENSOR_DELAY_FASTEST est destiné à des choses comme les podomètres: vous voulez que les données du capteur soient aussi rapides et fréquentes que possible, afin que vos calculs de pas soient aussi précis que possible.

Essayez de passer à la SENSOR_DELAY_FASTEST taux, et tester à nouveau! Cela devrait faire une grande différence.

2
Jeffrey Klardie