Calculating yaw angle without calibration - calibration

Suppose we have accelerometer and magnetometer. I'm wondering if there is an approach to calculate yaw angle without calibrating magnetometer if -
Yaw angle relative to the magnetic north is not required to be accurate; so when the device pointing to magnetic north, yaw angle is not required to be 0, it can be any degree. But the yaw angle needs to be accurate relative to rotation, means when the device rotates, the change of yaw angle needs to correctly reflect the change of actual degrees.
thanks,

Ted- I agree that calibrating magnetometers is a pain, but you do need each axis to give the same readings if they were pointed in the same direction. You do not need to know the inclination, declination or magnetic field for your location, however, to get the bearing from magnetic north.

This code fragment will point you in the right direction if you presumably haven't already found it.
motionManager.startDeviceMotionUpdatesUsingReferenceFrame(
CMAttitudeReferenceFrame.XArbitraryCorrectedZVertical,
toQueue: NSOperationQueue.currentQueue()!,
withHandler: {
[unowned self] (data: CMDeviceMotion?, error: NSError?) in
// ignore errors
if let gotData = data {
print(self.motionManager.deviceMotion!.attitude.yaw)
}
}
)

Related

Use Magnetometer and Accelerometer to calculate accurate Yaw

I'm currently using CMMotionManager attitude to get roll, pitch and yaw. However, after running for a few minutes, yaw is drifting and becoming not accurate.
I read there is a way to calculate the yaw using a combination of accelerometer and magnetometer that will keep yaw accurate with a compensation for the constant drift, however, I haven't yet found a working formula.
Here is a little part of my code that I use for getting motion updates.
motionManager.deviceMotionUpdateInterval = 0.1
motionManager.showsDeviceMovementDisplay = true
motionManager.startDeviceMotionUpdates(using: .xMagneticNorthZVertical, to: OperationQueue.current!)
{ deviceManager, error in
if let deviceManager = deviceManager {
let roll = deviceManager?.attitude.roll
let pitch deviceManager?.attitude.pitch
let yaw = deviceManager?.attitude.yaw // Get drifted over time
...
Any idea?
UPDATE
I could compensate the yaw drift by using the following formula:
Create initial reference point (fixed world frame reference) using magnetometer:
m_w = (m_x,m_y,m_z)
Take current magnetomer point (also fixed world frame reference) using magnetometer:
n_w = (n_x,n_y,n_z)
Convert projected reading to angles
a = atan2(m_z,m_x)
b = atan2(n_z,n_x)
Yaw drift can be calculated as follows
y_d = (a-b) * 180 / PI
Now deduct result from current accelerometer yaw.
WALLA!
Don't you mean giro? The MotionManager has a mode in where it mixes 3 inputs, Accelerometer, Gyroscope and the Magnetometer.
For this you gotta set it up to "Device motion"
Device motion. Call the startDeviceMotionUpdates(using:) or
startDeviceMotionUpdates() method to begin updates and periodically
access CMDeviceMotion objects by reading the deviceMotion
property. The startDeviceMotionUpdates(using:) method (new in iOS 5.0)
lets you specify a reference frame to be used for the attitude
estimates.
https://developer.apple.com/documentation/coremotion/cmmotionmanager
However, you must be aware that this increases the battery consumption quite a bit.

Calculate angle from the floor

I need to find an specific angle from iPhone CoreMotion data (pitch, yaw, roll, or quaternions). Lets imagine two lines. The first one should go perpendicularly from phone to the floor. Second one should point to the place where camera is looking at (if camera was working, then start of this line would be at the camera, and the end at the place displayed in the center of camera preview). And I need to find an angle between these two lines. I have no idea where to start, can someone help?
I have all the data from CoreMotion, so pitch, yaw, roll / gravity x,y,z, Attitude.quaternion/rotationMatrix.
If you have all parameters then hope in this way you will be able to find the required angle
θ = (1/Sin)(opposite/hypotenuse)
Needed to change way of looking at this. First I realized, that yaw was not affecting this at all. The solution was Pythagoras pattern with pitch and roll as parameters, so:
sqrt(pow(pitch, 2) + pow(roll, 2))
Actually this is still not working best when phone is slightly rotated over yaw axis. Don't know why, I'll try to figure it out. But it's small error, and it's only visible, when pitch is close to 90 degrees.

Transforming Pitch and Roll values in iOS

I have been trying to compare the pitch and roll pattern (2 second recording) readings.
What I have done is record a Pitch and Roll Values for 10 seconds. In those 10 seconds some similar motion is applied to the device.
When I plot these on graph, the values are also same.
This only happens when providing motion to the device from same orientation/position. For example, device is lying down on the table and a motion is provided to the device. All the readings are same.
But if the device is rotate 180 deg. ccw, the readings are inverted.
Is there a way I can achieve same reading for every position? Via applying some transformation formula? I have done it for acceleration values using pitch roll and yaw. But, don't know how to achieve this for pitch and roll itself.
Basically, what I want to achieve is that the Pitch and Roll values should be independent of yaw.
Here is the plot for pitch values.. all the readings are same expect the two starting from 1.5 in the graph. These were the two times when the device was rotated 180 deg. ccw
UPDATE:
I tried to store the CMAttitude in NSUSerDefaults and then applied the multiplyByInverseOfAttitude. But still the graph plot is inverse.
CMAttitude *currentAtt = motion.attitude;
if (firstAttitude)//firstAttitude is the stored CMAttitude
{
[currentAtt multiplyByInverseOfAttitude:firstAttitude];
}
CMQuaternion quat = currentAtt.quaternion;

Gyro angle way off on iOS

When I start the motion manager, keeping the phone basically still in my hand, I get erroneous values for the attitude. To get the rotation value, I use the CMAttitude object:
CMDeviceMotionHandler motionHandler = ^(CMDeviceMotion *motion, NSError *error) {
[self calculateNewPosition:motion];
_rotationMatrix = [self rotationToMat:[motion attitude].rotationMatrix];
};
[_motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical toQueue:_motionQueue withHandler:motionHandler];
Now, I know that there is noise in the measurements with tiny sized gyros, and the gravity vector probably needs calibration, but this seems to be too much off. After 0.5-1 seconds, the value of the rotations goes from 0 to over 20°?! Two examples for roll, pitch and yaw:
-1.001736 22.637596 -0.197573
-0.095075 29.075712 -0.014112
If it was the position drifting, when I use double integration, I would understand, but the rotation coming directly from the sensors?
Do you have any idea why this happens?
I just realized by seeing pictures in this question: Gyroscope on iPhone and testing out a bit more, that values are zero for a little while when starting the gyro, then, according to my configured coordinate frame (Z vertical), the values are adjusted to the current position of the phone.
So if I started with the phone in my hand at a 20° pitch, the value for pitch will be 0 for some small amount of time, then switch to 20°. Which means I have to wait for the rotation matrix to be non-zero to start tracking rotations.

Detect 360° iOS device rotation using gyroscope

I want my app to be able to detect a device rotation while the device is held horizontally. I could use readings from a compass but I believe yaw values from the gyroscope would be more accurate. Given yaw readings, what would be the best algorithm to determine a 360° rotation (either clockwise or counter-clockwise). And it has to be a full 360°, not just turning the phone 180° in one direction and back 180° in the opposite direction.
You would use CoreMotion to get the rotation about the vertical axis. You need to add the delta of rotation events. Every time more than a minimal value is in a different direction than the previous you reset your starting point. Then when you arrive at either plus or minus 360 degrees from this start point you have the rotation.
Here is an idea assuming that you can obtain the readout in short intervals, and that the yaw can be zeroed at a specific start point. This is different from the other answer, which detects full circles from a continuously adapted start point.
In this approach, we keep comparing the current yaw to the previous yaw, asking whether a checkpoint at 180 degrees, or PI has been passed. Initially, the checkpoint flag cp_pi is NO, and passing it in either direction toggles its state. Note that yaw changes its sign in two places, at the zero point and again at PI to -PI.
Assuming your object has two properties that are persistent between ticks of the detector, BOOL cp_pi; and float prev_yaw;, we consider that d_yaw is less than PI for crossing 0 and larger than PI for crossing at the opposite end of your circle. When crossing the opposite end, we toggle cp_pi. When cp_pi is YES while crossing 0, we are guaranteed to have passed a full circle - since otherwise, cp_pi would have been toggled back to NO:
-(void)tick
{
float yaw = [self zeroedYaw];
if ((fabs(yaw) == PI) || (yaw == 0.0f)) return;
if (yaw * prev_yaw < 0)
{
float d_yaw = fabs(yaw - prev_yaw);
if (d_yaw > PI)
{
cp_pi = ! cp_pi;
}
else if (cp_pi)
{
// fire detection event
}
}
prev_yaw = yaw;
}
Note that in order to make our life easier, we skip the detector function entirely if yaw is sitting right on one of the checkpoints.

Resources