Преобразование из вращения Эйлера ZXZ в вращения XYZ с фиксированной осью - PullRequest
5 голосов
/ 19 марта 2012

Проблема, с которой я столкнулся, заключается в том, что мне нужно преобразовать вращения XYZ с фиксированной осью в вращения Эйлера вокруг Z, затем X ', затем Z' '.

Вот соответствующие матрицы:

X: X

Y: Y

Z: Z

Объединено, как Rz (фунтов на квадратный дюйм) Ry (phi) Rx (тета) = Rxyz (тета, фи, пси);они дают:

Rxyz: Rxyz

И матрицу вращения для конкретного соглашения углов Эйлера, которое я хочу;это:

Эйлер: Euler

Итак, мой первоначальный план состоял в том, чтобы сравнить матричные элементы и извлечь нужные мне углы;Я придумал это (фактический текущий код в конце):

Code

Но это не работает при нескольких обстоятельствах.Наиболее очевидно, когда Cos (тета) Cos (фи) == 1;с тех пор Cos (бета) = 1, и поэтому Sin [бета] = 0. Где Sin (бета) s2 в коде.Это происходит только тогда, когда Cos (тета) и cos (фи) = +/- 1.

Так что сразу я могу просто исключить возможные ситуации;

Когда тета или фи = 0,180, 360, 540, ..., затем Cos (тета) и Cos (фи) равны +/- 1;

, поэтому мне нужно сделать это по-разному только для этих случаев;

И я получил следующий код:

public static double[] ZXZtoEuler(double θ, double φ, double ψ){

    θ *= Math.PI/180.0;
    φ *= Math.PI/180.0;
    ψ *= Math.PI/180.0;

    double α = -1;
    double β = -1;
    double γ = -1;

    double c2 = Math.cos(θ) * Math.cos(φ);

    β = Math.acos(r(c2));

    if(eq(c2,1) || eq(c2,-1)){
        if(eq(Math.cos(θ),1)){
            if(eq(Math.cos(φ),1)){
                α = 0.0;
                γ = ψ;
            }else if(eq(Math.cos(φ),-1)){
                α = 0.0;
                γ = Math.PI - ψ;
            }
        }else if(eq(Math.cos(θ),-1)){
            if(eq(Math.cos(φ),1)){
                α = 0.0;
                γ = -ψ;
            }else if(eq(Math.cos(φ),-1)){
                α = 0.0;
                γ = ψ + Math.PI;
            }
        }
    }else{

        //original way

        double s2 = Math.sin(β);

        double c3 = ( Math.sin(θ) * Math.cos(φ) )/ s2;
        double s1 = ( Math.sin(θ) * Math.sin(ψ) + Math.cos(θ) * Math.sin(φ) * Math.cos(ψ) )/s2;

        γ = Math.acos(r(c3));
        α = Math.asin(r(s1));

    }

    α *= 180/Math.PI;
    β *= 180/Math.PI;
    γ *= 180/Math.PI;

    return new double[] {r(α), r(β), r(γ)};
}

Где r и eq - это просто две простые функции;

public static double r(double a){
    double prec = 1000000000.0;
    return Math.round(a*prec)/prec;
}

static double thresh = 1E-4;
public static boolean eq(double a, double b){
    return (Math.abs(a-b) < thresh);
}

eq - это просто сравнение чисел для тестов, а r эточтобы предотвратить ошибки с плавающей точкой, выдвигая числа вне диапазона Math.acos / Math.asin и давая мне результаты NaN;

(то есть время от времени я получал Math.acos (1.000000000000000004) или что-то в этом роде.)

, который учитывает 4 случая вращения вокруг x и y, которые оставляют c2 == 1.

Но теперь проблема возникает;

ВсеЯ сделал выше, имеет смысл для меня, но это не дает правильные углы;

Вот некоторые выходные данные, в каждой паре, первыеуглы тета-пси-пси, а вторая из каждой пары - соответствующие альфа-бета-гамма-линии.Игнорируя ошибки округления, кажется, что некоторые углы отклоняются примерно на

[0.0, 0.0, 0.0] - correct!
[0.0, 0.0, 0.0]

[0.0, 0.0, 45.0] - correct!
[0.0, 0.0, 45.0]

[0.0, 0.0, 90.0] - correct!
[0.0, 0.0, 90.0]

[0.0, 0.0, 135.0] - correct!
[0.0, 0.0, 135.0]

[0.0, 0.0, 180.0] - correct
[0.0, 0.0, 180.0]

[0.0, 0.0, 225.0] - correct
[0.0, 0.0, 225.0]

[0.0, 0.0, 270.0] - correct
[0.0, 0.0, 270.0]

[0.0, 0.0, 315.0] - correct
[0.0, 0.0, 315.0]

[0.0, 45.0, 0.0] - incorrect: should be [90, 45, -90]
[90.0, 44.999982, 90.0]

[0.0, 45.0, 45.0]
[45.000018, 44.999982, 90.0]

[0.0, 45.0, 90.0]
[0.0, 44.999982, 90.0]

[0.0, 45.0, 135.0]
[-45.000018, 44.999982, 90.0]

[0.0, 45.0, 180.0]
[-90.0, 44.999982, 90.0]

[0.0, 45.0, 225.0]
[-45.000018, 44.999982, 90.0]

[0.0, 45.0, 270.0]
[0.0, 44.999982, 90.0]

[0.0, 45.0, 315.0]
[45.000018, 44.999982, 90.0]

[0.0, 90.0, 0.0]
[90.0, 90.0, 90.0]

[0.0, 90.0, 45.0]
[45.000018, 90.0, 90.0]

[0.0, 90.0, 90.0]
[0.0, 90.0, 90.0]

[0.0, 90.0, 135.0]
[-45.000018, 90.0, 90.0]

[0.0, 90.0, 180.0]
[-90.0, 90.0, 90.0]

[0.0, 90.0, 225.0]
[-45.000018, 90.0, 90.0]

Я думаю, это связано с тем, как работают Math.acos и Math.asin. Может кто-нибудь придумать решение?

EDIT: math.asin и math.acos возвращают значения от -pi / 2 до pi / 2 и 0 и pi соответственно.Это не двусмысленно, поэтому я не думаю, что проблема здесь.Кажется, у меня где-то может быть неправильная математика, но я не вижу дыру в моих рассуждениях ...

РЕДАКТИРОВАТЬ 2: Для всех, кто не знает, как работает ротация Эйлера, это так:

Euler Angles Gif

То есть, вращайтесь вокруг Z , затем вокруг новой оси X ( X '), затемвокруг новой оси Z '' .

1 Ответ

1 голос
/ 21 марта 2012

Я не совсем понял это, но одну вещь я заметил: вы используете функции arccos / arcsin, как если бы cos / sin были биективными, просто принимая их обратное. Однако, принимая арккос, рассмотрим общие решения функций дуги. Например, когда cos y = x, тогда существует два (ну, бесконечно много) решения:

  • y = arccos x + 2kPI, где k element Z
  • y = 2PI - arccos x + 2kPI, k, как указано выше

При k=-1 последнее уравнение сводится к

  • y = -arccos x

Итак, всего y = +- arccos x. По сути это сводится к тому, что cos симметрично по оси x=0. Аналогичный аргумент применяется к arcsin, что приводит к y = PI - asin xk=0 в общем решении sin y = x)

Это немедленно относится к вашему коду. Заявление γ = Math.acos(r(c3)); каким-то образом должно учитывать знак. Я борюсь с этим, должны быть критерии, чтобы выбрать «неправильное» решение.

...