Fondamentaux du quaternion

Objectif : Apprendre les bases de l’utilisation des quaternions dans ROS 2.

Niveau du didacticiel : Intermédiaire

Durée : 10 minutes

Arrière-plan

Un quaternion est une représentation à 4 tuples de l’orientation, qui est plus concise qu’une matrice de rotation. Les quaternions sont très efficaces pour analyser des situations où des rotations en trois dimensions sont impliquées. Les quaternions sont largement utilisés dans la robotique, la mécanique quantique, la vision par ordinateur et l’animation 3D.

Vous pouvez en savoir plus sur le concept mathématique sous-jacent sur Wikipedia. Vous pouvez également jeter un œil à une série de vidéos explorables Visualizing quaternions réalisées par 3blue1brown.

Dans ce didacticiel, vous apprendrez comment les quaternions et les méthodes de conversion fonctionnent dans ROS 2.

Conditions préalables

Cependant, ce n’est pas une exigence stricte et vous pouvez vous en tenir à toute autre bibliothèque de transformation géométrique qui vous convient le mieux. Vous pouvez jeter un œil à des bibliothèques comme transforms3d, scipy.spatial.transform, pytransform3d, numpy-quaternion ou blender.mathutils.

Composants d’un quaternion

ROS 2 utilise des quaternions pour suivre et appliquer des rotations. Un quaternion a 4 composantes (x, y, z, w). Dans ROS 2, w est en dernier, mais dans certaines bibliothèques comme Eigen, w peut être placé en première position. Le quaternion unitaire couramment utilisé qui ne produit aucune rotation autour des axes x/y/z est (0, 0, 0, 1), et peut être créé de la manière suivante :

#include <tf2/LinearMath/Quaternion.h>
...

tf2::Quaternion q;
// Create a quaternion from roll/pitch/yaw in radians (0, 0, 0)
q.setRPY(0, 0, 0);
// Print the quaternion components (0, 0, 0, 1)
RCLCPP_INFO(this->get_logger(), "%f %f %f %f",
            q.x(), q.y(), q.z(), q.w());

La magnitude d’un quaternion doit toujours être un. Si des erreurs numériques provoquent une magnitude de quaternion autre que un, ROS 2 imprimera des avertissements. Pour éviter ces avertissements, normalisez le quaternion :

q.normalize();

Types de quaternions dans ROS 2

ROS 2 utilise deux types de données quaternions : tf2::Quaternion et son équivalent geometry_msgs::msg::Quaternion. Pour convertir entre eux en C++, utilisez les méthodes de tf2_geometry_msgs.

C++

#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
...

tf2::Quaternion tf2_quat, tf2_quat_from_msg;
tf2_quat.setRPY(roll, pitch, yaw);
// Convert tf2::Quaternion to geometry_msgs::msg::Quaternion
geometry_msgs::msg::Quaternion msg_quat = tf2::toMsg(tf2_quat);

// Convert geometry_msgs::msg::Quaternion to tf2::Quaternion
tf2::convert(msg_quat, tf2_quat_from_msg);
// or
tf2::fromMsg(msg_quat, tf2_quat_from_msg);

Python

from geometry_msgs.msg import Quaternion
...

# Create a list of floats, which is compatible with tf2
# Quaternion methods
quat_tf = [0.0, 1.0, 0.0, 0.0]

# Convert a list to geometry_msgs.msg.Quaternion
msg_quat = Quaternion(x=quat_tf[0], y=quat_tf[1], z=quat_tf[2], w=quat_tf[3])

Opérations de quaternions

1 Pensez en RPY puis convertissez en quaternion

Il est facile pour nous de penser à des rotations autour d’axes, mais difficile de penser en termes de quaternions. Une suggestion est de calculer les rotations cibles en termes de roulis (autour d’un axe X), de tangage (autour de l’axe Y) et de lacet (autour de l’axe Z), puis de les convertir en un quaternion.

# quaternion_from_euler method is available in turtle_tf2_py/turtle_tf2_py/turtle_tf2_broadcaster.py
q = quaternion_from_euler(1.5707, 0, -1.5707)
print(f'The quaternion representation is x: {q[0]} y: {q[1]} z: {q[2]} w: {q[3]}.')

2 Appliquer une rotation de quaternion

Pour appliquer la rotation d’un quaternion à une pose, il suffit de multiplier le quaternion précédent de la pose par le quaternion représentant la rotation souhaitée. L’ordre de cette multiplication est important.

C++

#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
...

tf2::Quaternion q_orig, q_rot, q_new;

q_orig.setRPY(0.0, 0.0, 0.0);
// Rotate the previous pose by 180* about X
q_rot.setRPY(3.14159, 0.0, 0.0);
q_new = q_rot * q_orig;
q_new.normalize();

Python

q_orig = quaternion_from_euler(0, 0, 0)
# Rotate the previous pose by 180* about X
q_rot = quaternion_from_euler(3.14159, 0, 0)
q_new = quaternion_multiply(q_rot, q_orig)

3 Inversion d’un quaternion

Un moyen simple d’inverser un quaternion est de nier la composante w :

q[3] = -q[3]

4 Rotations relatives

Supposons que vous ayez deux quaternions du même cadre, q_1 et q_2. Vous voulez trouver la rotation relative, q_r, qui convertit q_1 en q_2 de la manière suivante :

q_2 = q_r * q_1

Vous pouvez résoudre pour q r similaire à la résolution d’une équation matricielle. Inverse q_ 1 et multiplie à droite les deux côtés. Là encore, l’ordre de multiplication est important :

q_r = q_2 * q_1_inverse

Voici un exemple pour obtenir la rotation relative de la pose précédente du robot à la pose actuelle du robot en python :

def quaternion_multiply(q0, q1):
    """
    Multiplies two quaternions.

    Input
    :param q0: A 4 element array containing the first quaternion (q01, q11, q21, q31)
    :param q1: A 4 element array containing the second quaternion (q02, q12, q22, q32)

    Output
    :return: A 4 element array containing the final quaternion (q03,q13,q23,q33)

    """
    # Extract the values from q0
    w0 = q0[0]
    x0 = q0[1]
    y0 = q0[2]
    z0 = q0[3]

    # Extract the values from q1
    w1 = q1[0]
    x1 = q1[1]
    y1 = q1[2]
    z1 = q1[3]

    # Computer the product of the two quaternions, term by term
    q0q1_w = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1
    q0q1_x = w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1
    q0q1_y = w0 * y1 - x0 * z1 + y0 * w1 + z0 * x1
    q0q1_z = w0 * z1 + x0 * y1 - y0 * x1 + z0 * w1

    # Create a 4 element array containing the final quaternion
    final_quaternion = np.array([q0q1_w, q0q1_x, q0q1_y, q0q1_z])

    # Return a 4 element array containing the final quaternion (q02,q12,q22,q32)
    return final_quaternion

q1_inv[0] = prev_pose.pose.orientation.x
q1_inv[1] = prev_pose.pose.orientation.y
q1_inv[2] = prev_pose.pose.orientation.z
q1_inv[3] = -prev_pose.pose.orientation.w # Negate for inverse

q2[0] = current_pose.pose.orientation.x
q2[1] = current_pose.pose.orientation.y
q2[2] = current_pose.pose.orientation.z
q2[3] = current_pose.pose.orientation.w

qr = quaternion_multiply(q2, q1_inv)

Résumé

Dans ce didacticiel, vous avez découvert les concepts fondamentaux d’un quaternion et ses opérations mathématiques associées, telles que l’inversion et la rotation. Vous avez également découvert ses exemples d’utilisation dans ROS 2 et les méthodes de conversion entre deux classes Quaternion distinctes.