Turning to face target in 3D using only angular acceleration


I’m toying with a AI of sorts for a 360° space RTS. 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

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()

        angle = self.unit.rotAccel

        # get needed rotation-to-target as quaternion
        thisQ = Quat(); thisQ.setFromAxisAngleRad(angle, 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)

        # 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 :frowning:

So, how can I prevent this :slight_smile: 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 etc., but I get the same behaviour.

I can provide a small example script, if needed (would need to prepare), that shows the problem.

Thanks, Cheers!

I don’t know what’s causing your problem, but why not use Panda’s built-in lookAt() function?