Combining Euler angles to get the correct orientation

Started by
9 comments, last by RobM 10 years, 4 months ago
I've been working on an issue for around 2 weeks now and I'm so close but yet so far from a solution.

I've been trying to create an orientation matrix from my IMU sensor (gyro, accelerometer and magnetometer) and I've got yaw and pitch working but I'm having a problem with roll.

In my current setup (I've tried all sorts of quaternion and matrix solutions), I'm computing the figure for yaw and this I can do regardless of how the sensor is tilted. It also returns the true yaw when the sensor is upside down, i.e. pitched by 180 degrees. So yaw gives me a 0-359 reading for which direction the sensor's +z axis is pointing in pretty much any orientation.

I've also managed to get the pitch in degrees between 0-359 and if I create myself a matrix using MatrixRotationYawPitchRoll with zero roll, I can happily move the sensor around and yaw and pitch work perfectly. If I hold the sensor flat and roll it, half way down, my screen object tends to go a bit strange in order to get itself in the right orientation for it being upside down and facing the opposite direction yaw-wise.

So just to clarify that, if I hold the sensor flat and z is pointing zero degrees north, if rotate it on that z axis 180 degrees, my avatar pitches over and ends up facing in the opposite direction but upside down as expected. If, from the original flat position again, I _roll_ it 180 degrees, my avatar [eventually] ends up upside down and facing me - as expected.

Now as it looks as though pitch and yaw work correctly, I naively thought it was a simple case of adding roll and that would be it. That's obviously not the case.

So the directx YawPitchRoll method apparently applies roll, then pitch, then yaw. I've read in several places that in order for this to work correctly, we need to do things like have pitch going from -90 to 90 only and I can understand that if you yaw'd around 180 degrees, the limited pitch movement combined with full yaw still gives you effectively 360 degrees of pitch. But what do I need to do for roll in this situation?

I know order matters and I guess it doesn't matter which one you choose as long as you stick to it so the DX roll, pitch, yaw seems to be the one to go for, especially as there's already a method to create the matrix.

So given the order is roll, pitch and yaw, which of these do I need to make full 0-360 and which need to be -90 to 90?

Sensor-wise, it returns degrees per second around each axis which I use to smooth out the accelerometer vector. The accelerometer vector always points toward gravity (down) so if you rotate the sensor, the accelerometer vector returned is effectively the inverse of that. I use this vector to get my pitch angle and I also couple it with the magnetometer vector to get the yaw. Roll is slightly more tricky because you have to effectively remove pitch in order to get the roll.

I've ordered a book on rotations and quaternions but until that arrives I wondered if any of you could give me a quick hand? I'm getting close to being out of ideas.

Thanks
Advertisement
So given the order is roll, pitch and yaw, ...

Well, without telling how exactly to interpret this order, the statement is almost meaningless. With yaw meaning the rotation around the world's up vector, pitch meaning the rotation around the side vector, and roll meaning the rotation around the forward vector, the usual order in which one wants to apply rotation is actually yaw, followed by pitch, followed by roll. In D3D (which uses row vectors) this means a matrix composition like

yaw * pitch * roll

But this would apply the rotations around world axes. You probably want them to be applied around the local axes. This requires to undo/redo previous rotations before applying the next one, what in fact yields in the composition

roll * pitch * yaw

Is that what you mean?

… which of these do I need to make full 0-360 and which need to be -90 to 90?

With the definition given above, yaw is used in range [0,360) or perhaps [-180,+180), pitch is used in [-90,+90] or perhaps [0,180] (north pole to south pole when looking from center of world), and roll is again in [0,360) degrees.

EDIT:

What came to my mind is this: If you have yaw then you have the forward vector, too. You further have the up (or down) vector. Using cross-products, you can compute the side vector and re-compute the forward vector (because I assume the up / down vector to be better determined in extreme orientations). The 3 vectors (assumed to be normalized to unit length) give the orientation matrix.

Is it actually needed to have Euler angles? If yes, then the symbolic orientation matrix as computed by roll * pitch * yaw can be seen as an equation system when being set equal to the (numeric) orientation matrix build from orientation vectors, as done above. This system can be solved (ready to use solutions can be found by your favorite internet search machine).

Have you tried such a solution already?

Thanks Haegarr..

I can't quote properly as I'm on my iPhone, but yes, it's needs to be rotations around the local axes.

I do need Euler angles (or at least I assume I do) because I need to use the gyro values to smooth out the noisiness of the accelerometer. The gyros drift over time but are accurate short term and the accelerometer is the opposite so I just use a complementary filter to keep the values steady - this works really nicely and avoids things like tapping the sensor (or at least gets rid of a lot of it).

I haven't tried yaw first 0-360, then pitch -90 to 90 and roll 0-360 so I'll give that a go later.

The D3DXMatrixRotationYawPitchRoll documentation on msdn says roll, pitch then yaw and its around the object's local axis which is why i started using it

I am going to ignore your question and tell you about how to solve the problem of integrating your sensor data. :)

The "correct" way to integrate the sensor data is to use some variation of a Kalman filter. Unfortunately, a Kalman filter is a complicated mathematical beast as it is, and in order to estimate a rotation a plain Kalman filter won't do. The last time I looked into this, the fashionable variation to use in this case was an unscented Kalman filter.

If you get overwhelmed by the complexity of the situation, there are dirty hacks you can use. A practical one would be to have a quaternion representing your current estimate of attitude, take the 3 inputs of the gyros as an angular velocity, integrate it into the quaternion with the appropriate `delta time'. When you read the magnetometer data (hopefully it's a 3-axis magnetometer), you get an idea of where the magnetic North is. By your current estimate of attitude, you expected magnetic North to be somewhere else, and you can compute the "smallest" rotation that would make your attitude agree with the input from the magnetometer (a cross product will do the trick): Go only a small fraction of the way there, because magnetometer data is very noisy. Similarly, if you have reason to believe your object doesn't have some serious linear acceleration, you can assume that the accelerometer is telling you where "up" is, and you should modify your attitude to go only a fraction of the way towards agreeing with it, because accelerometer data is noisy.

That's probably where I would start, but whether the hackish handling of the accelerometer data works or not depends on your application. Since this is a game forum, I assume your sensors are in a game controller, or something like that, and not on a rocket, so you should be fine.

I am going to ignore your question and tell you about how to solve the problem of integrating your sensor data. :)

The "correct" way to integrate the sensor data is to use some variation of a Kalman filter. Unfortunately, a Kalman filter is a complicated mathematical beast as it is, and in order to estimate a rotation a plain Kalman filter won't do. The last time I looked into this, the fashionable variation to use in this case was an unscented Kalman filter.

If you get overwhelmed by the complexity of the situation, there are dirty hacks you can use. A practical one would be to have a quaternion representing your current estimate of attitude, take the 3 inputs of the gyros as an angular velocity, integrate it into the quaternion with the appropriate `delta time'. When you read the magnetometer data (hopefully it's a 3-axis magnetometer), you get an idea of where the magnetic North is. By your current estimate of attitude, you expected magnetic North to be somewhere else, and you can compute the "smallest" rotation that would make your attitude agree with the input from the magnetometer (a cross product will do the trick): Go only a small fraction of the way there, because magnetometer data is very noisy. Similarly, if you have reason to believe your object doesn't have some serious linear acceleration, you can assume that the accelerometer is telling you where "up" is, and you should modify your attitude to go only a fraction of the way towards agreeing with it, because accelerometer data is noisy.

That's probably where I would start, but whether the hackish handling of the accelerometer data works or not depends on your application. Since this is a game forum, I assume your sensors are in a game controller, or something like that, and not on a rocket, so you should be fine.

I did look at the Kalman filter and then decided against it in favour of the complementary filter - it really does work nicely, 93% of the gyro combined with 7% of the accelerometer gives great results.

So your method is interesting, but I'm not sure it's that different to what I'm already doing, just perhaps reversed? I work out the yaw pitch roll from the accelerometer/magnetometer value and then smooth it with the gyro - is that about the same?

Edit: it's absolutely game-related! :)
I was just thinking about Haegarrs 360,180,360 post...

If my screen widget and sensor is in it's normal position, standing up and facing away from me and I roll the sensor 180 degrees, it will make the screen widget upside down and still facing away, but the sensor's yaw reading will be 180 degrees (assuming in the normal position it is zero degrees) because it had now flipped

So will I need to tailor my yaw reading depending on whether the y accelerometer reading is > or < than zero? At the moment it just returns the absolute heading from the sensor regardless of whether its up or down.

This is where I find it all a bit confusing...


I did look at the Kalman filter and then decided against it in favour of the complementary filter - it really does work nicely, 93% of the gyro combined with 7% of the accelerometer gives great results.

So your method is interesting, but I'm not sure it's that different to what I'm already doing, just perhaps reversed? I work out the yaw pitch roll from the accelerometer/magnetometer value and then smooth it with the gyro - is that about the same?

So why are you not doing all the operations using quaternions? What I described is not hard to do...

My understanding of quaternions, even though I have the ability to use them in my engine, is practical only. The blocker for me is this use case - the sensor is in it's 'normal' position again where the acceleration vector is pointing directly down. I can create a quaternion with a vector pointing up and use the yaw for the rotation around that vector. That works fine. But then if you pitch the sensor 90 degrees, the vector part is fine but the rotation around that vector is now not yaw, not in terms of the sensor at least, it's pitch and roll. So do we build the quaternion in a different way depending on the orientation of the sensor? I thought about this for days (literally) and it just felt wrong somehow. With my relative inexperience in quaternions (I only use them for slerping my animations), I decided it might be easier for me to understand using Euler angles.
I just have one more question on this if someone can help.

My accelerometer vector works like this (+x=right, +y=up,+z=away from you):

When the sensor is at rest sitting on the table, the vector is pointing directly down. When you pitch the device (with no roll), the vector rotates around the z axis (z is zero). When you roll the device (with no pitch), the vector rotates around the x axis (x is zero).

I'd like to get the angles of pitch and roll from this vector. I can easily get the pitch angle by using atan2 from x and y, but when you introduce roll, this becomes problematic when roll gets to 90+ degrees because the roll also uses the y component. So if roll is at 90 degrees, x is zero, z is 1 and y is 0. When you move through 90 degrees, y becomes negative and this messes up the pitch calculation because it flips it 180 degrees.

Am I on the right track here? The accelerometer vector does point in a unique position for every combination of pitch and roll so I should be able to compute them somehow.

Pulling my hair out on this a bit so I'd really appreciate any more help.

Thanks


When the sensor is at rest sitting on the table, the vector is pointing directly down.

That makes no sense. The acceleration should be pointing directly up when the device is resting on the table: The table is pushing the device up, which is what is being detected.

This topic is closed to new replies.

Advertisement