CARLA
Loading...
Searching...
No Matches
trafficmanager/PIDController.h
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#pragma once
8
9#include <algorithm>
10#include <cmath>
11
14
15namespace carla {
16namespace traffic_manager {
17
18namespace chr = std::chrono;
19
20using namespace constants::PID;
21
22using TimeInstance = chr::time_point<chr::system_clock, chr::nanoseconds>;
23
24namespace PID {
25
26/// Reference speed that closes on @a target_velocity at COMFORT_ACCELERATION
27/// instead of stepping to it. After an emergency stop the previous reference
28/// is meaningless, so it restarts from the vehicle speed.
29[[nodiscard]] inline float ShapeReferenceVelocity(
30 const float previous_reference,
31 const float vehicle_speed,
32 const float target_velocity,
33 const float control_dt,
34 const bool emergency_stop) {
35
36 float reference{emergency_stop ? vehicle_speed : previous_reference};
37
38 // Same period bounds as the controller, so a long frame cannot over-advance.
39 reference = std::min(
40 reference + COMFORT_ACCELERATION * std::clamp(control_dt, MIN_CONTROL_DT, MAX_CONTROL_DT),
41 target_velocity);
42 // Never below the current speed (the ramp must not brake) nor above the
43 // target (it must not cancel a requested deceleration).
44 reference = std::max(reference, std::min(vehicle_speed, target_velocity));
45 return std::min(reference, vehicle_speed + REFERENCE_LEAD_FRACTION * target_velocity);
46}
47
48/// The target is exactly zero behind a stopped vehicle; the floor avoids a 0/0
49/// NaN that the derivative term would turn into a NaN steering command.
50[[nodiscard]] inline float RelativeVelocityDeviation(
51 const float reference_velocity,
52 const float vehicle_speed,
53 const float target_velocity) {
54
55 return (reference_velocity - vehicle_speed) /
56 std::max(target_velocity, constants::MotionPlan::EPSILON_RELATIVE_SPEED);
57}
58
59/// A zero pedal demand bypasses the deadband, so pedals rest exactly at zero
60/// and nothing here delays a deceleration.
61inline void SmoothActuation(
62 const StateEntry &previous_state,
63 const float control_dt,
64 const float vehicle_speed,
65 ActuationSignal &actuation_signal) {
66
67 const float speed_ratio{
68 STEER_DEADBAND_REF_SPEED / std::max(vehicle_speed, STEER_DEADBAND_REF_SPEED)};
69 const float steer_deadband{STEER_DEADBAND * speed_ratio * speed_ratio};
70
71 if (vehicle_speed < LAUNCH_RAMP_SPEED) {
72 const float dt{std::clamp(control_dt, MIN_CONTROL_DT, MAX_CONTROL_DT)};
73 actuation_signal.throttle = std::min(
74 actuation_signal.throttle,
75 previous_state.throttle + MAX_LAUNCH_THROTTLE_RISE_RATE * dt);
76 }
77
78 if (actuation_signal.throttle > 0.0f &&
79 std::abs(actuation_signal.throttle - previous_state.throttle) < THROTTLE_DEADBAND) {
80 actuation_signal.throttle = previous_state.throttle;
81 }
82 if (actuation_signal.brake > 0.0f &&
83 std::abs(actuation_signal.brake - previous_state.brake) < BRAKE_DEADBAND) {
84 actuation_signal.brake = previous_state.brake;
85 }
86 if (std::abs(actuation_signal.steer - previous_state.steer) < steer_deadband) {
87 actuation_signal.steer = previous_state.steer;
88 }
89}
90
91/// This function calculates the actuation signals based on the resent state
92/// change of the vehicle to minimize PID error.
93///
94/// control_dt is the measured simulation-time period between the two states.
95/// The gains are tuned at the nominal DT (sync-mode 0.05 s); in asynchronous
96/// mode the real period is one server frame, which under render load (e.g. a
97/// path-traced rt_lens sensor) stretches to 2-4x nominal. Left uncompensated
98/// that halves the loop's phase margin and the lateral PID goes from damped
99/// to divergent weaving (measured on Town12: steer_std 0.11 at 19 fps ->
100/// 0.71 with full-lock swings at 11 fps), which then trips the stuck/K-turn
101/// recovery into alternating full-lock "jerking".
102/// lateral_pursuit_steer replaces the linearized proportional term of the
103/// lateral loop: the caller computes the geometric pure-pursuit command
104/// atan(wheelbase * margin * 2 sin(alpha) / d) / wheel_lock from the
105/// vehicle's own steering geometry, which is exact for every wheelbase and
106/// lock where a fixed linear gain is only correct for the car-class fleet it
107/// was tuned on. The integral term keeps its original form and units.
108/// lateral_damping_scale rescales the derivative term with the loop
109/// bandwidth (pursuit-distance schedule x steering-authority correction).
110/// steer_authority_correction (>= 1) scales the STEER_LIMIT_GAIN envelope:
111/// the envelope is a physical curvature/lateral-acceleration guard, so it
112/// must cap curvature, not raw normalized command.
113inline ActuationSignal RunStep(StateEntry present_state,
114 StateEntry previous_state,
115 const std::vector<float> &longitudinal_parameters,
116 const std::vector<float> &lateral_parameters,
117 const float vehicle_speed,
118 const float control_dt = DT,
119 const float lateral_pursuit_steer = 0.0f,
120 const float lateral_damping_scale = 1.0f,
121 const float steer_authority_correction = 1.0f) {
122
123 const float dt = std::max(MIN_CONTROL_DT, std::min(control_dt, MAX_CONTROL_DT));
124 const float inv_dt = 1.0f / dt;
125 // Sample-rate compensation for the lateral loop. The dominant effect of a
126 // longer control period is added loop delay: the crossover frequency the
127 // gains were tuned for no longer fits inside the slower sampling, so the
128 // loop must be slowed down proportionally. Never scale gains UP for fast
129 // ticks -- the tuning point is the ceiling.
130 const float gain_scale = std::min(1.0f, DT * inv_dt);
131
132 // Longitudinal PID calculation. The speed loop is first-order and far less
133 // delay-sensitive than the lateral loop, so only the integral/derivative
134 // discretization uses the measured period; the proportional gain stays.
135 const float expr_v =
136 longitudinal_parameters[0] * present_state.velocity_deviation +
137 longitudinal_parameters[1] * (present_state.velocity_deviation + previous_state.velocity_deviation) * dt +
138 longitudinal_parameters[2] * (present_state.velocity_deviation - previous_state.velocity_deviation) * inv_dt;
139
140 float throttle;
141 float brake;
142
143 if (expr_v > 0.0f) {
144 throttle = std::min(expr_v, MAX_THROTTLE);
145 brake = 0.0f;
146 } else {
147 throttle = 0.0f;
148 brake = std::min(std::abs(expr_v), MAX_BRAKE);
149 }
150
151 // Lateral PID calculation. The derivative input is clamped to the maximum
152 // physically plausible heading-error rate: a vehicle yaws well under
153 // 180 deg/s (MAX_DEVIATION_RATE in normalised units), so rates above that
154 // can only come from the target point jumping discontinuously (lane change
155 // start, buffer reseed). An unclamped derivative turns such jumps into a
156 // one-tick full-lock steer spike that swings the vehicle across the lane.
157 const float max_deviation_delta = MAX_DEVIATION_RATE * dt;
158 float deviation_delta = present_state.angular_deviation - previous_state.angular_deviation;
159 deviation_delta = std::max(-max_deviation_delta, std::min(deviation_delta, max_deviation_delta));
160 float steer = gain_scale * (
161 lateral_pursuit_steer +
162 lateral_parameters[1] * (present_state.angular_deviation + previous_state.angular_deviation) * dt +
163 lateral_damping_scale * lateral_parameters[2] * deviation_delta * inv_dt);
164
165 // Steering slew limit, applied as a rate so the physical steering speed is
166 // independent of the tick rate (a fixed per-tick step would triple the
167 // allowed rate at 60 fps and strangle it at 7 fps).
168 const float max_steering_diff = MAX_STEERING_RATE * dt;
169 steer = std::max(previous_state.steer - max_steering_diff, std::min(steer, previous_state.steer + max_steering_diff));
170 steer = std::max(-MAX_STEERING, std::min(steer, MAX_STEERING));
171
172 // Speed-scaled steering envelope (lateral-acceleration cap). See
173 // STEER_LIMIT_GAIN in Constants.h. The cap is a physical curvature bound,
174 // so it scales with the vehicle's steering-authority correction: without
175 // it, long vehicles saturate below the curvature a junction turn needs
176 // (measured: 45% of truck junction ticks pinned at the car-anchored limit)
177 // and sweep into the adjacent lane.
178 const float speed_sq = std::max(vehicle_speed * vehicle_speed, 1.0f);
179 const float steer_limit = std::min(
180 MAX_STEERING, steer_authority_correction * STEER_LIMIT_GAIN / speed_sq);
181 steer = std::max(-steer_limit, std::min(steer, steer_limit));
182
183 return ActuationSignal{throttle, brake, steer};
184}
185
186} // namespace PID
187} // namespace traffic_manager
188} // namespace carla
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 COMFORT_ACCELERATION
Definition Constants.h:323
static constexpr float MIN_CONTROL_DT
Definition Constants.h:291
static const float REFERENCE_LEAD_FRACTION
Definition Constants.h:327
static constexpr float MAX_LAUNCH_THROTTLE_RISE_RATE
Definition Constants.h:309
static constexpr float THROTTLE_DEADBAND
Definition Constants.h:310
static const float STEER_DEADBAND_REF_SPEED
Definition Constants.h:319
chr::time_point< chr::system_clock, chr::nanoseconds > TimeInstance
This file contains definitions of common data structures used in traffic manager.
Structure to hold the actuation signals.
Structure to hold the controller state.