2013-05-29 20:53:02 -03:00
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2013-08-29 02:34:34 -03:00
/*
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2012-08-21 23:08:14 -03:00
2013-04-23 08:02:18 -03:00
// Initial Code by Jon Challinger
// Modified by Paul Riseborough
2012-08-21 23:08:14 -03:00
2013-01-10 15:22:37 -04:00
# include <AP_Math.h>
2012-10-12 14:53:42 -03:00
# include <AP_HAL.h>
2013-01-01 23:44:02 -04:00
# include <AP_Common.h>
2012-08-21 23:08:14 -03:00
# include "AP_PitchController.h"
2012-10-12 14:53:42 -03:00
extern const AP_HAL : : HAL & hal ;
2012-08-21 23:08:14 -03:00
const AP_Param : : GroupInfo AP_PitchController : : var_info [ ] PROGMEM = {
2013-05-05 03:07:05 -03:00
2013-05-31 02:31:25 -03:00
// @Param: TCONST
2013-05-13 08:10:55 -03:00
// @DisplayName: Pitch Time Constant
2013-05-31 04:21:20 -03:00
// @Description: This controls the time constant in seconds from demanded to achieved pitch angle. A value of 0.5 is a good default and will work with nearly all models. Advanced users may want to reduce this time to obtain a faster response but there is no point setting a time less than the aircraft can achieve.
2013-05-13 08:10:55 -03:00
// @Range: 0.4 1.0
// @Units: seconds
2013-05-05 03:07:05 -03:00
// @Increment: 0.1
// @User: Advanced
2014-04-12 01:11:33 -03:00
AP_GROUPINFO ( " TCONST " , 0 , AP_PitchController , gains . tau , 0.5f ) ,
2013-05-05 03:07:05 -03:00
2013-05-31 02:31:25 -03:00
// @Param: P
2013-05-13 08:10:55 -03:00
// @DisplayName: Proportional Gain
// @Description: This is the gain from pitch angle to elevator. This gain works the same way as PTCH2SRV_P in the old PID controller and can be set to the same value.
2014-08-16 05:07:13 -03:00
// @Range: 0.1 3.0
2013-05-05 03:07:05 -03:00
// @Increment: 0.1
// @User: User
2014-04-12 01:11:33 -03:00
AP_GROUPINFO ( " P " , 1 , AP_PitchController , gains . P , 0.4f ) ,
2013-05-05 03:07:05 -03:00
2013-05-31 02:31:25 -03:00
// @Param: D
2013-05-13 08:10:55 -03:00
// @DisplayName: Damping Gain
// @Description: This is the gain from pitch rate to elevator. This adjusts the damping of the pitch control loop. It has the same effect as PTCH2SRV_D in the old PID controller and can be set to the same value, but without the spikes in servo demands. This gain helps to reduce pitching in turbulence. Some airframes such as flying wings that have poor pitch damping can benefit from increasing this gain term. This should be increased in 0.01 increments as too high a value can lead to a high frequency pitch oscillation that could overstress the airframe.
2013-05-05 03:07:05 -03:00
// @Range: 0 0.1
// @Increment: 0.01
// @User: User
2014-04-12 01:11:33 -03:00
AP_GROUPINFO ( " D " , 2 , AP_PitchController , gains . D , 0.02f ) ,
2013-05-05 03:07:05 -03:00
2013-05-31 02:31:25 -03:00
// @Param: I
2013-05-13 08:10:55 -03:00
// @DisplayName: Integrator Gain
// @Description: This is the gain applied to the integral of pitch angle. It has the same effect as PTCH2SRV_I in the old PID controller and can be set to the same value. Increasing this gain causes the controller to trim out constant offsets between demanded and measured pitch angle.
// @Range: 0 0.5
// @Increment: 0.05
2013-05-05 03:07:05 -03:00
// @User: User
2014-11-13 07:13:25 -04:00
AP_GROUPINFO ( " I " , 3 , AP_PitchController , gains . I , 0.04f ) ,
2013-05-05 03:07:05 -03:00
2013-05-31 02:31:25 -03:00
// @Param: RMAX_UP
2013-05-05 03:07:05 -03:00
// @DisplayName: Pitch up max rate
// @Description: This sets the maximum nose up pitch rate that the controller will demand (degrees/sec). Setting it to zero disables the limit.
// @Range: 0 100
// @Units: degrees/second
// @Increment: 1
2013-05-13 08:10:55 -03:00
// @User: Advanced
2014-04-12 01:11:33 -03:00
AP_GROUPINFO ( " RMAX_UP " , 4 , AP_PitchController , gains . rmax , 0.0f ) ,
2013-05-05 03:07:05 -03:00
2013-05-31 02:31:25 -03:00
// @Param: RMAX_DN
2013-05-05 03:07:05 -03:00
// @DisplayName: Pitch down max rate
// @Description: This sets the maximum nose down pitch rate that the controller will demand (degrees/sec). Setting it to zero disables the limit.
// @Range: 0 100
// @Units: degrees/second
// @Increment: 1
2013-05-13 08:10:55 -03:00
// @User: Advanced
2013-05-31 02:31:25 -03:00
AP_GROUPINFO ( " RMAX_DN " , 5 , AP_PitchController , _max_rate_neg , 0.0f ) ,
2013-05-05 03:07:05 -03:00
2013-05-31 02:31:25 -03:00
// @Param: RLL
2013-05-05 08:37:21 -03:00
// @DisplayName: Roll compensation
2013-05-05 03:07:05 -03:00
// @Description: This is the gain term that is applied to the pitch rate offset calculated as required to keep the nose level during turns. The default value is 1 which will work for all models. Advanced users can use it to correct for height variation in turns. If height is lost initially in turns this can be increased in small increments of 0.05 to compensate. If height is gained initially in turns then it can be decreased.
2013-05-05 08:37:21 -03:00
// @Range: 0.7 1.5
2013-05-05 03:07:05 -03:00
// @Increment: 0.05
// @User: User
2013-05-31 02:31:25 -03:00
AP_GROUPINFO ( " RLL " , 6 , AP_PitchController , _roll_ff , 1.0f ) ,
2013-05-05 03:07:05 -03:00
2013-06-11 05:35:31 -03:00
// @Param: IMAX
// @DisplayName: Integrator limit
2015-02-19 01:15:33 -04:00
// @Description: This limits the number of centi-degrees of elevator over which the integrator will operate. At the default setting of 3000 centi-degrees, the integrator will be limited to +- 30 degrees of servo travel. The maximum servo deflection is +- 45 degrees, so the default value represents a 2/3rd of the total control throw which is adequate for most aircraft unless they are severely out of trim or have very limited elevator control effectiveness.
2013-06-11 05:35:31 -03:00
// @Range: 0 4500
// @Increment: 1
// @User: Advanced
2015-02-19 01:15:33 -04:00
AP_GROUPINFO ( " IMAX " , 7 , AP_PitchController , gains . imax , 3000 ) ,
2013-06-11 05:35:31 -03:00
2012-08-21 23:08:14 -03:00
AP_GROUPEND
} ;
2013-07-10 10:25:06 -03:00
/*
Function returns an equivalent elevator deflection in centi - degrees in the range from - 4500 to 4500
A positive demand is up
Inputs are :
2013-07-17 22:57:11 -03:00
1 ) demanded pitch rate in degrees / second
2013-07-10 10:25:06 -03:00
2 ) control gain scaler = scaling_speed / aspeed
3 ) boolean which is true when stabilise mode is active
4 ) minimum FBW airspeed ( metres / sec )
5 ) maximum FBW airspeed ( metres / sec )
*/
2013-08-02 08:54:48 -03:00
int32_t AP_PitchController : : _get_rate_out ( float desired_rate , float scaler , bool disable_integrator , float aspeed )
2012-08-21 23:08:14 -03:00
{
2012-10-12 14:53:42 -03:00
uint32_t tnow = hal . scheduler - > millis ( ) ;
2012-08-21 23:08:14 -03:00
uint32_t dt = tnow - _last_t ;
if ( _last_t = = 0 | | dt > 1000 ) {
dt = 0 ;
}
_last_t = tnow ;
2013-05-05 07:12:27 -03:00
float delta_time = ( float ) dt * 0.001f ;
2012-08-21 23:08:14 -03:00
2013-07-10 10:25:06 -03:00
// Get body rate vector (radians/sec)
2013-08-14 01:56:18 -03:00
float omega_y = _ahrs . get_gyro ( ) . y ;
2013-07-10 10:25:06 -03:00
// Calculate the pitch rate error (deg/sec) and scale
2014-04-12 01:11:33 -03:00
float achieved_rate = ToDeg ( omega_y ) ;
float rate_error = ( desired_rate - achieved_rate ) * scaler ;
2013-07-10 10:25:06 -03:00
// Multiply pitch rate error by _ki_rate and integrate
2013-10-21 03:58:46 -03:00
// Scaler is applied before integrator so that integrator state relates directly to elevator deflection
// This means elevator trim offset doesn't change as the value of scaler changes with airspeed
2013-07-10 10:25:06 -03:00
// Don't integrate if in stabilise mode as the integrator will wind up against the pilots inputs
2014-04-12 01:11:33 -03:00
if ( ! disable_integrator & & gains . I > 0 ) {
float ki_rate = gains . I * gains . tau ;
2013-07-10 10:25:06 -03:00
//only integrate if gain and time step are positive and airspeed above min value.
2013-07-17 22:57:11 -03:00
if ( dt > 0 & & aspeed > 0.5f * float ( aparm . airspeed_min ) ) {
2013-10-21 03:58:46 -03:00
float integrator_delta = rate_error * ki_rate * delta_time * scaler ;
2013-07-10 10:25:06 -03:00
if ( _last_out < - 45 ) {
// prevent the integrator from increasing if surface defln demand is above the upper limit
integrator_delta = max ( integrator_delta , 0 ) ;
} else if ( _last_out > 45 ) {
// prevent the integrator from decreasing if surface defln demand is below the lower limit
integrator_delta = min ( integrator_delta , 0 ) ;
}
2015-05-22 18:39:38 -03:00
_pid_info . I + = integrator_delta ;
2013-07-10 10:25:06 -03:00
}
} else {
2015-05-22 18:39:38 -03:00
_pid_info . I = 0 ;
2013-07-10 10:25:06 -03:00
}
// Scale the integration limit
2014-04-12 01:11:33 -03:00
float intLimScaled = gains . imax * 0.01f ;
2013-07-10 10:25:06 -03:00
// Constrain the integrator state
2015-05-22 18:39:38 -03:00
_pid_info . I = constrain_float ( _pid_info . I , - intLimScaled , intLimScaled ) ;
2013-07-10 10:25:06 -03:00
2013-05-13 08:10:55 -03:00
// Calculate equivalent gains so that values for K_P and K_I can be taken across from the old PID law
// No conversion is required for K_D
2014-04-12 01:11:33 -03:00
float kp_ff = max ( ( gains . P - gains . I * gains . tau ) * gains . tau - gains . D , 0 ) / _ahrs . get_EAS2TAS ( ) ;
2013-07-10 10:25:06 -03:00
// Calculate the demanded control surface deflection
// Note the scaler is applied again. We want a 1/speed scaler applied to the feed-forward
// path, but want a 1/speed^2 scaler applied to the rate error path.
// This is because acceleration scales with speed^2, but rate scales with speed.
2015-05-22 18:39:38 -03:00
_pid_info . FF = desired_rate * kp_ff * scaler ;
_pid_info . D = rate_error * gains . D * scaler ;
_last_out = _pid_info . D + _pid_info . FF ;
_pid_info . desired = desired_rate ;
2014-04-12 01:11:33 -03:00
if ( autotune . running & & aspeed > aparm . airspeed_min ) {
// let autotune have a go at the values
// Note that we don't pass the integrator component so we get
// a better idea of how much the base PD controller
// contributed
autotune . update ( desired_rate , achieved_rate , _last_out ) ;
2014-04-13 09:11:57 -03:00
// set down rate to rate up when auto-tuning
_max_rate_neg . set_and_save_ifchanged ( gains . rmax ) ;
2014-04-12 01:11:33 -03:00
}
2015-05-22 18:39:38 -03:00
_last_out + = _pid_info . I ;
2013-07-10 10:25:06 -03:00
// Convert to centi-degrees and constrain
return constrain_float ( _last_out * 100 , - 4500 , 4500 ) ;
}
/*
Function returns an equivalent elevator deflection in centi - degrees in the range from - 4500 to 4500
A positive demand is up
Inputs are :
1 ) demanded pitch rate in degrees / second
2 ) control gain scaler = scaling_speed / aspeed
3 ) boolean which is true when stabilise mode is active
4 ) minimum FBW airspeed ( metres / sec )
5 ) maximum FBW airspeed ( metres / sec )
*/
int32_t AP_PitchController : : get_rate_out ( float desired_rate , float scaler )
{
2013-10-04 08:52:47 -03:00
float aspeed ;
if ( ! _ahrs . airspeed_estimate ( & aspeed ) ) {
// If no airspeed available use average of min and max
aspeed = 0.5f * ( float ( aparm . airspeed_min ) + float ( aparm . airspeed_max ) ) ;
}
return _get_rate_out ( desired_rate , scaler , false , aspeed ) ;
2013-07-10 10:25:06 -03:00
}
2013-05-13 08:10:55 -03:00
2013-07-17 22:57:11 -03:00
/*
get the rate offset in degrees / second needed for pitch in body frame
to maintain height in a coordinated turn .
Also returns the inverted flag and the estimated airspeed in m / s for
use by the rest of the pitch controller
*/
float AP_PitchController : : _get_coordination_rate_offset ( float & aspeed , bool & inverted ) const
2013-07-10 10:25:06 -03:00
{
2013-04-29 04:37:34 -03:00
float rate_offset ;
2013-08-14 01:56:18 -03:00
float bank_angle = _ahrs . roll ;
2013-06-01 09:27:01 -03:00
2013-04-29 04:37:34 -03:00
// limit bank angle between +- 80 deg if right way up
2013-05-05 07:12:27 -03:00
if ( fabsf ( bank_angle ) < radians ( 90 ) ) {
bank_angle = constrain_float ( bank_angle , - radians ( 80 ) , radians ( 80 ) ) ;
2013-07-17 22:57:11 -03:00
inverted = false ;
2013-05-05 07:12:27 -03:00
} else {
inverted = true ;
if ( bank_angle > 0.0f ) {
bank_angle = constrain_float ( bank_angle , radians ( 100 ) , radians ( 180 ) ) ;
} else {
bank_angle = constrain_float ( bank_angle , - radians ( 180 ) , - radians ( 100 ) ) ;
}
2013-04-29 04:37:34 -03:00
}
2013-08-14 01:56:18 -03:00
if ( ! _ahrs . airspeed_estimate ( & aspeed ) ) {
2013-04-29 04:37:34 -03:00
// If no airspeed available use average of min and max
2013-07-17 22:57:11 -03:00
aspeed = 0.5f * ( float ( aparm . airspeed_min ) + float ( aparm . airspeed_max ) ) ;
2013-04-29 04:37:34 -03:00
}
2013-08-14 01:56:18 -03:00
if ( abs ( _ahrs . pitch_sensor ) > 7000 ) {
2013-07-13 07:27:48 -03:00
// don't do turn coordination handling when at very high pitch angles
rate_offset = 0 ;
} else {
2013-12-02 19:29:18 -04:00
rate_offset = cosf ( _ahrs . pitch ) * fabsf ( ToDeg ( ( GRAVITY_MSS / max ( ( aspeed * _ahrs . get_EAS2TAS ( ) ) , float ( aparm . airspeed_min ) ) ) * tanf ( bank_angle ) * sinf ( bank_angle ) ) ) * _roll_ff ;
2013-07-13 07:27:48 -03:00
}
2013-05-05 07:12:27 -03:00
if ( inverted ) {
rate_offset = - rate_offset ;
}
2013-07-17 22:57:11 -03:00
return rate_offset ;
}
// Function returns an equivalent elevator deflection in centi-degrees in the range from -4500 to 4500
// A positive demand is up
// Inputs are:
// 1) demanded pitch angle in centi-degrees
// 2) control gain scaler = scaling_speed / aspeed
// 3) boolean which is true when stabilise mode is active
// 4) minimum FBW airspeed (metres/sec)
// 5) maximum FBW airspeed (metres/sec)
//
2013-08-02 08:54:48 -03:00
int32_t AP_PitchController : : get_servo_out ( int32_t angle_err , float scaler , bool disable_integrator )
2013-07-17 22:57:11 -03:00
{
// Calculate offset to pitch rate demand required to maintain pitch angle whilst banking
// Calculate ideal turn rate from bank angle and airspeed assuming a level coordinated turn
// Pitch rate offset is the component of turn rate about the pitch axis
float aspeed ;
float rate_offset ;
bool inverted ;
2014-04-12 01:11:33 -03:00
if ( gains . tau < 0.1f ) {
gains . tau . set ( 0.1f ) ;
2013-07-17 22:57:11 -03:00
}
rate_offset = _get_coordination_rate_offset ( aspeed , inverted ) ;
2013-04-23 08:02:18 -03:00
// Calculate the desired pitch rate (deg/sec) from the angle error
2014-04-12 01:11:33 -03:00
float desired_rate = angle_err * 0.01f / gains . tau ;
2012-08-21 23:08:14 -03:00
2013-05-05 07:12:27 -03:00
// limit the maximum pitch rate demand. Don't apply when inverted
// as the rates will be tuned when upright, and it is common that
// much higher rates are needed inverted
if ( ! inverted ) {
if ( _max_rate_neg & & desired_rate < - _max_rate_neg ) {
desired_rate = - _max_rate_neg ;
2014-04-12 01:11:33 -03:00
} else if ( gains . rmax & & desired_rate > gains . rmax ) {
desired_rate = gains . rmax ;
2013-05-05 07:12:27 -03:00
}
}
2012-08-21 23:08:14 -03:00
2013-05-05 07:12:27 -03:00
if ( inverted ) {
desired_rate = - desired_rate ;
}
2013-04-23 16:22:17 -03:00
// Apply the turn correction offset
desired_rate = desired_rate + rate_offset ;
2013-08-02 08:54:48 -03:00
return _get_rate_out ( desired_rate , scaler , disable_integrator , aspeed ) ;
2012-08-21 23:08:14 -03:00
}
void AP_PitchController : : reset_I ( )
{
2015-05-22 18:39:38 -03:00
_pid_info . I = 0 ;
2012-08-21 23:08:14 -03:00
}