use nalgebra::{self as na, vector}; use std::{any::Any, f32}; use crate::drone::controller::DroneController; pub use crate::drone::controller::JoystickInput; use crate::drone::MotorCharacteristics; const PROPORTIONAL_ONLY: bool = false; #[derive(Default)] pub struct PIDControllerState { last_time: f32, current_time: f32, last_rotation: na::Unit>, current_rotation: na::Unit>, angular_velocity: na::Vector3, last_error: na::Vector3, error_sum: na::Vector3, pub log_time: Vec, pub log_target: Vec>, pub log_current: Vec>, pub log_error: Vec>, } impl PIDControllerState { pub fn dt(&self) -> f32 { return self.current_time - self.last_time; } } pub struct PIDController { input: JoystickInput, pub target_rate: f32, pub proportional_multiplier: na::Vector3, pub integral_multiplier: na::Vector3, pub diferential_multiplier: na::Vector3, pub multiply_mode: bool, pub state: PIDControllerState, } impl Default for PIDController { fn default() -> Self { Self { input: JoystickInput::default(), target_rate: f32::consts::PI, state: Default::default(), multiply_mode: false, proportional_multiplier: vector![0.13, 3.0, 0.13], integral_multiplier: vector![0.0, 0.0, 0.0], diferential_multiplier: vector![0.001, 0.01, 0.001], } } } impl PIDController { /// Save log buffers to a CSV file pub fn save_logs_to_csv(&self, path: &str) -> std::io::Result<()> { use std::fs::File; use std::io::Write; let mut file = File::create(path)?; writeln!( file, "time,target_x,target_y,target_z,current_x,current_y,current_z,error_x,error_y,error_z" )?; for i in 0..self.state.log_time.len() { let t = self.state.log_time[i]; let tg = self.state.log_target[i]; let cur = self.state.log_current[i]; let err = self.state.log_error[i]; writeln!( file, "{},{},{},{},{},{},{},{},{},{}", t, tg.x, tg.y, tg.z, cur.x, cur.y, cur.z, err.x, err.y, err.z )?; } Ok(()) } pub fn set_input(&mut self, input: JoystickInput) { self.input = input; } pub fn get_angular_velocity(&self) -> na::Vector3 { return self.state.angular_velocity; } pub fn get_desired_angular_velocity(&self) -> na::Vector3 { let coords = na::Vector3::new( self.input.roll_input, self.input.yaw_input, self.input.pitch_input, ) * self.target_rate; return coords; } } impl DroneController for PIDController { fn set_rotation(&mut self, rotation: nalgebra::Unit>) { self.state.last_rotation = self.state.current_rotation; self.state.current_rotation = rotation; } fn set_angular_velocity(&mut self, angvel: nalgebra::Vector3) { self.state.angular_velocity = angvel; } fn set_time(&mut self, time: f32) { self.state.last_time = self.state.current_time; self.state.current_time = time; } fn set_motor_characteristics(&self, _motor_characteristics: &MotorCharacteristics) {} fn get_motor_throttles(&mut self) -> [f32; 4] { let rot = self.state.current_rotation; let current = rot.inverse().transform_vector(&self.get_angular_velocity()); let target = self.get_desired_angular_velocity(); let error: na::Vector3 = target - current; let throttle = self.input.throttle_input; self.state.log_time.push(self.state.current_time); self.state.log_target.push(target); self.state.log_current.push(current); self.state.log_error.push(error); let error_dif: na::Vector3 = error - self.state.last_error; let forces_to_apply = match PROPORTIONAL_ONLY { true => { vector![ error.x * self.proportional_multiplier.x, error.y * self.proportional_multiplier.y, error.z * self.proportional_multiplier.z ] } false => { vector![ error.x * self.proportional_multiplier.x + self.state.error_sum.x * self.integral_multiplier.x + error_dif.x * self.diferential_multiplier.x, error.y * self.proportional_multiplier.y + self.state.error_sum.y * self.integral_multiplier.y + error_dif.y * self.diferential_multiplier.y, error.z * self.proportional_multiplier.z + self.state.error_sum.z * self.integral_multiplier.z + error_dif.z * self.diferential_multiplier.z, ] } }; self.state.error_sum += error; self.state.last_error = error; let roll = forces_to_apply.z; let pitch = forces_to_apply.x; let yaw = forces_to_apply.y; let mut motors: [f32; 4] = match self.multiply_mode { true => { let mut t = [ 1.0 + yaw - pitch + roll, 1.0 - yaw - pitch - roll, 1.0 + yaw + pitch - roll, 1.0 - yaw + pitch + roll, ]; for m in t.iter_mut() { *m *= self.input.throttle_input; } t } false => { let t = [ throttle + yaw - pitch + roll, throttle - yaw - pitch - roll, throttle + yaw + pitch - roll, throttle - yaw + pitch + roll, ]; t } }; let max = motors .iter() .copied() .max_by(|a, b| a.total_cmp(b)) .unwrap_or(0.0); if max > 1.0 { for v in motors.iter_mut() { *v /= max; } } return motors; } fn as_any(&self) -> &dyn Any { self } fn as_mut_any(&mut self) -> &mut dyn Any { self } }