# Turning to face target in 3D using only angular acceleration

This topic is 2779 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi,

I'm toying with a very simple "AI" of sorts for a 360° space RTS. The language is python, the API is Panda3D. To face a target in 3D, the AI unit just may use angular acceleration on itself (just like a player would). In each frame, I update the units orientation like this (self.rotVec is a 3-vector containing the angular velocity of the unit, combining the current axis of rotation in the vector direction with the rotation speed in the vector length):

 # apply rotation to our direction q = self.node.getQuat() rot = self.rotVec * dt qq = Quat(0, rot.getX(), rot.getY(), rot.getZ()) q += (qq * q) * 0.5 self.node.setQuat(q) 

The update of the angular velocity according to the current target basically works like this (excerpt):

         p = self.node.getPos()              # position         q = self.node.getQuat()             # current orientation         v = q.getForward(); v.normalize()   # view         pd = self.dest - p; pd.normalize()  # vector to target         # get angle to target, and axis we need to rotate around to face it         vdotpd = clamp(v.dot(pd), -1, 1) # v dot pd, clamped to -1 .. 1 range         theta = math.acos(vdotpd)         if theta > math.pi: theta = math.pi - theta         axis = v.cross(pd); axis.normalize()         thisQ = Quat(); thisQ.setFromAxisAngleRad(theta > 0 and self.rotAccel or -self.rotAccel, axis); thisQ.normalize()         # damp the current rotation to allow settling                 damping = 0.99         self.unit.rotVec *= damping                  rVl = self.unit.rotVec.length()         normalizedRotVec = self.unit.rotVec; normalizedRotVec.normalize()                  # get current rotation (angular vel.) and convert to quaternion         currentQ = Quat()         if normalizedRotVec.length() >= .999 and normalizedRotVec.length() <= 1.001: currentQ.setFromAxisAngleRad(rVl * dt, normalizedRotVec) currentQ.normalize()         # combine current and needed rotation               newQ = currentQ * thisQ                # update angular speed         self.unit.rotVec = newQ.getAxisNormalized() * newQ.getAngleRad()                        # clamp rot speed         if rVl > self.unit.maxRotSpeed:             self.unit.rotVec *= (1.0 / rVl * self.unit.maxRotSpeed * .99)             rVl = self.unit.rotVec.length() 

Okay. Now my question/problem: The above works most of the time, but sometimes, my unit turns to face exactly 180 degrees away from the target! Sometimes, it approaches the direction to the target, almost gets there, and then suddenly turns away from it again and settles to the opposite side. And, in some rare cases, it starts to tumble and never finds the correct direction at all

So, how can I prevent this   I don't really get why this happens, as I get the axis and angle needed to turn the target. Does someone see a fundamental error here, or do I need to take a different approach? I already tried to use signed angles, not inverting the rotation angle based on the angle-to-target, etc., but I get the same behaviour. So - how would you make a unit slowly turn to face a target, by just using angular acc/vel (e.g. to feed into a physics engine)?

Thanks, Cheers!

##### Share on other sites
Are there restrictions as to how much angular acceleration can be applied? I believe solving this problem in all its generality is probably too difficult, but you probably don't need to do that.

For example, even if you are very close to the target orientation, if your angular velocity is too large, you may want to go around once and aim to stop there, instead of having to correct a lot to return to where you are now. In a more extreme example, if you have an object rotating at 3,000 rpm and you want it to stop facing a particular direction, you would have to figure out how many more turns you are going to let it spin before it stops.

So perhaps you can describe what your objects are and some idea of how fast they might be spinning, so we know if we have to consider those cases or not?

##### Share on other sites
I thought this must be a well-studied problem, and it is. You should probably read this paper about attitude control (PDF). It looks like you can make a simple controller using the sum of two terms to determine angular acceleration: something proportional to the difference between the attitude and the target attitude, and something proportional to the angular velocity. This is known as a PD controller, and it seems to be well known that it behaves well for this problem ("globally stable", I think).

##### Share on other sites
Cool, I'll read into that! Thanks!

(to answer your 1st question, the objects rotating are your usual space fighters / spaceship type of craft, and the cool thing about a system like this will then be that you could, e.g., put the whole sim into the gravity field of a sun, or something, and the fighters et al will correct their courses automatically.)

##### Share on other sites
Allright, I solved my problem, here's what was wrong for reference:

- angular velocity represented as 1 vector (its direction=axis, its length=angle) cannot be applied to a orientation-quaternion with the formula q' = W * q * .5, with W = Quat(0, rx, ry, rz). This formula is for angular velocity in the form (x-rot, y-rot, z-rot). The correct formula is: q' = W * q, with W = Quat(cos(|w| * 0.5), w * sin(|w| * 0.5)) and w = the angular velocity vector.
- I get the axis-to-rotate-around using acos(view.dot(vec_to_target)), but it needs to be transformed to the world coordinate system before applying it to the angular velocity.

Now it looks correct :-)

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 15
• 21
• 23
• 11
• 25