热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

第六十四篇、OC_计步器

计步器的实现方式主要有那么两种1.通过直接调用系统的健康数据,基于HealthKit框架的,但是貌似是一小时更新一次数据。如果要实时获取步数,这种方式并不是最佳。2.基于CoreMotion框

计步器的实现方式主要有那么两种

1.通过直接调用系统的健康数据,基于HealthKit框架的,但是貌似是一小时更新一次数据。如果要实时获取步数,这种方式并不是最佳。

2.基于CoreMotion框架,顾名思义就是加速计/加速度传感器

  》最早出现在iOS设备上的传感器之一

  》加速计用于检测设备在X、Y、Z轴上的加速度 (哪个方向有力的作用)

  》加速计可以用于检测设备的摇晃,经典应用场景(例如摇一摇、计步器等)

源码:

#import 
#import 

@interface StepManager : NSObject


@property (nonatomic) NSInteger step;                       // 运动步数(总计)

+ (StepManager *)sharedManager;


//开始计步
- (void)startWithStep;

//得到计步所消耗的卡路里
//+ (NSInteger)getStepCalorie;
//
//得到所走的路程(单位:米)
//+ (CGFloat)getStepDistance;
//
//得到运动所用的时间
//+ (NSInteger)getStepTime;

@end

 

#import "StepManager.h"
#import "StepModel.h"
#import 

// 计步器开始计步时间(秒)
#define ACCELERO_START_TIME 2

// 计步器开始计步步数(步)
#define ACCELERO_START_STEP 100

// 数据库存储步数采集间隔(步)
#define DB_STEP_INTERVAL 1


@interface StepManager ()

{


    NSMutableArray *arrAll;                 // 加速度传感器采集的原始数组
    int record_no_save;
    int record_no;
    NSDate *lastDate;

}
@property (nonatomic) NSInteger startStep;                          // 计步器开始步数

@property (nonatomic, retain) NSMutableArray *arrSteps;         // 步数数组
@property (nonatomic, retain) NSMutableArray *arrStepsSave;     // 数据库纪录步数数组

@property (nonatomic) CGFloat gpsDistance;                  // GPS轨迹的移动距离(总计)
@property (nonatomic) CGFloat agoGpsDistance;               // GPS轨迹的移动距离(之前)
@property (nonatomic) CGFloat agoActionDistance;            // 实际运动的移动距离(之前)

@property (nonatomic, retain) NSString *actionId;           // 运动识别ID
@property (nonatomic) CGFloat distance;                     // 运动里程(总计)
@property (nonatomic) NSInteger calorie;                    // 消耗卡路里(总计)
@property (nonatomic) NSInteger second;                     // 运动用时(总计)

@end

@implementation StepManager

static StepManager *sharedManager;
static CMMotionManager *motionManager;

+ (StepManager *)sharedManager
{
    @synchronized (self) {
        if (!sharedManager) {
            sharedManager = [[StepManager alloc]init];
            motionManager = [[CMMotionManager alloc]init];
        }
    }
    return sharedManager;
}

//开始计步
- (void)startWithStep
{
    if (!motionManager.isAccelerometerAvailable) {
        NSLog(@"加速度传感器不可用");
        return;
    }else {

        motionManager.accelerometerUpdateInterval = 1.0/40;
    }
    [self startAccelerometer];

}

- (void)startAccelerometer
{
 /*  @try 。。。@catch 。。。
  *  @try 后面跟或许会出现异常的程序,代码块
  *  @catch  当@try抛出异常时 系统会进行异常捕捉  具体可以了解下 NSException 异常类     @catch 在这里处理 程序所抛出的异常
  */

    @try  
    {
        //如果不支持陀螺仪,需要用加速传感器来采集数据
        if (!motionManager.isAccelerometerActive) {//  isAccelerometerAvailable方法用来查看加速度器的状态:是否Active(启动)。

            // 加速度传感器采集的原始数组
            if (arrAll == nil) {
                arrAll = [[NSMutableArray alloc] init];
            }
            else {
                [arrAll removeAllObjects];
            }

            /*
             1.push方式
             这种方式,是实时获取到Accelerometer的数据,并且用相应的队列来显示。即主动获取加速计的数据。
             */
            NSOperationQueue *queue = [[NSOperationQueue alloc] init];

            [motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){

                if (!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;

                //将信息保存在步数模型中
                StepModel *stepsAll = [[StepModel 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 2; i++) {
                        //如果数组个数大于3,继续,否则跳出循环,用连续的三个点,要判断其振幅是否一样,如果一样,然并卵
                        if (![arrBuffer objectAtIndex:i-1] || ![arrBuffer objectAtIndex:i] || ![arrBuffer objectAtIndex:i+1])
                        {
                            continue;
                        }
                        StepModel *bufferPrevious = (StepModel *)[arrBuffer objectAtIndex:i-1];
                        StepModel *bufferCurrent = (StepModel *)[arrBuffer objectAtIndex:i];
                        StepModel *bufferNext = (StepModel *)[arrBuffer objectAtIndex:i+1];
                        //控制震动幅度,,,,,,根据震动幅度让其加入踩点数组,
                        if (bufferCurrent.g <-0.12 && 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 ) {
                        StepModel *caidianCurrent = (StepModel *)[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;
                            //运动识别id
                            self.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 = (int)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 (motionManager.isAccelerometerActive) {

                                    //存一下时间
                                    lastDate = caidianCurrent.date;

                                    if (intervalCaidian >= ACCELERO_START_TIME * 1000) {// 计步器开始计步时间(秒)
                                        self.startStep = 0;
                                    }

                                    if (self.startStep //计步器开始计步步数 (步)

                                        self.startStep ++;
                                        break;
                                    }
                                    else if (self.startStep == ACCELERO_START_STEP) {
                                        self.startStep ++;
                                        // 计步器开始步数
                                        // 运动步数(总计)
                                        self.step = self.step + self.startStep;
                                    }
                                    else {
                                        self.step ++;
                                    }



                                    //步数在这里
                                    NSLog(@"步数%ld",self.step);

                                    int intervalMillSecOnd= [caidianCurrent.date timeIntervalSinceDate:[[self.arrSteps lastObject] date]] * 1000;
                                    if (intervalMillSecond >= 1000) {

                                        record_no++;

                                        caidianCurrent.record_no = record_no;

                                        caidianCurrent.step = (int)self.step;
                                        [self.arrSteps addObject:caidianCurrent];
                                    }

                                    // 每隔100步保存一条数据(将来插入DB用)
                                    StepModel *arrStepsSaveVHSSteps = (StepModel *)[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];

                                                    NSLog(@"---***%ld",self.step);
                                // 备份当前运动数据至文件中,以备APP异常退出时数据也不会丢失
                                        // [self bkRunningData];

                                    }
                                }
                            }

                            // 运动提醒检查
                            // [self checkActionAlarm];
                        }
                    }
                }
            }];

        }
    }@catch (NSException * e) {
        NSLog(@"Exception: %@", e);
        return;
    }
}

////得到计步所消耗的卡路里
//+ (NSInteger)getStepCalorie
//{
//    在这里原谅我并没有对其实现。本以为卡路里和步数的换算单位,一个公式就可以了。不查不知道,一查吓一跳原来还和其他众多因素有关:走路的快慢,步子的大小,体重的大小等等有关。。。笔者已吓尿,还是找算法大牛吧。
//}
//
////得到所走的路程(单位:米)
//+ (CGFloat)getStepDistance
//{
//    
//}
//
////得到运动所用的时间
//+ (NSInteger)getStepTime
//{
//    
//}

 

外部使用方式:

#import "ViewController.h"
#import "StepManager.h"
@interface ViewController ()
{
    NSTimer *_timer;
    UILabel *lable;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
      [[StepManager sharedManager] startWithStep];
      lable =[[ UILabel alloc]initWithFrame:CGRectMake(100, 300, 300, 40)];


    [self.view addSubview:lable];
    _timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(getStepNumber) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
}


- (void)getStepNumber
{

    lable.text = [NSString stringWithFormat:@"我走了  %ld步",[StepManager sharedManager].step];
}

 

当然还有特别注意,应用程序在后台也要做采集动作:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    //播放一段无声音乐,使其可以一直在后台进行计步  此方法为第三方 若要详细了解,请下载demo自行研究
    [[MMPDeepSleepPreventer sharedSingleton] startPreventSleep];
}

 


推荐阅读
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
author-avatar
隔壁老吴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有