原文:http://blog.csdn.net/gf771115/article/details/52121489

计步模块接触了一年多,最近又改需求了,所以又换了全新的统计步数的方法,整理一下吧。

在iPhone5s以前机型因为没有陀螺仪的存在,所以需要用加速度传感器来采集加速度值信息,然后根据震动幅度让其加入踩点数组并过滤,获取自己需要的步数数据。

直接上代码吧:

首先需要一个步数的model如下:

#import <Foundation/Foundation.h>@interface VHSSteps : NSObject
//步数模型
@property(nonatomic,strong) NSDate *date;@property(nonatomic,assign) int record_no;@property(nonatomic, strong) NSString *record_time;@property(nonatomic,assign) int step;//g是一个震动幅度的系数,通过一定的判断条件来判断是否计做一步
@property(nonatomic,assign) double g;@end

然后是如何获取步数,首先判断传感器是否可用

   //加速度传感器self.motionManager = [[CMMotionManager alloc] init];// 检查传感器到底在设备上是否可用if (!self.motionManager.accelerometerAvailable) {return;} else {// 更新频率是100Hz//以pull方式获取数据self.motionManager.accelerometerUpdateInterval = 1.0/40;}

可用的话开始实现统计步数的算法

 @try{//如果不支持陀螺仪,需要用加速传感器来采集数据if (!self.motionManager.isAccelerometerActive) {//  isAccelerometerAvailable方法用来查看加速度器的状态:是否Active(启动)。// 加速度传感器采集的原始数组if (arrAll == nil) {arrAll = [[NSMutableArray alloc] init];}else {[arrAll removeAllObjects];}/*1.push方式这种方式,是实时获取到Accelerometer的数据,并且用相应的队列来显示。即主动获取加速计的数据。*/NSOperationQueue *queue = [[NSOperationQueue alloc] init];[self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){if (!self.motionManager.isAccelerometerActive) {return;}//三个方向加速度值double x = accelerometerData.acceleration.x;double y = accelerometerData.acceleration.y;double z = accelerometerData.acceleration.z;//g是一个double值 ,根据它的大小来判断是否计为1步.double g = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) - 1;//将信息保存在步数模型中VHSSteps *stepsAll = [[VHSSteps alloc] init];stepsAll.date = [NSDate date];//日期NSDateFormatter *df = [[NSDateFormatter alloc] init] ;df.dateFormat  = @"yyyy-MM-dd HH:mm:ss";NSString *strYmd = [df stringFromDate:stepsAll.date];df = nil;stepsAll.record_time =strYmd;stepsAll.g = g;// 加速度传感器采集的原始数组
                [arrAll addObject:stepsAll];// 每采集10条,大约1.2秒的数据时,进行分析if (arrAll.count == 10) {// 步数缓存数组NSMutableArray *arrBuffer = [[NSMutableArray alloc] init];arrBuffer = [arrAll copy];[arrAll removeAllObjects];// 踩点数组NSMutableArray *arrCaiDian = [[NSMutableArray alloc] init];//遍历步数缓存数组for (int i = 1; i < arrBuffer.count - 2; i++) {//如果数组个数大于3,继续,否则跳出循环,用连续的三个点,要判断其振幅是否一样,如果一样,然并卵if (![arrBuffer objectAtIndex:i-1] || ![arrBuffer objectAtIndex:i] || ![arrBuffer objectAtIndex:i+1]){continue;}VHSSteps *bufferPrevious = (VHSSteps *)[arrBuffer objectAtIndex:i-1];VHSSteps *bufferCurrent = (VHSSteps *)[arrBuffer objectAtIndex:i];VHSSteps *bufferNext = (VHSSteps *)[arrBuffer objectAtIndex:i+1];//控制震动幅度,,,,,,根据震动幅度让其加入踩点数组,if (bufferCurrent.g < -0.12 && bufferCurrent.g < bufferPrevious.g && bufferCurrent.g < bufferNext.g) {[arrCaiDian addObject:bufferCurrent];}}//如果没有步数数组,初始化if (nil == self.arrSteps) {self.arrSteps = [[NSMutableArray alloc] init];self.arrStepsSave = [[NSMutableArray alloc] init];}// 踩点过滤for (int j = 0; j < arrCaiDian.count; j++) {VHSSteps *caidianCurrent = (VHSSteps *)[arrCaiDian objectAtIndex:j];//如果之前的步数为0,则重新开始记录if (self.arrSteps.count == 0) {//上次记录的时间lastDate = caidianCurrent.date;// 重新开始时,纪录No初始化record_no = 1;record_no_save = 1;// 运动识别号NSTimeInterval interval = [caidianCurrent.date timeIntervalSince1970];NSNumber *numInter = [[NSNumber alloc] initWithDouble:interval*1000];long long llInter = numInter.longLongValue;//运动识别idself.actionId = [NSString stringWithFormat:@"%lld",llInter];self.distance = 0.00f;self.second = 0;self.calorie = 0;self.step = 0;self.gpsDistance = 0.00f;self.agoGpsDistance = 0.00f;self.agoActionDistance = 0.00f;caidianCurrent.record_no = record_no;caidianCurrent.step = self.step;[self.arrSteps addObject:caidianCurrent];[self.arrStepsSave addObject:caidianCurrent];}else {int intervalCaidian = [caidianCurrent.date timeIntervalSinceDate:lastDate] * 1000;// 步行最大每秒2.5步,跑步最大每秒3.5步,超过此范围,数据有可能丢失int min = 259;if (intervalCaidian >= min) {if (self.motionManager.isAccelerometerActive) {//存一下时间lastDate = caidianCurrent.date;if (intervalCaidian >= ACCELERO_START_TIME * 1000) {// 计步器开始计步时间(秒)self.startStep = 0;}if (self.startStep < ACCELERO_START_STEP) {//计步器开始计步步数 (步)
                                        self.startStep ++;break;}else if (self.startStep == ACCELERO_START_STEP) {self.startStep ++;// 计步器开始步数// 运动步数(总计)self.step = self.step + self.startStep;}else {self.step ++;}//步数在这里NSLog(@"步数%d",self.step);int intervalMillSecond = [caidianCurrent.date timeIntervalSinceDate:[[self.arrSteps lastObject] date]] * 1000;if (intervalMillSecond >= 1000) {record_no++;caidianCurrent.record_no = record_no;caidianCurrent.step = self.step;[self.arrSteps addObject:caidianCurrent];}// 每隔100步保存一条数据(将来插入DB用)VHSSteps *arrStepsSaveVHSSteps = (VHSSteps *)[self.arrStepsSave lastObject];int intervalStep = caidianCurrent.step - arrStepsSaveVHSSteps.step;// DB_STEP_INTERVAL 数据库存储步数采集间隔(步) 100步if (self.arrStepsSave.count == 1 || intervalStep >= DB_STEP_INTERVAL) {//保存次数record_no_save++;caidianCurrent.record_no = record_no_save;[self.arrStepsSave addObject:caidianCurrent];// 备份当前运动数据至文件中,以备APP异常退出时数据也不会丢失// [self bkRunningData];
                                        }}}// 运动提醒检查// [self checkActionAlarm];
                        }}}}];}}@catch (NSException * e) {NSLog(@"Exception: %@", e);return;}

然后iPhone 5s出现了, 增加了 M7 运动协处理器,也带来了CMStepCounter类,从此我们就不用自己计算步数了,只要直接读取就好。

  首先还是要检测协处理器是否可用

    if (!([CMStepCounter isStepCountingAvailable] || [CMMotionActivityManager isActivityAvailable])) {NSString *msg = @"demo只支持iPhone5s以上机型.";UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Opps!"message:msgdelegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil];[alert show];}

然后才是获取步数的方法,主要有两种:

计步 第一种方法
     
     startStepCountingUpdatesToQueue:updateOn:withHandler:
     
     开始分发当前步数计数数据到第三方应用
     
     - (void)startStepCountingUpdatesToQueue:(NSOperationQueue *)queue updateOn:(NSInteger)stepCounts withHandler:(CMStepUpdateHandler)handler
     Parameters
     
     queue
     
     被指定执行特定的handler块的操作队列。第三方可以指定一个定制队列或者使用操作队列协助app的主线程。该参数不能为nil
     
     stepCounts
     
     记录的步伐数据,达到该数值去执行handler块。该数值必须大于0
     
     handler
     
     该块在步伐计数达到或超出数值时会被执行,该参数不能为nil。更多块方法信息参考CMStepQueryHandler。
     
     Discussion
     
     该方法实现对用户步伐数据的追踪,并周期性地唤起块方法去分发结果。当第三方调用了该方法,步伐计数器会重置当前步伐数为0,并开始计数。每次计数到达指定的步伐数时,会执行指定的handler块方法。比如,当设定stepCounts为100时,会在100,200,300等数目时发送更新,激活该块方法。每次发送到该块方法的步伐数目都是从你调用该方法开始的步伐数目总和。
     
     每次超过设定步数值时,指定的处理程序块handler会被执行。如果当超过设定值时第三方应用处在被挂起的状态,那程序块也不会被执行。当第三方应用被唤醒,程序块也不会执行,直到再次超过设定步数值。
     
     可以调用stopStepCountingUpdates方法去停止分发步数计数,当然当步数计数对像被销毁的时候,分发过程也会被停止。
    
  代码如下:

  if ([CMStepCounter isStepCountingAvailable]) {self.stepCounter = [[CMStepCounter alloc] init];[self.stepCounter startStepCountingUpdatesToQueue:self.operationQueueupdateOn:1withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error) {dispatch_async(dispatch_get_main_queue(), ^{if (error) {UIAlertView *error = [[UIAlertView alloc] initWithTitle:@"Opps!" message:@"error" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];[error show];}else {NSString *text = [NSString stringWithFormat:@"当前步数: %ld", (long)numberOfSteps];//这里是步数weakSelf.stepsLabel.text = text;}});}];
}

计步 第二种方法

queryStepCountStartingFrom:to:toQueue:withHandler:
     
     收集并返回某一时间段内的历史步数数据
     
     - (void)queryStepCountStartingFrom:(NSDate *)start to:(NSDate *)end toQueue:(NSOperationQueue *)queuewithHandler:(CMStepQueryHandler)handler
     Parameters
     
     start
     
     收集步数数据的开始时间,该参数不能为 nil.
     
     end
     
     收集步数数据的停止时间,该参数不能为nil.
     
     queue
     
     执行指定handler块的操作队列,第三方可以指定一个定制队列或者使用操作队列协助app的主线程。该参数不能为nil
     
     handler
     
     执行处理结果的块方法,该参数不能为nil。更多块方法信息参考CMStepQueryHandler。
     
     Discussion
     
     该方法为异步方法,会立即返回并且把结果分发到指定的handler块中处理。系统最多仅存储最近7天内的有效步数数据。如果在指定时间范围内没有数据,则会传递一个0值到handler块中。
  
代码如下

// 获取今日步数 __weak ViewController *weakSelf = self;
self.operationQueue = [[NSOperationQueue alloc] init];NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
// 开始日期
NSDate *startDate = [calendar dateFromComponents:components];
// 结束日期
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];if ([CMStepCounter isStepCountingAvailable]) {[self.stepCounter  queryStepCountStartingFrom:startDate to:endDate toQueue:self.operationQueue withHandler:^(NSInteger numberOfSteps, NSError * _Nullable error) {NSLog(@"%ld",numberOfSteps);dispatch_async(dispatch_get_main_queue(), ^{if (error) {UIAlertView *error = [[UIAlertView alloc] initWithTitle:@"Opps!" message:@"error" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];[error show];}else {weakSelf.totalLabel.text = [NSString stringWithFormat:@"今日总步数%ld",numberOfSteps];}});}];
}

另外,iOS7还增加了CMMotionActivity类,用来获取运动状态

  if ([CMMotionActivityManager isActivityAvailable]) {self.activityManager = [[CMMotionActivityManager alloc] init];[self.activityManager startActivityUpdatesToQueue:self.operationQueuewithHandler:^(CMMotionActivity *activity) {dispatch_async(dispatch_get_main_queue(), ^{NSString *status = [weakSelf statusForActivity:activity];NSString *confidence = [weakSelf stringFromConfidence:activity.confidence];weakSelf.statusLabel.text = [NSString stringWithFormat:@"状态: %@", status];weakSelf.confidenceLabel.text = [NSString stringWithFormat:@"速度: %@", confidence];});}];
}
- (NSString *)statusForActivity:(CMMotionActivity *)activity {NSMutableString *status = @"".mutableCopy;if (activity.stationary) {[status appendString:@"not moving"];}if (activity.walking) {if (status.length) [status appendString:@", "];[status appendString:@"on a walking person"];}if (activity.running) {if (status.length) [status appendString:@", "];[status appendString:@"on a running person"];}if (activity.automotive) {if (status.length) [status appendString:@", "];[status appendString:@"in a vehicle"];}if (activity.unknown || !status.length) {[status appendString:@"unknown"];}return status;
}- (NSString *)stringFromConfidence:(CMMotionActivityConfidence)confidence {switch (confidence) {case CMMotionActivityConfidenceLow:return @"Low";case CMMotionActivityConfidenceMedium:return @"Medium";case CMMotionActivityConfidenceHigh:return @"High";default:return nil;}
}

好吧,随着时间的推移,iOS8来了,也带来了healthkit,不过之前的方法满足需求也就还是用的CMStepCounter方法。

不过最近客户改需求了,手环,iWatch的数据也需要统计进来,就不得不用healthkit的方法了。

还是老套路,先检查能不能用

 //查看healthKit在设备上是否可用,ipad不支持HealthKitif(![HKHealthStore isHealthDataAvailable]){NSLog(@"设备不支持healthKit");}

然后获取步数

 //创建healthStore实例对象self.healthStore = [[HKHealthStore alloc] init];//设置需要获取的权限这里仅设置了步数HKObjectType *stepCount = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];NSSet *healthSet = [NSSet setWithObjects:stepCount, nil];//从健康应用中获取权限[self.healthStore requestAuthorizationToShareTypes:nil readTypes:healthSet completion:^(BOOL success, NSError * _Nullable error) {if (success){NSDateFormatter *formatter = [[NSDateFormatter alloc ]init];[formatter setDateFormat:@"yyyy-MM-dd"];NSDate *now = [NSDate date];NSString *todaystr = [formatter stringFromDate:now];NSDate *today = [formatter dateFromString:todaystr];NSDate *next = [today dateByAddingTimeInterval:24*60*60];
       //定义需要获取的数据为步数HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
       //设置获取的步数时间间隔NSDateComponents *dateComponents = [[NSDateComponents alloc] init];dateComponents.day = 1;NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:today endDate:next options:HKQueryOptionStrictStartDate];//创建查询统计对象collectionQueryHKStatisticsCollectionQuery *collectionQuery = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:predicate options: HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource anchorDate:[NSDate dateWithTimeIntervalSince1970:0] intervalComponents:dateComponents];collectionQuery.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection * __nullable result, NSError * __nullable error) {float numberOfSteps = 0;for (HKStatistics *statistic in result.statistics) {for (HKSource *source in statistic.sources) { //HKSource对象中的name可用于区分健康数据来源if ([source.name isEqualToString:deviceName]) {float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];numberOfSteps += steps;}  //deviceName是根据接入的设备做的标记,if ([deviceName isEqualToString:@"iPhone"]) {if ([source.name isEqualToString:[UIDevice currentDevice].name]) {float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];numberOfSteps += steps;}}else if ([deviceName isEqualToString:@"iWatch"] && ![source.name isEqualToString:[UIDevice currentDevice].name]){if ([source.bundleIdentifier hasPrefix:@"com.apple.health"]) {float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];numberOfSteps += steps;}}else if ([deviceName isEqualToString:@"xiaomi"]){if ([source.name isEqualToString:@"小米运动"] || [source.bundleIdentifier isEqualToString:@"HM.wristband"]) {float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];numberOfSteps += steps;}}}}NSLog(@"ff = %f",numberOfSteps);//步数看这里就好stepString = [NSString stringWithFormat:@"%.0f",numberOfSteps];//CGFloat distance = [VHSCommon getDistance:numberOfSteps];//int calorie = [VHSCommon getActionCalorie:distance speed:distance * 3600 / 24*60*60];//distanceString = [NSString stringWithFormat:@"%.2f",distance];//calorString = [NSString stringWithFormat:@"%d",calorie];
            };[self.healthStore executeQuery:collectionQuery];}else{NSLog(@"获取步数权限失败");}}];

demo完整代码在这里:

加速度传感器进行计步

CMStepCounter获取健康步数

关于healthkit的还没有整理好,过段时间再补充吧。

healthkit 之前的计步方案相关推荐

  1. iOS中的传感器---摇一摇, 计步器,距离感应,陀螺仪

    前几天项目中用到了一下CoreMotion框架,觉得iOS中的传感器还是挺好玩的,又花了点时间去了解了一下iOS中其他一些常用的传感器应用,今天简单做下总结. iOS中的传感器大致有以下几种: 运动传 ...

  2. android计步器简书,iOS中计步器的实现方案及原理

    前言 当前社会随着社会水平的越来越高,人民饮食也更加的丰富多样,随之而来就成就了越来越多可爱的胖子.胖子多了,一些"不良商贩"发现商机研发出了运动健身类的APP,如:Keep,咕咚 ...

  3. 智能手环功能模块设计_智能手环的设计的方案.doc

    智能手环的设计的方案 智能手环的设计的方案 摘 要 本手环设计本身添加了市面上智能手环的相关功能,能够实现显示时间.测量步数.热量.闹钟等功能.因此本设计也适用于普通的使用者,在能够拥有智能手环的便捷 ...

  4. 这块DIY墨水屏手表火了!外观可盐可甜,无线蓝牙计步闹钟一应俱全 | 开源

    月石一 发自 凹非寺 量子位 报道 | 公众号 QbitAI 还记得Pebble和索尼的墨水屏手表吗? △图源:Wikipedia/SONY 现在,开源的墨水屏智能手表Watchy来了! 先来一睹为快 ...

  5. 智能手机计步算法c语言实现,【转载】智能手机计步器算法的实现

    现 在的智能手机嵌入了一些微小的传感器,比如重力传感器.光传感器.声音传感器等.如何有效地利用这些传感器来开发一些应用,是一个值得深入研究的课题.比 如开发医疗健康的应用.运动量监视器等.本文采用ht ...

  6. 西安力邦智能医疗amp;可穿戴设备沙龙--第1期---苹果HealthKit、谷歌GoogleFit来袭,智能医疗要爆发吗?...

    背    景: "可穿戴设备"成为2014的行业热点,从Google Glass到苹果iWatch, 越来越多的企业推出了包含眼镜.腕带.鞋等各种可穿戴设备,"可穿戴&q ...

  7. 一加7pro运动计步功能_时隔多年,一加再次入局中端智能手机 这次能否成功?...

    此前有爆料称,今年一加还有一款名为一加Z的中端手机,而近日根据外媒MSPoweruser的报道,一加将在下个月发布一款全新经济型旗舰手机.不过有消息人士称,这款设备的官方名称将是OnePlus Nor ...

  8. 蓝牙运动手环app开发方案

    所谓智能蓝牙手环app软件开发,  就是内置蓝牙操作系统.通过连接网络来实现多种功能的手环产品,蓝牙手环一般能同步手机中的电话.短信.邮件.照片.音乐等相关数据.其实早在1982年,日本精工就通过其收 ...

  9. iOS Healthkit 使用探索分析

    一 基本认知层面: HealthKit框架提供了一个结构,应用可以使用它来分享健康和健身数据.HealthKit管理从不同来源获得的数据,并根据用户的偏好设置,自动将不同来源的所有数据合并起来.应用还 ...

最新文章

  1. linux c sql server 存储过程,SQL Server 2016 - 本机编译的函数
  2. LinQ高级查询、组合查询
  3. 同时使用传入和传出连接时,相互TLS身份验证存在Java问题
  4. Basic INFO - 在命令行Build InstallShield安装包工程获得压缩安装包
  5. ifconfig源码分析之与内核交互数据
  6. 清华大学python教材怎么样_经典!清华大学计算机系教材曝光:《Python编程金典》...
  7. nginx常用配置模板
  8. 百度搜索移动端流量词热度统计方法
  9. 7-112 约分最简分式
  10. 2020危险化学品经营单位安全管理人员考试题及危险化学品经营单位安全管理人员模拟考试
  11. Good Bye 2022: 2023 is NEAR C. Koxia and Number Theory
  12. java基础面试题及答案
  13. 这个618买到假货了怎么办?
  14. MobaXterm中文版
  15. php 支付宝证书 单笔转账到账户
  16. 18_位运算符(了解)
  17. 【应用案例】SequoiaDB+Spark搭建医院临床知识库系统
  18. 产品宣传片制作创意攻略的先决条件
  19. 转载:与吸烟者共同生活的人,患肺…
  20. 使用State Threads实现简单的服务器

热门文章

  1. 使用VIM搜索多个文件
  2. 关于 Kotlin 一系列的学习教程、文章。学好 Kotlin 从这里开始,争取做到最全、最详细。让没有编程经验的朋友也能通过这个系列教程用 Koltin 语言来开发项目
  3. 设计干货素材 | 玩转电商购物节插画设计
  4. 手机储存卡被写保护怎样删掉
  5. 最新!蔚来2022年第四季度被理想汽车超越,或将在2023年全面落后
  6. IE下按钮超链接无法跳转
  7. 高三党听音乐自习哪款蓝牙耳机好?平价高音质这五款蓝牙耳机值得入手
  8. 敏捷大数据与敏捷AI
  9. Python下绘制世界人口地图
  10. Android对于图片失真的一些方法!