I am reading the this tutorial and I got a bit confused. Why is the x value changing with the pitch when the pitch is a rotation about the x axis.
direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));
direction.y = sin(glm::radians(pitch));
direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
I get how the rest are changing, but not the cos(glm::radians(pitch)) in direction.x. How come this is the case ?
First of all, your pitch seems to rotate around Z, not X.
Second, Euler angles are applied sequentially, in this case Yaw is applied first, then Pitch. So as you noticed, yaw does not affect Y axis, as expected. But once it is applied, pitch rotates around the new Z axis, instead of the original one. If you set yaw=0, you will see that pitch no longer affects direction.z (since it is always 0).
Related
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.
I am trying to convert the orientation of an OpenVR controller that I have stored as a glm::vec3 of Euler angles into a glm::fquat and back, but I get wildly different results and the in-game behavior is just wrong (hard to explain, but the orientation of the object behaves normally for a small range of angles, then flips in weird axes).
This is my conversion code:
// get `orientation` from OpenVR controller sensor data
const glm::vec3 eulerAnglesInDegrees{orientation[PITCH], orientation[YAW], orientation[ROLL]};
debugPrint(eulerAnglesInDegrees);
const glm::fquat quaternion{glm::radians(eulerAnglesInDegrees)};
const glm::vec3 result{glm::degrees(glm::eulerAngles(quaternion))};
debugPrint(result);
// `result` should represent the same orientation as `eulerAnglesInDegrees`
I would expect eulerAnglesInDegrees and result to either be the same or equivalent representations of the same orientation, but that is apparently not the case. These are some example values I get printed out:
39.3851 5.17816 3.29104
39.3851 5.17816 3.29104
32.7636 144.849 44.3845
-147.236 35.1512 -135.616
39.3851 5.17816 3.29104
39.3851 5.17816 3.29104
32.0103 137.415 45.1592
-147.99 42.5846 -134.841
As you can see above, for some orientation ranges the conversion is correct, but for others it is completely different.
What am I doing wrong?
I've looked at existing questions and attempted a few things, including trying out every possible rotation order listed here, conjugating the quaternion, and other random things like flipping pitch/yaw/roll. Nothing gave me the expected result.
How can I convert euler angles to quaternions and back, representing the original orientation, using glm?
Some more examples of discrepancies:
original: 4; 175; 26;
computed: -175; 4; -153;
difference: 179; 171; 179;
original: -6; 173; 32;
computed: 173; 6; -147;
difference: -179; 167; 179;
original: 9; 268; -46;
computed: -170; -88; 133;
difference: 179; 356; -179;
original: -27; -73; 266;
computed: -27; -73; -93;
difference: 0; 0; 359;
original: -33; 111; 205;
computed: 146; 68; 25;
difference: -179; 43; 180;
I tried to find a pattern to fix the final computed results, but it doesn't seem like there's one easy to identify.
GIF + video of the behavior:
Full video on YouTube
Visual representation of my intuition/current understanding:
The above picture shows a sphere, and I'm in the center. When I aim the gun towards the green half of the sphere, the orientation is correct. When I aim the gun towards the red half of the sphere, it is incorrect - it seems like every axis is inverted, but I am not 100% sure that is the case.
32.7636 144.849 44.3845
-147.236 35.1512 -135.616
Those are the same. Left 33 or right 147. You are 180 from each other. Now look up 145 - that's past up that's 35 from horizon, your back is arched.
Now roll to get your back to the sky.
If you need to use Euler, try to keep pitch in -90 to +90, and roll in -180 to +180;
if (pitch > 90) {
pitch -= 90;
yaw += 180;
roll += 180;
}
if (roll > 180) {
roll = 360 - roll;
}
or something like that.
The definition of the any kind of 3 angles to represent a rotation is not only given by the order of the rotations, if they are extrinsic or extrinsic, but also which interval of angles you choose when you define the mapping of every element of the 3D Rotation Group to a tuple of 3 angles.
Unfortunately, it is common for software libraries to fail to explicit mention which subset of angles they support, so typically it is necessary to either test their behaviour or to direct inspect the source code. For a relevant issue regarding glm see https://github.com/g-truc/glm/issues/569, and see https://github.com/robotology/idyntree/pull/504 for a related discussion on another library on which I work.
In glm master, from a quick inspection of the code (https://github.com/g-truc/glm/blob/6543cc9ad1476dd62fbfbe3194fcf19412f0cbc0/glm/gtc/quaternion.inl#L10) and from the fact that in C++ asin image is roughly (-90.0, 90.0) and atan2 image is roughly (-180.0, 180.0), the assumed interval in glm seems to be roughly (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0), so by limiting the second angle (the yaw, using the names that you are using) to (-90.0, 90.0). So, what you are seeing at the GLM level is basically a mapping from the provided angles to equivalent angles in the (-180.0, 180.0) x (-90.0, 90.0) x (-180.0, 180.0) range.
However, the fact that this angles are equivalent depends on how they are used, i.e. if you have a library that clamps the euler angles outside the used ranges, instead of converting it to equivalent angles, then you will obtain strange results. For this reason, I think it would be interesting to understand your problem to know how this angles are generated (the middle angles in particular seems to be part of the range (-90, 270) that is a strange, even if valid choice) and how they are interpreted to render the object in the visualization. Once you understand that, even if the rendering function works fine for angles in the original applications ranges, you can write a function to map "original application angles" to "GLM angles" and its inverse, that you can use for your original purpose.
Roughly following tony's advice and after some trial&error and pattern identification, I managed to figure out a way to restore the original values after the conversion.
ox, oy, and oz are the original pitch, yaw, and roll in degrees, before any conversion;
fx, fy, and fz are the new pitch, yaw, and roll in degrees, obtained after converting "Euler -> quaternion -> Euler" (via glm::degrees(glm::eulerAngles(glm::normalize(quaternion)))).
if (oy > 90.f)
{
fx -= 180.f;
fy -= 180.f;
fy *= -1.f;
fz += 180.f;
if (ox > 0.f)
{
fx += 360.f;
}
}
The above code seems to make the original angle values and the one after the conversion match exactly. While it answers the original question, it doesn't solve my actual issue... I was converting to a quaternion in order to smoothly interpolate to another angle. However, it seems that using glm::mix on the quaternion after the conversion results - again - in very unpredictable rotations.
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.
I have a player in the shape of a sphere that can move around freely in the directions x and z.
The players current speed is stored in a vector that is added to the players position on every frame:
m_position += m_speed;
I also have a rotation matrix that I'd like to rotate in the direction that the player's moving in (imagine how a ball would rotate if it rolled on the floor).
Here's a short video to help visualise the problem: http://imgur.com/YrTG2al
Notice in the video when I start moving up and down (Z) as opposed to left and right (X) the rotation axis no longer matches the player's movement.
Code used to produce the results:
glm::vec3 UP = glm::vec3(0, 1, 0);
float rollSpeed = fabs(m_Speed.x + m_Speed.z);
if (rollSpeed > 0.0f) {
m_RotationMatrix = glm::rotate(m_RotationMatrix, rollSpeed, glm::cross(UP, glm::normalize(m_Speed)));
}
Thankful for help
Your rollSpeed computation is wrong -- e.g., if the signs of m_Speed.x and m_Speed.z speed are different, they will subtract. You need to use the norm of the speed in the plane:
float rollSpeed = sqrt(m_Speed.x * m_Speed.x + m_Speed.y * m_Speed.y);
To be more general about it, you can re-use your cross product instead. That way, your math is less likely to get out of sync -- something like:
glm::vec3 rollAxis = glm::cross(UP, m_speed);
float rollSpeed = glm::length(rollAxis);
m_RotationMatrix = glm::rotate(m_RotationMatrix, rollSpeed, rollAxis);
rollSpeed should be the size of the speed vector.
float rollSpeed = glm::length(m_Speed);
The matrix transform expects an angle. The angle of rotation will depend on the size of your ball. But say it's radius r then the angle (in radians) you need is
angle = rollSpeed/r;
If I understood correctly you need a matrix rotation which would work in any axis direction(x,y,z).
I think you should write a rotate() method per axis (x, y, z), also you should point to direction on which axis your direction points, you should write direction.x or direction.y or direction.z and rotation matrix will understand to where the direction vector is being point.
I'm working on a game that involves moving around a planet and have currently got the player moving in one direction using forward and back using:
if( forward)
yAngle = yAngle + 0.005f;
if( backward)
yAngle = yAngle - 0.005f;
if(left)
zAngle = zAngle - 0.005f;
if(right)
zAngle = zAngle + 0.005f;
Then these angles are put into the matrix.
The character rotates perfectly using the Z Rotation and moves forward and backwards orbiting the planet using the Y rotation but will only move in that direction.
How would I calculate the X and Y rotations so the character will move around the planet in the right direction?
The rotation matrix for a rotation around the x-axis of a 3D Cartesian coordinate system is
1 0 0
0 cos(xAngle) sin(xAngle)
0 -sin(xAngle) cos(xAngle)
The matrix for a rotation around the y-axis is
cos(yAngle) 0 -sin(yAngle)
0 1 0
sin(yAngle) 0 cos(yAngle)
The matrix for a rotation around the z-axis is
cos(zAngle) sin(zAngle) 0
-sin(zAngle) cos(zAngle) 0
0 0 1
You can reverse the sense of rotation of any of those matrices by reversing the signs of its sine terms.
There is no unique definition or matrix for a combined rotation about two axes, however, because the result depends on the order in which the rotations are performed. Of particular relevance to your case, it is still different if the rotations are performed in a series of small, alternating steps.
Therefore, rather than tracking overall rotation angles, I suggest keeping a transformation matrix describing the current orientation (so that, for example, the current direction to the ship is M * (1, 0, 0)). Update that on each tick, or otherwise as needed, by multiplying it by incremental rotation matrices of the above forms describing the movement since the last update. This should give you smooth movement consistent with intuitive controls.
I suggest trying something like this:
if (forward) {
xAngle += 0.005f * (cos(zAngle));
yAngle += 0.005f * (sin(zAngle));
}
if (backward) {
xAngle -= 0.005f * (cos(zAngle));
yAngle -= 0.005f * (sin(zAngle));
}
I'm going on a hunch here, but try it out!