如何在android中使用加速度计计算精确的步数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20324356/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:40:15  来源:igfitidea点击:

how to calculate exact foot step count using accelerometer in android?

androidalgorithmaccelerometer

提问by user3056585

I am developing some application like Runtastic Pedometer using the algorithmbut I am not getting any similarity between the results.

我正在使用该算法开发一些应用程序,例如 Runtastic Pedometer,但结果之间没有任何相似之处。

my code is as follows:

我的代码如下:

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

for registering sensors:

用于注册传感器:

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

in the algorithm i have different levels for sensitivity as public void

在算法中,我有不同的敏感性级别作为公共无效

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

on various sensitivity level my result is:

在各种灵敏度级别上,我的结果是:

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

I am not getting any proper pattern to match with the requirement. As per my analysis this application is using Sensor.TYPE_MAGNETIC_FIELDfor steps calculation please let me know some algorithm so that I can meet with the requirement.

我没有得到任何符合要求的正确模式。根据我的分析,此应用程序Sensor.TYPE_MAGNETIC_FIELD用于计算步数,请告诉我一些算法,以便我满足要求。

回答by Rasmusob

The first thing you need to do is decide on an algorithm. As far as I know there are roughly speaking three ways to detect steps using accelerometers that are described in the literature:

您需要做的第一件事是确定算法。据我所知,文献中描述的使用加速度计检测步数的方法大致分为三种:

  1. Use the Pythagorean theorem to calculate the magnitude of the acceleration vector of each sample from the accelerometer. Low-pass filter the magnitude signal to remove high frequency noise and then look for peaks and valleys in the filtered signal. You may need to add additional requirements to remove false positives. This is by far the simplest way to detect steps, it is also the way that most if not all ordinary pedometers of the sort that you can buy from a sports store work.

  2. Use Pythagoras' like in (1), then run the signal through an FFT and compare the output from the FFT to known outputs of walking. This requires you to have access to a fairly large amount of training data.

  3. Feed the accelerometer data into an algorithm that uses some suitable machine learning technique, for example a neural network or a digital wavelet transform. You can of course include other sensors in this approach. This also requires you to have access to a fairly large amount of training data.

  1. 使用勾股定理计算来自加速度计的每个样本的加速度矢量的大小。对幅度信号进行低通滤波以去除高频噪声,然后在滤波后的信号中寻找波峰和波谷。您可能需要添加其他要求以消除误报。这是迄今为止检测步数的最简单方法,也是您可以从体育用品商店购买的大多数(如果不是全部)普通计步器的工作方式。

  2. 像 (1) 中那样使用毕达哥拉斯,然后通过 FFT 运行信号并将 FFT 的输出与已知的步行输出进行比较。这要求您能够访问相当大量的训练数据。

  3. 将加速度计数据输入到使用一些合适的机器学习技术的算法中,例如神经网络或数字小波变换。您当然可以在这种方法中包含其他传感器。这也要求您能够访问相当大量的训练数据。

Once you have decided on an algorithm you will probably want to use something like Matlab or SciPy to test your algorithm on your computer using recordings that you have made on Android phones. Dump accelerometer data to a cvs file on your phone, make a record of how many steps the file represents, copy the file to your computer and run your algorithm on the data to see if it gets the step count right. That way you can detect problems with the algorithm and correct them.

一旦您决定了算法,您可能会想要使用 Matlab 或 SciPy 之类的工具,使用您在 Android 手机上制作的录音在您的计算机上测试您的算法。将加速度计数据转储到手机上的 cvs 文件中,记录该文件代表的步数,将文件复制到您的计算机并在数据上运行您的算法,以查看步数是否正确。这样你就可以检测算法的问题并纠正它们。

If this sounds difficult, then the best way to get access to good step detection is probably to wait until more phones come with the built-in step counter that KitKat enables.

如果这听起来很困难,那么获得良好步数检测的最佳方法可能是等到更多手机配备 KitKat 启用的内置步数计数器。

回答by madhu kotagiri

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

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

i hope this might be helpfull

我希望这可能会有所帮助

回答by Micha? Dobi Dobrzański

I am using step detection in my walking instrument. I get nice results of step detection.I use achartengineto plot accelerometer data. Take a look here. What I do:

我在我的步行仪器中使用了步数检测。 我得到了很好的步检测结果。我使用achartengine绘制加速度计数据。看看这里。我所做的:

  1. Analysis of magnitude vectorfor accelerometer sensor.
  2. Setting a changeable thresholdlevel. When signal from accelerometer is above it I count it as a step.
  3. Setting the time of inactive state (for step detection) after firstcrossing of the threshold.
  1. 加速度计传感器的幅度矢量分析。
  2. 设置可变的阈值级别。当来自加速度计的信号高于它时,我将其视为一步。
  3. 设置第一次越过阈值后的非活动状态时间(用于步检测)。

Point 3. is calculated:

第 3 点计算:

  • arbitrary setting the maximum tempo of our walking(e.g. 120bpm)
  • if 60bpm - 1000msec per step, then 120bpm - 500msecper step
  • accelerometer passes data with certain desired frequency (SENSOR_DELAY_NORMAL, SENSOR_DELAY_GAME, etc.). When DELAY_GAME: T ~= 20ms(this is included in Android documentation)
  • n- samples to omit (after passing the threshold)
  • n= 500msec / T
  • n= 500 / 20 = 25(plenty of them. You can adjust this value).
  • after that, the threshold becomes active.
  • 任意设置我们行走最大速度(例如 120bpm)
  • 如果 60bpm - 每步1000 毫秒,那么120bpm -每步500 毫秒
  • 加速度计以特定的所需频率(SENSOR_DELAY_NORMAL、SENSOR_DELAY_GAME 等)传递数据。当 DELAY_GAME: T ~= 20ms(这包含在 Android 文档中)
  • n- 要省略的样本(通过阈值后)
  • n= 500 毫秒/T
  • n= 500 / 20 = 25(很多。你可以调整这个值)。
  • 之后,阈值变为 active

Take a look at this picture: My application

看看这张照片: 我的应用程序

回答by Jeffrey Klardie

One main difference I spotted between your implementation and the code in the grepcode project is the way you register the listener.

我发现您的实现与 grepcode 项目中的代码之间的一个主要区别是您注册侦听器的方式。

Your code:

您的代码:

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

Their code:

他们的代码:

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

This is a big difference. SENSOR_DELAY_NORMALis intended for orientation changes, and is therefor not that fast (ever noticed that it takes some time between you rotating the device, and the device actually rotating? That's because this is some functionality that does not need to be super fast (that would probably be pretty annoying even). The rate at which you get updates is not that high).

这是一个很大的不同。SENSOR_DELAY_NORMAL用于方向改变,因此不是那么快(有没有注意到在你旋转设备和设备实际旋转之间需要一些时间?那是因为这是一些不需要超快的功能(这可能会甚至很烦人)您获得更新的速度并不高)。

On the other hand, SENSOR_DELAY_FASTESTis intended for things like pedometers: you want the sensor data as fast and often as possible, so your calculations of steps will be as accurate as possible.

另一方面,SENSOR_DELAY_FASTEST用于计步器之类的东西:您希望传感器数据尽可能快且频繁,因此您的步数计算将尽可能准确。

Try to switch to the SENSOR_DELAY_FASTESTrate, and test again! It should make a big difference.

尝试切换到SENSOR_DELAY_FASTEST速率,并再次测试!它应该有很大的不同。

回答by mig35

This is my realization. It was written about 1.5-2 years ago. And I really don't remember all this stuff that I wrote. But it worked. And it worked good for my needs.

这是我的觉悟。它写于大约 1.5-2 年前。我真的不记得我写的所有这些东西。但它奏效了。它很好地满足了我的需求。

I know that this is really big class (some methods are deleted), but may be it will be helpful. If not, I'll just remove this answer...

我知道这是一个非常大的类(删除了一些方法),但它可能会有所帮助。如果没有,我将删除此答案...

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