Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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)

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.

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.

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()

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top