CARLA
Loading...
Searching...
No Matches
MotionPlanStage.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 <algorithm>
8#include <cmath>
9#include <limits>
10
14
18
20
21namespace carla {
22namespace traffic_manager {
23
24using namespace constants::MotionPlan;
25using namespace constants::WaypointSelection;
26using namespace constants::SpeedThreshold;
27
32
34 const std::vector<ActorId> &vehicle_id_list,
35 SimulationState &simulation_state,
36 const Parameters &parameters,
37 const BufferMap &buffer_map,
38 TrackTraffic &track_traffic,
39 const std::vector<float> &urban_longitudinal_parameters,
40 const std::vector<float> &highway_longitudinal_parameters,
41 const std::vector<float> &urban_lateral_parameters,
42 const std::vector<float> &highway_lateral_parameters,
43 const LocalizationFrame &localization_frame,
44 const CollisionFrame&collision_frame,
45 const TLFrame &tl_frame,
46 ControlFrame &output_array,
47 RandomGenerator &random_device,
48 const LocalMapPtr &local_map,
49 std::unordered_map<ActorId, std::pair<float, bool>> &large_vehicles)
50 : vehicle_id_list(vehicle_id_list),
51 simulation_state(simulation_state),
52 parameters(parameters),
53 buffer_map(buffer_map),
54 track_traffic(track_traffic),
55 urban_longitudinal_parameters(urban_longitudinal_parameters),
56 highway_longitudinal_parameters(highway_longitudinal_parameters),
57 urban_lateral_parameters(urban_lateral_parameters),
58 highway_lateral_parameters(highway_lateral_parameters),
59 localization_frame(localization_frame),
60 collision_frame(collision_frame),
61 tl_frame(tl_frame),
62 output_array(output_array),
63 random_device(random_device),
64 local_map(local_map),
65 large_vehicles(large_vehicles) {}
66
68 current_timestamp = timestamp;
69}
70
71void MotionPlanStage::Update(const unsigned long index) {
72 const ActorId actor_id = vehicle_id_list.at(index);
73 const cg::Location vehicle_location = simulation_state.GetLocation(actor_id);
74 const cg::Vector3D vehicle_velocity = simulation_state.GetVelocity(actor_id);
75 const cg::Rotation vehicle_rotation = simulation_state.GetRotation(actor_id);
76 const float vehicle_speed = vehicle_velocity.Length();
77 const cg::Vector3D vehicle_heading = simulation_state.GetHeading(actor_id);
78 const bool vehicle_physics_enabled = simulation_state.IsPhysicsEnabled(actor_id);
79 const float vehicle_speed_limit = simulation_state.GetSpeedLimit(actor_id);
80 const Buffer &waypoint_buffer = buffer_map.at(actor_id);
81 const LocalizationData &localization = localization_frame.at(index);
82 const CollisionHazardData &collision_hazard = collision_frame.at(index);
83 const bool &tl_hazard = tl_frame.at(index);
84 StateEntry current_state;
85
86 // Instanciating teleportation transform as current vehicle transform.
87 cg::Transform teleportation_transform = cg::Transform(vehicle_location, vehicle_rotation);
88
89 // Get information about the hero location from the actor_id state.
91 bool is_hero_alive = hero_location != cg::Location(0, 0, 0);
92
93 if (simulation_state.IsDormant(actor_id) && parameters.GetRespawnDormantVehicles() && is_hero_alive) {
94 // Flushing controller state for vehicle.
95 current_state = {current_timestamp,
96 0.0f, 0.0f,
97 0.0f};
98
99 // Add entry to teleportation duration clock table if not present.
100 if (teleportation_instance.find(actor_id) == teleportation_instance.end()) {
101 teleportation_instance.insert({actor_id, current_timestamp});
102 }
103
104 // Get lower and upper bound for teleporting vehicle.
107 float dilate_factor = (upper_bound-lower_bound)/100.0f;
108
109 // Measuring time elapsed since last teleportation for the vehicle.
110 double elapsed_time = current_timestamp.elapsed_seconds - teleportation_instance.at(actor_id).elapsed_seconds;
111
112 if (parameters.GetSynchronousMode() || elapsed_time > HYBRID_MODE_DT) {
113 float random_sample = (static_cast<float>(random_device.next())*dilate_factor) + lower_bound;
114 NodeList teleport_waypoint_list = local_map->GetWaypointsInDelta(hero_location, ATTEMPTS_TO_TELEPORT, random_sample);
115 if (!teleport_waypoint_list.empty()) {
116 for (auto &teleport_waypoint : teleport_waypoint_list) {
117 GeoGridId geogrid_id = teleport_waypoint->GetGeodesicGridId();
118 if (track_traffic.IsGeoGridFree(geogrid_id)) {
119 teleportation_transform = teleport_waypoint->GetTransform();
120 teleportation_transform.location.z += 0.5f;
121 track_traffic.AddTakenGrid(geogrid_id, actor_id);
122 break;
123 }
124 }
125 }
126 }
127 output_array.at(index) = carla::rpc::Command::ApplyTransform(actor_id, teleportation_transform);
128
129 // Update the simulation state with the new transform of the vehicle after teleporting it.
130 KinematicState kinematic_state{teleportation_transform.location,
131 teleportation_transform.rotation,
132 vehicle_velocity, vehicle_speed_limit,
133 vehicle_physics_enabled, simulation_state.IsDormant(actor_id),
134 teleportation_transform.location};
135 simulation_state.UpdateKinematicState(actor_id, kinematic_state);
136 }
137
138 else {
139
140 // Target velocity for vehicle.
141 float max_target_velocity = parameters.GetVehicleTargetVelocity(actor_id, vehicle_speed_limit) / 3.6f;
142
143 // Algorithm to reduce speed near landmarks
144 float max_landmark_target_velocity = GetLandmarkTargetVelocity(*(waypoint_buffer.at(0)), vehicle_location, actor_id, max_target_velocity);
145
146 // Algorithm to reduce speed near turns
147 float max_turn_target_velocity = GetTurnTargetVelocity(waypoint_buffer, vehicle_location, max_target_velocity);
148 max_target_velocity = std::min(std::min(max_target_velocity, max_landmark_target_velocity), max_turn_target_velocity);
149
150 // Collision handling and target velocity correction.
151 std::pair<bool, float> collision_response = CollisionHandling(collision_hazard, tl_hazard, vehicle_velocity,
152 vehicle_heading, max_target_velocity);
153 bool collision_emergency_stop = collision_response.first;
154 float dynamic_target_velocity = collision_response.second;
155
156 // Don't enter junction if there isn't enough free space after the junction.
157 bool safe_after_junction = SafeAfterJunction(localization, tl_hazard, collision_emergency_stop);
158
159 // In case of collision or traffic light hazard.
160 bool emergency_stop = tl_hazard || collision_emergency_stop || !safe_after_junction;
161
162 if (vehicle_physics_enabled && !simulation_state.IsDormant(actor_id)) {
163 ActuationSignal actuation_signal{0.0f, 0.0f, 0.0f};
164
165 // Vehicle steering geometry. See constants::PID: the pursuit law and
166 // the steering-authority normalization below are computed from the
167 // vehicle's own wheelbase (approximated from the bounding-box length)
168 // and physical wheel lock instead of the car-class anchor.
169 const float vehicle_length{2.0f * simulation_state.GetDimensions(actor_id).x};
170 const float wheel_lock{std::max(simulation_state.GetMaxSteerAngle(actor_id), 1.0f)};
171 const float wheelbase{constants::PID::WHEELBASE_FRACTION * vehicle_length};
172
173 // The pursuit reference is the actor origin (bounding-box centre).
174 // Kinematically pure pursuit is exact at the rear axle, but for lane
175 // containment of a rigid body the centre is the optimal reference (the
176 // body then splits its wheelbase chord evenly across the lane), and a
177 // rear-axle reference was measured to add enough loop delay on long
178 // vehicles to weave through tight junction turns.
179 const cg::Location &pursuit_reference{vehicle_location};
180
181 // Resolve the target waypoint by interpolating along the buffer at
182 // target_point_distance ahead of the reference.
183 float target_point_distance{std::clamp(
184 vehicle_speed * TARGET_WAYPOINT_TIME_HORIZON,
187 auto target_data = GetTargetData(
188 waypoint_buffer,
189 target_point_distance,
190 pursuit_reference);
191
192 // Curvature-aware pursuit-distance cap. Pursuing a point d ahead tracks
193 // the chord of the path, which cuts a curve of radius R by the sagitta
194 // ~d^2/(8R): through junction turns (R ~ 11 m on Town10) the vehicle
195 // turns in before the lane does and sweeps ~1.4 m into the adjacent
196 // lane. Bound d so the geometric cut stays below
197 // MAX_PURSUIT_CHORD_SAGITTA; straight paths (R -> inf) are unaffected.
198 const auto mid_data = GetTargetData(
199 waypoint_buffer,
200 0.5f * target_point_distance,
201 pursuit_reference);
202 const float local_radius = GetThreePointCircleRadius(
203 waypoint_buffer.front()->GetLocation(),
204 mid_data.first,
205 target_data.first);
206 const float sagitta_distance_cap =
207 std::sqrt(8.0f * local_radius * MAX_PURSUIT_CHORD_SAGITTA);
208 if (sagitta_distance_cap < target_point_distance) {
209 target_point_distance = std::max(sagitta_distance_cap,
211 target_data = GetTargetData(
212 waypoint_buffer,
213 target_point_distance,
214 pursuit_reference);
215 }
216 const uint64_t target_index = target_data.second;
217 cg::Location target_location{target_data.first};
218 const SimpleWaypointPtr target_waypoint = waypoint_buffer.at(target_index);
219
220 // The lateral PID linearizes the pure-pursuit law, whose equivalent
221 // proportional gain is inversely proportional to the pursuit distance
222 // (kappa = 2 * sin(alpha) / d). The gains are tuned at the cruise
223 // anchor d = MAX_TARGET_WAYPOINT_DISTANCE; when the target sits closer
224 // (low speed, or the curvature cap above) the loop gain must scale by
225 // d_ref / d or the controller winds on steering too slowly for the
226 // path curvature and runs wide through junction turns.
227 const float lateral_gain_scale =
228 MAX_TARGET_WAYPOINT_DISTANCE / std::max(target_point_distance, 1.0f);
229
230 // Steering-authority normalization: normalized steer maps to curvature
231 // as kappa = tan(steer * max_steer_angle) / wheelbase, so long vehicles
232 // produce proportionally less curvature per command than the car-class
233 // fleet the gains and the STEER_LIMIT_GAIN envelope are anchored on.
234 // The STEER_LIMIT_GAIN envelope is a physical curvature guard, so it
235 // scales with the vehicle's authority. See constants::PID.
236 const float steer_authority_correction{std::clamp(
237 (vehicle_length / constants::PID::REF_VEHICLE_LENGTH) *
240
241 float base_offset{CalculateBaseOffset(
242 actor_id,
243 waypoint_buffer,
244 target_waypoint->CheckJunction(),
245 target_index)};
246 const auto right_vector = target_waypoint->GetTransform().GetRightVector();
247
248 // Suppress the wide-turn swing when the side it would move into is
249 // occupied by a nearby vehicle (parked, stopped or adjacent traffic).
250 if (std::abs(base_offset) > EPSILON) {
251 const cg::Vector3D offset_direction{
252 (base_offset > 0.0f) ? right_vector : (-1.0f * right_vector)};
253 if (IsWideTurnSideOccupied(actor_id, offset_direction, std::abs(base_offset))) {
254 base_offset = 0.0f;
255 }
256 }
257
258 const float offset{parameters.GetLaneOffset(actor_id) + base_offset};
259 const auto offset_location = cg::Location(cg::Vector3D(
260 offset * right_vector.x,
261 offset * right_vector.y,
262 0.0f));
263 target_location = target_location + offset_location;
264
265 // Compute the angular deviation directly as the angle between the
266 // vehicle heading and the target direction. atan2(0, 0) is
267 // well-defined and returns 0 when the reference and target locations
268 // coincide on the very first tick before any displacement.
269 const cg::Vector3D target_vector{target_location - pursuit_reference};
270 const float target_yaw{std::atan2(target_vector.y, target_vector.x) * 180.0f / PI};
271 float angular_deviation{target_yaw - vehicle_rotation.yaw};
272 if (angular_deviation > 180.0f) {
273 angular_deviation -= 360.0f;
274 } else if (angular_deviation < -180.0f) {
275 angular_deviation += 360.0f;
276 }
277 angular_deviation /= 180.0f; // Normalised to [-1, 1].
278
279 // Full throttle with saturated steering produces accelerating donuts
280 // when the target sits far off-heading (after a spin, a collision or
281 // a wrong-way resume). Cap the target speed while grossly misaligned
282 // so full-lock turns stay tight; speed resumes as heading converges.
283 if (std::abs(angular_deviation) > 0.25f) { // > 45 degrees
284 dynamic_target_velocity = std::min(dynamic_target_velocity, 3.0f);
285 }
286 // --- Stuck / misaligned vehicle recovery (K-turn) -----------------
287 // Two situations the forward PID cannot solve: a vehicle commanded to
288 // move (no red light, no vehicle ahead) that stays immobile is wedged
289 // against something and would grind at full lock forever; a vehicle
290 // whose target sits far off-heading (spun by a collision, player
291 // driving, wrong-way resume) would chase a target behind itself in
292 // full-lock circles. Both run a K-turn: alternate reverse and forward
293 // phases with full steering toward the target until roughly aligned,
294 // then resume the PID with a fresh state.
295 {
296 using namespace constants::StuckRecovery;
297 const double now = current_timestamp.elapsed_seconds;
298 const float abs_deviation = std::abs(angular_deviation);
299 bool recovering = false;
300 auto rec_it = recovery_state.find(actor_id);
301 if (rec_it != recovery_state.end()) {
302 if (abs_deviation < ALIGN_EXIT_DEVIATION) {
303 recovery_state.erase(rec_it);
304 stuck_since.erase(actor_id);
305 // Seed the fresh PID state with the current deviation: a zeroed
306 // previous deviation would make the first post-recovery tick see
307 // the full residual error as a one-tick change and kick the
308 // derivative term into a saturated steer, restarting the swing
309 // the K-turn just corrected.
310 pid_state_map[actor_id] = StateEntry{current_timestamp, angular_deviation, 0.0f, 0.0f};
311 } else {
312 recovering = true;
313 if (now >= rec_it->second.phase_until) {
314 rec_it->second.reversing = !rec_it->second.reversing;
315 rec_it->second.phase_until = now + PHASE_DURATION;
316 }
317 // Refresh the latched direction only once the deviation sign is
318 // unambiguous (target no longer near dead-astern).
319 if (abs_deviation < 0.5f) {
320 rec_it->second.steer_direction =
321 (angular_deviation > 0.0f) ? 1.0f : -1.0f;
322 }
323 }
324 }
325 if (!recovering && !emergency_stop) {
326 bool stuck_trigger = false;
327 if (dynamic_target_velocity > STUCK_SPEED && vehicle_speed < STUCK_SPEED) {
328 auto stuck_it = stuck_since.insert({actor_id, now}).first;
329 stuck_trigger = (now - stuck_it->second > STUCK_TIME);
330 } else {
331 stuck_since.erase(actor_id);
332 }
333 const bool misaligned = abs_deviation > ALIGN_ENTER_DEVIATION &&
334 vehicle_speed < MISALIGN_MAX_SPEED;
335 if (stuck_trigger || misaligned) {
336 recovery_state[actor_id] = RecoveryState{
337 now + PHASE_DURATION, true,
338 (angular_deviation > 0.0f) ? 1.0f : -1.0f};
339 recovering = true;
340 }
341 }
342 if (recovering) {
343 const RecoveryState &rs = recovery_state.at(actor_id);
344 carla::rpc::VehicleControl recovery_control;
345 recovery_control.reverse = rs.reversing;
346 recovery_control.throttle = rs.reversing ? REVERSE_THROTTLE : FORWARD_THROTTLE;
347 recovery_control.brake = 0.0f;
348 recovery_control.steer =
349 (rs.reversing ? -rs.steer_direction : rs.steer_direction) * RECOVERY_STEER;
350 output_array.at(index) = carla::rpc::Command::ApplyVehicleControl(actor_id, recovery_control);
351 return;
352 }
353 }
354
355 // If previous state for vehicle not found, initialize state entry.
356 if (pid_state_map.find(actor_id) == pid_state_map.end()) {
357 const auto initial_state = StateEntry{current_timestamp, 0.0f, 0.0f, 0.0f};
358 pid_state_map.insert({actor_id, initial_state});
359 }
360
361 // Retrieving the previous state.
362 traffic_manager::StateEntry previous_state;
363 previous_state = pid_state_map.at(actor_id);
364
365 // Select PID parameters.
366 std::vector<float> longitudinal_parameters;
367 std::vector<float> lateral_parameters;
368 if (vehicle_speed > HIGHWAY_SPEED) {
369 longitudinal_parameters = highway_longitudinal_parameters;
370 lateral_parameters = highway_lateral_parameters;
371 } else {
372 longitudinal_parameters = urban_longitudinal_parameters;
373 lateral_parameters = urban_lateral_parameters;
374 }
375
376 // Measured controller period in simulation time. In synchronous mode
377 // this is fixed_delta_seconds; in asynchronous mode it is one server
378 // frame, which under render load can stretch well past the nominal DT
379 // the gains were tuned at -- RunStep compensates. Non-positive values
380 // (first tick after registration or a recovery reseed, where previous
381 // and current share a timestamp) fall back to the nominal period.
382 float control_dt = static_cast<float>(
384 if (control_dt <= 0.0f) {
385 control_dt = DT;
386 }
387
388 const float reference_velocity{PID::ShapeReferenceVelocity(
389 previous_state.reference_velocity,
390 vehicle_speed,
391 dynamic_target_velocity,
392 control_dt,
393 emergency_stop)};
394 const float velocity_deviation{PID::RelativeVelocityDeviation(
395 reference_velocity, vehicle_speed, dynamic_target_velocity)};
396
397 // If physics is enabled for the vehicle, use PID controller.
398 // State update for vehicle.
399 current_state = {current_timestamp, angular_deviation, velocity_deviation, 0.0f};
400 current_state.reference_velocity = reference_velocity;
401
402 // Geometric pure-pursuit lateral command. The previous linearized
403 // proportional term (P * deviation) was tuned at car-scale commands
404 // (|steer| ~ 0.1) and over-curves long vehicles, which need
405 // |steer| ~ 0.4-0.5 through junction turns where tan(steer * lock) is
406 // well past its linear range (measured: trucks settling ~1 m inside
407 // their reference). Commanding the pure-pursuit curvature
408 // kappa = margin * 2 sin(alpha) / d and inverting the vehicle's own
409 // steering map atan(wheelbase * kappa) / lock is exact for every
410 // geometry, and reduces to the validated P = 2 linear gain for the
411 // anchor car at the cruise pursuit distance (see Constants.h).
412 const float wheel_lock_rad{wheel_lock * PI / 180.0f};
413 const float alpha_rad{angular_deviation * PI};
414 const float pursuit_curvature{constants::PID::PURSUIT_CURVATURE_MARGIN *
415 2.0f * std::sin(alpha_rad) /
416 std::max(target_point_distance, 1.0f)};
417 const float pursuit_steer{std::atan(wheelbase * pursuit_curvature) /
418 wheel_lock_rad};
419
420 // Damping (derivative) scale: pursuit-distance schedule x authority,
421 // capped at the ceiling the schedule alone reaches on the tuned fleet.
422 const float lateral_damping_scale{std::min(
423 lateral_gain_scale * steer_authority_correction,
425
426 // Controller actuation.
427 actuation_signal = PID::RunStep(current_state, previous_state,
428 longitudinal_parameters, lateral_parameters,
429 vehicle_speed, control_dt,
430 pursuit_steer,
431 lateral_damping_scale,
432 steer_authority_correction);
433
434 if (emergency_stop) {
435 actuation_signal.throttle = 0.0f;
436 actuation_signal.brake = 1.0f;
437 } else {
438 PID::SmoothActuation(previous_state, control_dt, vehicle_speed, actuation_signal);
439 }
440
441 // Constructing the actuation signal.
442 carla::rpc::VehicleControl vehicle_control;
443 vehicle_control.throttle = actuation_signal.throttle;
444 vehicle_control.brake = actuation_signal.brake;
445 vehicle_control.steer = actuation_signal.steer;
446
447 output_array.at(index) = carla::rpc::Command::ApplyVehicleControl(actor_id, vehicle_control);
448
449 // Updating PID state.
450 current_state.steer = actuation_signal.steer;
451 current_state.throttle = actuation_signal.throttle;
452 current_state.brake = actuation_signal.brake;
453 StateEntry &state = pid_state_map.at(actor_id);
454 state = current_state;
455 }
456 // For physics-less vehicles, determine position and orientation for teleportation.
457 else {
458 // Flushing controller state for vehicle.
459 current_state = {current_timestamp,
460 0.0f, 0.0f,
461 0.0f};
462
463 // Add entry to teleportation duration clock table if not present.
464 if (teleportation_instance.find(actor_id) == teleportation_instance.end()) {
465 teleportation_instance.insert({actor_id, current_timestamp});
466 }
467
468 // Measuring time elapsed since last teleportation for the vehicle.
469 double elapsed_time = current_timestamp.elapsed_seconds - teleportation_instance.at(actor_id).elapsed_seconds;
470
471 // Find a location ahead of the vehicle for teleportation to achieve intended velocity.
472 if (!emergency_stop && (parameters.GetSynchronousMode() || elapsed_time > HYBRID_MODE_DT)) {
473
474 // Target displacement magnitude to achieve target velocity.
475 const float target_displacement = dynamic_target_velocity * HYBRID_MODE_DT_FL;
476 SimpleWaypointPtr teleport_target = waypoint_buffer.front();
477 cg::Transform target_base_transform = teleport_target->GetTransform();
478 cg::Location target_base_location = target_base_transform.location;
479 cg::Vector3D target_heading = target_base_transform.GetForwardVector();
480 cg::Vector3D correct_heading = (target_base_location - vehicle_location).MakeUnitVector(EPSILON);
481
482 if (vehicle_location.Distance(target_base_location) < target_displacement) {
483 cg::Location teleportation_location = vehicle_location + cg::Location(target_heading.MakeUnitVector(EPSILON) * target_displacement);
484 teleportation_transform = cg::Transform(teleportation_location, target_base_transform.rotation);
485 }
486 else {
487 cg::Location teleportation_location = vehicle_location + cg::Location(correct_heading * target_displacement);
488 teleportation_transform = cg::Transform(teleportation_location, target_base_transform.rotation);
489 }
490 // In case of an emergency stop, stay in the same location.
491 // Also, teleport only once every dt in asynchronous mode.
492 } else {
493 teleportation_transform = cg::Transform(vehicle_location, simulation_state.GetRotation(actor_id));
494 }
495 // Constructing the actuation signal.
496 output_array.at(index) = carla::rpc::Command::ApplyTransform(actor_id, teleportation_transform);
497 simulation_state.UpdateKinematicHybridEndLocation(actor_id, teleportation_transform.location);
498 }
499 }
500}
501
503 const bool tl_hazard,
504 const bool collision_emergency_stop) {
505
506 SimpleWaypointPtr junction_end_point = localization.junction_end_point;
507 SimpleWaypointPtr safe_point = localization.safe_point;
508
509 bool safe_after_junction = true;
510 if (!tl_hazard && !collision_emergency_stop
511 && localization.is_at_junction_entrance
512 && junction_end_point != nullptr && safe_point != nullptr
513 && junction_end_point->DistanceSquared(safe_point) > SQUARE(MIN_SAFE_INTERVAL_LENGTH)) {
514
515 ActorIdSet passing_safe_point = track_traffic.GetPassingVehicles(safe_point->GetId());
516 ActorIdSet passing_junction_end_point = track_traffic.GetPassingVehicles(junction_end_point->GetId());
517 cg::Location mid_point = (junction_end_point->GetLocation() + safe_point->GetLocation())/2.0f;
518
519 // Only check for vehicles that have the safe point in their passing waypoint, but not
520 // the junction end point.
521 ActorIdSet difference;
522 std::set_difference(passing_safe_point.begin(), passing_safe_point.end(),
523 passing_junction_end_point.begin(), passing_junction_end_point.end(),
524 std::inserter(difference, difference.begin()));
525 if (difference.size() > 0) {
526 for (const ActorId &blocking_id: difference) {
527 cg::Location blocking_actor_location = simulation_state.GetLocation(blocking_id);
528 if (cg::Math::DistanceSquared(blocking_actor_location, mid_point) < SQUARE(MAX_JUNCTION_BLOCK_DISTANCE)
530 safe_after_junction = false;
531 break;
532 }
533 }
534 }
535 }
536
537 return safe_after_junction;
538}
539
541 const ActorId actor_id,
542 const Buffer &waypoint_buffer,
543 const bool is_target_junction,
544 const uint64_t target_index) {
545
546 // This offset is meant to make large vehicles do wider turns at intersections.
547 if (large_vehicles.find(actor_id) == large_vehicles.end() || !is_target_junction) {
548 return 0.0f;
549 }
550
551 // The wide-turn manoeuvre can be disabled per-vehicle or globally.
552 if (!parameters.GetLargeVehicleWideTurn(actor_id)) {
553 return 0.0f;
554 }
555
556 const auto &large_vehicle = large_vehicles.at(actor_id);
557
558 // Going straight at the intersection: no offset to apply.
559 if (large_vehicle.first == 0.0f) {
560 return 0.0f;
561 }
562
563 float junction_missing_length{0.0f};
564 for (unsigned long i = target_index; i < waypoint_buffer.size(); ++i) {
565 const SimpleWaypointPtr current_waypoint = waypoint_buffer.at(i);
566
567 if (i > target_index) {
568 const SimpleWaypointPtr prev_waypoint = waypoint_buffer.at(i - 1u);
569 const float new_distance = current_waypoint->Distance(prev_waypoint->GetLocation());
570 junction_missing_length += new_distance;
571 }
572
573 if (!current_waypoint->CheckJunction()) {
574 break;
575 }
576 }
577
578 const float junction_length{large_vehicle.first};
579 const bool turn_flag{large_vehicle.second};
580
581 // Scale the offset magnitude by the vehicle's actual length so a short bus
582 // and a long truck no longer share the same fixed displacement.
583 // GetDimensions returns half-extents, so the full length is twice the x extent.
584 const float vehicle_length{2.0f * simulation_state.GetDimensions(actor_id).x};
585 const float max_offset{LargeVehicleOffsetMagnitude(
586 vehicle_length,
590
591 // Fraction of the junction still ahead of the target waypoint (1 at the
592 // entry, 0 at the exit). The profile opens the vehicle up at the entry and
593 // attenuates the inboard cut-in near the exit.
594 const float t{junction_missing_length / junction_length};
596 t,
597 max_offset,
600
601 // Sign depends on the turn direction (right vs. left).
602 offset = turn_flag ? offset : -offset;
603 return offset;
604}
605
607 const ActorId actor_id,
608 const cg::Vector3D offset_direction,
609 const float offset_magnitude) {
610
611 const ActorIdSet overlapping_vehicles{track_traffic.GetOverlappingVehicles(actor_id)};
612 if (overlapping_vehicles.empty()) {
613 return false;
614 }
615
616 std::vector<std::pair<cg::Location, float>> neighbours;
617 neighbours.reserve(overlapping_vehicles.size());
618 for (const ActorId other_id : overlapping_vehicles) {
619 if (other_id == actor_id) {
620 continue;
621 }
622 const cg::Vector3D other_dimensions{simulation_state.GetDimensions(other_id)};
623 const float radius{std::max(other_dimensions.x, other_dimensions.y)};
624 neighbours.emplace_back(simulation_state.GetLocation(other_id), radius);
625 }
626
627 const cg::Location ego_location{simulation_state.GetLocation(actor_id)};
628 const cg::Vector3D ego_forward{simulation_state.GetHeading(actor_id)};
629 const float longitudinal_window{
631
633 ego_location,
634 ego_forward,
635 offset_direction,
636 offset_magnitude,
638 longitudinal_window,
639 neighbours);
640}
641
642std::pair<bool, float> MotionPlanStage::CollisionHandling(const CollisionHazardData &collision_hazard,
643 const bool tl_hazard,
644 const cg::Vector3D vehicle_velocity,
645 const cg::Vector3D vehicle_heading,
646 const float max_target_velocity) {
647 bool collision_emergency_stop = false;
648 float dynamic_target_velocity = max_target_velocity;
649 const float vehicle_speed = vehicle_velocity.Length();
650
651 if (collision_hazard.hazard && !tl_hazard) {
652 const ActorId other_actor_id = collision_hazard.hazard_actor_id;
653 const cg::Vector3D other_velocity = simulation_state.GetVelocity(other_actor_id);
654 const float vehicle_relative_speed = (vehicle_velocity - other_velocity).Length();
655 const float available_distance_margin = collision_hazard.available_distance_margin;
656
657 const float other_speed_along_heading = cg::Math::Dot(other_velocity, vehicle_heading);
658
659 // Consider collision avoidance decisions only if there is positive relative velocity
660 // of the ego vehicle (meaning, ego vehicle is closing the gap to the lead vehicle).
661 if (vehicle_relative_speed > EPSILON_RELATIVE_SPEED) {
662 // If other vehicle is approaching lead vehicle and lead vehicle is further
663 // than follow_lead_distance 0 kmph -> 5m, 100 kmph -> 10m.
664 float follow_lead_distance = FOLLOW_LEAD_FACTOR * vehicle_speed + MIN_FOLLOW_LEAD_DISTANCE;
665 if (available_distance_margin > follow_lead_distance) {
666 // Then reduce the gap between the vehicles till FOLLOW_LEAD_DISTANCE
667 // by maintaining a relative speed of other_speed_along_heading
668 dynamic_target_velocity = other_speed_along_heading;
669 }
670 // If vehicle is approaching a lead vehicle and the lead vehicle is further
671 // than CRITICAL_BRAKING_MARGIN but closer than FOLLOW_LEAD_DISTANCE.
672 else if (available_distance_margin > CRITICAL_BRAKING_MARGIN) {
673 // Then follow the lead vehicle by acquiring it's speed along current heading.
674 dynamic_target_velocity = std::max(other_speed_along_heading, RELATIVE_APPROACH_SPEED);
675 } else {
676 // If lead vehicle closer than CRITICAL_BRAKING_MARGIN, initiate emergency stop.
677 collision_emergency_stop = true;
678 }
679 }
680 if (available_distance_margin < CRITICAL_BRAKING_MARGIN) {
681 collision_emergency_stop = true;
682 }
683 }
684
685 float max_gradual_velocity = PERC_MAX_SLOWDOWN * vehicle_speed;
686 if (dynamic_target_velocity < vehicle_speed - max_gradual_velocity) {
687 // Don't slow more than PERC_MAX_SLOWDOWN per frame.
688 dynamic_target_velocity = vehicle_speed - max_gradual_velocity;
689 }
690 dynamic_target_velocity = std::min(max_target_velocity, dynamic_target_velocity);
691
692 return {collision_emergency_stop, dynamic_target_velocity};
693}
694
696 const cg::Location vehicle_location,
697 const ActorId actor_id,
698 float max_target_velocity) {
699
700 auto const max_distance = LANDMARK_DETECTION_TIME * max_target_velocity;
701
702 float landmark_target_velocity = std::numeric_limits<float>::max();
703
704 auto all_landmarks = waypoint.GetWaypoint()->GetAllLandmarksInDistance(max_distance, false);
705
706 for (auto &landmark: all_landmarks) {
707
708 auto landmark_location = landmark->GetWaypoint()->GetTransform().location;
709 auto landmark_type = landmark->GetType();
710 auto distance = landmark_location.Distance(vehicle_location);
711
712 if (distance > max_distance) {
713 continue;
714 }
715
716 float minimum_velocity = max_target_velocity;
717 if (landmark_type == "1000001") { // Traffic light
718 minimum_velocity = TL_TARGET_VELOCITY;
719 } else if (landmark_type == "206") { // Stop
720 minimum_velocity = STOP_TARGET_VELOCITY;
721 } else if (landmark_type == "205") { // Yield
722 minimum_velocity = YIELD_TARGET_VELOCITY;
723 } else if (landmark_type == "274") { // Speed limit
724 float value = static_cast<float>(landmark->GetValue()) / 3.6f;
725 value = parameters.GetVehicleTargetVelocity(actor_id, value);
726 minimum_velocity = (value < max_target_velocity) ? value : max_target_velocity;
727 } else {
728 continue;
729 }
730
731 float v = std::max(((max_target_velocity - minimum_velocity) / max_distance) * distance + minimum_velocity, minimum_velocity);
732 landmark_target_velocity = std::min(landmark_target_velocity, v);
733 }
734
735 return landmark_target_velocity;
736}
737
739 const cg::Location vehicle_location,
740 float max_target_velocity) {
741
742 if (waypoint_buffer.size() < 3) {
743 return max_target_velocity;
744 }
745
746 // Start at the waypoint just passed: the arc being cornered on began behind
747 // the front one, and starting at the front makes the speed sawtooth each time
748 // a waypoint is consumed. With several predecessors there is no single arc.
749 std::vector<cg::Location> path;
750 path.reserve(waypoint_buffer.size() + 1u);
751 float path_start_offset = vehicle_location.Distance(waypoint_buffer.front()->GetLocation());
752
753 const std::vector<SimpleWaypointPtr> passed_waypoints =
754 waypoint_buffer.front()->GetPreviousWaypoint();
755 if (passed_waypoints.size() == 1u) {
756 path.push_back(passed_waypoints.front()->GetLocation());
757 path_start_offset = -vehicle_location.Distance(path.front());
758 }
759 for (const auto &waypoint : waypoint_buffer) {
760 path.push_back(waypoint->GetLocation());
761 }
762
763 return GetPathSpeedLimit(path, path_start_offset, CURVATURE_SAMPLE_SPACING,
765 max_target_velocity);
766}
767
769 pid_state_map.erase(actor_id);
770 teleportation_instance.erase(actor_id);
771 stuck_since.erase(actor_id);
772 recovery_state.erase(actor_id);
773}
774
776 pid_state_map.clear();
778 stuck_since.clear();
779 recovery_state.clear();
780}
781
782} // namespace traffic_manager
783} // namespace carla
float distance
#define SQUARE(a)
This file contains various constants used in traffic manager arranged into sensible namespaces for re...
Definition Constants.h:14
long long first
Definition LaneGraph.cpp:34
double elapsed_seconds
Simulated seconds elapsed since the beginning of the current episode.
Definition Timestamp.h:33
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,...
Vector3D GetForwardVector() const
const std::vector< float > urban_longitudinal_parameters
std::unordered_map< ActorId, RecoveryState > recovery_state
const LocalizationFrame & localization_frame
std::unordered_map< ActorId, double > stuck_since
const std::vector< float > highway_lateral_parameters
std::unordered_map< ActorId, cc::Timestamp > teleportation_instance
bool SafeAfterJunction(const LocalizationData &localization, const bool tl_hazard, const bool collision_emergency_stop)
void Update(const unsigned long index)
const std::vector< float > highway_longitudinal_parameters
float CalculateBaseOffset(const ActorId actor_id, const Buffer &waypoint_buffer, const bool is_target_junction, const uint64_t target_index)
const std::vector< ActorId > & vehicle_id_list
float GetLandmarkTargetVelocity(const SimpleWaypoint &waypoint, const cg::Location vehicle_location, const ActorId actor_id, float max_target_velocity)
float GetTurnTargetVelocity(const Buffer &waypoint_buffer, const cg::Location vehicle_location, float max_target_velocity)
std::unordered_map< ActorId, std::pair< float, bool > > & large_vehicles
bool IsWideTurnSideOccupied(const ActorId actor_id, const cg::Vector3D offset_direction, const float offset_magnitude)
Returns true when the lateral band that the wide-turn offset would swing actor_id into is occupied by...
void RemoveActor(const ActorId actor_id)
void SetCycleTimestamp(const cc::Timestamp &timestamp)
Pins one frame for the whole cycle; reading it per vehicle lets a frame land mid-cycle and skews the ...
const std::vector< float > urban_lateral_parameters
std::unordered_map< ActorId, StateEntry > pid_state_map
MotionPlanStage(const std::vector< ActorId > &vehicle_id_list, SimulationState &simulation_state, const Parameters &parameters, const BufferMap &buffer_map, TrackTraffic &track_traffic, const std::vector< float > &urban_longitudinal_parameters, const std::vector< float > &highway_longitudinal_parameters, const std::vector< float > &urban_lateral_parameters, const std::vector< float > &highway_lateral_parameters, const LocalizationFrame &localization_frame, const CollisionFrame &collision_frame, const TLFrame &tl_frame, ControlFrame &output_array, RandomGenerator &random_device, const LocalMapPtr &local_map, std::unordered_map< ActorId, std::pair< float, bool > > &large_vehicles)
std::pair< bool, float > CollisionHandling(const CollisionHazardData &collision_hazard, const bool tl_hazard, const cg::Vector3D ego_velocity, const cg::Vector3D ego_heading, const float max_target_velocity)
bool GetLargeVehicleWideTurn(const ActorId &actor_id) const
Method to query whether the wide-turn manoeuvre is enabled for a large vehicle (per-vehicle override,...
float GetLaneOffset(const ActorId &actor_id) const
Method to query lane offset for a vehicle.
float GetVehicleTargetVelocity(const ActorId &actor_id, const float speed_limit) const
Method to query target velocity for a vehicle.
float GetUpperBoundaryRespawnDormantVehicles() const
Method to retrieve maximum distance from hero vehicle when respawning vehicles.
float GetLowerBoundaryRespawnDormantVehicles() const
Method to retrieve minimum distance from hero vehicle when respawning vehicles.
bool GetRespawnDormantVehicles() const
Method to retrieve if we are automatically respawning vehicles.
bool GetSynchronousMode() const
Method to get synchronous mode.
This is a simple wrapper class on Carla's waypoint object.
WaypointPtr GetWaypoint() const
Returns a carla::shared_ptr to carla::waypoint.
This class holds the state of all the vehicles in the simlation.
cg::Location GetLocation(const ActorId actor_id) const
void UpdateKinematicHybridEndLocation(ActorId actor_id, cg::Location location)
cg::Vector3D GetHeading(const ActorId actor_id) const
void UpdateKinematicState(ActorId actor_id, KinematicState state)
float GetSpeedLimit(const ActorId actor_id) const
cg::Rotation GetRotation(const ActorId actor_id) const
cg::Vector3D GetVelocity(const ActorId actor_id) const
bool IsPhysicsEnabled(const ActorId actor_id) const
cg::Vector3D GetDimensions(const ActorId actor_id) const
float GetMaxSteerAngle(const ActorId actor_id) const
bool IsDormant(const ActorId actor_id) const
ActorIdSet GetPassingVehicles(uint64_t waypoint_id) const
ActorIdSet GetOverlappingVehicles(ActorId actor_id) const
bool IsGeoGridFree(const GeoGridId geogrid_id) const
void AddTakenGrid(const GeoGridId geogrid_id, const ActorId actor_id)
ActuationSignal RunStep(StateEntry present_state, StateEntry previous_state, const std::vector< float > &longitudinal_parameters, const std::vector< float > &lateral_parameters, const float vehicle_speed, const float control_dt=DT, const float lateral_pursuit_steer=0.0f, const float lateral_damping_scale=1.0f, const float steer_authority_correction=1.0f)
This function calculates the actuation signals based on the resent state change of the vehicle to min...
float RelativeVelocityDeviation(const float reference_velocity, const float vehicle_speed, const float target_velocity)
The target is exactly zero behind a stopped vehicle; the floor avoids a 0/0 NaN that the derivative t...
float ShapeReferenceVelocity(const float previous_reference, const float vehicle_speed, const float target_velocity, const float control_dt, const bool emergency_stop)
Reference speed that closes on target_velocity at COMFORT_ACCELERATION instead of stepping to it.
void SmoothActuation(const StateEntry &previous_state, const float control_dt, const float vehicle_speed, ActuationSignal &actuation_signal)
A zero pedal demand bypasses the deadband, so pedals rest exactly at zero and nothing here delays a d...
static const float MAX_STEER_AUTHORITY_CORRECTION
Definition Constants.h:252
static const float PURSUIT_CURVATURE_MARGIN
Definition Constants.h:279
static const float MAX_LATERAL_GAIN_SCALE
Definition Constants.h:263
float LargeVehicleJunctionOffsetProfile(float t, float max_offset, float max_offset_point, float inboard_scale)
Lateral offset profile for the large-vehicle wide turn, as a function of t, the fraction of the junct...
float GetPathSpeedLimit(const std::vector< cg::Location > &path, const float path_start_offset, const float sample_spacing, const float lateral_acceleration, const float braking_deceleration, const float max_speed)
Lowest sqrt(R * lateral_acceleration + 2 * braking_deceleration * distance) over every three consecut...
std::deque< std::shared_ptr< SimpleWaypoint > > Buffer
std::shared_ptr< InMemoryMap > LocalMapPtr
Definition ALSM.h:38
std::unordered_set< ActorId > ActorIdSet
std::unordered_map< carla::ActorId, Buffer > BufferMap
std::vector< SimpleWaypointPtr > NodeList
Definition InMemoryMap.h:47
std::vector< CollisionHazardData > CollisionFrame
std::vector< bool > TLFrame
std::pair< cg::Location, uint64_t > GetTargetData(const Buffer &waypoint_buffer, float target_distance, cg::Location vehicle_location)
Returns the location reached by walking target_distance along the waypoint buffer from vehicle_locati...
float GetThreePointCircleRadius(cg::Location first_location, cg::Location middle_location, cg::Location last_location)
Returns the radius of the circle defined by three points in the XY plane.
bool IsOffsetSideOccupied(cg::Location ego_location, cg::Vector3D ego_forward, cg::Vector3D offset_direction, float offset_magnitude, float lateral_clearance, float longitudinal_window, const std::vector< std::pair< cg::Location, float > > &neighbours)
Returns true when a neighbouring vehicle sits in the lateral band that the wide-turn offset would swi...
float LargeVehicleOffsetMagnitude(float vehicle_length, float reference_length, float gain, float offset_cap)
Wide-turn offset magnitude scaled by vehicle length: clamp(gain * (vehicle_length - reference_length)...
std::vector< carla::rpc::Command > ControlFrame
std::vector< LocalizationData > LocalizationFrame
std::shared_ptr< SimpleWaypoint > SimpleWaypointPtr
This file contains definitions of common data structures used in traffic manager.
auto Distance(Location loc) const
constexpr float SquaredLength() const
Vector3D MakeUnitVector(const float epsilon=2.0f *std::numeric_limits< float >::epsilon()) const
Return a unit-length copy of this vector.
Structure to hold the actuation signals.
Structure to hold the controller state.
float reference_velocity
Zero is a safe initial value: the shaping raises it to the current speed.