use super::pid::{PIDLateralController, PIDLongitudinalController, PIDParams};
use crate::{agents::tools::get_speed, client::ActorBase, geom::Location, rpc::VehicleControl};
#[derive(Debug)]
pub struct VehiclePIDController {
lateral_controller: PIDLateralController,
longitudinal_controller: PIDLongitudinalController,
offset: f32,
max_throttle: f32,
max_brake: f32,
max_steering: f32,
past_steering: f32,
}
impl VehiclePIDController {
pub fn new(
lateral_params: PIDParams,
longitudinal_params: PIDParams,
offset: f32,
max_throttle: f32,
max_brake: f32,
max_steering: f32,
dt: f32,
) -> Self {
Self {
lateral_controller: PIDLateralController::new(lateral_params, dt),
longitudinal_controller: PIDLongitudinalController::new(longitudinal_params, dt),
offset,
max_throttle,
max_brake,
max_steering,
past_steering: 0.0,
}
}
pub fn change_lateral_pid(&mut self, params: PIDParams) {
self.lateral_controller.change_parameters(params);
}
pub fn change_longitudinal_pid(&mut self, params: PIDParams) {
self.longitudinal_controller.change_parameters(params);
}
pub fn set_offset(&mut self, offset: f32) {
self.offset = offset;
}
pub fn run_step<T: ActorBase>(
&mut self,
vehicle: &T,
target_speed: f32,
waypoint: &Location,
) -> crate::error::Result<VehicleControl> {
let vehicle_transform = vehicle.transform()?;
let current_speed = get_speed(vehicle);
let waypoint_adjusted = if self.offset.abs() > 0.001 {
use std::f32::consts::PI;
let yaw_rad = vehicle_transform.rotation.yaw * PI / 180.0;
let right_x = -yaw_rad.sin();
let right_y = yaw_rad.cos();
Location {
x: waypoint.x + right_x * self.offset,
y: waypoint.y + right_y * self.offset,
z: waypoint.z,
}
} else {
*waypoint
};
let mut steering = self.lateral_controller.run_step(
waypoint_adjusted.x,
waypoint_adjusted.y,
vehicle_transform.location.x,
vehicle_transform.location.y,
vehicle_transform.rotation.yaw,
);
let steering_diff = steering - self.past_steering;
if steering_diff.abs() > 0.1 {
steering = self.past_steering + 0.1 * steering_diff.signum();
}
self.past_steering = steering;
steering = steering.clamp(-self.max_steering, self.max_steering);
let acceleration = self
.longitudinal_controller
.run_step(target_speed, current_speed);
let (throttle, brake) = if acceleration >= 0.0 {
let throttle = acceleration.min(self.max_throttle);
(throttle, 0.0)
} else {
let brake = (-acceleration).min(self.max_brake);
(0.0, brake)
};
Ok(VehicleControl {
throttle,
steer: steering,
brake,
hand_brake: false,
reverse: false,
manual_gear_shift: false,
gear: 0,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_controller_creation() {
let lateral_params = PIDParams {
k_p: 1.95,
k_i: 1.4,
k_d: 0.01,
};
let longitudinal_params = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let controller = VehiclePIDController::new(
lateral_params,
longitudinal_params,
0.0,
0.75,
0.3,
0.8,
1.0 / 20.0,
);
assert_eq!(controller.offset, 0.0);
assert_eq!(controller.max_throttle, 0.75);
assert_eq!(controller.max_brake, 0.3);
assert_eq!(controller.max_steering, 0.8);
}
#[test]
fn test_parameter_changes() {
let lateral_params = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let longitudinal_params = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let mut controller = VehiclePIDController::new(
lateral_params,
longitudinal_params,
0.0,
0.75,
0.3,
0.8,
1.0 / 20.0,
);
let new_lateral = PIDParams {
k_p: 2.0,
k_i: 0.5,
k_d: 0.1,
};
controller.change_lateral_pid(new_lateral);
let new_longitudinal = PIDParams {
k_p: 1.5,
k_i: 0.0,
k_d: 0.2,
};
controller.change_longitudinal_pid(new_longitudinal);
controller.set_offset(1.5);
assert_eq!(controller.offset, 1.5);
}
}