1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-23 08:15:30 +03:00

Update autotune so it now actually changes the pid values.

This commit also fixes a few missing const keywords after the map file
was inspected.
This commit is contained in:
Dominic Clifton 2014-05-27 21:20:29 +01:00
parent 2cf686b36b
commit bc84f6d5d4
8 changed files with 105 additions and 59 deletions

View file

@ -4,31 +4,38 @@ Autotune helps to automatically tune your multirotor.
## Configuration. ## Configuration.
Autotune only works in HORIZON or ANGLE mode. Autotune only works in HORIZON or ANGLE mode, before using auto-tune it's best you setup so there is as little drift as possible.
Autotuning is best on a full battery in good flying conditions, i.e. no or minimal wind.
Configure a two position switch on your transmitter to activate the AUTOTUNE and HORIZON or ANGLE modes using the auxilary configuration.
You may find a momentary switch more suitable than a toggle switch.
Configure a switch on your transmitter to activate the AUTOTUNE and HORIZON or ANGLE modes using the auxilary configuration.
## Using autotuning ## Using autotuning
Turn off the autotune switch. If the autotune switch is on while not armed the warning LED will flash and you cannot arm.
Launch the multirotor. Launch the multirotor.
Flip the autotune switch on your transmitter. Turn on/hold the autotune switch on your transmitter.
Observe roll/left/right a few times. Observe roll left/right. A beep code will sound on the buzzer.
Turn off/release the switch while still flying to stop this phase of tuning.
PID settings will have been updated for ROLL/YAW. PID settings will have been updated for ROLL/YAW.
Disable the switch while still flying. Turn on/hold the switch again.
Enable the switch again. Observe pitch forwards/backwards. A beep code will sound on the buzzer.
Observe pitch forwards/backwards a few times. Turn off/release the switch while still flying to stop this phase of tuning.
PID settings will have been updated for PITCH/YAW. PID settings will have been updated for PITCH/YAW.
Disable the switch again. PIDS are updated, fly and see if it's better.
PIDS are tunes, fly and see if it's better. If it's worse, flip the switch again to restore previous pids that were present prior to arming.
Land. Land.
@ -36,7 +43,7 @@ Verify results via an app while power still applied if desired.
Cutting the power will loose the unsaved pids. Cutting the power will loose the unsaved pids.
If you're happy with the pids then while disarmed flip the autotune switch again to save all settings. If you're happy with the pids then while disarmed flip the autotune switch again to save all settings then flip it back so you can arm again.
A beeper will sound indicating the settings are saved. A beeper will sound indicating the settings are saved.

View file

@ -24,6 +24,14 @@ extern int16_t debug[4];
#define AUTOTUNE_D_MULTIPLIER 1.2f #define AUTOTUNE_D_MULTIPLIER 1.2f
#define AUTOTUNE_SETTLING_DELAY_MS 250 // 1/4 of a second. #define AUTOTUNE_SETTLING_DELAY_MS 250 // 1/4 of a second.
#define AUTOTUNE_INCREASE_MULTIPLIER 1.03f
#define AUTOTUNE_DECREASE_MULTIPLIER 0.97f
#define AUTOTUNE_MINIMUM_I_VALUE 0.001f
#define YAW_GAIN_MULTIPLIER 2.0f
typedef enum { typedef enum {
PHASE_IDLE = 0, PHASE_IDLE = 0,
PHASE_TUNE_ROLL, PHASE_TUNE_ROLL,
@ -31,6 +39,11 @@ typedef enum {
PHASE_SAVE_OR_RESTORE_PIDS, PHASE_SAVE_OR_RESTORE_PIDS,
} autotunePhase_e; } autotunePhase_e;
static const pidIndex_e angleIndexToPidIndexMap[] = {
PIDROLL,
PIDPITCH
};
#define AUTOTUNE_PHASE_MAX PHASE_SAVE_OR_RESTORE_PIDS #define AUTOTUNE_PHASE_MAX PHASE_SAVE_OR_RESTORE_PIDS
#define AUTOTUNE_PHASE_COUNT (AUTOTUNE_PHASE_MAX + 1) #define AUTOTUNE_PHASE_COUNT (AUTOTUNE_PHASE_MAX + 1)
@ -39,6 +52,7 @@ typedef enum {
static pidProfile_t *pidProfile; static pidProfile_t *pidProfile;
static pidProfile_t pidBackup; static pidProfile_t pidBackup;
static uint8_t pidController; static uint8_t pidController;
static uint8_t pidIndex;
static bool rising; static bool rising;
static uint8_t cycleCount; // TODO can we replace this with an enum to improve readability. static uint8_t cycleCount; // TODO can we replace this with an enum to improve readability.
static uint32_t timeoutAt; static uint32_t timeoutAt;
@ -50,6 +64,19 @@ static float targetAngle = 0;
static float targetAngleAtPeak; static float targetAngleAtPeak;
static float secondPeakAngle, firstPeakAngle; // deci dgrees, 180 deg = 1800 static float secondPeakAngle, firstPeakAngle; // deci dgrees, 180 deg = 1800
typedef struct fp_pid {
float p;
float i;
float d;
} fp_pid_t;
static fp_pid_t pid;
// These are used to convert between multiwii integer values to the float pid values used by the autotuner.
#define MULTIWII_P_MULTIPLIER 10.0f // e.g 0.4 * 10 = 40
#define MULTIWII_I_MULTIPLIER 1000.0f // e.g 0.030 * 1000 = 30
// Note there is no D multiplier since D values are stored and used AS-IS
bool isAutotuneIdle(void) bool isAutotuneIdle(void)
{ {
return phase == PHASE_IDLE; return phase == PHASE_IDLE;
@ -61,6 +88,11 @@ static void startNewCycle(void)
secondPeakAngle = firstPeakAngle = 0; secondPeakAngle = firstPeakAngle = 0;
} }
static void updatePidIndex(void)
{
pidIndex = angleIndexToPidIndexMap[autoTuneAngleIndex];
}
static void updateTargetAngle(void) static void updateTargetAngle(void)
{ {
if (rising) { if (rising) {
@ -75,19 +107,26 @@ static void updateTargetAngle(void)
#endif #endif
} }
float autotune(angle_index_t angleIndex, rollAndPitchInclination_t *inclination, float errorAngle) float autotune(angle_index_t angleIndex, const rollAndPitchInclination_t *inclination, float errorAngle)
{ {
float currentAngle;
if (!(phase == PHASE_TUNE_ROLL || phase == PHASE_TUNE_PITCH) || autoTuneAngleIndex != angleIndex) { if (!(phase == PHASE_TUNE_ROLL || phase == PHASE_TUNE_PITCH) || autoTuneAngleIndex != angleIndex) {
return errorAngle; return errorAngle;
} }
if (pidController == 2) {
// TODO support new baseflight pid controller
return errorAngle;
}
debug[0] = 0; debug[0] = 0;
#if 0 #if 0
debug[1] = inclination->rawAngles[angleIndex]; debug[1] = inclination->rawAngles[angleIndex];
#endif #endif
float currentAngle; updatePidIndex();
if (rising) { if (rising) {
currentAngle = DECIDEGREES_TO_DEGREES(inclination->rawAngles[angleIndex]); currentAngle = DECIDEGREES_TO_DEGREES(inclination->rawAngles[angleIndex]);
@ -115,16 +154,18 @@ float autotune(angle_index_t angleIndex, rollAndPitchInclination_t *inclination,
} else if (secondPeakAngle > 0) { } else if (secondPeakAngle > 0) {
if (cycleCount == 0) { if (cycleCount == 0) {
// when checking the I value, we would like to overshoot the target position by half of the max oscillation. // when checking the I value, we would like to overshoot the target position by half of the max oscillation.
// if (currentangle-targetangle<(FPAUTOTUNEMAXOSCILLATION>>1)) { if (currentAngle - targetAngle < AUTOTUNE_MAX_OSCILLATION / 2) {
// currentivalueshifted=lib_fp_multiply(currentivalueshifted,AUTOTUNEINCREASEMULTIPLIER); pid.i *= AUTOTUNE_INCREASE_MULTIPLIER;
// } else { } else {
// currentivalueshifted=lib_fp_multiply(currentivalueshifted,AUTOTUNEDECREASEMULTIPLIER); pid.i *= AUTOTUNE_DECREASE_MULTIPLIER;
// if (currentivalueshifted<AUTOTUNEMINIMUMIVALUE) currentivalueshifted=AUTOTUNEMINIMUMIVALUE; if (pid.i < AUTOTUNE_MINIMUM_I_VALUE) {
// } pid.i = AUTOTUNE_MINIMUM_I_VALUE;
}
}
// go back to checking P and D // go back to checking P and D
cycleCount = 1; cycleCount = 1;
// usersettings.pid_igain[autotuneindex]=0; pidProfile->I8[pidIndex] = 0;
startNewCycle(); startNewCycle();
} else { } else {
// we are checking P and D values // we are checking P and D values
@ -152,7 +193,6 @@ float autotune(angle_index_t angleIndex, rollAndPitchInclination_t *inclination,
// analyze the data // analyze the data
// Our goal is to have zero overshoot and to have AUTOTUNEMAXOSCILLATION amplitude // Our goal is to have zero overshoot and to have AUTOTUNEMAXOSCILLATION amplitude
if (secondPeakAngle > targetAngleAtPeak) { if (secondPeakAngle > targetAngleAtPeak) {
// overshot // overshot
debug[0] = 1; debug[0] = 1;
@ -161,16 +201,12 @@ float autotune(angle_index_t angleIndex, rollAndPitchInclination_t *inclination,
if (oscillationAmplitude > AUTOTUNE_MAX_OSCILLATION) { if (oscillationAmplitude > AUTOTUNE_MAX_OSCILLATION) {
// we have too much oscillation, so we can't increase D, so decrease P // we have too much oscillation, so we can't increase D, so decrease P
#endif #endif
// decrease P pid.p *= AUTOTUNE_DECREASE_MULTIPLIER;
//currentpvalueshifted=lib_fp_multiply(currentpvalueshifted, AUTOTUNEDECREASEMULTIPLIER); // TODO
#ifdef PREFER_HIGH_GAIN_SOLUTION #ifdef PREFER_HIGH_GAIN_SOLUTION
} else { } else {
// we don't have too much oscillation, so we can increase D" // we don't have too much oscillation, so we can increase D
#endif #endif
pid.d *= AUTOTUNE_INCREASE_MULTIPLIER;
// increase D
//currentdvalueshifted=lib_fp_multiply(currentdvalueshifted, AUTOTUNEINCREASEMULTIPLIER); // TODO
#ifdef PREFER_HIGH_GAIN_SOLUTION #ifdef PREFER_HIGH_GAIN_SOLUTION
} }
#endif #endif
@ -179,16 +215,16 @@ float autotune(angle_index_t angleIndex, rollAndPitchInclination_t *inclination,
debug[0] = 2; debug[0] = 2;
if (oscillationAmplitude > AUTOTUNE_MAX_OSCILLATION) { if (oscillationAmplitude > AUTOTUNE_MAX_OSCILLATION) {
// we have too much oscillation, so we should lower D // we have too much oscillation
//currentdvalueshifted=lib_fp_multiply(currentdvalueshifted, AUTOTUNEDECREASEMULTIPLIER); // TODO pid.d *= AUTOTUNE_DECREASE_MULTIPLIER;
} else { } else {
// we don't have too much oscillation, so we increase P // we don't have too much oscillation
//currentpvalueshifted=lib_fp_multiply(currentpvalueshifted, AUTOTUNEINCREASEMULTIPLIER); // TODO pid.p *= AUTOTUNE_INCREASE_MULTIPLIER;
} }
} }
//usersettings.pid_pgain[autotuneindex]=currentpvalueshifted>>AUTOTUNESHIFT; // TODO pidProfile->P8[pidIndex] = pid.p * MULTIWII_P_MULTIPLIER;
//usersettings.pid_dgain[autotuneindex]=currentdvalueshifted>>AUTOTUNESHIFT; // TODO pidProfile->D8[pidIndex] = pid.d;
// switch to the other direction and start a new cycle // switch to the other direction and start a new cycle
startNewCycle(); startNewCycle();
@ -197,7 +233,7 @@ float autotune(angle_index_t angleIndex, rollAndPitchInclination_t *inclination,
// switch to testing I value // switch to testing I value
cycleCount = 0; cycleCount = 0;
//usersettings.pid_igain[autotuneindex]=currentivalueshifted>>AUTOTUNESHIFT; // TODO pidProfile->I8[pidIndex] = pid.i * MULTIWII_I_MULTIPLIER;
} }
} }
} }
@ -254,30 +290,33 @@ void autotuneBeginNextPhase(pidProfile_t *pidProfileToTune, uint8_t pidControlle
pidProfile = pidProfileToTune; pidProfile = pidProfileToTune;
pidController = pidControllerInUse; pidController = pidControllerInUse;
updatePidIndex();
updateTargetAngle(); updateTargetAngle();
// TODO pid.p = pidProfile->P8[pidIndex] / MULTIWII_P_MULTIPLIER;
// currentpvalueshifted=usersettings.pid_pgain[autotuneindex]<<AUTOTUNESHIFT; pid.i = pidProfile->I8[pidIndex] / MULTIWII_I_MULTIPLIER;
// currentivalueshifted=usersettings.pid_igain[autotuneindex]<<AUTOTUNESHIFT; // divide by D multiplier to get our working value. We'll multiply by D multiplier when we are done.
// // divide by D multiplier to get our working value. We'll multiply by D multiplier when we are done. pid.d = pidProfile->D8[pidIndex] * (1.0f / AUTOTUNE_D_MULTIPLIER);
// usersettings.pid_dgain[autotuneindex]=lib_fp_multiply(usersettings.pid_dgain[autotuneindex],FPONEOVERAUTOTUNE_D_MULTIPLIER);
// currentdvalueshifted=usersettings.pid_dgain[autotuneindex]<<AUTOTUNESHIFT;
//
// usersettings.pid_igain[autotuneindex]=0;
pidProfile->D8[pidIndex] = pid.d;
pidProfile->I8[pidIndex] = 0;
} }
void autotuneEndPhase(void) void autotuneEndPhase(void)
{ {
// TODO if (phase == PHASE_TUNE_ROLL || phase == PHASE_TUNE_PITCH) {
// usersettings.pid_igain[autotuneindex]=currentivalueshifted>>AUTOTUNESHIFT;
// // we leave P alone, just update I and D
// // multiply by D multiplier. The best D is usually a little higher than what the algroithm produces.
// usersettings.pid_dgain[autotuneindex]=lib_fp_multiply(currentdvalueshifted,FPAUTOTUNE_D_MULTIPLIER)>>AUTOTUNESHIFT; pidProfile->I8[pidIndex] = pid.i * MULTIWII_I_MULTIPLIER;
//
// usersettings.pid_igain[YAWINDEX]=usersettings.pid_igain[ROLLINDEX]; // multiply by D multiplier. The best D is usually a little higher than what the algroithm produces.
// usersettings.pid_dgain[YAWINDEX]=usersettings.pid_dgain[ROLLINDEX]; pidProfile->D8[pidIndex] = (pid.d * AUTOTUNE_D_MULTIPLIER);
// usersettings.pid_pgain[YAWINDEX]=lib_fp_multiply(usersettings.pid_pgain[ROLLINDEX],YAWGAINMULTIPLIER);
pidProfile->P8[PIDYAW] = pidProfile->P8[PIDROLL] * YAW_GAIN_MULTIPLIER;
pidProfile->I8[PIDYAW] = pidProfile->I8[PIDROLL];
pidProfile->D8[PIDYAW] = pidProfile->D8[PIDROLL];
}
if (phase == AUTOTUNE_PHASE_MAX) { if (phase == AUTOTUNE_PHASE_MAX) {
phase = PHASE_IDLE; phase = PHASE_IDLE;

View file

@ -2,7 +2,7 @@
void autotuneReset(); void autotuneReset();
void autotuneBeginNextPhase(pidProfile_t *pidProfileToTune, uint8_t pidControllerInUse); void autotuneBeginNextPhase(pidProfile_t *pidProfileToTune, uint8_t pidControllerInUse);
float autotune(angle_index_t angleIndex, rollAndPitchInclination_t *inclination, float errorAngle); float autotune(angle_index_t angleIndex, const rollAndPitchInclination_t *inclination, float errorAngle);
void autotuneEndPhase(); void autotuneEndPhase();
bool isAutotuneIdle(void); bool isAutotuneIdle(void);

View file

@ -56,7 +56,7 @@ void resetErrorGyro(void)
errorGyroIf[YAW] = 0; errorGyroIf[YAW] = 0;
} }
angle_index_t rcAliasToAngleIndexMap[] = { AI_ROLL, AI_PITCH }; const angle_index_t rcAliasToAngleIndexMap[] = { AI_ROLL, AI_PITCH };
bool shouldAutotune(void) bool shouldAutotune(void)
{ {

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
enum { typedef enum {
PIDROLL, PIDROLL,
PIDPITCH, PIDPITCH,
PIDYAW, PIDYAW,
@ -13,7 +13,7 @@ enum {
PIDMAG, PIDMAG,
PIDVEL, PIDVEL,
PID_ITEM_COUNT PID_ITEM_COUNT
}; } pidIndex_e;
typedef struct pidProfile_s { typedef struct pidProfile_s {
uint8_t P8[PID_ITEM_COUNT]; uint8_t P8[PID_ITEM_COUNT];

View file

@ -78,12 +78,12 @@ typedef struct serialPortSearchResult_s {
uint8_t startSerialPortFunctionIndex; // used by findNextSerialPort uint8_t startSerialPortFunctionIndex; // used by findNextSerialPort
} serialPortSearchResult_t; } serialPortSearchResult_t;
static serialPortFunctionList_t serialPortFunctionList = { static const serialPortFunctionList_t serialPortFunctionList = {
4, 4,
serialPortFunctions serialPortFunctions
}; };
serialPortFunctionList_t *getSerialPortFunctionList(void) const serialPortFunctionList_t *getSerialPortFunctionList(void)
{ {
return &serialPortFunctionList; return &serialPortFunctionList;
} }

View file

@ -113,7 +113,7 @@ bool isSerialConfigValid(serialConfig_t *serialConfig);
bool doesConfigurationUsePort(serialConfig_t *serialConfig, serialPortIdentifier_e portIdentifier); bool doesConfigurationUsePort(serialConfig_t *serialConfig, serialPortIdentifier_e portIdentifier);
bool isSerialPortFunctionShared(serialPortFunction_e functionToUse, uint16_t functionMask); bool isSerialPortFunctionShared(serialPortFunction_e functionToUse, uint16_t functionMask);
serialPortFunctionList_t *getSerialPortFunctionList(void); const serialPortFunctionList_t *getSerialPortFunctionList(void);
void evaluateOtherData(uint8_t sr); void evaluateOtherData(uint8_t sr);
void handleSerial(void); void handleSerial(void);

View file

@ -316,7 +316,7 @@ static void openAllMSPSerialPorts(serialConfig_t *serialConfig)
} while (port); } while (port);
// XXX this function might help with adding support for MSP on more than one port, if not delete it. // XXX this function might help with adding support for MSP on more than one port, if not delete it.
serialPortFunctionList_t *serialPortFunctionList = getSerialPortFunctionList(); const serialPortFunctionList_t *serialPortFunctionList = getSerialPortFunctionList();
UNUSED(serialPortFunctionList); UNUSED(serialPortFunctionList);
} }