简体   繁体   English

3D 向量的旋转?

[英]Rotation of 3D vector?

I have two vectors as Python lists and an angle.我有两个向量 Python 列表和一个角度。 Eg:例如:

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

What is the best/easiest way to get the resulting vector when rotating the v vector around the axis?围绕轴旋转 v 矢量时,获得结果矢量的最佳/最简单方法是什么?

The rotation should appear to be counter clockwise for an observer to whom the axis vector is pointing.对于轴矢量指向的观察者,旋转应该看起来是逆时针的。 This is called the right hand rule这叫做右手法则

Using theEuler-Rodrigues formula :使用欧拉-罗德里格斯公式

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]

A one-liner, with numpy/scipy functions.具有 numpy/scipy 函数的单行代码。

We use the following:我们使用以下内容:

let a be the unit vector along axis , ie a = axis/norm(axis)a为沿axis的单位向量,即a = axis/norm(axis)
and A = I × a be the skew-symmetric matrix associated to a , ie the cross product of the identity matrix with a并且A = I × a是与a关联的斜对称矩阵,即单位矩阵与a的叉积

then M = exp(θ A) is the rotation matrix.那么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 (code here) computes the taylor series of the exponential: expm (这里的代码)计算指数的泰勒级数:
\sum_{k=0}^{20} \frac{1}{k!} (θ A)^k , so it's time expensive, but readable and secure. \sum_{k=0}^{20} \frac{1}{k!} (θ A)^k ,所以它的时间很昂贵,但可读且安全。 It can be a good way if you have few rotations to do but a lot of vectors.如果您要做的旋转很少但向量很多,这可能是一个好方法。

I just wanted to mention that if speed is required, wrapping unutbu's code in scipy's weave.inline and passing an already existing matrix as a parameter yields a 20-fold decrease in the running time.我只是想提一下,如果需要速度,将 unutbu 的代码包装在 scipy 的 weave.inline 中并将一个已经存在的矩阵作为参数传递,运行时间会减少 20 倍。

The code (in rotation_matrix_test.py):代码(在 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]])

The timing:时机:

>>> 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]

Here is an elegant method using quaternions that are blazingly fast;这是一种使用速度极快的四元数的优雅方法; I can calculate 10 million rotations per second with appropriately vectorised numpy arrays. It relies on the quaternion extension to numpy found here .我可以用适当矢量化的 numpy arrays 计算每秒 1000 万转。它依赖于在这里找到的 numpy 的四元数扩展。

Quaternion Theory: A quaternion is a number with one real and 3 imaginary dimensions usually written as q = w + xi + yj + zk where 'i', 'j', 'k' are imaginary dimensions.四元数理论:四元数是一个具有一个实数和三个虚数的数,通常写为q = w + xi + yj + zk ,其中“i”、“j”、“k”是虚数。 Just as a unit complex number 'c' can represent all 2d rotations by c=exp(i * theta) , a unit quaternion 'q' can represent all 3d rotations by q=exp(p) , where 'p' is a pure imaginary quaternion set by your axis and angle.正如单位复数“c”可以通过c=exp(i * theta)表示所有 2d 旋转一样,单位四元数“q”可以通过q=exp(p)表示所有 3d 次旋转,其中“p”是纯由轴和角度设置的虚构四元数。

We start by converting your axis and angle to a quaternion whose imaginary dimensions are given by your axis of rotation, and whose magnitude is given by half the angle of rotation in radians.我们首先将您的轴和角度转换为四元数,其虚构尺寸由您的旋转轴给出,其大小由旋转角度的一半以弧度表示。 The 4 element vectors (w, x, y, z) are constructed as follows: 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)

First, a numpy array of 4 elements is constructed with the real component w=0 for both the vector to be rotated vector and the rotation axis rot_axis .首先,构造一个 numpy 的 4 元素数组,对于要旋转的vector和旋转轴rot_axis ,实部 w=0。 The axis angle representation is then constructed by normalizing then multiplying by half the desired angle theta .然后通过归一化然后乘以所需角度theta的一半来构建轴角表示。 See here for why half the angle is required.请参阅此处了解为什么需要一半的角度。

Now create the quaternions v and qlog using the library, and get the unit rotation quaternion q by taking the exponential.现在使用库创建四元数vqlog ,并通过取指数得到单位旋转四元数q

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

Finally, the rotation of the vector is calculated by the following operation.最后,通过以下操作计算矢量的旋转。

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

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

Now just discard the real element and you have your rotated vector!现在只需丢弃真实元素,您就得到了旋转矢量!

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

Note that this method is particularly efficient if you have to rotate a vector through many sequential rotations, as the quaternion product can just be calculated as q = q1 * q2 * q3 * q4 *... * qn and then the vector is only rotated by 'q' at the very end using v' = q * v * conj(q).请注意,如果您必须通过多次顺序旋转来旋转矢量,则此方法特别有效,因为四元数乘积可以计算为 q = q1 * q2 * q3 * q4 *... * qn 然后仅旋转矢量通过'q'在最后使用v'= q * v * conj(q)。

This method gives you a seamless transformation between axis angle <---> 3d rotation operator simply by exp and log functions (yes log(q) just returns the axis-angle representation.).这种方法为您提供了轴角 <---> 3d 旋转运算符之间的无缝转换,只需通过explog函数即可(是的, log(q)仅返回轴角表示。)。 For further clarification of how quaternion multiplication etc, work, see here有关四元数乘法等工作原理的进一步说明,请参见此处

Take a look at http://vpython.org/contents/docs/visual/VisualIntro.html .看看http://vpython.org/contents/docs/visual/VisualIntro.html

It provides a vector class which has a method A.rotate(theta,B) .它提供了一个vector class ,它有一个方法A.rotate(theta,B) It also provides a helper function rotate(A,theta,B) if you don't want to call the method on A .如果您不想在A上调用该方法,它还提供了一个帮助程序 function rotate(A,theta,B)

http://vpython.org/contents/docs/visual/vector.html http://vpython.org/contents/docs/visual/vector.html

Use scipy's Rotation.from_rotvec() .使用 scipy 的Rotation.from_rotvec() The argument is the rotation vector (a unit vector) multiplied by the rotation angle in rads.参数是旋转矢量(单位矢量)乘以以弧度为单位的旋转角度。

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]

There are several more ways to use Rotation based on what data you have about the rotation:根据您拥有的关于旋转的数据,还有几种使用Rotation的方法:


Off-topic note: One line code is not necessarily better code as implied by some users.题外话:一行代码不一定是一些用户暗示的更好的代码。

I made a fairly complete library of 3D mathematics for Python{2,3}.我为 Python{2,3} 创建了一个相当完整的 3D 数学库。 It still does not use Cython, but relies heavily on the efficiency of numpy. You can find it here with pip:它仍然没有使用 Cython,而是严重依赖 numpy 的效率。你可以在这里找到它与 pip:

python[3] -m pip install math3d

Or have a look at my gitweb http://git.automatics.dyndns.dk/?p=pymath3d.git and now also on github: https://github.com/mortlind/pymath3d .或者看看我的 gitweb http://git.automatics.dyndns.dk/?p=pymath3d.git现在还有 github: https://github.com/mortlind/pymath3d

Once installed, in python you may create the orientation object which can rotate vectors, or be part of transform objects.安装后,在 python 中,您可以创建方向 object,它可以旋转矢量,或者成为变换对象的一部分。 Eg the following code snippet composes an orientation that represents a rotation of 1 rad around the axis [1,2,3], applies it to the vector [4,5,6], and prints the result:例如,下面的代码片段构成了一个表示围绕轴 [1,2,3] 旋转 1 rad 的方向,将其应用于向量 [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)

The output would be output 将是

<Vector: (2.53727, 6.15234, 5.71935)>

This is more efficient, by a factor of approximately four, as far as I can time it, than the oneliner using scipy posted by BM above.据我所知,这比上面 BM 发布的使用 scipy 的 oneliner 效率高大约四倍。 However, it requires installation of my math3d package.但是,它需要安装我的 math3d package。

It can also be solved using quaternion theory:也可以用四元数理论求解:

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]

Using pyquaternion is extremely simple;使用 pyquaternion 非常简单; to install it (while still in python), run in your console:要安装它(仍然在 python 中),请在您的控制台中运行:

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

Once installed:安装后:

  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)

Disclaimer: I am the author of this package免责声明:我是这个 package 的作者

While special classes for rotations can be convenient, in some cases one needs rotation matrices (eg for working with other libraries like the affine_transform functions in scipy).虽然旋转的特殊类可能很方便,但在某些情况下需要旋转矩阵(例如,用于与其他库一起工作,如 scipy 中的 affine_transform 函数)。 To avoid everyone implementing their own little matrix generating functions, there exists a tiny pure python package which does nothing more than providing convenient rotation matrix generating functions.为了避免每个人都实现自己的小矩阵生成函数,存在一个微小的纯 python package,它只是提供方便的旋转矩阵生成函数。 The package is on github ( mgen ) and can be installed via pip: package 在 github ( mgen ) 上,可以通过 pip 安装:

pip install mgen

Example usage copied from the 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.])

Note that the matrices are just regular numpy arrays, so no new data-structures are introduced when using this package.请注意,矩阵只是常规的 numpy arrays,因此在使用此 package 时不会引入新的数据结构。

I needed to rotate a 3D model around one of the three axes {x, y, z} in which that model was embedded and this was the top result for a search of how to do this in numpy. I used the following simple function:我需要围绕三个轴之一旋转 3D model,其中嵌入了 model,这是在 numpy 中搜索如何执行此操作的最佳结果。我使用了以下简单的 function:

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.],
  ]))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM