mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-03 11:24:10 +01:00
da1a762072
This is functionally the same as having a heading hold gyro in hardware. Code is copied from the simple stabilisation routine and is a bit rough at the minute, but initial testing looks good. git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1670 ebee16cc-31ac-478f-84a7-5cbb03baadba
713 lines
27 KiB
C
713 lines
27 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;
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|