#![allow(dead_code)] use nalgebra::{self as na, vector}; use std::{any::Any, f32}; use crate::drone::controller::DroneController; use crate::drone::JoystickInput; use crate::config::*; pub struct DroneState { pub rotation: na::UnitQuaternion, pub angular_velocity: na::Vector3, } pub enum ControllerModule { Rate { processor: PidProcessor, max_rate: f32, }, Angle { processor: PidProcessor, max_angle: f32, }, } impl ControllerModule { /// Takes the current setpoint (from joystick or previous layer) and /// returns the command for the next layer. pub fn process( &mut self, setpoint: na::Vector3, state: &DroneState, dt: f32, is_first_layer: bool, ) -> na::Vector3 { match self { ControllerModule::Angle { processor, max_angle, } => { // Setpoint is -1.0..1.0, scale it to target Radians let target_angles = setpoint * *max_angle; let (r, p, y) = state.rotation.euler_angles(); let current_angles = na::vector![r, y, p]; // Output of Angle PID = Desired Angular Velocity processor.update(target_angles, current_angles, dt) } ControllerModule::Rate { processor, max_rate, } => { // If Rate is the start of the chain (Acro mode), scale the joystick. // If it's the second layer, the setpoint is already a velocity from the Angle layer. let target_velocity = if is_first_layer { setpoint * *max_rate } else { setpoint }; let rot = state.rotation; let current = rot.inverse().transform_vector(&state.angular_velocity); // Output of Rate PID = Desired Torque/Correction Force processor.update(target_velocity, current, dt) } } } } pub struct StackedController { modules: Vec, config: SimulationConfig, // State drone_state: DroneState, input: JoystickInput, last_time: f32, current_time: f32, } impl StackedController { pub fn set_input(&mut self, inp: JoystickInput) { self.input = inp; } pub fn new(config: SimulationConfig) -> Self { let mut modules = Vec::new(); for layer in &config.layers { match layer { LayerConfig::Angle { pid, max_angle } => { modules.push(ControllerModule::Angle { processor: PidProcessor::new(pid), max_angle: *max_angle, }); } LayerConfig::Rate { pid, max_rate } => { modules.push(ControllerModule::Rate { processor: PidProcessor::new(pid), max_rate: *max_rate, }); } } } Self { modules, config, input: JoystickInput::default(), drone_state: DroneState { rotation: na::UnitQuaternion::identity(), angular_velocity: na::Vector3::zeros(), }, last_time: 0.0, current_time: 0.0, } } } impl DroneController for StackedController { fn set_rotation(&mut self, rot: na::UnitQuaternion) { self.drone_state.rotation = rot; } fn set_angular_velocity(&mut self, vel: na::Vector3) { self.drone_state.angular_velocity = vel; } fn set_time(&mut self, t: f32) { self.last_time = self.current_time; self.current_time = t; } fn get_motor_throttles(&mut self) -> [f32; 4] { let dt = (self.current_time - self.last_time).max(0.0001); // Input normalized -1.0 to 1.0 from joystick let mut setpoint = vector![ self.input.roll_input, self.input.yaw_input, self.input.pitch_input, ]; // --- CASCADED PROCESSING --- // If Layer 0 is Angle, setpoint becomes Desired Rate. // If Layer 1 is Rate, it takes that Desired Rate and outputs Torque. // for (i, module) in self.modules.iter_mut().enumerate() { let is_first_layer = i == 0; setpoint = module.process(setpoint, &self.drone_state, dt, is_first_layer); } // --- MOTOR MIXER --- let mut motors = [0.0; 4]; for i in 0..4 { let map = self.config.motor_map[i]; motors[i] = self.input.throttle_input + (map[0] * setpoint.x) // Roll + (map[1] * setpoint.y) // Yaw + (map[2] * setpoint.z); // Pitch } // Clamp while keeping relative values 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; } } motors } fn as_any(&self) -> &dyn Any { self } fn as_mut_any(&mut self) -> &mut dyn Any { self } } pub struct PidProcessor { kp: na::Vector3, ki: na::Vector3, kd: na::Vector3, integral: na::Vector3, last_error: na::Vector3, } impl PidProcessor { fn new(c: &PidConfig) -> Self { Self { kp: c.p_vec(), ki: c.i_vec(), kd: c.d_vec(), integral: na::Vector3::zeros(), last_error: na::Vector3::zeros(), } } fn update( &mut self, target: na::Vector3, current: na::Vector3, dt: f32, ) -> na::Vector3 { let error = target - current; self.integral += error * dt; let derivative = (error - self.last_error) / dt; self.last_error = error; error.component_mul(&self.kp) + self.integral.component_mul(&self.ki) + derivative.component_mul(&self.kd) } }