Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tail sitter vtol support #9347

Merged
merged 12 commits into from
Dec 12, 2023
3 changes: 3 additions & 0 deletions docs/MixerProfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ TailSitter is supported by add a 90deg offset to the board alignment. Set the bo
- Rate Settings
- ·······

### TailSitter support
TailSitter is supported by add a 90deg offset to the board alignment. Set the board aliment normally in the mixer_profile for FW mode(`set platform_type = AIRPLANE`), The motor trust axis should be same direction as the airplane nose. Then, in the mixer_profile for takeoff and landing `set platform_type = TAILSITTER`. The `TAILSITTER` platform type is same as `MULTIROTOR` platform type, expect for a 90 deg board alignment offset. In `TAILSITTER` mixer_profile, when motor trust/airplane nose is pointing to the sky, 'airplane bottom'/'multi rotor front' should facing forward in model preview. Set the motor/servo mixer according to multirotor orientation, Model should roll around geography's longitudinal axis, the roll axis of `TAILSITTER` will be yaw axis of `AIRPLANE`. In addition, When `MIXER TRANSITION` input is activated, a 45deg offset will be add to the target angle for angle mode.

## Happy flying

Remember that this is currently an emerging capability:
Expand Down
10 changes: 10 additions & 0 deletions docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -5762,6 +5762,16 @@ Delay before disarming when requested by switch (ms) [0-1000]

---

### tailsitter_orientation_offset

Apply a 90 deg pitch offset in sensor aliment for tailsitter flying mode

| Default | Min | Max |
| --- | --- | --- |
| OFF | OFF | ON |

---

### telemetry_halfduplex

S.Port telemetry only: Turn UART into UNIDIR for usage on F1 and F4 target. See Telemetry.md for details
Expand Down
1 change: 1 addition & 0 deletions src/main/fc/runtime_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ typedef enum {
ANTI_WINDUP_DEACTIVATED = (1 << 25),
LANDING_DETECTED = (1 << 26),
IN_FLIGHT_EMERG_REARM = (1 << 27),
TAILSITTER = (1 << 28), //offset the pitch angle by 90 degrees
} stateFlags_t;

#define DISABLE_STATE(mask) (stateFlags &= ~(mask))
Expand Down
5 changes: 5 additions & 0 deletions src/main/fc/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,11 @@ groups:
field: mixer_config.switchTransitionTimer
min: 0
max: 200
- name: tailsitter_orientation_offset
description: "Apply a 90 deg pitch offset in sensor aliment for tailsitter flying mode"
default_value: OFF
field: mixer_config.tailsitterOrientationOffset
type: bool



Expand Down
28 changes: 28 additions & 0 deletions src/main/flight/imu.c
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,33 @@ static void imuCalculateTurnRateacceleration(fpVector3_t *vEstcentrifugalAccelBF
lastspeed = currentspeed;
}

fpQuaternion_t* getTailSitterQuaternion(bool normal2tail){
static bool firstRun = true;
static fpQuaternion_t qNormal2Tail;
static fpQuaternion_t qTail2Normal;
if(firstRun){
fpAxisAngle_t axisAngle;
axisAngle.axis.x = 0;
axisAngle.axis.y = 1;
axisAngle.axis.z = 0;
axisAngle.angle = DEGREES_TO_RADIANS(-90);
axisAngleToQuaternion(&qNormal2Tail, &axisAngle);
quaternionConjugate(&qTail2Normal, &qNormal2Tail);
firstRun = false;
}
return normal2tail ? &qNormal2Tail : &qTail2Normal;
}

void imuUpdateTailSitter(void)
{
static bool lastTailSitter=false;
if (((bool)STATE(TAILSITTER)) != lastTailSitter){
fpQuaternion_t* rotation_for_tailsitter= getTailSitterQuaternion(STATE(TAILSITTER));
quaternionMultiply(&orientation, &orientation, rotation_for_tailsitter);
}
lastTailSitter = STATE(TAILSITTER);
}

static void imuCalculateEstimatedAttitude(float dT)
{
#if defined(USE_MAG)
Expand Down Expand Up @@ -763,6 +790,7 @@ static void imuCalculateEstimatedAttitude(float dT)
useCOG, courseOverGround,
accWeight,
magWeight);
imuUpdateTailSitter();
imuUpdateEulerAngles();
}

Expand Down
7 changes: 7 additions & 0 deletions src/main/flight/mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ void mixerUpdateStateFlags(void)
DISABLE_STATE(BOAT);
DISABLE_STATE(AIRPLANE);
DISABLE_STATE(MOVE_FORWARD_ONLY);
DISABLE_STATE(TAILSITTER);

if (currentMixerConfig.platformType == PLATFORM_AIRPLANE) {
ENABLE_STATE(FIXED_WING_LEGACY);
Expand All @@ -186,6 +187,12 @@ void mixerUpdateStateFlags(void)
ENABLE_STATE(ALTITUDE_CONTROL);
}

if (currentMixerConfig.tailsitterOrientationOffset) {
ENABLE_STATE(TAILSITTER);
} else {
DISABLE_STATE(TAILSITTER);
}

if (currentMixerConfig.hasFlaps) {
ENABLE_STATE(FLAPERON_AVAILABLE);
} else {
Expand Down
1 change: 1 addition & 0 deletions src/main/flight/mixer_profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ void pgResetFn_mixerProfiles(mixerProfile_t *instance)
.PIDProfileLinking = SETTING_MIXER_PID_PROFILE_LINKING_DEFAULT,
.automated_switch = SETTING_MIXER_AUTOMATED_SWITCH_DEFAULT,
.switchTransitionTimer = SETTING_MIXER_SWITCH_TRANS_TIMER_DEFAULT,
.tailsitterOrientationOffset = SETTING_TAILSITTER_ORIENTATION_OFFSET_DEFAULT,
});
for (int j = 0; j < MAX_SUPPORTED_MOTORS; j++)
{
Expand Down
1 change: 1 addition & 0 deletions src/main/flight/mixer_profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ typedef struct mixerConfig_s {
bool PIDProfileLinking;
bool automated_switch;
int16_t switchTransitionTimer;
bool tailsitterOrientationOffset;
} mixerConfig_t;
typedef struct mixerProfile_s {
mixerConfig_t mixer_config;
Expand Down
10 changes: 9 additions & 1 deletion src/main/flight/pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,11 @@ int16_t angleFreefloatDeadband(int16_t deadband, flight_dynamics_index_t axis)

static float computePidLevelTarget(flight_dynamics_index_t axis) {
// This is ROLL/PITCH, run ANGLE/HORIZON controllers
#ifdef USE_PROGRAMMING_FRAMEWORK
float angleTarget = pidRcCommandToAngle(getRcCommandOverride(rcCommand, axis), pidProfile()->max_angle_inclination[axis]);
#else
float angleTarget = pidRcCommandToAngle(rcCommand[axis], pidProfile()->max_angle_inclination[axis]);
#endif

// Automatically pitch down if the throttle is manually controlled and reduced bellow cruise throttle
if ((axis == FD_PITCH) && STATE(AIRPLANE) && FLIGHT_MODE(ANGLE_MODE) && !navigationIsControllingThrottle()) {
Expand Down Expand Up @@ -1087,7 +1091,6 @@ void FAST_CODE pidController(float dT)
}

for (int axis = 0; axis < 3; axis++) {
// Step 1: Calculate gyro rates
pidState[axis].gyroRate = gyro.gyroADCf[axis];

// Step 2: Read target
Expand Down Expand Up @@ -1122,6 +1125,11 @@ void FAST_CODE pidController(float dT)
if (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE) || isFlightAxisAngleOverrideActive(axis)) {
//If axis angle override, get the correct angle from Logic Conditions
float angleTarget = getFlightAxisAngleOverride(axis, computePidLevelTarget(axis));

//apply 45 deg offset for tailsitter when isMixerTransitionMixing is activated
if (STATE(TAILSITTER) && isMixerTransitionMixing && axis == FD_PITCH){
angleTarget += DEGREES_TO_DECIDEGREES(45);
}

//Apply the Level PID controller
pidLevel(angleTarget, &pidState[axis], axis, horizonRateMagnitude, dT);
Expand Down
40 changes: 25 additions & 15 deletions src/main/sensors/boardalignment.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "config/parameter_group.h"
#include "config/parameter_group_ids.h"
#include "fc/runtime_config.h"

#include "drivers/sensor.h"

Expand All @@ -45,6 +46,7 @@

static bool standardBoardAlignment = true; // board orientation correction
static fpMat3_t boardRotMatrix;
static fpMat3_t tailRotMatrix;

// no template required since defaults are zero
PG_REGISTER(boardAlignment_t, boardAlignment, PG_BOARD_ALIGNMENT, 0);
Expand All @@ -56,19 +58,19 @@ static bool isBoardAlignmentStandard(const boardAlignment_t *boardAlignment)

void initBoardAlignment(void)
{
if (isBoardAlignmentStandard(boardAlignment())) {
standardBoardAlignment = true;
} else {
fp_angles_t rotationAngles;

standardBoardAlignment = false;

rotationAngles.angles.roll = DECIDEGREES_TO_RADIANS(boardAlignment()->rollDeciDegrees );
rotationAngles.angles.pitch = DECIDEGREES_TO_RADIANS(boardAlignment()->pitchDeciDegrees);
rotationAngles.angles.yaw = DECIDEGREES_TO_RADIANS(boardAlignment()->yawDeciDegrees );

rotationMatrixFromAngles(&boardRotMatrix, &rotationAngles);
}
standardBoardAlignment=isBoardAlignmentStandard(boardAlignment());
fp_angles_t rotationAngles;

rotationAngles.angles.roll = DECIDEGREES_TO_RADIANS(boardAlignment()->rollDeciDegrees );
rotationAngles.angles.pitch = DECIDEGREES_TO_RADIANS(boardAlignment()->pitchDeciDegrees);
rotationAngles.angles.yaw = DECIDEGREES_TO_RADIANS(boardAlignment()->yawDeciDegrees );

rotationMatrixFromAngles(&boardRotMatrix, &rotationAngles);
fp_angles_t tailSitter_rotationAngles;
tailSitter_rotationAngles.angles.roll = DECIDEGREES_TO_RADIANS(0);
tailSitter_rotationAngles.angles.pitch = DECIDEGREES_TO_RADIANS(900);
tailSitter_rotationAngles.angles.yaw = DECIDEGREES_TO_RADIANS(0);
rotationMatrixFromAngles(&tailRotMatrix, &tailSitter_rotationAngles);
}

void updateBoardAlignment(int16_t roll, int16_t pitch)
Expand All @@ -85,15 +87,23 @@ void updateBoardAlignment(int16_t roll, int16_t pitch)
initBoardAlignment();
}

void applyTailSitterAlignment(fpVector3_t *fpVec)
{
if (!STATE(TAILSITTER)) {
return;
}
rotationMatrixRotateVector(fpVec, fpVec, &tailRotMatrix);
}

void applyBoardAlignment(float *vec)
{
if (standardBoardAlignment) {
if (standardBoardAlignment && (!STATE(TAILSITTER))) {
return;
}

fpVector3_t fpVec = { .v = { vec[X], vec[Y], vec[Z] } };
rotationMatrixRotateVector(&fpVec, &fpVec, &boardRotMatrix);

applyTailSitterAlignment(&fpVec);
vec[X] = lrintf(fpVec.x);
vec[Y] = lrintf(fpVec.y);
vec[Z] = lrintf(fpVec.z);
Expand Down
4 changes: 3 additions & 1 deletion src/main/sensors/boardalignment.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#pragma once

#include "config/parameter_group.h"
#include "common/vector.h"

typedef struct boardAlignment_s {
int16_t rollDeciDegrees;
Expand All @@ -30,4 +31,5 @@ PG_DECLARE(boardAlignment_t, boardAlignment);
void initBoardAlignment(void);
void updateBoardAlignment(int16_t roll, int16_t pitch);
void applySensorAlignment(float * dest, float * src, uint8_t rotation);
void applyBoardAlignment(float *vec);
void applyBoardAlignment(float *vec);
void applyTailSitterAlignment(fpVector3_t *vec);
2 changes: 1 addition & 1 deletion src/main/sensors/compass.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ void compassUpdate(timeUs_t currentTimeUs)
fpVector3_t rotated;

rotationMatrixRotateVector(&rotated, &v, &mag.dev.magAlign.externalRotation);

applyTailSitterAlignment(&rotated);
mag.magADC[X] = rotated.x;
mag.magADC[Y] = rotated.y;
mag.magADC[Z] = rotated.z;
Expand Down