CARLA
Loading...
Searching...
No Matches
test_transform_quaternion.cpp
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#include "test.h"
8
10
11#include <cmath>
12
13namespace {
14constexpr float kEps = 1e-5f;
15constexpr float kSqrtHalf = 0.70710678118654752440f; // sqrt(2)/2
16
17void ExpectNearArray3(
18 const std::array<float, 3> &actual,
19 const std::array<float, 3> &expected,
20 float eps = kEps) {
21 EXPECT_NEAR(actual[0], expected[0], eps);
22 EXPECT_NEAR(actual[1], expected[1], eps);
23 EXPECT_NEAR(actual[2], expected[2], eps);
24}
25
26void ExpectNearArray4(
27 const std::array<float, 4> &actual,
28 const std::array<float, 4> &expected,
29 float eps = kEps) {
30 EXPECT_NEAR(actual[0], expected[0], eps);
31 EXPECT_NEAR(actual[1], expected[1], eps);
32 EXPECT_NEAR(actual[2], expected[2], eps);
33 EXPECT_NEAR(actual[3], expected[3], eps);
34}
35
36} // namespace
37
38TEST(TransformQuaternion, identity_rotation_is_w_one) {
40 0.0f, 0.0f, 0.0f,
41 0.0f, 0.0f, 0.0f);
42 ExpectNearArray3(tf.translation, {0.0f, 0.0f, 0.0f});
43 ExpectNearArray4(tf.rotation, {1.0f, 0.0f, 0.0f, 0.0f});
44}
45
46TEST(TransformQuaternion, translation_y_axis_is_mirrored) {
48 3.0f, 4.0f, 5.0f,
49 0.0f, 0.0f, 0.0f);
50 ExpectNearArray3(tf.translation, {3.0f, -4.0f, 5.0f});
51}
52
53TEST(TransformQuaternion, pure_pitch_ninety_lands_on_minus_y_half) {
54 // CARLA pitch = +90 deg flips to rx = -pi/2; with all other components
55 // identity the quaternion is (cos(-pi/4), 0, sin(-pi/4), 0).
57 0.0f, 0.0f, 0.0f,
58 90.0f, 0.0f, 0.0f);
59 ExpectNearArray4(tf.rotation, {kSqrtHalf, 0.0f, -kSqrtHalf, 0.0f});
60}
61
62TEST(TransformQuaternion, pure_yaw_ninety_lands_on_minus_z_half) {
63 // CARLA yaw = +90 deg flips to ry = -pi/2; the quaternion picks up z =
64 // sin(-pi/4).
66 0.0f, 0.0f, 0.0f,
67 0.0f, 90.0f, 0.0f);
68 ExpectNearArray4(tf.rotation, {kSqrtHalf, 0.0f, 0.0f, -kSqrtHalf});
69}
70
71TEST(TransformQuaternion, pure_roll_ninety_lands_on_plus_x_half) {
72 // CARLA roll = +90 deg keeps the sign: rz = +pi/2, quaternion x = sin(pi/4).
74 0.0f, 0.0f, 0.0f,
75 0.0f, 0.0f, 90.0f);
76 ExpectNearArray4(tf.rotation, {kSqrtHalf, kSqrtHalf, 0.0f, 0.0f});
77}
78
79TEST(TransformQuaternion, quaternion_stays_unit_norm) {
80 for (float pitch : {-180.0f, -45.0f, 0.0f, 30.0f, 90.0f, 175.0f}) {
81 for (float yaw : {-90.0f, 0.0f, 60.0f}) {
82 for (float roll : {-30.0f, 0.0f, 45.0f}) {
84 0.0f, 0.0f, 0.0f,
85 pitch, yaw, roll);
86 const float norm =
87 tf.rotation[0] * tf.rotation[0]
88 + tf.rotation[1] * tf.rotation[1]
89 + tf.rotation[2] * tf.rotation[2]
90 + tf.rotation[3] * tf.rotation[3];
91 EXPECT_NEAR(norm, 1.0f, 1e-4f)
92 << "pitch=" << pitch << " yaw=" << yaw << " roll=" << roll;
93 }
94 }
95 }
96}
97
98TEST(TransformQuaternion, regression_guard_against_halved_deg_to_rad) {
99 // The pre-d9cfc79fb spelling used M_PI_2 / 180 in place of M_PI / 180,
100 // halving every rotation. A 180-degree pitch should land at quaternion
101 // w=0 (pi/2 half-angle, cos(pi/2)=0); the buggy spelling would land at
102 // w=cos(pi/4)=sqrt(2)/2. Guard against a regression to that spelling.
104 0.0f, 0.0f, 0.0f,
105 180.0f, 0.0f, 0.0f);
106 EXPECT_NEAR(tf.rotation[0], 0.0f, 1e-3f);
107 EXPECT_GT(std::abs(tf.rotation[2]), 0.9f);
108}
TransformQuaternion TransformFromCarlaRotation(float tx, float ty, float tz, float pitch_deg, float yaw_deg, float roll_deg) noexcept
TEST(TransformQuaternion, identity_rotation_is_w_one)