embedded_flight_motors/matrix/
mod.rs1use crate::ESC;
2use embedded_flight_core::{filter::LowPassFilter, MotorOutput};
3use num_traits::{Float, FloatConst, FromPrimitive, NumCast};
4
5mod motor;
6pub use motor::Motor;
7
8pub struct Limit {
9 pub roll: bool,
10 pub pitch: bool,
11 pub yaw: bool,
12 pub throttle_lower: bool,
13 pub throttle_upper: bool,
14}
15
16pub struct MotorMatrix<E, T, const N: usize> {
17 pub motors: [Motor<E, T>; N],
18 throttle_avg_max: T,
20 throttle_filter: LowPassFilter<T>,
21 rpy_scale: T,
22 throttle_thrust_max: T,
23 loop_rate: T,
24 pub thrust_boost_ratio: T,
26 pub compensation_gain: T,
27}
28
29impl<E, T> MotorMatrix<E, T, 4>
30where
31E: ESC,
32 E::Output: NumCast,
33 T: Float + FloatConst + FromPrimitive,
34{
35 pub fn quad(a: E, b: E, c: E, d: E) -> Self {
37 Self::from_motors([
38 Motor::from_angle(a, T::from_i8(90).unwrap(), T::from_i8(1).unwrap()),
39 Motor::from_angle(b, T::from_i8(-90).unwrap(), T::from_i8(1).unwrap()),
40 Motor::from_angle(c, T::from_i8(0).unwrap(), T::from_i8(-1).unwrap()),
41 Motor::from_angle(d, T::from_i16(180).unwrap(), T::from_i8(-1).unwrap()),
42 ])
43 }
44}
45
46impl<E, T, const N: usize> MotorMatrix<E, T, N>
47where
48 E: ESC,
49 E::Output: NumCast,
50 T: Float + FloatConst + FromPrimitive,
51{
52 pub fn from_motors(motors: [Motor<E, T>; N]) -> Self {
53 Self {
54 motors,
55 throttle_avg_max: T::zero(),
56 throttle_filter: LowPassFilter::default(),
57 rpy_scale: T::one(),
58 throttle_thrust_max: T::one(),
59 loop_rate: T::zero(),
60 thrust_boost_ratio: T::zero(),
61 compensation_gain: T::one(),
62 }
63 }
64
65 pub fn arm(&mut self) {
66 for motor in &mut self.motors {
67 motor.esc.arm();
68 }
69 }
70
71 pub fn output(&mut self, output: MotorOutput<T>) -> Limit {
72 self.output_with_throttle(output, T::zero())
73 }
74
75 pub fn output_with_throttle(&mut self, output: MotorOutput<T>, throttle: T) -> Limit {
76 let roll_thrust = (output.control[0] + output.feed_forward[0]) * self.compensation_gain;
77 let pitch_thrust = (output.control[1] + output.feed_forward[1]) * self.compensation_gain;
78 let yaw_thrust = (output.control[2] + output.feed_forward[2]) * self.compensation_gain;
79 let mut throttle_thrust =
80 self.throttle_filter
81 .filter(throttle, T::zero(), T::one() / self.loop_rate);
82
83 let throttle_thrust_max = self.thrust_boost_ratio
84 + (T::one() - self.thrust_boost_ratio)
85 * self.throttle_thrust_max
86 * self.compensation_gain;
87
88 let mut throttle_thrust_best_rpy = T::from_f32(0.5).unwrap().min(self.throttle_avg_max);
90
91 let mut limit = Limit {
92 roll: false,
93 pitch: false,
94 yaw: false,
95 throttle_lower: false,
96 throttle_upper: false,
97 };
98
99 if throttle_thrust <= T::zero() {
101 throttle_thrust = T::zero();
102 limit.throttle_lower = true;
103 }
104 if throttle_thrust >= throttle_thrust_max {
105 throttle_thrust = throttle_thrust_max;
106 limit.throttle_upper = true;
107 }
108
109 let mut yaw_allowed = T::one();
110
111 let mut rp_low = T::one(); let mut rp_high = -T::one(); for motor in &mut self.motors {
114 motor.thrust_rpyt_out = roll_thrust * motor.factor[0] + pitch_thrust * motor.factor[1];
116 if motor.thrust_rpyt_out < rp_low {
118 rp_low = motor.thrust_rpyt_out;
119 }
120 if motor.thrust_rpyt_out > rp_high {
122 rp_high = motor.thrust_rpyt_out;
123 }
124
125 if motor.factor[2] == T::zero() {
128 if (yaw_thrust * motor.factor[2]) >= T::zero() {
129 yaw_allowed = yaw_allowed.min(
130 ((T::one()
131 - (throttle_thrust_best_rpy + motor.thrust_rpyt_out).max(T::zero()))
132 / motor.factor[2])
133 .abs(),
134 );
135 } else {
136 yaw_allowed = yaw_allowed.min(
137 ((throttle_thrust_best_rpy + motor.thrust_rpyt_out.min(T::zero()))
138 / motor.factor[2])
139 .abs(),
140 );
141 }
142 }
143 }
144
145 let mut rpy_low = T::one(); let mut rpy_high = -T::one(); for motor in &mut self.motors {
148 motor.thrust_rpyt_out = motor.thrust_rpyt_out + yaw_thrust * motor.factor[2];
149
150 if motor.thrust_rpyt_out < rpy_low {
152 rpy_low = motor.thrust_rpyt_out;
153 }
154
155 if motor.thrust_rpyt_out > rpy_high {
158 rpy_high = motor.thrust_rpyt_out;
159 }
160 }
161
162 self.rpy_scale = if rpy_high - rpy_low > T::one() {
163 T::one() / (rpy_high - rpy_low)
164 } else {
165 self.rpy_scale.min(-self.throttle_avg_max / rpy_low)
166 };
167 self.throttle_avg_max = self
168 .throttle_avg_max
169 .max(throttle_thrust)
170 .min(self.throttle_thrust_max);
171
172 rpy_high = rpy_high * self.rpy_scale;
174 rpy_low = rp_low * self.rpy_scale;
175 throttle_thrust_best_rpy = -rpy_low;
176
177 let mut thr_adj = throttle_thrust - throttle_thrust_best_rpy;
179
180 if self.rpy_scale < T::one() {
181 limit.roll = true;
183 limit.pitch = true;
184 limit.yaw = true;
185 if thr_adj > T::zero() {
186 limit.throttle_upper = true;
187 }
188 thr_adj = T::zero();
189 } else {
190 if thr_adj < T::zero() {
191 thr_adj = T::zero();
194 } else if thr_adj > T::one() - (throttle_thrust_best_rpy + rpy_high) {
195 thr_adj = T::one() - (throttle_thrust_best_rpy + rpy_high);
197 limit.throttle_upper = true;
198 }
199 }
200
201 let throttle_thrust_best_plus_adj = throttle_thrust_best_rpy + thr_adj;
203 for motor in &mut self.motors {
204 motor.thrust_rpyt_out = (throttle_thrust_best_plus_adj * motor.throttle_factor)
205 + (self.rpy_scale * motor.thrust_rpyt_out);
206
207 motor.esc.output(<<E as ESC>::Output as NumCast>::from(motor.thrust_rpyt_out).unwrap())
208 }
209
210 limit
211 }
212}