use std::collections::VecDeque;
#[derive(Debug, Clone, Copy)]
pub struct PIDParams {
pub k_p: f32,
pub k_i: f32,
pub k_d: f32,
}
impl Default for PIDParams {
fn default() -> Self {
Self {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
}
}
}
#[derive(Debug)]
pub struct PIDLongitudinalController {
params: PIDParams,
dt: f32,
error_buffer: VecDeque<f32>,
max_buffer_size: usize,
}
impl PIDLongitudinalController {
pub fn new(params: PIDParams, dt: f32) -> Self {
Self {
params,
dt,
error_buffer: VecDeque::with_capacity(10),
max_buffer_size: 10,
}
}
pub fn change_parameters(&mut self, params: PIDParams) {
self.params = params;
}
pub fn run_step(&mut self, target_speed: f32, current_speed: f32) -> f32 {
let error = target_speed - current_speed;
self.error_buffer.push_back(error);
if self.error_buffer.len() > self.max_buffer_size {
self.error_buffer.pop_front();
}
let integral: f32 = self.error_buffer.iter().sum();
let derivative = if self.error_buffer.len() >= 2 {
let len = self.error_buffer.len();
self.error_buffer[len - 1] - self.error_buffer[len - 2]
} else {
0.0
};
self.params.k_p * error
+ self.params.k_i * integral * self.dt
+ self.params.k_d * derivative / self.dt
}
}
#[derive(Debug)]
pub struct PIDLateralController {
params: PIDParams,
dt: f32,
error_buffer: VecDeque<f32>,
max_buffer_size: usize,
}
impl PIDLateralController {
pub fn new(params: PIDParams, dt: f32) -> Self {
Self {
params,
dt,
error_buffer: VecDeque::with_capacity(10),
max_buffer_size: 10,
}
}
pub fn change_parameters(&mut self, params: PIDParams) {
self.params = params;
}
pub fn run_step(
&mut self,
waypoint_x: f32,
waypoint_y: f32,
vehicle_x: f32,
vehicle_y: f32,
vehicle_yaw: f32,
) -> f32 {
use std::f32::consts::PI;
let yaw_rad = vehicle_yaw * PI / 180.0;
let forward_x = yaw_rad.cos();
let forward_y = yaw_rad.sin();
let waypoint_vec_x = waypoint_x - vehicle_x;
let waypoint_vec_y = waypoint_y - vehicle_y;
let waypoint_vec_len =
(waypoint_vec_x * waypoint_vec_x + waypoint_vec_y * waypoint_vec_y).sqrt();
let forward_vec_len = (forward_x * forward_x + forward_y * forward_y).sqrt();
let wv_linalg = waypoint_vec_len * forward_vec_len;
let error = if wv_linalg < 1e-6 {
0.0
} else {
let dot_product = waypoint_vec_x * forward_x + waypoint_vec_y * forward_y;
let normalized_dot = (dot_product / wv_linalg).clamp(-1.0, 1.0);
let mut angle = normalized_dot.acos();
let cross_z = forward_x * waypoint_vec_y - forward_y * waypoint_vec_x;
if cross_z < 0.0 {
angle *= -1.0;
}
angle
};
self.error_buffer.push_back(error);
if self.error_buffer.len() > self.max_buffer_size {
self.error_buffer.pop_front();
}
let integral: f32 = self.error_buffer.iter().sum();
let derivative = if self.error_buffer.len() >= 2 {
let len = self.error_buffer.len();
self.error_buffer[len - 1] - self.error_buffer[len - 2]
} else {
0.0
};
let steering = self.params.k_p * error
+ self.params.k_i * integral * self.dt
+ self.params.k_d * derivative / self.dt;
steering.clamp(-1.0, 1.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_longitudinal_controller_proportional() {
let params = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let mut controller = PIDLongitudinalController::new(params, 0.05);
let acceleration = controller.run_step(30.0, 20.0);
assert!((acceleration - 10.0).abs() < 0.001);
let acceleration = controller.run_step(20.0, 30.0);
assert!((acceleration + 10.0).abs() < 0.001);
}
#[test]
fn test_longitudinal_controller_integral() {
let params = PIDParams {
k_p: 0.0,
k_i: 1.0,
k_d: 0.0,
};
let dt = 0.05;
let mut controller = PIDLongitudinalController::new(params, dt);
let accel1 = controller.run_step(30.0, 20.0);
assert!((accel1 - 10.0 * dt).abs() < 0.001);
let accel2 = controller.run_step(30.0, 20.0);
assert!((accel2 - 20.0 * dt).abs() < 0.001);
}
#[test]
fn test_lateral_controller_aligned() {
let params = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let mut controller = PIDLateralController::new(params, 0.05);
let steering = controller.run_step(10.0, 0.0, 0.0, 0.0, 0.0);
assert!(steering.abs() < 0.001);
}
#[test]
fn test_lateral_controller_left_deviation() {
let params = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let mut controller = PIDLateralController::new(params, 0.05);
let steering = controller.run_step(10.0, 5.0, 0.0, 0.0, 0.0);
assert!(steering > 0.0);
}
#[test]
fn test_lateral_controller_right_deviation() {
let params = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let mut controller = PIDLateralController::new(params, 0.05);
let steering = controller.run_step(10.0, -5.0, 0.0, 0.0, 0.0);
assert!(steering < 0.0);
}
#[test]
fn test_lateral_controller_clamping() {
let params = PIDParams {
k_p: 100.0, k_i: 0.0,
k_d: 0.0,
};
let mut controller = PIDLateralController::new(params, 0.05);
let steering = controller.run_step(10.0, 100.0, 0.0, 0.0, 0.0);
assert!((steering - 1.0).abs() < 0.001);
let steering = controller.run_step(10.0, -100.0, 0.0, 0.0, 0.0);
assert!((steering + 1.0).abs() < 0.001); }
#[test]
fn test_change_parameters() {
let params1 = PIDParams {
k_p: 1.0,
k_i: 0.0,
k_d: 0.0,
};
let mut controller = PIDLongitudinalController::new(params1, 0.05);
let accel1 = controller.run_step(30.0, 20.0);
assert!((accel1 - 10.0).abs() < 0.001);
let params2 = PIDParams {
k_p: 2.0,
k_i: 0.0,
k_d: 0.0,
};
controller.change_parameters(params2);
controller.error_buffer.clear();
let accel2 = controller.run_step(30.0, 20.0);
assert!((accel2 - 20.0).abs() < 0.001); }
}