math - quaternion - "Promedio" de mĂșltiples cuaterniones?
quaternion to angle (9)
Con los cuaterniones puede hacer lo mismo, pero con una pequeña corrección: 1. Niegue el cuaternión antes de promediar si su producto de puntos con suma previa es negativo. 2. Normalice el cuaternión promedio, el fin del promedio, si su biblioteca funciona con cuaterniones de unidades.
El cuaternión promedio representará una rotación aproximadamente promedio (error máximo de aproximadamente 5 grados).
ADVERTENCIA: la matriz promedio de diferentes orientaciones puede romperse si las rotaciones son demasiado diferentes.
Estoy intentando cambiar de matrices a cuaterniones para la animación esquelética en mi programa OpenGL, pero he encontrado un problema:
Dado un número de cuaterniones unitarios, necesito obtener un cuaternión que, cuando se usa para transformar un vector, proporcione un vector que sea el promedio del vector transformado por cada cuaternión individualmente. (con matrices, simplemente agregaría las matrices y las dividiría por el número de matrices)
Contrariamente a la creencia popular en la industria de los gráficos por computadora, existe un algoritmo sencillo para resolver este problema que es robusto, preciso y simple que proviene de la industria aeroespacial. Se ejecuta en el tiempo lineal en la cantidad de cuaterniones promediados más un factor constante (grande).
Deje Q = [a_1 * q_1 a_2 * q_2 ... a_n * q_n]
Donde a_i es el peso del I quaternion, y q_i es el I quaternion promediado, como un vector de columna. Q es por lo tanto una matriz 4xN.
El vector propio normalizado correspondiente al mayor valor propio de Q * Q ^ T es el promedio ponderado. Dado que Q * Q ^ T es autoadjuntivo y al menos hay métodos semi-definidos, rápidos y robustos para resolver ese problema propio. La computación del producto matriz-matriz es el único paso que crece con la cantidad de elementos promediados.
Vea esta nota técnica en el Diario de Orientación, Control y Dinámica de 2007 , que es un documento de resumen de este y otros métodos. En la era moderna, el método que mencioné anteriormente hace una buena compensación por la confiabilidad y robustez de la implementación, ¡y ya se publicó en los libros de texto en 1978!
Desafortunadamente no es terriblemente simple de hacer, pero es posible. Aquí hay un documento técnico que explica las matemáticas detrás de él: http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20070017872_2007014421.pdf
Echa un vistazo a la página de Unity3D Wiki (con código): http://wiki.unity3d.com/index.php/Averaging_Quaternions_and_Vectors
También esta publicación: http://forum.unity3d.com/threads/86898-Average-quaternions
Esta es mi implementación en python del algoritmo de Tolga Birdal:
import numpy as np
def quatWAvgMarkley(Q, weights):
''''''
Averaging Quaternions.
Arguments:
Q(ndarray): an Mx4 ndarray of quaternions.
weights(list): an M elements list, a weight for each quaternion.
''''''
# Form the symmetric accumulator matrix
A = np.zeros((4, 4))
M = Q.shape[0]
wSum = 0
for i in range(M):
q = Q[i, :]
w_i = weights[i]
A += w_i * (np.outer(q, q)) # rank 1 update
wSum += w_i
# scale
A /= wSum
# Get the eigenvector corresponding to largest eigen value
return np.linalg.eigh(A)[1][:, -1]
Hay un informe técnico de 2001 que establece que la media es realmente una buena aproximación, siempre que los cuaterniones estén muy juntos. (para el caso de -q = q, puedes voltear los que apuntan en la otra dirección multiplicándolos previamente por -1, de modo que todos los cuaterniones involucren vida en la misma media esfera.
Un enfoque aún mejor se esboza en este documento de 2007 , que implica el uso de una SVD. Este es el mismo papel al que Nathan hizo referencia. Me gustaría agregar que no solo hay un C ++, sino también una implementación de Matlab . Al ejecutar el script de prueba que viene con el código de matlab, puedo decir que da resultados bastante buenos para pequeñas perturbaciones (0.004 * ruido uniforme) de los cuaterniones involucrados:
qinit=rand(4,1);
Q=repmat(qinit,1,10);
% apply small perturbation to the quaternions
perturb=0.004;
Q2=Q+rand(size(Q))*perturb;
Intenté Slerping the quaternions como se sugiere here pero eso no funcionó para lo que estoy tratando de hacer (el modelo estaba distorsionado), así que simplemente terminé transformando los vectores por cada cuaternión y luego haciendo un promedio (hasta que puedo encontrar un mejor solución).
Los cuaterniones no son un conjunto ideal de DOF para usar en rotaciones al calcular un promedio no restringido.
Esto es lo que uso la mayor parte del tiempo (
[MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector3 ToAngularVelocity( this Quaternion q ) { if ( abs(qw) > 1023.5f / 1024.0f) return new Vector3(); var angle = acos( abs(qw) ); var gain = Sign(qw)*2.0f * angle / Sin(angle); return new Vector3(qx * gain, qy * gain, qz * 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(wx * siGain, wy * siGain, wz * 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; }`
No puedes agregar cuaterniones. Lo que puedes hacer es encontrar un cuaternión que rote continuamente entre dos ángulos, incluso a mitad de camino. La interpolación de quaternión se conoce como "slerp" y tiene una página de wikipedia. Este es un truco muy útil para la animación. En algunos aspectos, slerp es la razón principal para usar quaternions en gráficos de computadora.
Aquí está la implementación de la función MATLAB que utilizo para promediar Quaternions para la estimación de orientación. Es sencillo convertir el MATLAB a cualquier otro idioma, excepto que este método en particular (Markley 2007) requiere el cálculo de los vectores propios y los valores propios. Hay muchas bibliotecas (incluyendo Eigen C ++) que pueden hacer esto por usted.
Puede leer la descripción / encabezado del archivo para ver las matemáticas del documento original.
archivo matlab tomado de http://www.mathworks.com/matlabcentral/fileexchange/40098-tolgabirdal-averaging-quaternions :
% by Tolga Birdal
% Q is an Mx4 matrix of quaternions. weights is an Mx1 vector, a weight for
% each quaternion.
% Qavg is the weightedaverage quaternion
% This function is especially useful for example when clustering poses
% after a matching process. In such cases a form of weighting per rotation
% is available (e.g. number of votes), which can guide the trust towards a
% specific pose. weights might then be interpreted as the vector of votes
% per pose.
% Markley, F. Landis, Yang Cheng, John Lucas Crassidis, and Yaakov Oshman.
% "Averaging quaternions." Journal of Guidance, Control, and Dynamics 30,
% no. 4 (2007): 1193-1197.
function [Qavg]=quatWAvgMarkley(Q, weights)
% Form the symmetric accumulator matrix
A=zeros(4,4);
M=size(Q,1);
wSum = 0;
for i=1:M
q = Q(i,:)'';
w_i = weights(i);
A=w_i.*(q*q'')+A; % rank 1 update
wSum = wSum + w_i;
end
% scale
A=(1.0/wSum)*A;
% Get the eigenvector corresponding to largest eigen value
[Qavg, ~]=eigs(A,1);
end