Подтвердить что ты не робот

Вращение 3D-вектора?

У меня есть два вектора в виде списков Python и угла. Например:.

v = [3,5,0]
axis = [4,4,1]
theta = 1.2 #radian

Каков наилучший/самый простой способ получить полученный вектор при вращении вектора v вокруг оси?

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

4b9b3361

Ответ 2

Используя формулу Эйлера-Родригеса:

import numpy as np
import math

def rotation_matrix(axis, theta):
    """
    Return the rotation matrix associated with counterclockwise rotation about
    the given axis by theta radians.
    """
    axis = np.asarray(axis)
    axis = axis / math.sqrt(np.dot(axis, axis))
    a = math.cos(theta / 2.0)
    b, c, d = -axis * math.sin(theta / 2.0)
    aa, bb, cc, dd = a * a, b * b, c * c, d * d
    bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
    return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
                     [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
                     [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])

v = [3, 5, 0]
axis = [4, 4, 1]
theta = 1.2 

print(np.dot(rotation_matrix(axis, theta), v)) 
# [ 2.74911638  4.77180932  1.91629719]

Ответ 3

Однострочник, с функциями numpy/scipy.

Мы используем следующее:

пусть a будет единичным вектором вдоль оси, то есть a = ось/норма (ось)
и A = я × a будет кососимметричной матрицей, ассоциированной с a, то есть перекрестным произведением единичной матрицы с

тогда M = exp (θ A) - матрица вращения.

from numpy import cross, eye, dot
from scipy.linalg import expm, norm

def M(axis, theta):
    return expm(cross(eye(3), axis/norm(axis)*theta))

v, axis, theta = [3,5,0], [4,4,1], 1.2
M0 = M(axis, theta)

print(dot(M0,v))
# [ 2.74911638  4.77180932  1.91629719]

expm (код здесь) вычисляет ряд Тейлора экспоненты:
\sum_{k=0}^{20} \frac{1}{k!} (θ A)^k, поэтому это дорогое время, но удобочитаемое и безопасное. Это может быть хорошим способом, если у вас мало поворотов, но много векторов.

Ответ 4

Я просто хотел упомянуть, что если требуется скорость, обертывание кода unutbu в scipy weave.inline и передача уже существующей матрицы в качестве параметра дает 20-кратное уменьшение времени выполнения.

Код (в rotation_matrix_test.py):

import numpy as np
import timeit

from math import cos, sin, sqrt
import numpy.random as nr

from scipy import weave

def rotation_matrix_weave(axis, theta, mat = None):
    if mat == None:
        mat = np.eye(3,3)

    support = "#include <math.h>"
    code = """
        double x = sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
        double a = cos(theta / 2.0);
        double b = -(axis[0] / x) * sin(theta / 2.0);
        double c = -(axis[1] / x) * sin(theta / 2.0);
        double d = -(axis[2] / x) * sin(theta / 2.0);

        mat[0] = a*a + b*b - c*c - d*d;
        mat[1] = 2 * (b*c - a*d);
        mat[2] = 2 * (b*d + a*c);

        mat[3*1 + 0] = 2*(b*c+a*d);
        mat[3*1 + 1] = a*a+c*c-b*b-d*d;
        mat[3*1 + 2] = 2*(c*d-a*b);

        mat[3*2 + 0] = 2*(b*d-a*c);
        mat[3*2 + 1] = 2*(c*d+a*b);
        mat[3*2 + 2] = a*a+d*d-b*b-c*c;
    """

    weave.inline(code, ['axis', 'theta', 'mat'], support_code = support, libraries = ['m'])

    return mat

def rotation_matrix_numpy(axis, theta):
    mat = np.eye(3,3)
    axis = axis/sqrt(np.dot(axis, axis))
    a = cos(theta/2.)
    b, c, d = -axis*sin(theta/2.)

    return np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
                  [2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
                  [2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])

Время:

>>> import timeit
>>> 
>>> setup = """
... import numpy as np
... import numpy.random as nr
... 
... from rotation_matrix_test import rotation_matrix_weave
... from rotation_matrix_test import rotation_matrix_numpy
... 
... mat1 = np.eye(3,3)
... theta = nr.random()
... axis = nr.random(3)
... """
>>> 
>>> timeit.repeat("rotation_matrix_weave(axis, theta, mat1)", setup=setup, number=100000)
[0.36641597747802734, 0.34883809089660645, 0.3459300994873047]
>>> timeit.repeat("rotation_matrix_numpy(axis, theta)", setup=setup, number=100000)
[7.180983066558838, 7.172032117843628, 7.180462837219238]

Ответ 5

Вот элегантный метод с использованием кватернионов, которые являются невероятно быстрыми; Я могу рассчитать 10 миллионов оборотов в секунду с соответствующим векторным массивом. Он основан на расширении кватерниона до numpy, найденного здесь.

Теория кватернионов: кватернион - это число с одним вещественным и 3 мнимыми измерениями, обычно записываемое как q = w + xi + yj + zk где "i", "j", "k" - мнимые измерения. Подобно тому, как единичное комплексное число "c" может представлять все 2d вращения с помощью c=exp(i * theta), единичный кватернион "q" может представлять все 3d вращения с помощью q=exp(p), где "p" является чистым мнимый кватернион, заданный вашей осью и углом.

Мы начнем с преобразования вашей оси и угла в кватернион, мнимые размеры которого определяются вашей осью вращения, а величина равна половине угла поворота в радианах. Векторы из 4 элементов (w, x, y, z) строятся следующим образом:

import numpy as np
import quaternion as quat

v = [3,5,0]
axis = [4,4,1]
theta = 1.2 #radian

vector = np.array([0.] + v)
rot_axis = np.array([0.] + axis)
axis_angle = (theta*0.5) * rot_axis/np.linalg.norm(rot_axis)

Во- первых, NumPy массив из 4 элементов строится с действительной составляющей ш = 0 для обоих вектора для поворота vector и осью вращения rot_axis. Представление угла оси затем строится путем нормализации и умножения на половину желаемого угла theta. Смотрите здесь, почему требуется половина угла.

Теперь создайте кватернионы v и qlog используя библиотеку, и получите кватернион вращения единицы q, взяв экспоненту.

vec = quat.quaternion(*v)
qlog = quat.quaternion(*axis_angle)
q = np.exp(qlog)

Наконец, вращение вектора вычисляется следующей операцией.

v_prime = q * vec * np.conjugate(q)

print(v_prime) # quaternion(0.0, 2.7491163, 4.7718093, 1.9162971)

Теперь просто отбросьте реальный элемент, и у вас есть повернутый вектор!

v_prime_vec = v_prime.imag # [2.74911638 4.77180932 1.91629719] as a numpy array

Обратите внимание, что этот метод особенно эффективен, если вам нужно повернуть вектор на множество последовательных вращений, поскольку произведение кватернионов можно просто вычислить как q = q1 * q2 * q3 * q4 *... * qn, а затем вектор поворачивается только с помощью 'q' в самом конце, используя v '= q * v * con (q).

Этот метод дает вам плавное преобразование между оператором 3D-поворота оси <---> просто с помощью функций exp и log (yes log(q) просто возвращает представление угла по оси!). Подробнее о том, как работает кватернионное умножение и т.д., См. Здесь.

Ответ 6

Я сделал довольно полную библиотеку трехмерной математики для Python {2,3}. Он все еще не использует Cython, но сильно зависит от эффективности numpy. Вы можете найти его здесь с помощью pip:

python[3] -m pip install math3d

Или посмотрите мой gitweb http://git.automatics.dyndns.dk/?p=pymath3d.git, а теперь и на github: https://github.com/mortlind/pymath3d.

После установки в Python вы можете создать объект ориентации, который может вращать векторы или быть частью объектов преобразования. Например, следующий фрагмент кода создает ориентацию, которая представляет вращение на 1 рад вокруг оси [1,2,3], применяет его к вектору [4,5,6] и печатает результат:

import math3d as m3d
r = m3d.Orientation.new_axis_angle([1,2,3], 1)
v = m3d.Vector(4,5,6)
print(r * v)

Выход будет

<Vector: (2.53727, 6.15234, 5.71935)>

Насколько я могу рассчитать, это более эффективно, примерно в четыре раза, чем тот, кто использует scipy, опубликованный BM выше. Однако, это требует установки моего пакета math3d.

Ответ 7

Отказ от ответственности: я автор этого пакета

Хотя специальные классы для вращений могут быть удобны, в некоторых случаях нужны матрицы вращения (например, для работы с другими библиотеками, такими как функции affine_transform в scipy). Чтобы каждый не реализовывал свои собственные маленькие функции генерации матриц, существует крошечный чистый пакет python, который делает только удобные функции генерации матриц вращения. Пакет находится на github (mgen) и может быть установлен через pip:

pip install mgen

Пример использования скопирован из readme:

import numpy as np
np.set_printoptions(suppress=True)

from mgen import rotation_around_axis
from mgen import rotation_from_angles
from mgen import rotation_around_x

matrix = rotation_from_angles([np.pi/2, 0, 0], 'XYX')
matrix.dot([0, 1, 0])
# array([0., 0., 1.])

matrix = rotation_around_axis([1, 0, 0], np.pi/2)
matrix.dot([0, 1, 0])
# array([0., 0., 1.])

matrix = rotation_around_x(np.pi/2)
matrix.dot([0, 1, 0])
# array([0., 0., 1.])

Обратите внимание, что матрицы - это просто обычные числовые массивы, поэтому при использовании этого пакета новые структуры данных не вводятся.

Ответ 8

Использование pyquaternion чрезвычайно просто; чтобы установить его (пока в Python), запустите в своей консоли:

import pip;
pip.main(['install','pyquaternion'])

После установки:

  from pyquaternion import Quaternion
  v = [3,5,0]
  axis = [4,4,1]
  theta = 1.2 #radian
  rotated_v = Quaternion(axis=axis,angle=theta).rotate(v)

Ответ 9

Это также можно решить с помощью теории кватернионов:

def angle_axis_quat(theta, axis):
    """
    Given an angle and an axis, it returns a quaternion.
    """
    axis = np.array(axis) / np.linalg.norm(axis)
    return np.append([np.cos(theta/2)],np.sin(theta/2) * axis)

def mult_quat(q1, q2):
    """
    Quaternion multiplication.
    """
    q3 = np.copy(q1)
    q3[0] = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3]
    q3[1] = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2]
    q3[2] = q1[0]*q2[2] - q1[1]*q2[3] + q1[2]*q2[0] + q1[3]*q2[1]
    q3[3] = q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1] + q1[3]*q2[0]
    return q3

def rotate_quat(quat, vect):
    """
    Rotate a vector with the rotation defined by a quaternion.
    """
    # Transfrom vect into an quaternion 
    vect = np.append([0],vect)
    # Normalize it
    norm_vect = np.linalg.norm(vect)
    vect = vect/norm_vect
    # Computes the conjugate of quat
    quat_ = np.append(quat[0],-quat[1:])
    # The result is given by: quat * vect * quat_
    res = mult_quat(quat, mult_quat(vect,quat_)) * norm_vect
    return res[1:]

v = [3, 5, 0]
axis = [4, 4, 1]
theta = 1.2 

print(rotate_quat(angle_axis_quat(theta, axis), v))
# [2.74911638 4.77180932 1.91629719]

Ответ 10

Мне нужно было повернуть трехмерную модель вокруг одной из трех осей {x, y, z}, в которую была встроена эта модель, и это был лучший результат для поиска того, как сделать это в клочья. Я использовал следующую простую функцию:

def rotate(X, theta, axis='x'):
  '''Rotate multidimensional array 'X' 'theta' degrees around axis 'axis''''
  c, s = np.cos(theta), np.sin(theta)
  if axis == 'x': return np.dot(X, np.array([
    [1.,  0,  0],
    [0 ,  c, -s],
    [0 ,  s,  c]
  ]))
  elif axis == 'y': return np.dot(X, np.array([
    [c,  0,  -s],
    [0,  1,   0],
    [s,  0,   c]
  ]))
  elif axis == 'z': return np.dot(X, np.array([
    [c, -s,  0 ],
    [s,  c,  0 ],
    [0,  0,  1.],
  ]))

Ответ 11

Используйте scipy Rotation.from_rotvec(). Аргументом является вектор вращения (единичный вектор), умноженный на угол поворота в радах.

from scipy.spatial.transform import Rotation
from numpy.linalg import norm


v = [3, 5, 0]
axis = [4, 4, 1]
theta = 1.2

axis = axis / norm(axis)  # normalize the rotation vector first
rot = Rotation.from_rotvec(theta * axis)

new_v = rot.apply(v)  
print(new_v)    # results in [2.74911638 4.77180932 1.91629719]

Существует еще несколько способов использования Rotation зависимости от имеющихся у вас данных о ротации:

  • from_quat Инициализируется из кватернионов.

  • from_dcm Инициализируется из направляющих косинусных матриц.

  • from_euler Инициализируется из углов Эйлера.


Примечание не по теме: однострочный код не обязательно лучший код, как подразумевают некоторые пользователи.