Please bear with my long question, I am trying to make it as clear as possible.
What i am trying to do is, get the attitude(roll pitch and yaw) when a picture is tak
I think the following things are necessary to manage to task:
First of all you need a good understanding of quaternions (skip this if you already made friends with them). I recommend OpenGL:Tutorials:Using Quaternions to represent rotation or The Matrix and Quaternions FAQ. It helps to bear in mind that (x, y, z) represent the axis to rotate around (not normalised) and w = cos (alpha/2) i.e. stands approximately for the amount of rotation.
As CMQuaternion
is just a struct thus it's hard to do all the calculations. Use a full functional quaternion class instead like cocoamath (you need at least Quaternion.h, .m and QuaternionOperation.m from trunk).
Now the basic considerations:
The difference (or sometimes stated as division) between two quaternions being defined as the angular displacement from one orientation to another might be the way to go. It is defined as
d = a-1 * b
So this expresses the delta from the current position to the target position.
Having this delta you need to define the conditions to met for considering the target orientation as reached. My first idea is to use the delta angle. This can be easily retrieved from the w component of the above calculated d quaternion by:
alpha = 2 * arccos (w)
The domain of arccos is restricted but this shouldn't be a problem in this case as we are especially interested in small values.
Maybe it's worth to emphasize that every 3D rotation has two unit quaternion representations, q and -q. This might be confusing but doesn't matter.
Update: So a bit of pseudo code would look something like:
CMQuaternion cmQ = attitude.quaternion;
// Get an instance of cocoamath's Quaternion for our currently reported quaternion
Quaternion current = [Quaternion initWithRe:(double)cmQ.w i:(double)cmQ.x j:(double)cmQ.y k:(double)cmQ.z];
// the complex conjugate and normalised to be on the safe side
Quaternion inverse = [current inverse];
// build the delta, assuming you have your stored direction as class member targetQuaternion
Quaternion diff = [inverse multiply:targetQuaternion];
float alpha = 2 * acos (diff.Re);
// maxDeltaAngle is your class member variable defining the angle from which you assume the position as restored (radians)
if (fabs (alpha) < maxDeltaAngle) {
// do my fancy camera things
}
Yes it's not that trivial at the beginning. As Ali stated in his answer, visualisation is an important issue as well. My solution just solves the raw maths part.
I am not saying that the following is the solution to your problem, I have no idea how user-friendly this will be but I believe it is worth a shot. I see two issues: how you store the attitude and how you visualize them.
Storage. I would save the attitude either as a quaternion or as a rotation matrix; CMAttitude provides both. (I personally prefer rotation matrices to quaternions as rotation matrices are easier to understand in my opinion. It is just a personal preference.)
Visualization. You are using 3 markers to bring the phone to the same attitude as the saved one. These 3 markers came from yaw, pitch and roll, effectively ruining the stability of your application. Instead of these 3 markers I would visualize the rotation between the saved attitude and the current one. One way of doing it is to project the rotated 3 basis vectors onto the phone's screen. The nice thing about rotation matrices is that they give you exactly this without any extra computation. For example, to visualize the
| 1 0 0 |
| 0 1 0 |
| 0 0 1 |
rotation this rotation matrix represents, I only need to plot 3 arrows from [0, 0, 0]
to (i) [1, 0, 0]
, (ii) [0, 1, 0]
and (iii) [0, 0, 1]
. That is, I only need to plot either the rows or the columns of the matrix (also depends on what you want whether the rows or the columns are better).
To get the rotation between a saved rotation matrix S
and the current rotation matrix C
you need to compute CTS (in words: C
transpose times S
). You have aligned the phone with the saved attitude if your rotation matrix is the one shown above.
An excellent tutorial on rotation matrices is the Direction Cosine Matrix IMU: Theory manuscript.
Another way of visualizing the rotation between the saved and the current attitude is to transform it to angle-axis form. Then, I would project the axis to the phone's screen. The user first aligns the phone with the axis, than needs to rotate around the axis to match the saved attitude. Quaternions basically represent the axis-angle form, although in a nontrivial way.
It requires significant further work on your part to alter this idea according to your needs; this is definitely not a complete answer. Nevertheless, I hope this help a bit.