I'm developing a small indoor navigation app in which I use the gyro and the compass for the device orientation. I use the gyro to smooth the compass data. My sensor fusion looks like the following. This is my motionHandler where everything happens.
// Listen to events from the motionManager
motionHandler = ^ (CMDeviceMotion *motion, NSError *error) {
__block float heading;
heading = mHeading;
CMAttitude *currentAttitude = motion.attitude;
//Initial heading setting
if (lastHeading == 0 && heading != 0) {
updatedHeading = heading;
}
lastHeading = heading;
if (oldQuaternion.w != 0 || oldQuaternion.x != 0 || oldQuaternion.y != 0 || oldQuaternion.z != 0){
diffQuaternion = [self multiplyQuaternions:[self inverseQuaternion:oldQuaternion] :currentAttitude.quaternion];
diffQuaternion = [self normalizeQuaternion:diffQuaternion];
}
oldQuaternion = currentAttitude.quaternion;
diffYaw = RADIANS_TO_DEGREES([self yawFromQuaternion:diffQuaternion]);
quaternion = currentAttitude.quaternion;
//Get Pitch
rpy.pitch = -[self pitchFromQuaternion:quaternion];
rpy.pitch += M_PI/2;
//Use Yaw-Difference for Heading
updatedHeading = updatedHeading - diffYaw;
//Heading has to be between 0 and 360 degrees
if (updatedHeading < 0) {
updatedHeading = 360 + updatedHeading;
}
else if (updatedHeading > 360) {
updatedHeading -= 360;
}
//fusionate gyro estimated heading with new magneticHeading
updatedHeading = (19.0*updatedHeading + 1.0*heading)/20.0;
//generate queternion
rotation = [self createFromAxisAngle:0 :rpy.pitch :DEGREES_TO_RADIANS(updatedHeading)];
};
The actual sensor fusion formula is this line: updatedHeading = (19.0*updatedHeading + 1.0*heading)/20.0;
.
And this is my didUpdateHeading-function which receives the newest heading-information:
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
// Get new heading
mHeading = newHeading.magneticHeading;
mHeading += 90;
if (mHeading > 360) {
mHeading -= 360;
}
}
diffYaw
is the change of the heading computed by the gyroscope. rotation
ist the final quaternion.
This works perfect, except one particular case: at the transition between 0 und 360 degrees.
If updatedHeading
is near to but smaller than 360 and mHeading
is just above 0 the result moves in a circle. For example if updatedHeading
= 355 and mHeading
= 5, the correct result should be between 360 and 5. But my formula computes 337,5 degrees, which is clearly completly wrong!
There have to be any common workarounds for this problem, I thinkā¦
I usually do something like the following in these types of angle computations:
where angleDiff() is:
You may want to get updatedHeading back in the 0-360 range after this by:
Your example gives 355.5 with this computation.