Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMMotionManager vs UIAccelerometer efficiency

I've been working on an AR framework for a while now and am trying to update from UIAccelerometer (deprecated) to CMMotionManager but am running into some efficiency problems?

Basically it seems like CMMotionManager is MUCH larger and slower than UIAccelerometer is. Has anyone experienced performance issues with CMMotionManager before?


As you can see here, I had this:

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

and

-(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);
}

and all works great even on "older" devices like the iPhone 4 (not really old but yea...).

But when trying the exact same code but with CMMotionManager:

motionManager = [[CMMotionManager alloc] init];

with

[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);
                                    }];

The math seems to slow the crap out of it..! I say this because when I remove all the math part it works great.

An iPhone 5 will work alright but an iPhone 4S will show signs of lag and the iPhone 4 will just freeze...

(I can give you more details if you want but its relatively complicated to explain)

like image 489
glesage Avatar asked Dec 20 '22 03:12

glesage


2 Answers

I was just having this same problem, and wouldn't you know it, the solution was in the documentation ;)

The problem is with the block format. All of the tutorials seem to favor that method, but Apple recommends periodic polling of the CMMotionManager as a more performance oriented approach. The block format adds overhead.

From the CMMotionManager Class Reference:

To handle motion data by periodic sampling, the app calls a “start” method taking no arguments and periodically accesses the motion data held by a property for a given type of motion data. This approach is the recommended approach for apps such as games. Handling accelerometer data in a block introduces additional overhead, and most game apps are interested only the latest sample of motion data when they render a frame.

So what you want to do, from the docs again:

Call startAccelerometerUpdates to begin updates and periodically access CMAccelerometerData objects by reading the accelerometerData property.

Something along these lines

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

[mManager startAccelerometerUpdates];

Then, in some sort of periodically updating method of your choosing:

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

CMAccelerometerData *aData = mManager.accelerometerData;

This solution appears to work as well as UIAccelerometer on an iPhone 4 from the limited testing I've done.

like image 109
arton Avatar answered Dec 24 '22 11:12

arton


I use CADisplayLink.

First, setup CMMotionManager instance.

-(void)viewDidLoad
{
    [super viewDidLoad];

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

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


    [self setupDisplayLink];
}

Secondly setup displaylink instance like this:

-(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];
}

Display link is an object that is connected to your device screen, and which can run specified selector with specified frame rate; More on them here

Thirdly you should implement update: method you specified as display link selector.

-(void)update:(CADisplayLink *)displayLink
{
    // handle new accelerometer data here
    CMDeviceMotion *deviceMotion = self.motionManager.deviceMotion;
    NSLog(@"Acceleration: %@", deviceMotion.accelerometerData.acceleration);
}
like image 34
haawa Avatar answered Dec 24 '22 09:12

haawa