How to represent rotational velocity?

I’m working with some students on a spacecraft simulation. They’ve written their own physics engine, which works well enough in most cases.

But we’ve just discovered that Bad Things happen when the craft starts rotating more than 360 degrees per second in any axis. I never fully realized till now that a Quaternion can’t represent a rotation of more than 360 degrees. We were storing rotational velocity as a Quaternion, which seemed like a great idea at the time, as it’s easy enough to compose quaternions (e.g. to apply control inputs). But as soon as the velocity exceeds a certain amount, our spacecraft suddenly starts spinning the other way as if it had hit an invisible wall.

This is a serious problem almost universally overlooked in websites that talk about using quaternions to represent rotations. A quaternion can represent orientation just fine, where 90 degrees one way and 270 (or even 630) degrees the other way are all the same orientation. But for representing rotations, they are severely limited. And this breaks our physics simulation.

So. What should we be using to store rotational velocity in such a simulation? Euler angles? Something else?

Many thanks,

  • Joe

I suppose that you could try a Vector4 to store the data then convert that back to a Quaternion if you have to. I wouldn’t personally store rotational velocity in a Quaternion as they are designed specifically to represent orientation in 3d space.

You could also use a Vector3 and do some stuff with Euler angles to make that play nice. I would suggest storing rotational velocities in a Vector3 and using rigidbody.angularVelocity. Hope that helps!

There are no Rigidbody objects here, but you’re right, a Vector3 (just like Rigidbody.angularVelocity) appears to solve it. And converting the code over to that wasn’t as hard as I thought. It boils down to this:

            // Update the ship's rotation from the control inputs.
            Vector3 ctrlEuler = new Vector3((float)(ctrl.pitch * dt * 100),
                                            (float)(ctrl.yaw * dt * 100),
                                            (float)(ctrl.roll * dt * 100));
            ship.rotationalVelocity += ctrlEuler;

            // if no attitude controls are in use, decay rotation a bit (to simulate SAS)
            if (ctrl.pitch == 0 && ctrl.yaw == 0 && ctrl.roll == 0) {
                ship.rotationalVelocity *= 0.99f;
            }

            ship.rotation = ship.rotation * Quaternion.Euler(ship.rotationalVelocity * Time.deltaTime);

I was worried that I would need to somehow transform the control inputs to be relative to the ship’s current orientation, but we had our flight expert (who has logged obscene numbers of hours playing KSP) test it, and he proclaimed it correct.

AND, I learned something new about quaternions today. So it’s a good day!

Good on ya! Glad I was able to point you in the right direction. If you have any work you would like to show off or want to get in touch with me personally for future questions feel free to email me at hamsterbytellc@gmail.com . Best of luck in your future endeavours!

The other obvious solution to your specific problem would be to change your units. You said you were using a Quaternion to represent the rotation per second. If you used it to represent, say, the rotation per millisecond then you would presumably not have to worry about ever having rotations in excess of 360 degrees.

Yes, that would certainly postpone the problem substantially!

Postpone but not eliminate. Theoretically you could have a force strong enough to spin the object more than a thousand times a second. It may be a rare occurrence but it certainly isn’t impossible. The ceiling on a float is much higher than 360,000 so it gives you a much larger margin to play with. That being said, you could technically cause an error with a float value as well but the odds are substantially lower because of the higher ceiling. Either way will work fine I think, but my personal opinion is to stick with the Vector3 approach and use float values to err on the side of caution

Rotations in excess of 180 degrees per frame are probably something that you should outright disallow regardless of how you represent them (they will cause aliasing issues).

I assure you there exists some maximum linear velocity above which your program will have serious issues. The fact that there is some maximum angular velocity as well should not be surprising or upsetting (as long as that maximum is higher than what your gameplay requires).

1 Like

That’s true. Wasn’t even considering the aliasing issues. I was just trying to raise the ceiling. Perhaps the better way to do is to put a hard cap on the angular velocity then.