embedded_flight_control/attitude/
multi_copter.rs

1use super::AttitudeController;
2use crate::PID;
3use embedded_flight_core::MotorOutput;
4use nalgebra::Vector3;
5
6/// ```
7/// use embedded_flight_control::MultiCopterAttitudeController;
8///
9/// let mut controller = MultiCopterAttitudeController::default();
10///
11/// // Input the desired attitude and angular velocity with the current attitude
12/// controller.attitude_controller.input(
13///     Quaternion::default(),
14///     Vector3::default(),
15///     Quaternion::default(),
16/// );
17///
18/// // Output the control to the motors with the current gyroscope data.
19/// let output = controller.motor_output(Vector3::default(), 1);
20/// dbg!(output);
21/// ```
22pub struct MultiCopterAttitudeController {
23    // The angular velocity (in radians per second) in the body frame.
24    pub roll_rate: PID,
25    pub pitch_rate: PID,
26    pub yaw_rate: PID,
27    pub attitude_controller: AttitudeController,
28}
29
30impl Default for MultiCopterAttitudeController {
31    fn default() -> Self {
32        const AC_ATC_MULTI_RATE_RP_P: f32 = 0.135;
33        const AC_ATC_MULTI_RATE_RP_I: f32 = 0.135;
34        const AC_ATC_MULTI_RATE_RP_D: f32 = 0.0036;
35        const AC_ATC_MULTI_RATE_RP_IMAX: f32 = 0.5;
36        const AC_ATC_MULTI_RATE_RP_FILT_HZ: f32 = 20.;
37        const AC_ATC_MULTI_RATE_YAW_P: f32 = 0.180;
38        const AC_ATC_MULTI_RATE_YAW_I: f32 = 0.018;
39        const AC_ATC_MULTI_RATE_YAW_D: f32 = 0.;
40        const AC_ATC_MULTI_RATE_YAW_IMAX: f32 = 0.5;
41        const AC_ATC_MULTI_RATE_YAW_FILT_HZ: f32 = 2.5;
42
43        // 400 hz
44        let dt = 0.0025;
45
46        Self {
47            roll_rate: PID::new(
48                AC_ATC_MULTI_RATE_RP_P,
49                AC_ATC_MULTI_RATE_RP_I,
50                AC_ATC_MULTI_RATE_RP_D,
51                0.,
52                AC_ATC_MULTI_RATE_RP_IMAX,
53                AC_ATC_MULTI_RATE_RP_FILT_HZ,
54                0.,
55                AC_ATC_MULTI_RATE_RP_FILT_HZ,
56                dt,
57            ),
58            pitch_rate: PID::new(
59                AC_ATC_MULTI_RATE_RP_P,
60                AC_ATC_MULTI_RATE_RP_I,
61                AC_ATC_MULTI_RATE_RP_D,
62                0.,
63                AC_ATC_MULTI_RATE_RP_IMAX,
64                AC_ATC_MULTI_RATE_RP_FILT_HZ,
65                0.,
66                AC_ATC_MULTI_RATE_RP_FILT_HZ,
67                dt,
68            ),
69            yaw_rate: PID::new(
70                AC_ATC_MULTI_RATE_YAW_P,
71                AC_ATC_MULTI_RATE_YAW_I,
72                AC_ATC_MULTI_RATE_YAW_D,
73                0.,
74                AC_ATC_MULTI_RATE_YAW_IMAX,
75                AC_ATC_MULTI_RATE_RP_FILT_HZ,
76                AC_ATC_MULTI_RATE_YAW_FILT_HZ,
77                0.,
78                dt,
79            ),
80            attitude_controller: Default::default(),
81        }
82    }
83}
84
85impl MultiCopterAttitudeController {
86    /// Calculate the motor output of the controller (in -1 ~ +1) .
87    pub fn motor_output(&mut self, gyro: Vector3<f32>, now_ms: u32) -> MotorOutput<f32> {
88        self.motor_output_with_limit(gyro, now_ms, [false; 3])
89    }
90
91    /// Calculate the motor output of the controller (in -1 ~ +1) with an optional limit for roll, pitch, and yaw.
92    pub fn motor_output_with_limit(
93        &mut self,
94        gyro: Vector3<f32>,
95        now_ms: u32,
96        limit: [bool; 3],
97    ) -> MotorOutput<f32> {
98        // Move throttle vs attitude mixing towards desired.
99        // Called from here because this is conveniently called on every iteration
100        self.update_throttle_rpy_mix();
101
102        self.attitude_controller.ang_vel_body += self.attitude_controller.sysid_ang_vel_body;
103
104        let roll = self.roll_rate.update(
105            self.attitude_controller.ang_vel_body[0],
106            gyro[0],
107            limit[0],
108            now_ms,
109        ) + self.attitude_controller.actuator_sysid[0];
110        let pitch = self.roll_rate.update(
111            self.attitude_controller.ang_vel_body[1],
112            gyro[1],
113            limit[1],
114            now_ms,
115        ) + self.attitude_controller.actuator_sysid[1];
116        let yaw = self.roll_rate.update(
117            self.attitude_controller.ang_vel_body[2],
118            gyro[2],
119            limit[2],
120            now_ms,
121        ) + self.attitude_controller.actuator_sysid[2];
122
123        let roll_ff = self.roll_rate.feed_forward();
124        let pitch_ff = self.pitch_rate.feed_forward();
125        let yaw_ff = self.yaw_rate.feed_forward() * self.attitude_controller.feed_forward_scalar;
126
127        self.attitude_controller.sysid_ang_vel_body = Vector3::zeros();
128        self.attitude_controller.actuator_sysid = Vector3::zeros();
129
130        // TODO control_monitor_update();
131
132        MotorOutput::new(
133            Vector3::new(roll, pitch, yaw),
134            Vector3::new(roll_ff, pitch_ff, yaw_ff),
135        )
136    }
137
138    // Slew set_throttle_rpy_mix to requested value
139    pub fn update_throttle_rpy_mix(&mut self) {
140        // slew _throttle_rpy_mix to _throttle_rpy_mix_desired
141        if self.attitude_controller.throttle_rpy_mix
142            < self.attitude_controller.throttle_rpy_mix_desired
143        {
144            // increase quickly (i.e. from 0.1 to 0.9 in 0.4 seconds)
145            self.attitude_controller.throttle_rpy_mix += (2. * self.attitude_controller.dt).min(
146                self.attitude_controller.throttle_rpy_mix_desired
147                    - self.attitude_controller.throttle_rpy_mix,
148            );
149        } else if self.attitude_controller.throttle_rpy_mix
150            > self.attitude_controller.throttle_rpy_mix_desired
151        {
152            // reduce more slowly (from 0.9 to 0.1 in 1.6 seconds)
153            self.attitude_controller.throttle_rpy_mix -= (0.5 * self.attitude_controller.dt).min(
154                self.attitude_controller.throttle_rpy_mix
155                    - self.attitude_controller.throttle_rpy_mix_desired,
156            );
157        }
158
159        self.attitude_controller.throttle_rpy_mix = self
160            .attitude_controller
161            .throttle_rpy_mix
162            .max(0.1)
163            .min(self.attitude_controller.attitude_control_max);
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use nalgebra::{Quaternion, Vector3};
170
171    use super::MultiCopterAttitudeController;
172
173    #[test]
174    fn f() {}
175}