CMMotionManager与UIAccelerometer的效率比较

4
我已经在开发一个AR框架一段时间了,现在正在尝试从UIAccelerometer (已弃用)升级到CMMotionManager,但遇到了一些效率问题?
基本上看起来CMMotionManager比UIAccelerometer要大得多且速度较慢。有人之前遇到CMMotionManager的性能问题吗?

正如您可以在这里看到的那样,我有这个:

accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.updateInterval = 0.01;
[accelerometer setDelegate:self];

并且

-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
    rollingZ  = (acceleration.z * kFilteringFactor) + (rollingZ  * (1.0 - kFilteringFactor));
    rollingX = (acceleration.y * kFilteringFactor) + (rollingX * (1.0 - kFilteringFactor));

    if (rollingZ > 0.0)      currentInclination = inc_avg(atan(rollingX / rollingZ) + M_PI / 2.0);
    else if (rollingZ < 0.0) currentInclination = inc_avg(atan(rollingX / rollingZ) - M_PI / 2.0);
    else if (rollingX < 0)   currentInclination = inc_avg(M_PI/2.0);
    else if (rollingX >= 0)  currentInclination = inc_avg(3 * M_PI/2.0);
}

即使在像iPhone 4这样的“旧设备”上,所有功能都运作良好(虽然不是真正的旧设备)。

但是当尝试使用CMMotionManager时,代码完全相同:

motionManager = [[CMMotionManager alloc] init];

使用

[motionManager setAccelerometerUpdateInterval:0.01];
[motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue]
                                    withHandler: ^(CMAccelerometerData *accelerometerData, NSError *error){

                                        rollingZ = (accelerometerData.acceleration.z * kFilteringFactor) + (rollingZ  * (1.0 - kFilteringFactor));
                                        rollingX = (accelerometerData.acceleration.y * kFilteringFactor) + (rollingX * (1.0 - kFilteringFactor));

                                        if (rollingZ > 0.0)      currentInclination = inc_avg(atan(rollingX / rollingZ) + M_PI / 2.0);
                                        else if (rollingZ < 0.0) currentInclination = inc_avg(atan(rollingX / rollingZ) - M_PI / 2.0);
                                        else if (rollingX < 0)   currentInclination = inc_avg(M_PI/2.0);
                                        else if (rollingX >= 0)  currentInclination = inc_avg(3 * M_PI/2.0);
                                    }];

这个程序似乎被数学部分拖慢了速度...! 我这么说是因为当我移除所有的数学部分它就能很好地运行。

iPhone 5 可以正常工作,但 iPhone 4S 会出现一些延迟的迹象,而 iPhone 4 则会完全冻结...

(如果您想要更多细节,我可以提供,但相对来说解释起来比较复杂)

2个回答

7

我刚遇到了同样的问题,但是解决方案在文档中;)

问题出在块格式上。所有教程似乎都倾向于使用这种方法,但是苹果建议定期轮询 CMMotionManager 作为更加性能优化的方法。块格式会增加开销。

CMMotionManager 类参考中来看:

为了通过定期采样处理运动数据,应用程序调用一个不带参数的“start”方法,并定期访问给定类型的运动数据的属性中保存的运动数据。这种方法是游戏等应用程序的推荐方法。在块中处理加速度计数据会引入额外的开销,大多数游戏应用程序只关心它们在渲染画面时的最新运动数据样本。

所以,你需要做的事情就是从文档中再次找到:

调用 startAccelerometerUpdates 开始更新并通过读取 accelerometerData 属性定期访问 CMAccelerometerData 对象。

按照这些方式操作即可。

CMMotionManager *mManager = [(AppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];

[mManager startAccelerometerUpdates];

然后,以您选择的某种定期更新方法为基础:
CMMotionManager *mManager = [(SEPAppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];

CMAccelerometerData *aData = mManager.accelerometerData;

这个解决方案在我进行的有限测试中与 iPhone 4 上的 UIAccelerometer 一样有效。


4

我使用CADisplayLink

首先,设置CMMotionManager实例。

-(void)viewDidLoad
{
    [super viewDidLoad];

    self.motionManager = [[CMMotionManager alloc]init];

    if(self.motionManager.isDeviceMotionAvailable)
    {
        [self.motionManager startDeviceMotionUpdates];
    }


    [self setupDisplayLink];
}

第二步,设置 displaylink 实例如下:

-(void)setupDisplayLink
{
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
    displayLink.frameInterval = 10;// how many frames to skip before next update
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

显示链接是连接到您设备屏幕的对象,它可以以指定帧率运行指定选择器;更多信息在此处

第三步,您应该实现您指定为显示链接选择器的update:方法。

-(void)update:(CADisplayLink *)displayLink
{
    // handle new accelerometer data here
    CMDeviceMotion *deviceMotion = self.motionManager.deviceMotion;
    NSLog(@"Acceleration: %@", deviceMotion.accelerometerData.acceleration);
}

最后一行应该是:deviceMotion.accelerometerData.gravity - alecail
我认为DisplayLink是更好的解决方案,但你需要失效(invalidate)displaylink以避免因displaylink保留runloop而导致循环引用(retain cycle)。 - Developer Sheldon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接