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

Posted

技术标签:

【中文标题】如何在android中使用加速度计计算准确的步数?【英文标题】:how to calculate exact foot step count using accelerometer in android? 【发布时间】:2013-12-17 22:17:03 【问题描述】:

我正在使用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 进行步数计算,请告诉我一些算法,以便我可以满足要求。

【问题讨论】:

大多数高质量的计步器使用一种算法来检测以特定模式重复的任何运动。所以当例如以大约相同的频率(通常限于典型的步行跨度)连续检测到至少 3 个步骤,所有三个步骤都被添加。计步器会继续以大致相同的频率添加任何步数。这样可以过滤掉其他设备的运动,并且可以将灵敏度保持在较高的设置而不会产生太多噪音。 有点跑题了,如果您只是针对 API 19(您可能不是),则有一个内置的计步器和计步器软件传感器。我之前测试过,非常准确。也许尝试深入研究源代码? developer.android.com/reference/android/hardware/… 可以肯定的是,您在同一设备上同时测试了这个,对吧?需要回答的主要问题是您的代码和计步器代码之间有什么区别。您知道的任何差异,还是应该完全相同?编辑:刚刚验证onSensorChanged() 与计步器项目相同。 @kevin 我很好奇.. 是计步器软件还是硬件.. 我有三个设备都在运行 kitKat 一个 HTC 一个、一个 nexus 5 和一个 nexus 7 .. 当我打电话时查看传感器类型 step_counter 是否可用,它仅在 nexus 5 上注册为 true。 @erik 是的,我想是硬件,之前没有仔细研究过。 【参考方案1】:

我发现您的实现与 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 速率,然后再次测试!应该会有很大的不同。

【讨论】:

使用 FAST_DELAY 可以提高电池效率,大部分好应用都不使用这个参数。我们也对 FAST_DEAY 进行了测试,但结果没有改善。我在这里缺少磁传感器的概念。【参考方案2】:

这是我的领悟。它是大约 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;
        
    

【讨论】:

谢谢,但结果没有改善,你能解释一下使用磁传感器计数吗?我正在搜索它,但没有找到任何文章。 好吧。我希望我能在两年前写得更好更清晰.. 所以。我记得,我使用了两个峰值作为加速度计和磁力计。我使用画布渲染图形,并找到了一些计算步数的方法。我已经在这里实现了它。我错过了每个传感器的单个峰值,只有两个。还有更多。仅当您将设备放入口袋时,此代码才有效。 即使我移动,我的第一个位置和最后一个位置都是 -1。他们永远不会改变。请帮助【参考方案3】:

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

我希望这可能会有所帮助

【讨论】:

解释一些内容会有所帮助,因为链接可能会在将来的某个时候断开。【参考方案4】:

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

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

    使用毕达哥拉斯在 (1) 中的类似方法,然后通过 FFT 运行信号,并将 FFT 的输出与步行的已知输出进行比较。这需要您访问大量的训练数据。

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

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

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

【讨论】:

【参考方案5】:

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

    加速度计传感器的幅度矢量分析。 设置可变阈值级别。当来自加速度计的信号高于它时,我将其计为一个步骤。 设置第一次跨过阈值后的非活动状态时间(用于检测步数)。

计算点 3.:

任意设置我们步行的最大速度(例如 120bpm) 如果 60bpm - 1000msec 每步,则 120bpm - 500msec 每步 加速度计以特定所需频率(SENSOR_DELAY_NORMAL、SENSOR_DELAY_GAME 等)传递数据。当 DELAY_GAME 时:T ~= 20ms(这包含在 Android 文档中) n - 要省略的样本(通过阈值后) n = 500 毫秒/T n = 500 / 20 = 25(很多。你可以调整这个值)。 之后,阈值变为活动状态

看看这张图:

【讨论】:

您提供的链接已损坏。可以看看吗 更新了链接【参考方案6】:
 public void onSensorChanged(SensorEvent event) 
   if (event.sensor.getType()==Sensor.TYPE_ACCELEROMETER )
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];

            currentvectorSum = (x*x + y*y + z*z);
            if(currentvectorSum < 100 && inStep==false)
                inStep = true;
            
            if(currentvectorSum > 125 && inStep==true)
                inStep = false;
                numSteps++;
                Log.d("TAG_ACCELEROMETER", "\t" + numSteps);
            
        

【讨论】:

以上是关于如何在android中使用加速度计计算准确的步数?的主要内容,如果未能解决你的问题,请参考以下文章

需求-Android开发计步器调研与选型参考

Matlab从移动设备获取加速度数据对步数进行计数

是否有任何可行的算法来计算所有 iOS 设备(带或不带 M7 芯片)上的步数?

智能手机GPS定位算法研究

如何在 TensorFlow 中增加训练步数?

如何使用陀螺仪和加速度计找到行进的距离?