Skip to content
This repository was archived by the owner on Nov 17, 2021. It is now read-only.

Quaternion: Shortest rotation between two vectors #55

Merged
merged 5 commits into from
Dec 4, 2017

Conversation

MaEtUgR
Copy link
Member

@MaEtUgR MaEtUgR commented Nov 13, 2017

There are multiple application scenarios but I need this functionality for the quaternion controller: PX4/PX4-Autopilot#8003

And because of the reusability and the calculation itself suffering tedious corner cases I added it to the library with unit tests to ensure proper results.

A simple explanation for the corner cases: The input vectors are parallel but oposite and therefore require a 180 degree rotation but the cross product to get the perpendicular rotation axis is zero. For more discussion see: https://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another .

…hat maps one vector to another

including tedious corner case handling for parallel vectors with 180 degree rotations
@MaEtUgR MaEtUgR self-assigned this Nov 13, 2017
@MaEtUgR MaEtUgR force-pushed the quaternion-vec-to-vec branch from 9380e80 to a2dc1b5 Compare November 13, 2017 15:49
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 99.89% when pulling a2dc1b5 on quaternion-vec-to-vec into 3bd94fc on master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 99.89% when pulling a2dc1b5 on quaternion-vec-to-vec into 3bd94fc on master.

including all the parallel vector corner cases
@MaEtUgR MaEtUgR force-pushed the quaternion-vec-to-vec branch from a2dc1b5 to 8f959d0 Compare November 13, 2017 16:30
@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling 8f959d0 on quaternion-vec-to-vec into 3bd94fc on master.

@MaEtUgR
Copy link
Member Author

MaEtUgR commented Nov 13, 2017

Fixed coverage, there was a typo like error which resulted in not covering the case I designed the test for. I made the tests even simpler such that this cannot happen anymore.

* @param src source vector (no need to normalize)
* @param eps epsilon threshold which decides if a value is considered zero
*/
Quaternion(const Vector3<Type> &src, const Vector<Type, 3> dst = Vector3<Type>(0, 0, 1), const Type eps = 1e-5f) :
Copy link

@Stifael Stifael Nov 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Vector3< Type > dst = Vector3< Type >(0,0,1)
  • const Type eps = (Type)1e-5;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was Vector<Type, 3> which I switched to Vector3<Type> now, that's what you meant right?
const Type eps = (Type)1e-5 is enough, constants are casted auatomatically to the correct type on compile time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. Second statement is wrong... I put the explicit conversion now.

Quaternion &q = *this;
Vector3<Type> cr = src.cross(dst);
float dt = src.dot(dst);
if (cr.norm() < eps && dt < 0) {
Copy link

@Stifael Stifael Nov 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we not comment anything within matrix lib?
for someone who just reads the code the first time it would make it much easier to write that this is the case for parallel vectors pointing opposite

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right, I added comments for that.

q(0) = Type(0);
cr = src.cross(cr);
} else {
q(0) = src.dot(dst) + sqrt(src.norm_squared() * dst.norm_squared());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function is definitely not obvious.
maybe write something like "Half-Way Quaternion Solution".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment added

}
}
q(0) = Type(0);
cr = src.cross(cr);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I get it what you do here with the cr manipulation, but I cannot really verify that it is correct or not?
Do you have a link to an example?

Copy link
Member Author

@MaEtUgR MaEtUgR Nov 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a bulk of (code) examples summarized here: https://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another

Also if you look at the unit tests you see in the comments which one is going through which case. Plus I can share the MATLAB code I used for intial prototyping of the functionality.

@MaEtUgR
Copy link
Member Author

MaEtUgR commented Nov 29, 2017

@Stifael Please have another look.
Only thing I really chenged is: I took out the default destination vector because it's only confusing and not a big adavantage.

@MaEtUgR MaEtUgR force-pushed the quaternion-vec-to-vec branch 2 times, most recently from 0263638 to dbb0e9b Compare November 29, 2017 17:34
@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling dbb0e9b on quaternion-vec-to-vec into 41ad2bd on master.

Copy link
Member

@jgoppert jgoppert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@@ -71,6 +71,11 @@ class Vector : public Matrix<Type, M, 1>
return Type(sqrt(a.dot(a)));
}

Type norm_squared() const {
const Vector &a(*this);
return Type(a.dot(a));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it actually necessary to cast it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you're right, I forgot to get rid of it. It was only necessary for sqrt() which can return double...

…ched type conversions, took out default destination vector because confusing
@MaEtUgR MaEtUgR force-pushed the quaternion-vec-to-vec branch from dbb0e9b to 21b98db Compare December 4, 2017 12:28
@MaEtUgR
Copy link
Member Author

MaEtUgR commented Dec 4, 2017

I added the fix (took out the cast) to the latest commit.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling 21b98db on quaternion-vec-to-vec into 41ad2bd on master.

@MaEtUgR MaEtUgR merged commit f835d39 into master Dec 4, 2017
@MaEtUgR MaEtUgR deleted the quaternion-vec-to-vec branch December 4, 2017 14:34
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants