2013-08-23 16:28:53 +02:00
/****************************************************************************
*
* Copyright ( c ) 2013 Estimation and Control Library ( ECL ) . All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
2013-09-10 11:29:05 +02:00
* 3. Neither the name ECL nor the names of its contributors may be
2013-08-23 16:28:53 +02:00
* used to endorse or promote products derived from this software
* without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS
* OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* @ file ecl_pitch_controller . cpp
* Implementation of a simple orthogonal pitch PID controller .
*
2013-09-12 14:02:30 +02:00
* Authors and acknowledgements in header .
2013-08-23 16:28:53 +02:00
*/
# include "ecl_pitch_controller.h"
2013-09-08 20:04:39 +02:00
# include <math.h>
# include <stdint.h>
# include <float.h>
# include <geo/geo.h>
# include <ecl/ecl.h>
# include <mathlib/mathlib.h>
2013-10-20 20:10:31 +02:00
# include <systemlib/err.h>
2013-08-23 16:28:53 +02:00
ECL_PitchController : : ECL_PitchController ( ) :
_last_run ( 0 ) ,
2013-11-03 22:04:32 +01:00
_tc ( 0.1f ) ,
_k_p ( 0.0f ) ,
_k_i ( 0.0f ) ,
2013-11-13 11:05:22 +01:00
_k_ff ( 0.0f ) ,
2013-11-03 22:04:32 +01:00
_integrator_max ( 0.0f ) ,
_max_rate_pos ( 0.0f ) ,
_max_rate_neg ( 0.0f ) ,
_roll_ff ( 0.0f ) ,
2013-08-23 16:28:53 +02:00
_last_output ( 0.0f ) ,
_integrator ( 0.0f ) ,
_rate_error ( 0.0f ) ,
2013-11-03 22:04:32 +01:00
_rate_setpoint ( 0.0f ) ,
_bodyrate_setpoint ( 0.0f )
2013-08-23 16:28:53 +02:00
{
}
2013-10-15 22:00:56 +02:00
float ECL_PitchController : : control_attitude ( float pitch_setpoint , float roll , float pitch , float airspeed )
2013-08-23 16:28:53 +02:00
{
2013-09-07 12:53:39 +02:00
2013-09-07 13:50:38 +02:00
2013-09-09 21:06:08 +02:00
/* flying inverted (wings upside down) ? */
bool inverted = false ;
/* roll is used as feedforward term and inverted flight needs to be considered */
if ( fabsf ( roll ) < math : : radians ( 90.0f ) ) {
/* not inverted, but numerically still potentially close to infinity */
roll = math : : constrain ( roll , math : : radians ( - 80.0f ) , math : : radians ( 80.0f ) ) ;
} else {
/* inverted flight, constrain on the two extremes of -pi..+pi to avoid infinity */
/* note: the ranges are extended by 10 deg here to avoid numeric resolution effects */
if ( roll > 0.0f ) {
/* right hemisphere */
roll = math : : constrain ( roll , math : : radians ( 100.0f ) , math : : radians ( 180.0f ) ) ;
} else {
/* left hemisphere */
roll = math : : constrain ( roll , math : : radians ( - 100.0f ) , math : : radians ( - 180.0f ) ) ;
}
}
2013-09-07 13:50:38 +02:00
/* calculate the offset in the rate resulting from rolling */
2013-12-29 16:42:31 +01:00
//xxx needs explanation and conversion to body angular rates or should be removed
2013-09-07 13:50:38 +02:00
float turn_offset = fabsf ( ( CONSTANTS_ONE_G / airspeed ) *
2013-09-08 20:04:39 +02:00
tanf ( roll ) * sinf ( roll ) ) * _roll_ff ;
2013-09-09 21:06:08 +02:00
if ( inverted )
turn_offset = - turn_offset ;
2013-09-07 13:50:38 +02:00
2013-10-15 22:00:56 +02:00
/* Calculate the error */
2013-09-07 13:50:38 +02:00
float pitch_error = pitch_setpoint - pitch ;
2013-10-15 18:34:13 +02:00
2013-10-15 22:00:56 +02:00
/* Apply P controller: rate setpoint from current error and time constant */
2013-10-17 20:29:54 +02:00
_rate_setpoint = pitch_error / _tc ;
2013-09-07 13:50:38 +02:00
/* add turn offset */
_rate_setpoint + = turn_offset ;
2013-10-15 22:00:56 +02:00
/* limit the rate */ //XXX: move to body angluar rates
if ( _max_rate_pos > 0.01f & & _max_rate_neg > 0.01f ) {
if ( _rate_setpoint > 0.0f ) {
_rate_setpoint = ( _rate_setpoint > _max_rate_pos ) ? _max_rate_pos : _rate_setpoint ;
} else {
_rate_setpoint = ( _rate_setpoint < - _max_rate_neg ) ? - _max_rate_neg : _rate_setpoint ;
}
}
return _rate_setpoint ;
}
float ECL_PitchController : : control_bodyrate ( float roll , float pitch ,
float pitch_rate , float yaw_rate ,
float yaw_rate_setpoint ,
float airspeed_min , float airspeed_max , float airspeed , float scaler , bool lock_integrator )
{
/* get the usual dt estimate */
uint64_t dt_micros = ecl_elapsed_time ( & _last_run ) ;
_last_run = ecl_absolute_time ( ) ;
2013-10-19 19:12:03 +02:00
float dt = ( float ) dt_micros * 1e-6 f ;
2013-10-15 22:00:56 +02:00
/* lock integral for long intervals */
if ( dt_micros > 500000 )
lock_integrator = true ;
2013-10-19 22:07:27 +02:00
// float k_ff = math::max((_k_p - _k_i * _tc) * _tc - _k_d, 0.0f);
float k_ff = 0 ;
2013-10-15 22:00:56 +02:00
/* input conditioning */
if ( ! isfinite ( airspeed ) ) {
/* airspeed is NaN, +- INF or not available, pick center of band */
airspeed = 0.5f * ( airspeed_min + airspeed_max ) ;
} else if ( airspeed < airspeed_min ) {
airspeed = airspeed_min ;
}
/* Transform setpoint to body angular rates */
_bodyrate_setpoint = cosf ( roll ) * _rate_setpoint + cosf ( pitch ) * sinf ( roll ) * yaw_rate_setpoint ; //jacobian
2013-10-15 18:34:13 +02:00
/* Transform estimation to body angular rates */
2013-10-15 22:00:56 +02:00
float pitch_bodyrate = cosf ( roll ) * pitch_rate + cosf ( pitch ) * sinf ( roll ) * yaw_rate ; //jacobian
2013-10-15 18:34:13 +02:00
2013-10-15 22:00:56 +02:00
_rate_error = _bodyrate_setpoint - pitch_bodyrate ;
2013-09-07 13:50:38 +02:00
2013-10-19 22:07:27 +02:00
if ( ! lock_integrator & & _k_i > 0.0f & & airspeed > 0.5f * airspeed_min ) {
2013-09-07 13:50:38 +02:00
2013-10-19 14:14:44 +02:00
float id = _rate_error * dt ;
2013-09-07 13:50:38 +02:00
/*
2013-10-19 22:07:27 +02:00
* anti - windup : do not allow integrator to increase if actuator is at limit
2013-09-07 13:50:38 +02:00
*/
2013-10-20 15:03:11 +02:00
if ( _last_output < - 1.0f ) {
2013-09-07 13:50:38 +02:00
/* only allow motion to center: increase value */
2013-09-08 20:04:39 +02:00
id = math : : max ( id , 0.0f ) ;
2013-10-20 15:03:11 +02:00
} else if ( _last_output > 1.0f ) {
2013-09-07 13:50:38 +02:00
/* only allow motion to center: decrease value */
2013-09-08 20:04:39 +02:00
id = math : : min ( id , 0.0f ) ;
2013-09-07 13:50:38 +02:00
}
_integrator + = id ;
}
/* integrator limit */
2013-10-24 17:57:21 +02:00
//xxx: until start detection is available: integral part in control signal is limited here
2013-10-24 18:34:22 +02:00
float integrator_constrained = math : : constrain ( _integrator * _k_i , - _integrator_max , _integrator_max ) ;
2013-10-19 22:07:27 +02:00
/* Apply PI rate controller and store non-limited output */
2013-11-13 11:05:22 +01:00
_last_output = ( _bodyrate_setpoint * _k_ff + _rate_error * _k_p + integrator_constrained + _rate_setpoint * k_ff ) * scaler * scaler ; //scaler is proportional to 1/airspeed
2013-10-20 20:10:31 +02:00
// warnx("pitch: _integrator: %.4f, _integrator_max: %.4f, airspeed %.4f, _k_i %.4f, _k_p: %.4f", (double)_integrator, (double)_integrator_max, (double)airspeed, (double)_k_i, (double)_k_p);
// warnx("roll: _last_output %.4f", (double)_last_output);
2013-10-20 15:03:11 +02:00
return math : : constrain ( _last_output , - 1.0f , 1.0f ) ;
2013-08-23 16:28:53 +02:00
}
void ECL_PitchController : : reset_integrator ( )
{
_integrator = 0.0f ;
}