Подтвердить что ты не робот

Как вычислить точное количество шагов ноги с помощью акселерометра в android?

Я разрабатываю некоторые приложения, такие как Runtastic Pedometer, используя алгоритм, но я не получаю никакого сходства между результатами.

мой код выглядит следующим образом:

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

для регистрации датчиков:

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

в алгоритме у меня есть разные уровни чувствительности, как общественная пустота

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

на различном уровне чувствительности мой результат:

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

Я не получаю какой-либо надлежащий образец, чтобы соответствовать требованию. Согласно моему анализу, это приложение использует Sensor.TYPE_MAGNETIC_FIELD для расчета шагов, пожалуйста, дайте мне знать некоторый алгоритм, чтобы я мог удовлетворить требования.

4b9b3361

Ответ 1

Первое, что вам нужно сделать, это решить алгоритм. Насколько я знаю, примерно три способа обнаружить шаги с использованием акселерометров, которые описаны в литературе:

  • Используйте теорему Пифагора для вычисления величины вектора ускорения каждого образца с акселерометра. Фильтр низких частот подает сигнал амплитуды для удаления высокочастотного шума, а затем ищет пики и долины в отфильтрованном сигнале. Возможно, вам придется добавить дополнительные требования для удаления ложных срабатываний. Это, безусловно, самый простой способ обнаружить шаги, это также способ, что большинство, если не все обычные шагомеры, которые вы можете купить в спортивном магазине, работают.

  • Используйте Pythagoras, как в (1), затем запускайте сигнал через FFT и сравнивайте вывод с FFT с известными выходами ходьбы. Это требует от вас доступа к довольно большому количеству данных обучения.

  • Подайте данные акселерометра в алгоритм, который использует подходящую технику машинного обучения, например нейронную сеть или цифровое вейвлет-преобразование. Вы можете, конечно, включить другие датчики в этот подход. Это также требует, чтобы вы имели доступ к довольно большому количеству данных обучения.

Как только вы определитесь с алгоритмом, вы, вероятно, захотите использовать что-то вроде Matlab или SciPy, чтобы протестировать свой алгоритм на вашем компьютере, используя записи, сделанные вами на телефонах Android. Данные акселерометра дампа в файл cvs на вашем телефоне, сделайте запись о количестве шагов, которые представляет файл, скопируйте файл на свой компьютер и запустите свой алгоритм на данных, чтобы узнать, правильно ли он имеет счетчик шагов. Таким образом, вы можете обнаружить проблемы с алгоритмом и исправить их.

Если это звучит сложно, лучший способ получить доступ к хорошему обнаружению шагов - это, вероятно, ждать, пока больше телефонов не придут со встроенным счетчиком шагов, который позволяет KitKat.

Ответ 3

Я использую определение шага в своем инструменте для ходьбы. Я получаю хорошие результаты обнаружения шагов. Я использую achartengine для построения данных акселерометра. Посмотрите здесь. Что я делаю:

  1. Анализ вектора величины для датчика акселерометра.
  2. Установка изменяемого порогового уровня. Когда сигнал от акселерометра выше, я считаю его шагом.
  3. Установка времени неактивного состояния (для пошагового обнаружения) после первого пересечения порога.

Точка 3. рассчитывается:

  • произвольная установка максимального темпа нашей ходьбы (например, 120 ударов в минуту)
  • если 60 ударов в минуту - 1000 мсек на шаг, то 120 ударов в минуту - 500 мсек на шаг
  • акселерометр передает данные с определенной желаемой частотой (SENSOR_DELAY_NORMAL, SENSOR_DELAY_GAME и т.д.). Когда DELAY_GAME: T ~ = 20 мс (это включено в документацию по Android)
  • n - выборки для пропуска (после прохождения порога)
  • n= 500 мсек/т
  • n= 500/20 = 25 (их много. Вы можете настроить это значение).
  • после этого порог становится активным.

Посмотрите на эту картинку: My application

Ответ 4

Одно основное отличие, которое я заметил между вашей реализацией и кодом в проекте grepcode, - это то, как вы регистрируете слушателя.

Ваш код:

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

Их код:

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

Это большая разница. SENSOR_DELAY_NORMAL предназначен для изменения ориентации, и поэтому не так быстро (когда-либо замечал, что требуется некоторое время между вращением устройства и фактическим вращением устройства), потому что это некоторая функциональность, которая не должна быть супер быстрой ( это, вероятно, будет довольно раздражать даже). Скорость, с которой вы получаете обновления, не такая высокая).

С другой стороны, SENSOR_DELAY_FASTEST предназначен для таких вещей, как шагомеры: вы хотите, чтобы данные датчика были как можно быстрее и чаще, поэтому ваши расчеты шагов будут максимально точными.

Попробуйте переключиться на скорость SENSOR_DELAY_FASTEST и снова проверьте! Это должно иметь большое значение.

Ответ 5

Это моя реализация. Это было написано около 1,5-2 лет назад. И я действительно не помню всех этих вещей, которые я написал. Но это сработало. И это сработало для моих нужд.

Я знаю, что это действительно большой класс (некоторые методы удалены), но может быть, это будет полезно. Если нет, я просто удалю этот ответ...

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