|
@ -92,8 +92,228 @@ void TASK::run(void) { |
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
// If throttle not ignored then allow change altitude with the stick
|
|
|
// If throttle not ignored then allow change altitude with the stick
|
|
|
|
|
|
if( |
|
|
|
|
|
(abs(rcCommand[THROTTLE] - initialThrottleHold) > ALT_HOLD_THROTTLE_NEUTRAL_ZONE) && |
|
|
|
|
|
!flags.THROTTLE_IGNORED) |
|
|
|
|
|
{ |
|
|
|
|
|
// Slowly increase/decrease AltHold proportional to stick movement (+100 throttle gives ~ +50cm in 1 second with cycle time about 3-4ms)
|
|
|
|
|
|
AltHoldCorr += rcCommand[THROTTLE] - initialThrottleHold; |
|
|
|
|
|
if(abs(AltHoldCorr) > 512) { |
|
|
|
|
|
AltHold += AltHoldCorr / 512; |
|
|
|
|
|
AltHoldCorr %= 512; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
isAltHoldChanged = 1; |
|
|
|
|
|
} else { |
|
|
|
|
|
AltHold = alt.EstAlt; |
|
|
|
|
|
isAltHoldChanged = 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
rcCommand[THROTTLE] = initialThrottleHold + BaroPID; |
|
|
|
|
|
} |
|
|
|
|
|
#endif // BARO
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(THROTTLE_ANGLE_CORRECTION)
|
|
|
|
|
|
if( |
|
|
|
|
|
flags.ANGLE_MODE || |
|
|
|
|
|
flags.HORIZON_MODE |
|
|
|
|
|
) { |
|
|
|
|
|
rcCommand[THROTTLE] += throttleAngleCorrection; |
|
|
|
|
|
} |
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if GPS
|
|
|
|
|
|
// TODO: Split cos_yaw calculations into two phases (X and Y)
|
|
|
|
|
|
if( |
|
|
|
|
|
(flags.GPS_mode != GPS_MODE_NONE) && |
|
|
|
|
|
flags.GPS_FIX_HOME |
|
|
|
|
|
){ |
|
|
|
|
|
float sin_yaw_y = sin(att.heading * 0.0174532925f); |
|
|
|
|
|
float cos_yaw_x = cos(att.heading * 0.0174532925f); |
|
|
|
|
|
GPS_angle[ROLL] = (nav[LON] * cos_yaw_x - nav[LAT] * sin_yaw_y) / 10; |
|
|
|
|
|
GPS_angle[PITCH] = (nav[LON] * sin_yaw_y + nav[LAT] * cos_yaw_x) / 10; |
|
|
|
|
|
} else { |
|
|
|
|
|
GPS_angle[ROLL] = 0; |
|
|
|
|
|
GPS_angle[PITCH] = 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Used to communicate back nav angles to the GPS simulator (for HIL testing)
|
|
|
|
|
|
#if defined(GPS_SIMULATOR)
|
|
|
|
|
|
// SerialWrite(2,0xa5);
|
|
|
|
|
|
// SerialWrite16(2,nav[LAT]+rcCommand[PITCH]);
|
|
|
|
|
|
// SerialWrite16(2,nav[LON]+rcCommand[ROLL]);
|
|
|
|
|
|
// SerialWrite16(2,(nav[LAT]+rcCommand[PITCH])-(nav[LON]+rcCommand[ROLL])); //check
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#endif //GPS
|
|
|
|
|
|
|
|
|
|
|
|
// **** PITCH & ROLL & YAW PID ****
|
|
|
|
|
|
#if PID_CONTROLLER == 1 // evolved oldschool
|
|
|
|
|
|
if(flags.HORIZON_MODE) |
|
|
|
|
|
prop = _min( max( abs(rcCommand[PITCH]), abs(rcCommand[ROLL]) ), 512); |
|
|
|
|
|
|
|
|
|
|
|
// PITCH & ROLL
|
|
|
|
|
|
for(axis = 0; axis < 2; axis++){ |
|
|
|
|
|
rc = rcCommand[axis] << 1; |
|
|
|
|
|
error = rc - imu.gyroData[axis]; |
|
|
|
|
|
errorGyroI[axis] = constrain(errorGyroI[axis]+error, -16000, +16000); // WindUp 16 bits is ok here
|
|
|
|
|
|
if(abs(imu.gyroData[axis]) > 640) |
|
|
|
|
|
errorGyroI[axis] = 0; |
|
|
|
|
|
|
|
|
|
|
|
ITerm = (errorGyroI[axis] >> 7) * conf.pid[axis].I8 >> 6; // 16 bits is ok here 16000/125 = 128 ; 128*250
|
|
|
|
|
|
|
|
|
|
|
|
// PTerm = mul(rc, conf.pid[axis].P8) >> 6;
|
|
|
|
|
|
PTerm = (rc * conf.pid[axis].P8) >> 6; |
|
|
|
|
|
|
|
|
|
|
|
if( |
|
|
|
|
|
flags.ANGLE_MODE || |
|
|
|
|
|
flags.HORIZON_MODE |
|
|
|
|
|
){ |
|
|
|
|
|
// axis relying on ACC
|
|
|
|
|
|
// 50 degrees max inclination
|
|
|
|
|
|
errorAngle = constrain(rc + GPS_angle[axis], -500, +500) - att.angle[axis] + conf.angleTrim[axis]; // 16 bits is ok
|
|
|
|
|
|
errorAngleI[axis] = constrain(errorAngleI[axis] + errorAngle, -10000, +10000); |
|
|
|
|
|
|
|
|
|
|
|
// PTermACC = mul(errorAngle, conf.pid[PIDLEVEL].P8) >> 7; // 32 bits is needed for calculation: errorAngle*P8 could
|
|
|
|
|
|
PTermACC = (errorAngle * conf.pid[PIDLEVEL].P8) >> 7; // 32 bits is needed for calculation: errorAngle*P8 could
|
|
|
|
|
|
|
|
|
|
|
|
int16_t limit = conf.pid[PIDLEVEL].D8 * 5; |
|
|
|
|
|
PTermACC = constrain(PTermACC, -limit, +limit); |
|
|
|
|
|
|
|
|
|
|
|
// ITermACC = mul(errorAngleI[axis], conf.pid[PIDLEVEL].I8) >> 12; // 32 bits is needed for calculation:10000*I8 c
|
|
|
|
|
|
ITermACC = (errorAngleI[axis] * conf.pid[PIDLEVEL].I8) >> 12; // 32 bits is needed for calculation:10000*I8 c
|
|
|
|
|
|
|
|
|
|
|
|
ITerm = ITermACC + ((ITerm-ITermACC) * prop >> 9); |
|
|
|
|
|
PTerm = PTermACC + ((PTerm-PTermACC) * prop >> 9); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// PTerm -= mul(imu.gyroData[axis], dynP8[axis]) >> 6; // 32 bits is needed for calculation
|
|
|
|
|
|
PTerm -= (imu.gyroData[axis] * dynP8[axis]) >> 6; // 32 bits is needed for calculation
|
|
|
|
|
|
|
|
|
|
|
|
delta = imu.gyroData[axis] - lastGyro[axis]; // 16 bits is ok here, the dif between 2 consecutive gyro reads is
|
|
|
|
|
|
lastGyro[axis] = imu.gyroData[axis]; |
|
|
|
|
|
DTerm = delta1[axis] + delta2[axis] + delta; |
|
|
|
|
|
delta2[axis] = delta1[axis]; |
|
|
|
|
|
delta1[axis] = delta; |
|
|
|
|
|
|
|
|
|
|
|
// DTerm = mul(DTerm,dynD8[axis]) >> 5; // 32 bits is needed for calculation
|
|
|
|
|
|
DTerm = (DTerm * dynD8[axis]) >> 5; // 32 bits is needed for calculation
|
|
|
|
|
|
|
|
|
|
|
|
axisPID[axis] = PTerm + ITerm - DTerm; |
|
|
|
|
|
} // End of for(axis...
|
|
|
|
|
|
|
|
|
|
|
|
// YAW
|
|
|
|
|
|
#define GYRO_P_MAX 300
|
|
|
|
|
|
#define GYRO_I_MAX 250
|
|
|
|
|
|
|
|
|
|
|
|
// rc = mul(rcCommand[YAW] , (2 * conf.yawRate + 30)) >> 5;
|
|
|
|
|
|
rc = (rcCommand[YAW] * (2 * conf.yawRate + 30)) >> 5; |
|
|
|
|
|
|
|
|
|
|
|
error = rc - imu.gyroData[YAW]; |
|
|
|
|
|
// errorGyroI_YAW += mul(error,conf.pid[YAW].I8);
|
|
|
|
|
|
errorGyroI_YAW += (error * conf.pid[YAW].I8); |
|
|
|
|
|
errorGyroI_YAW = constrain(errorGyroI_YAW, 2 - ((int32_t)1 << 28), -2 + ((int32_t)1 << 28)); |
|
|
|
|
|
if(abs(rc) > 50) |
|
|
|
|
|
errorGyroI_YAW = 0; |
|
|
|
|
|
|
|
|
|
|
|
// PTerm = mul(error,conf.pid[YAW].P8) >> 6;
|
|
|
|
|
|
PTerm = (error * conf.pid[YAW].P8) >> 6; |
|
|
|
|
|
ITerm = constrain((int16_t)(errorGyroI_YAW >> 13), -GYRO_I_MAX, +GYRO_I_MAX); |
|
|
|
|
|
|
|
|
|
|
|
axisPID[YAW] = PTerm + ITerm; |
|
|
|
|
|
|
|
|
|
|
|
#elif PID_CONTROLLER == 2 // alexK
|
|
|
|
|
|
|
|
|
|
|
|
#define GYRO_I_MAX 256
|
|
|
|
|
|
#define ACC_I_MAX 256
|
|
|
|
|
|
prop = _min(max(abs(rcCommand[PITCH]), abs(rcCommand[ROLL])), 500); // range [0;500]
|
|
|
|
|
|
|
|
|
|
|
|
// ----------PID controller----------
|
|
|
|
|
|
for(axis = 0; axis < 3; axis++){ |
|
|
|
|
|
//-----Get the desired angle rate depending on flight mode
|
|
|
|
|
|
if( |
|
|
|
|
|
(flags.ANGLE_MODE || flags.HORIZON_MODE) && |
|
|
|
|
|
axis < 2 |
|
|
|
|
|
){ |
|
|
|
|
|
// MODE relying on ACC
|
|
|
|
|
|
// Calculate error and limit the angle to 50 degrees max inclination
|
|
|
|
|
|
errorAngle = constrain((rcCommand[axis] << 1) + GPS_angle[axis], -500, +500) - att.angle[axis] + conf.angleTrim[axis]; // 16
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(axis == 2){ |
|
|
|
|
|
// YAW is always gyro-controlled (MAG correction is applied to rcCommand)
|
|
|
|
|
|
AngleRateTmp = (((int32_t) (conf.yawRate + 27) * rcCommand[2]) >> 5); |
|
|
|
|
|
} else { |
|
|
|
|
|
if(!flags.ANGLE_MODE){ |
|
|
|
|
|
// Control is GYRO based (ACRO and HORIZON - direct sticks control is applied to rate PID
|
|
|
|
|
|
AngleRateTmp = ((int32_t) (conf.rollPitchRate + 27) * rcCommand[axis]) >> 4; |
|
|
|
|
|
if(flags.HORIZON_MODE){ |
|
|
|
|
|
// Mix up angle error to desired AngleRateTmp to add a little auto-level feel
|
|
|
|
|
|
AngleRateTmp += ((int32_t) errorAngle * conf.pid[PIDLEVEL].I8) >> 8; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
//it's the ANGLE mode - control is angle based, so control loop is needed
|
|
|
|
|
|
AngleRateTmp = ((int32_t) errorAngle * conf.pid[PIDLEVEL].P8) >> 4; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --------low-level gyro-based PID. ----------
|
|
|
|
|
|
// Used in stand-alone mode for ACRO, controlled by higher level regulators in other modes
|
|
|
|
|
|
// -----calculate scaled error.AngleRates
|
|
|
|
|
|
// multiplication of rcCommand corresponds to changing the sticks scaling here
|
|
|
|
|
|
RateError = AngleRateTmp - imu.gyroData[axis]; |
|
|
|
|
|
|
|
|
|
|
|
// -----calculate P component
|
|
|
|
|
|
PTerm = ((int32_t) RateError * conf.pid[axis].P8) >> 7; |
|
|
|
|
|
|
|
|
|
|
|
// -----calculate I component
|
|
|
|
|
|
// there should be no division before accumulating the error to integrator, because the precision would be reduced.
|
|
|
|
|
|
// Precision is critical, as I prevents from long-time drift. Thus, 32 bits integrator is used.
|
|
|
|
|
|
// Time correction (to avoid different I scaling for different builds based on average cycle time)
|
|
|
|
|
|
// is normalized to cycle time = 2048.
|
|
|
|
|
|
errorGyroI[axis] += (((int32_t) RateError * cycleTime) >> 11) * conf.pid[axis].I8; |
|
|
|
|
|
// limit maximum integrator value to prevent WindUp - accumulating extreme values when system is saturated.
|
|
|
|
|
|
// I coefficient (I8) moved before integration to make limiting independent from PID settings
|
|
|
|
|
|
errorGyroI[axis] = constrain(errorGyroI[axis], (int32_t) -GYRO_I_MAX << 13, (int32_t) +GYRO_I_MAX << 13); |
|
|
|
|
|
ITerm = errorGyroI[axis] >> 13; |
|
|
|
|
|
|
|
|
|
|
|
// -----calculate D-term
|
|
|
|
|
|
delta = RateError - lastError[axis]; // 16 bits is ok here, the dif between 2 consecutive gyro reads is limited
|
|
|
|
|
|
lastError[axis] = RateError; |
|
|
|
|
|
|
|
|
|
|
|
// Correct difference by cycle time. Cycle time is jittery (can be different 2 times), so calculated difference
|
|
|
|
|
|
// would be scaled by different dt each time. Division by dT fixes that.
|
|
|
|
|
|
delta = ((int32_t) delta * ((uint16_t)0xFFFF / (cycleTime >> 4))) >> 6; |
|
|
|
|
|
// add moving average here to reduce noise
|
|
|
|
|
|
deltaSum = delta1[axis] + delta2[axis] + delta; |
|
|
|
|
|
delta2[axis] = delta1[axis]; |
|
|
|
|
|
delta1[axis] = delta; |
|
|
|
|
|
|
|
|
|
|
|
// DTerm = (deltaSum*conf.pid[axis].D8)>>8;
|
|
|
|
|
|
// Solve overflow in calculation above...
|
|
|
|
|
|
DTerm = ((int32_t)deltaSum*conf.pid[axis].D8) >> 8; |
|
|
|
|
|
// -----calculate total PID output
|
|
|
|
|
|
axisPID[axis] = PTerm + ITerm + DTerm; |
|
|
} |
|
|
} |
|
|
|
|
|
#else
|
|
|
|
|
|
#error "*** you must set PID_CONTROLLER to one existing implementation"
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
mixTable(); |
|
|
|
|
|
|
|
|
|
|
|
// Do not update servos during unarmed calibration of sensors which are sensitive to vibration
|
|
|
|
|
|
/********************
|
|
|
|
|
|
#if defined(DISABLE_SERVOS_WHEN_UNARMED)
|
|
|
|
|
|
if(flags.ARMED) |
|
|
|
|
|
writeServos(); |
|
|
|
|
|
#else
|
|
|
|
|
|
if( |
|
|
|
|
|
(flags.ARMED) || |
|
|
|
|
|
((!calibratingG) && (!calibratingA))) |
|
|
|
|
|
writeServos(); |
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
********************/ |
|
|
|
|
|
writeMotors(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|