Skip to content

Conversation

shota3527
Copy link
Contributor

@shota3527 shota3527 commented Jul 27, 2025

User description

A requested feature by many tilting rotor VTOL build. The servo speed limit will not work properly during the mixer profile switch.
It was because the mixer profile switch will reload and reset all servo rules/speed limit filters. And still, there was no straightforward implementation to properly handle the servo speed limit.

This PR addresses the issue by adding new internal servo mixing rules during the mixer profile switch to carry over servoSpeedLimitFilter status and process it after the mixer profile switch

Bench test ok


PR Type

Enhancement


Description

This description is generated by an AI tool. It may have inaccuracies

  • Add servo speed filter carry-over during mixer profile switches

  • Implement helper structure to preserve filter states

  • Prevent servo speed limit reset on profile changes

  • Support tilting rotor VTOL builds with smooth transitions


Diagram Walkthrough

flowchart LR
  A["Current Servo Rules"] --> B["Extract Speed Filters"]
  B --> C["Store in Helper Array"]
  C --> D["Reset Servo Mixer"]
  D --> E["Load New Profile"]
  E --> F["Add Helper Rules"]
  F --> G["Restore Filter States"]
Loading

File Walkthrough

Relevant files
Enhancement
servos.c
Implement servo speed filter carry-over mechanism               

src/main/flight/servos.c

  • Add servo speed filter preservation logic during mixer profile
    switches
  • Implement servoMixerSwitchHelper array to store filter states
  • Create helper servo rules with INPUT_MIXER_SWITCH_HELPER input source
  • Restore speed limit filter states after profile reload
+34/-1   
servos.h
Add helper structures for servo filter preservation           

src/main/flight/servos.h

  • Add INPUT_MIXER_SWITCH_HELPER enum value for helper servo rules
  • Define servoMixerSwitch_t structure to track filter states
  • Include fields for target channel, rate, speed, and filter state
+9/-0     

Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
⚡ Recommended focus areas for review

Buffer Overflow

The code uses a fixed-size helper array with maxMoveFilters = MAX_SERVO_RULES/2 but doesn't validate that movefilterCount stays within bounds when accessing servoMixerSwitchHelper. The break condition only prevents adding more filters but doesn't protect against array access violations in the restoration loop.

int maxMoveFilters = MAX_SERVO_RULES/2;
int movefilterCount = 0;
servoMixerSwitch_t servoMixerSwitchHelper[maxMoveFilters]; // helper to keep track of servoSpeedLimitFilter of servo rules
memset(servoMixerSwitchHelper, 0, sizeof(servoMixerSwitchHelper));
for (int i = 0; i < servoRuleCount; i++) {
    if(currentServoMixer[i].inputSource == INPUT_MIXER_SWITCH_HELPER || movefilterCount >= maxMoveFilters) {
        break;
    }
    if(currentServoMixer[i].speed != 0 && servoSpeedLimitFilter[i].state !=0) {
        servoMixerSwitchHelper[movefilterCount].targetChannel = currentServoMixer[i].targetChannel;
        servoMixerSwitchHelper[movefilterCount].speed = currentServoMixer[i].speed;
        servoMixerSwitchHelper[movefilterCount].rate = currentServoMixer[i].rate;
        servoMixerSwitchHelper[movefilterCount].speedLimitFilterState = servoSpeedLimitFilter[i].state;
        movefilterCount++;
    }
}
Logic Error

The condition servoSpeedLimitFilter[i].state !=0 uses inequality comparison with a float value, which can be unreliable due to floating-point precision. This could cause filters with very small non-zero states to be incorrectly skipped during preservation.

if(currentServoMixer[i].speed != 0 && servoSpeedLimitFilter[i].state !=0) {
    servoMixerSwitchHelper[movefilterCount].targetChannel = currentServoMixer[i].targetChannel;
Resource Leak

Helper servo rules with INPUT_MIXER_SWITCH_HELPER are added to preserve filter states but there's no mechanism to clean them up or prevent them from accumulating across multiple profile switches, potentially consuming all available servo rule slots over time.

// add servo rules to handle the rate limit filter
for (int i = 0; i < movefilterCount; i++) {
    if (servoRuleCount >= MAX_SERVO_RULES) {
        break; // prevent overflow
    }
    currentServoMixer[servoRuleCount].targetChannel = servoMixerSwitchHelper[i].targetChannel;
    currentServoMixer[servoRuleCount].speed = servoMixerSwitchHelper[i].speed;
    currentServoMixer[servoRuleCount].rate = servoMixerSwitchHelper[i].rate;
    currentServoMixer[servoRuleCount].inputSource = INPUT_MIXER_SWITCH_HELPER; // no input
    servoSpeedLimitFilter[servoRuleCount].state = servoMixerSwitchHelper[i].speedLimitFilterState;
    servoRuleCount++;
}

Copy link

qodo-merge-pro bot commented Jul 27, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Use static allocation for array
Suggestion Impact:The suggestion was implemented - the array servoMixerSwitchHelper was changed from stack allocation to static allocation, though with a different constant name (MAX_SERVO_RULES_SWITCH_CARRY instead of MAX_SERVO_RULES/2)

code diff:

+    static servoMixerSwitch_t servoMixerSwitchHelper[MAX_SERVO_RULES_SWITCH_CARRY]; // helper to keep track of servoSpeedLimitFilter of servo rules

The array servoMixerSwitchHelper is allocated on the stack with a size that
could be large (MAX_SERVO_RULES/2). This could cause stack overflow in embedded
systems with limited stack space. Consider using static allocation or dynamic
allocation instead.

src/main/flight/servos.c [211-227]

 //move the rate filter to new servo rules
 int maxMoveFilters = MAX_SERVO_RULES/2;
 int movefilterCount = 0;
-servoMixerSwitch_t servoMixerSwitchHelper[maxMoveFilters]; // helper to keep track of servoSpeedLimitFilter of servo rules
+static servoMixerSwitch_t servoMixerSwitchHelper[MAX_SERVO_RULES/2]; // helper to keep track of servoSpeedLimitFilter of servo rules
 memset(servoMixerSwitchHelper, 0, sizeof(servoMixerSwitchHelper));
 for (int i = 0; i < servoRuleCount; i++) {
     if(currentServoMixer[i].inputSource == INPUT_MIXER_SWITCH_HELPER || movefilterCount >= maxMoveFilters) {
         break;
     }
     if(currentServoMixer[i].speed != 0 && servoSpeedLimitFilter[i].state !=0) {
         servoMixerSwitchHelper[movefilterCount].targetChannel = currentServoMixer[i].targetChannel;
         servoMixerSwitchHelper[movefilterCount].speed = currentServoMixer[i].speed;
         servoMixerSwitchHelper[movefilterCount].rate = currentServoMixer[i].rate;
         servoMixerSwitchHelper[movefilterCount].speedLimitFilterState = servoSpeedLimitFilter[i].state;
         movefilterCount++;
     }
 }

[Suggestion processed]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a potential stack overflow risk by allocating a large array on the stack in an embedded environment and proposes using static allocation, which is a critical fix for system stability.

High
  • Update

@shota3527
Copy link
Contributor Author

#10361

@shota3527 shota3527 changed the base branch from master to maintenance-8.x.x July 27, 2025 07:25
@Jetrell
Copy link

Jetrell commented Jul 28, 2025

@tohclin @SKelectronics @Pairan Any chance of testing these changes ?
The 9.0 development build for it can be found here.
A 9.0 Configurator build can be found here.

@Pairan
Copy link

Pairan commented Jul 28, 2025

... sure ... I will flash it to my T1 VTOL and see what to repair after that :DDD #holdmybeer

Guess I'll need a hand on how to get the hex and configurator #helpneeded

@shota3527
Copy link
Contributor Author

Thanks for testing
For testing, enable servo speed(set a value other than 0) for the tilting servo. Switch between multi rotor and airplane modes and see if the tilting servo is moving as intended. it hould be like this
https://github.com/user-attachments/assets/1d5217f8-bdc7-4b2a-a836-612db9d31054

Other than this, please check all other behaviors is the same before this commit.

@Pairan
Copy link

Pairan commented Jul 28, 2025

got the files at hand ... will play with it and report back in!

@Pairan
Copy link

Pairan commented Jul 28, 2025

Configurator wont let me in

2025-07-28 @ 17:21:31 -- Flight controller info, identifier: INAV, version: 9.0.0

2025-07-28 @ 17:21:31 -- This firmware version is not supported. This version of Configurator supports firmware from 8.0.0 to 9.0.0 (excluded)

I'm afraid I didn't get the Configurator V9 ... did I get the wrong link?

@Jetrell
Copy link

Jetrell commented Jul 28, 2025

@Pairan Sorry about that. Give is one a go. It was before the maintenance changes.

@Pairan
Copy link

Pairan commented Jul 29, 2025

Sure ... will do- yet I may need to check out what went wrong this time. could be that DIFF /ALL doesn't save the entire CLI that differs. So ... I'll dig into the config and then test it

@Pairan
Copy link

Pairan commented Jul 31, 2025

@shota3527 ... somehow my config behaves as before ... but not at yours. No servo speed on the way back from fixed wing to tricopter.

TRICOPTER / mixer 2:
image

Fixed wing / mixer 1:
image

image
# mixer_profile
mixer_profile 1

set platform_type = AIRPLANE
set has_flaps = ON
set model_preview_type = 26
set mixer_pid_profile_linking = ON

# Mixer: motor mixer

mmix reset

mmix 0  1.000  0.000  0.000  0.000
mmix 1  1.000  0.000  0.000  0.000

# Mixer: servo mixer
smix reset

smix 0 1 1 100 0 -1
smix 1 2 0 100 0 -1
smix 2 3 0 100 0 -1
smix 3 4 29 -120 25 -1
smix 4 5 29 120 25 -1
smix 5 6 18 100 40 -1


# mixer_profile
mixer_profile 2

set platform_type = TRICOPTER
set has_flaps = ON
set model_preview_type = 1
set mixer_pid_profile_linking = ON

# Mixer: motor mixer

mmix reset

mmix 0  1.000  1.000 -0.667  0.400
mmix 1  1.000 -1.000 -0.667 -0.400
mmix 2  1.000  0.000  1.330  0.000

# Mixer: servo mixer
smix reset

smix 0 1 1 100 0 -1
smix 1 2 0 100 0 -1
smix 2 3 0 100 0 -1
smix 3 4 29 100 25 -1
smix 4 5 29 -100 25 -1
smix 5 4 38 -85 25 -1
smix 6 5 38 85 25 -1
smix 7 6 18 100 40 -1

Did I miss something ?

@MartinHugh
Copy link

MartinHugh commented Jul 31, 2025

I have set the speed to 10us/s for both servos, in both mixers, including Mixer Transition.

FW to Transition is smooth but starts with a sudden overshoot before going back the other way
Transition to MR is smooth

MR to Transition is smooth
Transition to FW is smooth but starts with a sudden overshoot before going back the other way

In other words, with the settings I have there is what appears to be some problem switching between the two mixers

image

using commit
commit 49201d8 (HEAD -> sh_vtol_smooth801, origin/sh_vtol_smooth801)

@shota3527
Copy link
Contributor Author

@Pairan @MartinHugh
sorry, there was a bug in that commit.
fixed in the latest commit

@Pairan
Copy link

Pairan commented Aug 2, 2025

Is this now a INAV 8.x.x version? Latest Build isn't INAV 9 somehow

Tested it on the bench: IT WORKS as INAV 8.x.x!

Observation:

When switching from MR directly to FW everything goes very smoothly as desired.
https://youtu.be/IRaWgDg5JHA

BUT when you switch back FW to TRANSITION while in TRANSITION it has the old behaviour and jumps
https://youtu.be/3QGRx-S2NqM

I will test this live if the weather here is giving me a chance and report back in.

@MartinHugh
Copy link

MartinHugh commented Aug 2, 2025

commit:
commit 41c78d1 (HEAD -> sh_vtol_smooth801, origin/sh_vtol_smooth801)

now works for me (using a speed value of 10) on the bench.

Note that as @Pairan comments, this branch produces v8.0.1 and not v9.0.0, so the 8.0.1 configurator was needed to configure it.

@Pairan
Copy link

Pairan commented Aug 3, 2025

Fun fact: I crashed my T1 VTOL yesterday :) Yet it was not because of the patch I guess. When switching from TRANSITION to Fixed wing the T1 went UP 90° and while getting grip of the situation (LOS) the ground came to fast :)

I assume the servo speed needs to be rather fast that slow to make it a better experience.

@shota3527
Copy link
Contributor Author

Sorr to hear the crash. I suggest do not use servo speed unless there is a hard ware limit on servo speed.

@tohclin
Copy link

tohclin commented Sep 13, 2025

Hi @shota3527 , I am simply an INAV user and do not know how to compile the code. Could you provide the target hex files? Many thanks.

@tohclin
Copy link

tohclin commented Sep 15, 2025

Hi @shota3527 , I managed to build the hex file. It works. Thank you for making the patch.

@tohclin
Copy link

tohclin commented Sep 21, 2025

Ifound that from transition mode to control profile 1 the mixer profile switch form mixer profile 2 to 1, which is fine, but from control profile 1 back to transition the mixer profile switch from 1 to 2, which is questionable. I would think the mixer profile stays at 1 is better. control 1(mixer1) ----mixer1 --- transition ----mixer2---- control 2 (mixer2)

@tohclin
Copy link

tohclin commented Sep 21, 2025

I do have a hardware limit on servo speed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants