CARLA
Loading...
Searching...
No Matches
test_agents.cpp
Go to the documentation of this file.
1// Copyright (c) 2018-2020 CVC.
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5//
6// Server-free unit tests for the C++ port of agents.navigation.* (issue
7// carla-simulator/carla#9554). The tests exercise the parts that don't need
8// a live CARLA server: the Types.h enums and behavior factories, the pure
9// helpers in Misc.h, and the Default*Options() factories on the planner /
10// agent classes. Behaviour that requires a Vehicle / Map / TrafficLight is
11// covered by integration tests run against a real server.
12
13#include "test.h"
14
23
24#include <carla/geom/Location.h>
25#include <carla/geom/Rotation.h>
27
28#include <cmath>
29#include <type_traits>
30
31using namespace carla::agents::navigation;
35
36// =====================================================================
37// Types.h — enum sanity + behavior factory parity with Python.
38// =====================================================================
39
40TEST(agents_types, road_option_values_match_python) {
41 EXPECT_EQ(static_cast<int>(RoadOption::Void), -1);
42 EXPECT_EQ(static_cast<int>(RoadOption::Left), 1);
43 EXPECT_EQ(static_cast<int>(RoadOption::Right), 2);
44 EXPECT_EQ(static_cast<int>(RoadOption::Straight), 3);
45 EXPECT_EQ(static_cast<int>(RoadOption::LaneFollow), 4);
46 EXPECT_EQ(static_cast<int>(RoadOption::ChangeLaneLeft), 5);
47 EXPECT_EQ(static_cast<int>(RoadOption::ChangeLaneRight), 6);
48}
49
50TEST(agents_types, behavior_cautious_matches_python) {
51 const auto p = BehaviorParameters::Cautious();
52 EXPECT_FLOAT_EQ(p.max_speed, 40.0f);
53 EXPECT_FLOAT_EQ(p.speed_lim_dist, 6.0f);
54 EXPECT_FLOAT_EQ(p.speed_decrease, 12.0f);
55 EXPECT_FLOAT_EQ(p.safety_time, 3.0f);
56 EXPECT_FLOAT_EQ(p.min_proximity_threshold, 12.0f);
57 EXPECT_FLOAT_EQ(p.braking_distance, 6.0f);
58 EXPECT_EQ (p.tailgate_counter, 0);
59}
60
61TEST(agents_types, behavior_normal_matches_python) {
62 const auto p = BehaviorParameters::Normal();
63 EXPECT_FLOAT_EQ(p.max_speed, 50.0f);
64 EXPECT_FLOAT_EQ(p.speed_lim_dist, 3.0f);
65 EXPECT_FLOAT_EQ(p.speed_decrease, 10.0f);
66 EXPECT_FLOAT_EQ(p.safety_time, 3.0f);
67 EXPECT_FLOAT_EQ(p.min_proximity_threshold, 10.0f);
68 EXPECT_FLOAT_EQ(p.braking_distance, 5.0f);
69 EXPECT_EQ (p.tailgate_counter, 0);
70}
71
72TEST(agents_types, behavior_aggressive_matches_python) {
73 const auto p = BehaviorParameters::Aggressive();
74 EXPECT_FLOAT_EQ(p.max_speed, 70.0f);
75 EXPECT_FLOAT_EQ(p.speed_lim_dist, 1.0f);
76 EXPECT_FLOAT_EQ(p.speed_decrease, 8.0f);
77 EXPECT_FLOAT_EQ(p.safety_time, 3.0f);
78 EXPECT_FLOAT_EQ(p.min_proximity_threshold, 8.0f);
79 EXPECT_FLOAT_EQ(p.braking_distance, 4.0f);
80 // Aggressive disables tailgating by setting the counter to -1, so the
81 // `> 0` test in BehaviorAgent::Tailgating always fails.
82 EXPECT_EQ (p.tailgate_counter, -1);
83}
84
85// =====================================================================
86// Misc.h — pure helpers.
87// =====================================================================
88
89TEST(agents_misc, positive_clamps_at_zero) {
90 EXPECT_FLOAT_EQ(Positive(-3.5f), 0.0f);
91 EXPECT_FLOAT_EQ(Positive(0.0f), 0.0f);
92 EXPECT_FLOAT_EQ(Positive(2.5f), 2.5f);
93}
94
95TEST(agents_misc, compute_distance_basic) {
96 Location a(0.0f, 0.0f, 0.0f);
97 Location b(3.0f, 4.0f, 0.0f);
98 EXPECT_NEAR(ComputeDistance(a, b), 5.0f, 1e-3f);
99
100 Location c(1.0f, 2.0f, 2.0f);
101 Location d(1.0f, 2.0f, 2.0f);
102 // Identical points: epsilon-regularised result is small but nonzero.
103 EXPECT_NEAR(ComputeDistance(c, d), 0.0f, 1e-3f);
104}
105
106TEST(agents_misc, unit_vector_is_normalized) {
107 Location a(0.0f, 0.0f, 0.0f);
108 Location b(3.0f, 4.0f, 0.0f);
109 const auto u = UnitVector(a, b);
110 // 3-4-5 triangle: expect (0.6, 0.8, 0.0).
111 EXPECT_NEAR(u[0], 0.6f, 1e-3f);
112 EXPECT_NEAR(u[1], 0.8f, 1e-3f);
113 EXPECT_NEAR(u[2], 0.0f, 1e-3f);
114 // Magnitude is unit.
115 const float mag = std::sqrt(u[0]*u[0] + u[1]*u[1] + u[2]*u[2]);
116 EXPECT_NEAR(mag, 1.0f, 1e-3f);
117}
118
119TEST(agents_misc, is_within_distance_no_angle_filter) {
120 Transform ref(Location(0, 0, 0), Rotation(0, 0, 0));
121 Transform near_t(Location(5, 0, 0), Rotation(0, 0, 0));
122 Transform far_t(Location(50, 0, 0), Rotation(0, 0, 0));
123
124 EXPECT_TRUE (IsWithinDistance(near_t, ref, 10.0f));
125 EXPECT_FALSE(IsWithinDistance(far_t, ref, 10.0f));
126}
127
128TEST(agents_misc, is_within_distance_too_close_returns_true) {
129 // Per the Python helper, when target is essentially co-located with the
130 // reference (norm < 0.001), the function returns true regardless of
131 // distance/angle (avoids divide-by-zero on the angle calculation).
132 Transform ref(Location(10, 10, 0), Rotation(0, 90, 0));
133 Transform overlap(Location(10, 10, 0), Rotation(0, 0, 0));
134 EXPECT_TRUE(IsWithinDistance(overlap, ref, 0.001f, std::make_pair(0.0f, 90.0f)));
135}
136
137TEST(agents_misc, is_within_distance_with_forward_arc_filter) {
138 // Reference looks down +x. Python uses a strict open interval
139 // (min < angle < max), so a target exactly on the forward axis (angle=0°)
140 // is excluded from the (0°, 90°) cone — matched here by placing the
141 // target diagonally forward-right at 45°.
142 Transform ref(Location(0, 0, 0), Rotation(0, 0, 0));
143 Transform front_diag(Location(5, 5, 0), Rotation(0, 0, 0)); // 45° from +x
144 Transform behind(Location(-5, 0, 0), Rotation(0, 0, 0)); // 180° (behind)
145
146 EXPECT_TRUE (IsWithinDistance(front_diag, ref, 10.0f, std::make_pair(0.0f, 90.0f)));
147 EXPECT_FALSE(IsWithinDistance(behind, ref, 10.0f, std::make_pair(0.0f, 90.0f)));
148
149 // Strict-open boundary: target exactly on the forward axis (angle=0°) is
150 // NOT inside the (0°, 90°) interval. Matches Python parity.
151 Transform front_axial(Location(5, 0, 0), Rotation(0, 0, 0));
152 EXPECT_FALSE(IsWithinDistance(front_axial, ref, 10.0f, std::make_pair(0.0f, 90.0f)));
153}
154
155TEST(agents_misc, compute_magnitude_angle_zero_yaw) {
156 // Looking down +x, target also down +x → angle ~ 0°.
157 Location origin(0, 0, 0);
158 Location forward(10, 0, 0);
159 const auto [mag, angle] = ComputeMagnitudeAngle(forward, origin, /*orientation_deg=*/0.0f);
160 EXPECT_NEAR(mag, 10.0f, 1e-3f);
161 EXPECT_NEAR(angle, 0.0f, 1e-2f);
162}
163
164TEST(agents_misc, compute_magnitude_angle_perpendicular) {
165 // Looking down +x, target up +y → angle = 90°.
166 Location origin(0, 0, 0);
167 Location side(0, 5, 0);
168 const auto [mag, angle] = ComputeMagnitudeAngle(side, origin, /*orientation_deg=*/0.0f);
169 EXPECT_NEAR(mag, 5.0f, 1e-3f);
170 EXPECT_NEAR(angle, 90.0f, 1e-2f);
171}
172
173// =====================================================================
174// Default*Options factories — confirm they value-initialize and the
175// downstream defaults flow through (Python parity for the opt_dict).
176// =====================================================================
177
178TEST(agents_factories, local_planner_default_options) {
179 const auto opt = LocalPlanner::DefaultOptions();
180 // Python defaults: dt = 1/20, target_speed = 20 km/h, sampling = 2 m.
181 EXPECT_FLOAT_EQ(opt.dt, 1.0f / 20.0f);
182 EXPECT_FLOAT_EQ(opt.target_speed, 20.0f);
183 EXPECT_FLOAT_EQ(opt.sampling_radius, 2.0f);
184 EXPECT_FLOAT_EQ(opt.max_throttle, 0.75f);
185 EXPECT_FLOAT_EQ(opt.max_brake, 0.30f);
186 EXPECT_FLOAT_EQ(opt.max_steering, 0.80f);
187 EXPECT_FLOAT_EQ(opt.offset, 0.0f);
188 EXPECT_FLOAT_EQ(opt.base_min_distance, 3.0f);
189 EXPECT_FLOAT_EQ(opt.distance_ratio, 0.5f);
190 EXPECT_FALSE (opt.follow_speed_limits);
191
192 // The PID arg defaults come from VehiclePIDController::*Args definitions.
193 EXPECT_FLOAT_EQ(opt.lateral.K_P, 1.95f);
194 EXPECT_FLOAT_EQ(opt.lateral.K_I, 0.05f);
195 EXPECT_FLOAT_EQ(opt.lateral.K_D, 0.20f);
196 EXPECT_FLOAT_EQ(opt.longitudinal.K_P, 1.50f);
197 EXPECT_FLOAT_EQ(opt.longitudinal.K_I, 0.05f);
198 EXPECT_FLOAT_EQ(opt.longitudinal.K_D, 0.20f);
199}
200
201TEST(agents_factories, basic_agent_default_options_inherits_local_planner) {
202 const auto opt = BasicAgent::DefaultOptions();
203 // BasicAgent-specific defaults.
204 EXPECT_FALSE(opt.ignore_traffic_lights);
205 EXPECT_FALSE(opt.ignore_stop_signs);
206 EXPECT_FALSE(opt.ignore_vehicles);
207 EXPECT_FALSE(opt.use_bbs_detection);
208 EXPECT_FLOAT_EQ(opt.sampling_resolution, 2.0f);
209 EXPECT_FLOAT_EQ(opt.base_tlight_threshold, 5.0f);
210 EXPECT_FLOAT_EQ(opt.base_vehicle_threshold, 5.0f);
211 EXPECT_FLOAT_EQ(opt.detection_speed_ratio, 1.0f);
212 EXPECT_FLOAT_EQ(opt.max_brake, 0.6f);
213 // Inherited from LocalPlanner::Options.
214 EXPECT_FLOAT_EQ(opt.target_speed, 20.0f);
215 EXPECT_FLOAT_EQ(opt.dt, 1.0f / 20.0f);
216}
217
218TEST(agents_factories, constant_velocity_default_options) {
220 EXPECT_FALSE(opt.use_basic_behavior);
221 EXPECT_TRUE (std::isinf(opt.restart_time));
222 // Inherited from BasicAgent::Options.
223 EXPECT_FALSE(opt.ignore_traffic_lights);
224 EXPECT_FLOAT_EQ(opt.max_brake, 0.6f);
225}
226
227// =====================================================================
228// Compile-time API surface checks. These don't run any logic — they just
229// pin the expected types so a future header refactor that drifts from the
230// public contract fails to compile.
231// =====================================================================
232
233TEST(agents_api, surface_pins) {
234 using PlanItem = LocalPlanner::PlanItem;
235 static_assert(std::is_same_v<PlanItem,
236 std::pair<carla::SharedPtr<carla::client::Waypoint>, RoadOption>>,
237 "LocalPlanner::PlanItem must be (Waypoint, RoadOption)");
238
239 using RouteItem = GlobalRoutePlanner::RouteItem;
240 static_assert(std::is_same_v<RouteItem,
241 std::pair<carla::SharedPtr<carla::client::Waypoint>, RoadOption>>,
242 "GlobalRoutePlanner::RouteItem must be (Waypoint, RoadOption)");
243
244 using Behavior = BehaviorAgent::Behavior;
245 static_assert(static_cast<int>(Behavior::Cautious) != static_cast<int>(Behavior::Normal),
246 "BehaviorAgent::Behavior values must be distinct");
247 static_assert(static_cast<int>(Behavior::Normal) != static_cast<int>(Behavior::Aggressive),
248 "BehaviorAgent::Behavior values must be distinct");
249
250 // BasicAgent::Options derives from LocalPlanner::Options.
251 static_assert(std::is_base_of_v<LocalPlanner::Options, BasicAgent::Options>,
252 "BasicAgent::Options must inherit LocalPlanner::Options");
253 // ConstantOptions derives from BasicAgent::Options.
254 static_assert(std::is_base_of_v<BasicAgent::Options,
256 "ConstantOptions must inherit BasicAgent::Options");
257
258 // BehaviorAgent / ConstantVelocityAgent inherit BasicAgent.
259 static_assert(std::is_base_of_v<BasicAgent, BehaviorAgent>,
260 "BehaviorAgent must inherit BasicAgent");
261 static_assert(std::is_base_of_v<BasicAgent, ConstantVelocityAgent>,
262 "ConstantVelocityAgent must inherit BasicAgent");
263
264 // BasicAgent has a virtual destructor (so subclasses are safely deletable
265 // through a base pointer).
266 static_assert(std::has_virtual_destructor_v<BasicAgent>,
267 "BasicAgent must have a virtual destructor");
268
269 SUCCEED();
270}
static Options DefaultOptions()
Out-of-line default factory; see LocalPlanner::DefaultOptions().
Definition BasicAgent.h:171
static ConstantOptions DefaultConstantOptions()
Out-of-line default factory; see LocalPlanner::DefaultOptions().
std::pair< SharedPtr< client::Waypoint >, RoadOption > RouteItem
static Options DefaultOptions()
Construct a LocalPlanner.
std::pair< SharedPtr< client::Waypoint >, RoadOption > PlanItem
Euler angles of a rotation in CARLA's / Unreal's left-handed frame: x forward, y right,...
Definition Rotation.h:45
A rigid transform in CARLA's / Unreal's left-handed frame: x forward, y right, z up,...
bool IsWithinDistance(const geom::Transform &target, const geom::Transform &reference, float max_distance, std::optional< std::pair< float, float > > angle_interval)
Returns true when target is within max_distance of reference.
Definition Misc.cpp:78
RoadOption
Topological direction emitted by the planners.
float ComputeDistance(const geom::Location &a, const geom::Location &b)
Euclidean distance between two locations (matches numpy's regularised version that adds an epsilon to...
Definition Misc.cpp:150
float Positive(float v)
Clamp v at zero. Inline because it's tiny.
Definition Misc.h:74
std::pair< float, float > ComputeMagnitudeAngle(const geom::Location &target, const geom::Location &current, float orientation_deg)
Returns the (distance, angle in degrees) from current to target relative to a forward orientation ori...
Definition Misc.cpp:109
std::array< float, 3 > UnitVector(const geom::Location &a, const geom::Location &b)
Unit vector going from a to b, including the z component.
Definition Misc.cpp:139
static constexpr BehaviorParameters Aggressive()
static constexpr BehaviorParameters Cautious()
static constexpr BehaviorParameters Normal()
TEST(agents_types, road_option_values_match_python)