mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-01-18 03:52:11 +01:00
OP-1769 Ground Support
1. Receiver.c for ground allows disabled channels for all but yaw and throttle 2. Reciever.c applies a deadband on throttle 0 position 3. Arming understands ground have a 0 throttle position 4. Actuator optimisations in general and fixes for mixing 5. Actuator mixer support for reversible motors
This commit is contained in:
parent
8c31f96dd7
commit
52147333ed
@ -45,6 +45,9 @@
|
||||
#include "cameradesired.h"
|
||||
#include "manualcontrolcommand.h"
|
||||
#include "taskinfo.h"
|
||||
#include <systemsettings.h>
|
||||
#include <sanitycheck.h>
|
||||
#include <vtolpathfollowersettings.h>
|
||||
#undef PIOS_INCLUDE_INSTRUMENTATION
|
||||
#ifdef PIOS_INCLUDE_INSTRUMENTATION
|
||||
#include <pios_instrumentation.h>
|
||||
@ -76,26 +79,33 @@ static int8_t counter;
|
||||
// Private variables
|
||||
static xQueueHandle queue;
|
||||
static xTaskHandle taskHandle;
|
||||
static FrameType_t frameType = FRAME_TYPE_MULTIROTOR;
|
||||
static SystemSettingsThrustControlOptions thrustType = SYSTEMSETTINGS_THRUSTCONTROL_THROTTLE;
|
||||
|
||||
static float lastResult[MAX_MIX_ACTUATORS] = { 0 };
|
||||
static float filterAccumulator[MAX_MIX_ACTUATORS] = { 0 };
|
||||
static uint8_t pinsMode[MAX_MIX_ACTUATORS];
|
||||
// used to inform the actuator thread that actuator update rate is changed
|
||||
static volatile bool actuator_settings_updated;
|
||||
static ActuatorSettingsData actuatorSettings;
|
||||
static bool spinWhileArmed;
|
||||
|
||||
// used to inform the actuator thread that mixer settings are changed
|
||||
static volatile bool mixer_settings_updated;
|
||||
static MixerSettingsData mixerSettings;
|
||||
static int mixer_settings_count = 2;
|
||||
|
||||
// Private functions
|
||||
static void actuatorTask(void *parameters);
|
||||
static int16_t scaleChannel(float value, int16_t max, int16_t min, int16_t neutral);
|
||||
static void setFailsafe(const ActuatorSettingsData *actuatorSettings, const MixerSettingsData *mixerSettings);
|
||||
static float MixerCurve(const float throttle, const float *curve, uint8_t elements);
|
||||
static bool set_channel(uint8_t mixer_channel, uint16_t value, const ActuatorSettingsData *actuatorSettings);
|
||||
static void actuator_update_rate_if_changed(const ActuatorSettingsData *actuatorSettings, bool force_update);
|
||||
static void setFailsafe();
|
||||
static float MixerCurveFullRangeProportional(const float input, const float *curve, uint8_t elements);
|
||||
static float MixerCurveFullRangeAbsolute(const float input, const float *curve, uint8_t elements);
|
||||
static bool set_channel(uint8_t mixer_channel, uint16_t value);
|
||||
static void actuator_update_rate_if_changed(bool force_update);
|
||||
static void MixerSettingsUpdatedCb(UAVObjEvent *ev);
|
||||
static void ActuatorSettingsUpdatedCb(UAVObjEvent *ev);
|
||||
static void SettingsUpdatedCb(UAVObjEvent *ev);
|
||||
float ProcessMixer(const int index, const float curve1, const float curve2,
|
||||
const MixerSettingsData *mixerSettings, ActuatorDesiredData *desired,
|
||||
ActuatorDesiredData *desired,
|
||||
const float period);
|
||||
|
||||
// this structure is equivalent to the UAVObjects for one mixer.
|
||||
@ -104,6 +114,8 @@ typedef struct {
|
||||
int8_t matrix[5];
|
||||
} __attribute__((packed)) Mixer_t;
|
||||
|
||||
#define DIAG_MIXERSTATUS
|
||||
|
||||
/**
|
||||
* @brief Module initialization
|
||||
* @return 0
|
||||
@ -116,6 +128,9 @@ int32_t ActuatorStart()
|
||||
#ifdef PIOS_INCLUDE_WDG
|
||||
PIOS_WDG_RegisterFlag(PIOS_WDG_ACTUATOR);
|
||||
#endif
|
||||
SettingsUpdatedCb(NULL);
|
||||
MixerSettingsUpdatedCb(NULL);
|
||||
ActuatorSettingsUpdatedCb(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -149,6 +164,11 @@ int32_t ActuatorInitialize()
|
||||
MixerStatusInitialize();
|
||||
#endif
|
||||
|
||||
VtolPathFollowerSettingsInitialize();
|
||||
VtolPathFollowerSettingsConnectCallback(&SettingsUpdatedCb);
|
||||
SystemSettingsInitialize();
|
||||
SystemSettingsConnectCallback(&SettingsUpdatedCb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
MODULE_INITCALL(ActuatorInitialize, ActuatorStart);
|
||||
@ -178,7 +198,6 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
ActuatorDesiredData desired;
|
||||
MixerStatusData mixerStatus;
|
||||
FlightStatusData flightStatus;
|
||||
SystemSettingsThrustControlOptions thrustType;
|
||||
float throttleDesired;
|
||||
float collectiveDesired;
|
||||
|
||||
@ -186,21 +205,17 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
counter = PIOS_Instrumentation_CreateCounter(0xAC700001);
|
||||
#endif
|
||||
/* Read initial values of ActuatorSettings */
|
||||
ActuatorSettingsData actuatorSettings;
|
||||
|
||||
actuator_settings_updated = false;
|
||||
ActuatorSettingsGet(&actuatorSettings);
|
||||
|
||||
/* Read initial values of MixerSettings */
|
||||
MixerSettingsData mixerSettings;
|
||||
mixer_settings_updated = false;
|
||||
MixerSettingsGet(&mixerSettings);
|
||||
|
||||
/* Force an initial configuration of the actuator update rates */
|
||||
actuator_update_rate_if_changed(&actuatorSettings, true);
|
||||
actuator_update_rate_if_changed(true);
|
||||
|
||||
// Go to the neutral (failsafe) values until an ActuatorDesired update is received
|
||||
setFailsafe(&actuatorSettings, &mixerSettings);
|
||||
setFailsafe();
|
||||
|
||||
// Main task loop
|
||||
lastSysTime = xTaskGetTickCount();
|
||||
@ -214,20 +229,10 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
#ifdef PIOS_INCLUDE_INSTRUMENTATION
|
||||
PIOS_Instrumentation_TimeStart(counter);
|
||||
#endif
|
||||
/* Process settings updated events even in timeout case so we always act on the latest settings */
|
||||
if (actuator_settings_updated) {
|
||||
actuator_settings_updated = false;
|
||||
ActuatorSettingsGet(&actuatorSettings);
|
||||
actuator_update_rate_if_changed(&actuatorSettings, false);
|
||||
}
|
||||
if (mixer_settings_updated) {
|
||||
mixer_settings_updated = false;
|
||||
MixerSettingsGet(&mixerSettings);
|
||||
}
|
||||
|
||||
if (rc != pdTRUE) {
|
||||
/* Update of ActuatorDesired timed out. Go to failsafe */
|
||||
setFailsafe(&actuatorSettings, &mixerSettings);
|
||||
setFailsafe();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -240,7 +245,6 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
FlightStatusGet(&flightStatus);
|
||||
ActuatorDesiredGet(&desired);
|
||||
ActuatorCommandGet(&command);
|
||||
SystemSettingsThrustControlGet(&thrustType);
|
||||
|
||||
// read in throttle and collective -demultiplex thrust
|
||||
switch (thrustType) {
|
||||
@ -259,11 +263,16 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
|
||||
bool armed = flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMED;
|
||||
|
||||
// ground needs to cature for reverse which is negative throttle
|
||||
bool activeThrottle = (throttleDesired < 0.00f || throttleDesired > 0.00f);
|
||||
bool positiveThrottle = (throttleDesired > 0.00f);
|
||||
|
||||
// safety settings
|
||||
if (!armed) {
|
||||
throttleDesired = 0;
|
||||
}
|
||||
if (throttleDesired <= 0.00f || !armed) {
|
||||
|
||||
if ((frameType == FRAME_TYPE_GROUND && !activeThrottle) || (frameType != FRAME_TYPE_GROUND && throttleDesired <= 0.00f) || !armed) {
|
||||
// force set all other controls to zero if throttle is cut (previously set in Stabilization)
|
||||
if (actuatorSettings.LowThrottleZeroAxis.Roll == ACTUATORSETTINGS_LOWTHROTTLEZEROAXIS_TRUE) {
|
||||
desired.Roll = 0;
|
||||
@ -279,46 +288,47 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
#ifdef DIAG_MIXERSTATUS
|
||||
MixerStatusGet(&mixerStatus);
|
||||
#endif
|
||||
int nMixers = 0;
|
||||
Mixer_t *mixers = (Mixer_t *)&mixerSettings.Mixer1Type;
|
||||
for (int ct = 0; ct < MAX_MIX_ACTUATORS; ct++) {
|
||||
if (mixers[ct].type != MIXERSETTINGS_MIXER1TYPE_DISABLED) {
|
||||
nMixers++;
|
||||
}
|
||||
}
|
||||
if ((nMixers < 2) && !ActuatorCommandReadOnly()) { // Nothing can fly with less than two mixers.
|
||||
setFailsafe(&actuatorSettings, &mixerSettings); // So that channels like PWM buzzer keep working
|
||||
|
||||
if ((mixer_settings_count < 2) && !ActuatorCommandReadOnly()) { // Nothing can fly with less than two mixers.
|
||||
setFailsafe();
|
||||
continue;
|
||||
}
|
||||
|
||||
AlarmsClear(SYSTEMALARMS_ALARM_ACTUATOR);
|
||||
|
||||
bool activeThrottle = (throttleDesired < 0.00f || throttleDesired > 0.00f);
|
||||
bool positiveThrottle = (throttleDesired > 0.00f);
|
||||
bool spinWhileArmed = actuatorSettings.MotorsSpinWhileArmed == ACTUATORSETTINGS_MOTORSSPINWHILEARMED_TRUE;
|
||||
float curve1 = 0.0f;
|
||||
float curve2 = 0.0f;
|
||||
|
||||
float curve1 = MixerCurve(throttleDesired, mixerSettings.ThrottleCurve1, MIXERSETTINGS_THROTTLECURVE1_NUMELEM);
|
||||
// Interpolate curve 1 from throttleDesired as input.
|
||||
// assume reversible motor/mixer initially. We can later reverse this. The difference is simply that -ve throttleDesired values
|
||||
// map differently
|
||||
curve1 = MixerCurveFullRangeProportional(throttleDesired, mixerSettings.ThrottleCurve1, MIXERSETTINGS_THROTTLECURVE1_NUMELEM);
|
||||
|
||||
// The source for the secondary curve is selectable
|
||||
float curve2 = 0;
|
||||
AccessoryDesiredData accessory;
|
||||
switch (mixerSettings.Curve2Source) {
|
||||
uint8_t curve2Source = mixerSettings.Curve2Source;
|
||||
switch (curve2Source) {
|
||||
case MIXERSETTINGS_CURVE2SOURCE_THROTTLE:
|
||||
curve2 = MixerCurve(throttleDesired, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
// assume reversible motor/mixer initially
|
||||
curve2 = MixerCurveFullRangeProportional(throttleDesired, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
break;
|
||||
case MIXERSETTINGS_CURVE2SOURCE_ROLL:
|
||||
curve2 = MixerCurve(desired.Roll, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
// Throttle curve contribution the same for +ve vs -ve roll
|
||||
curve2 = MixerCurveFullRangeAbsolute(desired.Roll, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
break;
|
||||
case MIXERSETTINGS_CURVE2SOURCE_PITCH:
|
||||
curve2 = MixerCurve(desired.Pitch, mixerSettings.ThrottleCurve2,
|
||||
MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
// Throttle curve contribution the same for +ve vs -ve pitch
|
||||
curve2 = MixerCurveFullRangeAbsolute(desired.Pitch, mixerSettings.ThrottleCurve2,
|
||||
MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
break;
|
||||
case MIXERSETTINGS_CURVE2SOURCE_YAW:
|
||||
curve2 = MixerCurve(desired.Yaw, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
// Throttle curve contribution the same for +ve vs -ve yaw
|
||||
curve2 = MixerCurveFullRangeAbsolute(desired.Yaw, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
break;
|
||||
case MIXERSETTINGS_CURVE2SOURCE_COLLECTIVE:
|
||||
curve2 = MixerCurve(collectiveDesired, mixerSettings.ThrottleCurve2,
|
||||
MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
// assume reversible motor/mixer initially
|
||||
curve2 = MixerCurveFullRangeProportional(collectiveDesired, mixerSettings.ThrottleCurve2,
|
||||
MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
break;
|
||||
case MIXERSETTINGS_CURVE2SOURCE_ACCESSORY0:
|
||||
case MIXERSETTINGS_CURVE2SOURCE_ACCESSORY1:
|
||||
@ -327,14 +337,19 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
case MIXERSETTINGS_CURVE2SOURCE_ACCESSORY4:
|
||||
case MIXERSETTINGS_CURVE2SOURCE_ACCESSORY5:
|
||||
if (AccessoryDesiredInstGet(mixerSettings.Curve2Source - MIXERSETTINGS_CURVE2SOURCE_ACCESSORY0, &accessory) == 0) {
|
||||
curve2 = MixerCurve(accessory.AccessoryVal, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
// Throttle curve contribution the same for +ve vs -ve accessory....maybe not want we want.
|
||||
curve2 = MixerCurveFullRangeAbsolute(accessory.AccessoryVal, mixerSettings.ThrottleCurve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
|
||||
} else {
|
||||
curve2 = 0;
|
||||
curve2 = 0.0f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
curve2 = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
float *status = (float *)&mixerStatus; // access status objects as an array of floats
|
||||
float *status = (float *)&mixerStatus; // access status objects as an array of floats
|
||||
Mixer_t *mixers = (Mixer_t *)&mixerSettings.Mixer1Type;
|
||||
|
||||
for (int ct = 0; ct < MAX_MIX_ACTUATORS; ct++) {
|
||||
// During boot all camera actuators should be completely disabled (PWM pulse = 0).
|
||||
@ -343,20 +358,22 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
// Setting it to 1 by default means "Rescale this channel and enable PWM on its output".
|
||||
command.Channel[ct] = 1;
|
||||
|
||||
if (mixers[ct].type == MIXERSETTINGS_MIXER1TYPE_DISABLED) {
|
||||
uint8_t mixer_type = mixers[ct].type;
|
||||
|
||||
if (mixer_type == MIXERSETTINGS_MIXER1TYPE_DISABLED) {
|
||||
// Set to minimum if disabled. This is not the same as saying PWM pulse = 0 us
|
||||
status[ct] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((mixers[ct].type == MIXERSETTINGS_MIXER1TYPE_MOTOR) || (mixers[ct].type == MIXERSETTINGS_MIXER1TYPE_REVERSABLEMOTOR) || (mixers[ct].type == MIXERSETTINGS_MIXER1TYPE_SERVO)) {
|
||||
status[ct] = ProcessMixer(ct, curve1, curve2, &mixerSettings, &desired, dTSeconds);
|
||||
} else {
|
||||
status[ct] = -1;
|
||||
}
|
||||
|
||||
// Motors have additional protection for when to be on
|
||||
if (mixers[ct].type == MIXERSETTINGS_MIXER1TYPE_MOTOR) {
|
||||
if ((mixer_type == MIXERSETTINGS_MIXER1TYPE_MOTOR)) {
|
||||
if (curve1 < 0.0f) {
|
||||
curve1 = 0.0f;
|
||||
}
|
||||
if (curve2 < 0.0f) {
|
||||
curve2 = 0.0f;
|
||||
}
|
||||
status[ct] = ProcessMixer(ct, curve1, curve2, &desired, dTSeconds);
|
||||
// If not armed or motors aren't meant to spin all the time
|
||||
if (!armed ||
|
||||
(!spinWhileArmed && !positiveThrottle)) {
|
||||
@ -369,57 +386,60 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
(status[ct] < 0)) {
|
||||
status[ct] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Reversable Motors are like Motors but go to neutral instead of minimum
|
||||
if (mixers[ct].type == MIXERSETTINGS_MIXER1TYPE_REVERSABLEMOTOR) {
|
||||
} else if (mixer_type == MIXERSETTINGS_MIXER1TYPE_REVERSABLEMOTOR) {
|
||||
status[ct] = ProcessMixer(ct, curve1, curve2, &desired, dTSeconds);
|
||||
// Reversable Motors are like Motors but go to neutral instead of minimum
|
||||
// If not armed or motor is inactive - no "spinwhilearmed" for this engine type
|
||||
if (!armed || !activeThrottle) {
|
||||
filterAccumulator[ct] = 0;
|
||||
lastResult[ct] = 0;
|
||||
status[ct] = 0; // force neutral throttle
|
||||
}
|
||||
}
|
||||
} else if (mixer_type == MIXERSETTINGS_MIXER1TYPE_SERVO) {
|
||||
status[ct] = ProcessMixer(ct, curve1, curve2, &desired, dTSeconds);
|
||||
} else {
|
||||
status[ct] = -1;
|
||||
|
||||
// If an accessory channel is selected for direct bypass mode
|
||||
// In this configuration the accessory channel is scaled and mapped
|
||||
// directly to output. Note: THERE IS NO SAFETY CHECK HERE FOR ARMING
|
||||
// these also will not be updated in failsafe mode. I'm not sure what
|
||||
// the correct behavior is since it seems domain specific. I don't love
|
||||
// this code
|
||||
if ((mixers[ct].type >= MIXERSETTINGS_MIXER1TYPE_ACCESSORY0) &&
|
||||
(mixers[ct].type <= MIXERSETTINGS_MIXER1TYPE_ACCESSORY5)) {
|
||||
if (AccessoryDesiredInstGet(mixers[ct].type - MIXERSETTINGS_MIXER1TYPE_ACCESSORY0, &accessory) == 0) {
|
||||
status[ct] = accessory.AccessoryVal;
|
||||
} else {
|
||||
status[ct] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mixers[ct].type >= MIXERSETTINGS_MIXER1TYPE_CAMERAROLLORSERVO1) &&
|
||||
(mixers[ct].type <= MIXERSETTINGS_MIXER1TYPE_CAMERAYAW)) {
|
||||
CameraDesiredData cameraDesired;
|
||||
if (CameraDesiredGet(&cameraDesired) == 0) {
|
||||
switch (mixers[ct].type) {
|
||||
case MIXERSETTINGS_MIXER1TYPE_CAMERAROLLORSERVO1:
|
||||
status[ct] = cameraDesired.RollOrServo1;
|
||||
break;
|
||||
case MIXERSETTINGS_MIXER1TYPE_CAMERAPITCHORSERVO2:
|
||||
status[ct] = cameraDesired.PitchOrServo2;
|
||||
break;
|
||||
case MIXERSETTINGS_MIXER1TYPE_CAMERAYAW:
|
||||
status[ct] = cameraDesired.Yaw;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
// If an accessory channel is selected for direct bypass mode
|
||||
// In this configuration the accessory channel is scaled and mapped
|
||||
// directly to output. Note: THERE IS NO SAFETY CHECK HERE FOR ARMING
|
||||
// these also will not be updated in failsafe mode. I'm not sure what
|
||||
// the correct behavior is since it seems domain specific. I don't love
|
||||
// this code
|
||||
if ((mixer_type >= MIXERSETTINGS_MIXER1TYPE_ACCESSORY0) &&
|
||||
(mixer_type <= MIXERSETTINGS_MIXER1TYPE_ACCESSORY5)) {
|
||||
if (AccessoryDesiredInstGet(mixer_type - MIXERSETTINGS_MIXER1TYPE_ACCESSORY0, &accessory) == 0) {
|
||||
status[ct] = accessory.AccessoryVal;
|
||||
} else {
|
||||
status[ct] = -1;
|
||||
}
|
||||
} else {
|
||||
status[ct] = -1;
|
||||
}
|
||||
|
||||
// Disable camera actuators for CAMERA_BOOT_DELAY_MS after boot
|
||||
if (thisSysTime < (CAMERA_BOOT_DELAY_MS / portTICK_RATE_MS)) {
|
||||
command.Channel[ct] = 0;
|
||||
if ((mixer_type >= MIXERSETTINGS_MIXER1TYPE_CAMERAROLLORSERVO1) &&
|
||||
(mixer_type <= MIXERSETTINGS_MIXER1TYPE_CAMERAYAW)) {
|
||||
CameraDesiredData cameraDesired;
|
||||
if (CameraDesiredGet(&cameraDesired) == 0) {
|
||||
switch (mixer_type) {
|
||||
case MIXERSETTINGS_MIXER1TYPE_CAMERAROLLORSERVO1:
|
||||
status[ct] = cameraDesired.RollOrServo1;
|
||||
break;
|
||||
case MIXERSETTINGS_MIXER1TYPE_CAMERAPITCHORSERVO2:
|
||||
status[ct] = cameraDesired.PitchOrServo2;
|
||||
break;
|
||||
case MIXERSETTINGS_MIXER1TYPE_CAMERAYAW:
|
||||
status[ct] = cameraDesired.Yaw;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
status[ct] = -1;
|
||||
}
|
||||
|
||||
// Disable camera actuators for CAMERA_BOOT_DELAY_MS after boot
|
||||
if (thisSysTime < (CAMERA_BOOT_DELAY_MS / portTICK_RATE_MS)) {
|
||||
command.Channel[ct] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -454,7 +474,7 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
bool success = true;
|
||||
|
||||
for (int n = 0; n < ACTUATORCOMMAND_CHANNEL_NUMELEM; ++n) {
|
||||
success &= set_channel(n, command.Channel[n], &actuatorSettings);
|
||||
success &= set_channel(n, command.Channel[n]);
|
||||
}
|
||||
|
||||
PIOS_Servo_Update();
|
||||
@ -475,10 +495,10 @@ static void actuatorTask(__attribute__((unused)) void *parameters)
|
||||
* Process mixing for one actuator
|
||||
*/
|
||||
float ProcessMixer(const int index, const float curve1, const float curve2,
|
||||
const MixerSettingsData *mixerSettings, ActuatorDesiredData *desired, const float period)
|
||||
ActuatorDesiredData *desired, const float period)
|
||||
{
|
||||
static float lastFilteredResult[MAX_MIX_ACTUATORS];
|
||||
const Mixer_t *mixers = (Mixer_t *)&mixerSettings->Mixer1Type; // pointer to array of mixers in UAVObjects
|
||||
const Mixer_t *mixers = (Mixer_t *)&mixerSettings.Mixer1Type; // pointer to array of mixers in UAVObjects
|
||||
const Mixer_t *mixer = &mixers[index];
|
||||
|
||||
float result = ((((float)mixer->matrix[MIXERSETTINGS_MIXER1VECTOR_THROTTLECURVE1]) * curve1) +
|
||||
@ -495,18 +515,18 @@ float ProcessMixer(const int index, const float curve1, const float curve2,
|
||||
|
||||
// feed forward
|
||||
float accumulator = filterAccumulator[index];
|
||||
accumulator += (result - lastResult[index]) * mixerSettings->FeedForward;
|
||||
accumulator += (result - lastResult[index]) * mixerSettings.FeedForward;
|
||||
lastResult[index] = result;
|
||||
result += accumulator;
|
||||
if (period > 0.0f) {
|
||||
if (accumulator > 0.0f) {
|
||||
float invFilter = period / mixerSettings->AccelTime;
|
||||
float invFilter = period / mixerSettings.AccelTime;
|
||||
if (invFilter > 1) {
|
||||
invFilter = 1;
|
||||
}
|
||||
accumulator -= accumulator * invFilter;
|
||||
} else {
|
||||
float invFilter = period / mixerSettings->DecelTime;
|
||||
float invFilter = period / mixerSettings.DecelTime;
|
||||
if (invFilter > 1) {
|
||||
invFilter = 1;
|
||||
}
|
||||
@ -518,7 +538,7 @@ float ProcessMixer(const int index, const float curve1, const float curve2,
|
||||
|
||||
// acceleration limit
|
||||
float dt = result - lastFilteredResult[index];
|
||||
float maxDt = mixerSettings->MaxAccel * period;
|
||||
float maxDt = mixerSettings.MaxAccel * period;
|
||||
if (dt > maxDt) { // we are accelerating too hard
|
||||
result = lastFilteredResult[index] + maxDt;
|
||||
}
|
||||
@ -530,17 +550,23 @@ float ProcessMixer(const int index, const float curve1, const float curve2,
|
||||
|
||||
|
||||
/**
|
||||
* Interpolate a throttle curve. Throttle input should be in the range 0 to 1.
|
||||
* Output is in the range 0 to 1.
|
||||
* Interpolate a throttle curve
|
||||
* Full range input (-1 to 1) for yaw, roll, pitch
|
||||
* Output range (-1 to 1) reversible motor/throttle curve
|
||||
*
|
||||
* Input of -1 -> -lookup(1)
|
||||
* Input of 0 -> lookup(0)
|
||||
* Input of 1 -> lookup(1)
|
||||
*/
|
||||
static float MixerCurve(const float throttle, const float *curve, uint8_t elements)
|
||||
static float MixerCurveFullRangeProportional(const float input, const float *curve, uint8_t elements)
|
||||
{
|
||||
float scale = throttle * (float)(elements - 1);
|
||||
int idx1 = scale;
|
||||
float abs_input = fabsf(input);
|
||||
float scale = abs_input * (float)(elements - 1);
|
||||
int idx1 = scale;
|
||||
|
||||
scale -= (float)idx1; // remainder
|
||||
if (curve[0] < -1) {
|
||||
return throttle;
|
||||
return input;
|
||||
}
|
||||
if (idx1 < 0) {
|
||||
idx1 = 0; // clamp to lowest entry in table
|
||||
@ -553,7 +579,48 @@ static float MixerCurve(const float throttle, const float *curve, uint8_t elemen
|
||||
idx1 = elements - 1;
|
||||
}
|
||||
}
|
||||
return curve[idx1] * (1.0f - scale) + curve[idx2] * scale;
|
||||
|
||||
float unsigned_value = curve[idx1] * (1.0f - scale) + curve[idx2] * scale;
|
||||
if (input < 0.0f) {
|
||||
return -unsigned_value;
|
||||
} else {
|
||||
return unsigned_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate a throttle curve
|
||||
* Full range input (-1 to 1) for yaw, roll, pitch
|
||||
* Output range (0 to 1) non-reversible motor/throttle curve
|
||||
*
|
||||
* Input of -1 -> lookup(1)
|
||||
* Input of 0 -> lookup(0)
|
||||
* Input of 1 -> lookup(1)
|
||||
*/
|
||||
static float MixerCurveFullRangeAbsolute(const float input, const float *curve, uint8_t elements)
|
||||
{
|
||||
float abs_input = fabsf(input);
|
||||
float scale = abs_input * (float)(elements - 1);
|
||||
int idx1 = scale;
|
||||
|
||||
scale -= (float)idx1; // remainder
|
||||
if (curve[0] < -1) {
|
||||
return input;
|
||||
}
|
||||
if (idx1 < 0) {
|
||||
idx1 = 0; // clamp to lowest entry in table
|
||||
scale = 0;
|
||||
}
|
||||
int idx2 = idx1 + 1;
|
||||
if (idx2 >= elements) {
|
||||
idx2 = elements - 1; // clamp to highest entry in table
|
||||
if (idx1 >= elements) {
|
||||
idx1 = elements - 1;
|
||||
}
|
||||
}
|
||||
|
||||
float unsigned_value = curve[idx1] * (1.0f - scale) + curve[idx2] * scale;
|
||||
return unsigned_value;
|
||||
}
|
||||
|
||||
|
||||
@ -593,21 +660,22 @@ static int16_t scaleChannel(float value, int16_t max, int16_t min, int16_t neutr
|
||||
/**
|
||||
* Set actuator output to the neutral values (failsafe)
|
||||
*/
|
||||
static void setFailsafe(const ActuatorSettingsData *actuatorSettings, const MixerSettingsData *mixerSettings)
|
||||
static void setFailsafe()
|
||||
{
|
||||
/* grab only the parts that we are going to use */
|
||||
int16_t Channel[ACTUATORCOMMAND_CHANNEL_NUMELEM];
|
||||
|
||||
ActuatorCommandChannelGet(Channel);
|
||||
|
||||
const Mixer_t *mixers = (Mixer_t *)&mixerSettings->Mixer1Type; // pointer to array of mixers in UAVObjects
|
||||
const Mixer_t *mixers = (Mixer_t *)&mixerSettings.Mixer1Type; // pointer to array of mixers in UAVObjects
|
||||
|
||||
// Reset ActuatorCommand to safe values
|
||||
for (int n = 0; n < ACTUATORCOMMAND_CHANNEL_NUMELEM; ++n) {
|
||||
if (mixers[n].type == MIXERSETTINGS_MIXER1TYPE_MOTOR) {
|
||||
Channel[n] = actuatorSettings->ChannelMin[n];
|
||||
Channel[n] = actuatorSettings.ChannelMin[n];
|
||||
} else if (mixers[n].type == MIXERSETTINGS_MIXER1TYPE_SERVO || mixers[n].type == MIXERSETTINGS_MIXER1TYPE_REVERSABLEMOTOR) {
|
||||
Channel[n] = actuatorSettings->ChannelNeutral[n];
|
||||
// reversible motors need calibration wizard that allows channel neutral to be the 0 velocity point
|
||||
Channel[n] = actuatorSettings.ChannelNeutral[n];
|
||||
} else {
|
||||
Channel[n] = 0;
|
||||
}
|
||||
@ -618,7 +686,7 @@ static void setFailsafe(const ActuatorSettingsData *actuatorSettings, const Mixe
|
||||
|
||||
// Update servo outputs
|
||||
for (int n = 0; n < ACTUATORCOMMAND_CHANNEL_NUMELEM; ++n) {
|
||||
set_channel(n, Channel[n], actuatorSettings);
|
||||
set_channel(n, Channel[n]);
|
||||
}
|
||||
// Send the updated command
|
||||
PIOS_Servo_Update();
|
||||
@ -713,39 +781,39 @@ static inline bool buzzerState(buzzertype type)
|
||||
|
||||
|
||||
#if defined(ARCH_POSIX) || defined(ARCH_WIN32)
|
||||
static bool set_channel(uint8_t mixer_channel, uint16_t value, const ActuatorSettingsData *actuatorSettings)
|
||||
static bool set_channel(uint8_t mixer_channel, uint16_t value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static bool set_channel(uint8_t mixer_channel, uint16_t value, const ActuatorSettingsData *actuatorSettings)
|
||||
static bool set_channel(uint8_t mixer_channel, uint16_t value)
|
||||
{
|
||||
switch (actuatorSettings->ChannelType[mixer_channel]) {
|
||||
switch (actuatorSettings.ChannelType[mixer_channel]) {
|
||||
case ACTUATORSETTINGS_CHANNELTYPE_PWMALARMBUZZER:
|
||||
PIOS_Servo_Set(actuatorSettings->ChannelAddr[mixer_channel],
|
||||
buzzerState(BUZZ_BUZZER) ? actuatorSettings->ChannelMax[mixer_channel] : actuatorSettings->ChannelMin[mixer_channel]);
|
||||
PIOS_Servo_Set(actuatorSettings.ChannelAddr[mixer_channel],
|
||||
buzzerState(BUZZ_BUZZER) ? actuatorSettings.ChannelMax[mixer_channel] : actuatorSettings.ChannelMin[mixer_channel]);
|
||||
return true;
|
||||
|
||||
case ACTUATORSETTINGS_CHANNELTYPE_ARMINGLED:
|
||||
PIOS_Servo_Set(actuatorSettings->ChannelAddr[mixer_channel],
|
||||
buzzerState(BUZZ_ARMING) ? actuatorSettings->ChannelMax[mixer_channel] : actuatorSettings->ChannelMin[mixer_channel]);
|
||||
PIOS_Servo_Set(actuatorSettings.ChannelAddr[mixer_channel],
|
||||
buzzerState(BUZZ_ARMING) ? actuatorSettings.ChannelMax[mixer_channel] : actuatorSettings.ChannelMin[mixer_channel]);
|
||||
return true;
|
||||
|
||||
case ACTUATORSETTINGS_CHANNELTYPE_INFOLED:
|
||||
PIOS_Servo_Set(actuatorSettings->ChannelAddr[mixer_channel],
|
||||
buzzerState(BUZZ_INFO) ? actuatorSettings->ChannelMax[mixer_channel] : actuatorSettings->ChannelMin[mixer_channel]);
|
||||
PIOS_Servo_Set(actuatorSettings.ChannelAddr[mixer_channel],
|
||||
buzzerState(BUZZ_INFO) ? actuatorSettings.ChannelMax[mixer_channel] : actuatorSettings.ChannelMin[mixer_channel]);
|
||||
return true;
|
||||
|
||||
case ACTUATORSETTINGS_CHANNELTYPE_PWM:
|
||||
{
|
||||
uint8_t mode = pinsMode[actuatorSettings->ChannelAddr[mixer_channel]];
|
||||
uint8_t mode = pinsMode[actuatorSettings.ChannelAddr[mixer_channel]];
|
||||
switch (mode) {
|
||||
case ACTUATORSETTINGS_BANKMODE_ONESHOT125:
|
||||
// Remap 1000-2000 range to 125-250
|
||||
PIOS_Servo_Set(actuatorSettings->ChannelAddr[mixer_channel], value / ACTUATOR_ONESHOT125_PULSE_SCALE);
|
||||
PIOS_Servo_Set(actuatorSettings.ChannelAddr[mixer_channel], value / ACTUATOR_ONESHOT125_PULSE_SCALE);
|
||||
break;
|
||||
default:
|
||||
PIOS_Servo_Set(actuatorSettings->ChannelAddr[mixer_channel], value);
|
||||
PIOS_Servo_Set(actuatorSettings.ChannelAddr[mixer_channel], value);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
@ -770,12 +838,12 @@ static bool set_channel(uint8_t mixer_channel, uint16_t value, const ActuatorSet
|
||||
/**
|
||||
* @brief Update the servo update rate
|
||||
*/
|
||||
static void actuator_update_rate_if_changed(const ActuatorSettingsData *actuatorSettings, bool force_update)
|
||||
static void actuator_update_rate_if_changed(bool force_update)
|
||||
{
|
||||
static uint16_t prevBankUpdateFreq[ACTUATORSETTINGS_BANKUPDATEFREQ_NUMELEM];
|
||||
static uint8_t prevBankMode[ACTUATORSETTINGS_BANKMODE_NUMELEM];
|
||||
bool updateMode = force_update || (memcmp(prevBankMode, actuatorSettings->BankMode, sizeof(prevBankMode)) != 0);
|
||||
bool updateFreq = force_update || (memcmp(prevBankUpdateFreq, actuatorSettings->BankUpdateFreq, sizeof(prevBankUpdateFreq)) != 0);
|
||||
bool updateMode = force_update || (memcmp(prevBankMode, actuatorSettings.BankMode, sizeof(prevBankMode)) != 0);
|
||||
bool updateFreq = force_update || (memcmp(prevBankUpdateFreq, actuatorSettings.BankUpdateFreq, sizeof(prevBankUpdateFreq)) != 0);
|
||||
|
||||
// check if any setting is changed
|
||||
if (updateMode || updateFreq) {
|
||||
@ -784,15 +852,15 @@ static void actuator_update_rate_if_changed(const ActuatorSettingsData *actuator
|
||||
uint16_t freq[ACTUATORSETTINGS_BANKUPDATEFREQ_NUMELEM];
|
||||
uint32_t clock[ACTUATORSETTINGS_BANKUPDATEFREQ_NUMELEM] = { 0 };
|
||||
for (uint8_t i = 0; i < ACTUATORSETTINGS_BANKMODE_NUMELEM; i++) {
|
||||
if (force_update || (actuatorSettings->BankMode[i] != prevBankMode[i])) {
|
||||
if (force_update || (actuatorSettings.BankMode[i] != prevBankMode[i])) {
|
||||
PIOS_Servo_SetBankMode(i,
|
||||
actuatorSettings->BankMode[i] ==
|
||||
actuatorSettings.BankMode[i] ==
|
||||
ACTUATORSETTINGS_BANKMODE_PWM ?
|
||||
PIOS_SERVO_BANK_MODE_PWM :
|
||||
PIOS_SERVO_BANK_MODE_SINGLE_PULSE
|
||||
);
|
||||
}
|
||||
switch (actuatorSettings->BankMode[i]) {
|
||||
switch (actuatorSettings.BankMode[i]) {
|
||||
case ACTUATORSETTINGS_BANKMODE_ONESHOT125:
|
||||
freq[i] = 100; // Value must be small enough so CCr isn't update until the PIOS_Servo_Update is triggered
|
||||
clock[i] = ACTUATOR_ONESHOT125_CLOCK; // Setup an 2MHz timer clock
|
||||
@ -802,37 +870,73 @@ static void actuator_update_rate_if_changed(const ActuatorSettingsData *actuator
|
||||
clock[i] = ACTUATOR_PWM_CLOCK;
|
||||
break;
|
||||
default: // PWM
|
||||
freq[i] = actuatorSettings->BankUpdateFreq[i];
|
||||
freq[i] = actuatorSettings.BankUpdateFreq[i];
|
||||
clock[i] = ACTUATOR_PWM_CLOCK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(prevBankMode,
|
||||
actuatorSettings->BankMode,
|
||||
actuatorSettings.BankMode,
|
||||
sizeof(prevBankMode));
|
||||
|
||||
PIOS_Servo_SetHz(freq, clock, ACTUATORSETTINGS_BANKUPDATEFREQ_NUMELEM);
|
||||
|
||||
memcpy(prevBankUpdateFreq,
|
||||
actuatorSettings->BankUpdateFreq,
|
||||
actuatorSettings.BankUpdateFreq,
|
||||
sizeof(prevBankUpdateFreq));
|
||||
// retrieve mode from related bank
|
||||
for (uint8_t i = 0; i < MAX_MIX_ACTUATORS; i++) {
|
||||
uint8_t bank = PIOS_Servo_GetPinBank(i);
|
||||
pinsMode[i] = actuatorSettings->BankMode[bank];
|
||||
pinsMode[i] = actuatorSettings.BankMode[bank];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ActuatorSettingsUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
|
||||
{
|
||||
actuator_settings_updated = true;
|
||||
ActuatorSettingsGet(&actuatorSettings);
|
||||
spinWhileArmed = actuatorSettings.MotorsSpinWhileArmed == ACTUATORSETTINGS_MOTORSSPINWHILEARMED_TRUE;
|
||||
if (frameType == FRAME_TYPE_GROUND) {
|
||||
spinWhileArmed = false;
|
||||
}
|
||||
actuator_update_rate_if_changed(false);
|
||||
}
|
||||
|
||||
static void MixerSettingsUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
|
||||
{
|
||||
mixer_settings_updated = true;
|
||||
MixerSettingsGet(&mixerSettings);
|
||||
mixer_settings_count = 0;
|
||||
Mixer_t *mixers = (Mixer_t *)&mixerSettings.Mixer1Type;
|
||||
for (int ct = 0; ct < MAX_MIX_ACTUATORS; ct++) {
|
||||
if (mixers[ct].type != MIXERSETTINGS_MIXER1TYPE_DISABLED) {
|
||||
mixer_settings_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void SettingsUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
|
||||
{
|
||||
uint8_t TreatCustomCraftAs;
|
||||
|
||||
VtolPathFollowerSettingsTreatCustomCraftAsGet(&TreatCustomCraftAs);
|
||||
|
||||
frameType = GetCurrentFrameType();
|
||||
|
||||
if (frameType == FRAME_TYPE_CUSTOM) {
|
||||
switch (TreatCustomCraftAs) {
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_FIXEDWING:
|
||||
frameType = FRAME_TYPE_FIXED_WING;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_VTOL:
|
||||
frameType = FRAME_TYPE_MULTIROTOR;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_GROUND:
|
||||
frameType = FRAME_TYPE_GROUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SystemSettingsThrustControlGet(&thrustType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +62,7 @@ static bool forcedDisArm(void);
|
||||
* @input: ManualControlCommand, AccessoryDesired
|
||||
* @output: FlightStatus.Arming
|
||||
*/
|
||||
void armHandler(bool newinit)
|
||||
void armHandler(bool newinit, FrameType_t frameType)
|
||||
{
|
||||
static ArmState_t armState;
|
||||
|
||||
@ -82,7 +82,12 @@ void armHandler(bool newinit)
|
||||
|
||||
bool lowThrottle = cmd.Throttle < 0;
|
||||
|
||||
bool armSwitch = false;
|
||||
if (frameType == FRAME_TYPE_GROUND) {
|
||||
// Deadbanding appled in receiver.c at 2%
|
||||
lowThrottle = fabsf(cmd.Throttle) < 0.01f;
|
||||
}
|
||||
|
||||
bool armSwitch = false;
|
||||
|
||||
switch (settings.Arming) {
|
||||
case FLIGHTMODESETTINGS_ARMING_ACCESSORY0:
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <openpilot.h>
|
||||
#include <flightstatus.h>
|
||||
#include <sanitycheck.h>
|
||||
|
||||
typedef void (*handlerFunc)(bool newinit);
|
||||
|
||||
@ -45,7 +46,7 @@ typedef struct controlHandlerStruct {
|
||||
* @input: ManualControlCommand, AccessoryDesired
|
||||
* @output: FlightStatus.Arming
|
||||
*/
|
||||
void armHandler(bool newinit);
|
||||
void armHandler(bool newinit, FrameType_t frameType);
|
||||
|
||||
/**
|
||||
* @brief Handler to control Manual flightmode - input directly steers actuators
|
||||
|
@ -104,6 +104,7 @@ static const controlHandler handler_PATHPLANNER = {
|
||||
static float thrustAtBrakeStart = 0.0f;
|
||||
static float thrustLo = 0.0f;
|
||||
static float thrustHi = 0.0f;
|
||||
static FrameType_t frameType = FRAME_TYPE_MULTIROTOR;
|
||||
|
||||
#endif /* ifndef PIOS_EXCLUDE_ADVANCED_FEATURES */
|
||||
// Private variables
|
||||
@ -115,6 +116,7 @@ static void commandUpdatedCb(UAVObjEvent *ev);
|
||||
static void manualControlTask(void);
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
static uint8_t isAssistedFlightMode(uint8_t position, uint8_t flightMode, FlightModeSettingsData *modeSettings);
|
||||
static void SettingsUpdatedCb(UAVObjEvent *ev);
|
||||
#endif
|
||||
|
||||
#define assumptions (assumptions1 && assumptions2 && assumptions3 && assumptions4 && assumptions5 && assumptions6 && assumptions7 && assumptions_flightmode)
|
||||
@ -135,11 +137,14 @@ int32_t ManualControlStart()
|
||||
// clear alarms
|
||||
AlarmsClear(SYSTEMALARMS_ALARM_MANUALCONTROL);
|
||||
|
||||
SettingsUpdatedCb(NULL);
|
||||
|
||||
// Make sure unarmed on power up
|
||||
armHandler(true);
|
||||
armHandler(true, frameType);
|
||||
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
takeOffLocationHandlerInit();
|
||||
|
||||
#endif
|
||||
// Start main task
|
||||
PIOS_CALLBACKSCHEDULER_Dispatch(callbackHandle);
|
||||
@ -164,6 +169,8 @@ int32_t ManualControlInitialize()
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
VtolSelfTuningStatsInitialize();
|
||||
VtolPathFollowerSettingsInitialize();
|
||||
VtolPathFollowerSettingsConnectCallback(&SettingsUpdatedCb);
|
||||
SystemSettingsConnectCallback(&SettingsUpdatedCb);
|
||||
#endif
|
||||
callbackHandle = PIOS_CALLBACKSCHEDULER_Create(&manualControlTask, CALLBACK_PRIORITY, CBTASK_PRIORITY, CALLBACKINFO_RUNNING_MANUALCONTROL, STACK_SIZE_BYTES);
|
||||
|
||||
@ -171,13 +178,36 @@ int32_t ManualControlInitialize()
|
||||
}
|
||||
MODULE_INITCALL(ManualControlInitialize, ManualControlStart);
|
||||
|
||||
static void SettingsUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
|
||||
{
|
||||
uint8_t TreatCustomCraftAs;
|
||||
|
||||
VtolPathFollowerSettingsTreatCustomCraftAsGet(&TreatCustomCraftAs);
|
||||
|
||||
frameType = GetCurrentFrameType();
|
||||
|
||||
if (frameType == FRAME_TYPE_CUSTOM) {
|
||||
switch (TreatCustomCraftAs) {
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_FIXEDWING:
|
||||
frameType = FRAME_TYPE_FIXED_WING;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_VTOL:
|
||||
frameType = FRAME_TYPE_MULTIROTOR;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_GROUND:
|
||||
frameType = FRAME_TYPE_GROUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Module task
|
||||
*/
|
||||
static void manualControlTask(void)
|
||||
{
|
||||
// Process Arming
|
||||
armHandler(false);
|
||||
armHandler(false, frameType);
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
takeOffLocationHandler();
|
||||
#endif
|
||||
|
@ -44,7 +44,9 @@
|
||||
#endif
|
||||
#include <flightmodesettings.h>
|
||||
#include <systemsettings.h>
|
||||
#include <vtolpathfollowersettings.h>
|
||||
#include <taskinfo.h>
|
||||
#include <sanitycheck.h>
|
||||
|
||||
|
||||
#if defined(PIOS_INCLUDE_USB_RCTX)
|
||||
@ -72,6 +74,7 @@
|
||||
// Private variables
|
||||
static xTaskHandle taskHandle;
|
||||
static portTickType lastSysTime;
|
||||
static FrameType_t frameType = FRAME_TYPE_MULTIROTOR;
|
||||
|
||||
#ifdef USE_INPUT_LPF
|
||||
static portTickType lastSysTimeLPF;
|
||||
@ -84,6 +87,7 @@ static float scaleChannel(int16_t value, int16_t max, int16_t min, int16_t neutr
|
||||
static uint32_t timeDifferenceMs(portTickType start_time, portTickType end_time);
|
||||
static bool validInputRange(int16_t min, int16_t max, uint16_t value);
|
||||
static void applyDeadband(float *value, float deadband);
|
||||
static void SettingsUpdatedCb(UAVObjEvent *ev);
|
||||
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
static uint8_t isAssistedFlightMode(uint8_t position);
|
||||
@ -124,6 +128,7 @@ int32_t ReceiverStart()
|
||||
#ifdef PIOS_INCLUDE_WDG
|
||||
PIOS_WDG_RegisterFlag(PIOS_WDG_MANUAL);
|
||||
#endif
|
||||
SettingsUpdatedCb(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -142,12 +147,40 @@ int32_t ReceiverInitialize()
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
StabilizationSettingsInitialize();
|
||||
#endif
|
||||
VtolPathFollowerSettingsInitialize();
|
||||
VtolPathFollowerSettingsConnectCallback(&SettingsUpdatedCb);
|
||||
SystemSettingsInitialize();
|
||||
SystemSettingsConnectCallback(&SettingsUpdatedCb);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
MODULE_INITCALL(ReceiverInitialize, ReceiverStart);
|
||||
|
||||
static void SettingsUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
|
||||
{
|
||||
uint8_t TreatCustomCraftAs;
|
||||
|
||||
VtolPathFollowerSettingsTreatCustomCraftAsGet(&TreatCustomCraftAs);
|
||||
|
||||
frameType = GetCurrentFrameType();
|
||||
|
||||
if (frameType == FRAME_TYPE_CUSTOM) {
|
||||
switch (TreatCustomCraftAs) {
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_FIXEDWING:
|
||||
frameType = FRAME_TYPE_FIXED_WING;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_VTOL:
|
||||
frameType = FRAME_TYPE_MULTIROTOR;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_GROUND:
|
||||
frameType = FRAME_TYPE_GROUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Module task
|
||||
*/
|
||||
@ -243,22 +276,16 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
}
|
||||
}
|
||||
|
||||
// Check settings, if error raise alarm
|
||||
if (settings.ChannelGroups.Roll >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
|
||||
|| settings.ChannelGroups.Pitch >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
|
||||
|| settings.ChannelGroups.Yaw >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
|
||||
// Sanity Check Throttle and Yaw
|
||||
if (settings.ChannelGroups.Yaw >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
|
||||
|| settings.ChannelGroups.Throttle >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
|
||||
||
|
||||
// Check all channel mappings are valid
|
||||
cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] == (uint16_t)PIOS_RCVR_INVALID
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] == (uint16_t)PIOS_RCVR_INVALID
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] == (uint16_t)PIOS_RCVR_INVALID
|
||||
cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] == (uint16_t)PIOS_RCVR_INVALID
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE] == (uint16_t)PIOS_RCVR_INVALID
|
||||
||
|
||||
// Check the driver exists
|
||||
cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] == (uint16_t)PIOS_RCVR_NODRIVER
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] == (uint16_t)PIOS_RCVR_NODRIVER
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] == (uint16_t)PIOS_RCVR_NODRIVER
|
||||
cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] == (uint16_t)PIOS_RCVR_NODRIVER
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE] == (uint16_t)PIOS_RCVR_NODRIVER
|
||||
||
|
||||
// Check collective if required
|
||||
@ -282,15 +309,39 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (frameType != FRAME_TYPE_GROUND) {
|
||||
// Sanity Check Pitch and Roll
|
||||
if (settings.ChannelGroups.Roll >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
|
||||
|| settings.ChannelGroups.Pitch >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
|
||||
||
|
||||
// Check all channel mappings are valid
|
||||
cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] == (uint16_t)PIOS_RCVR_INVALID
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] == (uint16_t)PIOS_RCVR_INVALID
|
||||
||
|
||||
// Check the driver exists
|
||||
cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] == (uint16_t)PIOS_RCVR_NODRIVER
|
||||
|| cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] == (uint16_t)PIOS_RCVR_NODRIVER) {
|
||||
AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER, SYSTEMALARMS_ALARM_CRITICAL);
|
||||
cmd.Connected = MANUALCONTROLCOMMAND_CONNECTED_FALSE;
|
||||
ManualControlCommandSet(&cmd);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// decide if we have valid manual input or not
|
||||
valid_input_detected &= validInputRange(settings.ChannelMin.Throttle,
|
||||
settings.ChannelMax.Throttle, cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE])
|
||||
&& validInputRange(settings.ChannelMin.Roll,
|
||||
settings.ChannelMax.Roll, cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL])
|
||||
&& validInputRange(settings.ChannelMin.Yaw,
|
||||
settings.ChannelMax.Yaw, cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW])
|
||||
&& validInputRange(settings.ChannelMin.Pitch,
|
||||
settings.ChannelMax.Pitch, cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH]);
|
||||
settings.ChannelMax.Yaw, cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW]);
|
||||
|
||||
if (frameType != FRAME_TYPE_GROUND) {
|
||||
valid_input_detected &= validInputRange(settings.ChannelMin.Roll,
|
||||
settings.ChannelMax.Roll, cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL])
|
||||
&& validInputRange(settings.ChannelMin.Pitch,
|
||||
settings.ChannelMax.Pitch, cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH]);
|
||||
}
|
||||
|
||||
if (settings.ChannelGroups.Collective != MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
|
||||
valid_input_detected &= validInputRange(settings.ChannelMin.Collective,
|
||||
@ -321,10 +372,14 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
}
|
||||
|
||||
if (cmd.Connected == MANUALCONTROLCOMMAND_CONNECTED_FALSE) {
|
||||
cmd.Throttle = settings.FailsafeChannel.Throttle;
|
||||
cmd.Roll = settings.FailsafeChannel.Roll;
|
||||
cmd.Pitch = settings.FailsafeChannel.Pitch;
|
||||
cmd.Yaw = settings.FailsafeChannel.Yaw;
|
||||
if (frameType != FRAME_TYPE_GROUND) {
|
||||
cmd.Throttle = settings.FailsafeChannel.Throttle;
|
||||
} else {
|
||||
cmd.Throttle = 0.0f;
|
||||
}
|
||||
cmd.Roll = settings.FailsafeChannel.Roll;
|
||||
cmd.Pitch = settings.FailsafeChannel.Pitch;
|
||||
cmd.Yaw = settings.FailsafeChannel.Yaw;
|
||||
cmd.Collective = settings.FailsafeChannel.Collective;
|
||||
switch (thrustType) {
|
||||
case SYSTEMSETTINGS_THRUSTCONTROL_THROTTLE:
|
||||
@ -401,6 +456,9 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
applyDeadband(&cmd.Roll, deadband_checked);
|
||||
applyDeadband(&cmd.Pitch, deadband_checked);
|
||||
applyDeadband(&cmd.Yaw, deadband_checked);
|
||||
if (frameType == FRAME_TYPE_GROUND) { // assumes reversible motors
|
||||
applyDeadband(&cmd.Throttle, deadband_checked);
|
||||
}
|
||||
}
|
||||
#ifdef USE_INPUT_LPF
|
||||
// Apply Low Pass Filter to input channels, time delta between calls in ms
|
||||
|
Loading…
x
Reference in New Issue
Block a user