ardupilot/ArducopterNG/Heli.pde

429 lines
15 KiB
Plaintext

/*
www.ArduCopter.com - www.DIYDrones.com
Copyright (c) 2010. All rights reserved.
An Open Source Arduino based multicopter.
File : Heli.pde
Desc : code specific to traditional helicopters
Version : v1.0, Aug 27, 2010
Author(s): ArduCopter Team
Ted Carancho (aeroquad), Jose Julio, Jordi Muñoz,
Jani Hirvinen, Ken McEwans, Roberto Navoni,
Sandro Benigno, Chris Anderson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
* ************************************************************** *
ChangeLog:
* ************************************************************** *
TODO:
* ************************************************************** */
#if AIRFRAME == HELI
/**********************************************************************/
// heli_readUserConfig - reads values in from EEPROM
void heli_readUserConfig()
{
float magicNum = 0;
magicNum = readEEPROM(EEPROM_MAGIC_NUMBER_ADDR);
if( magicNum != EEPROM_MAGIC_NUMBER ) {
SerPri("No heli settings found in EEPROM. Using defaults");
heli_defaultUserConfig();
}else{
frontLeftCCPMmin = readEEPROM(FRONT_LEFT_CCPM_MIN_ADDR);
frontLeftCCPMmax = readEEPROM(FRONT_LEFT_CCPM_MAX_ADDR);
frontRightCCPMmin = readEEPROM(FRONT_RIGHT_CCPM_MIN_ADDR);
frontRightCCPMmax = readEEPROM(FRONT_RIGHT_CCPM_MAX_ADDR);
rearCCPMmin = readEEPROM(REAR_CCPM_MIN_ADDR);
rearCCPMmax = readEEPROM(REAR_CCPM_MAX_ADDR);
yawMin = readEEPROM(YAW_MIN_ADDR);
yawMax = readEEPROM(YAW_MAX_ADDR);
}
}
/**********************************************************************/
// default the heli specific values to defaults
void heli_defaultUserConfig()
{
// default CCPM values.
frontLeftCCPMmin = 1200;
frontLeftCCPMmax = 1800;
frontRightCCPMmin = 1900;
frontRightCCPMmax = 1100;
rearCCPMmin = 1200;
rearCCPMmax = 1800;
yawMin = 1200;
yawMax = 1800;
// default PID values - Roll
KP_QUAD_ROLL = 1.100;
KI_QUAD_ROLL = 0.200;
STABLE_MODE_KP_RATE_ROLL = -0.001;
// default PID values - Pitch
KP_QUAD_PITCH = 1.100;
KI_QUAD_PITCH = 0.120;
STABLE_MODE_KP_RATE_PITCH = -0.001;
// default PID values - Yaw
Kp_RateYaw = 3.500; // heading P term
Ki_RateYaw = 0.100; // heading I term
KP_QUAD_YAW = 0.200; // yaw rate P term
KI_QUAD_YAW = 0.040; // yaw rate I term
STABLE_MODE_KP_RATE_YAW = -0.010; // yaw rate D term
}
/**********************************************************************/
// displaySettings - displays heli specific user settings
void heli_displaySettings()
{
SerPri("frontLeftCCPM min: ");
SerPri(frontLeftCCPMmin);
SerPri("\tmax:");
SerPri(frontLeftCCPMmax);
if( abs(frontLeftCCPMmin-frontLeftCCPMmax)<50 || frontLeftCCPMmin < 900 || frontLeftCCPMmin > 2100 || frontLeftCCPMmax < 900 || frontLeftCCPMmax > 2100 )
SerPrln("\t\t<-- check");
else
SerPrln();
SerPri("frontRightCCPM min: ");
SerPri(frontRightCCPMmin);
SerPri("\tmax:");
SerPri(frontRightCCPMmax);
if( abs(frontRightCCPMmin-frontRightCCPMmax)<50 || frontRightCCPMmin < 900 || frontRightCCPMmin > 2100 || frontRightCCPMmax < 900 || frontRightCCPMmax > 2100 )
SerPrln("\t\t<-- check");
else
SerPrln();
SerPri("rearCCPM min: ");
SerPri(rearCCPMmin);
SerPri("\tmax:");
SerPri(rearCCPMmax);
if( abs(rearCCPMmin-rearCCPMmax)<50 || rearCCPMmin < 900 || rearCCPMmin > 2100 || rearCCPMmax < 900 || rearCCPMmax > 2100 )
SerPrln("\t\t<-- check");
else
SerPrln();
SerPri("yaw min: ");
SerPri(yawMin);
SerPri("\tmax:");
SerPri(yawMax);
if( abs(yawMin-yawMax)<50 || yawMin < 900 || yawMin > 2100 || yawMax < 900 || yawMax > 2100 )
SerPrln("\t\t<-- check");
else
SerPrln();
SerPrln();
}
////////////////////////////////////////////////////////////////////////////////
// Setup Procedure
////////////////////////////////////////////////////////////////////////////////
void heli_setup()
{
// read heli specific settings (like CCPM values) from EEPROM
heli_readUserConfig();
// update CCPM values
frontLeftCCPMslope = 100 / (frontLeftCCPMmax - frontLeftCCPMmin);
frontLeftCCPMintercept = 100 - (frontLeftCCPMslope * frontLeftCCPMmax);
frontRightCCPMslope = 100 / (frontRightCCPMmax - frontRightCCPMmin);
frontRightCCPMintercept = 100 - (frontRightCCPMslope * frontRightCCPMmax);
rearCCPMslope = 100 / (rearCCPMmax - rearCCPMmin);
rearCCPMintercept = 100 - (rearCCPMslope * rearCCPMmax);
yawSlope = 100 / (yawMax - yawMin);
yawIntercept = 50 - (yawSlope * yawMax);
// capture trims
heli_read_radio_trims();
// hardcode mids because we will use ccpm
roll_mid = ROLL_MID;
pitch_mid = PITCH_MID;
collective_mid = 1500;
yaw_mid = (yawMin+yawMax)/2;
// determine which axis APM will control
roll_control_switch = !SW_DIP1;
pitch_control_switch = !SW_DIP2;
yaw_control_switch = !SW_DIP3;
collective_control_switch = !SW_DIP4;
//position_control_switch = !SW_DIP4; // switch 4 controls whether we will do GPS hold or not
}
/*****************************************************************************************************/
// heli_read_radio_trims - captures roll, pitch and yaw trims (mids) although only yaw is actually used
// trim_yaw is used to center output to the tail which tends to be far from the
// physical middle of where the rudder can move. This helps keep the PID's I
// value low and avoid sudden turns left on takeoff
void heli_read_radio_trims()
{
int i;
float sumRoll = 0, sumPitch = 0, sumYaw = 0;
// initialiase trims to zero incase this is called more than once
trim_roll = 0.0;
trim_pitch = 0.0;
trim_yaw = 0.0;
// read radio a few times
for(int i=0; i<10; i++ )
{
// make sure new data has arrived
while( APM_RC.GetState() != 1 )
{
delay(20);
}
heli_read_radio();
sumRoll += ch_roll;
sumPitch += ch_pitch;
sumYaw += ch_yaw;
}
// set trim to average
trim_roll = sumRoll / 10.0;
trim_pitch = sumPitch / 10.0;
trim_yaw = sumYaw / 10.0;
// double check all is ok
if( trim_roll > 50.0 || trim_roll < -50 )
trim_roll = 0.0;
if( trim_pitch >50.0 || trim_roll < -50.0 )
trim_pitch = 0.0;
if( trim_yaw > 50.0 || trim_yaw < -50.0 )
trim_yaw = 0.0;
}
/**********************************************************************/
// Radio decoding
void heli_read_radio()
{
// left channel
ccpmPercents.x = frontLeftCCPMslope * APM_RC.InputCh(CHANNEL_FRONT_LEFT) + frontLeftCCPMintercept;
// right channel
ccpmPercents.y = frontRightCCPMslope * APM_RC.InputCh(CHANNEL_FRONT_RIGHT) + frontRightCCPMintercept;
// rear channel
ccpmPercents.z = rearCCPMslope * APM_RC.InputCh(CHANNEL_REAR) + rearCCPMintercept;
// decode the ccpm
rollPitchCollPercent = ccpmDeallocation * ccpmPercents;
// get the yaw (not coded)
yawPercent = (yawSlope * APM_RC.InputCh(CHANNEL_YAW) + yawIntercept) - trim_yaw;
// put decoded values into the global variables
ch_roll = rollPitchCollPercent.x;
ch_pitch = rollPitchCollPercent.y;
ch_collective = rollPitchCollPercent.z;
// allow a bit of a dead zone for the yaw
if( fabs(yawPercent) < 2 )
ch_yaw = 0;
else
ch_yaw = yawPercent;
// get the aux channel (for tuning on/off autopilot)
ch_aux = APM_RC.InputCh(CH_5) * ch_aux_slope + ch_aux_offset;
// convert to absolute angles
command_rx_roll = ch_roll / HELI_STICK_TO_ANGLE_FACTOR; // Convert stick position to absolute angles
command_rx_pitch = ch_pitch / HELI_STICK_TO_ANGLE_FACTOR; // Convert stick position to absolute angles
command_rx_collective = ch_collective;
command_rx_yaw = ch_yaw / HELI_YAW_STICK_TO_ANGLE_FACTOR; // Convert stick position to turn rate
// for use by non-heli parts of code
ch_throttle = 1000 + (ch_collective * 10);
// hardcode flight mode
flightMode = STABLE_MODE;
// Autopilot mode (only works on Stable mode)
if (flightMode == STABLE_MODE)
{
if(ch_aux < 1300) {
AP_mode = AP_AUTOMATIC_MODE; // Automatic mode : GPS position hold mode + altitude hold
//SerPrln("autopilot ON!");
}else{
AP_mode = AP_NORMAL_MODE; // Normal mode
//SerPrln("autopilot OFF!");
}
}
}
/**********************************************************************/
// output to swash plate based on control variables
// Uses these global variables:
// control_roll : -50 ~ 50
// control_pitch : -50 ~ 50
// control_collective : 0 ~ 100
// control_yaw : -50 ~ 50
void heli_moveSwashPlate()
{
static int count = 0;
// turn pitch, roll, collective commands into ccpm values (i.e. values for each servo)
ccpmPercents_out = ccpmAllocation * Vector3f(control_roll, control_pitch, control_collective);
// calculate values to be sent out to RC Output channels
leftOut = (ccpmPercents_out.x - frontLeftCCPMintercept) / frontLeftCCPMslope;
rightOut = (ccpmPercents_out.y - frontRightCCPMintercept) / frontRightCCPMslope;
rearOut = (ccpmPercents_out.z - rearCCPMintercept) / rearCCPMslope;
yawOut = (control_yaw - yawIntercept) / yawSlope;
APM_RC.OutputCh(CHANNEL_FRONT_LEFT,leftOut);
APM_RC.OutputCh(CHANNEL_FRONT_RIGHT,rightOut);
APM_RC.OutputCh(CHANNEL_REAR,rearOut);
APM_RC.OutputCh(CHANNEL_YAW,yawOut);
// InstantPWM
APM_RC.Force_Out0_Out1();
APM_RC.Force_Out2_Out3();
}
/**********************************************************************/
// ROLL, PITCH and YAW PID controls...
// Input : desired Roll, Pitch absolute angles
// collective as a percentage from 0~100
// yaw as a rate of rotation
// Output : control_roll - roll servo as a percentage (-50 to 50)
// control_pitch - pitch servo as a percentage (-50 to 50)
// control_collective - collective servo as a percentage (0 to 100)
// control_yaw - yaw servo as a percentage (0 to 100)
void heli_attitude_control(int command_roll, int command_pitch, int command_collective, int command_yaw)
{
static float firstIteration = 1;
static float command_yaw_previous = 0;
static float previousYawRate = 0;
float stable_roll, stable_pitch;
float currentYawRate;
float control_yaw_rate;
float err_heading;
static int aCounter = 0;
float heli_G_Dt;
// get current time
heli_G_Dt = (currentTimeMicros-heli_previousTimeMicros) * 0.000001; // Microseconds!!!
heli_previousTimeMicros = currentTimeMicros;
// always pass through collective command
control_collective = command_collective;
// ROLL CONTROL -- ONE PID
if( roll_control_switch )
{
// P term
err_roll = command_roll - ToDeg(roll);
err_roll = constrain(err_roll,-25,25); // to limit max roll command...
// I term
roll_I += err_roll*heli_G_Dt*KI_QUAD_ROLL;
roll_I = constrain(roll_I,-10,10);
// D term
roll_D = ToDeg(Omega[0]) * STABLE_MODE_KP_RATE_ROLL; // Take into account Angular velocity
roll_D = constrain(roll_D,-25,25);
// PID control
control_roll = KP_QUAD_ROLL*err_roll + roll_I + roll_D;
control_roll = constrain(control_roll,-50,50);
}else{
// straight pass through
control_roll = ch_roll;
}
// PITCH CONTROL -- ONE PIDS
if( pitch_control_switch )
{
// P term
err_pitch = command_pitch - ToDeg(pitch);
err_pitch = constrain(err_pitch,-25,25); // to limit max pitch command...
// I term
pitch_I += err_pitch * heli_G_Dt * KI_QUAD_PITCH;
pitch_I = constrain(pitch_I,-10,10);
// D term
pitch_D = ToDeg(Omega[1]) * STABLE_MODE_KP_RATE_PITCH; // Take into account Angular velocity
pitch_D = constrain(pitch_D,-25,25);
// PID control
control_pitch = KP_QUAD_PITCH*err_pitch + pitch_I + pitch_D;
control_pitch = constrain(control_pitch,-50,50);
}else{
control_pitch = ch_pitch;
}
// YAW CONTROL
if( yaw_control_switch )
{
if( command_yaw == 0 ) // heading hold mode
{
// check we don't need to reset targetHeading
if( command_yaw_previous != 0 )
targetHeading = ToDeg(yaw);
// ensure reasonable targetHeading
if( firstIteration || targetHeading > 180 || targetHeading < -180 )
{
firstIteration = 0;
targetHeading = ToDeg(yaw);
}
err_heading = Normalize_angle(targetHeading - ToDeg(yaw));
err_heading = constrain(err_heading,-90,90); // don't make it travel any faster beyond 90 degrees
// I term
heading_I += err_heading * heli_G_Dt * Ki_RateYaw;
heading_I = constrain(heading_I,-20,20);
// PID control - a bit bad - we're using the acro mode's PID values because there's not PID for heading
control_yaw_rate = Kp_RateYaw*err_heading + heading_I;
control_yaw_rate = constrain(control_yaw_rate,-100,100); // to limit max yaw command
}else{ // rate mode
err_heading = 0;
control_yaw_rate = command_yaw;
}
command_yaw_previous = command_yaw;
// YAW RATE CONTROL
currentYawRate = ToDeg(Gyro_Scaled_Z(read_adc(2)));
//currentYawRate = ToDeg(Omega_Vector[2]); <-- makes things very unstable!!
err_yaw = control_yaw_rate-currentYawRate;
// I term
yaw_I += err_yaw * heli_G_Dt * KI_QUAD_YAW;
yaw_I = constrain(yaw_I, -20, 20);
// D term
yaw_D = (currentYawRate-previousYawRate) * STABLE_MODE_KP_RATE_YAW; // Take into account the change in angular velocity
yaw_D = constrain(yaw_D,-25,25);
previousYawRate = currentYawRate;
// PID control
control_yaw = trim_yaw + (KP_QUAD_YAW*err_yaw + yaw_I + yaw_D); // add back in the yaw trim so that it is our center point
control_yaw = constrain(control_yaw,-50,50);
}else{
// straight pass through
control_yaw = ch_yaw;
}
Log_Write_PID(7,KP_QUAD_ROLL*err_roll,roll_I,roll_D,control_roll);
Log_Write_PID(8,KP_QUAD_PITCH*err_pitch,pitch_I,pitch_D,control_pitch);
Log_Write_PID(9,Kp_RateYaw*err_heading,heading_I,0,control_yaw_rate);
Log_Write_PID(10,KP_QUAD_YAW*err_yaw,yaw_I,yaw_D,control_yaw);
}
#endif // #if AIRFRAME == HELI