CARLA
Loading...
Searching...
No Matches
Rotation.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 <cmath>
10
11#include "carla/MsgPack.h"
12#include "carla/geom/Math.h"
14#include "carla/geom/Vector3D.h"
15
16#ifdef LIBCARLA_INCLUDED_FROM_UE4
18#include "Math/Rotator.h"
20#endif // LIBCARLA_INCLUDED_FROM_UE4
21
22namespace carla {
23namespace geom {
24
25 /// Euler angles of a rotation in **CARLA's / Unreal's left-handed frame**:
26 /// x forward, y right, z up, degrees.
27 ///
28 /// The three angles compose as
29 /// `R = Rz(+yaw) * Ry(-pitch) * Rx(-roll)` with the standard
30 /// (right-handed) elementary matrices, which is the same thing as saying:
31 ///
32 /// * `+yaw` turns the nose to the **right**;
33 /// * `+pitch` tilts the nose **up** -> `forward.z = +sin(pitch)`;
34 /// * `+roll` drops the **right** side -> `right.z = -cos(pitch)*sin(roll)`.
35 ///
36 /// This is CARLA 0.9.x's documented convention and it is what the engine
37 /// builds from the same `FRotator`, verified against rendered camera frames
38 /// on UE5.8. `RotateVector`, `Transform::GetMatrix` and
39 /// `Math::Get{Forward,Right,Up}Vector` all live in this frame and must not
40 /// be "corrected" towards a right-handed convention.
41 ///
42 /// For ROS / REP-103 consumers convert explicitly at the boundary with
43 /// `ToRightHanded()` / `FromRightHanded()`; see `RightHandedRotation` and
44 /// `Docs/coordinate_conventions.md`.
45 class Rotation {
46 public:
47
48 // =========================================================================
49 // -- Public data members --------------------------------------------------
50 // =========================================================================
51
52 float pitch = 0.0f;
53
54 float yaw = 0.0f;
55
56 float roll = 0.0f;
57
59
60 // =========================================================================
61 // -- Constructors ---------------------------------------------------------
62 // =========================================================================
63
64 Rotation() = default;
65
66 constexpr Rotation(float p, float y, float r)
67 : pitch(p),
68 yaw(y),
69 roll(r) {}
70
71 // =========================================================================
72 // -- Other methods --------------------------------------------------------
73 // =========================================================================
74
76 return Math::GetForwardVector(*this);
77 }
78
80 return Math::GetRightVector(*this);
81 }
82
84 return Math::GetUpVector(*this);
85 }
86
87 void RotateVector(Vector3D &in_point) const {
88 // Rotates Rz(yaw) * Ry(pitch) * Rx(roll) = first x, then y, then z,
89 // in Unreal's left-handed frame, matching what the engine does with
90 // the same FRotator and CARLA's documented 0.9.x semantics:
91 // +pitch tilts the forward axis UP -> z row has (+sp)
92 // +roll tilts the right axis DOWN -> z row has (-cp * sr)
93 // Empirically verified on the running UE5.8 server (2026-08-27): a
94 // camera spawned at Rotation(pitch=+20) fills the frame with sky and
95 // at Rotation(roll=+25) shows more ground on the right-hand side.
96 // #9751 briefly mirrored the pitch/roll signs here; that made this
97 // matrix disagree with the engine, with the ROS 2 bridge's own
98 // `ros2::TransformFromCarlaRotation`, and with every 0.9.x consumer.
99 const float cy = std::cos(Math::ToRadians(yaw));
100 const float sy = std::sin(Math::ToRadians(yaw));
101 const float cr = std::cos(Math::ToRadians(roll));
102 const float sr = std::sin(Math::ToRadians(roll));
103 const float cp = std::cos(Math::ToRadians(pitch));
104 const float sp = std::sin(Math::ToRadians(pitch));
105
106 Vector3D out_point;
107 out_point.x =
108 in_point.x * (cp * cy) +
109 in_point.y * (cy * sp * sr - sy * cr) +
110 in_point.z * (-cy * sp * cr - sy * sr);
111
112 out_point.y =
113 in_point.x * (cp * sy) +
114 in_point.y * (sy * sp * sr + cy * cr) +
115 in_point.z * (-sy * sp * cr + cy * sr);
116
117 out_point.z =
118 in_point.x * (sp) +
119 in_point.y * (-cp * sr) +
120 in_point.z * (cp * cr);
121
122 in_point = out_point;
123 }
124
125 Vector3D RotateVector(const Vector3D& in_point) const {
126 Vector3D out_point = in_point;
127 RotateVector(out_point);
128 return out_point;
129 }
130
131 void InverseRotateVector(Vector3D &in_point) const {
132 // Applies the transposed of the matrix used in RotateVector function,
133 // which is the rotation inverse.
134 const float cy = std::cos(Math::ToRadians(yaw));
135 const float sy = std::sin(Math::ToRadians(yaw));
136 const float cr = std::cos(Math::ToRadians(roll));
137 const float sr = std::sin(Math::ToRadians(roll));
138 const float cp = std::cos(Math::ToRadians(pitch));
139 const float sp = std::sin(Math::ToRadians(pitch));
140
141 Vector3D out_point;
142 out_point.x =
143 in_point.x * (cp * cy) +
144 in_point.y * (cp * sy) +
145 in_point.z * (sp);
146
147 out_point.y =
148 in_point.x * (cy * sp * sr - sy * cr) +
149 in_point.y * (sy * sp * sr + cy * cr) +
150 in_point.z * (-cp * sr);
151
152 out_point.z =
153 in_point.x * (-cy * sp * cr - sy * sr) +
154 in_point.y * (-sy * sp * cr + cy * sr) +
155 in_point.z * (cp * cr);
156
157 in_point = out_point;
158 }
159
160 // =========================================================================
161 // -- UE4 methods -------------------------------------------------
162 // =========================================================================
163
165 Rotation V0 = Rotation(*this);
166
167 V0.pitch = V0.pitch - (360.0f * trunc(V0.pitch/360.0f));
168 V0.yaw = V0.yaw - (360.0f * trunc(V0.yaw/360.0f));
169 V0.roll = V0.roll - (360.0f * trunc(V0.roll/360.0f));
170
171 if( V0.pitch < 0.0f ){
172 V0.pitch += 360.0f;
173 }
174 if( V0.yaw < 0.0f ){
175 V0.yaw += 360.0f;
176 }
177 if( V0.roll < 0.0f ){
178 V0.roll += 360.0f;
179 }
180
181 if( V0.pitch >= 180.0f ){
182 V0.pitch -= 360.0f;
183 }
184 if( V0.yaw >= 180.0f ){
185 V0.yaw -= 360.0f;
186 }
187 if( V0.roll >= 180.0f ){
188 V0.roll -= 360.0f;
189 }
190
191 return V0;
192 }
193
194 // =========================================================================
195 // -- Right-handed (ROS / FLU) boundary ------------------------------------
196 // =========================================================================
197
198 /// This rotation expressed in a right-handed, x-forward / y-**left** /
199 /// z-up frame (ROS REP-103 "FLU"), as intrinsic Z-Y-X Euler angles under
200 /// the right-hand rule.
201 ///
202 /// Mirroring the Y axis maps `Rz(+yaw) * Ry(-pitch) * Rx(-roll)` onto
203 /// `Rz(-yaw) * Ry(+(-pitch)) * Rx(+roll)`, so pitch and yaw flip sign and
204 /// roll does not. Example: `Rotation(pitch=20, yaw=30, roll=10)` becomes
205 /// `RightHandedRotation(roll=10, pitch=-20, yaw=-30)`.
207 return RightHandedRotation(roll, -pitch, -yaw);
208 }
209
210 /// Inverse of `ToRightHanded()`. The mapping is an involution, so the same
211 /// three sign flips apply in both directions.
212 static constexpr Rotation FromRightHanded(const RightHandedRotation &rhs) {
213 return Rotation(-rhs.pitch, -rhs.yaw, rhs.roll);
214 }
215
216 // =========================================================================
217 // -- Comparison operators -------------------------------------------------
218 // =========================================================================
219
220 bool operator==(const Rotation &rhs) const {
221 return ((pitch == rhs.pitch) && (yaw == rhs.yaw) && (roll == rhs.roll)) ||
222 // When comparing two normalized rotators the sum of both comps can be 180 according to UE, we are following same structure for rotators
223 ( ( abs(pitch) + abs(rhs.pitch) == 180.0f) && ( abs(yaw) + abs(rhs.yaw) == 180.0f) && ( abs(roll) + abs(rhs.roll) == 180.0f) );
224 }
225
226 bool operator!=(const Rotation &rhs) const {
227 return !(*this == rhs);
228 }
229
230 // =========================================================================
231 // -- Aritmetic operators -------------------------------------------------
232 // =========================================================================
233
234 Rotation operator+(const Rotation &rhs) const {
235 Rotation Result;
236 Result.pitch = pitch + rhs.pitch;
237 Result.yaw = yaw + rhs.yaw;
238 Result.roll = roll + rhs.roll;
239 return Result;
240 }
241
242 Rotation operator-(const Rotation &rhs) const {
243 Rotation Result;
244 Result.pitch = pitch - rhs.pitch;
245 Result.yaw = yaw - rhs.yaw;
246 Result.roll = roll - rhs.roll;
247 return Result;
248 }
249
251 pitch += rhs.pitch;
252 yaw += rhs.yaw;
253 roll += rhs.roll;
254 return *this;
255 }
256
258 pitch -= rhs.pitch;
259 yaw -= rhs.yaw;
260 roll -= rhs.roll;
261 return *this;
262 }
263
264
265 // =========================================================================
266 // -- Conversions to UE4 types ---------------------------------------------
267 // =========================================================================
268
269#ifdef LIBCARLA_INCLUDED_FROM_UE4
270
271 Rotation(const FRotator &rotator)
272 : Rotation(rotator.Pitch, rotator.Yaw, rotator.Roll) {}
273
274 operator FRotator() const {
275 return FRotator{pitch, yaw, roll};
276 }
277
278#endif // LIBCARLA_INCLUDED_FROM_UE4
279 };
280
281} // namespace geom
282} // namespace carla
float y
static Vector3D GetRightVector(const Rotation &rotation)
Compute the unit vector pointing towards the Y-axis of rotation.
Definition Math.cpp:128
static constexpr T ToRadians(T deg)
Definition Math.h:43
static Vector3D GetUpVector(const Rotation &rotation)
Compute the unit vector pointing towards the Y-axis of rotation.
Definition Math.cpp:144
static Vector3D GetForwardVector(const Rotation &rotation)
Compute the unit vector pointing towards the X-axis of rotation.
Definition Math.cpp:117
Euler angles of a rotation in CARLA's / Unreal's left-handed frame: x forward, y right,...
Definition Rotation.h:45
Vector3D RotateVector(const Vector3D &in_point) const
Definition Rotation.h:125
constexpr Rotation(float p, float y, float r)
Definition Rotation.h:66
constexpr RightHandedRotation ToRightHanded() const
This rotation expressed in a right-handed, x-forward / y-**left** / z-up frame (ROS REP-103 "FLU"),...
Definition Rotation.h:206
Rotation & operator+=(const Rotation &rhs)
Definition Rotation.h:250
static constexpr Rotation FromRightHanded(const RightHandedRotation &rhs)
Inverse of ToRightHanded().
Definition Rotation.h:212
Rotation Normalize() const
Definition Rotation.h:164
Rotation(const FRotator &rotator)
Definition Rotation.h:271
MSGPACK_DEFINE_ARRAY(pitch, yaw, roll)
bool operator==(const Rotation &rhs) const
Definition Rotation.h:220
Vector3D GetUpVector() const
Definition Rotation.h:83
Vector3D GetRightVector() const
Definition Rotation.h:79
Rotation operator-(const Rotation &rhs) const
Definition Rotation.h:242
Rotation & operator-=(const Rotation &rhs)
Definition Rotation.h:257
void InverseRotateVector(Vector3D &in_point) const
Definition Rotation.h:131
bool operator!=(const Rotation &rhs) const
Definition Rotation.h:226
void RotateVector(Vector3D &in_point) const
Definition Rotation.h:87
Rotation operator+(const Rotation &rhs) const
Definition Rotation.h:234
Vector3D GetForwardVector() const
Definition Rotation.h:75
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.