Quaternions are not an ideal set of DOF to use for rotations when computing an unconstrained average.
Here is what I use most of the time (
[MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector3 ToAngularVelocity( this Quaternion q ) { if ( abs(q.w) > 1023.5f / 1024.0f) return new Vector3(); var angle = acos( abs(q.w) ); var gain = Sign(q.w)*2.0f * angle / Sin(angle); return new Vector3(q.x * gain, q.y * gain, q.z * gain); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Quaternion FromAngularVelocity( this Vector3 w ) { var mag = w.magnitude; if (mag <= 0) return Quaternion.identity; var cs = cos(mag * 0.5f); var siGain = sin(mag * 0.5f) / mag; return new Quaternion(w.x * siGain, w.y * siGain, w.z * siGain, cs); } internal static Quaternion Average(this Quaternion refence, Quaternion[] source) { var refernceInverse = refence.Inverse(); Assert.IsFalse(source.IsNullOrEmpty()); Vector3 result = new Vector3(); foreach (var q in source) { result += (refernceInverse*q).ToAngularVelocity(); } return reference*((result / source.Length).FromAngularVelocity()); } internal static Quaternion Average(Quaternion[] source) { Assert.IsFalse(source.IsNullOrEmpty()); Vector3 result = new Vector3(); foreach (var q in source) { result += q.ToAngularVelocity(); } return (result / source.Length).FromAngularVelocity(); } internal static Quaternion Average(Quaternion[] source, int iterations) { Assert.IsFalse(source.IsNullOrEmpty()); var reference = Quaternion.identity; for(int i = 0;i < iterations;i++) { reference = Average(reference,source); } return reference; }`