CARLA
Loading...
Searching...
No Matches
BasicAgent.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
7
12
13#include "carla/client/Actor.h"
15#include "carla/client/Map.h"
19#include "carla/client/World.h"
21#include "carla/geom/Location.h"
23#include "carla/geom/Vector3D.h"
24#include "carla/road/Lane.h"
27
28#include <boost/geometry.hpp>
29#include <boost/geometry/geometries/point_xy.hpp>
30#include <boost/geometry/geometries/polygon.hpp>
31
32#include <algorithm>
33#include <array>
34#include <cmath>
35#include <cstdint>
36#include <utility>
37
38namespace carla {
39namespace agents {
40namespace navigation {
41
42namespace bg = boost::geometry;
43using BgPoint = bg::model::d2::point_xy<double>;
44using BgPolygon = bg::model::polygon<BgPoint>;
45
48
49namespace {
50
51inline bool LaneChangeAllows(LaneChange lc, bool want_left) {
52 // bitwise: Right=0x01, Left=0x02, Both=0x03.
53 if (want_left) {
54 return lc == LaneChange::Left || lc == LaneChange::Both;
55 }
56 return lc == LaneChange::Right || lc == LaneChange::Both;
57}
58
59} // namespace
60
61// ---------------------------------------------------------------------------
62
64 float target_speed_kmh,
65 const Options &options,
68 : _vehicle(std::move(vehicle)),
69 _last_traffic_light(nullptr),
70 _ignore_traffic_lights(options.ignore_traffic_lights),
71 _ignore_stop_signs(options.ignore_stop_signs),
72 _ignore_vehicles(options.ignore_vehicles),
73 _use_bbs_detection(options.use_bbs_detection),
74 _target_speed(target_speed_kmh),
75 _sampling_resolution(options.sampling_resolution),
76 _base_tlight_threshold(options.base_tlight_threshold),
77 _base_vehicle_threshold(options.base_vehicle_threshold),
78 _speed_ratio(options.detection_speed_ratio),
79 _max_brake(options.max_brake),
80 _offset(options.offset) {
81
82 // World is a value type in LibCarla — wrap it in a SharedPtr to match the
83 // Python-side `self._world` reference semantics that BehaviorAgent expects.
85
86 _map = map ? map : _world->GetMap();
87
88 // Forward the (possibly customised) options into the local planner. The
89 // caller passes target_speed via the dedicated arg, so propagate it through
90 // a copy of the LocalPlanner::Options slice.
91 LocalPlanner::Options lp_opts = static_cast<const LocalPlanner::Options &>(options);
92 lp_opts.target_speed = target_speed_kmh;
93 _local_planner = std::make_unique<LocalPlanner>(_vehicle, lp_opts, _map);
94
95 if (grp) {
96 _global_planner = grp;
97 } else {
99 new GlobalRoutePlanner(_map, static_cast<double>(_sampling_resolution)));
100 }
101
102 // Cache the static traffic-light list (mirrors Python __init__).
103 _lights_list = _world->GetActors()->Filter("*traffic_light*");
104}
105
106// ---------------------------------------------------------------------------
107
109 c.throttle = 0.0f;
110 c.brake = _max_brake;
111 c.hand_brake = false;
112 return c;
113}
114
115void BasicAgent::SetTargetSpeed(float speed_kmh) {
116 _target_speed = speed_kmh;
117 _local_planner->SetSpeed(speed_kmh);
118}
119
121 _local_planner->FollowSpeedLimits(value);
122}
123
127
131
133 std::optional<geom::Location> start) {
134 geom::Location start_location;
135 bool clean_queue;
136 if (!start.has_value()) {
137 auto target_wpt = _local_planner->TargetWaypoint();
138 // LocalPlanner::_target_waypoint is seeded via Map::GetWaypoint(), which
139 // can return nullptr (e.g. vehicle starts off-road, or the planner has
140 // not been re-targeted since construction). Fall back to the vehicle's
141 // current location so a route can still be planned.
142 if (target_wpt) {
143 start_location = target_wpt->GetTransform().location;
144 } else {
145 start_location = _vehicle->GetLocation();
146 }
147 clean_queue = true;
148 } else {
149 start_location = *start; // Use the provided start location
150 clean_queue = false;
151 }
152
153 auto start_wp = _map->GetWaypoint(start_location);
154 auto end_wp = _map->GetWaypoint(end);
155
156 auto plan = TraceRoute(start_wp, end_wp);
157 _local_planner->SetGlobalPlan(plan, /*stop_waypoint_creation=*/true, clean_queue);
158}
159
160void BasicAgent::SetGlobalPlan(const std::vector<LocalPlanner::PlanItem> &plan,
161 bool stop_waypoint_creation,
162 bool clean_queue) {
163 _local_planner->SetGlobalPlan(plan, stop_waypoint_creation, clean_queue);
164}
165
166std::vector<LocalPlanner::PlanItem>
169 if (!start || !end) return {}; // Null check before dereference
170 const auto &start_loc = start->GetTransform().location;
171 const auto &end_loc = end->GetTransform().location;
172 return _global_planner->TraceRoute(start_loc, end_loc);
173}
174
176 bool hazard_detected = false;
177
178 // Pull all relevant actors from the world.
179 auto vehicle_list = _world->GetActors()->Filter("*vehicle*");
180
181 const float vehicle_speed_ms = GetSpeed(_vehicle) / 3.6f;
182
183 // Vehicles in front?
184 const float max_vehicle_distance =
185 _base_vehicle_threshold + _speed_ratio * vehicle_speed_ms;
186 auto v_obs = VehicleObstacleDetected(vehicle_list, max_vehicle_distance);
187 if (v_obs.affected) {
188 hazard_detected = true;
189 }
190
191 // Red light?
192 const float max_tlight_distance =
193 _base_tlight_threshold + _speed_ratio * vehicle_speed_ms;
194 auto tl_pair = AffectedByTrafficLight(_lights_list, max_tlight_distance);
195 if (tl_pair.first) {
196 hazard_detected = true;
197 }
198
199 rpc::VehicleControl control = _local_planner->RunStep();
200 if (hazard_detected) {
201 control = AddEmergencyStop(control);
202 }
203 return control;
204}
205
206bool BasicAgent::Done() const {
207 return _local_planner->Done();
208}
209
211void BasicAgent::IgnoreStopSigns(bool active) { _ignore_stop_signs = active; }
212void BasicAgent::IgnoreVehicles(bool active) { _ignore_vehicles = active; }
213
214void BasicAgent::SetOffset(float offset) {
215 _local_planner->SetOffset(offset);
216}
217
218void BasicAgent::LaneChange(const std::string &direction,
219 float same_lane_time,
220 float other_lane_time,
221 float lane_change_time) {
222 const float speed = _vehicle->GetVelocity().Length();
223 auto path = GenerateLaneChangePath(
224 _map->GetWaypoint(_vehicle->GetLocation()),
225 direction,
226 same_lane_time * speed,
227 other_lane_time * speed,
228 lane_change_time * speed,
229 /*check=*/false,
230 /*lane_changes=*/1,
232 if (path.empty()) {
233 // Match the Python warning behaviour: log and still call set_global_plan
234 // (Python falls through). Keeping silent here — no logger dep needed.
235 }
236 SetGlobalPlan(path);
237}
238
239// ---------------------------------------------------------------------------
240
241std::pair<bool, SharedPtr<client::TrafficLight>>
243 std::optional<float> max_distance_opt) {
245 return {false, nullptr};
246 }
247
248 if (!lights_list) {
249 lights_list = _world->GetActors()->Filter("*traffic_light*");
250 }
251
252 const float max_distance =
253 max_distance_opt.value_or(_base_tlight_threshold);
254
257 _last_traffic_light = nullptr;
258 } else {
259 return {true, _last_traffic_light};
260 }
261 }
262
263 const auto ego_location = _vehicle->GetLocation();
264 auto ego_wpt = _map->GetWaypoint(ego_location);
265 if (!ego_wpt) {
266 // Vehicle is not on a road waypoint (off-road, mid-air, etc.); no
267 // road-based traffic-light association can be computed.
268 return {false, nullptr};
269 }
270
271 for (auto actor : *lights_list) {
272 auto traffic_light =
273 std::dynamic_pointer_cast<client::TrafficLight>(actor);
274 if (!traffic_light) {
275 continue;
276 }
277
278 const uint32_t tl_id = static_cast<uint32_t>(traffic_light->GetId());
279
281 auto it = _lights_map.find(tl_id);
282 if (it != _lights_map.end()) {
283 trigger_wp = it->second;
284 } else {
285 const auto trigger_location =
286 GetTrafficLightTriggerLocation(traffic_light);
287 trigger_wp = _map->GetWaypoint(trigger_location);
288 _lights_map.emplace(tl_id, trigger_wp);
289 }
290
291 if (!trigger_wp) {
292 // Trigger box for this light isn't on the road network — it can't
293 // affect an on-road vehicle. Skip (and the null cache entry above
294 // makes subsequent lookups short-circuit at the same point).
295 continue;
296 }
297
298 if (trigger_wp->GetTransform().location.Distance(ego_location) > max_distance) {
299 continue;
300 }
301
302 if (trigger_wp->GetRoadId() != ego_wpt->GetRoadId()) {
303 continue;
304 }
305
306 const auto ve_dir = ego_wpt->GetTransform().GetForwardVector();
307 const auto wp_dir = trigger_wp->GetTransform().GetForwardVector();
308 const float dot_ve_wp = ve_dir.x * wp_dir.x + ve_dir.y * wp_dir.y + ve_dir.z * wp_dir.z;
309 if (dot_ve_wp < 0.0f) {
310 continue;
311 }
312
313 if (traffic_light->GetState() != rpc::TrafficLightState::Red) {
314 continue;
315 }
316
317 if (IsWithinDistance(trigger_wp->GetTransform(),
318 _vehicle->GetTransform(),
319 max_distance,
320 std::make_pair(0.0f, 90.0f))) {
321 _last_traffic_light = traffic_light;
322 return {true, traffic_light};
323 }
324 }
325
326 return {false, nullptr};
327}
328
329// ---------------------------------------------------------------------------
330
332 SharedPtr<client::ActorList> vehicle_list,
333 std::optional<float> max_distance_opt,
334 float up_angle_th,
335 float low_angle_th,
336 int lane_offset) {
337
338 VehicleObstacle result;
339 if (_ignore_vehicles) {
340 return result;
341 }
342
343 if (!vehicle_list) {
344 vehicle_list = _world->GetActors()->Filter("*vehicle*");
345 }
346
347 const float max_distance =
348 max_distance_opt.value_or(_base_vehicle_threshold);
349
350 const auto ego_transform = _vehicle->GetTransform();
351 const auto ego_location = ego_transform.location;
352 auto ego_wpt = _map->GetWaypoint(ego_location);
353
354 // Sign correction for left-driving roads.
355 if (ego_wpt->GetLaneId() < 0 && lane_offset != 0) {
356 lane_offset *= -1;
357 }
358
359 // Front-of-ego transform (push location forward by half the bbox).
360 geom::Transform ego_front_transform = ego_transform;
361 {
362 const auto fv = ego_transform.GetForwardVector();
363 const float ext_x = _vehicle->GetBoundingBox().extent.x;
364 ego_front_transform.location += geom::Location(ext_x * fv.x,
365 ext_x * fv.y,
366 ext_x * fv.z);
367 }
368
369 const float ego_extent_y = _vehicle->GetBoundingBox().extent.y;
370 const bool opposite_invasion =
371 std::abs(_offset) + ego_extent_y > ego_wpt->GetLaneWidth() / 2.0;
372 const bool use_bbs =
373 _use_bbs_detection || opposite_invasion || ego_wpt->IsJunction();
374
375 // ---- Build the route polygon (use_bbs path).
376 auto build_route_polygon = [&]() -> std::optional<BgPolygon> {
377 std::vector<std::array<double, 3>> route_bb;
378 const float r_ext = ego_extent_y + _offset;
379 const float l_ext = -ego_extent_y + _offset;
380
381 auto append_pair = [&](const geom::Transform &t) {
382 const auto rv = t.GetRightVector();
383 const auto loc = t.location;
384 const geom::Location p1(loc.x + r_ext * rv.x, loc.y + r_ext * rv.y, loc.z);
385 const geom::Location p2(loc.x + l_ext * rv.x, loc.y + l_ext * rv.y, loc.z);
386 route_bb.push_back({{p1.x, p1.y, p1.z}});
387 route_bb.push_back({{p2.x, p2.y, p2.z}});
388 };
389
390 append_pair(ego_transform);
391
392 for (const auto &item : _local_planner->GetPlan()) {
393 const auto &wp = item.first;
394 if (ego_location.Distance(wp->GetTransform().location) > max_distance) {
395 break;
396 }
397 append_pair(wp->GetTransform());
398 }
399
400 if (route_bb.size() < 3) {
401 return std::nullopt;
402 }
403
404 BgPolygon poly;
405 for (const auto &v : route_bb) {
406 bg::append(poly.outer(), BgPoint(v[0], v[1]));
407 }
408 bg::correct(poly);
409 return poly;
410 };
411
412 std::optional<BgPolygon> route_polygon = build_route_polygon();
413
414 for (auto actor : *vehicle_list) {
415 if (!actor) continue;
416 if (actor->GetId() == _vehicle->GetId()) {
417 continue;
418 }
419
420 const auto target_transform = actor->GetTransform();
421 if (target_transform.location.Distance(ego_location) > max_distance) {
422 continue;
423 }
424
425 auto target_wpt = _map->GetWaypoint(target_transform.location,
426 /*project_to_road=*/true,
427 static_cast<int32_t>(LaneType::Any));
428
429 // Junction / bbox path.
430 if ((use_bbs || target_wpt->IsJunction()) && route_polygon.has_value()) {
431 const auto &target_bb = actor->GetBoundingBox();
432 const auto vertices = target_bb.GetWorldVertices(actor->GetTransform());
433
434 BgPolygon target_poly;
435 for (const auto &v : vertices) {
436 bg::append(target_poly.outer(), BgPoint(v.x, v.y));
437 }
438 bg::correct(target_poly);
439
440 if (bg::intersects(*route_polygon, target_poly)) {
441 result.affected = true;
442 result.actor = actor;
443 result.distance = ComputeDistance(actor->GetLocation(), ego_location);
444 return result;
445 }
446 } else {
447 // Simplified plan-waypoint approach.
448 if (target_wpt->GetRoadId() != ego_wpt->GetRoadId() ||
449 target_wpt->GetLaneId() != ego_wpt->GetLaneId() + lane_offset) {
450 auto next_pair = _local_planner->GetIncomingWaypointAndDirection(3);
451 auto next_wp = next_pair.first;
452 if (!next_wp) {
453 continue;
454 }
455 if (target_wpt->GetRoadId() != next_wp->GetRoadId() ||
456 target_wpt->GetLaneId() != next_wp->GetLaneId() + lane_offset) {
457 continue;
458 }
459 }
460
461 const auto target_forward = target_transform.GetForwardVector();
462 const float target_extent_x = actor->GetBoundingBox().extent.x;
463 geom::Transform target_rear_transform = target_transform;
464 target_rear_transform.location -= geom::Location(
465 target_extent_x * target_forward.x,
466 target_extent_x * target_forward.y,
467 0.0f);
468
469 if (IsWithinDistance(target_rear_transform,
470 ego_front_transform,
471 max_distance,
472 std::make_pair(low_angle_th, up_angle_th))) {
473 result.affected = true;
474 result.actor = actor;
475 result.distance = ComputeDistance(target_transform.location,
476 ego_transform.location);
477 return result;
478 }
479 }
480 }
481
482 return result;
483}
484
485// ---------------------------------------------------------------------------
486
487std::vector<LocalPlanner::PlanItem> BasicAgent::GenerateLaneChangePath(
489 const std::string &direction,
490 float distance_same_lane,
491 float distance_other_lane,
492 float lane_change_distance,
493 bool check,
494 int lane_changes,
495 float step_distance) {
496
497 distance_same_lane = std::max(distance_same_lane, 0.1f);
498 distance_other_lane = std::max(distance_other_lane, 0.1f);
499 lane_change_distance = std::max(lane_change_distance, 0.1f);
500
501 std::vector<LocalPlanner::PlanItem> plan;
502 plan.emplace_back(waypoint, RoadOption::LaneFollow);
503
505
506 // Same lane stretch.
507 {
508 float distance = 0.0f;
509 while (distance < distance_same_lane) {
510 auto next_wps = plan.back().first->GetNext(step_distance);
511 if (next_wps.empty()) return {};
512 auto next_wp = next_wps.front();
513 distance += static_cast<float>(
514 next_wp->GetTransform().location.Distance(
515 plan.back().first->GetTransform().location));
516 plan.emplace_back(next_wp, RoadOption::LaneFollow);
517 }
518 }
519
520 bool want_left;
521 if (direction == "left") {
523 want_left = true;
524 } else if (direction == "right") {
526 want_left = false;
527 } else {
528 return {};
529 }
530
531 int lane_changes_done = 0;
532 const float per_change_distance =
533 lane_change_distance / static_cast<float>(std::max(lane_changes, 1));
534
535 // Lane change steps.
536 while (lane_changes_done < lane_changes) {
537 auto next_wps = plan.back().first->GetNext(per_change_distance);
538 if (next_wps.empty()) return {};
539 auto next_wp = next_wps.front();
540
542 if (want_left) {
543 if (check && !LaneChangeAllows(next_wp->GetLaneChange(), /*want_left=*/true)) {
544 return {};
545 }
546 side_wp = next_wp->GetLeft();
547 } else {
548 if (check && !LaneChangeAllows(next_wp->GetLaneChange(), /*want_left=*/false)) {
549 return {};
550 }
551 side_wp = next_wp->GetRight();
552 }
553
554 if (!side_wp || side_wp->GetType() != LaneType::Driving) {
555 return {};
556 }
557
558 plan.emplace_back(side_wp, option);
559 ++lane_changes_done;
560 }
561
562 // Other lane stretch.
563 {
564 float distance = 0.0f;
565 while (distance < distance_other_lane) {
566 auto next_wps = plan.back().first->GetNext(step_distance);
567 if (next_wps.empty()) return {};
568 auto next_wp = next_wps.front();
569 distance += static_cast<float>(
570 next_wp->GetTransform().location.Distance(
571 plan.back().first->GetTransform().location));
572 plan.emplace_back(next_wp, RoadOption::LaneFollow);
573 }
574 }
575
576 return plan;
577}
578
579} // namespace navigation
580} // namespace agents
581} // namespace carla
float distance
SharedPtr< client::Map > _map
Definition BasicAgent.h:151
std::pair< bool, SharedPtr< client::TrafficLight > > AffectedByTrafficLight(SharedPtr< client::ActorList > lights=nullptr, std::optional< float > max_distance=std::nullopt)
Returns (affected, light) similar to Python _affected_by_traffic_light.
VehicleObstacle VehicleObstacleDetected(SharedPtr< client::ActorList > vehicles=nullptr, std::optional< float > max_distance=std::nullopt, float up_angle_th=90.0f, float low_angle_th=0.0f, int lane_offset=0)
Mirrors Python _vehicle_obstacle_detected.
rpc::VehicleControl AddEmergencyStop(rpc::VehicleControl c) const
Overwrites throttle/brake to perform an emergency stop.
void IgnoreTrafficLights(bool active=true)
SharedPtr< client::World > _world
Definition BasicAgent.h:150
BasicAgent(SharedPtr< client::Vehicle > vehicle, float target_speed_kmh=20.0f, const Options &options=DefaultOptions(), SharedPtr< client::Map > map=nullptr, SharedPtr< GlobalRoutePlanner > grp=nullptr)
SharedPtr< client::ActorList > _lights_list
Definition BasicAgent.h:155
void IgnoreVehicles(bool active=true)
void SetGlobalPlan(const std::vector< LocalPlanner::PlanItem > &plan, bool stop_waypoint_creation=true, bool clean_queue=true)
void FollowSpeedLimits(bool value=true)
void LaneChange(const std::string &direction, float same_lane_time=0.0f, float other_lane_time=0.0f, float lane_change_time=2.0f)
Replaces the current plan with one that performs a lane change.
SharedPtr< client::TrafficLight > _last_traffic_light
Definition BasicAgent.h:152
std::vector< LocalPlanner::PlanItem > TraceRoute(SharedPtr< client::Waypoint > start, SharedPtr< client::Waypoint > end)
SharedPtr< client::Vehicle > _vehicle
Definition BasicAgent.h:149
std::vector< LocalPlanner::PlanItem > GenerateLaneChangePath(SharedPtr< client::Waypoint > waypoint, const std::string &direction="left", float distance_same_lane=10.0f, float distance_other_lane=25.0f, float lane_change_distance=25.0f, bool check=true, int lane_changes=1, float step_distance=2.0f)
Mirrors Python _generate_lane_change_path.
virtual rpc::VehicleControl RunStep()
std::unordered_map< uint32_t, SharedPtr< client::Waypoint > > _lights_map
Definition BasicAgent.h:156
SharedPtr< GlobalRoutePlanner > _global_planner
Definition BasicAgent.h:154
std::unique_ptr< LocalPlanner > _local_planner
Definition BasicAgent.h:153
void SetDestination(const geom::Location &end, std::optional< geom::Location > start=std::nullopt)
Computes a route from start (or the current target waypoint) to end and forwards it to the local plan...
void IgnoreStopSigns(bool active=true)
GlobalRoutePlanner * GetGlobalPlanner()
C++ port of agents.navigation.global_route_planner.GlobalRoutePlanner.
LocalPlanner reproduces agents.navigation.local_planner.LocalPlanner.
A rigid transform in CARLA's / Unreal's left-handed frame: x forward, y right, z up,...
Vector3D GetForwardVector() const
LaneType
Can be used as flags.
Definition road/Lane.h:29
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
bg::model::d2::point_xy< double > BgPoint
geom::Location GetTrafficLightTriggerLocation(SharedPtr< client::TrafficLight > traffic_light)
Compute the world-space trigger location of a traffic light, mirroring the helper in agents....
Definition Misc.cpp:45
float GetSpeed(SharedPtr< client::Actor > actor)
Compute speed of an actor in km/h.
Definition Misc.cpp:37
bg::model::polygon< BgPoint > BgPolygon
This file contains definitions of common data structures used in traffic manager.
std::shared_ptr< T > SharedPtr
Definition Memory.h:15
Aggregated configuration. Mirrors the opt_dict in local_planner.py.
LaneChange
Can be used as flags.
Definition LaneMarking.h:50