ZERO Notificatons

NO Feedback yet!!

okay

xparo
X.P.A.R.O



project - Quaternion ↔ Euler Converter



Converting between quaternions and roll, pitch, yaw is a few lines of code in any language, and a common source of bugs, because each library has its own conventions for axis order, component order and angle ranges. This guide gives working code for the libraries ROS developers actually use, all checked against each other on ROS 2 Jazzy, and points out the traps in each.

The reference values

Every example below converts roll 0.1, pitch 0.2 and yaw π/2 (radians) in the ROS convention, fixed axes X, then Y, then Z. All four libraries we tested produced the same quaternion:

(x, y, z, w) = (−0.035341, 0.105669, 0.699167, 0.706223)

Use these numbers to check your own code. The Quaternion ↔ Euler Converter gives the same result and writes these snippets for any angles you type.

Python: tf_transformations

The ROS 2 port of the classic ROS 1 tf.transformations module. Install it with sudo apt install ros-jazzy-tf-transformations (it uses the transforms3d library).

from tf_transformations import euler_from_quaternion, quaternion_from_euler

q = quaternion_from_euler(0.1, 0.2, 1.5707963)       # returns [x, y, z, w]
roll, pitch, yaw = euler_from_quaternion(q)          # takes [x, y, z, w]

The default axes argument, 'sxyz', means static (fixed) X-Y-Z: the ROS convention. Both functions use x, y, z, w order, matching ROS messages.

Python: SciPy

from scipy.spatial.transform import Rotation

q = Rotation.from_euler('xyz', [0.1, 0.2, 1.5707963]).as_quat()   # [x, y, z, w]
rpy = Rotation.from_quat(q).as_euler('xyz')                       # [roll, pitch, yaw]

Lowercase 'xyz' means fixed axes (the ROS convention); uppercase 'XYZ' means moving axes, a different convention that gives different answers. as_quat() returns x, y, z, w by default. Pass degrees=True to work in degrees.

Python: from and to ROS messages

from geometry_msgs.msg import Quaternion
from tf_transformations import euler_from_quaternion, quaternion_from_euler

x, y, z, w = quaternion_from_euler(0.0, 0.0, yaw)
msg = Quaternion(x=x, y=y, z=z, w=w)

roll, pitch, yaw = euler_from_quaternion([msg.x, msg.y, msg.z, msg.w])

C++: tf2

#include <tf2/LinearMath/Matrix3x3.h>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>

tf2::Quaternion q;
q.setRPY(0.1, 0.2, M_PI / 2);                 // roll, pitch, yaw
geometry_msgs::msg::Quaternion msg = tf2::toMsg(q);

tf2::Quaternion back;
tf2::fromMsg(msg, back);
double roll, pitch, yaw;
tf2::Matrix3x3(back).getRPY(roll, pitch, yaw);

Older tutorials include tf2_geometry_msgs.h; on ROS 2 Jazzy only the .hpp header exists. Add tf2 and tf2_geometry_msgs as dependencies in package.xml and CMakeLists.txt. getRPY returns pitch in [−π/2, π/2] and roll and yaw in [−π, π], which is usually what you want.

C++: Eigen

#include <Eigen/Geometry>

// yaw, then pitch, then roll about moving axes = the ROS fixed-axis convention
Eigen::Quaterniond q = Eigen::AngleAxisd(yaw,   Eigen::Vector3d::UnitZ())
                     * Eigen::AngleAxisd(pitch, Eigen::Vector3d::UnitY())
                     * Eigen::AngleAxisd(roll,  Eigen::Vector3d::UnitX());

Two traps:

  • Component order. The constructor Eigen::Quaterniond(w, x, y, z) takes w first, but q.coeffs() returns x, y, z, w. Use the named accessors q.x(), q.w() and so on when copying to a ROS message.
  • eulerAngles ranges. q.toRotationMatrix().eulerAngles(2, 1, 0) returns yaw, pitch, roll, but keeps the first angle in [0, π]. For an orientation built from yaw −1.0, pitch 0.2, roll 0.1, we got back yaw 2.14, pitch 2.94, roll −3.04: the same orientation, but not the numbers anyone expects. For roll, pitch, yaw in ROS ranges, convert to tf2::Quaternion and use getRPY, or compute the angles with the formulas below.

The formulas, for any language

From roll r, pitch p, yaw y: x = sin(r/2)cos(p/2)cos(y/2) − cos(r/2)sin(p/2)sin(y/2) y = cos(r/2)sin(p/2)cos(y/2) + sin(r/2)cos(p/2)sin(y/2) z = cos(r/2)cos(p/2)sin(y/2) − sin(r/2)sin(p/2)cos(y/2) w = cos(r/2)cos(p/2)cos(y/2) + sin(r/2)sin(p/2)sin(y/2) From a unit quaternion: roll = atan2(2(w·x + y·z), 1 − 2(x² + y²)) pitch = asin(clamp(2(w·y − z·x), −1, 1)) yaw = atan2(2(w·z + x·y), 1 − 2(y² + z²))

Clamp the asin argument: rounding can push it slightly past ±1 and produce NaN. Near pitch ±90°, roll and yaw are not unique, as explained in gimbal lock explained.

Testing your conversion

  • Check the reference values above.
  • Round-trip random angles (with pitch away from ±90°) and compare rotations, not raw numbers, since q and −q are equal.
  • Look at the result: the converter's 3D view, or RViz's TF display, catches sign errors that numbers hide.

More guides

Oct. 4, 2026, 9:40 a.m.
Static Transforms in ROS 2: Getting Sensor Frames Right
Read more..
Oct. 4, 2026, 9:42 a.m.
Gimbal Lock Explained with a Worked Example
Read more..
Oct. 4, 2026, 9:43 a.m.
Roll, Pitch, Yaw in ROS: Axis Conventions and Rotation Order (REP 103)
Read more..
Oct. 4, 2026, 9:44 a.m.
Quaternions for ROS Developers: What x, y, z, w Actually Mean
Read more..

If you have any query or problem
feel free to contact us
email: [email protected]