web-dev-qa-db-fra.com

Rotation du vecteur 3D?

J'ai deux vecteurs comme Python et un angle. E.g .:

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

Quelle est la meilleure façon/la plus simple d’obtenir le vecteur résultant lors de la rotation du vecteur v autour de l’axe?

La rotation devrait apparaître dans le sens contraire des aiguilles d'une montre pour un observateur vers lequel le vecteur d'axe est dirigé. C'est ce qu'on appelle le règle de la main droite

61
Mads Skjern

Jetez un oeil à http://vpython.org/contents/docs/visual/VisualIntro.html .

Il fournit une classe vector qui a une méthode A.rotate(theta,B). Il fournit également une fonction d'assistance rotate(A,theta,B) si vous ne souhaitez pas appeler la méthode sur A.

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

12
agf

En utilisant le formule d'Euler-Rodrigues :

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

Une ligne, avec des fonctions numpy/scipy.

Nous utilisons les éléments suivants:

soit a le vecteur unitaire le long de axe , c’est-à-dire a = axe/norme (axe)
et A = I × a soit la matrice asymétrique associée à a , c'est-à-dire le produit croisé de la matrice d'identité avec a

alors M = exp (θ A) est la matrice de rotation.

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) calcule la série taylor de l'exponentielle:
\sum_{k=0}^{20} \frac{1}{k!} (θ A)^k, il est donc coûteux en temps, mais lisible et sécurisé. Ce peut être un bon moyen si vous avez peu de rotations à faire mais beaucoup de vecteurs.

44
B. M.

Je voulais juste mentionner que si la vitesse est requise, en enveloppant le code de unutbu dans weave.inline de scipy et en passant une matrice déjà existante en tant que paramètre, le temps d'exécution sera 20 fois plus court.

Le code (dans 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]])

Le 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]
20
juniper-

Voici une méthode élégante utilisant des quaternions extrêmement rapides. Je peux calculer 10 millions de rotations par seconde avec des tableaux numpy correctement vectorisés. Il s’appuie sur l’extension quaternion de numpy trouvée ici .

Quaternion Theory: Un quaternion est un nombre avec une dimension réelle et 3 dimensions imaginaires, généralement écrit sous la forme q = w + xi + yj + zk, Où 'i', 'j', 'k' sont des dimensions imaginaires. Tout comme un nombre unitaire complexe 'c' peut représenter toutes les rotations en 2D par c=exp(i * theta), un quaternion unitaire 'q' peut représenter toutes les rotations en 3D par q=exp(p), où 'p' est pur quaternion imaginaire défini par votre axe et angle.

Nous commençons par convertir votre axe et votre angle en un quaternion dont les dimensions imaginaires sont données par votre axe de rotation et dont la grandeur est donnée par la moitié de l’angle de rotation en radians. Les 4 éléments vecteurs (w, x, y, z) Sont construits comme suit:

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)

Tout d'abord, un tableau numpy de 4 éléments est construit avec le composant réel w = 0 pour le vecteur à faire pivoter vector et l'axe de rotation rot_axis. La représentation de l'angle des axes est ensuite construite en normalisant puis en multipliant par la moitié de l'angle souhaité theta. Voir ici pour savoir pourquoi la moitié de l'angle est requise.

Créez maintenant les quaternions v et qlog à l'aide de la bibliothèque, puis récupérez le quaternion à rotation d'unité q en prenant l'exponentielle.

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

Enfin, la rotation du vecteur est calculée par l'opération suivante.

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

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

Maintenant, supprimez le véritable élément et vous avez votre vecteur pivoté!

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

Notez que cette méthode est particulièrement efficace si vous devez faire pivoter un vecteur de nombreuses rotations séquentielles, car le produit quaternion peut simplement être calculé comme suit: q = q1 * q2 * q3 * q4 * ... * qn et le vecteur ne fait alors que pivoter. par 'q' à la toute fin en utilisant v '= q * v * conj (q).

Cette méthode vous donne une transformation transparente entre les angles d'axe <---> de l'opérateur de rotation 3D simplement par exp et log fonctions (yes log(q)) renvoie simplement la représentation de l'axe-angle !) Pour plus de précisions sur le fonctionnement de la multiplication de quaternion, etc., voir ici

14
henneray

J'ai réalisé une bibliothèque assez complète de mathématiques 3D pour Python {2,3}. Il n'utilise toujours pas Cython, mais s'appuie fortement sur l'efficacité de numpy. Vous pouvez le trouver ici avec pip:

python[3] -m pip install math3d

Ou jetez un oeil à mon gitweb http://git.automatics.dyndns.dk/?p=pymath3d.git et maintenant aussi sur github: https://github.com/ mortlind/pymath3d .

Une fois installé, dans python, vous pouvez créer un objet d’orientation pouvant faire pivoter des vecteurs ou faire partie d’un objet de transformation. Par exemple, l’extrait de code suivant compose une orientation représentant une rotation de 1 rad autour de l’axe [1,2,3], l'applique au vecteur [4,5,6] et affiche le résultat:

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

La sortie serait

<Vector: (2.53727, 6.15234, 5.71935)>

C’est plus efficace, dans la mesure du possible, d’un facteur environ quatre fois supérieur à celui de l’élément à l’aide de Scipy posté par B. M. ci-dessus. Cependant, cela nécessite l'installation de mon paquet math3d.

6
Morten Lind

Disclaimer: Je suis l'auteur de ce paquet

Des classes spéciales pour les rotations peuvent être pratiques, mais dans certains cas, vous avez besoin de matrices de rotation (par exemple, pour travailler avec d'autres bibliothèques telles que les fonctions affine_transform de scipy). Pour éviter que chacun n’implémente ses propres petites fonctions génératrices de matrice, il existe un minuscule paquet python) qui ne fournit rien de plus que de fournir des fonctions pratiques de génération de matrice de rotation. Le paquet est sur github ( mgen ) et peut être installé via pip:

pip install mgen

Exemple d'utilisation copié du fichier Lisez-moi:

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

Notez que les matrices ne sont que des tableaux numpy ordinaires. Par conséquent, aucune nouvelle structure de données n'est introduite lors de l'utilisation de ce paquet.

2
NOhs

Il peut également être résolu en utilisant la théorie des quaternions:

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]
1
Guillaume Mougeot

Utiliser pyquaternion est extrêmement simple. pour l'installer (toujours en python), lancez dans votre console:

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

Une fois installé:

  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)
1
Dr.PP

J'avais besoin de faire pivoter un modèle 3D autour de l'un des trois axes {x, y, z} dans lesquels ce modèle était intégré. Il s'agissait du meilleur résultat pour une recherche de la manière de le faire numpy. J'ai utilisé la fonction simple suivante:

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

Utilisez le Rotation.from_rotvec() de scipy. L'argument est le vecteur de rotation (un vecteur unitaire) multiplié par l'angle de rotation en 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]

Il existe plusieurs façons d'utiliser Rotation en fonction des données dont vous disposez sur la rotation:

  • from_quat Initialisé à partir de quaternions.

  • from_dcm Initialisé à partir des matrices de cosinus de direction.

  • from_euler Initialisé sous un angle d'Euler.


Remarque hors sujet: Le code à une ligne est pas nécessairement un meilleur code, comme l'impliquent certains utilisateurs.

0
Fermi paradox