“平均”多四元数?

问题描述 投票:41回答:10

我试图让从矩阵到四元数在我的OpenGL程序骨骼动画切换,但我已经遇到了一个问题:

由于一些单位四元的,我需要得到一个四元数是用于转化的载体将提供一个载体,其是由每个四元数转化单独载体的平均时。 (用矩阵的I将简单地添加矩阵在一起并通过矩阵的数量划分)

math quaternions
10个回答
0
投票

四元数是不DOF的理想的一组计算不受约束的平均时用于旋转。

以下是我用大部分的时间(

[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;

    }`

3
投票

这是我在托加Birdal算法的Python实现:

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]

2
投票

有其中规定,实际上意味着是一个相当不错的近似,提供了四元躺在距离很近的technical report from 2001。 (为-q = q的情况下,你可以只翻转,通过预先乘以-1他们在另一个方向指向的,使所有参与生活在同一个半球的四元数。

一个更好的做法是勾勒this paper from 2007,使用SVD涉及。这是内森引用相同的纸张。我想补充一点,不仅仅有一个C ++,也Matlab implementation。从执行其自带的MATLAB代码的测试脚本,我可以说,它给所涉及的四元数的小pertubations(0.004 *均匀噪声)相当不错的成绩:

qinit=rand(4,1);
Q=repmat(qinit,1,10);

% apply small perturbation to the quaternions 
perturb=0.004;
Q2=Q+rand(size(Q))*perturb;

1
投票

平均如果与以前的总和点积为负1,前四元数取负:与四元,你可以做同样的事情,但小幅盘整。 2.规范化平均四元,平均的结束,如果你的库单位四元的作品。

平均的四元数将代表大约平均旋转(最大误差约5度)。

警告:不同方向的平均矩阵如果旋转太大的不同被打破。


1
投票

因为这里有不同的方法,我写了一个MATLAB脚本对它们进行比较。这些结果似乎表明,简单平均和归一化四元数(一般为从团结维基方法,称为simple_average这里)可能会为其中四元数足够相似,小的偏差是可以接受的情况下是不够的。

下面是输出:

everything okay, max angle offset == 9.5843
qinit to average: 0.47053 degrees
qinit to simple_average: 0.47059 degrees
average to simple_average: 0.00046228 degrees
loop implementation to matrix implementation: 3.4151e-06 degrees

而这里的代码:

%% Generate random unity quaternion
rng(42); % set arbitrary seed for random number generator
M = 100;
qinit=rand(1,4) - 0.5;
qinit=qinit/norm(qinit);
Qinit=repmat(qinit,M,1);

%% apply small perturbation to the quaternions 
perturb=0.05; % 0.05 => +- 10 degrees of rotation (see angles_deg)
Q = Qinit + 2*(rand(size(Qinit)) - 0.5)*perturb;
Q = Q ./ vecnorm(Q, 2, 2); % Normalize perturbed quaternions
Q_inv = Q * diag([1 -1 -1 -1]); % calculated inverse perturbed rotations

%% Test if everything worked as expected: assert(Q2 * Q2_inv = unity)
unity = quatmultiply(Q, Q_inv);
Q_diffs = quatmultiply(Qinit, Q_inv);
angles = 2*acos(Q_diffs(:,1));
angles_deg = wrapTo180(rad2deg(angles));
if sum(sum(abs(unity - repmat([1 0 0 0], M, 1)))) > 0.0001
    disp('error, quaternion inversion failed for some reason');
else
    disp(['everything okay, max angle offset == ' num2str(max(angles_deg))])
end

%% Calculate average using matrix implementation of eigenvalues algorithm
[average,~] = eigs(transpose(Q) * Q, 1);
average = transpose(average);
diff = quatmultiply(qinit, average * diag([1 -1 -1 -1]));
diff_angle = 2*acos(diff(1));

%% Calculate average using algorithm from https://stackoverflow.com/a/29315869/1221661
average2 = quatWAvgMarkley(Q, ones(M,1));
diff2 = quatmultiply(average, average2 * diag([1 -1 -1 -1]));
diff2_angle = 2*acos(diff2(1));

%% Simply add coefficients and normalize the result
simple_average = sum(Q) / norm(sum(Q));
simple_diff = quatmultiply(qinit, simple_average * diag([1 -1 -1 -1]));
simple_diff_angle = 2*acos(simple_diff(1));
simple_to_complex = quatmultiply(simple_average, average * diag([1 -1 -1 -1]));
simple_to_complex_angle = 2*acos(simple_to_complex(1));

%% Compare results
disp(['qinit to average: ' num2str(wrapTo180(rad2deg(diff_angle))) ' degrees']);
disp(['qinit to simple_average: ' num2str(wrapTo180(rad2deg(simple_diff_angle))) ' degrees']);
disp(['average to simple_average: ' num2str(wrapTo180(rad2deg(simple_to_complex_angle))) ' degrees']);
disp(['loop implementation to matrix implementation: ' num2str(wrapTo180(rad2deg(diff2_angle))) ' degrees']);
© www.soinside.com 2019 - 2024. All rights reserved.