Android计步器算法实现(2)
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−1datan>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)相关推荐
- Android计步器算法实现
最近在研究惯性导航和其他导航算法的融合,顺手把计步.步长等一堆算法写成类了,舒服~ 这篇文章我不会具体的讲解实现原理,有兴趣研究的朋友直接看我写的计步算法实现和步长计算. Android系统有自带的计 ...
- 一种基于陀螺仪传感器的准确计步器算法
一种基于陀螺仪传感器的准确计步器算法 A Gyroscope Based Accurate Pedometer Algorithm 作者:Sampath Jayalath.Nimsiri Abhaya ...
- Android计步器的实现(1)
最近项目中要加一个计步器的功能,Github上搜索一堆,都是bug漫天飞(微信也有bug^_^,关于bug的原因有:异常开关机.调整手机时间. 正常开关机.跨天问题,这几种原因复合在一起更容易造成计步 ...
- # Pedometer 计步器算法简介
Pedometer 计步器算法简介 以Android 6.0 M 为例,它为Pedometer计步器(以下简称Pedo)提供了两颗虚拟传感器(以下用sensor):Step Counter和Step ...
- android 计步器
我们经常会看到微信 QQ 以及其他一些运动app里面都有一个计步功能,那它是怎么实现的呢? 今天我们就来实现一下,以下代码都是从一个整体项目中抽离出来的,为了理解简单方便我把UI部分数据保存部分全部都 ...
- android 计步器 开发,Android计步器开发
本文只赘述Android计步器开发里计步的原理. 在Android4.4版本之后,新增了STEP_COUNTER和STEP_DECTECTOR STEP_COUNTER表示自从开机以来,你走的步数累计 ...
- Android计步器的实现(2)
上一篇见: Android计步器的实现(1) 2.时间戳工具 public abstract class Util4Pedometer {/*** @return milliseconds since ...
- Android计步器小Demo
描述 android计步器的实现,自定义的一个弧形进度条,记步通过手机的传感器来实现,也就是说不支持传感器的机子(应该很老的了吧)就没有效果.看看效果图: 自定义View public class S ...
- 计步器算法简述和模块使用
计步器作为算法功能模块, 本应该和业务模块分离, 解耦合. 这样也可以更加专注于算法的升级. 我来简述一下计步器算法的实现原理, 给大家一些启发. 1. 算法 对于所有计步器而言, 早期必然是使用加速 ...
最新文章
- VTK:图片之ImageThreshold
- MySQL高级 - SQL优化 - group by 优化
- Django里面是文件静态化的方法
- vue echarts动态数据定时刷新
- 褪去华衣 裸视学习 - 机器学习 - 转
- macOS 开发 - Command Line Tool 命令行工具
- python spss写论文_自从用 spss 写了论文。。。。
- 4412开发板项目实战-云服务器智能家居
- 三种男性最需要的营养素
- CAD数据坐标系统问题
- 给初入测试/开发程序员的几点建议,把困难当做猎物......
- Win10添加右键打开cmd和Powershell窗口(管理员/非管理员)
- 【conda环境下如何安装rdkit】
- IBHLink S7++ 模块 AEG 调功器 Thyro-S 1S 400-100 HRL1
- 动词语气(虚拟语气)
- 基于RFM模型实现的零售精准营销响应预测系统
- Ruby 简单入门(二)
- 为地震中丧身的同胞哀悼
- ajax 使用gbk编码格式,jQuery ajax提交中文编码(gbk)解决
- GRBL v1.1版本命令说明