2014-01-29 09:50:32 -04:00
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
# include "AC_AttitudeControl_Heli.h"
2015-08-11 03:28:41 -03:00
# include <AP_HAL/AP_HAL.h>
2014-01-29 09:50:32 -04:00
// table of user settable parameters
2015-10-25 14:03:46 -03:00
const AP_Param : : GroupInfo AC_AttitudeControl_Heli : : var_info [ ] = {
2015-05-08 02:49:09 -03:00
// parameters from parent vehicle
AP_NESTEDGROUPINFO ( AC_AttitudeControl , 0 ) ,
2015-03-04 03:23:20 -04:00
2015-09-21 23:35:53 -03:00
// @Param: PIRO_COMP
// @DisplayName: Piro Comp Enable
// @Description: Pirouette compensation enabled
// @Range: 0:Disabled 1:Enabled
// @User: Advanced
AP_GROUPINFO ( " PIRO_COMP " , 0 , AC_AttitudeControl_Heli , _piro_comp_enabled , 0 ) ,
2015-10-13 16:02:21 -03:00
// @Param: HOVR_ROL_TRM
// @DisplayName: Hover Roll Trim
// @Description: Trim the hover roll angle to counter tail rotor thrust in a hover
// @Units: Centi-Degrees
// @Range: 0 1000
// @User: Advanced
AP_GROUPINFO ( " HOVR_ROL_TRM " , 1 , AC_AttitudeControl_Heli , _hover_roll_trim , AC_ATTITUDE_HELI_HOVER_ROLL_TRIM_DEFAULT ) ,
2014-01-29 09:50:32 -04:00
AP_GROUPEND
} ;
2014-08-20 10:14:25 -03:00
// passthrough_bf_roll_pitch_rate_yaw - passthrough the pilots roll and pitch inputs directly to swashplate for flybar acro mode
2015-11-24 17:11:50 -04:00
void AC_AttitudeControl_Heli : : passthrough_bf_roll_pitch_rate_yaw ( float roll_passthrough , float pitch_passthrough , float yaw_rate_bf_cds )
2014-08-20 10:14:25 -03:00
{
2015-11-24 17:11:50 -04:00
// convert from centidegrees on public interface to radians
float yaw_rate_bf_rads = radians ( yaw_rate_bf_cds * 0.01f ) ;
2015-06-22 02:49:25 -03:00
// store roll, pitch and passthroughs
2015-11-24 17:11:50 -04:00
// NOTE: this abuses yaw_rate_bf_rads
2014-08-20 10:14:25 -03:00
_passthrough_roll = roll_passthrough ;
_passthrough_pitch = pitch_passthrough ;
2015-11-24 17:11:50 -04:00
_passthrough_yaw = degrees ( yaw_rate_bf_rads ) * 100.0f ;
2014-08-20 10:14:25 -03:00
// set rate controller to use pass through
_flags_heli . flybar_passthrough = true ;
// set bf rate targets to current body frame rates (i.e. relax and be ready for vehicle to switch out of acro)
2015-11-24 23:28:42 -04:00
_att_target_ang_vel_rads . x = _ahrs . get_gyro ( ) . x ;
_att_target_ang_vel_rads . y = _ahrs . get_gyro ( ) . y ;
2014-08-20 10:14:25 -03:00
// accel limit desired yaw rate
2015-11-24 17:11:50 -04:00
if ( get_accel_yaw_max_radss ( ) > 0.0f ) {
float rate_change_limit_rads = get_accel_yaw_max_radss ( ) * _dt ;
2015-11-24 23:28:42 -04:00
float rate_change_rads = yaw_rate_bf_rads - _att_target_ang_vel_rads . z ;
2015-11-24 17:11:50 -04:00
rate_change_rads = constrain_float ( rate_change_rads , - rate_change_limit_rads , rate_change_limit_rads ) ;
2015-11-24 23:28:42 -04:00
_att_target_ang_vel_rads . z + = rate_change_rads ;
2014-08-20 10:14:25 -03:00
} else {
2015-11-24 23:28:42 -04:00
_att_target_ang_vel_rads . z = yaw_rate_bf_rads ;
2014-08-20 10:14:25 -03:00
}
integrate_bf_rate_error_to_angle_errors ( ) ;
2015-11-24 23:28:42 -04:00
_att_error_rot_vec_rad . x = 0 ;
_att_error_rot_vec_rad . y = 0 ;
2014-08-20 10:14:25 -03:00
// update our earth-frame angle targets
2015-12-02 14:45:34 -04:00
Vector3f att_error_euler_rad ;
2015-11-24 20:18:35 -04:00
// convert angle error rotation vector into 321-intrinsic euler angle difference
// NOTE: this results an an approximation linearized about the vehicle's attitude
2015-12-05 04:28:48 -04:00
if ( ang_vel_to_euler_rate ( Vector3f ( _ahrs . roll , _ahrs . pitch , _ahrs . yaw ) , _att_error_rot_vec_rad , att_error_euler_rad ) ) {
2015-12-02 14:45:34 -04:00
_att_target_euler_rad . x = wrap_PI ( att_error_euler_rad . x + _ahrs . roll ) ;
_att_target_euler_rad . y = wrap_PI ( att_error_euler_rad . y + _ahrs . pitch ) ;
_att_target_euler_rad . z = wrap_2PI ( att_error_euler_rad . z + _ahrs . yaw ) ;
2014-08-22 10:50:33 -03:00
}
2014-08-20 10:14:25 -03:00
// handle flipping over pitch axis
2015-12-18 19:18:39 -04:00
if ( _att_target_euler_rad . y > M_PI_F / 2.0f ) {
_att_target_euler_rad . x = wrap_PI ( _att_target_euler_rad . x + M_PI_F ) ;
_att_target_euler_rad . y = wrap_PI ( M_PI_F - _att_target_euler_rad . x ) ;
_att_target_euler_rad . z = wrap_2PI ( _att_target_euler_rad . z + M_PI_F ) ;
2014-08-20 10:14:25 -03:00
}
2015-12-18 19:18:39 -04:00
if ( _att_target_euler_rad . y < - M_PI_F / 2.0f ) {
_att_target_euler_rad . x = wrap_PI ( _att_target_euler_rad . x + M_PI_F ) ;
_att_target_euler_rad . y = wrap_PI ( - M_PI_F - _att_target_euler_rad . x ) ;
_att_target_euler_rad . z = wrap_2PI ( _att_target_euler_rad . z + M_PI_F ) ;
2014-08-20 10:14:25 -03:00
}
// convert body-frame angle errors to body-frame rate targets
2015-11-24 23:28:42 -04:00
update_ang_vel_target_from_att_error ( ) ;
2014-08-20 10:14:25 -03:00
// set body-frame roll/pitch rate target to current desired rates which are the vehicle's actual rates
2015-11-24 23:28:42 -04:00
_ang_vel_target_rads . x = _att_target_ang_vel_rads . x ;
_ang_vel_target_rads . y = _att_target_ang_vel_rads . y ;
2014-08-20 10:14:25 -03:00
// add desired target to yaw
2015-11-24 23:28:42 -04:00
_ang_vel_target_rads . z + = _att_target_ang_vel_rads . z ;
2014-08-20 10:14:25 -03:00
}
2015-11-20 15:44:29 -04:00
// subclass non-passthrough too, for external gyro, no flybar
2015-11-28 13:56:25 -04:00
void AC_AttitudeControl_Heli : : input_rate_bf_roll_pitch_yaw ( float roll_rate_bf_cds , float pitch_rate_bf_cds , float yaw_rate_bf_cds )
2015-11-20 15:44:29 -04:00
{
2015-11-28 13:56:25 -04:00
_passthrough_yaw = yaw_rate_bf_cds ;
2015-11-20 15:44:29 -04:00
2015-11-28 13:56:25 -04:00
AC_AttitudeControl : : input_rate_bf_roll_pitch_yaw ( roll_rate_bf_cds , pitch_rate_bf_cds , yaw_rate_bf_cds ) ;
2015-11-20 15:44:29 -04:00
}
2014-01-29 09:50:32 -04:00
//
// rate controller (body-frame) methods
//
// rate_controller_run - run lowest level rate controller and send outputs to the motors
// should be called at 100hz or more
void AC_AttitudeControl_Heli : : rate_controller_run ( )
{
// call rate controllers and send output to motors object
2014-08-20 10:14:25 -03:00
// if using a flybar passthrough roll and pitch directly to motors
if ( _flags_heli . flybar_passthrough ) {
_motors . set_roll ( _passthrough_roll ) ;
_motors . set_pitch ( _passthrough_pitch ) ;
} else {
2015-11-24 23:28:42 -04:00
rate_bf_to_motor_roll_pitch ( _ang_vel_target_rads . x , _ang_vel_target_rads . y ) ;
2014-07-03 16:49:48 -03:00
}
2015-06-22 02:49:25 -03:00
if ( _flags_heli . tail_passthrough ) {
_motors . set_yaw ( _passthrough_yaw ) ;
} else {
2015-11-24 23:28:42 -04:00
_motors . set_yaw ( rate_bf_to_motor_yaw ( _ang_vel_target_rads . z ) ) ;
2015-06-22 02:49:25 -03:00
}
2014-01-29 09:50:32 -04:00
}
2015-06-23 10:41:47 -03:00
// get lean angle max for pilot input that prioritises altitude hold over lean angle
float AC_AttitudeControl_Heli : : get_althold_lean_angle_max ( ) const
{
// calc maximum tilt angle based on throttle
2015-11-24 17:11:50 -04:00
float ret = acosf ( constrain_float ( _throttle_in_filt . get ( ) / 900.0f , 0.0f , 1000.0f ) / 1000.0f ) ;
// TEMP: convert to centi-degrees for public interface
return degrees ( ret ) * 100.0f ;
2015-06-23 10:41:47 -03:00
}
2014-01-29 09:50:32 -04:00
//
// private methods
//
//
// body-frame rate controller
//
2015-11-24 17:11:50 -04:00
// rate_bf_to_motor_roll_pitch - ask the rate controller to calculate the motor outputs to achieve the target rate in radians/second
void AC_AttitudeControl_Heli : : rate_bf_to_motor_roll_pitch ( float rate_roll_target_rads , float rate_pitch_target_rads )
2014-01-29 09:50:32 -04:00
{
2014-05-02 21:42:43 -03:00
float roll_pd , roll_i , roll_ff ; // used to capture pid values
float pitch_pd , pitch_i , pitch_ff ; // used to capture pid values
2015-11-24 17:11:50 -04:00
float rate_roll_error_rads , rate_pitch_error_rads ; // simply target_rate - current_rate
2014-01-29 09:50:32 -04:00
float roll_out , pitch_out ;
2014-07-13 04:48:30 -03:00
const Vector3f & gyro = _ahrs . get_gyro ( ) ; // get current rates
2014-01-29 09:50:32 -04:00
// calculate error
2015-11-24 17:11:50 -04:00
rate_roll_error_rads = rate_roll_target_rads - gyro . x ;
rate_pitch_error_rads = rate_pitch_target_rads - gyro . y ;
2014-01-29 09:50:32 -04:00
2015-11-24 17:11:50 -04:00
// For legacy reasons, we convert to centi-degrees before inputting to the PID
_pid_rate_roll . set_input_filter_all ( degrees ( rate_roll_error_rads ) * 100.0f ) ;
_pid_rate_roll . set_desired_rate ( degrees ( rate_roll_target_rads ) * 100.0f ) ;
_pid_rate_pitch . set_input_filter_all ( degrees ( rate_pitch_error_rads ) * 100.0f ) ;
_pid_rate_pitch . set_desired_rate ( degrees ( rate_pitch_target_rads ) * 100.0f ) ;
2015-01-23 06:07:41 -04:00
2014-01-29 09:50:32 -04:00
// call p and d controllers
2015-01-23 06:07:41 -04:00
roll_pd = _pid_rate_roll . get_p ( ) + _pid_rate_roll . get_d ( ) ;
pitch_pd = _pid_rate_pitch . get_p ( ) + _pid_rate_pitch . get_d ( ) ;
2014-01-29 09:50:32 -04:00
// get roll i term
roll_i = _pid_rate_roll . get_integrator ( ) ;
// update i term as long as we haven't breached the limits or the I term will certainly reduce
2015-11-24 17:11:50 -04:00
if ( ! _flags_heli . limit_roll | | ( ( roll_i > 0 & & rate_roll_error_rads < 0 ) | | ( roll_i < 0 & & rate_roll_error_rads > 0 ) ) ) {
2015-08-08 00:03:01 -03:00
if ( _flags_heli . leaky_i ) {
roll_i = ( ( AC_HELI_PID & ) _pid_rate_roll ) . get_leaky_i ( AC_ATTITUDE_HELI_RATE_INTEGRATOR_LEAK_RATE ) ;
} else {
roll_i = _pid_rate_roll . get_i ( ) ;
}
2014-01-29 09:50:32 -04:00
}
// get pitch i term
pitch_i = _pid_rate_pitch . get_integrator ( ) ;
// update i term as long as we haven't breached the limits or the I term will certainly reduce
2015-11-24 17:11:50 -04:00
if ( ! _flags_heli . limit_pitch | | ( ( pitch_i > 0 & & rate_pitch_error_rads < 0 ) | | ( pitch_i < 0 & & rate_pitch_error_rads > 0 ) ) ) {
2015-08-08 00:03:01 -03:00
if ( _flags_heli . leaky_i ) {
pitch_i = ( ( AC_HELI_PID & ) _pid_rate_pitch ) . get_leaky_i ( AC_ATTITUDE_HELI_RATE_INTEGRATOR_LEAK_RATE ) ;
} else {
pitch_i = _pid_rate_pitch . get_i ( ) ;
}
2014-01-29 09:50:32 -04:00
}
2014-05-02 21:42:43 -03:00
2015-11-24 17:11:50 -04:00
// For legacy reasons, we convert to centi-degrees before inputting to the feedforward
roll_ff = roll_feedforward_filter . apply ( ( ( AC_HELI_PID & ) _pid_rate_roll ) . get_vff ( degrees ( rate_roll_target_rads ) * 100.0f ) , _dt ) ;
pitch_ff = pitch_feedforward_filter . apply ( ( ( AC_HELI_PID & ) _pid_rate_pitch ) . get_vff ( degrees ( rate_pitch_target_rads ) * 100.0f ) , _dt ) ;
2014-01-29 09:50:32 -04:00
// add feed forward and final output
2014-05-02 21:42:43 -03:00
roll_out = roll_pd + roll_i + roll_ff ;
pitch_out = pitch_pd + pitch_i + pitch_ff ;
2014-01-29 09:50:32 -04:00
// constrain output and update limit flags
2015-05-08 15:41:27 -03:00
if ( fabsf ( roll_out ) > AC_ATTITUDE_RATE_RP_CONTROLLER_OUT_MAX ) {
2014-01-29 09:50:32 -04:00
roll_out = constrain_float ( roll_out , - AC_ATTITUDE_RATE_RP_CONTROLLER_OUT_MAX , AC_ATTITUDE_RATE_RP_CONTROLLER_OUT_MAX ) ;
_flags_heli . limit_roll = true ;
} else {
_flags_heli . limit_roll = false ;
}
2015-05-08 15:41:27 -03:00
if ( fabsf ( pitch_out ) > AC_ATTITUDE_RATE_RP_CONTROLLER_OUT_MAX ) {
2014-01-29 09:50:32 -04:00
pitch_out = constrain_float ( pitch_out , - AC_ATTITUDE_RATE_RP_CONTROLLER_OUT_MAX , AC_ATTITUDE_RATE_RP_CONTROLLER_OUT_MAX ) ;
_flags_heli . limit_pitch = true ;
} else {
_flags_heli . limit_pitch = false ;
}
// output to motors
2014-02-10 00:24:11 -04:00
_motors . set_roll ( roll_out ) ;
_motors . set_pitch ( pitch_out ) ;
2014-01-29 09:50:32 -04:00
2015-09-21 23:35:53 -03:00
// Piro-Comp, or Pirouette Compensation is a pre-compensation calculation, which basically rotates the Roll and Pitch Rate I-terms as the
// helicopter rotates in yaw. Much of the built-up I-term is needed to tip the disk into the incoming wind. Fast yawing can create an instability
// as the built-up I-term in one axis must be reduced, while the other increases. This helps solve that by rotating the I-terms before the error occurs.
// It does assume that the rotor aerodynamics and mechanics are essentially symmetrical about the main shaft, which is a generally valid assumption.
if ( _piro_comp_enabled ) {
2014-01-29 09:50:32 -04:00
2015-09-21 23:35:53 -03:00
int32_t piro_roll_i , piro_pitch_i ; // used to hold I-terms while doing piro comp
2014-01-29 09:50:32 -04:00
piro_roll_i = roll_i ;
piro_pitch_i = pitch_i ;
Vector2f yawratevector ;
2015-09-21 23:35:53 -03:00
yawratevector . x = cosf ( - _ahrs . get_gyro ( ) . z * _dt ) ;
yawratevector . y = sinf ( - _ahrs . get_gyro ( ) . z * _dt ) ;
2014-01-29 09:50:32 -04:00
yawratevector . normalize ( ) ;
roll_i = piro_roll_i * yawratevector . x - piro_pitch_i * yawratevector . y ;
pitch_i = piro_pitch_i * yawratevector . x + piro_roll_i * yawratevector . y ;
2015-09-21 23:35:53 -03:00
_pid_rate_pitch . set_integrator ( pitch_i ) ;
_pid_rate_roll . set_integrator ( roll_i ) ;
2014-01-29 09:50:32 -04:00
}
2015-09-21 23:35:53 -03:00
2014-01-29 09:50:32 -04:00
}
2015-11-24 17:11:50 -04:00
// rate_bf_to_motor_yaw - ask the rate controller to calculate the motor outputs to achieve the target rate in radians/second
float AC_AttitudeControl_Heli : : rate_bf_to_motor_yaw ( float rate_target_rads )
2014-01-29 09:50:32 -04:00
{
2015-05-22 21:01:03 -03:00
float pd , i , vff , aff ; // used to capture pid values for logging
2015-11-24 17:11:50 -04:00
float current_rate_rads ; // this iteration's rate
float rate_error_rads ; // simply target_rate - current_rate
2014-01-30 03:19:59 -04:00
float yaw_out ;
2014-01-29 09:50:32 -04:00
// get current rate
// To-Do: make getting gyro rates more efficient?
2015-11-24 17:11:50 -04:00
current_rate_rads = _ahrs . get_gyro ( ) . z ;
2014-01-29 09:50:32 -04:00
// calculate error and call pid controller
2015-11-24 17:11:50 -04:00
rate_error_rads = rate_target_rads - current_rate_rads ;
2015-01-23 06:07:41 -04:00
2015-11-24 17:11:50 -04:00
// For legacy reasons, we convert to centi-degrees before inputting to the PID
_pid_rate_yaw . set_input_filter_all ( degrees ( rate_error_rads ) * 100.0f ) ;
_pid_rate_yaw . set_desired_rate ( degrees ( rate_target_rads ) * 100.0f ) ;
2015-01-23 06:07:41 -04:00
// get p and d
pd = _pid_rate_yaw . get_p ( ) + _pid_rate_yaw . get_d ( ) ;
2014-01-29 09:50:32 -04:00
// get i term
i = _pid_rate_yaw . get_integrator ( ) ;
// update i term as long as we haven't breached the limits or the I term will certainly reduce
2015-11-24 17:11:50 -04:00
if ( ! _flags_heli . limit_yaw | | ( ( i > 0 & & rate_error_rads < 0 ) | | ( i < 0 & & rate_error_rads > 0 ) ) ) {
2015-05-28 18:29:27 -03:00
if ( ( ( AP_MotorsHeli & ) _motors ) . rotor_runup_complete ( ) ) {
2015-01-23 06:07:41 -04:00
i = _pid_rate_yaw . get_i ( ) ;
2014-01-30 03:19:59 -04:00
} else {
2015-01-23 06:07:41 -04:00
i = ( ( AC_HELI_PID & ) _pid_rate_yaw ) . get_leaky_i ( AC_ATTITUDE_HELI_RATE_INTEGRATOR_LEAK_RATE ) ; // If motor is not running use leaky I-term to avoid excessive build-up
2014-01-30 03:19:59 -04:00
}
2014-01-29 09:50:32 -04:00
}
2014-05-10 11:17:32 -03:00
2015-11-24 17:11:50 -04:00
// For legacy reasons, we convert to centi-degrees before inputting to the feedforward
vff = yaw_velocity_feedforward_filter . apply ( ( ( AC_HELI_PID & ) _pid_rate_yaw ) . get_vff ( degrees ( rate_target_rads ) * 100.0f ) , _dt ) ;
aff = yaw_acceleration_feedforward_filter . apply ( ( ( AC_HELI_PID & ) _pid_rate_yaw ) . get_aff ( degrees ( rate_target_rads ) * 100.0f ) , _dt ) ;
2014-05-02 21:42:43 -03:00
2014-01-30 03:19:59 -04:00
// add feed forward
2015-05-22 21:01:03 -03:00
yaw_out = pd + i + vff + aff ;
2014-01-29 09:50:32 -04:00
2014-01-30 03:19:59 -04:00
// constrain output and update limit flag
2015-05-08 15:41:27 -03:00
if ( fabsf ( yaw_out ) > AC_ATTITUDE_RATE_YAW_CONTROLLER_OUT_MAX ) {
2014-01-30 03:19:59 -04:00
yaw_out = constrain_float ( yaw_out , - AC_ATTITUDE_RATE_YAW_CONTROLLER_OUT_MAX , AC_ATTITUDE_RATE_YAW_CONTROLLER_OUT_MAX ) ;
_flags_heli . limit_yaw = true ;
} else {
_flags_heli . limit_yaw = false ;
}
2014-01-29 09:50:32 -04:00
2014-01-30 03:19:59 -04:00
// output to motors
return yaw_out ;
2014-01-29 09:50:32 -04:00
}
//
// throttle functions
//
2015-04-03 15:52:20 -03:00
// returns a throttle including compensation for roll/pitch angle
2014-01-29 09:50:32 -04:00
// throttle value should be 0 ~ 1000
2015-04-03 15:52:20 -03:00
float AC_AttitudeControl_Heli : : get_boosted_throttle ( float throttle_in )
2014-01-29 09:50:32 -04:00
{
2014-01-30 20:59:50 -04:00
// no angle boost for trad helis
_angle_boost = 0 ;
2015-04-03 15:52:20 -03:00
return throttle_in ;
2014-01-29 09:50:32 -04:00
}