Dorian
Dorian

Reputation: 417

Quaternion from two vector pairs

I have two vector pairs (before and after rotation).

before rotation: [x1,y1,z1] [x2,y2,z2]

after rotation: [x1',y1',z1'] [x2',y2',z2']

How to create a quaternion representing this rotation?

Upvotes: 3

Views: 5425

Answers (5)

showhand
showhand

Reputation: 1

It is fine to find the quaternion from v1 to v2.

The final q = (cos A/2, sin A/2 * axis), where the A is the angle between v1 and v2, axis is the normed axis.

Multiply both side by 2 * cos A/2,

Then we have 2 * cos A/2 *q = (1+cos A, sin A * axis)

(where cos A = dot(v1, v2)/|v1|/|v2| and axis = cross(v1, v2).normalize() = cross(v1, v2)/|v1|/|v2|/sin A.)

Then 2 * cos A/2 *q = (1+dot(v1, v2)/|v1|/|v2|, cross(v1, v2)/|v1|/|v2|)

Finally q = (1+dot(v1, v2)/|v1|/|v2|, cross(v1, v2)/|v1|/|v2|).normalize()

Upvotes: 0

Martin McCormick
Martin McCormick

Reputation: 1

A mature solution to this problem is called Triad. Triad is one of the earliest and simplest solutions to the spacecraft attitude determination problem and is extremely efficient computationally.

With Triad, the idea is to replace your paired set of two vectors, with a paired set of three vectors, where the extra vector is generated with a cross-product. By normalizing the vectors, you can solve for a rotation matrix without a matrix inverse or an SVD (as is needed in more general instances of the problem -- see Wahba's Problem)

For full algorithm, see: https://en.wikipedia.org/wiki/Triad_method

You can then convert the solved rotation matrix from Triad to a rotation quaternion:

qw = √(1 + m00 + m11 + m22) /2
qx = (m21 - m12)/( 4 *qw)
qy = (m02 - m20)/( 4 *qw)
qz = (m10 - m01)/( 4 *qw)

In general to make the conversion to quaternion robust, you should consider looking at the matrix trace as discussed here: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/

Finally, consider an alternative to Triad that directly computes the optimal quaternion called QUEST.

Upvotes: 0

chrmcg
chrmcg

Reputation: 21

I translated marcv81's very helpful blog post into Three.js:

const rotateVectorsSimultaneously = (u0, v0, u2, v2) => {
    const q2 = new THREE.Quaternion().setFromUnitVectors(u0, u2);

    const v1 = v2.clone().applyQuaternion(q2.clone().conjugate());

    const v0_proj = v0.projectOnPlane(u0);
    const v1_proj = v1.projectOnPlane(u0);

    let angleInPlane = v0_proj.angleTo(v1_proj);
    if (v1_proj.dot(new THREE.Vector3().crossVectors(u0, v0)) < 0) {
        angleInPlane *= -1;
    }
    const q1 = new THREE.Quaternion().setFromAxisAngle(u0, angleInPlane);

    const q = new THREE.Quaternion().multiplyQuaternions(q2, q1);
    return q;
};

Because angleTo always returns a positive value, I manually flip the sign of the angle depending on which side of the u0-v0 plane v1 is on.

Upvotes: 2

Archie
Archie

Reputation: 2672

Well, first you can find the rotation axis using vector-multiplication (cross-multiplication):

axis = v1 x v2;

Then you can compute the rotation angle:

sinA = |axis| / |v1|*|v2|
cosA = v1 . v2 / |v1|*|v2|

Here | | - is vector length operation, and . - is dot-multiplication

And finally, your quaternion is:

Q(w,x,y,z) = (cosA, axis.x * sinA, axis.y * sinA, axis.z * sinA)

Upvotes: 1

marcv81
marcv81

Reputation: 876

In most cases there is no rotation which transforms 2 vectors into 2 other vectors. Here is a simple way to visualize why: a rotation does not change the angle between vectors. If the angle between the 2 vectors before the rotation is different from the angle between the 2 vectors after the rotation, then there is no rotation which meets your criteria.

This said there may be an optimal quaternion with an acceptable error which "almost" rotates your 2 vector pairs. There are a number of algorithms which vary in speed and precision to find such a quaternion. I wrote a fast C++ algorithm for an Arduino application where the speed is critical but the precision is less important.

http://robokitchen.tumblr.com/post/67060392720/finding-a-rotation-quaternion-from-two-pairs-of-vectors

Before rotation: u0, v0. After rotation: u2, v2.

Quaternion q2 = Quaternion::fromTwoVectors(u0, u2);
Vector v1 = v2.rotate(q2.conjugate());
Vector v0_proj = v0.projectPlane(u0);
Vector v1_proj = v1.projectPlane(u0);
Quaternion q1 = Quaternion::fromTwoVectors(v0_proj, v1_proj);
return (q2 * q1).normalized();

If this does not meet the requirements of your own application try to google Wabha's problem.

Upvotes: 3

Related Questions