web-dev-qa-db-ja.com

Androidの加速度計を使用して正確な足の歩数を計算する方法は?

algorithm を使用して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ステップ計算については、要件を満たすためにアルゴリズムを教えてください。

42
user3056585

最初に行う必要があるのは、アルゴリズムの決定です。私が知る限り、文献に記載されている加速度計を使用してステップを検出するには、おおまかに言って3つの方法があります。

  1. ピタゴラスの定理を使用して、加速度計からの各サンプルの加速度ベクトルの大きさを計算します。振幅信号をローパスフィルター処理して高周波ノイズを除去し、フィルター処理された信号のピークと谷を探します。誤検知を削除するには、要件を追加する必要がある場合があります。これは歩数を検出する最も簡単な方法であり、スポーツ店で購入できる種類の通常の歩数計ではないにしても、ほとんどすべての方法で動作します。

  2. (1)のようにピタゴラスを使用し、FFTを介して信号を実行し、FFTからの出力を歩行の既知の出力と比較します。そのためには、かなり大量のトレーニングデータにアクセスする必要があります。

  3. 加速度計のデータを、ニューラルネットワークやデジタルウェーブレット変換などの適切な機械学習手法を使用するアルゴリズムに送ります。もちろん、このアプローチに他のセンサーを含めることができます。これには、かなり大量のトレーニングデータにアクセスする必要もあります。

アルゴリズムを決定したら、MatlabやSciPyのようなものを使用して、Android電話。加速度計データをCVSにダンプします。ファイルを携帯電話に保存し、そのファイルが表すステップ数を記録し、ファイルをコンピューターにコピーし、データに対してアルゴリズムを実行して、ステップ数が正しいかどうかを確認します。それら。

これが難しいように思える場合、適切なステップ検出にアクセスするための最善の方法は、おそらくキットカットが有効にする組み込みのステップカウンターを備えた電話機がさらに来るまで待つことです。

19
Rasmusob

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

これが役に立つことを願っています

18
madhu kotagiri

歩行器具でステップ検出を使用しています。 ステップ検出のいい結果が得られます。achartengineを使用して加速度計データをプロットします。 こちら をご覧ください。私がやること:

  1. 加速度センサーの振幅ベクトルの分析。
  2. 変更可能なしきい値レベルの設定。加速度計からの信号がそれを上回っているとき、それをステップとして数えます。
  3. しきい値のfirstを超えた後の非アクティブ状態の時間の設定(ステップ検出用)。

ポイント3.は計算されます:

  • ウォーキングの最大テンポを任意に設定する(例:120bpm)
  • 60bpm-ステップごとに1000msecの場合、120bpm-500msecステップごと
  • 加速度計は、特定の望ましい周波数(SENSOR_DELAY_NORMAL、SENSOR_DELAY_GAMEなど)でデータを渡します。 DELAY_GAMEの場合:T〜= 20ms(これはAndroidドキュメント)に含まれています)
  • n-除外するサンプル(しきい値を超えた後)
  • n= 500msec/T
  • n= 500/20 =25(それらの多く。この値を調整します)。
  • その後、しきい値がアクティブになります

この写真を見てください: My application

これが私の実現です。約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;
        }
    }
}
2
mig35

実装とgrepcodeプロジェクトのコードの間に見た主な違いの1つは、リスナーを登録する方法です。

あなたのコード:

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レートに切り替えて、もう一度テストしてください!それは大きな違いを生むはずです。

2
Jeffrey Klardie