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"
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
2019-06-27 06:33:15 -03:00
AP_GROUPINFO ( " P " , 0 , AC_PID , _kp , 0 ) ,
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
2019-06-27 06:33:15 -03:00
AP_GROUPINFO ( " I " , 1 , AC_PID , _ki , 0 ) ,
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
2019-06-27 06:33:15 -03:00
AP_GROUPINFO ( " D " , 2 , AC_PID , _kd , 0 ) ,
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
AP_GROUPINFO ( " FF " , 4 , AC_PID , _kff , 0 ) ,
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
2019-06-27 06:33:15 -03:00
AP_GROUPINFO ( " IMAX " , 5 , AC_PID , _kimax , 0 ) ,
// 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
2019-06-27 06:33:15 -03:00
AP_GROUPINFO ( " FLTT " , 9 , AC_PID , _filt_T_hz , AC_PID_TFILT_HZ_DEFAULT ) ,
// @Param: FLTE
// @DisplayName: PID Error filter frequency in Hz
// @Description: Error filter frequency in Hz
// @Units: Hz
AP_GROUPINFO ( " FLTE " , 10 , AC_PID , _filt_E_hz , AC_PID_EFILT_HZ_DEFAULT ) ,
// @Param: FLTD
// @DisplayName: PID Derivative term filter frequency in Hz
// @Description: Derivative filter frequency in Hz
// @Units: Hz
AP_GROUPINFO ( " FLTD " , 11 , AC_PID , _filt_D_hz , AC_PID_DFILT_HZ_DEFAULT ) ,
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
AP_GROUPINFO ( " SMAX " , 12 , AC_PID , _slew_rate_max , 0 ) ,
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 ,
float dt , float initial_srmax , float initial_srtau ) :
2021-04-16 00:34:59 -03:00
_dt ( dt )
2015-02-11 08:06:04 -04:00
{
// load parameter values from eeprom
AP_Param : : setup_object_defaults ( this , var_info ) ;
_kp = initial_p ;
_ki = initial_i ;
_kd = initial_d ;
2019-06-27 06:33:15 -03:00
_kff = initial_ff ;
_kimax = fabsf ( initial_imax ) ;
filt_T_hz ( initial_filt_T_hz ) ;
filt_E_hz ( initial_filt_E_hz ) ;
filt_D_hz ( initial_filt_D_hz ) ;
2020-09-24 00:28:24 -03:00
_slew_rate_max . set ( initial_srmax ) ;
_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
}
// set_dt - set time step in seconds
void AC_PID : : set_dt ( float dt )
{
// set dt and calculate the input filter alpha
_dt = dt ;
}
2019-06-27 06:33:15 -03:00
// filt_T_hz - set target filter hz
void AC_PID : : filt_T_hz ( float hz )
{
_filt_T_hz . set ( fabsf ( hz ) ) ;
}
// filt_E_hz - set error filter hz
void AC_PID : : 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
void AC_PID : : filt_D_hz ( float hz )
{
_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
void AC_PID : : slew_limit ( float smax )
{
_slew_rate_max . set ( fabsf ( smax ) ) ;
}
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
float AC_PID : : update_all ( float target , float measurement , bool limit )
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
if ( _flags . _reset_filter ) {
_flags . _reset_filter = false ;
2019-06-27 06:33:15 -03:00
_target = target ;
_error = _target - measurement ;
2015-04-09 00:06:07 -03:00
_derivative = 0.0f ;
2019-06-27 06:33:15 -03:00
} else {
float error_last = _error ;
_target + = get_filt_T_alpha ( ) * ( target - _target ) ;
_error + = get_filt_E_alpha ( ) * ( ( _target - measurement ) - _error ) ;
// calculate and filter derivative
if ( _dt > 0.0f ) {
float derivative = ( _error - error_last ) / _dt ;
_derivative + = get_filt_D_alpha ( ) * ( derivative - _derivative ) ;
}
2015-04-09 00:06:07 -03:00
}
2019-06-27 06:33:15 -03:00
// update I term
update_i ( limit ) ;
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
2021-03-31 21:12:11 -03: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 ;
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 ;
return P_out + _integrator + D_out ;
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.
float AC_PID : : update_error ( float error , 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
}
2019-06-27 06:33:15 -03:00
_target = 0.0f ;
2015-02-11 08:06:04 -04:00
// reset input filter to value received
if ( _flags . _reset_filter ) {
_flags . _reset_filter = false ;
2019-06-27 06:33:15 -03:00
_error = error ;
2015-02-11 08:06:04 -04:00
_derivative = 0.0f ;
2019-06-27 06:33:15 -03:00
} else {
float error_last = _error ;
_error + = get_filt_E_alpha ( ) * ( error - _error ) ;
// calculate and filter derivative
if ( _dt > 0.0f ) {
float derivative = ( _error - error_last ) / _dt ;
_derivative + = get_filt_D_alpha ( ) * ( derivative - _derivative ) ;
}
2015-02-11 08:06:04 -04:00
}
2019-06-27 06:33:15 -03:00
// update I term
update_i ( limit ) ;
2015-02-11 08:06:04 -04:00
2019-06-27 06:33:15 -03:00
float P_out = ( _error * _kp ) ;
float D_out = ( _derivative * _kd ) ;
2012-01-29 01:21:43 -04:00
2020-09-24 00:28:24 -03:00
// calculate slew limit modifier for P+D
2021-03-31 21:12:11 -03: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 ;
2019-06-27 06:33:15 -03:00
_pid_info . target = 0.0f ;
_pid_info . actual = 0.0f ;
_pid_info . error = _error ;
_pid_info . P = P_out ;
_pid_info . D = D_out ;
return P_out + _integrator + D_out ;
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
void AC_PID : : update_i ( bool limit )
2015-02-11 08:06:04 -04:00
{
2019-06-27 06:33:15 -03:00
if ( ! is_zero ( _ki ) & & is_positive ( _dt ) ) {
// 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 ) ) ) ) {
_integrator + = ( ( float ) _error * _ki ) * _dt ;
_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 ;
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
{
2019-06-27 06:33:15 -03:00
return _error * _kp ;
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
{
return _kd * _derivative ;
}
2017-02-10 01:25:27 -04:00
2019-06-27 06:33:15 -03:00
float AC_PID : : get_ff ( )
2012-01-29 01:21:43 -04:00
{
2019-06-27 06:33:15 -03:00
_pid_info . FF = _target * _kff ;
return _target * _kff ;
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
{
2012-08-17 02:37:26 -03:00
_integrator = 0 ;
2012-01-29 01:21:43 -04:00
}
2020-12-01 11:02:54 -04:00
void AC_PID : : reset_I_smoothly ( )
{
float reset_time = AC_PID_RESET_TC * 3.0f ;
uint64_t now = AP_HAL : : micros64 ( ) ;
if ( ( now - _reset_last_update ) > 5e5 ) {
_reset_counter = 0 ;
}
if ( ( float ) _reset_counter < ( reset_time / _dt ) ) {
_integrator = _integrator - ( _dt / ( _dt + AC_PID_RESET_TC ) ) * _integrator ;
_reset_counter + + ;
} else {
_integrator = 0 ;
}
_reset_last_update = now ;
}
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 ( ) ;
_kimax . load ( ) ;
_kimax = fabsf ( _kimax ) ;
_filt_T_hz . load ( ) ;
_filt_E_hz . load ( ) ;
_filt_D_hz . load ( ) ;
2012-01-29 01:21:43 -04:00
}
2015-02-11 08:06:04 -04:00
// save_gains - save gains to eeprom
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 ( ) ;
_kimax . save ( ) ;
_filt_T_hz . save ( ) ;
_filt_E_hz . save ( ) ;
_filt_D_hz . save ( ) ;
2015-02-11 08:06:04 -04:00
}
/// Overload the function call operator to permit easy initialisation
2019-06-27 06:33:15 -03:00
void AC_PID : : operator ( ) ( float p_val , float i_val , float d_val , float ff_val , float imax_val , float input_filt_T_hz , float input_filt_E_hz , float input_filt_D_hz , float dt )
2015-02-11 08:06:04 -04:00
{
2019-06-27 06:33:15 -03:00
_kp = p_val ;
_ki = i_val ;
_kd = d_val ;
_kff = ff_val ;
_kimax = fabsf ( imax_val ) ;
_filt_T_hz = input_filt_T_hz ;
_filt_E_hz = input_filt_E_hz ;
_filt_D_hz = input_filt_D_hz ;
2015-02-11 08:06:04 -04:00
_dt = dt ;
2012-01-29 01:21:43 -04:00
}
2014-04-17 18:03:53 -03:00
2019-06-27 06:33:15 -03:00
// get_filt_T_alpha - get the target filter alpha
float AC_PID : : get_filt_T_alpha ( ) const
{
return get_filt_alpha ( _filt_T_hz ) ;
}
// get_filt_E_alpha - get the error filter alpha
float AC_PID : : get_filt_E_alpha ( ) const
2015-03-11 02:16:26 -03:00
{
2019-06-27 06:33:15 -03:00
return get_filt_alpha ( _filt_E_hz ) ;
}
// get_filt_D_alpha - get the derivative filter alpha
float AC_PID : : get_filt_D_alpha ( ) const
{
return get_filt_alpha ( _filt_D_hz ) ;
}
// get_filt_alpha - calculate a filter alpha
float AC_PID : : get_filt_alpha ( float filt_hz ) const
{
2020-11-08 06:57:10 -04:00
return calc_lowpass_alpha_dt ( _dt , filt_hz ) ;
2014-04-17 18:03:53 -03:00
}
2019-06-27 06:33:15 -03:00
void AC_PID : : set_integrator ( float target , float measurement , float i )
{
set_integrator ( target - measurement , i ) ;
}
void AC_PID : : set_integrator ( float error , float i )
{
_integrator = constrain_float ( i - error * _kp , - _kimax , _kimax ) ;
2019-10-16 05:10:28 -03:00
_pid_info . I = _integrator ;
}
void AC_PID : : set_integrator ( float i )
{
_integrator = constrain_float ( i , - _kimax , _kimax ) ;
_pid_info . I = _integrator ;
2019-06-27 06:33:15 -03:00
}