Per-Axis (Euler) Rotation Control While Avoiding Gimbal Lock

I am writing a target-following script with numerous per-axis settings. Below is the most distilled example I can provide of the rotation portion:

private void Rotate()
    var _target = Quaternion.LookRotation(Target.position - transform.position, Target.up).eulerAngles;
    var _next = transform.eulerAngles;

    for (var i = 0; i < 3; i++)
        _next <em>= Mathf.LerpAngle(_next<em>, _target<em>, smoothRotateSpeed _* Time.deltaTime);_</em></em></em>

transform.localRotation = Quaternion.Euler(_next);
In the example above, “smoothRotationSpeed” is a Vector3 that applies a different smoothing value to each axis.
Unfortunately, because I am modifying each axis individually, I am running into Gimbal lock issues. How can one avoid or compensate for Gimbal lock without losing per-axis (euler) control?

My answer from:

I can’t guarantee this will work. But give it a try. I basically only deal with the euler angles of the rotation between current and target. I adjust how much impact that rotation has, and bring it back to quaternions. Dealing with quats as much as possible.

I generalized the ‘FromToRotation’ as its own static method. That’s because I have my own “QuaternionUtil” class that contains a lot of methods likes this. I’m really annoyed by the lack of methods built into the Quaternion from Unity. They have a FromToRotation to get a quaternion between two vectors, but not between two quaternions. For shame.

Note I also renamed the variables.

using UnityEngine;
using System.Collections;
public class GimbalTest : MonoBehaviour {
    public Transform _target;
    public Vector3 smoothRotateSpeed;
    // Use this for initialization
    void Start ()
    // Update is called once per frame
    void Update ()
        //var target = Quaternion.LookRotation(_target.position - this.transform.position, _target.up).eulerAngles;
        //var next = transform.eulerAngles;
        //for (var i = 0; i < 3; i++)
        //    next <em>= Mathf.LerpAngle(next<em>, target<em>, smoothRotateSpeed _* Time.deltaTime);_</em></em></em>

//transform.localRotation = Quaternion.Euler(next);

var targetRot = Quaternion.LookRotation(_target.position - this.transform.position, _target.up);
var delta = FromToRotation(this.transform.rotation, targetRot);
var deltaEuler = delta.eulerAngles;

for (var i = 0; i < 3; i++)
deltaEuler = Mathf.LerpAngle(0f, deltaEuler, smoothRotateSpeed * Time.deltaTime);

transform.rotation *= Quaternion.Euler(deltaEuler);

public static Quaternion FromToRotation(Quaternion start, Quaternion end)
return Quaternion.Inverse(start) * end;