Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/MixerProfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,28 @@ This feature is mainly for RTH in a failsafe event. When set properly, model wil
Set `mixer_automated_switch` to `ON` in mixer_profile for MC mode. Set `mixer_switch_trans_timer` in mixer_profile for MC mode for the time required to gain airspeed for your model before entering to FW mode.
When `mixer_automated_switch`:`OFF` is set for all mixer_profiles(defaults). Model will not perform automated transition at all.

### Mission-authorized VTOL transition (waypoint User Action)

INAV supports mission-requested VTOL transitions through the existing automated transition path. This is configured with:

- `nav_vtol_mission_transition_user_action` (`OFF`, `USER1`, `USER2`, `USER3`, `USER4`)
- `nav_vtol_mission_transition_min_altitude_cm` (optional, `0` disables minimum-altitude check)
- `mixer_switch_trans_airspeed_cm_s` (optional, airspeed-based MC->FW switch threshold)

On each navigable mission waypoint (`WAYPOINT`, `POSHOLD_TIME`, `LAND`), the configured USER action bit is used as absolute target selector:

- selected USER bit = `0` -> transition to MC / MULTIROTOR profile
- selected USER bit = `1` -> transition to FW / AIRPLANE profile
- This is **not** a toggle command.
- If already in the requested profile type, the action is treated as complete (idempotent).

The mission pauses while transition is in progress and resumes after completion.

For MC -> FW mission transitions, navigation uses a straight acceleration segment (no loiter) to build speed before hot-switch.
When `mixer_switch_trans_airspeed_cm_s > 0` and valid pitot airspeed is available, automated MC->FW switching uses this airspeed target. If airspeed is unavailable, transition falls back to `mixer_switch_trans_timer`.

Manual RC switching (`MIXER PROFILE 2`, `MIXER TRANSITION`) remains blocked during normal active navigation. Mission VTOL transition does not bypass the hot-switch safety guard; it only authorizes switching inside the automated transition state.

## TailSitter (planned for INAV 7.1)
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 `tailsitter_orientation_offset = ON ` to apply orientation offset. orientation offset will also add a 45deg orientation offset.

Expand Down
31 changes: 31 additions & 0 deletions docs/Navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,37 @@ Parameters:

* `<flag>` - Last waypoint must have `flag` set to 165 (0xA5).

### Mission VTOL transition using existing User Actions

Mission VTOL transition can be requested.

Configuration:

- `nav_vtol_mission_transition_user_action` selects which waypoint User Action (`USER1..USER4`) is used as the mission VTOL target selector.
- `nav_vtol_mission_transition_min_altitude_cm` optionally enforces a minimum altitude before transition start (`0` disables check).
- `nav_vtol_mission_transition_track_distance_cm` configures straight-line MC->FW transition guidance distance.
- `mixer_switch_trans_airspeed_cm_s` configures MC->FW airspeed threshold for automated profile switching.

Behavior on each navigable mission waypoint (`WAYPOINT`, `POSHOLD_TIME`, `LAND`):

- The configured USER bit is an **absolute target selector**:
- `0`: transition to MC / MULTIROTOR profile
- `1`: transition to FW / AIRPLANE profile
- This command is **not** a toggle.
- The command is idempotent: if already in the requested target profile type, the mission continues immediately.
- If a transition is needed, mission progression pauses while automated transition runs, then resumes only after completion.

Transition behavior in this MVP:

- MC -> FW: straight-line acceleration segment (no loiter), heading from the next waypoint bearing when available, otherwise current heading.
- MC -> FW switch point: if valid pitot airspeed is available and `mixer_switch_trans_airspeed_cm_s > 0`, switch to FW occurs at/above the configured airspeed. If airspeed is unavailable, timer-based fallback (`mixer_switch_trans_timer`) is used.
- FW -> MC: mission pauses during automated transition, then resumes after switching back to MC profile.
- Strict altitude hold is not enforced during MC -> FW transition; natural climb is allowed.

Safety and scope:

- This path uses authorized automated transition state handling; it does not permit manual mixer profile switching during normal waypoint navigation.

`wp save` - Checks list of waypoints and save from FC to EEPROM (warning: it also saves all unsaved CLI settings like normal `save`).

`wp reset` - Resets the list, sets the number of waypoints to 0 and marks the list as invalid (but doesn't delete the waypoint definitions).
Expand Down
26 changes: 26 additions & 0 deletions src/main/fc/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ tables:
- name: nav_wp_mission_restart
enum: navMissionRestart_e
values: ["START", "RESUME", "SWITCH"]
- name: nav_wp_user_action
enum: navMissionUserAction_e
values: ["OFF", "USER1", "USER2", "USER3", "USER4"]
- name: djiRssiSource
values: ["RSSI", "CRSF_LQ"]
enum: djiRssiSource_e
Expand Down Expand Up @@ -1277,6 +1280,12 @@ groups:
field: mixer_config.switchTransitionTimer
min: 0
max: 200
- name: mixer_switch_trans_airspeed_cm_s
description: "Airspeed threshold [cm/s] for MC->FW automated profile switch. If > 0 and valid pitot airspeed is available, transition will switch to FW only after this speed is reached. If airspeed is unavailable, timer-based fallback (`mixer_switch_trans_timer`) is used."
default_value: 0
field: mixer_config.switchTransitionAirspeed
min: 0
max: 10000
- name: tailsitter_orientation_offset
description: "Apply a 90 deg pitch offset in sensor aliment for tailsitter flying mode"
default_value: OFF
Expand Down Expand Up @@ -2623,6 +2632,23 @@ groups:
default_value: "RESUME"
field: general.flags.waypoint_mission_restart
table: nav_wp_mission_restart
- name: nav_vtol_mission_transition_user_action
description: "Selects which waypoint USER action bit (`USER1`..`USER4`) is used as mission VTOL target selector. OFF disables this feature. On navigable mission waypoints: selected USER bit = 1 requests FW profile, selected USER bit = 0 requests MC profile."
default_value: "OFF"
field: general.vtol_mission_transition_user_action
table: nav_wp_user_action
- name: nav_vtol_mission_transition_min_altitude_cm
description: "Minimum altitude [cm] required to start a mission-authorized VTOL transition. Set to 0 to disable the minimum-altitude check."
default_value: 0
field: general.vtol_mission_transition_min_altitude
min: 0
max: 50000
- name: nav_vtol_mission_transition_track_distance_cm
description: "Straight-line target distance [cm] used during mission-authorized MC->FW transition guidance. This controls how far ahead the transition heading target is placed."
default_value: 100000
field: general.vtol_mission_transition_track_distance
min: 1000
max: 500000
- name: nav_wp_multi_mission_index
description: "Index of active mission selected from multi mission WP entry loaded in flight controller. Limited to a maximum of 9 missions."
default_value: 1
Expand Down
62 changes: 50 additions & 12 deletions src/main/flight/mixer_profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "flight/failsafe.h"
#include "navigation/navigation.h"
#include "navigation/navigation_private.h"
#include "sensors/pitotmeter.h"

#include "fc/fc_core.h"
#include "fc/config.h"
Expand All @@ -37,7 +38,7 @@ bool isMixerTransitionMixing_requested;
mixerProfileAT_t mixerProfileAT;
int nextMixerProfileIndex;

PG_REGISTER_ARRAY_WITH_RESET_FN(mixerProfile_t, MAX_MIXER_PROFILE_COUNT, mixerProfiles, PG_MIXER_PROFILE, 1);
PG_REGISTER_ARRAY_WITH_RESET_FN(mixerProfile_t, MAX_MIXER_PROFILE_COUNT, mixerProfiles, PG_MIXER_PROFILE, 2);

void pgResetFn_mixerProfiles(mixerProfile_t *instance)
{
Expand All @@ -53,6 +54,7 @@ void pgResetFn_mixerProfiles(mixerProfile_t *instance)
.controlProfileLinking = SETTING_MIXER_CONTROL_PROFILE_LINKING_DEFAULT,
.automated_switch = SETTING_MIXER_AUTOMATED_SWITCH_DEFAULT,
.switchTransitionTimer = SETTING_MIXER_SWITCH_TRANS_TIMER_DEFAULT,
.switchTransitionAirspeed = SETTING_MIXER_SWITCH_TRANS_AIRSPEED_CM_S_DEFAULT,
.tailsitterOrientationOffset = SETTING_TAILSITTER_ORIENTATION_OFFSET_DEFAULT,
.transition_PID_mmix_multiplier_roll = SETTING_TRANSITION_PID_MMIX_MULTIPLIER_ROLL_DEFAULT,
.transition_PID_mmix_multiplier_pitch = SETTING_TRANSITION_PID_MMIX_MULTIPLIER_PITCH_DEFAULT,
Expand Down Expand Up @@ -112,6 +114,25 @@ void setMixerProfileAT(void)
mixerProfileAT.transitionTransEndTime = mixerProfileAT.transitionStartTime + (timeMs_t)currentMixerConfig.switchTransitionTimer * 100;
}

static bool requestTransitionsToFixedWing(const mixerProfileATRequest_e required_action)
{
return required_action == MIXERAT_REQUEST_RTH || required_action == MIXERAT_REQUEST_MISSION_TO_FW;
}

static bool mixerATReadyForHotSwitch(const mixerProfileATRequest_e required_action)
{
#ifdef USE_PITOT
if (requestTransitionsToFixedWing(required_action) &&
currentMixerConfig.switchTransitionAirspeed > 0 &&
pitotValidForAirspeed()) {
return getAirspeedEstimate() >= currentMixerConfig.switchTransitionAirspeed;
}
#endif

// Timer remains a fallback when airspeed is not configured/available.
return millis() > mixerProfileAT.transitionTransEndTime;
}

bool platformTypeConfigured(flyingPlatformType_e platformType)
{
if (!isModeActivationConditionPresent(BOXMIXERPROFILE)){
Expand All @@ -120,6 +141,18 @@ bool platformTypeConfigured(flyingPlatformType_e platformType)
return mixerConfigByIndex(nextMixerProfileIndex)->platformType == platformType;
}

static bool missionTransitionToMultirotorTypeConfigured(void)
{
if (!isModeActivationConditionPresent(BOXMIXERPROFILE)) {
return false;
}

const flyingPlatformType_e nextPlatformType = mixerConfigByIndex(nextMixerProfileIndex)->platformType;
return nextPlatformType == PLATFORM_MULTIROTOR ||
nextPlatformType == PLATFORM_TRICOPTER ||
nextPlatformType == PLATFORM_HELICOPTER;
}

bool checkMixerATRequired(mixerProfileATRequest_e required_action)
{
//return false if mixerAT condition is not required or setting is not valid
Expand All @@ -132,17 +165,22 @@ bool checkMixerATRequired(mixerProfileATRequest_e required_action)
return false;
}

if(currentMixerConfig.automated_switch){
if ((required_action == MIXERAT_REQUEST_RTH) && STATE(MULTIROTOR))
{
return true;
}
if ((required_action == MIXERAT_REQUEST_LAND) && STATE(AIRPLANE))
{
return true;
}
switch (required_action) {
case MIXERAT_REQUEST_RTH:
return currentMixerConfig.automated_switch && STATE(MULTIROTOR) && platformTypeConfigured(PLATFORM_AIRPLANE);

case MIXERAT_REQUEST_LAND:
return currentMixerConfig.automated_switch && STATE(AIRPLANE) && missionTransitionToMultirotorTypeConfigured();

case MIXERAT_REQUEST_MISSION_TO_FW:
return STATE(MULTIROTOR) && platformTypeConfigured(PLATFORM_AIRPLANE);

case MIXERAT_REQUEST_MISSION_TO_MC:
return STATE(AIRPLANE) && missionTransitionToMultirotorTypeConfigured();

default:
return false;
}
return false;
}

bool mixerATUpdateState(mixerProfileATRequest_e required_action)
Expand Down Expand Up @@ -173,7 +211,7 @@ bool mixerATUpdateState(mixerProfileATRequest_e required_action)
break;
case MIXERAT_PHASE_TRANSITIONING:
isMixerTransitionMixing_requested = true;
if (millis() > mixerProfileAT.transitionTransEndTime){
if (mixerATReadyForHotSwitch(required_action)){
isMixerTransitionMixing_requested = false;
outputProfileHotSwitch(nextMixerProfileIndex);
mixerProfileAT.phase = MIXERAT_PHASE_IDLE;
Expand Down
5 changes: 4 additions & 1 deletion 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 controlProfileLinking;
bool automated_switch;
int16_t switchTransitionTimer;
uint16_t switchTransitionAirspeed;
bool tailsitterOrientationOffset;
int16_t transition_PID_mmix_multiplier_roll;
int16_t transition_PID_mmix_multiplier_pitch;
Expand All @@ -34,6 +35,8 @@ typedef enum {
MIXERAT_REQUEST_NONE, //no request, stats checking only
MIXERAT_REQUEST_RTH,
MIXERAT_REQUEST_LAND,
MIXERAT_REQUEST_MISSION_TO_FW,
MIXERAT_REQUEST_MISSION_TO_MC,
MIXERAT_REQUEST_ABORT,
} mixerProfileATRequest_e;

Expand Down Expand Up @@ -81,4 +84,4 @@ bool outputProfileHotSwitch(int profile_index);
bool checkMixerProfileHotSwitchAvalibility(void);
void activateMixerConfig(void);
void mixerConfigInit(void);
void outputProfileUpdateTask(timeUs_t currentTimeUs);
void outputProfileUpdateTask(timeUs_t currentTimeUs);
Loading