CARLA
Loading...
Searching...
No Matches
TrafficManagerGeometry.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
8
9#include <algorithm>
10#include <array>
11#include <cmath>
12#include <limits>
13
15
16namespace carla {
17namespace traffic_manager {
18
21
23 cg::Location first_location,
24 cg::Location middle_location,
25 cg::Location last_location) {
26
27 // Work in a frame local to the middle point. The previous circumcenter
28 // formulation differenced products of squared world coordinates
29 // (x1*x1 - x3*x3, ...): at |coords| ~200 m those products are ~4e4 with
30 // float32's ~7 significant digits, so curvature-scale differences cancel
31 // catastrophically and the returned radius is noise. Translating to a
32 // local frame removes the large common offset before any squaring.
33 const float ax{first_location.x - middle_location.x};
34 const float ay{first_location.y - middle_location.y};
35 const float bx{last_location.x - middle_location.x};
36 const float by{last_location.y - middle_location.y};
37
38 // Circumradius R = (|AB| * |CB| * |AC|) / (4 * area), with
39 // 2 * area = |ax * by - ay * bx|.
40 const float cross{ax * by - ay * bx};
41 if (std::abs(cross) <= EPSILON) {
42 return std::numeric_limits<float>::max(); // collinear points
43 }
44 const float side_a{std::sqrt(ax * ax + ay * ay)};
45 const float side_b{std::sqrt(bx * bx + by * by)};
46 const float side_c{std::sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by))};
47 return (side_a * side_b * side_c) / (2.0f * std::abs(cross));
48}
49
50std::pair<cg::Location, uint64_t> InterpolateBufferAt(
51 const std::vector<cg::Location> &waypoint_locations,
52 float target_distance,
53 cg::Location vehicle_location) {
54
55 if (waypoint_locations.empty()) {
56 return std::make_pair(vehicle_location, 0u);
57 }
58
59 if (waypoint_locations.size() == 1u) {
60 return std::make_pair(waypoint_locations.front(), 0u);
61 }
62
63 const float target_square_distance{target_distance * target_distance};
64
65 // Walk every waypoint and find the first one whose distance is greater than
66 // or equal to target_distance. That waypoint is the upper bracket; the one
67 // before it is the lower bracket.
68 size_t closest_index{0u};
69 size_t farthest_index{0u};
70 bool found_bracket{false};
71 for (size_t i = 0u; i < waypoint_locations.size(); ++i) {
72 const float close_dist_square{vehicle_location.DistanceSquared(waypoint_locations.at(i))};
73
74 if (close_dist_square < target_square_distance) {
75 closest_index = i;
76 } else {
77 farthest_index = i;
78 found_bracket = true;
79 break;
80 }
81 }
82
83 // Target distance is beyond every waypoint in the buffer: clamp to the
84 // last waypoint instead of extrapolating off the front segment.
85 if (!found_bracket) {
86 const size_t last_index{waypoint_locations.size() - 1u};
87 return std::make_pair(waypoint_locations.at(last_index), last_index);
88 }
89
90 // Target distance is shorter than the distance to the first waypoint:
91 // bracket with the first segment so the interpolation produces a point
92 // ahead of the vehicle along the planned path.
93 if (closest_index == 0u && farthest_index == 0u) {
94 farthest_index = 1u;
95 }
96
97 const cg::Location target_close_location{waypoint_locations.at(closest_index)};
98 const cg::Location target_far_location{waypoint_locations.at(farthest_index)};
99 const float target_close_distance{vehicle_location.Distance(target_close_location)};
100 const float target_far_distance{vehicle_location.Distance(target_far_location)};
101
102 // Guard against the bracketing pair coinciding (zero denominator).
103 const float span{target_far_distance - target_close_distance};
104 if (std::abs(span) <= EPSILON) {
105 return std::make_pair(target_close_location, closest_index);
106 }
107
108 const float t{(target_distance - target_close_distance) / span};
109 const cg::Location target_location{
110 target_close_location.x + (target_far_location.x - target_close_location.x) * t,
111 target_close_location.y + (target_far_location.y - target_close_location.y) * t,
112 target_close_location.z + (target_far_location.z - target_close_location.z) * t};
113
114 return std::make_pair(target_location, closest_index);
115}
116
117std::pair<cg::Location, uint64_t> GetTargetData(
118 const Buffer &waypoint_buffer,
119 float target_distance,
120 cg::Location vehicle_location) {
121
122 std::vector<cg::Location> locations;
123 locations.reserve(waypoint_buffer.size());
124 for (const SimpleWaypointPtr &waypoint : waypoint_buffer) {
125 locations.emplace_back(waypoint->GetLocation());
126 }
127 return InterpolateBufferAt(locations, target_distance, vehicle_location);
128}
129
131 const std::vector<cg::Location> &path,
132 const float path_start_offset,
133 const float sample_spacing,
134 const float lateral_acceleration,
135 const float braking_deceleration,
136 const float max_speed) {
137
138 if (path.size() < 3u || sample_spacing <= 0.0f) {
139 return max_speed;
140 }
141
142 float speed_limit = max_speed;
143 std::array<cg::Location, 3> samples{path.front(), path.front(), path.front()};
144 uint32_t sample_count = 1u;
145 cg::Location previous_location = path.front();
146 float travelled = 0.0f;
147 float next_sample_at = sample_spacing;
148
149 for (const cg::Location &location : path) {
150 const float segment = location.Distance(previous_location);
151 const float segment_start = travelled;
152 travelled += segment;
153
154 while (segment > EPSILON && travelled >= next_sample_at) {
155 const float fraction = (next_sample_at - segment_start) / segment;
156 samples = {samples[1], samples[2],
157 cg::Location{previous_location.x + (location.x - previous_location.x) * fraction,
158 previous_location.y + (location.y - previous_location.y) * fraction,
159 previous_location.z + (location.z - previous_location.z) * fraction}};
160 // Braking distance is measured to the first of the three samples.
161 const float distance_to_arc =
162 path_start_offset + next_sample_at - 2.0f * sample_spacing;
163 next_sample_at += sample_spacing;
164
165 if (++sample_count < 3u) {
166 continue;
167 }
168 const float radius = GetThreePointCircleRadius(samples[0], samples[1], samples[2]);
169 speed_limit = std::min(
170 speed_limit,
171 std::sqrt(radius * lateral_acceleration +
172 2.0f * braking_deceleration * std::max(distance_to_arc, 0.0f)));
173 }
174 previous_location = location;
175 }
176
177 return speed_limit;
178}
179
181 float t,
182 float max_offset,
183 float max_offset_point,
184 float inboard_scale) {
185
186 t = std::clamp(t, 0.0f, 1.0f);
187
188 // From +offset (inboard, near the exit) to -offset (outboard, near the
189 // entry); entry and exit stay at 0 for a smooth transition. The vehicle
190 // opens up at the entry to perform a wider turn, then returns along a
191 // straighter trajectory.
192 float offset{0.0f};
193 if (t < max_offset_point) {
194 const float a{t / max_offset_point};
195 offset = max_offset * 0.5f * (1.0f - std::cos(PI * a));
196 } else if (t < 1.0f - max_offset_point) {
197 const float a{(t - max_offset_point) / (1.0f - 2.0f * max_offset_point)};
198 offset = max_offset * std::cos(PI * a);
199 } else {
200 const float a{(t - (1.0f - max_offset_point)) / max_offset_point};
201 offset = -max_offset * 0.5f * (1.0f + std::cos(PI * a));
202 }
203
204 // Attenuate the inboard (positive) excursion — the exit "cut-in" that drives
205 // a long vehicle's rear into the inside shoulder — while preserving the
206 // outboard (negative) swing that opens the turn up.
207 if (offset > 0.0f) {
208 offset *= inboard_scale;
209 }
210 return offset;
211}
212
214 float vehicle_length,
215 float reference_length,
216 float gain,
217 float offset_cap) {
218
219 const float scaled{gain * (vehicle_length - reference_length)};
220 return std::clamp(scaled, 0.0f, offset_cap);
221}
222
224 cg::Location ego_location,
225 cg::Vector3D ego_forward,
226 cg::Vector3D offset_direction,
227 float offset_magnitude,
228 float lateral_clearance,
229 float longitudinal_window,
230 const std::vector<std::pair<cg::Location, float>> &neighbours) {
231
232 for (const auto &[location, radius] : neighbours) {
233 const cg::Vector3D relative{
234 location.x - ego_location.x,
235 location.y - ego_location.y,
236 0.0f};
237 const float lateral{relative.x * offset_direction.x + relative.y * offset_direction.y};
238 const float longitudinal{relative.x * ego_forward.x + relative.y * ego_forward.y};
239
240 const bool on_offset_side{
241 lateral > -radius
242 && lateral <= offset_magnitude + lateral_clearance + radius};
243 const bool alongside{std::abs(longitudinal) <= longitudinal_window + radius};
244
245 if (on_offset_side && alongside) {
246 return true;
247 }
248 }
249 return false;
250}
251
252} // namespace traffic_manager
253} // namespace carla
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::pair< cg::Location, uint64_t > InterpolateBufferAt(const std::vector< cg::Location > &waypoint_locations, float target_distance, cg::Location vehicle_location)
Locations-only variant of GetTargetData.
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::shared_ptr< SimpleWaypoint > SimpleWaypointPtr
This file contains definitions of common data structures used in traffic manager.
constexpr auto DistanceSquared(Location loc) const
auto Distance(Location loc) const