1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-02-01 17:52:12 +01:00
peabody124 6d0df704a7 Flight/Modules: Corrected code to use tabs
find ./flight/OpenPilot/Modules/ \! \( -name '*~' -a -prune \) -type f    |
xargs -I{} bash -c 'echo {}; dos2unix {}; gnuindent -npro -kr -i8 -ts8 -sob -ss
-ncs -cp1 -il0 -hnl -l150 {};'

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1776 ebee16cc-31ac-478f-84a7-5cbb03baadba
2010-09-27 07:28:34 +00:00

639 lines
25 KiB
C

/**
******************************************************************************
* @addtogroup OpenPilotModules OpenPilot Modules
* @{
* @addtogroup ActuatorModule Actuator Module
* @brief Compute servo/motor settings based on @ref ActuatorDesired "desired actuator positions" and aircraft type.
* This is where all the mixing of channels is computed.
* @{
*
* @file actuator.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief Actuator module. Drives the actuators (servos, motors etc).
*
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* 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, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "openpilot.h"
#include "actuator.h"
#include "actuatorsettings.h"
#include "vtolsettings.h"
#include "vtolstatus.h"
#include "systemsettings.h"
#include "actuatordesired.h"
#include "actuatorcommand.h"
#include "manualcontrolcommand.h"
//yaw stabilisation for helis...
#include "stabilizationsettings.h"
#include "attitudeactual.h"
//yaw stabilisation for helis...
// Private constants
#define MAX_QUEUE_SIZE 2
#define STACK_SIZE configMINIMAL_STACK_SIZE
#define TASK_PRIORITY (tskIDLE_PRIORITY+4)
#define FAILSAFE_TIMEOUT_MS 100
// Private types
//yaw stabilisation for helis...
//currently just coppied from simple stabilisation
#define YAW_INTEGRAL_LIMIT 0.5
AttitudeActualData attitudeActual;
StabilizationSettingsData stabSettings;
float ccpmYawError, ccpmYawErrorLast;
float ccpmYawDerivative;
float ccpmYawIntegral, ccpmYawIntegralLimit;
float desiredYaw;
static float bound(float val, float min, float max);
//yaw stabilisation for helis...
// Private variables
static xQueueHandle queue;
static xTaskHandle taskHandle;
// Private functions
static void actuatorTask(void *parameters);
static int32_t mixerFixedWing(const ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd);
static int32_t mixerFixedWingElevon(const ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd);
static int32_t mixerVTOL(const ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd);
static int32_t InterpolateCCPMCurve(float *Output, float input, const float *Curve);
static int32_t mixerCCPM(ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd);
static int16_t scaleChannel(float value, int16_t max, int16_t min, int16_t neutral);
static void setFailsafe();
static float bound(float val, float min, float max);
/**
* @brief Module initialization
* @return 0
*/
int32_t ActuatorInitialize()
{
// Create object queue
queue = xQueueCreate(MAX_QUEUE_SIZE, sizeof(UAVObjEvent));
// Listen for ExampleObject1 updates
ActuatorDesiredConnectQueue(queue);
ccpmYawErrorLast = 0.0;
// Start main task
xTaskCreate(actuatorTask, (signed char *)"Actuator", STACK_SIZE, NULL, TASK_PRIORITY, &taskHandle);
return 0;
}
/**
* @brief Main module task
*/
static void actuatorTask(void *parameters)
{
// UAVObjEvent ev;
ActuatorSettingsData settings;
SystemSettingsData sysSettings;
ActuatorDesiredData desired;
ActuatorCommandData cmd;
portTickType lastSysTime;
// Set servo update frequency (done only on start-up)
ActuatorSettingsGet(&settings);
PIOS_Servo_SetHz(settings.ChannelUpdateFreq[0], settings.ChannelUpdateFreq[1]);
// Go to the neutral (failsafe) values until an ActuatorDesired update is received
setFailsafe();
// Main task loop
lastSysTime = xTaskGetTickCount();
while (1) {
// Wait until the ActuatorDesired object is updated, if a timeout then go to failsafe
/*if ( xQueueReceive(queue, &ev, FAILSAFE_TIMEOUT_MS / portTICK_RATE_MS) != pdTRUE )
{
setFailsafe();
continue;
} */
// Read settings
ActuatorSettingsGet(&settings);
SystemSettingsGet(&sysSettings);
// Reset ActuatorCommand to neutral values
for (int n = 0; n < ACTUATORCOMMAND_CHANNEL_NUMELEM; ++n) {
cmd.Channel[n] = settings.ChannelNeutral[n];
}
// Read input object
ActuatorDesiredGet(&desired);
// Call appropriate mixer depending on the airframe configuration
if (sysSettings.AirframeType == SYSTEMSETTINGS_AIRFRAMETYPE_FIXEDWING) {
if (mixerFixedWing(&settings, &desired, &cmd) == -1) {
AlarmsSet(SYSTEMALARMS_ALARM_ACTUATOR, SYSTEMALARMS_ALARM_CRITICAL);
} else {
AlarmsClear(SYSTEMALARMS_ALARM_ACTUATOR);
}
} else if (sysSettings.AirframeType == SYSTEMSETTINGS_AIRFRAMETYPE_FIXEDWINGELEVON) {
if (mixerFixedWingElevon(&settings, &desired, &cmd) == -1) {
AlarmsSet(SYSTEMALARMS_ALARM_ACTUATOR, SYSTEMALARMS_ALARM_CRITICAL);
} else {
AlarmsClear(SYSTEMALARMS_ALARM_ACTUATOR);
}
} else if (sysSettings.AirframeType == SYSTEMSETTINGS_AIRFRAMETYPE_VTOL) {
if (mixerVTOL(&settings, &desired, &cmd) == -1) {
AlarmsSet(SYSTEMALARMS_ALARM_ACTUATOR, SYSTEMALARMS_ALARM_CRITICAL);
} else {
AlarmsClear(SYSTEMALARMS_ALARM_ACTUATOR);
}
} else if (sysSettings.AirframeType == SYSTEMSETTINGS_AIRFRAMETYPE_HELICP) {
if (mixerCCPM(&settings, &desired, &cmd) == -1) {
AlarmsSet(SYSTEMALARMS_ALARM_ACTUATOR, SYSTEMALARMS_ALARM_CRITICAL);
} else {
AlarmsClear(SYSTEMALARMS_ALARM_ACTUATOR);
}
}
// Update output object
ActuatorCommandSet(&cmd);
// Update in case read only (eg. during servo configuration)
ActuatorCommandGet(&cmd);
// Update servo outputs
for (int n = 0; n < ACTUATORCOMMAND_CHANNEL_NUMELEM; ++n) {
PIOS_Servo_Set(n, cmd.Channel[n]);
}
// Wait until next update
vTaskDelayUntil(&lastSysTime, settings.UpdatePeriod / portTICK_RATE_MS);
}
}
/**
* Mixer for Fixed Wing airframes. Converts desired roll,pitch,yaw and throttle to servo outputs.
* @return -1 if error, 0 if success
*/
static int32_t mixerFixedWing(const ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd)
{
// Check settings
if (settings->FixedWingPitch1 == ACTUATORSETTINGS_FIXEDWINGPITCH1_NONE ||
settings->FixedWingRoll1 == ACTUATORSETTINGS_FIXEDWINGROLL1_NONE ||
settings->FixedWingThrottle == ACTUATORSETTINGS_FIXEDWINGTHROTTLE_NONE) {
return -1;
}
ManualControlCommandData manualControl;
ManualControlCommandGet(&manualControl);
// Set pitch servo command
cmd->Channel[settings->FixedWingPitch1] = scaleChannel(desired->Pitch, settings->ChannelMax[settings->FixedWingPitch1],
settings->ChannelMin[settings->FixedWingPitch1],
settings->ChannelNeutral[settings->FixedWingPitch1]);
if (settings->FixedWingPitch2 != ACTUATORSETTINGS_FIXEDWINGPITCH2_NONE) {
cmd->Channel[settings->FixedWingPitch2] = scaleChannel(desired->Pitch, settings->ChannelMax[settings->FixedWingPitch2],
settings->ChannelMin[settings->FixedWingPitch2],
settings->ChannelNeutral[settings->FixedWingPitch2]);
}
// Set roll servo command
cmd->Channel[settings->FixedWingRoll1] = scaleChannel(desired->Roll, settings->ChannelMax[settings->FixedWingRoll1],
settings->ChannelMin[settings->FixedWingRoll1],
settings->ChannelNeutral[settings->FixedWingRoll1]);
if (settings->FixedWingRoll2 != ACTUATORSETTINGS_FIXEDWINGROLL2_NONE) {
cmd->Channel[settings->FixedWingRoll2] = scaleChannel(desired->Roll, settings->ChannelMax[settings->FixedWingRoll2],
settings->ChannelMin[settings->FixedWingRoll2],
settings->ChannelNeutral[settings->FixedWingRoll2]);
}
// Set yaw servo command
if (settings->FixedWingYaw != ACTUATORSETTINGS_FIXEDWINGYAW_NONE) {
cmd->Channel[settings->FixedWingYaw] = scaleChannel(desired->Yaw, settings->ChannelMax[settings->FixedWingYaw],
settings->ChannelMin[settings->FixedWingYaw],
settings->ChannelNeutral[settings->FixedWingYaw]);
}
// Set throttle servo command
if (manualControl.Armed == MANUALCONTROLCOMMAND_ARMED_TRUE)
cmd->Channel[settings->FixedWingThrottle] = scaleChannel(desired->Throttle, settings->ChannelMax[settings->FixedWingThrottle],
settings->ChannelMin[settings->FixedWingThrottle],
settings->ChannelNeutral[settings->FixedWingThrottle]);
else
cmd->Channel[settings->FixedWingThrottle] = -1;
// Done
return 0;
}
/**
* Mixer for Fixed Wing airframes with elevons. Converts desired roll,pitch,yaw and throttle to servo outputs.
* @return -1 if error, 0 if success
*/
static int32_t mixerFixedWingElevon(const ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd)
{
// Check settings
if (settings->FixedWingRoll1 == ACTUATORSETTINGS_FIXEDWINGROLL1_NONE ||
settings->FixedWingRoll2 == ACTUATORSETTINGS_FIXEDWINGROLL2_NONE ||
settings->FixedWingThrottle == ACTUATORSETTINGS_FIXEDWINGTHROTTLE_NONE) {
return -1;
}
ManualControlCommandData manualControl;
ManualControlCommandGet(&manualControl);
// Set first elevon servo command
cmd->Channel[settings->FixedWingRoll1] = scaleChannel(desired->Pitch + desired->Roll, settings->ChannelMax[settings->FixedWingRoll1],
settings->ChannelMin[settings->FixedWingRoll1],
settings->ChannelNeutral[settings->FixedWingRoll1]);
// Set second elevon servo command
cmd->Channel[settings->FixedWingRoll2] = scaleChannel(desired->Pitch - desired->Roll, settings->ChannelMax[settings->FixedWingRoll2],
settings->ChannelMin[settings->FixedWingRoll2],
settings->ChannelNeutral[settings->FixedWingRoll2]);
// Set throttle servo command
if (manualControl.Armed == MANUALCONTROLCOMMAND_ARMED_TRUE)
cmd->Channel[settings->FixedWingThrottle] = scaleChannel(desired->Throttle, settings->ChannelMax[settings->FixedWingThrottle],
settings->ChannelMin[settings->FixedWingThrottle],
settings->ChannelNeutral[settings->FixedWingThrottle]);
else
cmd->Channel[settings->FixedWingThrottle] = -1;
// Done
return 0;
}
/**
* Mixer for VTOL (quads and octo copters). Converts desired roll,pitch,yaw and throttle to servo outputs.
*
* I will probably add settings to change the weighting for each control to each motor. This will allow more
* flexible support for various motor topologies. For now hard coding in simple versions and lettings the
* scaling capabilities fix the subtlties.
*
* Also, because of how the Throttle ranges from 0 to 1, the motors should too!
*
* @return -1 if error, 0 if success
*/
static int32_t mixerVTOL(const ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd)
{
VTOLSettingsData vtolSettings;
VTOLStatusData vtolStatus;
VTOLSettingsGet(&vtolSettings);
VTOLStatusGet(&vtolStatus);
ManualControlCommandData manualControl;
ManualControlCommandGet(&manualControl);
const int vtolMin = -1;
int vtolMax = -1;
if ((manualControl.Armed == MANUALCONTROLCOMMAND_ARMED_TRUE) && (desired->Throttle >= 0))
vtolMax = 1;
else
vtolMax = -1;
if (((settings->VTOLMotorN != ACTUATORSETTINGS_VTOLMOTORN_NONE) +
(settings->VTOLMotorNE != ACTUATORSETTINGS_VTOLMOTORS_NONE) +
(settings->VTOLMotorE != ACTUATORSETTINGS_VTOLMOTORE_NONE) +
(settings->VTOLMotorSE != ACTUATORSETTINGS_VTOLMOTORSE_NONE) +
(settings->VTOLMotorS != ACTUATORSETTINGS_VTOLMOTORS_NONE) +
(settings->VTOLMotorSW != ACTUATORSETTINGS_VTOLMOTORSW_NONE) +
(settings->VTOLMotorW != ACTUATORSETTINGS_VTOLMOTORW_NONE) + (settings->VTOLMotorNW != ACTUATORSETTINGS_VTOLMOTORNW_NONE)) <= 2) {
return -1; // can't fly with 2 or less engines (i believe)
}
if (settings->VTOLMotorN != ACTUATORSETTINGS_VTOLMOTORN_NONE) {
vtolStatus.MotorN = desired->Throttle * vtolSettings.MotorN[VTOLSETTINGS_MOTORN_THROTTLE] +
desired->Pitch * vtolSettings.MotorN[VTOLSETTINGS_MOTORN_PITCH] +
desired->Roll * vtolSettings.MotorN[VTOLSETTINGS_MOTORN_ROLL] + desired->Yaw * vtolSettings.MotorN[VTOLSETTINGS_MOTORN_YAW];
cmd->Channel[settings->VTOLMotorN] = scaleChannel(bound(vtolStatus.MotorN, vtolMin, vtolMax),
settings->ChannelMax[settings->VTOLMotorN],
settings->ChannelMin[settings->VTOLMotorN],
settings->ChannelNeutral[settings->VTOLMotorN]);
}
if (settings->VTOLMotorNE != ACTUATORSETTINGS_VTOLMOTORNE_NONE) {
vtolStatus.MotorNE = desired->Throttle * vtolSettings.MotorNE[VTOLSETTINGS_MOTORNE_THROTTLE] +
desired->Pitch * vtolSettings.MotorNE[VTOLSETTINGS_MOTORNE_PITCH] +
desired->Roll * vtolSettings.MotorNE[VTOLSETTINGS_MOTORNE_ROLL] + desired->Yaw * vtolSettings.MotorNE[VTOLSETTINGS_MOTORNE_YAW];
cmd->Channel[settings->VTOLMotorNE] = scaleChannel(bound(vtolStatus.MotorNE, vtolMin, vtolMax),
settings->ChannelMax[settings->VTOLMotorNE],
settings->ChannelMin[settings->VTOLMotorNE],
settings->ChannelNeutral[settings->VTOLMotorNE]);
}
if (settings->VTOLMotorE != ACTUATORSETTINGS_VTOLMOTORE_NONE) {
vtolStatus.MotorE = desired->Throttle * vtolSettings.MotorE[VTOLSETTINGS_MOTORE_THROTTLE] +
desired->Pitch * vtolSettings.MotorE[VTOLSETTINGS_MOTORE_PITCH] +
desired->Roll * vtolSettings.MotorE[VTOLSETTINGS_MOTORE_ROLL] + desired->Yaw * vtolSettings.MotorE[VTOLSETTINGS_MOTORE_YAW];
cmd->Channel[settings->VTOLMotorE] = scaleChannel(bound(vtolStatus.MotorE, vtolMin, vtolMax),
settings->ChannelMax[settings->VTOLMotorE],
settings->ChannelMin[settings->VTOLMotorE],
settings->ChannelNeutral[settings->VTOLMotorE]);
}
if (settings->VTOLMotorSE != ACTUATORSETTINGS_VTOLMOTORSE_NONE) {
vtolStatus.MotorSE = desired->Throttle * vtolSettings.MotorSE[VTOLSETTINGS_MOTORSE_THROTTLE] +
desired->Pitch * vtolSettings.MotorSE[VTOLSETTINGS_MOTORSE_PITCH] +
desired->Roll * vtolSettings.MotorSE[VTOLSETTINGS_MOTORSE_ROLL] + desired->Yaw * vtolSettings.MotorSE[VTOLSETTINGS_MOTORSE_YAW];
cmd->Channel[settings->VTOLMotorSE] = scaleChannel(bound(vtolStatus.MotorSE, vtolMin, vtolMax),
settings->ChannelMax[settings->VTOLMotorSE],
settings->ChannelMin[settings->VTOLMotorSE],
settings->ChannelNeutral[settings->VTOLMotorSE]);
}
if (settings->VTOLMotorS != ACTUATORSETTINGS_VTOLMOTORS_NONE) {
vtolStatus.MotorS = desired->Throttle * vtolSettings.MotorS[VTOLSETTINGS_MOTORS_THROTTLE] +
desired->Pitch * vtolSettings.MotorS[VTOLSETTINGS_MOTORS_PITCH] +
desired->Roll * vtolSettings.MotorS[VTOLSETTINGS_MOTORS_ROLL] + desired->Yaw * vtolSettings.MotorS[VTOLSETTINGS_MOTORS_YAW];
cmd->Channel[settings->VTOLMotorS] = scaleChannel(bound(vtolStatus.MotorS, vtolMin, vtolMax),
settings->ChannelMax[settings->VTOLMotorS],
settings->ChannelMin[settings->VTOLMotorS],
settings->ChannelNeutral[settings->VTOLMotorS]);
}
if (settings->VTOLMotorSW != ACTUATORSETTINGS_VTOLMOTORSW_NONE) {
vtolStatus.MotorSW = bound(desired->Throttle * vtolSettings.MotorSW[VTOLSETTINGS_MOTORSW_THROTTLE] +
desired->Pitch * vtolSettings.MotorSW[VTOLSETTINGS_MOTORSW_PITCH] +
desired->Roll * vtolSettings.MotorSW[VTOLSETTINGS_MOTORSW_ROLL] +
desired->Yaw * vtolSettings.MotorSW[VTOLSETTINGS_MOTORSW_YAW], vtolMin, vtolMax);
cmd->Channel[settings->VTOLMotorSW] = scaleChannel(vtolStatus.MotorSW,
settings->ChannelMax[settings->VTOLMotorSW],
settings->ChannelMin[settings->VTOLMotorSW],
settings->ChannelNeutral[settings->VTOLMotorSW]);
}
if (settings->VTOLMotorW != ACTUATORSETTINGS_VTOLMOTORW_NONE) {
vtolStatus.MotorW = desired->Throttle * vtolSettings.MotorW[VTOLSETTINGS_MOTORW_THROTTLE] +
desired->Pitch * vtolSettings.MotorW[VTOLSETTINGS_MOTORW_PITCH] +
desired->Roll * vtolSettings.MotorW[VTOLSETTINGS_MOTORW_ROLL] + desired->Yaw * vtolSettings.MotorW[VTOLSETTINGS_MOTORW_YAW];
cmd->Channel[settings->VTOLMotorW] = scaleChannel(bound(vtolStatus.MotorW, vtolMin, vtolMax),
settings->ChannelMax[settings->VTOLMotorW],
settings->ChannelMin[settings->VTOLMotorW],
settings->ChannelNeutral[settings->VTOLMotorW]);
}
if (settings->VTOLMotorNW != ACTUATORSETTINGS_VTOLMOTORNW_NONE) {
vtolStatus.MotorNW = desired->Throttle * vtolSettings.MotorNW[VTOLSETTINGS_MOTORNW_THROTTLE] +
desired->Pitch * vtolSettings.MotorNW[VTOLSETTINGS_MOTORNW_PITCH] +
desired->Roll * vtolSettings.MotorNW[VTOLSETTINGS_MOTORNW_ROLL] + desired->Yaw * vtolSettings.MotorNW[VTOLSETTINGS_MOTORNW_YAW];
cmd->Channel[settings->VTOLMotorNW] = scaleChannel(bound(vtolStatus.MotorNW, vtolMin, vtolMax),
settings->ChannelMax[settings->VTOLMotorNW],
settings->ChannelMin[settings->VTOLMotorNW],
settings->ChannelNeutral[settings->VTOLMotorNW]);
}
VTOLStatusSet(&vtolStatus);
return 0;
}
static int32_t InterpolateCCPMCurve(float *output, float input, const float *curve)
{
int8_t curvIndex;
float slope;
float offset;
float value;
//determine which section of the 5 point curve we are on
if (input <= .25) {
curvIndex = 0;
offset = 0;
} else if (input <= .50) {
curvIndex = 1;
offset = 0.25;
} else if (input <= .75) {
curvIndex = 2;
offset = 0.50;
} else {
curvIndex = 3;
offset = 0.75;
}
//calculate the linear interpolation for the selected segment
slope = (curve[curvIndex + 1] - curve[curvIndex]) / 0.25;
value = curve[curvIndex] + ((input - offset) * slope);
//bound the output to valid percentage values
if (value > 100.0)
value = 100.0;
if (value < 0.0)
value = 0.0;
*output = value;
return 0;
}
/**
* Mixer for CCPM (single rotor helicopters). Converts desired roll,pitch,yaw and throttle to servo outputs.
* @return -1 if error, 0 if success
*/
static int32_t mixerCCPM(ActuatorSettingsData * settings, const ActuatorDesiredData * desired, ActuatorCommandData * cmd)
{
#define degtorad 0.0174532925 //pi/180
// TODO: Implement CCPM mixers
int configOK;
float curveValue;
float throttle;
float bladePitch;
float CCPMCyclicConstant;
float servoW;
float servoX;
float servoY;
float servoZ;
configOK = 0;
//calculate the throttle value based on the curve - scale to 0.0 -> +1.0
InterpolateCCPMCurve(&curveValue, desired->Throttle, settings->CCPMThrottleCurve);
throttle = curveValue / 100.0;
//calculate the Blade Pitch value based on the curve - scale to -1.0 -> +1.0
InterpolateCCPMCurve(&curveValue, desired->Throttle, settings->CCPMPitchCurve);
bladePitch = (curveValue / 50.0) - 1.0;
//calculate how much Cyclic to apply to the mixing
CCPMCyclicConstant = 1 - settings->CCPMCollectiveConstant;
// Set ServoW servo command
if (settings->CCPMServoW != ACTUATORSETTINGS_CCPMSERVOW_NONE) {
servoW = (CCPMCyclicConstant * ((sin((settings->CCPMAngleW + settings->CCPMCorrectionAngle) * degtorad) * desired->Roll)
+ (cos((settings->CCPMAngleW + settings->CCPMCorrectionAngle) * degtorad) * desired->Pitch)))
+ (settings->CCPMCollectiveConstant * bladePitch);
cmd->Channel[settings->CCPMServoW] = scaleChannel(servoW, settings->ChannelMax[settings->CCPMServoW],
settings->ChannelMin[settings->CCPMServoW],
settings->ChannelNeutral[settings->CCPMServoW]);
configOK++;
}
// Set ServoX servo command
if (settings->CCPMServoX != ACTUATORSETTINGS_CCPMSERVOX_NONE) {
servoX = (CCPMCyclicConstant * ((sin((settings->CCPMAngleX + settings->CCPMCorrectionAngle) * degtorad) * desired->Roll)
+ (cos((settings->CCPMAngleX + settings->CCPMCorrectionAngle) * degtorad) * desired->Pitch)))
+ (settings->CCPMCollectiveConstant * bladePitch);
cmd->Channel[settings->CCPMServoX] = scaleChannel(servoX, settings->ChannelMax[settings->CCPMServoX],
settings->ChannelMin[settings->CCPMServoX],
settings->ChannelNeutral[settings->CCPMServoX]);
configOK++;
}
// Set ServoY servo command
if (settings->CCPMServoY != ACTUATORSETTINGS_CCPMSERVOY_NONE) {
servoY = (CCPMCyclicConstant * ((sin((settings->CCPMAngleY + settings->CCPMCorrectionAngle) * degtorad) * desired->Roll)
+ (cos((settings->CCPMAngleY + settings->CCPMCorrectionAngle) * degtorad) * desired->Pitch)))
+ (settings->CCPMCollectiveConstant * bladePitch);
cmd->Channel[settings->CCPMServoY] = scaleChannel(servoY, settings->ChannelMax[settings->CCPMServoY],
settings->ChannelMin[settings->CCPMServoY],
settings->ChannelNeutral[settings->CCPMServoY]);
configOK++;
}
// Set ServoZ servo command
if (settings->CCPMServoZ != ACTUATORSETTINGS_CCPMSERVOZ_NONE) {
servoZ = (CCPMCyclicConstant * ((sin((settings->CCPMAngleZ + settings->CCPMCorrectionAngle) * degtorad) * desired->Roll)
+ (cos((settings->CCPMAngleZ + settings->CCPMCorrectionAngle) * degtorad) * desired->Pitch)))
+ (settings->CCPMCollectiveConstant * bladePitch);
cmd->Channel[settings->CCPMServoZ] = scaleChannel(servoZ, settings->ChannelMax[settings->CCPMServoZ],
settings->ChannelMin[settings->CCPMServoZ],
settings->ChannelNeutral[settings->CCPMServoZ]);
configOK++;
}
// Set throttle servo command
if (settings->CCPMThrottle != ACTUATORSETTINGS_CCPMTHROTTLE_NONE) {
cmd->Channel[settings->CCPMThrottle] = scaleChannel(throttle, settings->ChannelMax[settings->CCPMThrottle],
settings->ChannelMin[settings->CCPMThrottle],
settings->ChannelNeutral[settings->CCPMThrottle]);
} else {
configOK = 0;
}
// Set TailRotor servo command
if (settings->CCPMYawStabilizationInManualMode == ACTUATORSETTINGS_CCPMYAWSTABILIZATIONINMANUALMODE_FALSE) { //update the servo as per the desired control mode (manual or stabalized)
if (settings->CCPMTailRotor != ACTUATORSETTINGS_CCPMTAILROTOR_NONE) {
cmd->Channel[settings->CCPMTailRotor] = scaleChannel(desired->Yaw, settings->ChannelMax[settings->CCPMTailRotor],
settings->ChannelMin[settings->CCPMTailRotor],
settings->ChannelNeutral[settings->CCPMTailRotor]);
} else {
configOK = 0;
}
ccpmYawErrorLast = 0.0;
} else { //TODO need to implement the stabilization PID loop to provide heading hold gyro functionality in manual control mode
//currently just coppied from simple stabilisation
AttitudeActualGet(&attitudeActual);
StabilizationSettingsGet(&stabSettings);
if (desired->Yaw < 0) {
desiredYaw = 360 + (desired->Yaw * 180.0);
} else {
desiredYaw = (desired->Yaw * 180.0);
}
// Yaw stabilization control loop
ccpmYawError = desiredYaw - attitudeActual.Yaw;
//this should make it take the quickest path to reach the desired yaw
if (ccpmYawError > 180.0)
ccpmYawError -= 360;
if (ccpmYawError < -180.0)
ccpmYawError += 360;
ccpmYawDerivative = ccpmYawError - ccpmYawErrorLast;
ccpmYawIntegralLimit = YAW_INTEGRAL_LIMIT / stabSettings.YawKi;
ccpmYawIntegral = bound(ccpmYawIntegral + ccpmYawError * stabSettings.UpdatePeriod, -ccpmYawIntegralLimit, ccpmYawIntegralLimit);
desiredYaw = stabSettings.YawKp * ccpmYawError + stabSettings.YawKi * ccpmYawIntegral + stabSettings.YawKd * ccpmYawDerivative;;
desiredYaw = bound(desiredYaw, -1.0, 1.0);
ccpmYawErrorLast = ccpmYawError;
if (settings->CCPMTailRotor != ACTUATORSETTINGS_CCPMTAILROTOR_NONE) {
cmd->Channel[settings->CCPMTailRotor] = scaleChannel(desiredYaw, settings->ChannelMax[settings->CCPMTailRotor],
settings->ChannelMin[settings->CCPMTailRotor],
settings->ChannelNeutral[settings->CCPMTailRotor]);
} else {
configOK = 0;
}
}
if (configOK < 2)
return -1;
return 0;
}
/**
* Convert channel from -1/+1 to servo pulse duration in microseconds
*/
static int16_t scaleChannel(float value, int16_t max, int16_t min, int16_t neutral)
{
int16_t valueScaled;
// Scale
if (value >= 0.0) {
valueScaled = (int16_t) (value * ((float)(max - neutral))) + neutral;
} else {
valueScaled = (int16_t) (value * ((float)(neutral - min))) + neutral;
}
if (max > min) {
if (valueScaled > max)
valueScaled = max;
if (valueScaled < min)
valueScaled = min;
} else {
if (valueScaled < max)
valueScaled = max;
if (valueScaled > min)
valueScaled = min;
}
return valueScaled;
}
/**
* Set actuator output to the neutral values (failsafe)
*/
static void setFailsafe()
{
ActuatorSettingsData settings;
ActuatorCommandData cmd;
// Read settings
ActuatorSettingsGet(&settings);
// Reset ActuatorCommand to neutral values
for (int n = 0; n < ACTUATORCOMMAND_CHANNEL_NUMELEM; ++n) {
cmd.Channel[n] = settings.ChannelNeutral[n];
}
// Set alarm
AlarmsSet(SYSTEMALARMS_ALARM_ACTUATOR, SYSTEMALARMS_ALARM_CRITICAL);
// Update servo outputs
for (int n = 0; n < ACTUATORCOMMAND_CHANNEL_NUMELEM; ++n) {
PIOS_Servo_Set(n, cmd.Channel[n]);
}
// Update output object
ActuatorCommandSet(&cmd);
}
/**
* Bound input value between limits
*/
static float bound(float val, float min, float max)
{
if (val < min) {
val = min;
} else if (val > max) {
val = max;
}
return val;
}
/**
* @}
* @}
*/