Android计步器算法实现(2)

  • 前言
  • 算法实现的意义
    • 现实原因
    • 优缺点
  • 算法原理
    • 运动状态判断
    • 计步原理
    • 步长计算原理
  • Java实现
  • 补充
    • 观测点的作用
    • 数据的存放方式

前言

在之前我也写过两篇关于计步器的博客,一篇介绍原理,一篇给出封装好的类型。这里是新算法Android工程github地址。

之前的那套算法自己平时玩玩其实还是没问题的,但是在大工程里面运用遇到了一些不得不优化的问题:

  • 定时器太耗资源
  • 每次计算的时间间隔短(几十毫秒),但计算量大

因此针对这两个问题需要再一次修改算法。新算法相比过去优点在于:

  • 大大降低计算量
  • 不需要使用计时器,减小系统负担

算法实现的意义

现实原因

对于大部分的开发者来说,自己写一套计步器算法是非常没有必要的,调用系统自带的计步器即可。那么我又为什么要自己动手呢?实际是有几个原因的:

  • 自己实现的算法有利于做步长估计
  • 鉴于国内Android手机碎片化严重,不同厂商的手机计步表现差异(下面说明)很大,自己实现的算法便于控制差异性

不同厂商的手机计步器主要差异在这几个方面:

  • 计步器的敏感度,较敏感的算法会把手部的轻微抖动也算成走步,敏感度低的算法则计步丢失会比较严重
  • 计步器每次返回的步数,因为Android手机用的是系统回调的方式而不是查询的方式,因此有的手机是走一步就回调(大部分Android手机),而有的则是走四步回调(苹果手机和少部分Android手机),还有些非固定数值返回的手机就不提了
  • 计步器判决人在行走的时间长短,所有系统级算法都会等人走了一定步数后才能确定人确实在行走,然后才开始回调(此时会把之前走的步数一起返回)。大部分手机算法需要等十步左右才开始,华为手机一般要等二三十步

优缺点

自己实现算法的意义就是希望避开上面说的差异性问题,实现算法上的统一。个人认为优点在于:

  • 多了步长估计
  • 每走一步就提示步态变化

缺点在于:

  • 精度略微降低(每百步差一步左右)
  • 没有步行的预判时间

算法原理

运动状态判断

判断人的运动状态。简单做,利用滑动窗求方差,方差超过一定门限就判断在运动中。

计步原理

计步算法原理实际上相当简单。我们来看走路时候的波形(加速度数据):

这个是处理过的信号,但是真实信号实际上就是这么简单。我们只要把每个峰值从中抓出来然后加点判决门限就大功告成了。

步长计算原理

这里用到了两个参数H和T,见图:

H对应峰谷值的差值,T对应两个波谷之间的时间差。带入公式得到步长:
length=0.37−0.000155T+0.1638H;length=0.37-0.000155T+0.1638\sqrt{H};length=0.37−0.000155T+0.1638H​;
公式还不是最优的,需要验证改进。

Java实现

public class StepController {//状态:静止public static int STAY=0;//状态:运动public static int MOVE=1;//运动状态判断数组private float[] stateValue=new float[20];//加速度数组private float[] Accs=new float[100];//每次传入加速度的时间,省去定时器private long[] time=new long[100];//数组下标private int count=0;//行走步数private int Step=0;//步长private float Length=0;//行进距离private float Distance=0;//状态private int State=STAY;//回调public interface StepCallback{void refreshStep(int step,float stepLength,float distance);}private StepCallback callback;public StepController(StepCallback callback){this.callback=callback;}//传入加速度和时间public void refreshAcc(float[] values,long timestamp){Accs[count]=(float) Math.sqrt(values[0]*values[0]+values[1]*values[1]+values[2]*values[2]);time[count]=timestamp;//检查运动状态stateValue[count%20]=Accs[count];checkState();//设置检测点,检测点的值为到最新数据的距离,如果太近了将无法确定其为波峰波谷//检测点的作用是来减少计算量final int ckpoint = Accs.length / 5;//运动状态判断加上判断检测点是否有波谷存在if (State==MOVE&&Accs[(count-ckpoint+Accs.length)%Accs.length]<Accs[(count-ckpoint+Accs.length+1)%Accs.length]&&Accs[(count-ckpoint+Accs.length)%Accs.length]<Accs[(count-ckpoint+Accs.length-1)%Accs.length]) {//求均值float ave = 0;for (int i = 0; i < Accs.length; ++i) {ave += Accs[i];}ave /= Accs.length;//调整数组顺序,把新数据放在前面,同时把数据减去均值float[] data = new float[Accs.length];for (int i = 0, j = count; i < data.length; ++i, --j) {if (j < 0) j += data.length;data[i] = Accs[j] - ave;}//寻找波峰波谷float[] sign = new float[Accs.length];for (int i = 1; i < Accs.length - 1; ++i) {if (Math.abs(data[i]) > 0.8 &&Math.abs(data[i]) > Math.abs(data[i - 1]) &&Math.abs(data[i]) > Math.abs(data[i + 1])) {if (data[i] > 0) {sign[i] = 1;} else {sign[i] = -1;}}}//取相邻波峰中的最大值和相邻波谷中的最小值for (int i = 1; i < sign.length - 1; ) {int index = i;while (++i < sign.length - 1 &&(sign[i] == 0 || sign[i] == sign[index])) {if (sign[i] != 0 && Math.abs(data[i]) > Math.abs(data[index])) {sign[index] = 0;index = i;} else {sign[i] = 0;}}}//再次判断检测点是否是波谷if (sign[ckpoint] < 0) {int index = ckpoint;//寻找下个波峰while (++index < sign.length && sign[index] == 0) ;if (index < sign.length && sign[index] > 0) {int peak = index;//寻找下个波谷while (++index < sign.length && sign[index] == 0) ;if (index < sign.length && sign[index] < 0) {int valley = index;//计算H和Tfloat H = data[peak] - 0.5f * data[ckpoint] - 0.5f * data[valley];long T = time[(count - ckpoint + time.length) % time.length] - time[(count - valley + time.length) % time.length];//门限判决if (H > 3 && T > 300 && T < 1400) {//步长计算DetectStepLength((int) T, H);++Step;callback.refreshStep(Step,Length,Distance);}}}}}if (++count==Accs.length) count=0;}//运动状态判断private void checkState(){float ave=0;float var=0;//求均值for (int i=0;i<stateValue.length;++i){ave+=stateValue[i];}ave/=stateValue.length;//求方差for (int i=0;i<stateValue.length;++i){var+=(stateValue[i]-ave)*(stateValue[i]-ave);}var/=stateValue.length;//状态判决State=var>0.5?MOVE:STAY;}//步长计算,该公式利用最小二乘法推导出,有一定可信性private void DetectStepLength(int time,float f){float steplength=0.35f-0.000155f*time+0.1638f*(float) Math.sqrt(f);this.Length=(this.Length+steplength)/2;Distance+=steplength;}
}

补充

注释写的比较简单,这里根据评论就多补充一点。

观测点的作用

为什么要设置一个观测点?首先在判断一个值是否为峰值时,我们知道峰值满足:
{datan>datan−1datan>datan+1\begin{cases} \quad data_n>data_{n-1}\\ \quad data_n>data_{n+1} \end{cases}{datan​>datan−1​datan​>datan+1​​
因此对于一组信号来说,新来的信号是无法判断是否为峰值的,因为此时n=0n=0n=0因此没有datan−1data_{n-1}datan−1​,所以至少要等n=1n=1n=1时才可做判断。

我们再看一组数据:

[1,3,2,4,5,2,1]

可以知道这段数据中有两个峰值3和5,然而3只是假峰,我们需要的是真峰5。为了避免被假峰所欺骗,nnn的值就需要再往后挪一挪,因此就有了所谓的观测点(算法中总数为100的数据时定在20的位置上)。

算法第一步在观测点判断是否为峰值(或者谷值),是则进一步判断是否为真峰值(或者真谷值),然后再做下一步判断。

数据的存放方式

关于这一点,我觉得完全没有什么技术含量。还是解释一下吧。

获取信号的时候,我们往往希望每次把最新数据放在数组第一位,然后最老的数据放在数组最后一位。但是这种存放方式每次传入新的数组时都需要调整数组顺序,很浪费计算资源。这样是不可取的,因此我的思路是这样的:

int count=0;
float[] data=new float[100];void fun(float value){data[count]=value;if(count==100) count=0;
}

这种存放方式有种好处,不需要调整数据顺序,每次都把存放最久的数据给覆盖,非常简单。

这种方式的麻烦在于取数据的时候,比如我想要第21位的数据,若是顺序调整好的数组,只需取得data[20]即可。我的方式却需要倒算一次data[count-20],还要冒着count<20,数组溢出的风险。然而也有一种一劳永逸的方法:

data[(100+count-20)%100]

这样一来就没有数组溢出的风险了。当然这样做计算量也增大了,不过实在比调整数组顺序好太多了。

Android计步器算法实现(2)相关推荐

  1. Android计步器算法实现

    最近在研究惯性导航和其他导航算法的融合,顺手把计步.步长等一堆算法写成类了,舒服~ 这篇文章我不会具体的讲解实现原理,有兴趣研究的朋友直接看我写的计步算法实现和步长计算. Android系统有自带的计 ...

  2. 一种基于陀螺仪传感器的准确计步器算法

    一种基于陀螺仪传感器的准确计步器算法 A Gyroscope Based Accurate Pedometer Algorithm 作者:Sampath Jayalath.Nimsiri Abhaya ...

  3. Android计步器的实现(1)

    最近项目中要加一个计步器的功能,Github上搜索一堆,都是bug漫天飞(微信也有bug^_^,关于bug的原因有:异常开关机.调整手机时间. 正常开关机.跨天问题,这几种原因复合在一起更容易造成计步 ...

  4. # Pedometer 计步器算法简介

    Pedometer 计步器算法简介 以Android 6.0 M 为例,它为Pedometer计步器(以下简称Pedo)提供了两颗虚拟传感器(以下用sensor):Step Counter和Step ...

  5. android 计步器

    我们经常会看到微信 QQ 以及其他一些运动app里面都有一个计步功能,那它是怎么实现的呢? 今天我们就来实现一下,以下代码都是从一个整体项目中抽离出来的,为了理解简单方便我把UI部分数据保存部分全部都 ...

  6. android 计步器 开发,Android计步器开发

    本文只赘述Android计步器开发里计步的原理. 在Android4.4版本之后,新增了STEP_COUNTER和STEP_DECTECTOR STEP_COUNTER表示自从开机以来,你走的步数累计 ...

  7. Android计步器的实现(2)

    上一篇见: Android计步器的实现(1) 2.时间戳工具 public abstract class Util4Pedometer {/*** @return milliseconds since ...

  8. Android计步器小Demo

    描述 android计步器的实现,自定义的一个弧形进度条,记步通过手机的传感器来实现,也就是说不支持传感器的机子(应该很老的了吧)就没有效果.看看效果图: 自定义View public class S ...

  9. 计步器算法简述和模块使用

    计步器作为算法功能模块, 本应该和业务模块分离, 解耦合. 这样也可以更加专注于算法的升级. 我来简述一下计步器算法的实现原理, 给大家一些启发. 1. 算法 对于所有计步器而言, 早期必然是使用加速 ...

最新文章

  1. VTK:图片之ImageThreshold
  2. MySQL高级 - SQL优化 - group by 优化
  3. Django里面是文件静态化的方法
  4. vue echarts动态数据定时刷新
  5. 褪去华衣 裸视学习 - 机器学习 - 转
  6. macOS 开发 - Command Line Tool 命令行工具
  7. python spss写论文_自从用 spss 写了论文。。。。
  8. 4412开发板项目实战-云服务器智能家居
  9. 三种男性最需要的营养素
  10. CAD数据坐标系统问题
  11. 给初入测试/开发程序员的几点建议,把困难当做猎物......
  12. Win10添加右键打开cmd和Powershell窗口(管理员/非管理员)
  13. 【conda环境下如何安装rdkit】
  14. IBHLink S7++ 模块 AEG 调功器 Thyro-S 1S 400-100 HRL1
  15. 动词语气(虚拟语气)
  16. 基于RFM模型实现的零售精准营销响应预测系统
  17. Ruby 简单入门(二)
  18. 为地震中丧身的同胞哀悼
  19. ajax 使用gbk编码格式,jQuery ajax提交中文编码(gbk)解决
  20. GRBL v1.1版本命令说明

热门文章

  1. 百度地图上定位自己所在的位置
  2. GAN网络详解(从零入门)
  3. 七年级计算机基本结构,七年级信息技术计算机基本组成和工作原理
  4. 《云计算架构技术与实践》连载(2):1.2 云计算的发展趋势
  5. pDC 与 GetDC() 区别
  6. 【案例】绘制国际象棋棋盘?(turtle 登场)
  7. CSS定位布局流和网络请求引入
  8. 限流的抖音号怎么养?养号方法是什么?
  9. 2020年度个税汇算清缴怎么办理?直接上干货!
  10. windows获取公网IP,记录公网ip脚本和命令