LCOV - code coverage report
Current view: top level - AC_AutoTune - AC_AutoTune_Multi.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 687 750 91.6 %
Date: 2026-09-09 11:02:08 Functions: 38 38 100.0 %

          Line data    Source code
       1             : #include "AC_AutoTune_config.h"
       2             : 
       3             : #if AC_AUTOTUNE_ENABLED
       4             : 
       5             : #include "AC_AutoTune_Multi.h"
       6             : 
       7             : #include <AP_Logger/AP_Logger.h>
       8             : #include <AP_Scheduler/AP_Scheduler.h>
       9             : #include <GCS_MAVLink/GCS.h>
      10             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      11             : 
      12             : /*
      13             :  * autotune support for multicopters
      14             :  *
      15             :  *
      16             :  * Instructions:
      17             :  *      1) Set up one flight mode switch position to be AltHold.
      18             :  *      2) Set the Ch7 Opt or Ch8 Opt to AutoTune to allow you to turn the auto tuning on/off with the ch7 or ch8 switch.
      19             :  *      3) Ensure the ch7 or ch8 switch is in the LOW position.
      20             :  *      4) Wait for a calm day and go to a large open area.
      21             :  *      5) Take off and put the vehicle into AltHold mode at a comfortable altitude.
      22             :  *      6) Set the ch7/ch8 switch to the HIGH position to engage auto tuning:
      23             :  *          a) You will see it twitch about 20 degrees left and right for a few minutes, then it will repeat forward and back.
      24             :  *          b) Use the roll and pitch stick at any time to reposition the copter if it drifts away (it will use the original PID gains during repositioning and between tests).
      25             :  *             When you release the sticks it will continue auto tuning where it left off.
      26             :  *          c) Move the ch7/ch8 switch into the LOW position at any time to abandon the autotuning and return to the origin PIDs.
      27             :  *          d) Make sure that you do not have any trim set on your transmitter or the autotune may not get the signal that the sticks are centered.
      28             :  *      7) When the tune completes the vehicle will change back to the original PID gains.
      29             :  *      8) Put the ch7/ch8 switch into the LOW position then back to the HIGH position to test the tuned PID gains.
      30             :  *      9) Put the ch7/ch8 switch into the LOW position to fly using the original PID gains.
      31             :  *      10) If you are happy with the autotuned PID gains, leave the ch7/ch8 switch in the HIGH position, land and disarm to save the PIDs permanently.
      32             :  *          If you DO NOT like the new PIDS, switch ch7/ch8 LOW to return to the original PIDs. The gains will not be saved when you disarm
      33             :  *
      34             :  * What it's doing during each "twitch":
      35             :  *      a) invokes 90 deg/sec rate request
      36             :  *      b) records maximum "forward" roll rate and bounce back rate
      37             :  *      c) when copter reaches 20 degrees or 1 second has passed, it commands level
      38             :  *      d) tries to keep max rotation rate between 80% ~ 100% of requested rate (90deg/sec) by adjusting rate P
      39             :  *      e) increases rate D until the bounce back becomes greater than 10% of requested rate (90deg/sec)
      40             :  *      f) decreases rate D until the bounce back becomes less than 10% of requested rate (90deg/sec)
      41             :  *      g) increases rate P until the max rotate rate becomes greater than the request rate (90deg/sec)
      42             :  *      h) invokes a 20deg angle request on roll or pitch
      43             :  *      i) increases stab P until the maximum angle becomes greater than 110% of the requested angle (20deg)
      44             :  *      j) decreases stab P by 25%
      45             :  *
      46             :  */
      47             : 
      48             : #define AUTOTUNE_TESTING_STEP_TIMEOUT_MS   2000U     // timeout for tuning mode's testing step
      49             : 
      50             : #define AUTOTUNE_RD_STEP                  0.05     // minimum increment when increasing/decreasing Rate D term
      51             : #define AUTOTUNE_RP_STEP                  0.05     // minimum increment when increasing/decreasing Rate P term
      52             : #define AUTOTUNE_SP_STEP                  0.05     // minimum increment when increasing/decreasing Stab P term
      53             : #define AUTOTUNE_PI_RATIO_FOR_TESTING      0.1     // I is set 10x smaller than P during testing
      54             : #define AUTOTUNE_PI_RATIO_FINAL            1.0     // I is set 1x P after testing
      55             : #define AUTOTUNE_YAW_PI_RATIO_FINAL        0.1     // I is set 1x P after testing
      56             : #define AUTOTUNE_RD_MAX                  0.200     // maximum Rate D value
      57             : #define AUTOTUNE_RLPF_MIN                  1.0     // minimum Rate Yaw filter value
      58             : #define AUTOTUNE_RLPF_MAX                  5.0     // maximum Rate Yaw filter value
      59             : #define AUTOTUNE_FLTE_MIN                  2.5     // minimum Rate Yaw error filter value
      60             : #define AUTOTUNE_RP_MIN                   0.01     // minimum Rate P value
      61             : #define AUTOTUNE_RP_MAX                    2.0     // maximum Rate P value
      62             : 
      63             : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
      64             :  #define AUTOTUNE_SP_MAX                   10.0     // maximum Stab P value
      65             : #else
      66             : #define AUTOTUNE_SP_MAX                   40.0     // maximum Stab P value
      67             : #endif
      68             : 
      69             : #define AUTOTUNE_SP_MIN                    0.5     // maximum Stab P value
      70             : #define AUTOTUNE_RP_ACCEL_MIN            4000.0    // Minimum acceleration for Roll and Pitch
      71             : #define AUTOTUNE_Y_ACCEL_MIN             1000.0    // Minimum acceleration for Yaw
      72             : #define AUTOTUNE_Y_FILT_FREQ              10.0     // Autotune filter frequency when testing Yaw
      73             : #define AUTOTUNE_D_UP_DOWN_MARGIN          0.2     // The margin below the target that we tune D in
      74             : #define AUTOTUNE_ACCEL_RP_BACKOFF          1.0     // back off from maximum acceleration
      75             : #define AUTOTUNE_ACCEL_Y_BACKOFF           1.0     // back off from maximum acceleration
      76             : 
      77             : // roll and pitch axes
      78             : #define AUTOTUNE_TARGET_RATE_RLLPIT_CDS     18000   // target roll/pitch rate during AUTOTUNE_STEP_TWITCHING step
      79             : #define AUTOTUNE_TARGET_MIN_RATE_RLLPIT_CDS 4500    // target min roll/pitch rate during AUTOTUNE_STEP_TWITCHING step
      80             : 
      81             : // yaw axis
      82             : #define AUTOTUNE_TARGET_RATE_YAW_CDS        9000        // target yaw rate during AUTOTUNE_STEP_TWITCHING step
      83             : #define AUTOTUNE_TARGET_MIN_RATE_YAW_CDS    1500        // minimum target yaw rate during AUTOTUNE_STEP_TWITCHING step
      84             : 
      85             : #define AUTOTUNE_TARGET_ANGLE_MAX_RP_SCALE  1.0 / 2.0   // minimum target angle, as a fraction of angle_max, during TESTING_RATE step that will cause us to move to next step
      86             : #define AUTOTUNE_TARGET_ANGLE_MAX_Y_SCALE   1.0         // minimum target angle, as a fraction of angle_max, during TESTING_RATE step that will cause us to move to next step
      87             : #define AUTOTUNE_TARGET_ANGLE_MIN_RP_SCALE  1.0 / 3.0   // minimum target angle, as a fraction of angle_max, during TESTING_RATE step that will cause us to move to next step
      88             : #define AUTOTUNE_TARGET_ANGLE_MIN_Y_SCALE   1.0 / 6.0   // minimum target angle, as a fraction of angle_max, during TESTING_RATE step that will cause us to move to next step
      89             : #define AUTOTUNE_ANGLE_ABORT_RP_SCALE       2.5 / 3.0   // maximum allowable angle during testing, as a fraction of angle_max
      90             : #define AUTOTUNE_ANGLE_MAX_Y_SCALE          1.0         // maximum allowable angle during testing, as a fraction of angle_max
      91             : #define AUTOTUNE_ANGLE_NEG_RP_SCALE         1.0 / 5.0   // maximum allowable angle during testing, as a fraction of angle_max
      92             : 
      93             : // second table of user settable parameters for quadplanes, this
      94             : // allows us to go beyond the 64 parameter limit
      95             : const AP_Param::GroupInfo AC_AutoTune_Multi::var_info[] = {
      96             : 
      97             :     // @Param: AXES
      98             :     // @DisplayName: Autotune axis bitmask
      99             :     // @Description: 1-byte bitmap of axes to autotune
     100             :     // @Bitmask: 0:Roll,1:Pitch,2:Yaw,3:YawD
     101             :     // @User: Standard
     102             :     AP_GROUPINFO("AXES", 1, AC_AutoTune_Multi, axis_bitmask,  7),  // AUTOTUNE_AXIS_BITMASK_DEFAULT
     103             : 
     104             :     // @Param: AGGR
     105             :     // @DisplayName: Autotune aggressiveness
     106             :     // @Description: Autotune aggressiveness. Defines the bounce back used to detect size of the D term.
     107             :     // @Range: 0.05 0.10
     108             :     // @User: Standard
     109             :     AP_GROUPINFO("AGGR", 2, AC_AutoTune_Multi, aggressiveness, 0.075f),
     110             : 
     111             :     // @Param: MIN_D
     112             :     // @DisplayName: AutoTune minimum D
     113             :     // @Description: Defines the minimum D gain
     114             :     // @Range: 0.0001 0.005
     115             :     // @User: Standard
     116             :     AP_GROUPINFO("MIN_D", 3, AC_AutoTune_Multi, min_d,  0.0005f),
     117             : 
     118             :     // @Param: GMBK
     119             :     // @DisplayName: AutoTune Gain Margin Backoff
     120             :     // @Description: Fraction by which tuned P and D gains are reduced after rate and angle AutoTune steps complete. This provides extra stability margin by reducing gains slightly from the optimal values found during tuning. A value of 0.0 applies no reduction. A value of 0.25 reduces tuned gains by 25%.
     121             :     // @Range: 0.0 0.5
     122             :     // @User: Standard
     123             :     AP_GROUPINFO("GMBK", 4, AC_AutoTune_Multi, gain_backoff,  0.25),
     124             : 
     125             :     AP_GROUPEND
     126             : };
     127             : 
     128             : // constructor
     129        1610 : AC_AutoTune_Multi::AC_AutoTune_Multi()
     130             : {
     131        1610 :     tune_seq[0] = TuneType::TUNE_COMPLETE;
     132        1610 :     AP_Param::setup_object_defaults(this, var_info);
     133        1610 : }
     134             : 
     135     1063537 : void AC_AutoTune_Multi::do_gcs_announcements()
     136             : {
     137     1063537 :     const uint32_t now_ms = AP_HAL::millis();
     138     1063537 :     if (now_ms - last_announce_ms < AUTOTUNE_ANNOUNCE_INTERVAL_MS) {
     139     1062153 :         return;
     140             :     }
     141        1384 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AutoTune: %s %s %u%%", get_axis_name(), get_tune_type_name(), (success_counter * (100/AUTOTUNE_SUCCESS_COUNT)));
     142        1384 :     last_announce_ms = now_ms;
     143             : }
     144             : 
     145             : // Prepares all tuning state variables and target values for a new twitch test.
     146        2182 : void AC_AutoTune_Multi::test_init()
     147             : {
     148             :     float target_max_rate;
     149        2182 :     switch (axis) {
     150         393 :     case AxisType::ROLL:
     151         393 :         angle_abort = target_angle_max_rp_cd();
     152         393 :         target_max_rate = MAX(AUTOTUNE_TARGET_MIN_RATE_RLLPIT_CDS, step_scaler * AUTOTUNE_TARGET_RATE_RLLPIT_CDS);
     153         393 :         target_rate = constrain_float(degrees(attitude_control->max_rate_step_bf_roll()) * 100.0, AUTOTUNE_TARGET_MIN_RATE_RLLPIT_CDS, target_max_rate);
     154         393 :         target_angle = constrain_float(degrees(attitude_control->max_angle_step_bf_roll()) * 100.0, target_angle_min_rp_cd(), target_angle_max_rp_cd());
     155         393 :         rotation_rate_filt.set_cutoff_frequency(attitude_control->get_rate_roll_pid().filt_D_hz() * 2.0);
     156         393 :         break;
     157             : 
     158         386 :     case AxisType::PITCH:
     159         386 :         angle_abort = target_angle_max_rp_cd();
     160         386 :         target_max_rate = MAX(AUTOTUNE_TARGET_MIN_RATE_RLLPIT_CDS, step_scaler * AUTOTUNE_TARGET_RATE_RLLPIT_CDS);
     161         386 :         target_rate = constrain_float(degrees(attitude_control->max_rate_step_bf_pitch()) * 100.0, AUTOTUNE_TARGET_MIN_RATE_RLLPIT_CDS, target_max_rate);
     162         386 :         target_angle = constrain_float(degrees(attitude_control->max_angle_step_bf_pitch()) * 100.0, target_angle_min_rp_cd(), target_angle_max_rp_cd());
     163         386 :         rotation_rate_filt.set_cutoff_frequency(attitude_control->get_rate_pitch_pid().filt_D_hz() * 2.0);
     164         386 :         break;
     165             : 
     166        1403 :     case AxisType::YAW:
     167             :     case AxisType::YAW_D:
     168        1403 :         angle_abort = target_angle_max_y_cd();
     169        1403 :         target_max_rate = MAX(AUTOTUNE_TARGET_MIN_RATE_YAW_CDS, step_scaler*AUTOTUNE_TARGET_RATE_YAW_CDS);
     170        1403 :         target_rate = constrain_float(degrees(attitude_control->max_rate_step_bf_yaw() * 0.75) * 100.0, AUTOTUNE_TARGET_MIN_RATE_YAW_CDS, target_max_rate);
     171        1403 :         target_angle = constrain_float(degrees(attitude_control->max_angle_step_bf_yaw() * 0.75) * 100.0, target_angle_min_y_cd(), target_angle_max_y_cd());
     172        1403 :         if (axis == AxisType::YAW_D) {
     173         760 :             rotation_rate_filt.set_cutoff_frequency(attitude_control->get_rate_yaw_pid().filt_D_hz() * 2.0);
     174             :         } else {
     175         643 :             rotation_rate_filt.set_cutoff_frequency(AUTOTUNE_Y_FILT_FREQ);
     176             :         }
     177        1403 :         break;
     178             :     }
     179             : 
     180        2182 :     if ((tune_type == TuneType::ANGLE_P_DOWN) || (tune_type == TuneType::ANGLE_P_UP)) {
     181             :         // TODO: Evaluate whether initializing rotation_rate_filt with start_rate improves results for other axes
     182         471 :         rotation_rate_filt.reset(start_rate);
     183             :     } else {
     184        1711 :         rotation_rate_filt.reset(0.0);
     185             :     }
     186        2182 :     angle_step_commanded = false;
     187        2182 :     test_rate_max = 0.0;
     188        2182 :     test_rate_min = 0.0;
     189        2182 :     test_angle_max = 0.0;
     190        2182 :     test_angle_min = 0.0;
     191        2182 :     accel_measure_rate_max = 0.0;
     192        2182 : }
     193             : 
     194      304666 : void AC_AutoTune_Multi::test_run(AxisType test_axis, const float dir_sign)
     195             : {
     196             :     // hold current attitude
     197             : 
     198      304666 :     if ((tune_type == TuneType::ANGLE_P_DOWN) || (tune_type == TuneType::ANGLE_P_UP)) {
     199             :         // step angle targets on first iteration
     200       93747 :         if (!angle_step_commanded) {
     201         471 :             angle_step_commanded = true;
     202             :             // Testing increasing stabilize P gain so will set lean angle target
     203         471 :             switch (test_axis) {
     204         172 :             case AxisType::ROLL:
     205             :                 // request roll to 20deg
     206         172 :                 attitude_control->input_angle_step_bf_roll_pitch_yaw_rad(dir_sign * cd_to_rad(target_angle), 0.0, 0.0);
     207         172 :                 break;
     208         172 :             case AxisType::PITCH:
     209             :                 // request pitch to 20deg
     210         172 :                 attitude_control->input_angle_step_bf_roll_pitch_yaw_rad(0.0, dir_sign * cd_to_rad(target_angle), 0.0);
     211         172 :                 break;
     212         127 :             case AxisType::YAW:
     213             :             case AxisType::YAW_D:
     214             :                 // request yaw to 20deg
     215         127 :                 attitude_control->input_angle_step_bf_roll_pitch_yaw_rad(0.0, 0.0, dir_sign * cd_to_rad(target_angle));
     216         127 :                 break;
     217             :             } 
     218             :         } else {
     219       93276 :             attitude_control->input_rate_bf_roll_pitch_yaw_rads(0.0, 0.0, 0.0);
     220             :         }
     221             :     } else {
     222             :         // Testing rate P and D gains so will set body-frame rate targets.
     223             :         // Rate controller will use existing body-frame rates and convert to motor outputs
     224             :         // for all axes except the one we override here.
     225      210919 :         switch (test_axis) {
     226        6220 :         case AxisType::ROLL:
     227             :             // override body-frame roll rate
     228        6220 :             attitude_control->input_rate_step_bf_roll_pitch_yaw_rads(dir_sign * cd_to_rad(target_rate + start_rate), 0.0f, 0.0f);
     229        6220 :             break;
     230        5964 :         case AxisType::PITCH:
     231             :             // override body-frame pitch rate
     232        5964 :             attitude_control->input_rate_step_bf_roll_pitch_yaw_rads(0.0f, dir_sign * cd_to_rad(target_rate + start_rate), 0.0f);
     233        5964 :             break;
     234      198735 :         case AxisType::YAW:
     235             :         case AxisType::YAW_D:
     236             :             // override body-frame yaw rate
     237      198735 :             attitude_control->input_rate_step_bf_roll_pitch_yaw_rads(0.0f, 0.0f, dir_sign * cd_to_rad(target_rate + start_rate));
     238      198735 :             break;
     239             :         }
     240             :     }
     241             : 
     242             :     // capture this iteration's rotation rate and lean angle
     243      304666 :     float gyro_reading = 0;
     244      304666 :     switch (test_axis) {
     245       32495 :     case AxisType::ROLL:
     246       32495 :         gyro_reading = ahrs_view->get_gyro().x;
     247       32495 :         lean_angle = dir_sign * (ahrs_view->roll_sensor - (int32_t)start_angle);
     248       32495 :         break;
     249       32162 :     case AxisType::PITCH:
     250       32162 :         gyro_reading = ahrs_view->get_gyro().y;
     251       32162 :         lean_angle = dir_sign * (ahrs_view->pitch_sensor - (int32_t)start_angle);
     252       32162 :         break;
     253      240009 :     case AxisType::YAW:
     254             :     case AxisType::YAW_D:
     255      240009 :         gyro_reading = ahrs_view->get_gyro().z;
     256      240009 :         lean_angle = dir_sign * wrap_180_cd(ahrs_view->yaw_sensor-(int32_t)start_angle);
     257      240009 :         break;
     258             :     }
     259             : 
     260             :     // Add filter to measurements
     261             :     float filter_value;
     262      304666 :     switch (tune_type) {
     263       93747 :     case TuneType::ANGLE_P_DOWN:
     264             :     case TuneType::ANGLE_P_UP:
     265       93747 :         filter_value = dir_sign * (degrees(gyro_reading) * 100.0);
     266       93747 :         break;
     267      210919 :     default:
     268      210919 :         filter_value = dir_sign * (degrees(gyro_reading) * 100.0 - start_rate);
     269      210919 :         break;
     270             :     }
     271      913998 :     rotation_rate = rotation_rate_filt.apply(filter_value,
     272      304666 :                     AP::scheduler().get_loop_period_s());
     273             : 
     274      304666 :     switch (tune_type) {
     275      112073 :     case TuneType::RATE_D_UP:
     276             :     case TuneType::RATE_D_DOWN:
     277      112073 :         twitching_test_rate(lean_angle, rotation_rate, target_rate, test_rate_min, test_rate_max, test_angle_min);
     278      112073 :         twitching_measure_acceleration(test_accel_max_cdss, rotation_rate, accel_measure_rate_max);
     279      112073 :         twitching_abort_rate(lean_angle, rotation_rate, angle_abort, test_rate_min, test_angle_min);
     280      112073 :         break;
     281       98846 :     case TuneType::RATE_P_UP:
     282       98846 :         twitching_test_rate(lean_angle, rotation_rate, target_rate * (1 + 0.5 * aggressiveness), test_rate_min, test_rate_max, test_angle_min);
     283       98846 :         twitching_measure_acceleration(test_accel_max_cdss, rotation_rate, accel_measure_rate_max);
     284       98846 :         twitching_abort_rate(lean_angle, rotation_rate, angle_abort, test_rate_min, test_angle_min);
     285       98846 :         break;
     286       93747 :     case TuneType::ANGLE_P_DOWN:
     287             :     case TuneType::ANGLE_P_UP:
     288       93747 :         twitching_test_angle(lean_angle, rotation_rate, target_angle * (1 + 0.5 * aggressiveness), test_angle_min, test_angle_max, test_rate_min, test_rate_max);
     289       93747 :         twitching_measure_acceleration(test_accel_max_cdss, rotation_rate - dir_sign * start_rate, accel_measure_rate_max);
     290       93747 :         break;
     291           0 :     case TuneType::RATE_FF_UP:
     292             :     case TuneType::MAX_GAINS:
     293             :     case TuneType::TUNE_CHECK:
     294             :         // this should never happen
     295           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     296           0 :         break;
     297           0 :     default:
     298           0 :         break;
     299             :     }
     300      304666 : }
     301             : 
     302             : // backup_gains_and_initialise - store current gains as originals
     303             : //  called before tuning starts to backup original gains
     304           5 : void AC_AutoTune_Multi::backup_gains_and_initialise()
     305             : {
     306           5 :     AC_AutoTune::backup_gains_and_initialise();
     307             : 
     308           5 :     aggressiveness.set(constrain_float(aggressiveness, 0.05, 0.2));
     309             : 
     310           5 :     orig_bf_feedforward = attitude_control->get_bf_feedforward();
     311             : 
     312             :     // backup original pids and initialise tuned pid values
     313           5 :     orig_roll_rp = attitude_control->get_rate_roll_pid().kP();
     314           5 :     orig_roll_ri = attitude_control->get_rate_roll_pid().kI();
     315           5 :     orig_roll_rd = attitude_control->get_rate_roll_pid().kD();
     316           5 :     orig_roll_rff = attitude_control->get_rate_roll_pid().ff();
     317           5 :     orig_roll_dff = attitude_control->get_rate_roll_pid().kDff();
     318           5 :     orig_roll_fltt = attitude_control->get_rate_roll_pid().filt_T_hz();
     319           5 :     orig_roll_smax = attitude_control->get_rate_roll_pid().slew_limit();
     320           5 :     orig_roll_sp = attitude_control->get_angle_roll_p().kP();
     321           5 :     orig_roll_accel_radss = attitude_control->get_accel_roll_max_radss();
     322           5 :     tune_roll_rp = attitude_control->get_rate_roll_pid().kP();
     323           5 :     tune_roll_rd = attitude_control->get_rate_roll_pid().kD();
     324           5 :     tune_roll_sp = attitude_control->get_angle_roll_p().kP();
     325           5 :     tune_roll_accel_radss = attitude_control->get_accel_roll_max_radss();
     326             : 
     327           5 :     orig_pitch_rp = attitude_control->get_rate_pitch_pid().kP();
     328           5 :     orig_pitch_ri = attitude_control->get_rate_pitch_pid().kI();
     329           5 :     orig_pitch_rd = attitude_control->get_rate_pitch_pid().kD();
     330           5 :     orig_pitch_rff = attitude_control->get_rate_pitch_pid().ff();
     331           5 :     orig_pitch_dff = attitude_control->get_rate_pitch_pid().kDff();
     332           5 :     orig_pitch_fltt = attitude_control->get_rate_pitch_pid().filt_T_hz();
     333           5 :     orig_pitch_smax = attitude_control->get_rate_pitch_pid().slew_limit();
     334           5 :     orig_pitch_sp = attitude_control->get_angle_pitch_p().kP();
     335           5 :     orig_pitch_accel_radss = attitude_control->get_accel_pitch_max_radss();
     336           5 :     tune_pitch_rp = attitude_control->get_rate_pitch_pid().kP();
     337           5 :     tune_pitch_rd = attitude_control->get_rate_pitch_pid().kD();
     338           5 :     tune_pitch_sp = attitude_control->get_angle_pitch_p().kP();
     339           5 :     tune_pitch_accel_radss = attitude_control->get_accel_pitch_max_radss();
     340             : 
     341           5 :     orig_yaw_rp = attitude_control->get_rate_yaw_pid().kP();
     342           5 :     orig_yaw_ri = attitude_control->get_rate_yaw_pid().kI();
     343           5 :     orig_yaw_rd = attitude_control->get_rate_yaw_pid().kD();
     344           5 :     orig_yaw_rff = attitude_control->get_rate_yaw_pid().ff();
     345           5 :     orig_yaw_dff = attitude_control->get_rate_yaw_pid().kDff();
     346           5 :     orig_yaw_fltt = attitude_control->get_rate_yaw_pid().filt_T_hz();
     347           5 :     orig_yaw_smax = attitude_control->get_rate_yaw_pid().slew_limit();
     348           5 :     orig_yaw_rLPF = attitude_control->get_rate_yaw_pid().filt_E_hz();
     349           5 :     orig_yaw_accel_radss = attitude_control->get_accel_yaw_max_radss();
     350           5 :     orig_yaw_sp = attitude_control->get_angle_yaw_p().kP();
     351           5 :     tune_yaw_rp = attitude_control->get_rate_yaw_pid().kP();
     352           5 :     tune_yaw_rd = attitude_control->get_rate_yaw_pid().kD();
     353           5 :     tune_yaw_rLPF = attitude_control->get_rate_yaw_pid().filt_E_hz();
     354           5 :     if (yaw_d_enabled() && is_zero(tune_yaw_rd)) {
     355           1 :         tune_yaw_rd = min_d;
     356             :     }
     357           5 :     if (yaw_enabled() && is_zero(tune_yaw_rLPF)) {
     358           0 :         tune_yaw_rLPF = AUTOTUNE_FLTE_MIN;
     359             :     }
     360           5 :     tune_yaw_sp = attitude_control->get_angle_yaw_p().kP();
     361           5 :     tune_yaw_accel_radss = attitude_control->get_accel_yaw_max_radss();
     362             : 
     363           5 :     LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_INITIALISED);
     364           5 : }
     365             : 
     366             : // load_orig_gains - set gains to their original values
     367             : //  called by stop and failed functions
     368          11 : void AC_AutoTune_Multi::load_orig_gains()
     369             : {
     370          11 :     attitude_control->use_sqrt_controller(true);
     371          11 :     attitude_control->bf_feedforward(orig_bf_feedforward);
     372          11 :     if (roll_enabled()) {
     373           9 :         if (!is_zero(orig_roll_rp)) {
     374           9 :             attitude_control->get_rate_roll_pid().set_kP(orig_roll_rp);
     375           9 :             attitude_control->get_rate_roll_pid().set_kI(orig_roll_ri);
     376           9 :             attitude_control->get_rate_roll_pid().set_kD(orig_roll_rd);
     377           9 :             attitude_control->get_rate_roll_pid().set_ff(orig_roll_rff);
     378           9 :             attitude_control->get_rate_roll_pid().set_kDff(orig_roll_dff);
     379           9 :             attitude_control->get_rate_roll_pid().set_filt_T_hz(orig_roll_fltt);
     380           9 :             attitude_control->get_rate_roll_pid().set_slew_limit(orig_roll_smax);
     381           9 :             attitude_control->get_angle_roll_p().set_kP(orig_roll_sp);
     382           9 :             attitude_control->set_accel_roll_max_radss(orig_roll_accel_radss);
     383             :         }
     384             :     }
     385          11 :     if (pitch_enabled()) {
     386           9 :         if (!is_zero(orig_pitch_rp)) {
     387           9 :             attitude_control->get_rate_pitch_pid().set_kP(orig_pitch_rp);
     388           9 :             attitude_control->get_rate_pitch_pid().set_kI(orig_pitch_ri);
     389           9 :             attitude_control->get_rate_pitch_pid().set_kD(orig_pitch_rd);
     390           9 :             attitude_control->get_rate_pitch_pid().set_ff(orig_pitch_rff);
     391           9 :             attitude_control->get_rate_pitch_pid().set_kDff(orig_pitch_dff);
     392           9 :             attitude_control->get_rate_pitch_pid().set_filt_T_hz(orig_pitch_fltt);
     393           9 :             attitude_control->get_rate_pitch_pid().set_slew_limit(orig_pitch_smax);
     394           9 :             attitude_control->get_angle_pitch_p().set_kP(orig_pitch_sp);
     395           9 :             attitude_control->set_accel_pitch_max_radss(orig_pitch_accel_radss);
     396             :         }
     397             :     }
     398          11 :     if (yaw_enabled() || yaw_d_enabled()) {
     399          11 :         if (!is_zero(orig_yaw_rp)) {
     400          11 :             attitude_control->get_rate_yaw_pid().set_kP(orig_yaw_rp);
     401          11 :             attitude_control->get_rate_yaw_pid().set_kI(orig_yaw_ri);
     402          11 :             attitude_control->get_rate_yaw_pid().set_kD(orig_yaw_rd);
     403          11 :             attitude_control->get_rate_yaw_pid().set_ff(orig_yaw_rff);
     404          11 :             attitude_control->get_rate_yaw_pid().set_kDff(orig_yaw_dff);
     405          11 :             attitude_control->get_rate_yaw_pid().set_filt_E_hz(orig_yaw_rLPF);
     406          11 :             attitude_control->get_rate_yaw_pid().set_filt_T_hz(orig_yaw_fltt);
     407          11 :             attitude_control->get_rate_yaw_pid().set_slew_limit(orig_yaw_smax);
     408          11 :             attitude_control->get_angle_yaw_p().set_kP(orig_yaw_sp);
     409          11 :             attitude_control->set_accel_yaw_max_radss(orig_yaw_accel_radss);
     410             :         }
     411             :     }
     412          11 : }
     413             : 
     414             : // load_tuned_gains - load tuned gains
     415           3 : void AC_AutoTune_Multi::load_tuned_gains()
     416             : {
     417           3 :     attitude_control->use_sqrt_controller(true);
     418           3 :     if (!attitude_control->get_bf_feedforward()) {
     419           0 :         attitude_control->bf_feedforward(true);
     420           0 :         attitude_control->set_accel_roll_max_radss(0.0);
     421           0 :         attitude_control->set_accel_pitch_max_radss(0.0);
     422             :     }
     423           3 :     if ((axes_completed & AUTOTUNE_AXIS_BITMASK_ROLL) && roll_enabled() && !is_zero(tune_roll_rp)) {
     424           3 :         attitude_control->get_rate_roll_pid().set_kP(tune_roll_rp);
     425           3 :         attitude_control->get_rate_roll_pid().set_kI(tune_roll_rp*AUTOTUNE_PI_RATIO_FINAL);
     426           3 :         attitude_control->get_rate_roll_pid().set_kD(tune_roll_rd);
     427           3 :         attitude_control->get_rate_roll_pid().set_ff(orig_roll_rff);
     428           3 :         attitude_control->get_rate_roll_pid().set_kDff(orig_roll_dff);
     429           3 :         attitude_control->get_angle_roll_p().set_kP(tune_roll_sp);
     430           3 :         attitude_control->set_accel_roll_max_radss(tune_roll_accel_radss);
     431             :     }
     432           3 :     if ((axes_completed & AUTOTUNE_AXIS_BITMASK_PITCH) && pitch_enabled() && !is_zero(tune_pitch_rp)) {
     433           3 :         attitude_control->get_rate_pitch_pid().set_kP(tune_pitch_rp);
     434           3 :         attitude_control->get_rate_pitch_pid().set_kI(tune_pitch_rp*AUTOTUNE_PI_RATIO_FINAL);
     435           3 :         attitude_control->get_rate_pitch_pid().set_kD(tune_pitch_rd);
     436           3 :         attitude_control->get_rate_pitch_pid().set_ff(orig_pitch_rff);
     437           3 :         attitude_control->get_rate_pitch_pid().set_kDff(orig_pitch_dff);
     438           3 :         attitude_control->get_angle_pitch_p().set_kP(tune_pitch_sp);
     439           3 :         attitude_control->set_accel_pitch_max_radss(tune_pitch_accel_radss);
     440             :     }
     441           3 :     if ((((axes_completed & AUTOTUNE_AXIS_BITMASK_YAW) && yaw_enabled())
     442           6 :             || ((axes_completed & AUTOTUNE_AXIS_BITMASK_YAW_D) && yaw_d_enabled())) && !is_zero(tune_yaw_rp)) {
     443           3 :         attitude_control->get_rate_yaw_pid().set_kP(tune_yaw_rp);
     444           3 :         attitude_control->get_rate_yaw_pid().set_kI(tune_yaw_rp*AUTOTUNE_YAW_PI_RATIO_FINAL);
     445           3 :         if (yaw_d_enabled()) {
     446           0 :             attitude_control->get_rate_yaw_pid().set_kD(tune_yaw_rd);
     447             :         }
     448           3 :         if (yaw_enabled()) {
     449           3 :             attitude_control->get_rate_yaw_pid().set_filt_E_hz(tune_yaw_rLPF);
     450             :         }
     451           3 :         attitude_control->get_rate_yaw_pid().set_ff(orig_yaw_rff);
     452           3 :         attitude_control->get_rate_yaw_pid().set_kDff(orig_yaw_dff);
     453           3 :         attitude_control->get_angle_yaw_p().set_kP(tune_yaw_sp);
     454           3 :         attitude_control->set_accel_yaw_max_radss(tune_yaw_accel_radss);
     455             :     }
     456           3 : }
     457             : 
     458             : // load_intra_test_gains - gains used between tests
     459             : //  called during testing mode's update-gains step to set gains ahead of return-to-level step
     460        2187 : void AC_AutoTune_Multi::load_intra_test_gains()
     461             : {
     462             :     // we are restarting tuning so reset gains to tuning-start gains (i.e. low I term)
     463             :     // sanity check the gains
     464        2187 :     attitude_control->use_sqrt_controller(true);
     465        2187 :     attitude_control->bf_feedforward(true);
     466        2187 :     if (roll_enabled()) {
     467        1295 :         attitude_control->get_rate_roll_pid().set_kP(orig_roll_rp);
     468        1295 :         attitude_control->get_rate_roll_pid().set_kI(orig_roll_rp*AUTOTUNE_PI_RATIO_FOR_TESTING);
     469        1295 :         attitude_control->get_rate_roll_pid().set_kD(orig_roll_rd);
     470        1295 :         attitude_control->get_rate_roll_pid().set_ff(orig_roll_rff);
     471        1295 :         attitude_control->get_rate_roll_pid().set_kDff(orig_roll_dff);
     472        1295 :         attitude_control->get_rate_roll_pid().set_filt_T_hz(orig_roll_fltt);
     473        1295 :         attitude_control->get_rate_roll_pid().set_slew_limit(orig_roll_smax);
     474        1295 :         attitude_control->get_angle_roll_p().set_kP(orig_roll_sp);
     475             :     }
     476        2187 :     if (pitch_enabled()) {
     477        1295 :         attitude_control->get_rate_pitch_pid().set_kP(orig_pitch_rp);
     478        1295 :         attitude_control->get_rate_pitch_pid().set_kI(orig_pitch_rp*AUTOTUNE_PI_RATIO_FOR_TESTING);
     479        1295 :         attitude_control->get_rate_pitch_pid().set_kD(orig_pitch_rd);
     480        1295 :         attitude_control->get_rate_pitch_pid().set_ff(orig_pitch_rff);
     481        1295 :         attitude_control->get_rate_pitch_pid().set_kDff(orig_pitch_dff);
     482        1295 :         attitude_control->get_rate_pitch_pid().set_filt_T_hz(orig_pitch_fltt);
     483        1295 :         attitude_control->get_rate_pitch_pid().set_slew_limit(orig_pitch_smax);
     484        1295 :         attitude_control->get_angle_pitch_p().set_kP(orig_pitch_sp);
     485             :     }
     486        2187 :     if (yaw_enabled() || yaw_d_enabled()) {
     487        2187 :         attitude_control->get_rate_yaw_pid().set_kP(orig_yaw_rp);
     488        2187 :         attitude_control->get_rate_yaw_pid().set_kI(orig_yaw_rp*AUTOTUNE_PI_RATIO_FOR_TESTING);
     489        2187 :         attitude_control->get_rate_yaw_pid().set_kD(orig_yaw_rd);
     490        2187 :         attitude_control->get_rate_yaw_pid().set_ff(orig_yaw_rff);
     491        2187 :         attitude_control->get_rate_yaw_pid().set_kDff(orig_yaw_dff);
     492        2187 :         attitude_control->get_rate_yaw_pid().set_filt_T_hz(orig_yaw_fltt);
     493        2187 :         attitude_control->get_rate_yaw_pid().set_slew_limit(orig_yaw_smax);
     494        2187 :         attitude_control->get_rate_yaw_pid().set_filt_E_hz(orig_yaw_rLPF);
     495        2187 :         attitude_control->get_angle_yaw_p().set_kP(orig_yaw_sp);
     496             :     }
     497        2187 : }
     498             : 
     499             : // load_test_gains - load the to-be-tested gains for a single axis
     500             : // called by control_attitude() just before it beings testing a gain (i.e. just before it twitches)
     501        2182 : void AC_AutoTune_Multi::load_test_gains()
     502             : {
     503        2182 :     attitude_control->use_sqrt_controller(false);
     504        2182 :     switch (axis) {
     505         393 :     case AxisType::ROLL:
     506         393 :         attitude_control->get_rate_roll_pid().set_kP(tune_roll_rp);
     507         393 :         attitude_control->get_rate_roll_pid().set_kI(tune_roll_rp * 0.01);
     508         393 :         attitude_control->get_rate_roll_pid().set_kD(tune_roll_rd);
     509         393 :         attitude_control->get_rate_roll_pid().set_ff(0.0);
     510         393 :         attitude_control->get_rate_roll_pid().set_kDff(0.0);
     511         393 :         attitude_control->get_rate_roll_pid().set_filt_T_hz(0.0);
     512         393 :         attitude_control->get_rate_roll_pid().set_slew_limit(0.0);
     513         393 :         attitude_control->get_angle_roll_p().set_kP(tune_roll_sp);
     514         393 :         break;
     515         386 :     case AxisType::PITCH:
     516         386 :         attitude_control->get_rate_pitch_pid().set_kP(tune_pitch_rp);
     517         386 :         attitude_control->get_rate_pitch_pid().set_kI(tune_pitch_rp * 0.01);
     518         386 :         attitude_control->get_rate_pitch_pid().set_kD(tune_pitch_rd);
     519         386 :         attitude_control->get_rate_pitch_pid().set_ff(0.0);
     520         386 :         attitude_control->get_rate_pitch_pid().set_kDff(0.0);
     521         386 :         attitude_control->get_rate_pitch_pid().set_filt_T_hz(0.0);
     522         386 :         attitude_control->get_rate_pitch_pid().set_slew_limit(0.0);
     523         386 :         attitude_control->get_angle_pitch_p().set_kP(tune_pitch_sp);
     524         386 :         break;
     525        1403 :     case AxisType::YAW:
     526             :     case AxisType::YAW_D:
     527        1403 :         attitude_control->get_rate_yaw_pid().set_kP(tune_yaw_rp);
     528        1403 :         attitude_control->get_rate_yaw_pid().set_kI(tune_yaw_rp * 0.01);
     529        1403 :         attitude_control->get_rate_yaw_pid().set_ff(0.0);
     530        1403 :         attitude_control->get_rate_yaw_pid().set_kDff(0.0);
     531        1403 :         if (axis == AxisType::YAW_D) {
     532         760 :             attitude_control->get_rate_yaw_pid().set_kD(tune_yaw_rd);
     533             :         } else {
     534         643 :             attitude_control->get_rate_yaw_pid().set_kD(0.0);
     535         643 :             attitude_control->get_rate_yaw_pid().set_filt_E_hz(tune_yaw_rLPF);
     536             :         }
     537        1403 :         attitude_control->get_rate_yaw_pid().set_filt_T_hz(0.0);
     538        1403 :         attitude_control->get_rate_yaw_pid().set_slew_limit(0.0);
     539        1403 :         attitude_control->get_angle_yaw_p().set_kP(tune_yaw_sp);
     540        1403 :         break;
     541             :     }
     542        2182 : }
     543             : 
     544             : // save_tuning_gains - save the final tuned gains for each axis
     545             : // save discovered gains to eeprom if autotuner is enabled (i.e. switch is in the high position)
     546           3 : void AC_AutoTune_Multi::save_tuning_gains()
     547             : {
     548             :     // see if we successfully completed tuning of at least one axis
     549           3 :     if (axes_completed == 0) {
     550           0 :         return;
     551             :     }
     552             : 
     553           3 :     if (!attitude_control->get_bf_feedforward()) {
     554           0 :         attitude_control->bf_feedforward_save(true);
     555           0 :         attitude_control->save_accel_roll_max_radss(0.0);
     556           0 :         attitude_control->save_accel_pitch_max_radss(0.0);
     557             :     }
     558             : 
     559             :     // sanity check the rate P values
     560           3 :     if ((axes_completed & AUTOTUNE_AXIS_BITMASK_ROLL) && roll_enabled() && !is_zero(tune_roll_rp)) {
     561             :         // rate roll gains
     562           3 :         attitude_control->get_rate_roll_pid().set_kP(tune_roll_rp);
     563           3 :         attitude_control->get_rate_roll_pid().set_kI(tune_roll_rp*AUTOTUNE_PI_RATIO_FINAL);
     564           3 :         attitude_control->get_rate_roll_pid().set_kD(tune_roll_rd);
     565           3 :         attitude_control->get_rate_roll_pid().set_ff(orig_roll_rff);
     566           3 :         attitude_control->get_rate_roll_pid().set_kDff(orig_roll_dff);
     567           3 :         attitude_control->get_rate_roll_pid().set_filt_T_hz(orig_roll_fltt);
     568           3 :         attitude_control->get_rate_roll_pid().set_slew_limit(orig_roll_smax);
     569           3 :         attitude_control->get_rate_roll_pid().save_gains();
     570             : 
     571             :         // stabilize roll
     572           3 :         attitude_control->get_angle_roll_p().set_kP(tune_roll_sp);
     573           3 :         attitude_control->get_angle_roll_p().save_gains();
     574             : 
     575             :         // acceleration roll
     576           3 :         attitude_control->save_accel_roll_max_radss(tune_roll_accel_radss);
     577             : 
     578             :         // resave pids to originals in case the autotune is run again
     579           3 :         orig_roll_rp = attitude_control->get_rate_roll_pid().kP();
     580           3 :         orig_roll_ri = attitude_control->get_rate_roll_pid().kI();
     581           3 :         orig_roll_rd = attitude_control->get_rate_roll_pid().kD();
     582           3 :         orig_roll_rff = attitude_control->get_rate_roll_pid().ff();
     583           3 :         orig_roll_dff = attitude_control->get_rate_roll_pid().kDff();
     584           3 :         orig_roll_sp = attitude_control->get_angle_roll_p().kP();
     585           3 :         orig_roll_accel_radss = attitude_control->get_accel_roll_max_radss();
     586             :     }
     587             : 
     588           3 :     if ((axes_completed & AUTOTUNE_AXIS_BITMASK_PITCH) && pitch_enabled() && !is_zero(tune_pitch_rp)) {
     589             :         // rate pitch gains
     590           3 :         attitude_control->get_rate_pitch_pid().set_kP(tune_pitch_rp);
     591           3 :         attitude_control->get_rate_pitch_pid().set_kI(tune_pitch_rp*AUTOTUNE_PI_RATIO_FINAL);
     592           3 :         attitude_control->get_rate_pitch_pid().set_kD(tune_pitch_rd);
     593           3 :         attitude_control->get_rate_pitch_pid().set_ff(orig_pitch_rff);
     594           3 :         attitude_control->get_rate_pitch_pid().set_kDff(orig_pitch_dff);
     595           3 :         attitude_control->get_rate_pitch_pid().set_filt_T_hz(orig_pitch_fltt);
     596           3 :         attitude_control->get_rate_pitch_pid().set_slew_limit(orig_pitch_smax);
     597           3 :         attitude_control->get_rate_pitch_pid().save_gains();
     598             : 
     599             :         // stabilize pitch
     600           3 :         attitude_control->get_angle_pitch_p().set_kP(tune_pitch_sp);
     601           3 :         attitude_control->get_angle_pitch_p().save_gains();
     602             : 
     603             :         // acceleration pitch
     604           3 :         attitude_control->save_accel_pitch_max_radss(tune_pitch_accel_radss);
     605             : 
     606             :         // resave pids to originals in case the autotune is run again
     607           3 :         orig_pitch_rp = attitude_control->get_rate_pitch_pid().kP();
     608           3 :         orig_pitch_ri = attitude_control->get_rate_pitch_pid().kI();
     609           3 :         orig_pitch_rd = attitude_control->get_rate_pitch_pid().kD();
     610           3 :         orig_pitch_rff = attitude_control->get_rate_pitch_pid().ff();
     611           3 :         orig_pitch_dff = attitude_control->get_rate_pitch_pid().kDff();
     612           3 :         orig_pitch_sp = attitude_control->get_angle_pitch_p().kP();
     613           3 :         orig_pitch_accel_radss = attitude_control->get_accel_pitch_max_radss();
     614             :     }
     615             : 
     616           3 :     if ((((axes_completed & AUTOTUNE_AXIS_BITMASK_YAW) && yaw_enabled())
     617           6 :             || ((axes_completed & AUTOTUNE_AXIS_BITMASK_YAW_D) && yaw_d_enabled())) && !is_zero(tune_yaw_rp)) {
     618             :         // rate yaw gains
     619           3 :         attitude_control->get_rate_yaw_pid().set_kP(tune_yaw_rp);
     620           3 :         attitude_control->get_rate_yaw_pid().set_kI(tune_yaw_rp*AUTOTUNE_YAW_PI_RATIO_FINAL);
     621           3 :         attitude_control->get_rate_yaw_pid().set_ff(orig_yaw_rff);
     622           3 :         attitude_control->get_rate_yaw_pid().set_kDff(orig_yaw_dff);
     623           3 :         attitude_control->get_rate_yaw_pid().set_filt_T_hz(orig_yaw_fltt);
     624           3 :         attitude_control->get_rate_yaw_pid().set_slew_limit(orig_yaw_smax);
     625           3 :         if (yaw_d_enabled()) {
     626           0 :             attitude_control->get_rate_yaw_pid().set_kD(tune_yaw_rd);
     627             :         }
     628           3 :         if (yaw_enabled()) {
     629           3 :             attitude_control->get_rate_yaw_pid().set_filt_E_hz(tune_yaw_rLPF);
     630             :         }
     631           3 :         attitude_control->get_rate_yaw_pid().save_gains();
     632             : 
     633             :         // stabilize yaw
     634           3 :         attitude_control->get_angle_yaw_p().set_kP(tune_yaw_sp);
     635           3 :         attitude_control->get_angle_yaw_p().save_gains();
     636             : 
     637             :         // acceleration yaw
     638           3 :         attitude_control->save_accel_yaw_max_radss(tune_yaw_accel_radss);
     639             : 
     640             :         // resave pids to originals in case the autotune is run again
     641           3 :         orig_yaw_rp = attitude_control->get_rate_yaw_pid().kP();
     642           3 :         orig_yaw_ri = attitude_control->get_rate_yaw_pid().kI();
     643           3 :         orig_yaw_rd = attitude_control->get_rate_yaw_pid().kD();
     644           3 :         orig_yaw_rff = attitude_control->get_rate_yaw_pid().ff();
     645           3 :         orig_yaw_dff = attitude_control->get_rate_yaw_pid().kDff();
     646           3 :         orig_yaw_rLPF = attitude_control->get_rate_yaw_pid().filt_E_hz();
     647           3 :         orig_yaw_sp = attitude_control->get_angle_yaw_p().kP();
     648           3 :         orig_yaw_accel_radss = attitude_control->get_accel_yaw_max_radss();
     649             :     }
     650             : 
     651             :     // update GCS and log save gains event
     652           3 :     update_gcs(AUTOTUNE_MESSAGE_SAVED_GAINS);
     653           3 :     LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_SAVEDGAINS);
     654             : 
     655           3 :     reset();
     656             : }
     657             : 
     658             : // report final gains for a given axis to GCS
     659          14 : void AC_AutoTune_Multi::report_final_gains(AxisType test_axis) const
     660             : {
     661          14 :     switch (test_axis) {
     662           4 :         case AxisType::ROLL:
     663           4 :             report_axis_gains("Roll", tune_roll_rp, tune_roll_rp*AUTOTUNE_PI_RATIO_FINAL, tune_roll_rd, tune_roll_sp, tune_roll_accel_radss);
     664           4 :             break;
     665           4 :         case AxisType::PITCH:
     666           4 :             report_axis_gains("Pitch", tune_pitch_rp, tune_pitch_rp*AUTOTUNE_PI_RATIO_FINAL, tune_pitch_rd, tune_pitch_sp, tune_pitch_accel_radss);
     667           4 :             break;
     668           5 :         case AxisType::YAW:
     669           5 :             report_axis_gains("Yaw(E)", tune_yaw_rp, tune_yaw_rp*AUTOTUNE_YAW_PI_RATIO_FINAL, 0, tune_yaw_sp, tune_yaw_accel_radss);
     670           5 :             break;
     671           1 :         case AxisType::YAW_D:
     672           1 :             report_axis_gains("Yaw(D)", tune_yaw_rp, tune_yaw_rp*AUTOTUNE_YAW_PI_RATIO_FINAL, tune_yaw_rd, tune_yaw_sp, tune_yaw_accel_radss);
     673           1 :             break;
     674             :     }
     675          14 : }
     676             : 
     677             : // report gain formatting helper
     678          14 : void AC_AutoTune_Multi::report_axis_gains(const char* axis_string, float rate_P, float rate_I, float rate_D, float angle_P, float max_accel_radss) const
     679             : {
     680          14 :     GCS_SEND_TEXT(MAV_SEVERITY_NOTICE,"AutoTune: %s complete", axis_string);
     681          14 :     GCS_SEND_TEXT(MAV_SEVERITY_NOTICE,"AutoTune: %s Rate: P:%0.3f, I:%0.3f, D:%0.4f", axis_string, rate_P, rate_I,rate_D);
     682          14 :     GCS_SEND_TEXT(MAV_SEVERITY_NOTICE,"AutoTune: %s Angle P:%0.3f, Max Accel:%0.0f", axis_string, angle_P, rad_to_cd(max_accel_radss));
     683          14 : }
     684             : 
     685             : // Measures peak angular rates and bounce-back behavior during rate tuning.
     686             : // Updates step timer and determines if tuning step is complete.
     687      210919 : void AC_AutoTune_Multi::twitching_test_rate(float angle, float rate, float rate_target_max, float &meas_rate_min, float &meas_rate_max, float &meas_angle_min)
     688             : {
     689      210919 :     const uint32_t now_ms = AP_HAL::millis();
     690             : 
     691             :     // capture maximum rate
     692      210919 :     if (rate > meas_rate_max) {
     693             :         // the measurement is continuing to increase without stopping
     694      126105 :         meas_rate_max = rate;
     695      126105 :         meas_rate_min = rate;
     696      126105 :         meas_angle_min = angle;
     697             :     }
     698             : 
     699             :     // capture minimum measurement after the measurement has peaked (aka "bounce back")
     700      210919 :     if ((rate < meas_rate_min) && (meas_rate_max > rate_target_max * 0.25)) {
     701             :         // the measurement is bouncing back
     702       62600 :         meas_rate_min = rate;
     703       62600 :         meas_angle_min = angle;
     704             :     }
     705             : 
     706             :     // calculate early stopping time based on the time it takes to get to 63.21%
     707      210919 :     if (meas_rate_max < rate_target_max * 0.6321) {
     708             :         // the measurement not reached the 63.21% threshold yet
     709       95083 :         step_timeout_ms = (now_ms - step_start_time_ms) * 3;
     710       95083 :         step_timeout_ms = MIN(step_timeout_ms, AUTOTUNE_TESTING_STEP_TIMEOUT_MS);
     711             :     }
     712             : 
     713      210919 :     if (meas_rate_max > rate_target_max) {
     714             :         // the measured rate has passed the maximum target rate
     715         376 :         step = Step::UPDATE_GAINS;
     716             :     }
     717             : 
     718      210919 :     if (meas_rate_max - meas_rate_min > meas_rate_max * aggressiveness) {
     719             :         // the measurement has passed 50% of the maximum rate and bounce back is larger than the threshold
     720         707 :         step = Step::UPDATE_GAINS;
     721             :     }
     722             : 
     723      210919 :     if (now_ms - step_start_time_ms >= step_timeout_ms) {
     724             :         // we have passed the maximum stop time
     725         540 :         step = Step::UPDATE_GAINS;
     726             :     }
     727      210919 : }
     728             : 
     729             : // twitching_test_rate - twitching tests
     730             : // update min and max and test for end conditions
     731      210919 : void AC_AutoTune_Multi::twitching_abort_rate(float angle, float rate, float angle_max, float meas_rate_min, float angle_min)
     732             : {
     733      210919 :     if (angle >= angle_max) {
     734         120 :         if (is_equal(rate, meas_rate_min) || (angle_min > 0.95 * angle_max)) {
     735             :             // we have reached the angle limit before completing the measurement of maximum and minimum
     736             :             // reduce the maximum target rate
     737          85 :             if (step_scaler > 0.2f) {
     738          85 :                 step_scaler *= 0.9f;
     739             :             } else {
     740           0 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
     741           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "AutoTune: Twitch Size Determination Failed");
     742           0 :                 mode = TuneMode::FAILED;
     743           0 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_FAILED);
     744             :             }
     745             :             // ignore result and start test again
     746          85 :             step = Step::ABORT;
     747             :         } else {
     748          35 :             step = Step::UPDATE_GAINS;
     749             :         }
     750             :     }
     751      210919 : }
     752             : 
     753             : // twitching_test_angle - twitching tests
     754             : // update min and max and test for end conditions
     755       93747 : void AC_AutoTune_Multi::twitching_test_angle(float angle, float rate, float angle_target_max, float &meas_angle_min, float &meas_angle_max, float &meas_rate_min, float &meas_rate_max)
     756             : {
     757       93747 :     const uint32_t now_ms = AP_HAL::millis();
     758             : 
     759             :     // capture maximum angle
     760       93747 :     if (angle > meas_angle_max) {
     761             :         // the angle still increasing
     762       77286 :         meas_angle_max = angle;
     763       77286 :         meas_angle_min = angle;
     764             :     }
     765             : 
     766             :     // capture minimum angle after we have reached a reasonable maximum angle
     767       93747 :     if ((angle < meas_angle_min) && (meas_angle_max > angle_target_max * 0.25)) {
     768             :         // the measurement is bouncing back
     769        4379 :         meas_angle_min = angle;
     770             :     }
     771             : 
     772             :     // capture maximum rate
     773       93747 :     if (rate > meas_rate_max) {
     774             :         // the measurement is still increasing
     775       18019 :         meas_rate_max = rate;
     776       18019 :         meas_rate_min = rate;
     777             :     }
     778             : 
     779             :     // capture minimum rate after we have reached maximum rate
     780       93747 :     if (rate < meas_rate_min) {
     781             :         // the measurement is still decreasing
     782       69841 :         meas_rate_min = rate;
     783             :     }
     784             : 
     785             :     // calculate early stopping time based on the time it takes to get to 63.21%
     786       93747 :     if (meas_angle_max < angle_target_max * 0.6321) {
     787             :         // the measurement not reached the 63.21% threshold yet
     788       33216 :         step_timeout_ms = (now_ms - step_start_time_ms) * 3;
     789       33216 :         step_timeout_ms = MIN(step_timeout_ms, AUTOTUNE_TESTING_STEP_TIMEOUT_MS);
     790             :     }
     791             : 
     792       93747 :     if (meas_angle_max > angle_target_max) {
     793             :         // the measurement has passed the maximum angle
     794          32 :         step = Step::UPDATE_GAINS;
     795             :     }
     796             : 
     797       93747 :     if (meas_angle_max - meas_angle_min > meas_angle_max * aggressiveness) {
     798             :         // the measurement has passed 50% of the maximum angle and bounce back is larger than the threshold
     799           0 :         step = Step::UPDATE_GAINS;
     800             :     }
     801             : 
     802       93747 :     if (now_ms - step_start_time_ms >= step_timeout_ms) {
     803             :         // we have passed the maximum stop time
     804         439 :         step = Step::UPDATE_GAINS;
     805             :     }
     806       93747 : }
     807             : 
     808             : // twitching_measure_acceleration - measure rate of change of measurement
     809      304666 : void AC_AutoTune_Multi::twitching_measure_acceleration(float &accel_average, float rate, float &rate_max) const
     810             : {
     811      304666 :     if (rate_max < rate) {
     812      143210 :         rate_max = rate;
     813      143210 :         accel_average = (1000.0 * rate_max) / (AP_HAL::millis() - step_start_time_ms);
     814             :     }
     815      304666 : }
     816             : 
     817             : // update gains for the rate p up tune type
     818         543 : void AC_AutoTune_Multi::updating_rate_p_up_all(AxisType test_axis)
     819             : {
     820         543 :     switch (test_axis) {
     821         102 :     case AxisType::ROLL:
     822         102 :         updating_rate_p_up_d_down(tune_roll_rd, min_d, AUTOTUNE_RD_STEP, tune_roll_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     823         102 :         break;
     824         102 :     case AxisType::PITCH:
     825         102 :         updating_rate_p_up_d_down(tune_pitch_rd, min_d, AUTOTUNE_RD_STEP, tune_pitch_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     826         102 :         break;
     827         337 :     case AxisType::YAW:
     828         337 :         updating_rate_p_up_d_down(tune_yaw_rLPF, AUTOTUNE_RLPF_MIN, AUTOTUNE_RD_STEP, tune_yaw_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max, false);
     829         337 :         break;
     830           2 :     case AxisType::YAW_D:
     831           2 :         updating_rate_p_up_d_down(tune_yaw_rd, min_d, AUTOTUNE_RD_STEP, tune_yaw_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     832           2 :         break;
     833             :     }
     834         543 : }
     835             : 
     836             : // update gains for the rate d up tune type
     837         652 : void AC_AutoTune_Multi::updating_rate_d_up_all(AxisType test_axis)
     838             : {
     839         652 :     switch (test_axis) {
     840          26 :     case AxisType::ROLL:
     841          26 :         updating_rate_d_up(tune_roll_rd, min_d, AUTOTUNE_RD_MAX, AUTOTUNE_RD_STEP, tune_roll_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     842          26 :         break;
     843          26 :     case AxisType::PITCH:
     844          26 :         updating_rate_d_up(tune_pitch_rd, min_d, AUTOTUNE_RD_MAX, AUTOTUNE_RD_STEP, tune_pitch_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     845          26 :         break;
     846         109 :     case AxisType::YAW:
     847         109 :         updating_rate_d_up(tune_yaw_rLPF, AUTOTUNE_RLPF_MIN, AUTOTUNE_RLPF_MAX, AUTOTUNE_RD_STEP, tune_yaw_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     848         109 :         break;
     849         491 :     case AxisType::YAW_D:
     850         491 :         updating_rate_d_up(tune_yaw_rd, min_d, AUTOTUNE_RD_MAX, AUTOTUNE_RD_STEP, tune_yaw_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     851         491 :         break;
     852             :     }
     853         652 : }
     854             : 
     855             : // update gains for the rate d down tune type
     856         431 : void AC_AutoTune_Multi::updating_rate_d_down_all(AxisType test_axis)
     857             : {
     858         431 :     switch (test_axis) {
     859          93 :     case AxisType::ROLL:
     860          93 :         updating_rate_d_down(tune_roll_rd, min_d, AUTOTUNE_RD_STEP, tune_roll_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     861          93 :         break;
     862          86 :     case AxisType::PITCH:
     863          86 :         updating_rate_d_down(tune_pitch_rd, min_d, AUTOTUNE_RD_STEP, tune_pitch_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     864          86 :         break;
     865          36 :     case AxisType::YAW:
     866          36 :         updating_rate_d_down(tune_yaw_rLPF, AUTOTUNE_RLPF_MIN, AUTOTUNE_RD_STEP, tune_yaw_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     867          36 :         break;
     868         216 :     case AxisType::YAW_D:
     869         216 :         updating_rate_d_down(tune_yaw_rd, min_d, AUTOTUNE_RD_STEP, tune_yaw_rp, AUTOTUNE_RP_MIN, AUTOTUNE_RP_MAX, AUTOTUNE_RP_STEP, target_rate, test_rate_min, test_rate_max);
     870         216 :         break;
     871             :     }
     872         431 : }
     873             : 
     874             : // update gains for the angle p up tune type
     875         378 : void AC_AutoTune_Multi::updating_angle_p_up_all(AxisType test_axis)
     876             : {
     877         378 :     switch (test_axis) {
     878         152 :     case AxisType::ROLL:
     879         152 :         updating_angle_p_up(tune_roll_sp, AUTOTUNE_SP_MAX, AUTOTUNE_SP_STEP, target_angle, test_angle_max, test_rate_min, test_rate_max);
     880         152 :         break;
     881         152 :     case AxisType::PITCH:
     882         152 :         updating_angle_p_up(tune_pitch_sp, AUTOTUNE_SP_MAX, AUTOTUNE_SP_STEP, target_angle, test_angle_max, test_rate_min, test_rate_max);
     883         152 :         break;
     884          74 :     case AxisType::YAW:
     885             :     case AxisType::YAW_D:
     886          74 :         updating_angle_p_up(tune_yaw_sp, AUTOTUNE_SP_MAX, AUTOTUNE_SP_STEP, target_angle, test_angle_max, test_rate_min, test_rate_max);
     887          74 :         break;
     888             :     }
     889         378 : }
     890             : 
     891             : // update gains for the angle p down tune type
     892          93 : void AC_AutoTune_Multi::updating_angle_p_down_all(AxisType test_axis)
     893             : {
     894          93 :     switch (test_axis) {
     895          20 :     case AxisType::ROLL:
     896          20 :         updating_angle_p_down(tune_roll_sp, AUTOTUNE_SP_MIN, AUTOTUNE_SP_STEP, target_angle, test_angle_max, test_rate_min, test_rate_max);
     897          20 :         break;
     898          20 :     case AxisType::PITCH:
     899          20 :         updating_angle_p_down(tune_pitch_sp, AUTOTUNE_SP_MIN, AUTOTUNE_SP_STEP, target_angle, test_angle_max, test_rate_min, test_rate_max);
     900          20 :         break;
     901          53 :     case AxisType::YAW:
     902             :     case AxisType::YAW_D:
     903          53 :         updating_angle_p_down(tune_yaw_sp, AUTOTUNE_SP_MIN, AUTOTUNE_SP_STEP, target_angle, test_angle_max, test_rate_min, test_rate_max);
     904          53 :         break;
     905             :     }
     906          93 : }
     907             : 
     908             : // set gains post tune for the tune type
     909          70 : void AC_AutoTune_Multi::set_tuning_gains_with_backoff(AxisType test_axis)
     910             : {
     911             :     // ensure gain_backoff has not been set outside limits
     912          70 :     gain_backoff.set_and_save_ifchanged(constrain_float(gain_backoff, 0.0f, 0.5f));
     913             : 
     914          70 :     switch (tune_type) {
     915          14 :     case TuneType::RATE_D_UP:
     916          14 :         break;
     917          14 :     case TuneType::RATE_D_DOWN:
     918          14 :         break;
     919          14 :     case TuneType::RATE_P_UP:
     920             :         switch (test_axis) {
     921           4 :         case AxisType::ROLL:
     922           4 :             tune_roll_rd = tune_roll_rd * (1.0f - gain_backoff);
     923           4 :             tune_roll_rp = tune_roll_rp * (1.0f - gain_backoff);
     924           4 :             break;
     925           4 :         case AxisType::PITCH:
     926           4 :             tune_pitch_rd = tune_pitch_rd * (1.0f - gain_backoff);
     927           4 :             tune_pitch_rp = tune_pitch_rp * (1.0f - gain_backoff);
     928           4 :             break;
     929           5 :         case AxisType::YAW:
     930           5 :             tune_yaw_rp = tune_yaw_rp * (1.0f - gain_backoff);
     931           5 :             break;
     932           1 :         case AxisType::YAW_D:
     933           1 :             tune_yaw_rd = tune_yaw_rd * (1.0f - gain_backoff);
     934           1 :             tune_yaw_rp = tune_yaw_rp * (1.0f - gain_backoff);
     935           1 :             break;
     936             :         }
     937          14 :         break;
     938          14 :     case TuneType::ANGLE_P_DOWN:
     939          14 :         break;
     940          14 :     case TuneType::ANGLE_P_UP:
     941             :         switch (test_axis) {
     942           4 :         case AxisType::ROLL:
     943           4 :             tune_roll_sp = tune_roll_sp * (1.0f - gain_backoff) * (1.0f - aggressiveness);
     944           4 :             tune_roll_accel_radss = cd_to_rad(MAX(AUTOTUNE_RP_ACCEL_MIN, test_accel_max_cdss * AUTOTUNE_ACCEL_RP_BACKOFF));
     945           4 :             break;
     946           4 :         case AxisType::PITCH:
     947           4 :             tune_pitch_sp = tune_pitch_sp * (1.0f - gain_backoff) * (1.0f - aggressiveness);
     948           4 :             tune_pitch_accel_radss = cd_to_rad(MAX(AUTOTUNE_RP_ACCEL_MIN, test_accel_max_cdss * AUTOTUNE_ACCEL_RP_BACKOFF));
     949           4 :             break;
     950           6 :         case AxisType::YAW:
     951             :         case AxisType::YAW_D:
     952           6 :             tune_yaw_sp = tune_yaw_sp * (1.0f - gain_backoff) * (1.0f - aggressiveness);
     953           6 :             tune_yaw_accel_radss = cd_to_rad(MAX(AUTOTUNE_Y_ACCEL_MIN, test_accel_max_cdss * AUTOTUNE_ACCEL_Y_BACKOFF));
     954           6 :             break;
     955             :         }
     956          14 :         break;
     957           0 :     case TuneType::RATE_FF_UP:
     958             :     case TuneType::MAX_GAINS:
     959             :     case TuneType::TUNE_CHECK:
     960             :         // this should never happen
     961           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     962           0 :         break;
     963           0 :     case TuneType::TUNE_COMPLETE:
     964           0 :         break;
     965             :     }
     966          70 : }
     967             : 
     968             : // updating_rate_d_up - increase D and adjust P to optimize the D term for a little bounce back
     969             : // optimize D term while keeping the maximum just below the target by adjusting P
     970         652 : void AC_AutoTune_Multi::updating_rate_d_up(float &tune_d, float tune_d_min, float tune_d_max, float tune_d_step_ratio, float &tune_p, float tune_p_min, float tune_p_max, float tune_p_step_ratio, float rate_target, float meas_rate_min, float meas_rate_max)
     971             : {
     972         652 :     if (meas_rate_max > rate_target) {
     973             :         // if maximum measurement was higher than target
     974             :         // reduce P gain (which should reduce maximum)
     975         218 :         tune_p -= tune_p * tune_p_step_ratio;
     976         218 :         if (tune_p < tune_p_min) {
     977             :             // P gain is at minimum so start reducing D
     978           0 :             tune_p = tune_p_min;
     979           0 :             tune_d -= tune_d * tune_d_step_ratio;
     980           0 :             if (tune_d <= tune_d_min) {
     981             :                 // We have reached minimum D gain so stop tuning
     982           0 :                 tune_d = tune_d_min;
     983           0 :                 success_counter = AUTOTUNE_SUCCESS_COUNT;
     984           0 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
     985             :                 // This may be mean AGGR should be increased or MIN_D decreased
     986           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AutoTune: Min Rate D limit reached");
     987             :             }
     988             :         }
     989         434 :     } else if ((meas_rate_max < rate_target * (1.0 - AUTOTUNE_D_UP_DOWN_MARGIN)) && (tune_p <= tune_p_max)) {
     990             :         // we have not achieved a high enough maximum to get a good measurement of bounce back.
     991             :         // increase P gain (which should increase maximum)
     992         299 :         tune_p += tune_p * tune_p_step_ratio;
     993         299 :         if (tune_p >= tune_p_max) {
     994           0 :             tune_p = tune_p_max;
     995           0 :             LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
     996             :         }
     997             :     } else {
     998             :         // we have a good measurement of bounce back
     999         135 :         if (meas_rate_max-meas_rate_min > meas_rate_max * aggressiveness) {
    1000             :             // ignore the next result unless it is the same as this one
    1001          36 :             ignore_next = true;
    1002             :             // bounce back is bigger than our threshold so increment the success counter
    1003          36 :             success_counter++;
    1004             :         } else {
    1005          99 :             if (ignore_next == false) {
    1006             :                 // bounce back is smaller than our threshold so decrement the success counter
    1007          98 :                 if (success_counter > 0) {
    1008           0 :                     success_counter--;
    1009             :                 }
    1010             :                 // increase D gain (which should increase bounce back)
    1011          98 :                 tune_d += tune_d*tune_d_step_ratio * 2.0;
    1012             :                 // stop tuning if we hit maximum D
    1013          98 :                 if (tune_d >= tune_d_max) {
    1014           5 :                     tune_d = tune_d_max;
    1015           5 :                     success_counter = AUTOTUNE_SUCCESS_COUNT;
    1016           5 :                     LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1017             :                 }
    1018             :             } else {
    1019           1 :                 ignore_next = false;
    1020             :             }
    1021             :         }
    1022             :     }
    1023         652 : }
    1024             : 
    1025             : // updating_rate_d_down - decrease D and adjust P to optimize the D term for no bounce back
    1026             : // optimize D term while keeping the maximum just below the target by adjusting P
    1027         431 : void AC_AutoTune_Multi::updating_rate_d_down(float &tune_d, float tune_d_min, float tune_d_step_ratio, float &tune_p, float tune_p_min, float tune_p_max, float tune_p_step_ratio, float rate_target, float meas_rate_min, float meas_rate_max)
    1028             : {
    1029         431 :     if (meas_rate_max > rate_target) {
    1030             :         // if maximum measurement was higher than target
    1031             :         // reduce P gain (which should reduce maximum)
    1032         102 :         tune_p -= tune_p*tune_p_step_ratio;
    1033         102 :         if (tune_p < tune_p_min) {
    1034             :             // P gain is at minimum so start reducing D gain
    1035           0 :             tune_p = tune_p_min;
    1036           0 :             tune_d -= tune_d*tune_d_step_ratio;
    1037           0 :             if (tune_d <= tune_d_min) {
    1038             :                 // We have reached minimum D so stop tuning
    1039           0 :                 tune_d = tune_d_min;
    1040           0 :                 success_counter = AUTOTUNE_SUCCESS_COUNT;
    1041           0 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1042             :                 // This may be mean AGGR should be increased or MIN_D decreased
    1043           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AutoTune: Min Rate D limit reached");
    1044             :             }
    1045             :         }
    1046         329 :     } else if ((meas_rate_max < rate_target*(1.0 - AUTOTUNE_D_UP_DOWN_MARGIN)) && (tune_p <= tune_p_max)) {
    1047             :         // we have not achieved a high enough maximum to get a good measurement of bounce back.
    1048             :         // increase P gain (which should increase maximum)
    1049         124 :         tune_p += tune_p * tune_p_step_ratio;
    1050         124 :         if (tune_p >= tune_p_max) {
    1051           0 :             tune_p = tune_p_max;
    1052           0 :             LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1053             :         }
    1054             :     } else {
    1055             :         // we have a good measurement of bounce back
    1056         205 :         if (meas_rate_max - meas_rate_min < meas_rate_max * aggressiveness) {
    1057          71 :             if (ignore_next == false) {
    1058             :                 // bounce back is less than our threshold so increment the success counter
    1059          57 :                 success_counter++;
    1060             :             } else {
    1061          14 :                 ignore_next = false;
    1062             :             }
    1063             :         } else {
    1064             :             // ignore the next result unless it is the same as this one
    1065         134 :             ignore_next = true;
    1066             :             // bounce back is larger than our threshold so decrement the success counter
    1067         134 :             if (success_counter > 0) {
    1068           1 :                 success_counter--;
    1069             :             }
    1070             :             // decrease D gain (which should decrease bounce back)
    1071         134 :             tune_d -= tune_d * tune_d_step_ratio;
    1072             :             // stop tuning if we hit minimum D
    1073         134 :             if (tune_d <= tune_d_min) {
    1074           0 :                 tune_d = tune_d_min;
    1075           0 :                 success_counter = AUTOTUNE_SUCCESS_COUNT;
    1076           0 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1077             :                 // This may be mean AGGR should be increased or MIN_D decreased
    1078           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AutoTune: Min Rate D limit reached");
    1079             :             }
    1080             :         }
    1081             :     }
    1082         431 : }
    1083             : 
    1084             : // updating_rate_p_up_d_down - increase P to ensure the target is reached while checking bounce back isn't increasing
    1085             : // P is increased until we achieve our target within a reasonable time while reducing D if bounce back increases above the threshold
    1086         543 : void AC_AutoTune_Multi::updating_rate_p_up_d_down(float &tune_d, float tune_d_min, float tune_d_step_ratio, float &tune_p, float tune_p_min, float tune_p_max, float tune_p_step_ratio, float rate_target, float meas_rate_min, float meas_rate_max, bool fail_min_d)
    1087             : {
    1088         543 :     if (meas_rate_max > rate_target * (1.0 + 0.5 * aggressiveness)) {
    1089             :         // ignore the next result unless it is the same as this one
    1090          56 :         ignore_next = true;
    1091             :         // if maximum measurement was greater than target so increment the success counter
    1092          56 :         success_counter++;
    1093         487 :     } else if ((meas_rate_max < rate_target) && (meas_rate_max > rate_target * (1.0 - AUTOTUNE_D_UP_DOWN_MARGIN)) && (meas_rate_max - meas_rate_min > meas_rate_max * aggressiveness) && (tune_d > tune_d_min)) {
    1094             :         // if bounce back was larger than the threshold so decrement the success counter
    1095         189 :         if (success_counter > 0) {
    1096           0 :             success_counter--;
    1097             :         }
    1098             :         // Reduce D gain if bounce-back exceeds threshold.
    1099         189 :         tune_d -= tune_d * tune_d_step_ratio;
    1100             :         // do not decrease the D term past the minimum
    1101         189 :         if (tune_d <= tune_d_min) {
    1102           4 :             tune_d = tune_d_min;
    1103           4 :             LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1104           4 :             if (fail_min_d) {
    1105           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "AutoTune: Rate D Gain Determination Failed");
    1106           0 :                 mode = TuneMode::FAILED;
    1107           0 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_FAILED);
    1108             :             }
    1109             :         }
    1110             :         // decrease P gain to match D gain reduction
    1111         189 :         tune_p -= tune_p * tune_p_step_ratio;
    1112             :         // do not decrease the P term past the minimum
    1113         189 :         if (tune_p <= tune_p_min) {
    1114           0 :             tune_p = tune_p_min;
    1115           0 :             GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "AutoTune: Rate P Gain Determination Failed");
    1116           0 :             mode = TuneMode::FAILED;
    1117           0 :             LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_FAILED);
    1118             :         }
    1119             :     } else {
    1120         298 :         if (ignore_next == false) {
    1121             :             // if maximum measurement was lower than target so decrement the success counter
    1122         273 :             if (success_counter > 0) {
    1123           4 :                 success_counter--;
    1124             :             }
    1125             :             // increase P gain (which should increase the maximum)
    1126         273 :             tune_p += tune_p * tune_p_step_ratio;
    1127             :             // stop tuning if we hit maximum P
    1128         273 :             if (tune_p >= tune_p_max) {
    1129           1 :                 tune_p = tune_p_max;
    1130           1 :                 success_counter = AUTOTUNE_SUCCESS_COUNT;
    1131           1 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1132             :             }
    1133             :         } else {
    1134          25 :             ignore_next = false;
    1135             :         }
    1136             :     }
    1137         543 : }
    1138             : 
    1139             : // updating_angle_p_down - decrease P until we don't reach the target before time out
    1140             : // P is decreased to ensure we are not overshooting the target
    1141          93 : void AC_AutoTune_Multi::updating_angle_p_down(float &tune_p, float tune_p_min, float tune_p_step_ratio, float angle_target, float meas_angle_max, float meas_rate_min, float meas_rate_max)
    1142             : {
    1143          93 :     if (meas_angle_max < angle_target * (1 + 0.5 * aggressiveness)) {
    1144          69 :         if (ignore_next == false) {
    1145             :             // if maximum measurement was lower than target so increment the success counter
    1146          56 :             success_counter++;
    1147             :         } else {
    1148          13 :             ignore_next = false;
    1149             :         }
    1150             :     } else {
    1151             :         // ignore the next result unless it is the same as this one
    1152          24 :         ignore_next = true;
    1153             :         // if maximum measurement was higher than target so decrement the success counter
    1154          24 :         if (success_counter > 0) {
    1155           0 :             success_counter--;
    1156             :         }
    1157             :         // decrease P gain (which should decrease the maximum)
    1158          24 :         tune_p -= tune_p*tune_p_step_ratio;
    1159             :         // stop tuning if we hit maximum P
    1160          24 :         if (tune_p <= tune_p_min) {
    1161           0 :             tune_p = tune_p_min;
    1162           0 :             LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1163           0 :             GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "AutoTune: Angle P Gain Determination Failed");
    1164           0 :             mode = TuneMode::FAILED;
    1165           0 :             LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_FAILED);
    1166             :        }
    1167             :     }
    1168          93 : }
    1169             : 
    1170             : // updating_angle_p_up - increase P to ensure the target is reached
    1171             : // P is increased until we achieve our target within a reasonable time
    1172         378 : void AC_AutoTune_Multi::updating_angle_p_up(float &tune_p, float tune_p_max, float tune_p_step_ratio, float angle_target, float meas_angle_max, float meas_rate_min, float meas_rate_max)
    1173             : {
    1174         472 :     if ((meas_angle_max > angle_target * (1 + 0.5 * aggressiveness)) ||
    1175          94 :         ((meas_angle_max > angle_target) && (meas_rate_min < -meas_rate_max * aggressiveness))) {
    1176             :         // ignore the next result unless it is the same as this one
    1177          24 :         ignore_next = true;
    1178             :         // if maximum measurement was greater than target so increment the success counter
    1179          24 :         success_counter++;
    1180             :     } else {
    1181         354 :         if (ignore_next == false) {
    1182             :             // if maximum measurement was lower than target so decrement the success counter
    1183         351 :             if (success_counter > 0) {
    1184           0 :                 success_counter--;
    1185             :             }
    1186             :             // increase P gain (which should increase the maximum)
    1187         351 :             tune_p += tune_p * tune_p_step_ratio;
    1188             :             // stop tuning if we hit maximum P
    1189         351 :             if (tune_p >= tune_p_max) {
    1190           8 :                 tune_p = tune_p_max;
    1191           8 :                 success_counter = AUTOTUNE_SUCCESS_COUNT;
    1192           8 :                 LOGGER_WRITE_EVENT(LogEvent::AUTOTUNE_REACHED_LIMIT);
    1193             :             }
    1194             :         } else {
    1195           3 :             ignore_next = false;
    1196             :         }
    1197             :     }
    1198         378 : }
    1199             : 
    1200             : #if HAL_LOGGING_ENABLED
    1201        2097 : void AC_AutoTune_Multi::Log_AutoTune()
    1202             : {
    1203        2097 :     if ((tune_type == TuneType::ANGLE_P_DOWN) || (tune_type == TuneType::ANGLE_P_UP)) {
    1204         471 :         switch (axis) {
    1205         172 :         case AxisType::ROLL:
    1206         172 :             Log_Write_AutoTune(axis, tune_type, target_angle, test_angle_min, test_angle_max, tune_roll_rp, tune_roll_rd, tune_roll_sp, test_accel_max_cdss);
    1207         172 :             break;
    1208         172 :         case AxisType::PITCH:
    1209         172 :             Log_Write_AutoTune(axis, tune_type, target_angle, test_angle_min, test_angle_max, tune_pitch_rp, tune_pitch_rd, tune_pitch_sp, test_accel_max_cdss);
    1210         172 :             break;
    1211          76 :         case AxisType::YAW:
    1212          76 :             Log_Write_AutoTune(axis, tune_type, target_angle, test_angle_min, test_angle_max, tune_yaw_rp, tune_yaw_rLPF, tune_yaw_sp, test_accel_max_cdss);
    1213          76 :             break;
    1214          51 :         case AxisType::YAW_D:
    1215          51 :             Log_Write_AutoTune(axis, tune_type, target_angle, test_angle_min, test_angle_max, tune_yaw_rp, tune_yaw_rd, tune_yaw_sp, test_accel_max_cdss);
    1216          51 :             break;
    1217             :         }
    1218             :     } else {
    1219        1626 :         switch (axis) {
    1220         221 :         case AxisType::ROLL:
    1221         221 :             Log_Write_AutoTune(axis, tune_type, target_rate, test_rate_min, test_rate_max, tune_roll_rp, tune_roll_rd, tune_roll_sp, test_accel_max_cdss);
    1222         221 :             break;
    1223         214 :         case AxisType::PITCH:
    1224         214 :             Log_Write_AutoTune(axis, tune_type, target_rate, test_rate_min, test_rate_max, tune_pitch_rp, tune_pitch_rd, tune_pitch_sp, test_accel_max_cdss);
    1225         214 :             break;
    1226         482 :         case AxisType::YAW:
    1227         482 :             Log_Write_AutoTune(axis, tune_type, target_rate, test_rate_min, test_rate_max, tune_yaw_rp, tune_yaw_rLPF, tune_yaw_sp, test_accel_max_cdss);
    1228         482 :             break;
    1229         709 :         case AxisType::YAW_D:
    1230         709 :             Log_Write_AutoTune(axis, tune_type, target_rate, test_rate_min, test_rate_max, tune_yaw_rp, tune_yaw_rd, tune_yaw_sp, test_accel_max_cdss);
    1231         709 :             break;
    1232             :         }
    1233             :     }
    1234             : 
    1235        2097 : }
    1236             : 
    1237      304666 : void AC_AutoTune_Multi::Log_AutoTuneDetails()
    1238             : {
    1239      304666 :     Log_Write_AutoTuneDetails(lean_angle, rotation_rate);
    1240      304666 : }
    1241             : 
    1242             : // @LoggerMessage: ATUN
    1243             : // @Description: Copter/QuadPlane AutoTune
    1244             : // @Vehicles: Copter, Plane
    1245             : // @Field: TimeUS: Time since system startup
    1246             : // @Field: Axis: which axis is currently being tuned
    1247             : // @Field: TuneStep: step in autotune process
    1248             : // @Field: Targ: target angle or rate, depending on tuning step
    1249             : // @Field: Min: measured minimum target angle or rate
    1250             : // @Field: Max: measured maximum target angle or rate
    1251             : // @Field: RP: new rate gain P term
    1252             : // @Field: RD: new rate gain D term
    1253             : // @Field: SP: new angle P term
    1254             : // @Field: ddt: maximum measured twitching acceleration
    1255             : 
    1256             : // Write an Autotune data packet
    1257        2097 : void AC_AutoTune_Multi::Log_Write_AutoTune(AxisType _axis, TuneType tune_step, float meas_target, float meas_min, float meas_max, float new_gain_rp, float new_gain_rd, float new_gain_sp, float new_ddt)
    1258             : {
    1259        4194 :     AP::logger().Write(
    1260             :         "ATUN",
    1261             :         "TimeUS,Axis,TuneStep,Targ,Min,Max,RP,RD,SP,ddt",
    1262             :         "s--ddd---o",
    1263             :         "F--000---0",
    1264             :         "QBBfffffff",
    1265             :         AP_HAL::micros64(),
    1266             :         axis,
    1267             :         tune_step,
    1268        2097 :         meas_target*0.01,
    1269        2097 :         meas_min*0.01,
    1270        2097 :         meas_max*0.01,
    1271             :         new_gain_rp,
    1272             :         new_gain_rd,
    1273             :         new_gain_sp,
    1274             :         new_ddt);
    1275        2097 : }
    1276             : 
    1277             : // Write an Autotune data packet
    1278      304666 : void AC_AutoTune_Multi::Log_Write_AutoTuneDetails(float angle_cd, float rate_cds)
    1279             : {
    1280             :     // @LoggerMessage: ATDE
    1281             :     // @Description: AutoTune data packet
    1282             :     // @Field: TimeUS: Time since system startup
    1283             :     // @Field: Angle: current angle
    1284             :     // @Field: Rate: current angular rate
    1285      609332 :     AP::logger().WriteStreaming(
    1286             :         "ATDE",
    1287             :         "TimeUS,Angle,Rate",
    1288             :         "sdk",
    1289             :         "F00",
    1290             :         "Qff",
    1291             :         AP_HAL::micros64(),
    1292      304666 :         angle_cd*0.01,
    1293      304666 :         rate_cds*0.01);
    1294      304666 : }
    1295             : #endif  // HAL_LOGGING_ENABLED
    1296             : 
    1297        1558 : float AC_AutoTune_Multi::target_angle_max_rp_cd() const
    1298             : {
    1299        1558 :     return attitude_control->lean_angle_max_cd() * AUTOTUNE_TARGET_ANGLE_MAX_RP_SCALE;
    1300             : }
    1301             : 
    1302        2806 : float AC_AutoTune_Multi::target_angle_max_y_cd() const
    1303             : {
    1304             :     // Aircraft with small lean angle will generally benefit from proportional smaller yaw twitch size
    1305        2806 :     return attitude_control->lean_angle_max_cd() * AUTOTUNE_TARGET_ANGLE_MAX_Y_SCALE;
    1306             : }
    1307             : 
    1308         779 : float AC_AutoTune_Multi::target_angle_min_rp_cd() const
    1309             : {
    1310         779 :     return attitude_control->lean_angle_max_cd() * AUTOTUNE_TARGET_ANGLE_MIN_RP_SCALE;
    1311             : }
    1312             : 
    1313        1403 : float AC_AutoTune_Multi::target_angle_min_y_cd() const
    1314             : {
    1315             :     // Aircraft with small lean angle will generally benefit from proportional smaller yaw twitch size
    1316        1403 :     return attitude_control->lean_angle_max_cd() * AUTOTUNE_TARGET_ANGLE_MIN_Y_SCALE;
    1317             : }
    1318             : 
    1319      304666 : float AC_AutoTune_Multi::angle_lim_max_rp_cd() const
    1320             : {
    1321      304666 :     return attitude_control->lean_angle_max_cd() * AUTOTUNE_ANGLE_ABORT_RP_SCALE;
    1322             : }
    1323             : 
    1324      304666 : float AC_AutoTune_Multi::angle_lim_neg_rpy_cd() const
    1325             : {
    1326      304666 :     return attitude_control->lean_angle_max_cd() * AUTOTUNE_ANGLE_NEG_RP_SCALE;
    1327             : }
    1328             : 
    1329             : // get_testing_step_timeout_ms accessor
    1330        2182 : uint32_t AC_AutoTune_Multi::get_testing_step_timeout_ms() const
    1331             : {
    1332        2182 :     return AUTOTUNE_TESTING_STEP_TIMEOUT_MS;
    1333             : }
    1334             : 
    1335             : 
    1336             : #endif  // AC_AUTOTUNE_ENABLED

Generated by: LCOV version 1.14