I dug this code up from 2006 on this forum: Code (csharp): var Quaternion[] quats; Quaternion avg = Quaternion(0,0,0,0); for (var q : Quaternion in quats) { if (Quaternion.Dot (q, avg) > 0) { avg.x += q.x; avg.y += q.y ; avg.z += q.z; avg.w += q.w; } else { avg.x += -q.x; avg.y += -q.y; avg.z += -q.z; avg.w += -q.w; } } var mag = Mathf.Sqrt(avg.x* avg.x + avg.y* avg.y + avg.z * avg.z + avg.w * avg.w); if (mag > 0.0001) { avg.x /= mag; avg.y /= mag; avg.z /= mag; avg.w /= mag; } else avg = quats[0]; But I think the author said theres an easier way with slerp and multiplication. Anyone know it or some easier code?

Thanks all. For reference I did get a good average with this code: Code (csharp): var test : Quaternion = Quaternion.identity; test = Quaternion.Lerp(first.transform.rotation, second.transform.rotation, 0.5);

That's basically what I posted. Replace "quaternion1" with "first.transform.rotation" etc.; you don't need to define variables on separate lines like that. --Eric

Lerp or Slerp is only accurate if you have only two quaternions. If you have more than two, that method will give you a different result if you execute it in a different order. Here is the mathematically correct way of averaging more than two quaternions: Wiki: http://wiki.unity3d.com/index.php/Averaging_Quaternions_and_Vectors Code (csharp): //Global variable which holds the amount of rotations which //need to be averaged. int addAmount = 0; //Global variable which represents the additive quaternion Quaternion addedRotation = Quaternion.identity; //The averaged rotational value Quaternion averageRotation; //multipleRotations is an array which holds all the quaternions //which need to be averaged. Quaternion[] multipleRotations new Quaternion[totalAmount]; //Loop through all the rotational values. foreach(Quaternion singleRotation in multipleRotations){ //Temporary values float w; float x; float y; float z; //Amount of separate rotational values so far addAmount++; float addDet = 1.0f / (float)addAmount; addedRotation.w += singleRotation.w; w = addedRotation.w * addDet; addedRotation.x += singleRotation.x; x = addedRotation.x * addDet; addedRotation.y += singleRotation.y; y = addedRotation.y * addDet; addedRotation.z += singleRotation.z; z = raddedRotation.z * addDet; //Normalize. Note: experiment to see whether you //can skip this step. float D = 1.0f / (w*w + x*x + y*y + z*z); w *= D; x *= D; y *= D; z *= D; //The result is valid right away, without //first going through the entire array. averageRotation = new Quaternion(x, y, z, w); } Edit: Before you average a quaternion, you have to check whether it has x y z and w reversed compared to the rest. The rotation of q and -q is the same, even though the sign of x y z and w is reversed. However, quaternions q and -q cannot be averaged right away and have to have its sign reversed first. The way to solve it is to take the first quaternion in the array and then taking a dot product with each other quaternion. If the result is negative then the quaternion must have x y z and w reversed before it is averaged with the rest of it. Like this: Code (csharp): //Get an average (mean) from more than two quaternions (with two, slerp would be used). //Note: this only works if all the quaternions are relatively close together. //Usage: //-Cumulative is an external Vector4 which holds all the added x y z and w components. //-newRotation is the next rotation to be added to the average pool //-firstRotation is the first quaternion of the array to be averaged //-addAmount holds the total amount of quaternions which are currently added //This function returns the current average quaternion public static Quaternion AverageQuaternion(ref Vector4 cumulative, Quaternion newRotation, Quaternion firstRotation, int addAmount){ float w = 0.0f; float x = 0.0f; float y = 0.0f; float z = 0.0f; //Before we add the new rotation to the average (mean), we have to check whether the quaternion has to be inverted. Because //q and -q are the same rotation, but cannot be averaged, we have to make sure they are all the same. if(!Math3d.AreQuaternionsClose(newRotation, firstRotation)){ newRotation = Math3d.InverseSignQuaternion(newRotation); } //Average the values float addDet = 1f/(float)addAmount; cumulative.w += newRotation.w; w = cumulative.w * addDet; cumulative.x += newRotation.x; x = cumulative.x * addDet; cumulative.y += newRotation.y; y = cumulative.y * addDet; cumulative.z += newRotation.z; z = cumulative.z * addDet; //note: if speed is an issue, you can skip the normalization step return NormalizeQuaternion(x, y, z, w); } public static Quaternion NormalizeQuaternion(float x, float y, float z, float w){ float lengthD = 1.0f / (w*w + x*x + y*y + z*z); w *= lengthD; x *= lengthD; y *= lengthD; z *= lengthD; return new Quaternion(x, y, z, w); } //Changes the sign of the quaternion components. This is not the same as the inverse. public static Quaternion InverseSignQuaternion(Quaternion q){ return new Quaternion(-q.x, -q.y, -q.z, -q.w); } //Returns true if the two input quaternions are close to each other. This can //be used to check whether or not one of two quaternions which are supposed to //be very similar but has its component signs reversed (q has the same rotation as //-q) public static bool AreQuaternionsClose(Quaternion q1, Quaternion q2){ float dot = Quaternion.Dot(q1, q2); if(dot < 0.0f){ return false; } else{ return true; } }

Sorry to bump this old thread but it's the only one which had the answer I seek. Thanks for the solution, but what do you mean by that?

The rotations from all quaternions should not be too much different. Let's say you have a bunch of quaternions, each pointing at a random star in the sky. Averaging those will fail. They are too different. Now imagine you have a bunch of quaternions, each pointing at the same star, but made with different measurement equipment in your yard. Those are probably close enough. Of course a quaternion doesn't just point at something, but have a rotation as well. Same logic for the rotation part. It shouldn't differ too much. I used that function to average a bunch of world space pose estimates from an AR marker. The input estimates surely have some jitter, but the average output is accurate. Some more information and links scientific papers here, if you are interested in averaging totally random quaternions: http://stackoverflow.com/questions/12374087/average-of-multiple-quaternions

Adding to that, mathematically correct is a quite strong term but according to this it could yield a close approximation as long as the difference in the angles of rotation is small. But INSTEAD of float D = 1.0f / (w*w + x*x + y*y + z*z); you should do float D = 1.0f / Mathf.sqrt(w*w + x*x + y*y + z*z); (but mind that Unity will normalize it for you, so this can be omitted anyway) AND you should initialize addedRotation to the first value of the array or set it to 0, 0, 0, 0 (otherwise the identity rotation will be part of your average!) The code bellow might work better, and q -q should not be a problem (I am not sure about efficiency though) Code (csharp): // assuming qArray.Length > 1 Quaternion AverageQuaternion(Quaternion [] qArray){ Quaternion qAvg = qArray[0]; float weight; for (int i = 1 ; i < qArray.Length; i++) { weight = 1.0f / (float)(i+1); qAvg = Quaternion.Slerp(qAvg, qArray[i], weight); } return qAvg; }

how to get the right result quaternion when giving different quaternion with weights? Q1 0.1 Q2 0.3 Q3 0.4 Q4 0.2 Get the result Q?

Slerp(identity, q1, w1) * Slerp(idenity, q2, w2) * Slerp(idenity, q3, w3) * Slerp(identity, q4, w4) ... That'd be my first attempt

Calculating average, not weight based. This worked for me, based on lordofduct response. Code (CSharp): Quaternion q_average = Quaternion.identity ; List <Quaternion> qList = new List <Quaternion> () qList.Add (Q0) ; // add some first quaternion qList.Add (Q1) ; // add some second quaternion qList.Add (Q2) ; // add some next quaternion // ad some more quaternions float averageWeight = 1f / siblings.Count ; for ( int i = 0; i < qList.Count; i ++ ) { Quaternion q = qList [ i ] ; // based on [URL='https://forum.unity.com/members/lordofduct.66428/']lordofduct[/URL] response q_average *= Quaternion.Slerp ( Quaternion.identity, q, f_averageWeight ) ; } // output rotation - attach to some object, or whatever this.transform.rotation = q_average ;

In some cases (when you have one look-from-point and lots of lookAt points) it might be simpler to do whatever math on the points, then find one quaternion. You lose local-z spin, but you probably didn't want it anyway.

Chaining Slerps to together isn't quite the proper way to go about it. I would simply convert the quaternions into look directions (forward and up pairs), add up the directions, normalize them, then convert the result pairs back to a quaternion via LookRotation(). and you got your average (which might be a zero length, but hey that actually can be a valid answer). Why? Order matters when you add Quaternions together. Unlike normal numbers, Quaternions lack the Communtative property. If you put the same group of Quaternions into that sort of quaternion-only averaging function, with varying orders, you can actually get different averaged quaternion results.

It depends on what you are trying to solve for, but I would expect that you will get a reasonable result by first converting the quaternions into a linear space before applying a weight. The easiest way to convert to a linear, mostly orthogonal space is to take the derivative. Try averaging the weighted logarithms, and the use the exponent of the result. This effectively converts every quaternion into an angular velocity and takes resulting average velocity. The angular velocities are the velocities that would result in the full rotation after 1s. Hope that helps. I can provide code if desired. David

This has its shortcomings as mentioned, as the problem of averaging rotations is ill posed. But it will generally work well for rotations that are not too far apart and for rotations that do not have inherent constraints. I.e. where you want to minimize the average rotation between the result and the inputs. If you have constraints (like minimize roll) you can do better. Code (CSharp): [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(Quaternion[] source) { Assert.IsFalse(source.IsNullOrEmpty()); Vector3 result = new Vector3(); foreach (var q in source) { result += q.ToAngularVelocity(); } return (result / source.Length).FromAngularVelocity(); } Hope that helps! David

Actually you can get a better result if performance is not a concern byt iterating to find the minimum max error: Code (CSharp): [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 refence*((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; }

Awesome stuff. Is there any way to add weights to each rotation? Edit: Multiplying the ToAngularVelocity() by the weight (0.0 - 1.0) and returning result.FromAngularVelocity() seems to work fine.

This is my solution for averaging weighted rotations (quaternions). Base is a class for weighted rotations with a weight between 0 and 1. Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class WeightedRotation { public Quaternion Rotation; public float RotWeight; // between 0 und 1 public WeightedRotation(Quaternion rotation, float weigth) { if (weigth > 1 || weigth < 0) { Debug.LogError("weight out of bound"); } else { Rotation = rotation; RotWeight = weigth; } } } public class AverageRotations : MonoBehaviour { public List<WeightedRotation> Rotations; private Quaternion AverageRotation() { if (Rotations.Count > 0) { Quaternion q_average = Rotations[0].Rotation; for (int i = 1; i < Rotations.Count; i++) { float weight = 1f / (i + 1) * Rotations[i].RotWeight; q_average = Quaternion.Slerp(q_average, Rotations[i].Rotation, weight); } Rotations.Clear(); return q_average; } Rotations.Clear(); return transform.rotation; } }

Like I mentioned before chaining Slerps is not how you average several Quaternions. Slerp is only a valid average between 2 quaternions. Think of it like this. Whats the average of [1,2,3,4,5]? (1+2+3+4+5)/5 = 15/5 = 3. The average is 3. But chaining slerps is basically ((((1+2)/2 + 3)/2 + 4)/2 + 5)/2 = 4.0625. these are not the same! It favors the last elements. The earlier Quaternions become exponentially insignificant the more members you try to calculate this way. It ONLY works if there are two elements! including more will give an incorrectly skewed result. Plus, due to the dimension of math Quaternions reside in, they lack the commutative property, meaning order matters... quite a lot. Even just adding the 2 same rotations will give completely different answers based on which one is added to which. Take your code for example. Say you take 3 different rotations, give them whatever weights you like and you attempt to calculate that result. Remember that result. now try the same thing again. take the exact same 3 rotations, give them the exact same 3 weights. but you shuffle the order in the list which you used to calculate them. unless you did something like choose the exact same rotation/weights 3 times the 2nd result will be different from the 1st and will play favorites to the later quaternions more. To average Quaternions correctly By far the simplest way that's actually correct is to convert each quaternion into a forward and up look direction. Doing so pulls the Quaternion out of the higher dimension of math it resides in. - Add all forwards together, add all ups together. You can even include your weights if you wanted its trivial for vectors. - Then normalize the forwardSum, normalize the upSum. - create a new quaternion using that normalizedForwardSum and normalizedUpSum. That quaternion is the answer, its the proper averaged quaternion out of all quaternions, weighted or not, and regardless of order, that you've included.

Well you missed that they used a weight of 1f / (i + 1). So your example is wrong. The chaining is NOT ((((1+2)/2 + 3)/2 + 4)/2 + 5)/2 but the weight gets progressively smaller and sticks closer to the average value. So the first step would do (1*(1/2) + 2*(1/2)). The second step would take the result of the first and does this: ((1*(1/2) + 2*(1/2))*(2/3) + 3*(1/3)) the next step is (((1*(1/2) + 2*(1/2))*(2/3) + 3*(1/3))*(3/4) + 4*(1/4)) and the last one (((1*(1/2) + 2*(1/2))*(2/3) + 3*(1/3))*(3/4) + 4*(1/4))*(4/5) + 5*(1/5) which gives you the value 3 as well. So the lerp / slerp is not used to simply pick the midpoint but a weighted factor so it adds up correctly. Though while this does work mathematically, I'm sure it's not that numerically stable and also not terribly efficient either.

It may have been a poor example, but my main point actually still stands. To be clear, I didn't have a problem with the pre-weights, I have a problem with the slerps. Even when taking the 1f / (i + 1) pre-weights into account, it doesn't fix the pitfalls Quaternion's Slerp itself is susceptible to. Yes, the pre weights correct the typical imbalance for a set of numbers chained this way, and generally it should give an accurate average. Of the formulas posted on this thread that try to solve using a Slerp-chaining technique its among the most accurate implementations. but it's not 100% accurate. It doesn't solve the other problems inherent specifically to Quaternions. Using this formula I can still easily think up sets of Quaternions which would calculate to a completely incorrect average, where a correct average is still possible to acquire. Lets say you have 3 quaternions A,B,and C. Each with forward vectors Vector3.Left, Vector3.Right, and Vector3.Down. What is the Quaternion result D of Slerp(A,B)? Where should the result point to? Its basically undefined since Slerp can't give a valid answer between rotations that are polar opposites. and now what happens if you try to find Quaternion E from Slerp(D,C)? you end up with an answer that can't be guaranteed to be correct. However if you take those same 3 rotations (A,B,C) and change the order in which you slerp them: D = Slerp(A,C) E = Slerp(D,B) you won't get undefined answers. it'll even be the correct average if you include on the pre-weights. But this presents a problem. This means the formula now has to validate each slerp and compensate when it returns undefines. suddenly the formula is no longer so simple if it wants to be consistently correct. This is why I push for calculating via look directions so much, it bypasses this issue entirely, its conceptually intuitive to follow the logic, and doesn't require any pre-weighting.

Put the code of @Elecman into a out of the box usable class Code (CSharp): public static partial class Math { public static Quaternion AverageQuaternion(Quaternion[] quats) { if(quats.Length == 0) { return Quaternion.identity; } Vector4 cumulative = new Vector4(0, 0, 0, 0); foreach(Quaternion quat in quats) { AverageQuaternion_Internal(ref cumulative, quat, quats[0]); } float addDet = 1f / (float)quats.Length; float x = cumulative.x * addDet; float y = cumulative.y * addDet; float z = cumulative.z * addDet; float w = cumulative.w * addDet; //note: if speed is an issue, you can skip the normalization step return NormalizeQuaternion(new Quaternion(x,y,z,w)); } //Get an average (mean) from more then two quaternions (with two, slerp would be used). //Note: this only works if all the quaternions are relatively close together. //Usage: //-Cumulative is an external Vector4 which holds all the added x y z and w components. //-newRotation is the next rotation to be added to the average pool //-firstRotation is the first quaternion of the array to be averaged //-addAmount holds the total amount of quaternions which are currently added static void AverageQuaternion_Internal(ref Vector4 cumulative, Quaternion newRotation, Quaternion firstRotation) { //Before we add the new rotation to the average (mean), we have to check whether the quaternion has to be inverted. Because //q and -q are the same rotation, but cannot be averaged, we have to make sure they are all the same. if (!AreQuaternionsClose(newRotation, firstRotation)) { newRotation = InverseSignQuaternion(newRotation); } //Average the values cumulative.w += newRotation.w; cumulative.x += newRotation.x; cumulative.y += newRotation.y; cumulative.z += newRotation.z; } public static Quaternion NormalizeQuaternion(Quaternion quat) { float lengthD = 1.0f / Mathf.Sqrt(quat.w * quat.w + quat.x * quat.x + quat.y * quat.y + quat.z * quat.z); quat.x *= lengthD; quat.y *= lengthD; quat.z *= lengthD; quat.w *= lengthD; return quat; } //Changes the sign of the quaternion components. This is not the same as the inverse. public static Quaternion InverseSignQuaternion(Quaternion q) { return new Quaternion(-q.x, -q.y, -q.z, -q.w); } //Returns true if the two input quaternions are close to each other. This can //be used to check whether or not one of two quaternions which are supposed to //be very similar but has its component signs reversed (q has the same rotation as //-q) public static bool AreQuaternionsClose(Quaternion q1, Quaternion q2) { float dot = Quaternion.Dot(q1, q2); if (dot < 0.0f) { return false; } else { return true; } } }

I actually considered this approach before I came here and the problem is if you average two quaternions that are exact opposites of eacth other you get zero for a look vector and zero for an up vector. But in most other cases I think it would work fine? I'm sure you would have to normalized the averaged forward and up vectors. I don't know if you can use a direction vector who's magnitude is not 1 for defining quaternions. In truth, I have never tried.

So, I finally had a need for the weighted average of quaternions, and of the three major approaches shown in this thread, I have to say that @JoshuaMcKenzie 's approach was the simplest to implement, and the most predictable. The white ball gizmos are denoting the weights that I am applying to each pink monkey target. The weights sum up to 1. (Thanks to @Kybernetik for his implementation of this polar blending tree in Animancer, I am not going to publish my adaptation of that routine.) I then orient the golden monkey to match the weighted average of the pink monkey targets' rotations. In this snippet, you can see the kind of instability that @CCrowell just mentioned; when the golden monkey moves right behind the center target monkey, there's a little bit of chunkiness, because the farther monkeys are rotated diametrically opposite the center one. In most cases, this won't be a problem, and can be overcome with some careful avoidance of the deadzone. Code (CSharp): Quaternion WeightedAverageQuaternions(Quaternion[] quaternions, float[] weights) { Assert.IsTrue(quaternions != null && quaternions.Length > 0); Assert.IsTrue(weights != null && weights.Length >= quaternions.Length); int count = quaternions.Length; Vector3 forwardSum = Vector3.zero; Vector3 upwardSum = Vector3.zero; for (int i = 0; i < count; i++) { forwardSum += weights[i] * (quaternions[i] * Vector3.forward); upwardSum += weights[i] * (quaternions[i] * Vector3.up); } forwardSum /= (float)count; upwardSum /= (float)count; return Quaternion.LookRotation(forwardSum, upwardSum); }