CARLA
Loading...
Searching...
No Matches
geom/Quaternion.h
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7#pragma once
8
9#include "carla/MsgPack.h"
10#include "carla/geom/Math.h"
13#include "carla/geom/Rotation.h"
14#include "carla/geom/Vector3D.h"
15
16#include <cmath>
17
18namespace carla {
19namespace geom {
20
21 /// Quaternion representing a 3D rotation, stored as `(x, y, z, w)`.
22 ///
23 /// **Frame: right-handed**, x forward / y **left** / z up (ROS REP-103
24 /// "FLU"). It is the quaternion of `RightHandedRotation`, not of CARLA's
25 /// left-handed `Rotation`, and it is bit-for-bit the one the ROS 2 bridge
26 /// publishes (`ros2::TransformFromCarlaRotation` in
27 /// `carla/ros2/publishers/TransformQuaternion.h`).
28 ///
29 /// The `Quaternion(const Rotation &)` constructor therefore performs a
30 /// handedness change: it routes through `Rotation::ToRightHanded()`, which
31 /// negates **yaw and pitch** and leaves roll alone. (The #9751 commit
32 /// message says "yaw and roll"; that is wrong -- see
33 /// `Docs/coordinate_conventions.md`.) With those signs the basis vectors of
34 /// `Quaternion(r)` -- which convert back to CARLA's frame on the way out --
35 /// reproduce, column for column, the matrix of `Rotation::RotateVector` and
36 /// `Transform::GetMatrix`.
37 class Quaternion {
38 public:
39
40 // =========================================================================
41 // -- Public data members --------------------------------------------------
42 // =========================================================================
43
44 float x = 0.0f;
45 float y = 0.0f;
46 float z = 0.0f;
47 float w = 1.0f;
48
50
51 // =========================================================================
52 // -- Constructors ---------------------------------------------------------
53 // =========================================================================
54
55 Quaternion() = default;
56
57 constexpr Quaternion(float ix, float iy, float iz, float iw)
58 : x(ix),
59 y(iy),
60 z(iz),
61 w(iw) {
62 }
63
64 /// Quaternion of a right-handed (FLU) Euler triple, degrees, intrinsic
65 /// Z-Y-X: `q = qz(yaw) * qy(pitch) * qx(roll)`. This is the textbook
66 /// right-handed roll-pitch-yaw formula -- no sign tricks live here.
67 explicit Quaternion(const RightHandedRotation &rotation) {
68 const float half_yaw_rad =
69 static_cast<float>(Math::ToRadians<double>(rotation.yaw)) * 0.5f;
70 const float half_pitch_rad =
71 static_cast<float>(Math::ToRadians<double>(rotation.pitch)) * 0.5f;
72 const float half_roll_rad =
73 static_cast<float>(Math::ToRadians<double>(rotation.roll)) * 0.5f;
74
75 const float cy = std::cos(half_yaw_rad);
76 const float sy = std::sin(half_yaw_rad);
77 const float cp = std::cos(half_pitch_rad);
78 const float sp = std::sin(half_pitch_rad);
79 const float cr = std::cos(half_roll_rad);
80 const float sr = std::sin(half_roll_rad);
81
82 w = cr * cp * cy + sr * sp * sy;
83 x = sr * cp * cy - cr * sp * sy;
84 y = cr * sp * cy + sr * cp * sy;
85 z = cr * cp * sy - sr * sp * cy;
86 }
87
88 /// Quaternion of a CARLA (left-handed) Euler rotation, degrees.
89 ///
90 /// The handedness change happens exactly once, in
91 /// `Rotation::ToRightHanded()`: **yaw and pitch are negated, roll is
92 /// not**. The resulting `(x, y, z, w)` is in the right-handed FLU frame
93 /// and is the value the ROS 2 bridge publishes.
94 explicit Quaternion(const Rotation &rotation)
95 : Quaternion(rotation.ToRightHanded()) {
96 }
97
98 // =========================================================================
99 // -- Other methods --------------------------------------------------------
100 // =========================================================================
101
102 /// This rotation as right-handed (FLU) Euler angles, degrees, intrinsic
103 /// Z-Y-X. Inverse of `Quaternion(const RightHandedRotation &)`.
105 const float sin_pitch = 2.0f * (w * y - z * x);
106 const float clamped_sin_pitch =
107 (sin_pitch > 1.0f) ? 1.0f : (sin_pitch < -1.0f) ? -1.0f : sin_pitch;
108 const float pitch_rad = std::asin(clamped_sin_pitch);
109
110 const float roll_rad = std::atan2(
111 2.0f * (w * x + y * z),
112 1.0f - 2.0f * (x * x + y * y));
113 const float yaw_rad = std::atan2(
114 2.0f * (w * z + x * y),
115 1.0f - 2.0f * (y * y + z * z));
116
117 return RightHandedRotation(
118 static_cast<float>(Math::ToDegrees<double>(roll_rad)),
119 static_cast<float>(Math::ToDegrees<double>(pitch_rad)),
120 static_cast<float>(Math::ToDegrees<double>(yaw_rad)));
121 }
122
123 /// Convert this quaternion back to a CARLA (left-handed) Euler rotation.
124 /// Inverse of the `Quaternion(Rotation)` constructor; the handedness
125 /// change is undone once, by `Rotation::FromRightHanded()`.
127 const float sin_pitch = 2.0f * (w * y - z * x);
128 const float clamped_sin_pitch =
129 (sin_pitch > 1.0f) ? 1.0f : (sin_pitch < -1.0f) ? -1.0f : sin_pitch;
130 const float pitch_rad = std::asin(clamped_sin_pitch);
131
132 const float roll_rad = std::atan2(
133 2.0f * (w * x + y * z),
134 1.0f - 2.0f * (x * x + y * y));
135 const float yaw_rad = std::atan2(
136 2.0f * (w * z + x * y),
137 1.0f - 2.0f * (y * y + z * z));
138
140 static_cast<float>(Math::ToDegrees<double>(roll_rad)),
141 static_cast<float>(Math::ToDegrees<double>(pitch_rad)),
142 static_cast<float>(Math::ToDegrees<double>(yaw_rad))));
143 }
144
145 /// Rotate a right-handed vector by this quaternion. Computes
146 /// `q * v_pure * q_conjugate` via the Rodrigues form
147 /// `v + 2w*(q.xyz x v) + 2*(q.xyz x (q.xyz x v))`.
149 const float tx = 2.0f * (y * in.z - z * in.y);
150 const float ty = 2.0f * (z * in.x - x * in.z);
151 const float tz = 2.0f * (x * in.y - y * in.x);
152
153 const float cx = y * tz - z * ty;
154 const float cy = z * tx - x * tz;
155 const float cz = x * ty - y * tx;
156
157 return RightHandedVector3D(
158 in.x + w * tx + cx,
159 in.y + w * ty + cy,
160 in.z + w * tz + cz);
161 }
162
163 /// Inverse rotation of a right-handed vector. For a unit quaternion this
164 /// is equivalent to rotating by the conjugate.
168
169 /// Unit vector pointing toward the local X axis of this rotation,
170 /// expressed in CARLA's left-handed frame. The canonical axes are
171 /// passed through the `Vector3D -> RightHandedVector3D` boundary
172 /// conversion so the Y-sign flip happens on both ends of the rotation.
174 return static_cast<Vector3D>(RotatedVector(Vector3D(1.0f, 0.0f, 0.0f)));
175 }
176
177 /// Unit vector pointing toward the local Y axis of this rotation,
178 /// expressed in CARLA's left-handed frame.
180 return static_cast<Vector3D>(RotatedVector(Vector3D(0.0f, 1.0f, 0.0f)));
181 }
182
183 /// Unit vector pointing toward the local Z axis of this rotation,
184 /// expressed in CARLA's left-handed frame.
186 return static_cast<Vector3D>(RotatedVector(Vector3D(0.0f, 0.0f, 1.0f)));
187 }
188
189 /// Conjugate of this quaternion. For a unit quaternion the conjugate is
190 /// also its inverse.
192 return Quaternion(-x, -y, -z, w);
193 }
194
195 /// Inverse of this quaternion (assumes non-zero norm).
197 const float n2 = x * x + y * y + z * z + w * w;
198 if (n2 <= 0.0f) {
199 return Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
200 }
201 const float k = 1.0f / n2;
202 return Quaternion(-x * k, -y * k, -z * k, w * k);
203 }
204
205 float Length() const {
206 return std::sqrt(x * x + y * y + z * z + w * w);
207 }
208
210 const float length = Length();
211 if (length <= 0.0f) {
212 return Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
213 }
214 const float k = 1.0f / length;
215 return Quaternion(x * k, y * k, z * k, w * k);
216 }
217
218 /// Hamilton product `(*this) * rhs`.
219 Quaternion operator*(const Quaternion &rhs) const {
220 return Quaternion(
221 w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y,
222 w * rhs.y - x * rhs.z + y * rhs.w + z * rhs.x,
223 w * rhs.z + x * rhs.y - y * rhs.x + z * rhs.w,
224 w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z);
225 }
226
227 // =========================================================================
228 // -- Comparison operators -------------------------------------------------
229 // =========================================================================
230
231 bool operator==(const Quaternion &rhs) const {
232 return (x == rhs.x) && (y == rhs.y) && (z == rhs.z) && (w == rhs.w);
233 }
234
235 bool operator!=(const Quaternion &rhs) const {
236 return !(*this == rhs);
237 }
238 };
239
240} // namespace geom
241} // namespace carla
Quaternion representing a 3D rotation, stored as (x, y, z, w).
bool operator!=(const Quaternion &rhs) const
bool operator==(const Quaternion &rhs) const
Quaternion Conjugate() const
Conjugate of this quaternion.
Vector3D GetRightVector() const
Unit vector pointing toward the local Y axis of this rotation, expressed in CARLA's left-handed frame...
Rotation Rotator() const
Convert this quaternion back to a CARLA (left-handed) Euler rotation.
RightHandedRotation ToRightHandedRotation() const
This rotation as right-handed (FLU) Euler angles, degrees, intrinsic Z-Y-X.
Quaternion UnitQuaternion() const
constexpr Quaternion(float ix, float iy, float iz, float iw)
Quaternion(const RightHandedRotation &rotation)
Quaternion of a right-handed (FLU) Euler triple, degrees, intrinsic Z-Y-X: q = qz(yaw) * qy(pitch) * ...
Vector3D GetForwardVector() const
Unit vector pointing toward the local X axis of this rotation, expressed in CARLA's left-handed frame...
RightHandedVector3D InverseRotatedVector(const RightHandedVector3D &in) const
Inverse rotation of a right-handed vector.
Quaternion operator*(const Quaternion &rhs) const
Hamilton product (*this) * rhs.
RightHandedVector3D RotatedVector(const RightHandedVector3D &in) const
Rotate a right-handed vector by this quaternion.
MSGPACK_DEFINE_ARRAY(x, y, z, w)
Quaternion Inverse() const
Inverse of this quaternion (assumes non-zero norm).
Vector3D GetUpVector() const
Unit vector pointing toward the local Z axis of this rotation, expressed in CARLA's left-handed frame...
Quaternion(const Rotation &rotation)
Quaternion of a CARLA (left-handed) Euler rotation, degrees.
Euler angles of a rotation in CARLA's / Unreal's left-handed frame: x forward, y right,...
Definition Rotation.h:45
static constexpr Rotation FromRightHanded(const RightHandedRotation &rhs)
Inverse of ToRightHanded().
Definition Rotation.h:212
This file contains definitions of common data structures used in traffic manager.
Euler angles of a rotation expressed in a right-handed frame, in degrees.
float yaw
Rotation about the up (+Z) axis, degrees, right-hand rule.
float roll
Rotation about the forward (+X) axis, degrees, right-hand rule.
float pitch
Rotation about the left (+Y) axis, degrees, right-hand rule.
Internal adapter that holds a Vector3D in a right-handed coordinate frame.