Как рассчитать угол по матрице вращения

Я использую два изображения одного объекта, объект в некоторой степени повернут от своего первого изображения.

Я рассчитал ПОЗИЦИЮ каждого изображения и преобразовал вектор вращения в матрицу с помощью Rodergues (). Теперь, как мне рассчитать и посмотреть, насколько он повернут от своего первого положения?

Я пробовал много способов, но ответы не были близки

РЕДАКТИРОВАТЬ: Моя камера зафиксирована, только объект движется.


person N.J    schedule 22.02.2013    source источник


Ответы (8)


Мы можем получить углы Эйлера из матрицы вращения, используя следующую формулу.

Учитывая матрицу вращения 3 × 3

введите описание изображения здесь

Три угла Эйлера равны

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Здесь atan2 - это та же функция арктангенса с проверкой квадранта, которую вы обычно находите в C или Matlab.

Примечание. Следует соблюдать осторожность, если угол вокруг оси Y составляет точно +/- 90 °. В этом случае все элементы в первом столбце и последней строке, кроме элемента в нижнем углу, который равен 1 или -1, будут равны 0 (cos (1) = 0). Одним из решений было бы зафиксировать поворот вокруг оси x на 180 ° и вычислить угол вокруг оси z из: atan2 (r_12, -r_22).

См. Также https://www.geometrictools.com/Documentation/EulerAngles.pdf, который включает реализации для шести различных порядков углов Эйлера.

person Krish    schedule 22.02.2013
comment
Большое спасибо за вашу помощь .. +1 - person N.J; 26.02.2013
comment
Есть ли также способ определить, в каком порядке эти вращения должны применяться для достижения этой матрицы? - person x squared; 13.09.2014
comment
Здесь порядок вращения - Rx, затем Ry, затем Rz. Таким образом, Rz Ry Rx = R - person Patrick Stalph; 25.05.2018
comment
Как была получена косинусная часть вычисления высоты тона (sqrt из r_32 ^ 2 + r_33 ^ 3)? Большая часть литературы, которую я читаю, дает только расчет арксинуса для шага. В матрице ZYX я также могу получить то же самое через sqrt r_00 ^ 2 + r_10 ^ 2. - person John Ernest; 07.12.2019

Если R - матрица вращения (3x3), то угол поворота будет acos ((tr (R) -1) / 2), где tr ( R) - след матрицы (т.е. сумма диагональных элементов).

Это то, о чем вы просили; Я считаю, что вероятность того, что это не то, что вам нужно, составляет 90%.

person Beta    schedule 22.02.2013
comment
это дает ровно одно скалярное значение, соответствующее углу поворота по какой оси? - person Saravanabalagi Ramachandran; 07.12.2017
comment
@SaravanabalagiRamachandran: Конечно, собственный вектор матрицы. Просто решите (R - I) X = 0 для X (где X и 0 - векторы). - person Beta; 07.12.2017
comment
Что такое angle of rotation в этом случае? - person mrgloom; 14.10.2019

Хотел бы внести здесь свой вклад, поскольку я работал над той же проблемой. Я добавляю ценность приведенным выше ответам, публикуя чистую реализацию Python для преобразования трехмерной матрицы вращения (3x3) в соответствующие углы поворота (Rx), тангажа (Ry), рыскания (Rz).

Контрольный псевдокод: https://www.gregslabaugh.net/publications/euler.pdf (ссылка несколько больше недействительна / не работает в 2021 году ... но, тем не менее, я включаю ее здесь для полноты)

Настройка эталонной задачи: Допустим, у нас есть матрица вращения 3x3, и мы хотим извлечь углы Эйлера в градусах. Я сделаю реализацию Python как можно более «очевидной», чтобы упростить расшифровку того, что происходит в сценарии. Соответствующие кодировщики могут оптимизировать его для собственного использования.

Допущения. Сначала мы вращаем вокруг оси x, затем вокруг оси y и, наконец, вокруг оси z. Это определение порядка должно соблюдаться при адаптации этого фрагмента кода.

"""
Illustration of the rotation matrix / sometimes called 'orientation' matrix
R = [ 
       R11 , R12 , R13, 
       R21 , R22 , R23,
       R31 , R32 , R33  
    ]

REMARKS: 
1. this implementation is meant to make the mathematics easy to be deciphered
from the script, not so much on 'optimized' code. 
You can then optimize it to your own style. 

2. I have utilized naval rigid body terminology here whereby; 
2.1 roll -> rotation about x-axis 
2.2 pitch -> rotation about the y-axis 
2.3 yaw -> rotation about the z-axis (this is pointing 'upwards') 
"""
from math import (
    asin, pi, atan2, cos 
)

if R31 != 1 and R31 != -1: 
     pitch_1 = -1*asin(R31)
     pitch_2 = pi - pitch_1 
     roll_1 = atan2( R32 / cos(pitch_1) , R33 /cos(pitch_1) ) 
     roll_2 = atan2( R32 / cos(pitch_2) , R33 /cos(pitch_2) ) 
     yaw_1 = atan2( R21 / cos(pitch_1) , R11 / cos(pitch_1) )
     yaw_2 = atan2( R21 / cos(pitch_2) , R11 / cos(pitch_2) ) 

     # IMPORTANT NOTE here, there is more than one solution but we choose the first for this case for simplicity !
     # You can insert your own domain logic here on how to handle both solutions appropriately (see the reference publication link for more info). 
     pitch = pitch_1 
     roll = roll_1
     yaw = yaw_1 
else: 
     yaw = 0 # anything (we default this to zero)
     if R31 == -1: 
        pitch = pi/2 
        roll = yaw + atan2(R12,R13) 
     else: 
        pitch = -pi/2 
        roll = -1*yaw + atan2(-1*R12,-1*R13) 

# convert from radians to degrees
roll = roll*180/pi 
pitch = pitch*180/pi
yaw = yaw*180/pi 

rxyz_deg = [roll , pitch , yaw] 

Надеюсь, это поможет другим программистам!

person aaronlhe    schedule 13.10.2020

Для справки, этот код вычисляет углы Эйлера в MATLAB:

function Eul = RotMat2Euler(R)

if R(1,3) == 1 | R(1,3) == -1
  %special case
  E3 = 0; %set arbitrarily
  dlta = atan2(R(1,2),R(1,3));
  if R(1,3) == -1
    E2 = pi/2;
    E1 = E3 + dlta;
  else
    E2 = -pi/2;
    E1 = -E3 + dlta;
  end
else
  E2 = - asin(R(1,3));
  E1 = atan2(R(2,3)/cos(E2), R(3,3)/cos(E2));
  E3 = atan2(R(1,2)/cos(E2), R(1,1)/cos(E2));
end

Eul = [E1 E2 E3];

Код предоставлен Грэмом Тейлором, Джеффом Хинтоном и Сэмом Роуисом. Дополнительную информацию см. здесь

person Curnane    schedule 28.09.2017
comment
Я хотел попробовать, поэтому написал версию Python: import numpy as np def rot_mat_to_euler (r): if (r [0, 2] == 1) | (r [0, 2] == -1): # специальный случай e3 = 0 # произвольно установить dlt = np.arctan2 (r [0, 1], r [0, 2]), если r [0, 2] = = -1: e2 = np.pi / 2 e1 = e3 + dlt else: e2 = -np.pi / 2 e1 = -e3 + dlt else: e2 = -np.arcsin (r [0, 2]) e1 = np.arctan2 (r [1, 2] /np.cos (e2), r [2, 2] /np.cos (e2)) e3 = np.arctan2 (r [0, 1] /np.cos (e2 ), r [0, 0] /np.cos (e2)) return [e1, e2, e3] - person Бојан Матовски; 05.04.2019

Пусть R1c и R2c будут двумя матрицами вращения, которые вы вычислили. Они выражают поворот от объекта в позах 1 и 2 соответственно к кадру камеры (отсюда второй суффикс c). Вам нужна матрица вращения от позы 1 к позе 2, то есть R12. Чтобы вычислить его, вы должны мысленно повернуть объект из положения_1 в камеру, а затем из камеры в позу_2. Последнее вращение является инверсией позы_2 к камере, используемой R2c, следовательно:

R12 = R1c * inv(R2c)

Затем из матрицы R12 вы можете вычислить угол и ось вращения, используя формулу Родигеса.

person Francesco Callari    schedule 24.02.2013

Предположим, что он в основном вращается вокруг определенной оси координат (человек стоит перед камерой и вращается вокруг, чтобы получить матрицу вращения), попробуйте следующий код:

float calc_angle(Eigen::Matrix3f &R_, int axis_)
{
    //! the coordinate system is consistent with "vedo"
    //! suppose it mainly rotates around a certain coordinate axis(X/Y/Z)
    
    Eigen::Vector3f aX_(1.0f, 0.0f, 0.0f);
    Eigen::Vector3f aY_(0.0f, 1.0f, 0.0f);
    Eigen::Vector3f aZ_(0.0f, 0.0f, 1.0f);

    Eigen::Vector3f v0_, v1_;
    int axis_contrary_[2];
    switch (axis_)
    {
    case 0 /* x */:
        axis_contrary_[0] = 1;
        axis_contrary_[1] = 2;
        v0_ = aY_;
        v1_ = aZ_;
        break;
    case 1 /* y */:
        axis_contrary_[0] = 0;
        axis_contrary_[1] = 2;
        v0_ = aX_;
        v1_ = aZ_;
        break;
    case 2 /* z */:
        axis_contrary_[0] = 0;
        axis_contrary_[1] = 1;
        v0_ = aX_;
        v1_ = aY_;
        break;
    }

    Eigen::Vector3f v0_new_ = R_ * v0_; //R_.col(axis_contrary_[0]);
    v0_new_(axis_) = 0.0f;
    v0_new_.normalize();

    Eigen::Vector3f v1_new_ = R_ * v1_; //R_.col(axis_contrary_[1]);
    v1_new_(axis_) = 0.0f;
    v1_new_.normalize();

    Eigen::Vector3f v2_new_0_ = v0_.cross(v0_new_);
    Eigen::Vector3f v2_new_1_ = v1_.cross(v1_new_);
    bool is_reverse = ((v2_new_0_[axis_] + v2_new_1_[axis_]) / 2.0f < 0.0f);

    float cos_theta_0_ = v0_new_(axis_contrary_[0]);
    float cos_theta_1_ = v1_new_(axis_contrary_[1]);
    float theta_0_ = std::acos(cos_theta_0_) / 3.14f * 180.0f;
    float theta_1_ = std::acos(cos_theta_1_) / 3.14f * 180.0f;
    // std::cout << "theta_0_: " << theta_0_ << std::endl;
    // std::cout << "theta_1_: " << theta_1_ << std::endl;
    float theta_ = (theta_0_ + theta_1_) / 2.0f;

    float deg_;
    if (!is_reverse)
    {
        deg_ = theta_;
    }
    else
    {
        deg_ = 360.0f - theta_;
    }

    return deg_;
}

и вы можете визуализировать с помощью следующего кода:

import numpy as np
from glob import glob
from vedo import *

path_folder = ".../data/20210203_175550/res_R"
path_R_ALL = sorted(glob(path_folder + "/*_R.txt"))
path_t_ALL = sorted(glob(path_folder + "/*_t.txt"))

o = np.array([0, 0, 0])
x = np.mat([1, 0, 0]).T
y = np.mat([0, 1, 0]).T
z = np.mat([0, 0, 1]).T

vp = Plotter(axes=4)
vp += Box((0, 0, 0), 3, 3, 3, alpha=0.1)
for i, (path_R, path_t) in enumerate(zip(path_R_ALL, path_t_ALL)):
    R = np.loadtxt(path_R)
    R = np.mat(R.reshape(3, 3)).T
    # t = np.loadtxt(path_t)
    # t = np.mat(t).T

    Ax = Line(o, R*x, c="r")
    Ay = Line(o, R*y, c="g")
    Az = Line(o, R*z, c="b")
    vp += Ax
    vp += Ay
    vp += Az
    vp.show(interactive=1)
    vp -= Ax
    vp -= Ay
    vp -= Az

    x_new = R*x
    x_new[1] = 0
    x_new = x_new / np.linalg.norm(x_new)
    # print("x_new:", x_new)

    z_new = R*z
    z_new[1] = 0
    z_new = z_new / np.linalg.norm(z_new)
    # print("z_new:", z_new)

    cos_thetaX = x.T * x_new
    thetaX = np.arccos(cos_thetaX) / 3.14 * 180

    cos_thetaZ = z.T * z_new
    thetaZ = np.arccos(cos_thetaZ) / 3.14 * 180

    # print(x, x_new)
    tmpX = np.cross(x.T, x_new.T)
    # print("tmpX:", tmpX)
    if tmpX[0][1] < 0:
        thetaX = 360 - thetaX

    tmpZ = np.cross(z.T, z_new.T)
    # print("tmpZ:", tmpZ)
    if tmpZ[0][1] < 0:
        thetaZ = 360 - thetaZ
    # print(i, tmpX, tmpZ)
    print(i, thetaX, thetaZ)
person LogWell    schedule 13.05.2021

В случае 2D - код Python.

    import numpy as np
    import matplotlib.pyplot as plt

    def get_random_a(r = 3, centre_x = 5, centre_y = 5):
        angle = np.random.uniform(low=0.0, high=2 * np.pi)
        x = np.cos(angle) * r 
        y = np.sin(angle) * r 

        x += centre_x
        y += centre_y 

        return x, y 

    def norm(x):
        return np.sqrt(x[0] ** 2 + x[1] ** 2) 

    def normalize_vector(x):
        return x / norm(x)



    def rotate_A_onto_B(vector_a, vector_b ):

        A = normalize_vector(vector_a)
        B = normalize_vector(vector_b)

        cos_theta = np.dot(A, B)
        sin_theta = np.cross(A, B)

        theta = np.arctan2(sin_theta, cos_theta)

        M = np.array ( [[np.cos(theta ), -np.sin(theta)],
                        [np.sin(theta), np.cos(theta) ]
                       ])

        M_dash = np.array( [ [cos_theta, -sin_theta], 
                        [sin_theta, cos_theta]
                      ])

        print( f" np all close of M and M_dash : {np.allclose(M, M_dash)}" )

        vector_a = vector_a[:, np.newaxis]
        rotated_vector_a = np.dot(M, vector_a)
        return rotated_vector_a.squeeze()


    #--------------
    #----------------
    centre_x, centre_y = 5, 5
    r = 3 
    b = (centre_x, centre_y - r) 

    vector_b = np.array ( ( b[0] - centre_x, b[1] - centre_y ) )
    x, y = get_random_a(r, centre_x, centre_y)
    vector_a = np.array ( ( x - centre_x, y - centre_y ) )

    rotated_vector_a = rotate_A_onto_B(vector_a, vector_b)
    print("is the answer corrent ? ", np.allclose(rotated_vector_a, vector_b))

    print(rotated_vector_a)

    # plot 
    plt.plot( [centre_x, x], [ centre_y, y ]  )
    plt.plot( [centre_x, b[0]], [centre_y, b[1]] )

    plt.scatter( [centre_x, x, b[0] ], [ centre_y, y, b[1] ], c = "r")

    plt.text(centre_x, centre_y, f"centre : {centre_x, centre_y}")
    plt.text(x, y, f"a : {x, y}")
    plt.text(b[0], b[1], f"b : {b[0], b[1]}")

    plt.xlim(left = centre_x - r - 1, right = centre_x + r + 1 )
    plt.ylim(bottom= centre_y -r - 1 , top = centre_y + r +1 )

    plt.show()
person Davas Rohit    schedule 24.05.2021

Я думаю, вы хотите знать, как вычислить точный угол из матрицы вращения. Прежде всего, вы должны решить упорядочить (XYZ, ZYZ, ZXZ, и т. д.) из результата произведения вращения, вы можете получить углы с помощью обратной синусоидальной функции. (используя матрицу вращения, которую вы уже взяли!)

person insun    schedule 20.07.2021
comment
Пожалуйста, дополните свой ответ более подробной информацией. - person AsthaUndefined; 20.07.2021