Finding a quaternion of rotation between two quaternions - c++

I am trying to understand quaternion rotations and have written these two code snippets to rotate a unit vector along the X-axis to the Y-axis.
// Approach 1
Eigen::Vector3f a(1,0,0), b(0,1,0);
Eigen::Quaternionf qr;
qr.setFromTwoVectors(a,b);
// Approach 2
Eigen::Quaternionf q1(0,1,0,0), q2(0,0,1,0), qr_alt;
qr_alt = q1.inverse() * q2; // q2 = q1 * delta_q (delta_q = rotation quaternion)
However, these two quaternions are not the same. When converted to Euler angles, qr_alt results in pi radians in yaw while qr correctly results in pi/2 radians in yaw.
What is the correct way to calculate the rotation angle between two quaternions?

Related

Quaternion rotation ignoring yaw

I'am working with Quaternion and one LSM6DSO32 captor gyro + accel. So I fused datas coming from my captor and after that I have a Quaternion, everything works well.
Now I'd like to detect if my Quaternion has rotated more than 90° about a initial quaternion, here is what I do, first I have q1 is my initial quaternion, q2 is the Quaternion coming from my fusion data, to detect if q2 has rotated more than 90° from q1 I do :
q_conj = conjugateQuaternion(q2);
q_mulitply = multiplyQuaternion(q1, q_conj);
float angle = (2 * acos(q_mulitply.element.w)) * RAD_TO_DEG;
if(angle > 90.0f)
do something
this is works very well I can detect if q2 has rotated more than 90°. But my "problem" is I also detect 90° rotation in yaw, and I don't want integrate yaw in my test. Is it possible to nullify yaw (z component in my quaternion) without modify w, x and y component ?
My final objective is to detect a rotation more than 90° but without caring yaw, and I don't want to use Euler angle because I want avoid Gimbal lock
Edit : I want to calculate the magnitude between q1and q2 and don't care about yaw
The "yaw" of a quaternion generally means q_yaw in a quaternion formed by q_roll * q_pitch * q_yaw. So that quaternion without its yaw would be q_roll * q_pitch. If you have the pitch and roll values at hand, the easiest thing to do is just to reconstruct the quaternion while ignoring q_yaw.
However, if we are really dealing with a completely arbitrary quaternion, we'll have to get from q_roll * q_pitch * q_yaw to q_roll * q_pitch.
We can do it by appending the opposite transformation at the end of the equation: q_roll * q_pitch * q_yaw * conj(q_yaw). q_yaw * conj(q_yaw) is guaranteed to be the identity quaternion as long as we are only dealing with normalized quaternions. And since we are dealing with rotations, that's a safe-enough assumption.
In other words, removing the "Yaw" of a quaternion would involve:
Find the yaw of the quaternion
Multiply the quaternion by the conjugate of that.
So we need to find the yaw of the quaternion, which is how much the forward vector is rotated around the up axis by that quaternion.
The simplest way to do that is to just try it out, and measure the result:
Transform a reference forward vector (on the ground plane) by the quaternion
Take that and project it back on the ground plane.
Get the angle between this projection and the reference vector.
Form a "Yaw" quaternion with that angle around the Up axis.
Putting all this together, and assuming you are using a Y=up system of coordinates, it would look roughly like this:
quat remove_yaw(quat q) {
vec3 forward{0, 0, -1};
vec3 up{0, 1, 0};
vec3 transformed = q.rotate(forward);
vec3 projected = transformed.project_on_plane(up);
if( length(projected) < epsilon ) {
// TODO: unsolvable, what should happen here?
}
float theta = acos(dot(normalize(projected), forward));
quat yaw_quat = quat.from_axis_angle(up, theta);
return multiply(q, conjugate(yaw_quat));
}
This can be simplified a bit, obviously. For example, the conjugate of a axis-angle quaternion is the same thing as a quaternion of the negative angle around the same axis, and I'm sure there are other possible simplifications here. However, I wanted to illustrate the principle as clearly as possible.
There's also a singularity when the pitch is exactly ±90°. In these cases the yaw is gimbal-locked into being indistinguishable from roll, so you'll have to figure out what you want to do when length(projected) < epsilon.

How to use quaternions to describe a rotation angle which is more than 360 degrees?

I'm trying to use quaternions to do rotation animation.
My algorithm creates Quaternions, and slerps every frame.
Here is my code to construct a quaternion by the axis and the rotation angle.
template <typename U>
Quaternion(Vector3<U> vec, const float& angle)
{
vec.normalize();
float cosa = cos(angle/2);
float sina = sin(angle/2);
w = cosa;
x = sina * vec.x;
y = sina * vec.y;
z = sina * vec.z;
}
Then I found that when I tried to rotate 4π radians, the animation does not work because the quaternion I created is equivalent to 0 degrees.
I wonder if quaternions can represent rotations over 360 degrees? Or is my animation algorithm in need of improvement?
I wonder if quaternions can represent rotations over 360 degrees?
No, it can not.
Quaternions between the range [360;720] will treated as rotations at the other direction: [-360;0].
And quaternions between the range [720*k; 720*(k+1)] will be treated as rotations [0;720].
If you use slerp for this kind of animation, quaternions are not good for them.
Quaternions can only slerp between angles which are smaller than 360.
If you still want to do this, use a different representation, like axis-angle.
Rotating be 360 degrees is the same as rotating by 0 degrees. To rotate by an angle alpha bigger than 360 simply rotate by alpha-360 or more general by alpha % 360.
(360 used as synonym for 2pi, you need to take care about degree vs radians of course. And not sure if thats a typo, but 360 degree is 2pi not 4pi)
PS: Actually I think there is nothing wrong with your code, and maybe you dont have to change anything. It's just your expectations that were wrong: You should get the same for a rotation by 4pi as for a rotation by 0.
Think of quaternions as instant rotations - rotating by 4π radians instantly is the same as doing nothing.
This is not what you want when you animate rotation of 4π radians over 20 seconds. You can solve it by creating an Euler Vector (a 3D vector whose direction represents the axis of rotation, same as in quaternion, while its length represents the speed/angle of the rotation), see https://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation. Later, multiply it by time passed and convert it into quaternion or 3D matrix depending on what your graphics wants.

How to rotate 3D camera with glm

So, I have a Camera class, witch has vectors forward, up and position. I can move camera by changing position, and I'm calculating its matrix with this:
glm::mat4 view = glm::lookAt(camera->getPos(),
camera->getTarget(), //Caclates forwards end point, starting from pos
camera->getUp());
Mu question is, how can I rotate the camera without getting gimbal lock. I haven't found any good info about glm quaternion, or even quaternion in 3d programming
glm makes quaternions relatively easy. You can initiate a quaternion with a glm::vec3 containing your Euler Angles, e.g glm::fquat(glm::vec3(x,y,z)). You can rotate a quaternion by another quaternion by multiplication, ( r = r1 * r2 ), and this does so without a gimbal lock. To use a quaternion to generate your matrix, use glm::mat_cast(yourQuat) which turns it into a rotational matrix.
So, assuming you are making a 3D app, store your orientation in a quaternion and your position in a vec4, then, to generate your View matrix, you could use a vec4(0,0,1,1) and multiply that against the matrix generated by your quaternion, then adding it to the position, which will give you the target. The up vector can be obtained by multiplying the quaternion's matrix to vec4(0,1,0,1). Tell me if you have anymore questions.
For your two other questions Assuming you are using opengl and your Z axis is the forward axis. (Positive X moves away from the user. )
1). To transform your forward vector, you can rotate about your Y and X axis on your quaternion. E.g glm::fquat(glm::vec3(rotationUpandDown, rotationLeftAndRight, 0)). and multiply that into your orientation quaternion.
2).If you want to roll, find which component your forward axis is on. Since you appear to be using openGL, this axis is most likely your positive Z axis. So if you want to roll, glm::quat(glm::vec3(0,0,rollAmt)). And multiply that into your orientation quaternion. oriention = rollquat * orientation.
Note::Here is a function that might help you, I used to use this for my Cameras.
To make a quat that transform 1 vector to another, e.g one forward vector to another.
//Creates a quat that turns U to V
glm::quat CreateQuatFromTwoVectors(cvec3 U, cvec3 V)
{
cvec3 w = glm::cross(U,V);
glm::quat q = glm::quat(glm::dot(U,V), w.x, w.y, w.z);
q.w += sqrt(q.x*q.x + q.w*q.w + q.y*q.y + q.z*q.z);
return glm::normalize(q);
}

Calculate the absolute position of the point having the position before rotation and rotation angles

How can I rotate the point (x, y, z) by angles (rx, ry, rz) about their respective axes?
That is, how do I determine the point (x1, y1, z1) resulting from the rotation of (x, y, z) by rotation angles (rx, ry, rz)?
Are there any DirectX routines which accomplish this?
What you are asking about is how to use Euler Angles for performing rotations. There are several conventions you can choose from, but it looks to me like you are interested in applying rotation about the Z axis, followed by rotation about Y and then rotation about X. For this you would post multiply by the matrix
where
c1 = cos(rx) s1 = sin(rx)
c2 = cos(ry) s2 = sin(ry)
cs = cos(rz) s3 = sin(rz)
There are several problems with this approach, one of the more common being gimbal lock. The preferred approach is to use one of the angle-axis formulations. The two most common of those are Unit Quaternion Rotations and Euler-Rodreigues Rotation Matrices. These can be composed to generate any of the 12 Euler Rotation matrices by explicitly defining three rotation axes and their associated rotation angles and then multiplying the resulting rotation representation in the reverse order they are to be applied to the vectors to be rotated.
DirectX uses Quaternions for performing rotations.
During my electronics(EM) classes I learnt converting cartesian to polar cordinates using the formula
x = r sinq cosf, y = r sinq sinf, z = r cosq
More Info Here
q is theta, f is phi.

rotate a Vector to reach orthogonality with another vector

I have 2 vectors (V1{x1, y1, z1}, V2{x2, y2, z2}) , and I want rotate V1 around X-Axis, Y-Axis and Z-Axis to be parallel to V2. I want to find 3 rotation angles.
Is there any general formula I can use to find them?
I would do that in this way:
A = V1xV2; //Cross product, this gives the axis of rotation
sin_angle = length(A)/( |V1| |V2|); //sine of the angle between vectors
angle = asin(sin_angle);
A_n = normalize(A);
Now you can build a quaternion with angle and A_n.
q = (A_n.x i + A_n.y j + A_n.z k)*sin(angle/2) + cos(angle/2);
And use these formulas to get your euler angles.
Do you really need the rotation angles, or is it a rotation matrix you're looking for. If the latter, you can do it the way it's done in OpenFOAM: http://github.com/OpenFOAM/OpenFOAM-2.1.x/blob/master/src/OpenFOAM/primitives/transform/transform.H#L45
Note that in OpenFOAM for vector the & operator denotes the inner product, the ^ operator the cross product and * is the outer product. The sqr function computes the element-wise squares, magSqr the square of the magnitude of a vector (i.e. v&v).