Align to velocity vector using Quaternions

Started by
19 comments, last by nardove 14 years, 2 months ago
Hi, I am studying how to use quaternions to orientate an object to its velocity, I did lots of research on the web about the subject, but they all say the same thing and I cant work out how to implement the concepts, so I am asking for a bit of guidance. Having a velocity vector and location vector how can I align my 3D object to face in the same direction as the velocity? So far I have the following data: Vec3D new_dir = new Vec3D(velocity); new_dir.normalize(); Vec3D new_up = new Vec3D(0.0, 1.0, 0.0); new_up.normalize(); // do I need to normalize this vector? Vec3D crossP = new_dir.cross(new_up); crossP.normalize(); float dotP = new_dir.dot(new_up); float angle = new_dir.angleBetween(new_up, true); From that how can I create a Quaternion? and with that Quaternion How can I rotate the object? I am using Processing as programming language which is based on Java Many thanks rS
Advertisement
Quote: Vec3D new_up = new Vec3D(0.0, 1.0, 0.0);
new_up.normalize(); // do I need to normalize this vector?
The question of whether the vector needs to be normalized should be easy enough to answer for yourself. What is the length of new_up after it's constructed? What does the 'normalize' operation do? Mathematically, would the 'normalize' operation change the vector in any way in this case? From that, you should be able to determine whether the normalization is needed.
Quote:Having a velocity vector and location vector how can I align my 3D object to face in the same direction as the velocity?
For simply aligning an object with a direction vector (the velocity vector in this case), the position of the object doesn't matter.

There are quite a few ways to align an object with a direction vector. The first thing you have to figure out is what direction the object 'points' in local space, so that you know what it is exactly that you need to align. For most objects, the local-space direction vector will be one of the cardinal axes (often z or x).

The next thing to realize is that there's an infinite number of orientations that will satisfy the specified constraints. Imagine an airplane flying straight up. The plane can roll around its local forward axis while still being aligned with its 'direction' vector. So one way or another, you need to narrow it down to a single orientation.

The 'fixed up' method you seem to be trying to use is one way to do that. It'll work fine so long as the direction vector isn't parallel or nearly parallel with the up vector. The full method would look something like this:
vector3 side = cross(up, forward);side.normalize();up = cross(forward, side);matrix m = matrix_from_basis_vectors(side, up, forward);quaternion q = quaternion_from_matrix(m);
The above pseudocode performs the conversion by way of a matrix simply because 'quaternion from matrix' functions are fairly common, while 'quaternion from basis vectors' functions aren't so common (even though they're basically equivalent).

Another approach to aligning an object with a direction vector is to apply a relative rotation that rotates the object into alignment over the shortest arc. This can be done using an axis-angle computation, but there's also a nice quaternion-specific algorithm that does it a bit more elegantly.
Cristal clear, I will try it out

Cheers
rS
Hi jyk, so you said using my method "It'll work fine so long as the direction vector isn't parallel or nearly parallel with the up vector"

1. is there a way to avoid that? I can see that my object flip somethimes back and forward, is that the reason? if so can it be prevented?

2. will your pseudo code rotating using a quaternion fix the problem in 1?

Cheers
rS
Quote:Original post by nardove
Hi jyk, so you said using my method "It'll work fine so long as the direction vector isn't parallel or nearly parallel with the up vector"

1. is there a way to avoid that? I can see that my object flip somethimes back and forward, is that the reason? if so can it be prevented?

2. will your pseudo code rotating using a quaternion fix the problem in 1?
The example I gave is susceptible to the problem I mentioned. The problem is here:
// If 'up' and 'forward' are nearly parallel, the magnitude (length) of side will be small.vector3 side = cross(up, forward);// Normalizing a vector with small magnitude can have unpredictable results. Mathematically, you// should be able to normalize any vector with non-zero length, but due to the limits of floating-point// precision, normalization may fail even for vectors with very small magnitude (that is, you'll get// undefined results, e.g. NaN's).side.normalize();// If normalizing the 'side' vector fails, everything from here on out will be invalid.up = cross(forward, side);matrix m = matrix_from_basis_vectors(side, up, forward);quaternion q = quaternion_from_matrix(m);
From a conceptual standpoint, it makes sense that the algorithm doesn't work when the direction and up vectors are aligned. The purpose of the up vector is to provide additional information about how to orient the object, and if it's parallel to the direction vector, it provides no such information.

If this case is likely to come up in your simulation, I'd recommend instead using relative rotations to align your object incrementally. The basic algorithm looks like this:
vector3 axis = cross(current_forward, desired_forward);float length = axis.length();if (length > epsilon) {    axis /= length;    float angle = atan2(length, dot(current_forward, desired_forward));    quaternion q = quaternion_from_axis_angle(axis, angle);    orientation = q * orientation; // 'orientation' is a quaternion    orientation.normalize();}
That's off the top of my head, but I think I got it right.

There's also a more elegant version of this algorithm that is specific to quaternions (the above is a general form of the algorithm that will work equally well with quaternions and matrices). I would try the above first and make sure it works, but if you want I can post the quaternion-specific version as well.
Hi, I havent update my code to your prev pseudocode using quaternions, from my prev code I change crossP to new_side, so this is how I perform my orientation

-- INI CODE

Vec3D new_dir = new Vec3D(velocity);
new_dir.normalize();
Vec3D new_up = new Vec3D(0.0, 1.0, 0.0);
new_up.normalize(); // do I need to normalize this vector?
Vec3D new_side = new_dir.cross(new_up);
new_side.normalize();

float dotP = new_dir.dot(new_up);
float angle = new_dir.angleBetween(new_up, true);

pushMatrix();
// update location
translate(location.x, location.y, location.z);
// orientation to velocity
rotate(-angle, new_side.x, new_side.y, new_side.z);
/* draw happens here */
popMatrix();

-- END CODE

You said things will go wrong when new_side.normalize(); fails, and from there I have to check

float length = new_side.length();
if (length > EPSILON) {
new_side /= length;
....

from there you lost me, will it be possible provide a pseudocode using my logic to rotate instead of quaternions?

Thanks a lot this post has been great source of information and understanding
rS
Quote:from there you lost me, will it be possible provide a pseudocode using my logic to rotate instead of quaternions?
Unfortunately I don't think I can provide a working version of your method, because I don't quite understand what it's doing (or trying to do). Among other things, there's some context missing; it's not really clear from your example (to me at least) how the transforms are being handled, and what the 'push' and 'pop' calls are supposed to do.

Perhaps you could describe in a bit more detail what type of behavior you're after. Are you trying to get the object to rotate incrementally each update so as to align with the velocity vector? Or are you trying to build a transform 'from scratch' that will align the object with the velocity vector?
I understand, I ask too much, ok so push and pop Matrix

"The pushMatrix() function saves the current coordinate system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in conjuction with the other transformation methods and may be embedded to control the scope of the transformations."

very similar to opengl gl.glPushMatrix() and gl.glPopMatrix()

Using this code I calculate my angle and axis of rotation

Vec3D new_dir = new Vec3D(velocity);
new_dir.normalize();
Vec3D new_up = new Vec3D(0.0, 1.0, 0.0);
new_up.normalize(); // do I need to normalize this vector?
Vec3D new_side = new_dir.cross(new_up);
new_side.normalize();

float dotP = new_dir.dot(new_up);
float angle = new_dir.angleBetween(new_up, true);

So angle = angle of rotation and new_side = axis

This code I do the matrix transformations

pushMatrix();
// update location
translate(location.x, location.y, location.z);
// orientation to velocity
rotate(-angle, new_side.x, new_side.y, new_side.z);
/* draw happens here */
popMatrix();

Here is a link to my work in progress, so you can see the problem, and you can also look at the code, all the prev code is in the Boids Class in the display() method

http://nardove.com/p5/swimming_jellie/swimming_jellie_test_2/

If you click and drag the jelly will follow the mouse position and if you are on top an drag to the sides you can see the jump in the rotation, if you let it wander it becomes more clear the sudden jump in the orientation

Cheers
rS
I understand what the matrix push/pop functions do, but what's not immediately clear from your example is the state of the transform matrix going into the push/pop block. Is it identity? Are these transforms being combined with an existing transform? Are the transforms cumulative?

I'm sure I could figure it out if I examined your code carefully enough, but it would be easier if the example were a little more clear.

Actually, just answering this question from my previous post:
Quote:Perhaps you could describe in a bit more detail what type of behavior you're after. Are you trying to get the object to rotate incrementally each update so as to align with the velocity vector? Or are you trying to build a transform 'from scratch' that will align the object with the velocity vector?
Would probably help.
There is no other matrix transformations, the only transformations are inside the push and pop, all I am doing is taking the velocity vector and create a new direction vector from it, them I declare a hardocoded up vector, and with those 2 I take the cross product to get my axis of rotation, them I calculate the angle of rotation between the new direction vector and the hardcoded up (0, 1, 0)

and plug those 2 in the rotate method like this rotate(angle, croosp.x, crossp.y, crossp.z);

I can see that some times there is a flip of the rotation making the object to face the opposite direction and also if the object goes up it will do a 360 rotation int the y axis I belive.

This concept of 3D orientation is new to me, and from that it is dificult for me to explain in more detail.

I have a wander steering behavior going on, and I want the objects to face the same direction as their velocity, at the moment it is working but like I said there are moments were they go nuts.

Sorry if I cant explain it in more detail you see I am a rooky in the 3D programming stuff.

All your help its very much appreciated!
rS

This topic is closed to new replies.

Advertisement