iOS传感器:利用磁力计完成一个AR场景应用1. 磁力计的介绍2. 磁力计的使用3. 开始我们的小案例

其实最近一直惦记着要更新简书,但是手头上正在开发一个个人APP,碰到了若干不太容易搞定的问题,所以更新就一拖再拖。被催无数次之后,终于在这个周末下定决心要更新一篇。

前面咱们用了陀螺仪、加速传感器做了一些好玩的效果,今天咱们就用用第三个传感器--磁力计--来做一个AR的场景。说到AR这个词,请大家不要喷我哈,并没有用到WWDC刚出的ARKit。而且今天这个例子重点是学习使用磁力计,本质上来讲和AR关系并不大。

image.png

磁力计跟前面的加速计、陀螺仪,都是用到了上次说的iOS当中的那个核心运动框架CoreMotion, 也都用了CMMotionManager

完成后的效果,能看到在视频输出的下面会有一个随着屏幕移动的天空星辰背景图,同时屏幕左上角会实时打印当前的方向信息、地理信息。

磁力计.gif

1. 磁力计的介绍

磁强计指的是各种用于测量磁场的仪器,也称磁力仪、高斯计。它可以感应地球的磁场,获得方向信息。

1.1 应用场景

那显而易见,典型的应用场景就是用在电子罗盘和导航上面。

之前看到过某个大神用磁力计简直玩出了花儿,隔空抓牛的感觉。利用iPhone上磁力计、加速计和麦克风实现平面和三维上的磁铁追踪,并能实时的反馈在iPhone 屏幕上。

image.png

看上去屌炸了,有没有?宅胖还专门找到了这篇文章的报道,有兴趣的可以进去看看,里面有实现后的视频。http://mobile.163.com/14/1127/09/AC2356CQ0011179O.html

1.2 需要了解的基本概念

要用到磁力计,经常会听到有人说到“磁北”、“真北”这两个高频词,CoreMotion也会给我们返回这两个数值。是什么意思呐?

  • 真北:指的是地理的北极
  • 磁北:指的是磁场北极

纳尼?这是什么鬼?来来来,咱们科普一下。

  1. 磁北 磁北是以大地磁场为基准的,通过各种传感器传送的方位都是以磁北为基准的。BUT!!!!敲黑板!!!!!磁北的具体位置是随着时间而改变的。 也就是说咱们随着地球的旋转,咱们除了有一年四季的变化、时间的变化,连磁场都会发生改变。嗯,是这样的。
  2. 真北 由于磁北是会变化的,那我们怎么用?不可能还要计算地球自转轴、考虑时间因素吧。所以才有了真北这个概念。

真北是地球自转的地理北极,这个就是考虑到了各种因素,是一个固定的位置。所以咱们电子罗盘神马的所指的北,说的是这个真北。通常情况下,方位都是需要矫正到真北的。苹果很贴心啊,真北就不用自己算了,直接也会有返回的数值。

剩下的还有磁偏角校正、网络北、网络北校正、收敛角等等学术概念。

那岂不是电子罗盘上面的北和指南针上面的北不一致啊?

问这个问题的童鞋那是相当的聪明呀,那肯定是不一致的。不过误差也是在可感官接受的范围内。在等会儿的例子里面,咱们把这两个数值都打印出来,自己看看。

2. 磁力计的使用

2.1 使用步骤

磁力计同样也是通过CoreMotion 这个框架来管理的,所以和前面两个传感器一样,四个标准步骤:

  • 初始化CMMotionManager管理对象;
  • 调用管理对象的对象方法获取数据;
  • 处理数据;
  • 当不需要使用的时候,停止获取数据。

2.2 磁力计数据获取的两种方法

CoreMotion中有2种获取数据方式,一种叫做PUSH的方式,一种叫做PULL的方式。顾名思义,PUSH就是被动的获取。设定完了之后,线程定时把获取到的数据推送回来。可想而知,对于资源的消耗是会稍微大一点的。PULL,就是要去索取。拉一下才会获取到数据。不要不给。上一次代码是Swift的,这一次咱们就使用OC啦。

2.2.1 PULL的方式

//PULL的方法获取数据
- (void)pullMagnetometer {
    //    判断磁力计是否可用
    if (self.manager.magnetometerAvailable) {
        //        设置磁力计采样频率
        self.manager.magnetometerUpdateInterval = 0.1;
        
        //开始更新,后台线程开始运行。这是Pull方式。
        [self.manager startMagnetometerUpdates];
        NSLog(@"X = %f,Y = %f,Z = %f",self.manager.magnetometerData.magneticField.x,self.manager.magnetometerData.magneticField.y,self.manager.magnetometerData.magneticField.z);
        
    } else {
        NSLog(@"It cannot be used!");
    }
}

2.2.2 PUSH的方式

//PUSH的方法获取数据
- (void)pushMagnetometer {
    //    判断磁力计是否可用
    if (self.manager.magnetometerAvailable) {
        //        设置磁力计采样频率
        self.manager.magnetometerUpdateInterval = 0.1;
        
        //Push方式获取和处理数据,这里我们一样只是做了简单的打印。把采样的工作放在了主线程中。
        [self.manager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMagnetometerData * _Nullable magnetometerData, NSError * _Nullable error) {
            NSLog(@"X = %f,Y = %f,Z = %f",magnetometerData.magneticField.x,magnetometerData.magneticField.y,magnetometerData.magneticField.z);
        }];
    } else {
        NSLog(@"It cannot be used!");
    }
}

3. 开始我们的小案例

写这个案例的时候发现要实现一个比较逼真的AR,咱们有好多东东都没有分享过。所以例子写完之后,写这篇文章的时候又对这个例子做了一些调整。大幅简化删减了了好多需求。但是,最后还是使用了相机、百度地图,如果这两个都不用,那真的一点都不能算是AR了。

完成后的效果,能看到在视频输出的下面会有一个随着屏幕移动的天空星辰背景图,同时屏幕左上角会实时打印当前的方向信息、地理信息。

磁力计.gif

小案例里面的相机不用紧张,咱们后面也还是会分享的。还有一个之前说过的,多线程也记得的哈,下一个系列就来补。

3.1 思维导图

image.png

3.2 准备工作

3.2.1 SDK的导入

这个例子里面咱们用了百度地图,所以需要导入百度地图的SDK。因为咱们没有分享过如何使用第三方库,可以看看这篇文章iOS·采用第三方(百度地图SDK)实现定位等功能开发

3.2.2 相机、定位权限的索取

iPhone对于APP使用用户的隐私权限做了很严格的规定,每个APP使用用户隐私之前必须要让用户知道并且同意。大概也正是因为这点,本宅胖才这么爱iPhone吧。虽然开发的时候就面临着很多问题,但至少产品始终是站在用户的角度考虑问题的。

在Info.plist中向用户索取相机和地理位置信息的权限。

  • Privacy - Camera Usage Description
  • Privacy - Location When In Use Usage Description

image.png

麦克风、媒体库的权限就不需要了。之前没有删减的那个案例里面用到了这个。说起来好心疼~~~

3.2.3 相机使用

相机在这个案例里面,使用的是AVFoundation框架。也是很心痛,这部分之前没有分享过。所以如果等不及俺的分享,可以先看看这个。Objc的第21期内容:iOS上的相机捕捉

别忘了在头文件<AVFoundation/AVFoundation.h>,同时遵守代理协议AVCaptureVideoDataOutputSampleBufferDelegate

3.3 创建动态活动的星空背景

从网上找到的星空图是4000*2800的大小,要让它完全超出屏幕。这样才能根据手机的移动进行活动。

同样的,为了能够明显的看到效果,在从陀螺仪获取到的数值之后,添加了一个放大倍数。这个小例子里面咱们使用的是5。

3.3.1 使用陀螺仪进行防抖

如果陀螺仪返回的数据在某个特定小范围内,我们就是视同只是手抖,不对图片本身进行处理。这样就看不到背景图片明显抖动的感觉了。

  • 注意:陀螺仪返回的各轴旋转角度是有可能为负数的,所以别忘了用绝对值进行判断。
//            做一下防抖动的处理,如果手机旋转的不太大,就不执行操作
if (fabs(gyroData.rotationRate.x) * multiplier < 0.2 && fabs(gyroData.rotationRate.y) * multiplier < 0.2) {
    return ;
}

3.3.2 让背景星空图随着屏幕进行运行

直接修改背景图的center就好了,让原center添加上需要进行的位移量就可以实现了。 这里需要注意的是,需要对边界值进行处理。如果屏幕旋转的乱七八糟,我们要让视频输出层下面始终有一个背景存在。

            //            因为背景图的大小事屏幕宽度的三倍,高度的两倍。为了防止超出边界,进行限制
            if (imageRotationX > self.view.frame.size.width * 1.5) {
                imageRotationX = self.view.frame.size.width * 1.5;
            }
            
            if(imageRotationX < (- self.view.frame.size.width * 0.5)){
                imageRotationX=(- self.view.frame.size.width * 0.5);
            }
            
            if (imageRotationY > self.view.frame.size.height) {
                imageRotationY = self.view.frame.size.height;
            }
            if (imageRotationY < 0) {
                imageRotationY = 0;
            }

3.4 利用百度地图输出磁力计信息、经纬度、高度

3.4.1 输出磁力计信息

根据百度地图SDK的文档,在用户的方向信息放生变化之后,会调用以下的方法。 这里我们没有做任何特殊的处理,就只是简单的打印出来了磁北、真北、三轴的偏移量。

等会儿运行的时候大家就能看到之前的问题,到底磁北、真北之间相差多少。

/**
 *用户方向更新后,会调用此函数
 *@param userLocation 新的用户位置
 */
- (void)didUpdateUserHeading:(BMKUserLocation *)userLocation {
    self.magnetometerInfo.numberOfLines = 0;
    self.magnetometerInfo.text = [NSString stringWithFormat:@"磁北:%.0f,真北:%.0f \n偏移:%.0f \nx:%.1f y:%.1f z:%.1f",
                                  userLocation.heading.magneticHeading,userLocation.heading.trueHeading,userLocation.heading.headingAccuracy,userLocation.heading.x,userLocation.heading.y,userLocation.heading.z];
}

3.4.2 输出用户位置信息:经纬度、高度

/**
 *用户位置更新后,会调用此函数
 *@param userLocation 新的用户位置
 */
- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation
{
    self.physicalLocation.numberOfLines = 0;
    self.physicalLocation.text = [NSString stringWithFormat:@"经度:%f \n纬度:%f \n高度:%f",userLocation.location.coordinate.longitude,userLocation.location.coordinate.latitude,userLocation.location.altitude];
}

源代码下载地址:OC下载地址

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏钱曙光的专栏

一周极客热文:5种类型的程序员

一位过来的老鸟程序员总结了自己多年的程序员经历,把程序员分为以下五类: 补漏型:当哪里出现差错的时候他会迅速的修补 完美主义强迫症型:“你想对我的代码做什么!”...

1727
来自专栏java一日一条

Android性能优化案例研究(上)

这是Google的Android开发工程师Romain Guy刊登在个人Blog上的一篇文章。Romain Guy 作为Android图形渲染和系统优化的专家,...

501
来自专栏FreeBuf

实用技巧 | 如何通过IP地址进行精准定位

*本文原创作者:allen权,转载须注明来自FreeBuf.COM 在甲方工作的朋友可能会遇到这样的问题,服务器或者系统经常被扫描,通过IP地址我们只能查到某...

2345
来自专栏web前端教室

前端新人学习中的陌生感是如何产生的,以及解决思路。

今天在给零基础班的同学们讲nodeJs的时候,也许是讲课讲到一定程度了吧,突然一下就能体会到他们面对一个“新东西”的时候的那种陌生感、疏离感了。一定程度上能够感...

1955
来自专栏GopherCoder

『Go 语言学习专栏』-- 第七期

1976
来自专栏有趣的django

Django+xadmin打造在线教育平台(六)

代码 github下载 九、课程章节信息 9.1.模板和urls  拷贝course-comments.html 和 course-video.html放入 ...

4409
来自专栏大数据挖掘DT机器学习

不编程,手把手教你如何从网络采集海量数据

作者:赵一鸣 摘自:微信公号“沙漠之鹰” 不少朋友都会问:几十万条租房,二手房,薪酬,乃至天气数据都是从哪里来的?其实这些数据在十几分钟内就可以采集到! ...

3748
来自专栏Python攻城狮

Python采集微博热评进行情感分析祝你狗年脱单

如果自己需要爬(cai)虫(ji)的数据量比较大,为了防止被网站封Ip,可以分时段爬取,另外对于爬到的数据一般是用来存储数据库,这就需要对数据进行去重处理,记录...

742
来自专栏Android先生

如何写出一篇登得上大雅之堂的技术博客

本文原标题为:【Mac流派】程序猿修炼之道(7)-技能篇之如何写出一篇登得上大雅之堂的技术博客,后因标题如古时女人的裹脚布——又臭又长,遂改为:...

793
来自专栏MixLab科技+设计实验室

从Storyboard到DIY实现一个漫画生成器-01

用户只需拍摄一段视频并将其加载到 Storyboard 中即可将视频转换为单页漫画的布局。该应用会自动选择有趣的帧,并将其应用于6种视觉样式中的一种。生成的漫画...

1014

扫码关注云+社区