I need some help using gyroscope on iPhone. I can't understand readings from CMAttitude regarding pitch, roll and yaw in a particular situation.
This is my code
- (void)handleDeviceMotion:(CMDeviceMotion*)motion {
NSLog(@"Yaw %f ",attitude.yaw * 180 / M_PI);
NSLog(@"Pitch %f ",attitude.pitch * 180 / M_PI);
NSLog(@"Roll %f ",attitude.roll * 180 / M_PI);
}
Let's suppose that iPhone is laying down on a plane as in the following figure:
pitch, roll and yaw are (almost) 0 degrees and any turn around an axis returns understandable readings. For example, turning the device right, the Yaw decreases and Pitch and Roll remains at 0.
Now the iPhone is in the following position:
and measurement is started again.
Readings are: Yaw = 0, Pitch = 90, Roll = 0
Since the devices rotated around this axis, Pitch increases.
Moving the iPhone into this position:
readings are: Yaw = 30, Pitch = 90, Roll = 0
Once again, since the device rotates around the Yaw axis, this value changes and the others not.
Moving the device around the Roll axis:
readings are: Yaw = 0, Pitch = 90, Roll = -20.
Now what I can't understand. Moving the iPhone around a circle of radius R (R > 0), like in this figure:
Yaw changes meanwhile Pitch and Roll don't.
I would have expected Yaw had remained unchanged and Roll had changed.
How can compensate this since I am interested exclusively to rotations around Yaw axis made by the user ?
Another problem I have is drifting. The iPhone is in the position described in the second figure, taken in my hand at rest for a long time (1 minute or more). The Yaw constantly increase. Any idea how to compensate the drifting ?
Thank you in advance
UPDATE I have followed Kay suggestions but nothing changes. Some more detail on my code. I would like to use Yaw to rotated an UIImage only when user rotate the device around the Yaw axis. This works, but when the user rotates around its own vertical axis the Yaw changes. I suppose that is not correct since when the user moves around its vertical axis, the devices doesn't rotate around its own Yaw axis. May be I am wrong. This is my original code:
- (void)handleDeviceMotion:(CMDeviceMotion*)motion {
CMAttitude *attitude = motion.attitude;
NSLog(@"Yaw %f ",attitude.yaw * 180 / M_PI);
NSLog(@"Pitch %f ",attitude.pitch * 180 / M_PI);
NSLog(@"Roll %f ",attitude.roll * 180 / M_PI);
image.transform = CGAffineTransformMakeRotation(-attitude.yaw);
}
This is the code after Kay suggestion:
- (void)handleDeviceMotion:(CMDeviceMotion*)motion {
CMAttitude *attitude = motion.attitude;
if (startAttitude == 0) {
startAttitude = attitude;
}
[attitude multiplyByInverseOfAttitude:startAttitude];
NSLog(@"Yaw %f ",attitude.yaw * 180 / M_PI);
NSLog(@"Pitch %f ",attitude.pitch * 180 / M_PI);
NSLog(@"Roll %f ",attitude.roll * 180 / M_PI);
image.transform = CGAffineTransformMakeRotation(-attitude.yaw);
}
I start device motion monitoring with
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical toQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error){
[self performSelectorOnMainThread:@selector(handleDeviceMotion:) withObject:motion waitUntilDone:YES];
}];
[Completely revised]
At a first glance I mistook the situation as running into some typical problems related to Euler Angles. Because they are close to this problem area and really important to bear in mind, I leave this part of the original answer. Euler Angle problems are:
1: Yaw=30°, 2: Pitch=90°
or by 1: Pitch=90°, 2: Roll=30°
But as you stated in your comment the true culprit seems to be the reference frame. Starting with iOS 5.0 Apple enhanced the sensor fusion algorithm and considers magnetic field data as well to calculate CMAttitude. Although there is still the old method startDeviceMotionUpdates, it now uses a default reference CMAttitudeReferenceFrameXArbitraryZVertical so that all measurement is related to "Z axis is vertical and the X axis points in an arbitrary direction in the horizontal plane".
In order to get your data in respect to the reference at start (or any other rotation) you need to store the CMAttitude
instance you want as reference and then use CMAttitude
's multiplyByInverseOfAttitude method to transform every new attitude i.e. at the beginning of your handleDeviceMotion
method.
I think this is related to a bug in iOS 6 like the one I described in Drifting yaw angle after moving fast as it used to work fine in previous version. I filed a bug - let's see when they gonna fix it.
UPDATE 2
As comments and chat showed the goal was to control a robot by just tilting the device. In this case knowing the full rotation state of the device is messing up control when changing the walking direction. Thus a pure accelerometer based approach using CMDeviceMotion.gravity is far more convenient.