1111#include "flight/filter.h"
1212#include "io/led.h"
1313#include "util/util.h"
14+ #include "util/vector.h"
1415
1516#define PID_SIZE 3
1617
2122#define RELAX_FACTOR_YAW (RELAX_FACTOR_YAW_DEG * DEGTORAD)
2223
2324/// output limit
24- static const float out_limit [ PID_SIZE ] = {0.8 , 0.8 , 0.6 };
25+ static const vec3_t out_limit = {. roll = 0.8f , . pitch = 0.8f , . yaw = 0.6f };
2526
2627// limit of integral term (abs)
27- static const float integral_limit [ PID_SIZE ] = {0.8 , 0.8 , 0.6 };
28+ static const vec3_t integral_limit = {. roll = 0.8f , . pitch = 0.8f , . yaw = 0.6f };
2829
29- static const float pid_scales [PID_SIZE ][PID_SIZE ] = {
30- // roll, pitch, yaw
31- {1.0f / 628.0f , 1.0f / 628.0f , 1.0f / 314.0f }, // kp
32- {1.0f / 50.0f , 1.0f / 50.0f , 1.0f / 50.0f }, // ki
33- {1.0f / 120.0f , 1.0f / 120.0f , 1.0f / 120.0f }, // kd
30+ static const vec3_t pid_scales [PID_SIZE ] = {
31+ {.roll = 1.0f / 628.0f , .pitch = 1.0f / 628.0f , .yaw = 1.0f / 314.0f }, // kp
32+ {.roll = 1.0f / 100.0f , .pitch = 1.0f / 100.0f , .yaw = 1.0f / 100.0f }, // ki - includes historical 0.5x scaling from Silverware
33+ {.roll = 1.0f / 37500.0f , .pitch = 1.0f / 37500.0f , .yaw = 1.0f / 37500.0f }, // kd - includes 0.0032 constant (0.0032 / 120 = 1 / 37500)
3434};
3535
36- static float lastrate [ PID_SIZE ] = {0 , 0 , 0 };
37- static float lastsetpoint [ PID_SIZE ] = {0 , 0 , 0 };
36+ static vec3_t lastrate = {. roll = 0 , . pitch = 0 , . yaw = 0 };
37+ static vec3_t lastsetpoint = {. roll = 0 , . pitch = 0 , . yaw = 0 };
3838
3939static vec3_t ierror ;
4040static vec3_t last_error ;
@@ -58,31 +58,41 @@ void pid_init() {
5858}
5959
6060// (iwindup = 0 windup is not allowed) (iwindup = 1 windup is allowed)
61- static inline float pid_compute_iterm_windup (uint8_t x , float pid_output ) {
62- if ((pid_output >= out_limit [x ]) && (state .error .axis [x ] > 0 )) {
63- return 0.0f ;
64- }
65- if ((pid_output <= - out_limit [x ]) && (state .error .axis [x ] < 0 )) {
66- return 0.0f ;
67- }
61+ static inline vec3_t pid_compute_iterm_windup_vec (const vec3_t * pid_output ) {
62+ vec3_t windup = {.roll = 1.0f , .pitch = 1.0f , .yaw = 1.0f };
6863
69- #ifdef ITERM_RELAX // Roll - Pitch Setpoint based I term relax method
70- static float avg_setpoint [3 ] = {0 , 0 , 0 };
71- if (x < 2 ) {
72- lpf (& avg_setpoint [x ], state .setpoint .axis [x ], lpfcalc (state .looptime , 1.0f / (float )RELAX_FREQUENCY_HZ )); // 11 Hz filter
73- const float hpfSetpoint = fabsf (state .setpoint .axis [x ] - avg_setpoint [x ]);
74- return max (1.0f - hpfSetpoint / RELAX_FACTOR , 0.0f );
75- }
64+ for (uint8_t x = 0 ; x < PID_SIZE ; x ++ ) {
65+ if ((pid_output -> axis [x ] >= out_limit .axis [x ]) && (state .error .axis [x ] > 0 )) {
66+ windup .axis [x ] = 0.0f ;
67+ } else if ((pid_output -> axis [x ] <= - out_limit .axis [x ]) && (state .error .axis [x ] < 0 )) {
68+ windup .axis [x ] = 0.0f ;
69+ }
70+ #ifdef ITERM_RELAX
71+ else {
72+ static vec3_t avg_setpoint = {.roll = 0 , .pitch = 0 , .yaw = 0 };
73+ static float lpf_coeff = 0 ;
74+ static float lpf_coeff_yaw = 0 ;
75+ if (lpf_coeff == 0 ) {
76+ lpf_coeff = lpfcalc (state .looptime , 1.0f / (float )RELAX_FREQUENCY_HZ );
77+ lpf_coeff_yaw = lpfcalc (state .looptime , 1.0f / (float )RELAX_FREQUENCY_HZ_YAW );
78+ }
79+ if (x < 2 ) {
80+ lpf (& avg_setpoint .axis [x ], state .setpoint .axis [x ], lpf_coeff );
81+ const float hpfSetpoint = fabsf (state .setpoint .axis [x ] - avg_setpoint .axis [x ]);
82+ windup .axis [x ] = max (1.0f - hpfSetpoint / RELAX_FACTOR , 0.0f );
83+ }
7684#ifdef ITERM_RELAX_YAW
77- else { // axis is yaw
78- lpf (& avg_setpoint [x ], state .setpoint .axis [x ], lpfcalc ( state . looptime , 1.0f / ( float ) RELAX_FREQUENCY_HZ_YAW )); // 25 Hz filter
79- const float hpfSetpoint = fabsf (state .setpoint .axis [x ] - avg_setpoint [x ]);
80- return max (1.0f - hpfSetpoint / RELAX_FACTOR_YAW , 0.0f );
81- }
85+ else {
86+ lpf (& avg_setpoint . axis [x ], state .setpoint .axis [x ], lpf_coeff_yaw );
87+ const float hpfSetpoint = fabsf (state .setpoint .axis [x ] - avg_setpoint . axis [x ]);
88+ windup . axis [ x ] = max (1.0f - hpfSetpoint / RELAX_FACTOR_YAW , 0.0f );
89+ }
8290#endif
91+ }
8392#endif
93+ }
8494
85- return 1.0f ;
95+ return windup ;
8696}
8797
8898static inline float pid_filter_dterm (uint8_t x , float dterm ) {
@@ -92,26 +102,29 @@ static inline float pid_filter_dterm(uint8_t x, float dterm) {
92102 return dterm ;
93103}
94104
95- static inline bool pid_should_enable_iterm ( uint8_t x ) {
105+ static inline vec3_t pid_should_enable_iterm_vec ( ) {
96106 static bool stick_movement = false;
97107 if (!flags .arm_state ) {
98108 // disarmed, disable, flag no movement
99109 stick_movement = false;
100- return false ;
110+ return ( vec3_t ){{ 0.0f , 0.0f , 0.0f }} ;
101111 }
102112 if (flags .in_air ) {
103113 // in-air, enable, flag no movement
104114 stick_movement = false;
105- return true ;
115+ return ( vec3_t ){{ 1.0f , 1.0f , 1.0f }} ;
106116 }
107117
108- if (fabsf (state .setpoint .axis [x ]) > 0.1f ) {
109- // record first stick crossing
118+ // Check for stick movement on any axis
119+ if (fabsf (state .setpoint .roll ) > 0.1f ||
120+ fabsf (state .setpoint .pitch ) > 0.1f ||
121+ fabsf (state .setpoint .yaw ) > 0.1f ) {
110122 stick_movement = true;
111123 }
112124
113- // enable if we recored stick movement previously
114- return stick_movement ;
125+ // enable if we recorded stick movement previously
126+ const float enable = stick_movement ? 1.0f : 0.0f ;
127+ return (vec3_t ){{enable , enable , enable }};
115128}
116129
117130static inline float pid_voltage_compensation () {
@@ -159,30 +172,36 @@ void pid_calc() {
159172 // rotates errors
160173 ierror = vec3_rotate (ierror , state .gyro_delta_angle );
161174
162- #pragma GCC unroll 3
163- for (uint8_t x = 0 ; x < PID_SIZE ; x ++ ) {
164- const float current_kp = profile_current_pid_rates ()-> kp .axis [x ] * pid_scales [0 ][x ];
165- const float current_ki = profile_current_pid_rates ()-> ki .axis [x ] * pid_scales [1 ][x ];
166- const float current_kd = profile_current_pid_rates ()-> kd .axis [x ] * pid_scales [2 ][x ];
175+ // Calculate deltas for derivatives
176+ const vec3_t setpoint_delta = vec3_sub (state .setpoint , lastsetpoint );
177+ const vec3_t gyro_delta = vec3_sub (state .gyro , lastrate );
167178
168- // P term
169- state .pid_p_term .axis [x ] = state .error .axis [x ] * current_kp ;
179+ // Pre-calculate all PID gains using vec3 operations
180+ const pid_rate_t * rates = profile_current_pid_rates ();
181+ const vec3_t current_kp = vec3_mul (vec3_mul_elem (rates -> kp , pid_scales [0 ]), v_compensation );
170182
171- // Pid Voltage Comp applied to P term only
172- state .pid_p_term . axis [ x ] *= v_compensation ;
183+ // Pre-calculate common terms
184+ const float ki_looptime = state .looptime * ( 1.0f / 3.0f ); // Simpson's rule constant * looptime
173185
174- // I term
175- const float iterm_windup = pid_compute_iterm_windup (x , pid_output .axis [x ]);
176- if (!pid_should_enable_iterm (x )) {
177- // wind down integral while we are still on ground and we do not get any input from the sticks
178- ierror .axis [x ] *= 0.98f ;
179- }
180- // SIMPSON_RULE_INTEGRAL
181- // assuming similar time intervals
182- ierror .axis [x ] = ierror .axis [x ] + 0.5f * (1.0f / 3.0f ) * (last_error2 .axis [x ] + 4 * last_error .axis [x ] + state .error .axis [x ]) * current_ki * iterm_windup * state .looptime ;
183- ierror .axis [x ] = constrain (ierror .axis [x ], - integral_limit [x ], integral_limit [x ]);
184- last_error2 .axis [x ] = last_error .axis [x ];
185- last_error .axis [x ] = state .error .axis [x ];
186+ // Pre-multiply Ki and Kd with their time factors
187+ const vec3_t current_ki = vec3_mul (vec3_mul_elem (rates -> ki , pid_scales [1 ]), ki_looptime );
188+ const vec3_t current_kd = vec3_mul (vec3_mul_elem (rates -> kd , pid_scales [2 ]), state .looptime_inverse );
189+ const vec3_t iterm_enable = pid_should_enable_iterm_vec ();
190+ const vec3_t iterm_windup = pid_compute_iterm_windup_vec (& pid_output );
191+ const bool rx_filter_enabled = state .rx_filter_hz > 0.1f ;
192+
193+ #pragma GCC unroll 3
194+ #pragma GCC ivdep // no loop dependencies, safe to vectorize
195+ for (uint8_t x = 0 ; x < PID_SIZE ; x ++ ) {
196+ // P term (voltage compensation already applied to current_kp)
197+ state .pid_p_term .axis [x ] = state .error .axis [x ] * current_kp .axis [x ];
198+
199+ // I term - combine decay and simpson integration
200+ const float simpson_sum = last_error2 .axis [x ] + 4.0f * last_error .axis [x ] + state .error .axis [x ];
201+ const float ierror_delta = simpson_sum * current_ki .axis [x ] * iterm_windup .axis [x ];
202+ // If iterm disabled, decay by 0.98, otherwise add the delta
203+ ierror .axis [x ] = iterm_enable .axis [x ] ? (ierror .axis [x ] + ierror_delta ) : (ierror .axis [x ] * 0.98f );
204+ ierror .axis [x ] = constrain (ierror .axis [x ], - integral_limit .axis [x ], integral_limit .axis [x ]);
186205
187206 state .pid_i_term .axis [x ] = ierror .axis [x ];
188207
@@ -194,19 +213,23 @@ void pid_calc() {
194213 transition_setpoint_weight = (fabsf (state .rx_filtered .axis [x ]) * (stick_transition [x ] / stick_accelerator [x ])) + (1 - stick_transition [x ]);
195214 }
196215
197- float setpoint_derivative = ( state . setpoint . axis [x ] - lastsetpoint [ x ]) * current_kd * state . timefactor ;
198- if (state . rx_filter_hz > 0.1f ) {
216+ float setpoint_derivative = setpoint_delta . axis [x ] * current_kd . axis [ x ] ;
217+ if (rx_filter_enabled ) {
199218 setpoint_derivative = filter_lp_pt1_step (& rx_filter , & rx_filter_state [x ], setpoint_derivative );
200219 }
201- lastsetpoint [x ] = state .setpoint .axis [x ];
202220
203- const float gyro_derivative = (state .gyro .axis [x ] - lastrate [x ]) * current_kd * state .timefactor * tda_compensation ;
204- lastrate [x ] = state .gyro .axis [x ];
221+ const float gyro_derivative = gyro_delta .axis [x ] * current_kd .axis [x ] * tda_compensation ;
205222
206223 const float dterm = (setpoint_derivative * stick_accelerator [x ] * transition_setpoint_weight ) - (gyro_derivative );
207224 state .pid_d_term .axis [x ] = pid_filter_dterm (x , dterm );
208225
209226 state .pidoutput .axis [x ] = pid_output .axis [x ] = state .pid_p_term .axis [x ] + state .pid_i_term .axis [x ] + state .pid_d_term .axis [x ];
210- state .pidoutput .axis [x ] = constrain (state .pidoutput .axis [x ], - out_limit [x ], out_limit [x ]);
227+ state .pidoutput .axis [x ] = constrain (state .pidoutput .axis [x ], - out_limit . axis [x ], out_limit . axis [x ]);
211228 }
229+
230+ // Update history
231+ last_error2 = last_error ;
232+ last_error = state .error ;
233+ lastsetpoint = state .setpoint ;
234+ lastrate = state .gyro ;
212235}
0 commit comments