Skip to content

Euler angles are horrible

Mike Boyle edited this page Jul 25, 2019 · 9 revisions

I hope I'm not being too subtle about this: I despise Euler angles — and not just because they are the single most common cause of ridiculous "bug" reports. They are intrinsically utterly terrible. Euler was an absolute genius; anyone who still use these angles is less likely to be.

Quaternions are faster, more accurate, basically free of singularities, more intuitive, and generally easier to understand. You can work entirely without Euler angles — I certainly do, even though I do some pretty complicated stuff with rotations. You absolutely never need Euler angles on a fundamental level. But if you really have to use them for some practical reason (presumably because you interact with legacy systems), they are supported by this software module.

Conventions

One of the worst problems with Euler angles is that there are so many different conventions in use with them — related to which axes you rotate about, and in which order. Even the least imaginative counting of these conventions gives 12 different possible conventions, but then there are always people who don't even abide by the basic assumptions that go into that count, meaning that there are really many dozens of conventions that have actually been used by someone somewhere. Add to this the fact that there isn't even agreement on how to write down quaternions, what it means to multiply two quaternions, or how to use them to rotate vectors, and the correspondence between Euler angles and quaternions becomes basically arbitrary.

All I can say that will be of any use to anyone is that the conventions used for Euler angles in this package assume that a set of angles given by (alpha, beta, gamma) corresponds to a quaternion as

Q = (alpha*quaternion.z/2).exp() * (beta*quaternion.y/2).exp() * (gamma*quaternion.z/2).exp()

(where angles are naturally measured in radians). Both the functions for conversion to and from Euler angles use this convention. This is really all that I think is unambiguous. If you want to use Euler angles — which I discourage — you have to decide for yourself what this expression means, and how it relates to what you think of as a rotation.

Now, personally, I interpret this as an initial right-handed rotation about the z axis through an angle gamma, followed by a right-handed rotation about the (non-rotated) y axis through an angle beta, followed by a right-handed rotation about the (non-rotated) z axis through an angle alpha. If I were to use Q to rotate a vector v, I would do so as vprime = Q * v * Q.inverse(). But seriously, this is just my personal set of choices, and is open to interpretation.

It is absolutely critical that you understand the conventions that you are using — not just for Euler angles, but also for quaternions and rotations generally. Write simple tests of your code for things like rotations by 90° about the basis axes, the composition of a few simple rotations, and so on. There are far too many ways to interpret oversimplified names for rotations for you to just read something like "ZYZ" and think you know what someone means by that.

Nothing in this package restricts you to use my conventions for rotations or Euler angles, except for things like the order in which parts of the quaternion are given, and the basic products between quaternions — things like the fact that

quaternion(0, 1, 0, 0) * quaternion(0, 0, 1, 0)

is the same as

quaternion(0, 0, 0, 1)

instead of (0, 0, 0, -1) or (1, 0, 0, 0), which really are actual conventions that other people do use for quaternions. Again, those are just choices that have to be made. I think this code uses the most common choices, but other people may disagree.

Opening issues and pull requests

There are just two places in this code that use Euler angles: the functions as_euler_angles and from_euler_angles. Before you open an issue relating to either of these, please read, understand, and come to truly believe in what I've written above. You should also read through the docstrings for those two functions (here and here, respectively) to understand what they are intended to do, so that you're not just expecting something different. It would also be nice if you could search through closed issues to make sure no one has brought up your point previously. Here are all the issues that mention Euler angles.

If you still wish to open an issue or pull request that discusses Euler angles, you must show evidence that

  1. You understand that the definitions of Euler angles are subject to arbitrary choices.
  2. You understand that I have made a certain set of choices that may differ from yours but are not "wrong" in any sense.
  3. Even assuming my conventions, there is something about my code that is wrong or could be substantially improved.

If you don't show evidence of all three of these, I will close your issue or pull request and point you here without further explanation. If I'm in a good mood, I might leave it at that and not call you names directly, though your issue will likely get the nuisance label.

Do not, under any circumstances, bother opening an issue that claims my choice of conventions is wrong. I will close the issue without further comment, and will likely be openly hostile to you if you attempt to interact with me in any other way, ever. I'm already annoyed with you that you made me think about Euler angles; don't make it worse by being rude. If you don't like my choices, feel free to copy my code and alter it to suit your needs.

I am also not interested in any pull requests that change my conventions, and I'll be quite skeptical about requests to add more capabilities for using Euler angles. I sincerely want to discourage people from using Euler angles as much as possible, so I don't want to be supporting increasingly complex code that helps them use Euler angles.

Clone this wiki locally