CARLA
Loading...
Searching...
No Matches
LocalPlanner.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
9#include "carla/client/Map.h"
12#include "carla/client/World.h"
13#include "carla/geom/Location.h"
15
16#include <algorithm>
17#include <cmath>
18#include <iostream>
19
20namespace carla {
21namespace agents {
22namespace navigation {
23
24namespace {
25
26constexpr float kKmhToMs = 1.0f / 3.6f;
27
28/// Compute the topological connection between @a current and @a next_wp,
29/// matching `_compute_connection` in local_planner.py.
30RoadOption ComputeConnection(
31 SharedPtr<client::Waypoint> current,
32 SharedPtr<client::Waypoint> next_wp,
33 float threshold = 35.0f) {
34 // Python uses `% 360` and `% 180` on yaws to wrap them. Replicate with
35 // `std::fmod` and a positive-fixup, since C++ `%` doesn't accept floats.
36 auto wrap = [](float v, float mod) {
37 float r = std::fmod(v, mod);
38 if (r < 0.0f) r += mod;
39 return r;
40 };
41
42 const float n = wrap(next_wp->GetTransform().rotation.yaw, 360.0f);
43 const float c = wrap(current->GetTransform().rotation.yaw, 360.0f);
44 const float diff = wrap(n - c, 180.0f);
45
46 if (diff < threshold || diff > (180.0f - threshold)) {
48 }
49 if (diff > 90.0f) {
50 return RoadOption::Left;
51 }
52 return RoadOption::Right;
53}
54
55/// Mirror of `_retrieve_options` in local_planner.py.
56std::vector<RoadOption> RetrieveOptions(
57 const std::vector<SharedPtr<client::Waypoint>> &candidates,
58 SharedPtr<client::Waypoint> current) {
59 std::vector<RoadOption> options;
60 options.reserve(candidates.size());
61 for (const auto &next_wp : candidates) {
62 // The Python code probes 3 m further to disambiguate intersections that
63 // share an entry waypoint.
64 auto next_next = next_wp->GetNext(3.0);
65 if (next_next.empty()) {
66 options.push_back(ComputeConnection(current, next_wp));
67 } else {
68 options.push_back(ComputeConnection(current, next_next.front()));
69 }
70 }
71 return options;
72}
73
74} // namespace
75
76// =========================================================================
77// LocalPlanner
78// =========================================================================
79
82 const Options &opt,
84 : _vehicle(std::move(vehicle)),
85 _map(std::move(map)),
86 _dt(opt.dt),
87 _target_speed(opt.target_speed),
88 _sampling_radius(opt.sampling_radius),
89 _lateral_args(opt.lateral),
90 _longitudinal_args(opt.longitudinal),
91 _max_throttle(opt.max_throttle),
92 _max_brake(opt.max_brake),
93 _max_steering(opt.max_steering),
94 _offset(opt.offset),
95 _base_min_distance(opt.base_min_distance),
96 _distance_ratio(opt.distance_ratio),
97 _follow_speed_limits(opt.follow_speed_limits),
98 _rng(std::random_device{}()) {
99 // If no map was supplied, pull it from the vehicle's world (mirrors the
100 // `map_inst` fallback in local_planner.py).
101 if (!_map && _vehicle) {
102 _map = _vehicle->GetWorld().GetMap();
103 }
105}
106
108 _vehicle_controller = std::make_unique<VehiclePIDController>(
109 _vehicle,
112 _offset,
116
117 if (_vehicle && _map) {
118 auto current_waypoint = _map->GetWaypoint(_vehicle->GetLocation());
119 _target_waypoint = current_waypoint;
122 }
123}
124
126 _vehicle.reset();
127}
128
129void LocalPlanner::SetSpeed(float speed_kmh) {
131 std::cout << "WARNING: The max speed is currently set to follow the speed"
132 " limits. Use 'follow_speed_limits' to deactivate this\n";
133 }
134 _target_speed = speed_kmh;
135}
136
138 _follow_speed_limits = value;
139}
140
142 // Bound k by remaining queue capacity, like Python's
143 // `available_entries = self._waypoints_queue.maxlen - len(...)`.
144 const std::size_t available = (_waypoints_queue.size() < _max_queue_size)
146 : 0;
147 k = std::min(k, available);
148
149 for (std::size_t i = 0; i < k; ++i) {
150 if (_waypoints_queue.empty()) break;
151 auto last_waypoint = _waypoints_queue.back().first;
152 if (!last_waypoint) break;
153 auto next_waypoints = last_waypoint->GetNext(_sampling_radius);
154
155 if (next_waypoints.empty()) {
156 break;
157 }
158
159 SharedPtr<client::Waypoint> next_waypoint;
160 RoadOption road_option;
161 if (next_waypoints.size() == 1) {
162 next_waypoint = next_waypoints.front();
163 road_option = RoadOption::LaneFollow;
164 } else {
165 // Random choice among the available branches.
166 auto road_options_list = RetrieveOptions(next_waypoints, last_waypoint);
167 std::uniform_int_distribution<std::size_t> dist(
168 0, road_options_list.size() - 1);
169 const std::size_t idx = dist(_rng);
170 road_option = road_options_list[idx];
171 // Python pulls `next_waypoints[road_options_list.index(road_option)]`,
172 // i.e. the *first* candidate that shares the chosen RoadOption. With a
173 // pre-randomised idx that's just the same as picking idx directly,
174 // because there's a 1:1 alignment between the lists (the first option
175 // matching the random pick is the random pick itself).
176 next_waypoint = next_waypoints[idx];
177 }
178
179 _waypoints_queue.emplace_back(next_waypoint, road_option);
180 }
181}
182
184 const std::vector<PlanItem> &plan,
185 bool stop_waypoint_creation,
186 bool clean_queue) {
187 if (clean_queue) {
188 _waypoints_queue.clear();
189 }
190
191 // The Python implementation grows the deque's `maxlen` whenever the new
192 // plan exceeds it; we keep a soft cap and just raise it as needed.
193 const std::size_t new_plan_length =
194 plan.size() + _waypoints_queue.size();
195 if (new_plan_length > _max_queue_size) {
196 _max_queue_size = new_plan_length;
197 }
198
199 for (const auto &elem : plan) {
200 _waypoints_queue.push_back(elem);
201 }
202 _stop_waypoint_creation = stop_waypoint_creation;
203}
204
205void LocalPlanner::SetOffset(float offset) {
206 _offset = offset;
208 _vehicle_controller->SetOffset(offset);
209 }
210}
211
214 _target_speed = _vehicle->GetSpeedLimit();
215 }
216
220 }
221
222 // Purge waypoints we've already passed.
223 if (_vehicle) {
224 const auto veh_location = _vehicle->GetLocation();
225 const float vehicle_speed_ms = GetSpeed(_vehicle) * kKmhToMs;
226 const float min_distance =
227 _base_min_distance + _distance_ratio * vehicle_speed_ms;
228
229 std::size_t num_removed = 0;
230 for (const auto &item : _waypoints_queue) {
231 // Don't remove the very last waypoint until we're almost on top of it
232 // (mirrors Python's `min_distance = 1` in the same situation).
233 const float threshold =
234 (_waypoints_queue.size() - num_removed == 1) ? 1.0f : min_distance;
235
236 const auto &wp_loc = item.first->GetTransform().location;
237 if (veh_location.Distance(wp_loc) < threshold) {
238 ++num_removed;
239 } else {
240 break;
241 }
242 }
243 for (std::size_t i = 0; i < num_removed; ++i) {
244 _waypoints_queue.pop_front();
245 }
246 }
247
248 rpc::VehicleControl control;
249 if (_waypoints_queue.empty()) {
250 control.steer = 0.0f;
251 control.throttle = 0.0f;
252 control.brake = 1.0f;
253 control.hand_brake = false;
254 control.manual_gear_shift = false;
255 } else {
256 _target_waypoint = _waypoints_queue.front().first;
257 _target_road_option = _waypoints_queue.front().second;
259 }
260
261 if (debug && _vehicle && _target_waypoint) {
262 auto world = std::make_shared<client::World>(_vehicle->GetWorld());
263 DrawWaypoints(world, {_target_waypoint}, 1.0f);
264 }
265
266 return control;
267}
268
269std::pair<SharedPtr<client::Waypoint>, RoadOption>
271 if (steps >= 0
272 && static_cast<std::size_t>(steps) < _waypoints_queue.size()) {
273 return _waypoints_queue[static_cast<std::size_t>(steps)];
274 }
275 if (!_waypoints_queue.empty()) {
276 return _waypoints_queue.back();
277 }
278 return {nullptr, RoadOption::Void};
279}
280
281const std::deque<LocalPlanner::PlanItem> &LocalPlanner::GetPlan() const {
282 return _waypoints_queue;
283}
284
285bool LocalPlanner::Done() const {
286 return _waypoints_queue.empty();
287}
288
292
296
297} // namespace navigation
298} // namespace agents
299} // namespace carla
void ComputeNextWaypoints(std::size_t k)
Append up to k waypoints to the back of the queue, branching with random choice on intersections (mir...
VehiclePIDController::LongitudinalArgs _longitudinal_args
std::deque< PlanItem > _waypoints_queue
SharedPtr< client::Vehicle > _vehicle
SharedPtr< client::Waypoint > _target_waypoint
rpc::VehicleControl RunStep(bool debug=false)
Drive one tick. Returns the VehicleControl to apply.
RoadOption TargetRoadOption() const
Currently active target RoadOption (mirrors target_road_option).
void InitController()
Build the VehiclePIDController and seed the queue with the vehicle's current waypoint.
void FollowSpeedLimits(bool value=true)
Toggle automatic following of road speed limits.
SharedPtr< client::Waypoint > TargetWaypoint() const
Currently active target waypoint (mirrors target_waypoint).
void SetSpeed(float speed_kmh)
Override the target speed in km/h.
VehiclePIDController::LateralArgs _lateral_args
bool Done() const
True when the queue is empty.
LocalPlanner(SharedPtr< client::Vehicle > vehicle, const Options &opt=DefaultOptions(), SharedPtr< client::Map > map=nullptr)
void SetGlobalPlan(const std::vector< PlanItem > &plan, bool stop_waypoint_creation=true, bool clean_queue=true)
Replace (or append to) the waypoint queue.
const std::deque< PlanItem > & GetPlan() const
Read-only view of the queued plan.
std::pair< SharedPtr< client::Waypoint >, RoadOption > GetIncomingWaypointAndDirection(int steps=3) const
Peek steps ahead in the queue, falling back to the last entry if the queue is too short.
std::unique_ptr< VehiclePIDController > _vehicle_controller
void SetOffset(float offset)
Apply a lateral offset to the underlying PID controller.
void ResetVehicle()
Drop the vehicle reference (mirrors reset_vehicle).
RoadOption
Topological direction emitted by the planners.
void DrawWaypoints(SharedPtr< client::World > world, const std::vector< SharedPtr< client::Waypoint > > &waypoints, float z)
Draw the supplied waypoints in the world for a fixed lifetime.
Definition Misc.cpp:159
float GetSpeed(SharedPtr< client::Actor > actor)
Compute speed of an actor in km/h.
Definition Misc.cpp:37
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.