Controlling angular momentum using PID

In an earlier post I had a look at controlling animation using PID-controllers. Generally, these controllers work on a single variable in a single dimensions. This is not always sufficient in a 3D world, so I wanted to explore the possibility of expanding it to more dimensions. Specifically I wanted to control orientation, and make my objects turn the right way using PID controllers.

Chances are you do, but if you don't know what quaternions are, might I suggest having a quick look at that first, as I will use these to get the appropriate rotation.

Determining the error

PID work with an 'error' quantity that can be multiplied (for P), integrated (for I), and differentiable (for D). As mentioned in that previous post, I'll be using piecewise linear approximations of the error, which means that we can cut back on our requirements a bit, we just need to be able to add (to keep a sum for I) and subtract it (to make a slope for D).

There is one physical property which fits nicely to these criteria while staying well behaved, namely torque. Torque can be added and subtracted without worrying about in which order it's applied, and the result is an effective torque being applied to the center of mass, much like the way forces work, but for rotation. This is what I want to use.

Since we're working with torque, I chose an error quantity which fits how torque is often represented, namely the (right-handed) rotation axis, and the norm of the vector is the angle around that axis. Obviously this isn't torque, as our set-point is an orientation, but our output is, so having a simple scaling operation turn our error vectors into our torque output is quite practical.

What's left is to determine the error vector. I use quaternions for both current orientation and set-point. Using a current orientation 'c', and a set-point 's', and a rotation 'q' which rotates 'c' into 's'.

q c = s

Multiply with inverse 'c' from the right (remember, quaternion multiplication, like matrix multiplication, is not commutative) and we acquire 'q'

q = s c^{-1}

Now, 'q' is a unit quaternion, but since a unit quaternion represents a rotation described by

q = cos({lpha over 2}) + sin({lpha over 2}) (hat{i}x + hat{j}y + hat{k}z)

with the angle 'a' being rotated around the unit rotation axis vector (x, y, z). Determining the angle,

{lpha over 2} = cos^{-1}(Re(q))

allows us to get the rotation axis by simply dividing the imaginary parts by sin(a/2). Caution though, this is zero at 0 and 360 degrees, meaning we're not really rotating around any axis (quaternions represent orientation rather than rotation, and cannot describe multiple turns), so we need to check for that.

Putting it together

So we've got our error vector, now we can start putting our three parts together.


The proportional part has been the set up so far, so this is pretty straight forward. Our error vector is the angle and axis off the mark, and we set our proportional torque by simple scaling.


Since torque can be summed to produce an effective torque, integration should work fine. It is however not quite as obvious as it might appear, as we are technically summing rotations, not torque. The integration works more or less like integrating the one dimensional angle error, however, summing the error vectors like this has the added effect of letting the axis of the torque lag behind which actually aggravates the precession.


Differentiation has a different set of brain teasers, where summing two similar vectors produces a longer vector in a direction between the two, the difference between two rotation vectors can produce a different axis altogether. This has the largest effect when objects are precessing, as the error magnitude will remain constant and the axis orientation will shift, resulting in an axis which is perpendicular. This works to our advantage though, as it will counter the effects of the P- and I- part, which will tend to make our object precess once we have off-axis torque applied.


I've been playing around with this for a while, and it's working well so far. I will need a little bit more time to figure out the kinks, especially how it would behave when I start applying more off-axis torque, trying to induce a gyroscopic precession. As long as the objects spins on an axis, it will act as a single dimension PID working with a single angle, and a single torque input. It also appears to handle small perturbations elegantly, and works nicely for my current applications. Time will tell.

comments powered by Disqus