2012-01-29 01:21:43 -04:00
/// @file AC_PID.cpp
/// @brief Generic PID algorithm
2015-08-11 03:28:41 -03:00
# include <AP_Math/AP_Math.h>
2012-01-29 01:21:43 -04:00
# include "AC_PID.h"
2023-07-27 10:58:50 -03:00
# define AC_PID_DEFAULT_NOTCH_ATTENUATION 40
2015-10-25 14:03:46 -03:00
const AP_Param : : GroupInfo AC_PID : : var_info [ ] = {
2012-12-10 09:28:39 -04:00
// @Param: P
// @DisplayName: PID Proportional Gain
// @Description: P Gain which produces an output value that is proportional to the current error value
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " P " , 0 , AC_PID , _kp , default_kp ) ,
2015-02-11 08:06:04 -04:00
2012-12-10 09:28:39 -04:00
// @Param: I
// @DisplayName: PID Integral Gain
// @Description: I Gain which produces an output that is proportional to both the magnitude and the duration of the error
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " I " , 1 , AC_PID , _ki , default_ki ) ,
2015-02-11 08:06:04 -04:00
2012-12-10 09:28:39 -04:00
// @Param: D
// @DisplayName: PID Derivative Gain
// @Description: D Gain which produces an output that is proportional to the rate of change of the error
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " D " , 2 , AC_PID , _kd , default_kd ) ,
2015-02-11 08:06:04 -04:00
// 3 was for uint16 IMAX
2019-06-27 06:33:15 -03:00
// @Param: FF
// @DisplayName: FF FeedForward Gain
// @Description: FF Gain which produces an output value that is proportional to the demanded input
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " FF " , 4 , AC_PID , _kff , default_kff ) ,
2015-02-11 08:06:04 -04:00
2012-12-10 09:28:39 -04:00
// @Param: IMAX
// @DisplayName: PID Integral Maximum
// @Description: The maximum/minimum value that the I term can output
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " IMAX " , 5 , AC_PID , _kimax , default_kimax ) ,
2019-06-27 06:33:15 -03:00
// 6 was for float FILT
// 7 is for float ILMI and FF
2015-02-11 08:06:04 -04:00
2019-06-27 06:33:15 -03:00
// index 8 was for AFF
// @Param: FLTT
// @DisplayName: PID Target filter frequency in Hz
// @Description: Target filter frequency in Hz
2016-02-17 22:10:53 -04:00
// @Units: Hz
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " FLTT " , 9 , AC_PID , _filt_T_hz , default_filt_T_hz ) ,
2019-06-27 06:33:15 -03:00
// @Param: FLTE
// @DisplayName: PID Error filter frequency in Hz
// @Description: Error filter frequency in Hz
// @Units: Hz
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " FLTE " , 10 , AC_PID , _filt_E_hz , default_filt_E_hz ) ,
2019-06-27 06:33:15 -03:00
// @Param: FLTD
// @DisplayName: PID Derivative term filter frequency in Hz
// @Description: Derivative filter frequency in Hz
// @Units: Hz
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " FLTD " , 11 , AC_PID , _filt_D_hz , default_filt_D_hz ) ,
2015-02-11 08:06:04 -04:00
2020-09-24 00:28:24 -03:00
// @Param: SMAX
// @DisplayName: Slew rate limit
// @Description: Sets an upper limit on the slew rate produced by the combined P and D gains. If the amplitude of the control action produced by the rate feedback exceeds this value, then the D+P gain is reduced to respect the limit. This limits the amplitude of high frequency oscillations caused by an excessive gain. The limit should be set to no more than 25% of the actuators maximum slew rate to allow for load effects. Note: The gain will not be reduced to less than 10% of the nominal value. A value of zero will disable this feature.
// @Range: 0 200
// @Increment: 0.5
// @User: Advanced
2022-12-29 22:20:32 -04:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " SMAX " , 12 , AC_PID , _slew_rate_max , default_slew_rate_max ) ,
2020-09-24 00:28:24 -03:00
2023-09-18 22:46:00 -03:00
// @Param: PDMX
// @DisplayName: PD sum maximum
// @Description: The maximum/minimum value that the sum of the P and D term can output
// @User: Advanced
AP_GROUPINFO ( " PDMX " , 13 , AC_PID , _kpdmax , 0 ) ,
2023-07-27 10:58:50 -03:00
// @Param: D_FF
// @DisplayName: PID Derivative FeedForward Gain
// @Description: FF D Gain which produces an output that is proportional to the rate of change of the target
// @Range: 0 0.02
// @Increment: 0.0001
// @User: Advanced
2023-10-01 14:21:00 -03:00
AP_GROUPINFO_FLAGS_DEFAULT_POINTER ( " D_FF " , 14 , AC_PID , _kdff , default_kdff ) ,
2023-07-27 10:58:50 -03:00
2023-10-01 14:21:00 -03:00
# if AP_FILTER_ENABLED
2023-07-27 10:58:50 -03:00
// @Param: NTF
2023-10-01 14:21:00 -03:00
// @DisplayName: PID Target notch filter index
// @Description: PID Target notch filter index
// @Range: 1 8
2023-07-27 10:58:50 -03:00
// @User: Advanced
2023-10-01 14:21:00 -03:00
AP_GROUPINFO ( " NTF " , 15 , AC_PID , _notch_T_filter , 0 ) ,
2023-07-27 10:58:50 -03:00
// @Param: NEF
2023-10-01 14:21:00 -03:00
// @DisplayName: PID Error notch filter index
// @Description: PID Error notch filter index
// @Range: 1 8
2023-07-27 10:58:50 -03:00
// @User: Advanced
2023-10-01 14:21:00 -03:00
AP_GROUPINFO ( " NEF " , 16 , AC_PID , _notch_E_filter , 0 ) ,
2023-07-27 10:58:50 -03:00
# endif
2012-08-17 02:37:26 -03:00
AP_GROUPEND
2012-02-12 07:25:50 -04:00
} ;
2012-01-29 01:21:43 -04:00
2015-02-11 08:06:04 -04:00
// Constructor
2020-09-24 00:28:24 -03:00
AC_PID : : AC_PID ( float initial_p , float initial_i , float initial_d , float initial_ff , float initial_imax , float initial_filt_T_hz , float initial_filt_E_hz , float initial_filt_D_hz ,
2023-07-27 10:58:50 -03:00
float initial_srmax , float initial_srtau , float initial_dff ) :
2022-12-29 22:20:32 -04:00
default_kp ( initial_p ) ,
default_ki ( initial_i ) ,
default_kd ( initial_d ) ,
default_kff ( initial_ff ) ,
2024-09-20 09:20:42 -03:00
default_kdff ( initial_dff ) ,
2022-12-29 22:20:32 -04:00
default_kimax ( initial_imax ) ,
default_filt_T_hz ( initial_filt_T_hz ) ,
default_filt_E_hz ( initial_filt_E_hz ) ,
default_filt_D_hz ( initial_filt_D_hz ) ,
2024-09-20 09:20:42 -03:00
default_slew_rate_max ( initial_srmax )
2015-02-11 08:06:04 -04:00
{
// load parameter values from eeprom
AP_Param : : setup_object_defaults ( this , var_info ) ;
2022-12-29 22:20:32 -04:00
// this param is not in the table, so its default is no loaded in the call above
_slew_rate_tau . set ( initial_srtau ) ;
2015-02-11 08:06:04 -04:00
// reset input filter to first value received
_flags . _reset_filter = true ;
2015-05-21 22:43:43 -03:00
memset ( & _pid_info , 0 , sizeof ( _pid_info ) ) ;
2021-03-31 21:12:11 -03:00
// slew limit scaler allows for plane to use degrees/sec slew
// limit
_slew_limit_scale = 1 ;
2015-02-11 08:06:04 -04:00
}
2019-06-27 06:33:15 -03:00
// filt_T_hz - set target filter hz
2024-07-30 08:37:50 -03:00
void AC_PID : : set_filt_T_hz ( float hz )
2019-06-27 06:33:15 -03:00
{
_filt_T_hz . set ( fabsf ( hz ) ) ;
}
// filt_E_hz - set error filter hz
2024-07-30 08:37:50 -03:00
void AC_PID : : set_filt_E_hz ( float hz )
2015-02-11 08:06:04 -04:00
{
2019-06-27 06:33:15 -03:00
_filt_E_hz . set ( fabsf ( hz ) ) ;
}
2015-03-11 02:16:26 -03:00
2019-06-27 06:33:15 -03:00
// filt_D_hz - set derivative filter hz
2024-07-30 08:37:50 -03:00
void AC_PID : : set_filt_D_hz ( float hz )
2019-06-27 06:33:15 -03:00
{
_filt_D_hz . set ( fabsf ( hz ) ) ;
2015-02-11 08:06:04 -04:00
}
2021-11-05 17:52:42 -03:00
// slew_limit - set slew limit
2024-07-30 08:37:50 -03:00
void AC_PID : : set_slew_limit ( float smax )
2021-11-05 17:52:42 -03:00
{
_slew_rate_max . set ( fabsf ( smax ) ) ;
}
2023-07-27 10:58:50 -03:00
void AC_PID : : set_notch_sample_rate ( float sample_rate )
{
2023-10-01 14:21:00 -03:00
# if AP_FILTER_ENABLED
if ( _notch_T_filter = = 0 & & _notch_E_filter = = 0 ) {
2023-07-27 10:58:50 -03:00
return ;
}
2023-10-01 14:21:00 -03:00
if ( _notch_T_filter ! = 0 ) {
if ( _target_notch = = nullptr ) {
2024-05-26 22:24:08 -03:00
_target_notch = NEW_NOTHROW NotchFilterFloat ( ) ;
2023-10-01 14:21:00 -03:00
}
AP_Filter * filter = AP : : filters ( ) . get_filter ( _notch_T_filter ) ;
if ( filter ! = nullptr & & ! filter - > setup_notch_filter ( * _target_notch , sample_rate ) ) {
delete _target_notch ;
_target_notch = nullptr ;
_notch_T_filter . set ( 0 ) ;
}
2023-07-27 10:58:50 -03:00
}
2023-10-01 14:21:00 -03:00
if ( _notch_E_filter ! = 0 ) {
if ( _error_notch = = nullptr ) {
2024-05-26 22:24:08 -03:00
_error_notch = NEW_NOTHROW NotchFilterFloat ( ) ;
2023-10-01 14:21:00 -03:00
}
AP_Filter * filter = AP : : filters ( ) . get_filter ( _notch_E_filter ) ;
if ( filter ! = nullptr & & ! filter - > setup_notch_filter ( * _error_notch , sample_rate ) ) {
delete _error_notch ;
_error_notch = nullptr ;
_notch_E_filter . set ( 0 ) ;
}
2023-07-27 10:58:50 -03:00
}
# endif
}
2019-06-27 06:33:15 -03:00
// update_all - set target and measured inputs to PID controller and calculate outputs
// target and error are filtered
// the derivative is then calculated and filtered
// the integral is then updated based on the setting of the limit flag
2022-05-28 12:58:25 -03:00
float AC_PID : : update_all ( float target , float measurement , float dt , bool limit , float boost )
2015-02-11 08:06:04 -04:00
{
2015-04-09 00:06:07 -03:00
// don't process inf or NaN
2019-06-27 06:33:15 -03:00
if ( ! isfinite ( target ) | | ! isfinite ( measurement ) ) {
return 0.0f ;
2015-04-09 00:06:07 -03:00
}
2015-03-29 07:02:46 -03:00
2015-04-09 00:06:07 -03:00
// reset input filter to value received
2023-12-11 14:44:30 -04:00
_pid_info . reset = _flags . _reset_filter ;
2015-04-09 00:06:07 -03:00
if ( _flags . _reset_filter ) {
_flags . _reset_filter = false ;
2024-07-05 22:23:14 -03:00
// Reset target filter
2019-06-27 06:33:15 -03:00
_target = target ;
2023-10-01 14:21:00 -03:00
# if AP_FILTER_ENABLED
if ( _target_notch ! = nullptr ) {
_target_notch - > reset ( ) ;
_target = _target_notch - > apply ( _target ) ;
}
2024-07-05 22:23:14 -03:00
# endif
// Calculate error and reset error filter
_error = _target - measurement ;
# if AP_FILTER_ENABLED
2023-10-01 14:21:00 -03:00
if ( _error_notch ! = nullptr ) {
_error_notch - > reset ( ) ;
_error = _error_notch - > apply ( _error ) ;
2023-07-27 10:58:50 -03:00
}
# endif
2024-07-05 22:23:14 -03:00
// Zero derivatives
_derivative = 0.0f ;
_target_derivative = 0.0f ;
2019-06-27 06:33:15 -03:00
} else {
2024-07-05 22:23:14 -03:00
// Apply target filters
const float target_last = _target ;
2023-10-01 14:21:00 -03:00
# if AP_FILTER_ENABLED
// apply notch filters before FTLD/FLTE to avoid shot noise
if ( _target_notch ! = nullptr ) {
target = _target_notch - > apply ( target ) ;
}
2024-07-05 22:23:14 -03:00
# endif
_target + = get_filt_T_alpha ( dt ) * ( target - _target ) ;
// Calculate error and apply error filter
const float error_last = _error ;
float error = _target - measurement ;
# if AP_FILTER_ENABLED
2023-10-01 14:21:00 -03:00
if ( _error_notch ! = nullptr ) {
error = _error_notch - > apply ( error ) ;
2023-07-27 10:58:50 -03:00
}
# endif
_error + = get_filt_E_alpha ( dt ) * ( error - _error ) ;
2019-06-27 06:33:15 -03:00
// calculate and filter derivative
2022-12-02 08:34:53 -04:00
if ( is_positive ( dt ) ) {
float derivative = ( _error - error_last ) / dt ;
_derivative + = get_filt_D_alpha ( dt ) * ( derivative - _derivative ) ;
2023-07-27 10:58:50 -03:00
_target_derivative = ( _target - target_last ) / dt ;
2019-06-27 06:33:15 -03:00
}
2015-04-09 00:06:07 -03:00
}
2019-06-27 06:33:15 -03:00
// update I term
2022-12-02 08:34:53 -04:00
update_i ( dt , limit ) ;
2019-06-27 06:33:15 -03:00
float P_out = ( _error * _kp ) ;
float D_out = ( _derivative * _kd ) ;
2020-09-24 00:28:24 -03:00
// calculate slew limit modifier for P+D
2022-12-02 08:34:53 -04:00
_pid_info . Dmod = _slew_limiter . modifier ( ( _pid_info . P + _pid_info . D ) * _slew_limit_scale , dt ) ;
2021-04-04 06:49:31 -03:00
_pid_info . slew_rate = _slew_limiter . get_slew_rate ( ) ;
2020-09-24 00:28:24 -03:00
P_out * = _pid_info . Dmod ;
D_out * = _pid_info . Dmod ;
2022-05-28 12:58:25 -03:00
// boost output if required
P_out * = boost ;
D_out * = boost ;
2023-10-11 15:02:55 -03:00
_pid_info . PD_limit = false ;
2023-09-12 16:51:54 -03:00
// Apply PD sum limit if enabled
if ( is_positive ( _kpdmax ) ) {
const float PD_sum_abs = fabsf ( P_out + D_out ) ;
if ( PD_sum_abs > _kpdmax ) {
const float PD_scale = _kpdmax / PD_sum_abs ;
P_out * = PD_scale ;
D_out * = PD_scale ;
_pid_info . PD_limit = true ;
}
}
2019-06-27 06:33:15 -03:00
_pid_info . target = _target ;
_pid_info . actual = measurement ;
_pid_info . error = _error ;
_pid_info . P = P_out ;
_pid_info . D = D_out ;
2023-10-01 14:21:00 -03:00
_pid_info . FF = _target * _kff ;
_pid_info . DFF = _target_derivative * _kdff ;
2019-06-27 06:33:15 -03:00
2023-09-18 22:46:00 -03:00
return P_out + D_out + _integrator ;
2015-02-11 08:06:04 -04:00
}
2019-06-27 06:33:15 -03:00
// update_error - set error input to PID controller and calculate outputs
// target is set to zero and error is set and filtered
// the derivative then is calculated and filtered
// the integral is then updated based on the setting of the limit flag
// Target and Measured must be set manually for logging purposes.
// todo: remove function when it is no longer used.
2022-12-02 08:34:53 -04:00
float AC_PID : : update_error ( float error , float dt , bool limit )
2012-01-29 01:21:43 -04:00
{
2015-04-09 00:06:07 -03:00
// don't process inf or NaN
2019-06-27 06:33:15 -03:00
if ( ! isfinite ( error ) ) {
return 0.0f ;
2015-04-09 00:06:07 -03:00
}
2023-09-23 14:42:47 -03:00
// Reuse update all code path, zero target and pass negative error as measurement
// Passing as measurement bypasses any target filtering to maintain behaviour
// Negate as update all calculates error as target - measurement
_target = 0.0 ;
const float output = update_all ( 0.0 , - error , dt , limit ) ;
2019-06-27 06:33:15 -03:00
2023-09-23 14:42:47 -03:00
// Make sure logged target and actual are still 0 to maintain behaviour
_pid_info . target = 0.0 ;
_pid_info . actual = 0.0 ;
2023-09-12 16:51:54 -03:00
2023-09-23 14:42:47 -03:00
return output ;
2015-02-11 08:06:04 -04:00
}
2019-06-27 06:33:15 -03:00
// update_i - update the integral
// If the limit flag is set the integral is only allowed to shrink
2022-12-02 08:34:53 -04:00
void AC_PID : : update_i ( float dt , bool limit )
2015-02-11 08:06:04 -04:00
{
2022-12-02 08:34:53 -04:00
if ( ! is_zero ( _ki ) & & is_positive ( dt ) ) {
2019-06-27 06:33:15 -03:00
// Ensure that integrator can only be reduced if the output is saturated
if ( ! limit | | ( ( is_positive ( _integrator ) & & is_negative ( _error ) ) | | ( is_negative ( _integrator ) & & is_positive ( _error ) ) ) ) {
2022-12-02 08:34:53 -04:00
_integrator + = ( ( float ) _error * _ki ) * dt ;
2019-06-27 06:33:15 -03:00
_integrator = constrain_float ( _integrator , - _kimax , _kimax ) ;
2012-08-17 02:37:26 -03:00
}
2019-06-27 06:33:15 -03:00
} else {
_integrator = 0.0f ;
2012-08-17 02:37:26 -03:00
}
2019-06-27 06:33:15 -03:00
_pid_info . I = _integrator ;
2021-01-02 14:04:11 -04:00
_pid_info . limit = limit ;
2023-12-11 14:44:30 -04:00
// Set I set flag for logging and clear
_pid_info . I_term_set = _flags . _I_set ;
_flags . _I_set = false ;
2012-01-29 01:21:43 -04:00
}
2019-06-27 06:33:15 -03:00
float AC_PID : : get_p ( ) const
2012-01-29 01:21:43 -04:00
{
2023-12-11 14:27:45 -04:00
return _pid_info . P ;
2012-01-29 01:21:43 -04:00
}
2019-06-27 06:33:15 -03:00
float AC_PID : : get_i ( ) const
2017-02-10 01:25:27 -04:00
{
2019-06-27 06:33:15 -03:00
return _integrator ;
2017-02-10 01:25:27 -04:00
}
2019-06-27 06:33:15 -03:00
float AC_PID : : get_d ( ) const
{
2023-12-11 14:27:45 -04:00
return _pid_info . D ;
2019-06-27 06:33:15 -03:00
}
2017-02-10 01:25:27 -04:00
2023-12-10 16:03:38 -04:00
float AC_PID : : get_ff ( ) const
2012-01-29 01:21:43 -04:00
{
2023-10-01 14:21:00 -03:00
return _pid_info . FF + _pid_info . DFF ;
2012-01-29 01:21:43 -04:00
}
2014-02-14 02:52:24 -04:00
void AC_PID : : reset_I ( )
2012-01-29 01:21:43 -04:00
{
2023-12-11 14:44:30 -04:00
_flags . _I_set = true ;
2022-01-08 07:10:48 -04:00
_integrator = 0.0 ;
2020-12-01 11:02:54 -04:00
}
2023-10-01 14:21:00 -03:00
// load original gains from eeprom, used by autotune to restore gains after tuning
2014-02-14 02:52:24 -04:00
void AC_PID : : load_gains ( )
2012-01-29 01:21:43 -04:00
{
2012-08-17 02:37:26 -03:00
_kp . load ( ) ;
_ki . load ( ) ;
_kd . load ( ) ;
2019-06-27 06:33:15 -03:00
_kff . load ( ) ;
_filt_T_hz . load ( ) ;
_filt_E_hz . load ( ) ;
_filt_D_hz . load ( ) ;
2012-01-29 01:21:43 -04:00
}
2023-10-01 14:21:00 -03:00
// save original gains to eeprom, used by autotune to save gains before tuning
2014-02-14 02:52:24 -04:00
void AC_PID : : save_gains ( )
2012-01-29 01:21:43 -04:00
{
2012-08-17 02:37:26 -03:00
_kp . save ( ) ;
_ki . save ( ) ;
_kd . save ( ) ;
2019-06-27 06:33:15 -03:00
_kff . save ( ) ;
_filt_T_hz . save ( ) ;
_filt_E_hz . save ( ) ;
_filt_D_hz . save ( ) ;
2015-02-11 08:06:04 -04:00
}
2019-06-27 06:33:15 -03:00
// get_filt_T_alpha - get the target filter alpha
2022-12-02 08:34:53 -04:00
float AC_PID : : get_filt_T_alpha ( float dt ) const
2019-06-27 06:33:15 -03:00
{
2022-12-02 08:34:53 -04:00
return calc_lowpass_alpha_dt ( dt , _filt_T_hz ) ;
2019-06-27 06:33:15 -03:00
}
// get_filt_E_alpha - get the error filter alpha
2022-12-02 08:34:53 -04:00
float AC_PID : : get_filt_E_alpha ( float dt ) const
2015-03-11 02:16:26 -03:00
{
2022-12-02 08:34:53 -04:00
return calc_lowpass_alpha_dt ( dt , _filt_E_hz ) ;
2019-06-27 06:33:15 -03:00
}
// get_filt_D_alpha - get the derivative filter alpha
2022-12-02 08:34:53 -04:00
float AC_PID : : get_filt_D_alpha ( float dt ) const
2019-06-27 06:33:15 -03:00
{
2022-12-02 08:34:53 -04:00
return calc_lowpass_alpha_dt ( dt , _filt_D_hz ) ;
2014-04-17 18:03:53 -03:00
}
2019-06-27 06:33:15 -03:00
2022-01-08 07:10:48 -04:00
void AC_PID : : set_integrator ( float integrator )
2019-06-27 06:33:15 -03:00
{
2023-12-11 14:44:30 -04:00
_flags . _I_set = true ;
2022-01-08 07:10:48 -04:00
_integrator = constrain_float ( integrator , - _kimax , _kimax ) ;
2019-10-16 05:10:28 -03:00
}
2022-12-02 08:34:53 -04:00
void AC_PID : : relax_integrator ( float integrator , float dt , float time_constant )
2019-10-16 05:10:28 -03:00
{
2022-01-08 07:10:48 -04:00
integrator = constrain_float ( integrator , - _kimax , _kimax ) ;
2022-12-02 08:34:53 -04:00
if ( is_positive ( dt ) ) {
2023-12-11 14:44:30 -04:00
_flags . _I_set = true ;
2022-12-02 08:34:53 -04:00
_integrator = _integrator + ( integrator - _integrator ) * ( dt / ( dt + time_constant ) ) ;
}
2019-06-27 06:33:15 -03:00
}