mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-13 11:29:58 +03:00
Merge 429010cd23
into 3f62795f38
This commit is contained in:
commit
9b2beabc9b
22 changed files with 976 additions and 173 deletions
44
SPEC README.txt
Normal file
44
SPEC README.txt
Normal file
|
@ -0,0 +1,44 @@
|
|||
Disclaimer:
|
||||
Street League Spec, its partners, and the authors of this software are not responsible for injury or damage to property resulting from the use of this software. Use at your own risk
|
||||
|
||||
What is the RPM limiter?
|
||||
The RPM limiter seeks to better equalize the performance of drone motors for spec racing purposes. A PID loop is used to limit the drone's maximum rpm to 13k rpm at 100% throttle. The limiter also linearizes the rpm of the motors such that, for example, 50% throttle will result in 50% of 13k rpm, 25% will result in 25% of the max throttle, etc. This means that battery sag will have very little effect on the rpm of the motors throughout the flight, and you will not feel it the same way you typically do. Please ensure voltage alarms configured on your drone.
|
||||
|
||||
IMPORTANT - Before/after flashing:
|
||||
Before flashing, click auto-detect in the firmware tab in betaflight or select your FC from the dropdown
|
||||
- This tells Betaflight which custom defaults it should apply to your quad after flashing.
|
||||
When you connect for the first time, you should be prompted to apply custom defaults. You MUST do this or your FC may not work properly.
|
||||
If you are not prompted to apply custom defaults, use the custom defaults found on the Betaflight's github
|
||||
- https://github.com/betaflight/unified-targets/tree/master/configs/default
|
||||
|
||||
Which HEX do I use?
|
||||
- connect your fc to betaflight
|
||||
- open the cli tab and type "version"
|
||||
- look for board_name: (YOUR BOARD NAME HERE)
|
||||
- this will show you which board you should flash
|
||||
- if in doubt, check what target your FC's manufacterur recommends in their user manual
|
||||
- If you have any questions, please reach out on our discord: https://discord.gg/C4HHYccaqk
|
||||
|
||||
How do I activate the boost?
|
||||
- We've hijacked the beeper mode in betaflight in order to enable boost without any complicated cli commands
|
||||
- Simply open the modes tab and apply an aux channel and range to the beeper mode
|
||||
- We also recommend enabling the boost bar in your OSD so you can keep track of your boost usage
|
||||
- Open the OSD tab and enable "Battery usage" and leave the drop down on "Graphical remaining"
|
||||
|
||||
Optional CLI Commands:
|
||||
- if you are having issues launching your drone (E.G. low power when you try to take off), consider lowering your moter idle speed.
|
||||
- You can also try the cli command "set rpm_limiter_idle_rpm = 14"
|
||||
- This sets the expected idle rpm / 100. For street league a good starting place is 1400 RPM, which is `set rpm_limiter_idle_rpm = 14`.
|
||||
THE QUAD WILL SPIN UP TO THIS RPM WHEN ARMED, SO DO NOT SET TO ANY VALUE ABOVE 50!
|
||||
|
||||
|
||||
Blackbox setup:
|
||||
You can view the rpm averaged accross the 4 motors as well as the P term, I term and D term in the blackbox. Simply `set debug_mode = RPM_LIMITER` in the cli. The values logged as a result are as follows in this order
|
||||
Debug 0. Average RPM
|
||||
Debug 1. RPM error: Difference between desired RPM limit and smoothed average rpm. RPM limit is rpm_limiter_rpm_limit for linearization off and rpm_limiter_rpm_limit*throttle for linearization on. Positive means overspeed, negative means underspeed
|
||||
Debug 2. I term (positive means term is pulling the throttle down)
|
||||
Debug 3. D term (positive means term is pulling the throttle down)
|
||||
|
||||
Am I spec yet?
|
||||
Once you have flashed the HEX, your FC is spec! If you have any issues, please verify they don't also exist on stock betaflight. Then report them here.
|
||||
- https://github.com/StreetLeagueSpec/betaflight
|
26
build.sh
Normal file
26
build.sh
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Check if the input file was provided
|
||||
if [ -z dos2unix "$1" ]; then
|
||||
echo "Usage: $0 <input-file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Convert line endings of the script file to LF
|
||||
dos2unix "$0" >/dev/null 2>&1
|
||||
|
||||
# Loop through each line in the input file
|
||||
while read -r line; do
|
||||
# Extract the device name from the current line
|
||||
device=$(echo "$line" | cut -d',' -f1)
|
||||
|
||||
# Skip the header row
|
||||
if [ "$device" == "DEVICE_NAME" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Run the make command for the current device
|
||||
echo "Building firmware for $device"
|
||||
make "$device" EXTRA_FLAGS="-D'RELEASE_NAME=4.4.1' -DCLOUD_BUILD -DUSE_DSHOT -DUSE_GPS -DUSE_GPS_PLUS_CODES -DUSE_LED_STRIP -DUSE_OSD -DUSE_OSD_HD -DUSE_OSD_SD -DUSE_PINIO -DUSE_SERIALRX -DUSE_SERIALRX_CRSF -DUSE_SERIALRX_DEFAULT -DUSE_SERIALRX_GHST -DUSE_SERIALRX_SBUS -DUSE_SERIALRX_SRXL2 -DUSE_SERIALRX_SPEKTRUM -DUSE_TELEMETRY -DUSE_TELEMETRY_CRSF -DUSE_TELEMETRY_GHST -DUSE_VTX"
|
||||
#make JHEF411 EXTRA_FLAGS="-D'RELEASE_NAME=4.4.1' -DCLOUD_BUILD -DUSE_DSHOT -DUSE_GPS -DUSE_GPS_PLUS_CODES -DUSE_LED_STRIP -DUSE_OSD -DUSE_OSD_HD -DUSE_OSD_SD -DUSE_PINIO -DUSE_SERIALRX -DUSE_SERIALRX_CRSF -DUSE_SERIALRX_DEFAULT -DUSE_SERIALRX_GHST -DUSE_SERIALRX_SBUS -DUSE_SERIALRX_SRXL2 -DUSE_SERIALRX_SPEKTRUM -DUSE_TELEMETRY -DUSE_TELEMETRY_CRSF -DUSE_TELEMETRY_GHST -DUSE_VTX
|
||||
done < "$1"
|
7
common-devices.csv
Normal file
7
common-devices.csv
Normal file
|
@ -0,0 +1,7 @@
|
|||
OMNIBUSF4SD
|
||||
AIKONF4
|
||||
AIKONF7
|
||||
HOBBYWING_XROTORF4G3
|
||||
HOBBYWING_XROTORF7CONV
|
||||
BETAFPVF411
|
||||
JHEF411
|
|
1
device.csv
Normal file
1
device.csv
Normal file
|
@ -0,0 +1 @@
|
|||
BetafpvF411RX
|
|
365
devices.csv
Normal file
365
devices.csv
Normal file
|
@ -0,0 +1,365 @@
|
|||
ALIENFLIGHTF4
|
||||
ALIENFLIGHTNGF7
|
||||
ALIENFLIGHTNGF7_DUAL
|
||||
ALIENFLIGHTNGF7_ELRS
|
||||
ALIENFLIGHTNGF7_RX
|
||||
AIKONF4
|
||||
AIKONF7
|
||||
AG3XF4
|
||||
AG3XF7
|
||||
AIRBOTF4
|
||||
AIRBOTF4SD
|
||||
AIRBOTF4V2
|
||||
AIRBOTF7
|
||||
AIRBOTF7HDV
|
||||
HELSEL_STRIKERF7
|
||||
NOX
|
||||
OMNIBUSF4
|
||||
OMNIBUSF4FW
|
||||
OMNIBUSF4NANOV7
|
||||
OMNIBUSF4SD
|
||||
OMNIBUSF4V6
|
||||
OMNIBUSF7
|
||||
OMNIBUSF7NANOV7
|
||||
OMNIBUSF7V2
|
||||
AIRB-OMNINXT4
|
||||
AIRB-OMNINXT7
|
||||
ALIENWHOOPF4
|
||||
MERCURYG4
|
||||
APEXF7
|
||||
AXISFLYINGF7
|
||||
AXISFLYINGF7PRO
|
||||
BETAFPVF405
|
||||
BETAFPVF411
|
||||
BETAFPVF411RX
|
||||
BETAFPVF4SX1280
|
||||
BETAFPVF722
|
||||
BETAFPVH743
|
||||
BLUEJAYF4
|
||||
NERO
|
||||
MATEKF405STD_CLONE
|
||||
CLRACINGF4
|
||||
CLRACINGF7
|
||||
RUSRACE_F4
|
||||
RUSRACE_F7
|
||||
SIRMIXALOT
|
||||
CYCLONEF405_PRO
|
||||
CYCLONEF722_PRO
|
||||
DARWINF411
|
||||
DARWINF4SX1280HD
|
||||
DARWINF722HD
|
||||
DAKEFPVF405
|
||||
DAKEFPVF411
|
||||
DAKEFPVF722
|
||||
DALRCF405
|
||||
DALRCF722DUAL
|
||||
SOULF4
|
||||
DFR_F722_DUAL_HD
|
||||
FURYF4
|
||||
FURYF4OSD
|
||||
MAMBAF405US
|
||||
MAMBAF405US_I2C
|
||||
MAMBAF405_2022A
|
||||
MAMBAF405_2022B
|
||||
MAMBAF411
|
||||
MAMBAF722
|
||||
MAMBAF722_2022A
|
||||
MAMBAF722_2022B
|
||||
MAMBAF722_I2C
|
||||
MAMBAF722_X8
|
||||
MAMBAG4
|
||||
MAMBAH743
|
||||
MAMBAH743_2022B
|
||||
ELINF405
|
||||
ELINF722
|
||||
DYSF44530D
|
||||
DYSF4PRO
|
||||
EACHINEF411_AIO
|
||||
EACHINEF722
|
||||
EACHINEF722_AIO
|
||||
EMAX_BABYHAWK_II_HD
|
||||
EMAX_TINYHAWKF4SX1280
|
||||
EMAX_TINYHAWK_F411RX
|
||||
EXF722DUAL
|
||||
EXUAVF4PRO
|
||||
FF_FORTINIF4
|
||||
FF_FORTINIF4_REV03
|
||||
FF_PIKOF4
|
||||
FF_PIKOF4OSD
|
||||
FF_RACEPIT
|
||||
FF_RACEPITF7
|
||||
FF_RACEPITF7_MINI
|
||||
FF_RACEPIT_MINI
|
||||
FLYCOLORF7
|
||||
FLYCOLORF7_AIO
|
||||
KIWIF4
|
||||
KIWIF4V2
|
||||
PLUMF4
|
||||
GRAVITYF7
|
||||
REVOLT
|
||||
REVOLTOSD
|
||||
FLYWOOF405
|
||||
FLYWOOF405NANO
|
||||
FLYWOOF405PRO
|
||||
FLYWOOF405S_AIO
|
||||
FLYWOOF411
|
||||
FLYWOOF411EVO_HD
|
||||
FLYWOOF411HEX
|
||||
FLYWOOF411V2
|
||||
FLYWOOF411_5IN1_AIO
|
||||
FLYWOOF745
|
||||
FLYWOOF745NANO
|
||||
FLYWOOF7DUAL
|
||||
ANYFCF7
|
||||
ANYFCM7
|
||||
DRONIUSF7
|
||||
F4BY
|
||||
FENIX_F405
|
||||
SPARKY2
|
||||
WORMFC
|
||||
FOXEERF405
|
||||
FOXEERF722DUAL
|
||||
FOXEERF722V2
|
||||
FOXEERF722V3
|
||||
FOXEERF722V4
|
||||
FOXEERF745V2_AIO
|
||||
FOXEERF745V3_AIO
|
||||
FOXEERF745V4_AIO
|
||||
FOXEERF745_AIO
|
||||
FOXEERH743
|
||||
BETAFLIGHTF4
|
||||
FPVM_BETAFLIGHTF7
|
||||
XRACERF4
|
||||
FRSKYF4
|
||||
GEELANGF411
|
||||
XILOF4
|
||||
GEPRCF405
|
||||
GEPRCF411
|
||||
GEPRCF411SX1280
|
||||
GEPRCF411_AIO
|
||||
GEPRCF411_PRO
|
||||
GEPRCF722
|
||||
GEPRCF722BT
|
||||
GEPRCF722_BT_HD
|
||||
GEPRCF745_BT_HD
|
||||
GEPRCG4AIO
|
||||
GEPRC_F722_AIO
|
||||
GEP_F405_VTX_V3
|
||||
GEMEF411
|
||||
GEMEF722
|
||||
CRAZYBEEF4DX
|
||||
CRAZYBEEF4DXS
|
||||
CRAZYBEEF4FR
|
||||
CRAZYBEEF4FS
|
||||
CRAZYBEEF4SX1280
|
||||
HAKRCF405
|
||||
HAKRCF405D
|
||||
HAKRCF405V2
|
||||
HAKRCF411
|
||||
HAKRCF411D
|
||||
HAKRCF722
|
||||
HAKRCF722D
|
||||
HAKRCF722MINI
|
||||
HAKRCF722V2
|
||||
HAKRCF7230D
|
||||
KD722
|
||||
KAKUTEF4
|
||||
KAKUTEF4V2
|
||||
KAKUTEF7
|
||||
KAKUTEF7HDV
|
||||
KAKUTEF7MINI
|
||||
KAKUTEF7MINIV3
|
||||
KAKUTEF7V2
|
||||
KAKUTEH7
|
||||
KAKUTEH7MINI
|
||||
KAKUTEH7V2
|
||||
TALONF4V2
|
||||
TALONF7DJIHD
|
||||
TALONF7FUSION
|
||||
TALONF7V2
|
||||
HIFIONRCF7
|
||||
HGLRCF405
|
||||
HGLRCF405AS
|
||||
HGLRCF411
|
||||
HGLRCF411SX1280
|
||||
HGLRCF411SX1280_15A
|
||||
HGLRCF722
|
||||
HGLRCF722E
|
||||
HGLRCF745
|
||||
ZEUSF4EVO
|
||||
ZEUSF4FR
|
||||
ZEUSF722_AIO
|
||||
NIDICI_F4
|
||||
HOBBYWING_XROTORF4G3
|
||||
HOBBYWING_XROTORF7CONV
|
||||
IFLIGHT_BLITZ_F405
|
||||
IFLIGHT_BLITZ_F411RX
|
||||
IFLIGHT_BLITZ_F722
|
||||
IFLIGHT_BLITZ_F722_X1
|
||||
IFLIGHT_BLITZ_F7_AIO
|
||||
IFLIGHT_BLITZ_F7_PRO
|
||||
IFLIGHT_F405_AIO
|
||||
IFLIGHT_F405_TWING
|
||||
IFLIGHT_F411_AIO32
|
||||
IFLIGHT_F411_PRO
|
||||
IFLIGHT_F4SX1280
|
||||
IFLIGHT_F722_TWING
|
||||
IFLIGHT_F745_AIO
|
||||
IFLIGHT_F745_AIO_V2
|
||||
IFLIGHT_H743_AIO_V2
|
||||
IFLIGHT_H7_TWING
|
||||
IFLIGHT_SUCCEX_E_F4
|
||||
IFLIGHT_SUCCEX_E_F7
|
||||
JBF7
|
||||
JBF7_DJI
|
||||
JBF7_V2
|
||||
JHEF405
|
||||
JHEF405PRO
|
||||
JHEF411
|
||||
JHEF745
|
||||
JHEF7DUAL
|
||||
JHEH743_AIO
|
||||
SYNERGYF4
|
||||
LDARC_F411
|
||||
ELLE0
|
||||
FISHDRONEF4
|
||||
KROOZX
|
||||
PIRXF4
|
||||
PODIUMF4
|
||||
STACKX
|
||||
VRRACE
|
||||
LUXAIO
|
||||
LUXF4OSD
|
||||
LUXF7HDV
|
||||
LUXMINIF7
|
||||
MERAKRCF405
|
||||
MERAKRCF722
|
||||
MLTEMPF4
|
||||
MLTYPHF4
|
||||
MATEKF405AIO
|
||||
MATEKF405CTR
|
||||
MATEKF405MINI
|
||||
MATEKF405SE
|
||||
MATEKF405STD
|
||||
MATEKF405TE
|
||||
MATEKF405TE_SD
|
||||
MATEKF411
|
||||
MATEKF411RX
|
||||
MATEKF411SE
|
||||
MATEKF722
|
||||
MATEKF722HD
|
||||
MATEKF722MINI
|
||||
MATEKF722SE
|
||||
MATEKH743
|
||||
NBD_CRICKETF7
|
||||
NBD_CRICKETF7V2
|
||||
NBD_GALAXYAIO255
|
||||
NBD_INFINITY200RS
|
||||
NBD_INFINITYAIO
|
||||
NBD_INFINITYAIOV2
|
||||
NBD_INFINITYAIOV2PRO
|
||||
NBD_INFINITYF4
|
||||
FLOWBOX
|
||||
FLOWBOXV2
|
||||
HYBRIDG4
|
||||
NEUTRONRCF407
|
||||
NEUTRONRCF411AIO
|
||||
NEUTRONRCF411SX1280
|
||||
NEUTRONRCF722AIO
|
||||
NEUTRONRCF7AIO
|
||||
NEUTRONRCG4AIO
|
||||
NEUTRONRCH743AIO
|
||||
NEUTRONRCH7BT
|
||||
UAVPNG030MINI
|
||||
REVO
|
||||
REVONANO
|
||||
PYRODRONEF4
|
||||
PYRODRONEF4PDB
|
||||
PYRODRONEF7
|
||||
AIRF7
|
||||
MINI_H743_HD
|
||||
ARESF7
|
||||
BEEROTORF4
|
||||
BLADE_F7
|
||||
BLADE_F7_HD
|
||||
RUSHCORE7
|
||||
RUSHF7AIO
|
||||
AOCODAF405
|
||||
AOCODAF405V2MPU6000
|
||||
AOCODAF405V2MPU6500
|
||||
AOCODAF722BLE
|
||||
AOCODAF722MINI
|
||||
AOCODARCF411_AIO
|
||||
AOCODARCF722_AIO
|
||||
AOCODARCF7DUAL
|
||||
AOCODARCH7DUAL
|
||||
SKYSTARSF405
|
||||
SKYSTARSF405AIO
|
||||
SKYSTARSF411
|
||||
SKYSTARSF7HD
|
||||
SKYSTARSF7HDPRO
|
||||
SKYSTARSH7HD
|
||||
ATOMRCF405
|
||||
ATOMRCF411
|
||||
ATOMRCF722
|
||||
SKYZONEF405
|
||||
SPEEDYBEEF4
|
||||
SPEEDYBEEF405V3
|
||||
SPEEDYBEEF7
|
||||
SPEEDYBEEF7MINI
|
||||
SPEEDYBEEF7MINIV2
|
||||
SPEEDYBEEF7V2
|
||||
SPEEDYBEEF7V3
|
||||
SPEEDYBEE_F745_AIO
|
||||
SPCMAKERF411
|
||||
SPEDIXF4
|
||||
SPRACINGF4EVO
|
||||
SPRACINGF4NEO
|
||||
SPRACINGF7DUAL
|
||||
SPRACINGH7EXTREME
|
||||
SPRACINGH7NANO
|
||||
SPRACINGH7RF
|
||||
NUCLEOF722
|
||||
STM32F411DISCOVERY
|
||||
STM32F4DISCOVERY
|
||||
SUB250F411
|
||||
MODULARF4
|
||||
TCMMF411
|
||||
TCMMF7
|
||||
COLIBRI
|
||||
PODRACERAIO
|
||||
TMH7
|
||||
TMOTORF4
|
||||
TMOTORF411
|
||||
TMOTORF4SX1280
|
||||
TMOTORF7
|
||||
TMOTORF722SE
|
||||
TMOTORF7V2
|
||||
TMOTORF7_AIO
|
||||
TMOTORH743
|
||||
TMOTORVELOXF7V2
|
||||
TMPACERF7
|
||||
TMPACERF7MINI
|
||||
TMVELOXF411
|
||||
TMVELOXF7
|
||||
TRANSTECF405HD
|
||||
TRANSTECF411
|
||||
TRANSTECF411AIO
|
||||
TRANSTECF411HD
|
||||
TRANSTECF7
|
||||
TRANSTECF7HD
|
||||
FPVCYCLEF401
|
||||
TUNERCF405
|
||||
VGOODF722DUAL
|
||||
VGOODRCF4
|
||||
VGOODRCF405_DJI
|
||||
VGOODRCF411_DJI
|
||||
VGOODRCF722_DJI
|
||||
VIVAF4AIO
|
||||
WIZZF7HD
|
||||
YUPIF4
|
||||
YUPIF7
|
||||
ZEEZF7
|
||||
ZEEZF7V2
|
||||
ZEEZF7V3
|
||||
ZEEZWHOOP
|
|
3
my-devices.csv
Normal file
3
my-devices.csv
Normal file
|
@ -0,0 +1,3 @@
|
|||
HOBBYWING_XROTORF7CONV
|
||||
CRAZYBEEF4FR
|
||||
|
|
|
@ -108,5 +108,6 @@ const char * const debugModeNames[DEBUG_COUNT] = {
|
|||
"VTX_MSP",
|
||||
"GPS_DOP",
|
||||
"FAILSAFE",
|
||||
"DSHOT_TELEMETRY_COUNTS"
|
||||
"DSHOT_TELEMETRY_COUNTS",
|
||||
"RPM_LIMITER"
|
||||
};
|
||||
|
|
|
@ -107,6 +107,7 @@ typedef enum {
|
|||
DEBUG_GPS_DOP,
|
||||
DEBUG_FAILSAFE,
|
||||
DEBUG_DSHOT_TELEMETRY_COUNTS,
|
||||
DEBUG_RPM_LIMITER,
|
||||
DEBUG_COUNT
|
||||
} debugType_e;
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#define FC_VERSION_MINOR 4 // increment when a minor release is made (small new feature, change etc)
|
||||
#define FC_VERSION_PATCH_LEVEL 4 // increment when a bug is fixed
|
||||
|
||||
#define SPEC_VERSION "UDL-1.0.0-locked"
|
||||
|
||||
#define FC_VERSION_STRING STR(FC_VERSION_MAJOR) "." STR(FC_VERSION_MINOR) "." STR(FC_VERSION_PATCH_LEVEL)
|
||||
|
||||
extern const char* const targetName;
|
||||
|
|
|
@ -4951,6 +4951,7 @@ static void printVersion(const char *cmdName, bool printBoardInfo)
|
|||
|
||||
cliPrintf("# %s / %s (%s) %s %s / %s (%s) MSP API: %s",
|
||||
FC_FIRMWARE_NAME,
|
||||
SPEC_VERSION,
|
||||
targetName,
|
||||
systemConfig()->boardIdentifier,
|
||||
FC_VERSION_STRING,
|
||||
|
|
|
@ -853,7 +853,8 @@ const clivalue_t valueTable[] = {
|
|||
{ PARAM_NAME_MOTOR_PWM_PROTOCOL, VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_MOTOR_PWM_PROTOCOL }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorPwmProtocol) },
|
||||
{ PARAM_NAME_MOTOR_PWM_RATE, VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 200, 32000 }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorPwmRate) },
|
||||
{ "motor_pwm_inversion", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorPwmInversion) },
|
||||
{ PARAM_NAME_MOTOR_POLES, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 4, UINT8_MAX }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, motorPoleCount) },
|
||||
//street league modification
|
||||
{ PARAM_NAME_MOTOR_POLES, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 12, 12 }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, motorPoleCount) },
|
||||
{ "motor_output_reordering", VAR_UINT8 | MASTER_VALUE | MODE_ARRAY, .config.array.length = MAX_SUPPORTED_MOTORS, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorOutputReordering)},
|
||||
|
||||
// PG_THROTTLE_CORRECTION_CONFIG
|
||||
|
@ -931,6 +932,52 @@ const clivalue_t valueTable[] = {
|
|||
{ "beeper_dshot_beacon_tone", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = {1, DSHOT_CMD_BEACON5 }, PG_BEEPER_CONFIG, offsetof(beeperConfig_t, dshotBeaconTone) },
|
||||
#endif
|
||||
#endif // USE_BEEPER
|
||||
// PG_MIXER_CONFIG
|
||||
{ "yaw_motors_reversed", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, yaw_motors_reversed) },
|
||||
{ "mixer_type", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_MIXER_TYPE }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, mixer_type) },
|
||||
{ "crashflip_motor_percent", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 100 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, crashflip_motor_percent) },
|
||||
{ "crashflip_expo", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 100 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, crashflip_expo) },
|
||||
//Street League customization
|
||||
{ "rpm_limiter_idle_rpm", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 999 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_idle_rpm) },
|
||||
|
||||
//street league locked
|
||||
{ "rpm_limiter", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor) },
|
||||
{ "rpm_limiter_p", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 20, 20 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_p) },
|
||||
{ "rpm_limiter_i", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 17, 17 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_i) },
|
||||
{ "rpm_limiter_d", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 3, 3 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_d) },
|
||||
{ "rpm_limiter_rpm_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 305, 305 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_limit) },
|
||||
{ "rpm_limiter_afterburner", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 45, 45 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner) },
|
||||
{ "rpm_limiter_afterburner_duration", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 5, 5 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_duration) },
|
||||
{ "rpm_limiter_afterburner_hold_to_use", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_hold_to_use) },
|
||||
{ "rpm_limiter_afterburner_tank_count", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 1, 1 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_tank_count) },
|
||||
{ "rpm_limiter_afterburner_reset", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_reset) },
|
||||
{ "rpm_limiter_acceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 60, 60 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_acceleration_limit) },
|
||||
{ "rpm_limiter_deceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 60, 60 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_deceleration_limit) },
|
||||
{ "rpm_limiter_k_factor", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 3000, 3000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_k_factor) },
|
||||
{ "rpm_limiter_full_linearization", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_linearization) },
|
||||
|
||||
//street league unlocked
|
||||
// { "rpm_limiter", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor) },
|
||||
// { "rpm_limiter_p", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 10000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_p) },
|
||||
// { "rpm_limiter_i", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 10000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_i) },
|
||||
// { "rpm_limiter_d", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 10000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_d) },
|
||||
// { "rpm_limiter_rpm_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 999 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_limit) },
|
||||
// { "rpm_limiter_afterburner", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 999 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner) },
|
||||
// { "rpm_limiter_afterburner_duration", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 255 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_duration) },
|
||||
// { "rpm_limiter_afterburner_hold_to_use", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_hold_to_use) },
|
||||
// { "rpm_limiter_afterburner_tank_count", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 255 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_tank_count) },
|
||||
// { "rpm_limiter_afterburner_reset", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_reset) },
|
||||
// { "rpm_limiter_acceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 50000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_acceleration_limit) },
|
||||
// { "rpm_limiter_deceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 50000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_deceleration_limit) },
|
||||
// { "rpm_limiter_k_factor", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 1, 50000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_k_factor) },
|
||||
// { "rpm_limiter_full_linearization", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_linearization) },
|
||||
|
||||
{ "3d_deadband_high", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_RANGE_MIDDLE, PWM_PULSE_MAX }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, deadband3d_high) },
|
||||
{ "3d_neutral", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_PULSE_MIN, PWM_PULSE_MAX }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, neutral3d) },
|
||||
{ "3d_deadband_throttle", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 1, 100 }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, deadband3d_throttle) },
|
||||
{ "3d_limit_low", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_PULSE_MIN, PWM_RANGE_MIDDLE }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, limit3d_low) },
|
||||
{ "3d_limit_high", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_RANGE_MIDDLE, PWM_PULSE_MAX }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, limit3d_high) },
|
||||
{ "3d_switched_mode", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, switched_mode3d) },
|
||||
|
||||
// PG_MIXER_CONFIG
|
||||
{ "yaw_motors_reversed", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, yaw_motors_reversed) },
|
||||
|
@ -1699,6 +1746,7 @@ const clivalue_t valueTable[] = {
|
|||
#endif
|
||||
#endif
|
||||
{ "craft_name", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, craftName) },
|
||||
{ "drone_id", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, droneId) },
|
||||
#ifdef USE_OSD
|
||||
{ "pilot_name", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, pilotName) },
|
||||
#endif
|
||||
|
|
|
@ -111,6 +111,7 @@ PG_REGISTER_WITH_RESET_TEMPLATE(pilotConfig_t, pilotConfig, PG_PILOT_CONFIG, 2);
|
|||
PG_RESET_TEMPLATE(pilotConfig_t, pilotConfig,
|
||||
.craftName = { 0 },
|
||||
.pilotName = { 0 },
|
||||
.droneId = { 0 },
|
||||
);
|
||||
|
||||
PG_REGISTER_WITH_RESET_TEMPLATE(systemConfig_t, systemConfig, PG_SYSTEM_CONFIG, 3);
|
||||
|
|
|
@ -36,6 +36,7 @@ typedef enum {
|
|||
typedef struct pilotConfig_s {
|
||||
char craftName[MAX_NAME_LENGTH + 1];
|
||||
char pilotName[MAX_NAME_LENGTH + 1];
|
||||
char droneId[MAX_NAME_LENGTH + 1];
|
||||
} pilotConfig_t;
|
||||
|
||||
PG_DECLARE(pilotConfig_t, pilotConfig);
|
||||
|
|
|
@ -78,7 +78,9 @@ typedef enum {
|
|||
BOXSTICKCOMMANDDISABLE,
|
||||
BOXBEEPERMUTE,
|
||||
BOXREADY,
|
||||
BOXBOOST,
|
||||
CHECKBOX_ITEM_COUNT
|
||||
|
||||
} boxId_e;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
|
@ -109,113 +107,114 @@ static FAST_DATA_ZERO_INIT int8_t motorOutputMixSign;
|
|||
|
||||
static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs)
|
||||
{
|
||||
static uint16_t rcThrottlePrevious = 0; // Store the last throttle direction for deadband transitions
|
||||
static timeUs_t reversalTimeUs = 0; // time when motors last reversed in 3D mode
|
||||
UNUSED(currentTimeUs);
|
||||
// static uint16_t rcThrottlePrevious = 0; // Store the last throttle direction for deadband transitions
|
||||
// static timeUs_t reversalTimeUs = 0; // time when motors last reversed in 3D mode
|
||||
static float motorRangeMinIncrease = 0;
|
||||
|
||||
float currentThrottleInputRange = 0;
|
||||
if (mixerRuntime.feature3dEnabled) {
|
||||
uint16_t rcCommand3dDeadBandLow;
|
||||
uint16_t rcCommand3dDeadBandHigh;
|
||||
// if (mixerRuntime.feature3dEnabled) {
|
||||
// uint16_t rcCommand3dDeadBandLow;
|
||||
// uint16_t rcCommand3dDeadBandHigh;
|
||||
|
||||
if (!ARMING_FLAG(ARMED)) {
|
||||
rcThrottlePrevious = rxConfig()->midrc; // When disarmed set to mid_rc. It always results in positive direction after arming.
|
||||
}
|
||||
// if (!ARMING_FLAG(ARMED)) {
|
||||
// rcThrottlePrevious = rxConfig()->midrc; // When disarmed set to mid_rc. It always results in positive direction after arming.
|
||||
// }
|
||||
|
||||
if (IS_RC_MODE_ACTIVE(BOX3D) || flight3DConfig()->switched_mode3d) {
|
||||
// The min_check range is halved because the output throttle is scaled to 500us.
|
||||
// So by using half of min_check we maintain the same low-throttle deadband
|
||||
// stick travel as normal non-3D mode.
|
||||
const int mincheckOffset = (rxConfig()->mincheck - PWM_RANGE_MIN) / 2;
|
||||
rcCommand3dDeadBandLow = rxConfig()->midrc - mincheckOffset;
|
||||
rcCommand3dDeadBandHigh = rxConfig()->midrc + mincheckOffset;
|
||||
} else {
|
||||
rcCommand3dDeadBandLow = rxConfig()->midrc - flight3DConfig()->deadband3d_throttle;
|
||||
rcCommand3dDeadBandHigh = rxConfig()->midrc + flight3DConfig()->deadband3d_throttle;
|
||||
}
|
||||
// if (IS_RC_MODE_ACTIVE(BOX3D) || flight3DConfig()->switched_mode3d) {
|
||||
// // The min_check range is halved because the output throttle is scaled to 500us.
|
||||
// // So by using half of min_check we maintain the same low-throttle deadband
|
||||
// // stick travel as normal non-3D mode.
|
||||
// const int mincheckOffset = (rxConfig()->mincheck - PWM_RANGE_MIN) / 2;
|
||||
// rcCommand3dDeadBandLow = rxConfig()->midrc - mincheckOffset;
|
||||
// rcCommand3dDeadBandHigh = rxConfig()->midrc + mincheckOffset;
|
||||
// } else {
|
||||
// rcCommand3dDeadBandLow = rxConfig()->midrc - flight3DConfig()->deadband3d_throttle;
|
||||
// rcCommand3dDeadBandHigh = rxConfig()->midrc + flight3DConfig()->deadband3d_throttle;
|
||||
// }
|
||||
|
||||
const float rcCommandThrottleRange3dLow = rcCommand3dDeadBandLow - PWM_RANGE_MIN;
|
||||
const float rcCommandThrottleRange3dHigh = PWM_RANGE_MAX - rcCommand3dDeadBandHigh;
|
||||
// const float rcCommandThrottleRange3dLow = rcCommand3dDeadBandLow - PWM_RANGE_MIN;
|
||||
// const float rcCommandThrottleRange3dHigh = PWM_RANGE_MAX - rcCommand3dDeadBandHigh;
|
||||
|
||||
if (rcCommand[THROTTLE] <= rcCommand3dDeadBandLow || isFlipOverAfterCrashActive()) {
|
||||
// INVERTED
|
||||
motorRangeMin = mixerRuntime.motorOutputLow;
|
||||
motorRangeMax = mixerRuntime.deadbandMotor3dLow;
|
||||
#ifdef USE_DSHOT
|
||||
if (isMotorProtocolDshot()) {
|
||||
motorOutputMin = mixerRuntime.motorOutputLow;
|
||||
motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
motorOutputMin = mixerRuntime.deadbandMotor3dLow;
|
||||
motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow;
|
||||
}
|
||||
// if (rcCommand[THROTTLE] <= rcCommand3dDeadBandLow || isFlipOverAfterCrashActive()) {
|
||||
// // INVERTED
|
||||
// motorRangeMin = mixerRuntime.motorOutputLow;
|
||||
// motorRangeMax = mixerRuntime.deadbandMotor3dLow;
|
||||
// #ifdef USE_DSHOT
|
||||
// if (isMotorProtocolDshot()) {
|
||||
// motorOutputMin = mixerRuntime.motorOutputLow;
|
||||
// motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow;
|
||||
// } else
|
||||
// #endif
|
||||
// {
|
||||
// motorOutputMin = mixerRuntime.deadbandMotor3dLow;
|
||||
// motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow;
|
||||
// }
|
||||
|
||||
if (motorOutputMixSign != -1) {
|
||||
reversalTimeUs = currentTimeUs;
|
||||
}
|
||||
motorOutputMixSign = -1;
|
||||
// if (motorOutputMixSign != -1) {
|
||||
// reversalTimeUs = currentTimeUs;
|
||||
// }
|
||||
// motorOutputMixSign = -1;
|
||||
|
||||
rcThrottlePrevious = rcCommand[THROTTLE];
|
||||
throttle = rcCommand3dDeadBandLow - rcCommand[THROTTLE];
|
||||
currentThrottleInputRange = rcCommandThrottleRange3dLow;
|
||||
} else if (rcCommand[THROTTLE] >= rcCommand3dDeadBandHigh) {
|
||||
// NORMAL
|
||||
motorRangeMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
motorRangeMax = mixerRuntime.motorOutputHigh;
|
||||
motorOutputMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh;
|
||||
if (motorOutputMixSign != 1) {
|
||||
reversalTimeUs = currentTimeUs;
|
||||
}
|
||||
motorOutputMixSign = 1;
|
||||
rcThrottlePrevious = rcCommand[THROTTLE];
|
||||
throttle = rcCommand[THROTTLE] - rcCommand3dDeadBandHigh;
|
||||
currentThrottleInputRange = rcCommandThrottleRange3dHigh;
|
||||
} else if ((rcThrottlePrevious <= rcCommand3dDeadBandLow &&
|
||||
!flight3DConfigMutable()->switched_mode3d) ||
|
||||
isMotorsReversed()) {
|
||||
// INVERTED_TO_DEADBAND
|
||||
motorRangeMin = mixerRuntime.motorOutputLow;
|
||||
motorRangeMax = mixerRuntime.deadbandMotor3dLow;
|
||||
// rcThrottlePrevious = rcCommand[THROTTLE];
|
||||
// throttle = rcCommand3dDeadBandLow - rcCommand[THROTTLE];
|
||||
// currentThrottleInputRange = rcCommandThrottleRange3dLow;
|
||||
// } else if (rcCommand[THROTTLE] >= rcCommand3dDeadBandHigh) {
|
||||
// // NORMAL
|
||||
// motorRangeMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
// motorRangeMax = mixerRuntime.motorOutputHigh;
|
||||
// motorOutputMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
// motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh;
|
||||
// if (motorOutputMixSign != 1) {
|
||||
// reversalTimeUs = currentTimeUs;
|
||||
// }
|
||||
// motorOutputMixSign = 1;
|
||||
// rcThrottlePrevious = rcCommand[THROTTLE];
|
||||
// throttle = rcCommand[THROTTLE] - rcCommand3dDeadBandHigh;
|
||||
// currentThrottleInputRange = rcCommandThrottleRange3dHigh;
|
||||
// } else if ((rcThrottlePrevious <= rcCommand3dDeadBandLow &&
|
||||
// !flight3DConfigMutable()->switched_mode3d) ||
|
||||
// isMotorsReversed()) {
|
||||
// // INVERTED_TO_DEADBAND
|
||||
// motorRangeMin = mixerRuntime.motorOutputLow;
|
||||
// motorRangeMax = mixerRuntime.deadbandMotor3dLow;
|
||||
|
||||
#ifdef USE_DSHOT
|
||||
if (isMotorProtocolDshot()) {
|
||||
motorOutputMin = mixerRuntime.motorOutputLow;
|
||||
motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
motorOutputMin = mixerRuntime.deadbandMotor3dLow;
|
||||
motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow;
|
||||
}
|
||||
// #ifdef USE_DSHOT
|
||||
// if (isMotorProtocolDshot()) {
|
||||
// motorOutputMin = mixerRuntime.motorOutputLow;
|
||||
// motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow;
|
||||
// } else
|
||||
// #endif
|
||||
// {
|
||||
// motorOutputMin = mixerRuntime.deadbandMotor3dLow;
|
||||
// motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow;
|
||||
// }
|
||||
|
||||
if (motorOutputMixSign != -1) {
|
||||
reversalTimeUs = currentTimeUs;
|
||||
}
|
||||
motorOutputMixSign = -1;
|
||||
// if (motorOutputMixSign != -1) {
|
||||
// reversalTimeUs = currentTimeUs;
|
||||
// }
|
||||
// motorOutputMixSign = -1;
|
||||
|
||||
throttle = 0;
|
||||
currentThrottleInputRange = rcCommandThrottleRange3dLow;
|
||||
} else {
|
||||
// NORMAL_TO_DEADBAND
|
||||
motorRangeMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
motorRangeMax = mixerRuntime.motorOutputHigh;
|
||||
motorOutputMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh;
|
||||
if (motorOutputMixSign != 1) {
|
||||
reversalTimeUs = currentTimeUs;
|
||||
}
|
||||
motorOutputMixSign = 1;
|
||||
throttle = 0;
|
||||
currentThrottleInputRange = rcCommandThrottleRange3dHigh;
|
||||
}
|
||||
if (currentTimeUs - reversalTimeUs < 250000) {
|
||||
// keep iterm zero for 250ms after motor reversal
|
||||
pidResetIterm();
|
||||
}
|
||||
} else {
|
||||
// throttle = 0;
|
||||
// currentThrottleInputRange = rcCommandThrottleRange3dLow;
|
||||
// } else {
|
||||
// // NORMAL_TO_DEADBAND
|
||||
// motorRangeMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
// motorRangeMax = mixerRuntime.motorOutputHigh;
|
||||
// motorOutputMin = mixerRuntime.deadbandMotor3dHigh;
|
||||
// motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh;
|
||||
// if (motorOutputMixSign != 1) {
|
||||
// reversalTimeUs = currentTimeUs;
|
||||
// }
|
||||
// motorOutputMixSign = 1;
|
||||
// throttle = 0;
|
||||
// currentThrottleInputRange = rcCommandThrottleRange3dHigh;
|
||||
// }
|
||||
// if (currentTimeUs - reversalTimeUs < 250000) {
|
||||
// // keep iterm zero for 250ms after motor reversal
|
||||
// pidResetIterm();
|
||||
// }
|
||||
// } else {
|
||||
throttle = rcCommand[THROTTLE] - PWM_RANGE_MIN + throttleAngleCorrection;
|
||||
currentThrottleInputRange = PWM_RANGE;
|
||||
|
||||
|
@ -245,13 +244,13 @@ static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs)
|
|||
#if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION)
|
||||
float motorRangeAttenuationFactor = 0;
|
||||
// reduce motorRangeMax when battery is full
|
||||
if (mixerRuntime.vbatSagCompensationFactor > 0.0f) {
|
||||
if (!mixerConfig()->govenor && mixerRuntime.vbatSagCompensationFactor > 0.0f) {
|
||||
const uint16_t currentCellVoltage = getBatterySagCellVoltage();
|
||||
// batteryGoodness = 1 when voltage is above vbatFull, and 0 when voltage is below vbatLow
|
||||
float batteryGoodness = 1.0f - constrainf((mixerRuntime.vbatFull - currentCellVoltage) / mixerRuntime.vbatRangeToCompensate, 0.0f, 1.0f);
|
||||
motorRangeAttenuationFactor = (mixerRuntime.vbatRangeToCompensate / mixerRuntime.vbatFull) * batteryGoodness * mixerRuntime.vbatSagCompensationFactor;
|
||||
DEBUG_SET(DEBUG_BATTERY, 2, lrintf(batteryGoodness * 100));
|
||||
DEBUG_SET(DEBUG_BATTERY, 3, lrintf(motorRangeAttenuationFactor * 1000));
|
||||
DEBUG_SET(DEBUG_BATTERY, 2, batteryGoodness * 100);
|
||||
DEBUG_SET(DEBUG_BATTERY, 3, motorRangeAttenuationFactor * 1000);
|
||||
}
|
||||
motorRangeMax = isFlipOverAfterCrashActive() ? mixerRuntime.motorOutputHigh : mixerRuntime.motorOutputHigh - motorRangeAttenuationFactor * (mixerRuntime.motorOutputHigh - mixerRuntime.motorOutputLow);
|
||||
#else
|
||||
|
@ -262,8 +261,7 @@ static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs)
|
|||
motorOutputMin = motorRangeMin;
|
||||
motorOutputRange = motorRangeMax - motorRangeMin;
|
||||
motorOutputMixSign = 1;
|
||||
}
|
||||
|
||||
// }
|
||||
throttle = constrainf(throttle / currentThrottleInputRange, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
|
@ -346,17 +344,212 @@ static void applyFlipOverAfterCrashModeToMotors(void)
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t getAfterburnerTanksRemaining(void)
|
||||
{
|
||||
return mixerRuntime.afterburnerTanksRemaining;
|
||||
}
|
||||
|
||||
float getAfterburnerTankPercent(void)
|
||||
{
|
||||
return mixerRuntime.afterburnerTankPercent;
|
||||
}
|
||||
|
||||
static void applyRPMLimiter(void)
|
||||
{
|
||||
if (mixerRuntime.govenorEnabled) {
|
||||
float RPM_GOVENOR_LIMIT = 0.0f;
|
||||
float averageRPM = 0.0f;
|
||||
float averageRPM_smoothed = 0.0f;
|
||||
float PIDOutput = 0.0f;
|
||||
float rcCommandThrottle = (rcCommand[THROTTLE]-1000)/1000.0f;
|
||||
float maxRPMLimit = 0.0f;
|
||||
|
||||
maxRPMLimit = mixerRuntime.RPMLimit;
|
||||
|
||||
//afterburner code
|
||||
//if drone is armed
|
||||
if (ARMING_FLAG(ARMED)) {
|
||||
//if the afterburner switch is engaged
|
||||
if(IS_RC_MODE_ACTIVE(BOXBOOST)) {
|
||||
//if the afterburner isn't initiated
|
||||
if(mixerRuntime.afterburnerInitiated == false) {
|
||||
//if there's charge in the tank
|
||||
if(mixerRuntime.afterburnerTankPercent>0.0f) {
|
||||
//if we have another tank left
|
||||
if(mixerRuntime.afterburnerTanksRemaining>0) {
|
||||
mixerRuntime.afterburnerInitiated = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//if the afterburner switch is NOT engaged
|
||||
} else {
|
||||
//if hold to boost is enabled
|
||||
if(mixerRuntime.afterburnerHoldToBoost) {
|
||||
mixerRuntime.afterburnerInitiated = false;
|
||||
}
|
||||
}
|
||||
|
||||
//use afterburner
|
||||
if(mixerRuntime.afterburnerInitiated) {
|
||||
//if the tank is empty, reset
|
||||
if(mixerRuntime.afterburnerTankPercent<=0.0f) {
|
||||
mixerRuntime.afterburnerInitiated = false;
|
||||
//only refil the tank if we have one remaining
|
||||
if(mixerRuntime.afterburnerTanksRemaining>0) {
|
||||
mixerRuntime.afterburnerTanksRemaining -= 1;
|
||||
}
|
||||
if(mixerRuntime.afterburnerTanksRemaining>0) {
|
||||
mixerRuntime.afterburnerTankPercent = 100.0f;
|
||||
}
|
||||
|
||||
}
|
||||
//tank percent decreases linearly
|
||||
mixerRuntime.afterburnerTankPercent -= (pidGetDT()/(mixerRuntime.afterburnerDuration))*100.0f;
|
||||
//tank percent can never be above 100%
|
||||
mixerRuntime.afterburnerTankPercent = MIN(mixerRuntime.afterburnerTankPercent,100.0f);
|
||||
//tank percent can never be below 0%
|
||||
mixerRuntime.afterburnerTankPercent = MAX(mixerRuntime.afterburnerTankPercent,0.0f);
|
||||
//increase the rpm limit
|
||||
maxRPMLimit = mixerRuntime.RPMLimit+(mixerRuntime.afterburnerRPM*mixerRuntime.afterburnerTankPercent*0.01f);
|
||||
}
|
||||
}else{
|
||||
if(mixerRuntime.afterburnerReset) {
|
||||
mixerRuntime.afterburnerTankPercent = 100.0f;
|
||||
mixerRuntime.afterburnerTanksRemaining = mixerConfig()->govenor_rpm_afterburner_tank_count;
|
||||
}
|
||||
}
|
||||
|
||||
//Street League customization
|
||||
if (mixerRuntime.rpmLinearization) {
|
||||
//scales rpm setpoint between idle rpm and max rpm limit based on throttle percent
|
||||
RPM_GOVENOR_LIMIT = ((maxRPMLimit - mixerConfig()->govenor_idle_rpm))*100.0f*(rcCommandThrottle) + mixerConfig()->govenor_idle_rpm * 100.0f;
|
||||
|
||||
//limit the speed with which the rpm setpoint can change based on the rpm_limiter_acceleration_limit cli command
|
||||
float acceleration = RPM_GOVENOR_LIMIT - mixerRuntime.govenorPreviousRPMLimit;
|
||||
if(acceleration > 0) {
|
||||
acceleration = MIN(acceleration, mixerRuntime.govenorAccelerationLimit);
|
||||
RPM_GOVENOR_LIMIT = mixerRuntime.govenorPreviousRPMLimit + acceleration;
|
||||
}
|
||||
else if(acceleration < 0) {
|
||||
acceleration = MAX(acceleration, -mixerRuntime.govenorDecelerationLimit);
|
||||
RPM_GOVENOR_LIMIT = mixerRuntime.govenorPreviousRPMLimit + acceleration;
|
||||
}
|
||||
} else {
|
||||
throttle = throttle * mixerRuntime.govenorExpectedThrottleLimit;
|
||||
RPM_GOVENOR_LIMIT = ((maxRPMLimit))*100.0f;
|
||||
}
|
||||
|
||||
//get the rpm averaged across the motors
|
||||
bool motorsSaturated = false;
|
||||
bool motorsDesaturated = false;
|
||||
for (int i = 0; i < getMotorCount(); i++) {
|
||||
averageRPM += (float)getDshotTelemetry(i);
|
||||
if (motor[i] >= motorConfig()->maxthrottle) {
|
||||
motorsSaturated = true;
|
||||
}
|
||||
if (motor[i] <= motorConfig()->minthrottle) {
|
||||
motorsDesaturated = false;
|
||||
}
|
||||
}
|
||||
averageRPM = 100.0f * averageRPM / (float)(getMotorCount()*mixerRuntime.motorPoleCount/2.0f);
|
||||
|
||||
//get the smoothed rpm to avoid d term noise
|
||||
averageRPM_smoothed = mixerRuntime.govenorPreviousSmoothedRPM + (float)mixerRuntime.govenorDelayK * (averageRPM - mixerRuntime.govenorPreviousSmoothedRPM); //kinda braindead to convert to rps then back
|
||||
|
||||
float smoothedRPMError = averageRPM_smoothed - RPM_GOVENOR_LIMIT;
|
||||
float govenorP = smoothedRPMError * mixerRuntime.govenorPGain; //+ when overspped
|
||||
float govenorD = (smoothedRPMError-mixerRuntime.govenorPreviousSmoothedRPMError) * mixerRuntime.govenorDGain; // + when quickly going overspeed
|
||||
|
||||
if (mixerRuntime.rpmLinearization) {
|
||||
/*
|
||||
//don't let I term wind up if throttle is below the motor idle
|
||||
if (rcCommandThrottle < motorConfig()->digitalIdleOffsetValue / 10000.0f) {
|
||||
mixerRuntime.govenorI *= 1.0f/(1.0f+(pidGetDT()*10.0f)); //slowly ramp down i term instead of resetting to avoid throttle pulsing cheats
|
||||
} else {
|
||||
//don't let I term wind up if motors are saturated. Otherwise, motors may stay at high throttle even after low throttle is commanded
|
||||
if(!motorsSaturated)
|
||||
{
|
||||
mixerRuntime.govenorI += smoothedRPMError * mixerRuntime.govenorIGain; // + when overspeed
|
||||
}
|
||||
}*/
|
||||
|
||||
//don't let I term wind up if throttle is below the motor idle
|
||||
if (rcCommandThrottle < motorConfig()->digitalIdleOffsetValue / 10000.0f) {
|
||||
mixerRuntime.govenorI *= 1.0f/(1.0f+(pidGetDT()*10.0f)); //slowly ramp down i term
|
||||
//mixerRuntime.govenorI = 0.0f;
|
||||
} else {
|
||||
//don't let I term wind up if motors are saturated. Otherwise, motors may stay at high throttle even after low throttle is commanded
|
||||
if(!motorsSaturated && !motorsDesaturated)
|
||||
{
|
||||
mixerRuntime.govenorI += smoothedRPMError * mixerRuntime.govenorIGain; // + when overspeed
|
||||
}else{
|
||||
mixerRuntime.govenorI *= 1.0f/(1.0f+(pidGetDT()*10.0f)); //slowly ramp down i term
|
||||
}
|
||||
}
|
||||
|
||||
//sum our pid terms
|
||||
PIDOutput = govenorP + mixerRuntime.govenorI + govenorD; //more + when overspeed, should be subtracted from throttle
|
||||
|
||||
} else {
|
||||
throttle = throttle * mixerRuntime.govenorExpectedThrottleLimit;
|
||||
mixerRuntime.govenorI += smoothedRPMError * mixerRuntime.govenorIGain; // + when overspeed
|
||||
mixerRuntime.govenorI = MAX(mixerRuntime.govenorI, 0.0f);
|
||||
PIDOutput = govenorP + mixerRuntime.govenorI + govenorD; //more + when overspeed, should be subtracted from throttle
|
||||
if (PIDOutput > 0.05) {
|
||||
mixerRuntime.govenorExpectedThrottleLimit = 0.9994 * mixerRuntime.govenorExpectedThrottleLimit;
|
||||
}
|
||||
if (PIDOutput < -0.05 && rcCommand[THROTTLE] > 1950 && !motorsSaturated) {
|
||||
mixerRuntime.govenorExpectedThrottleLimit = (1+1-0.9994) * mixerRuntime.govenorExpectedThrottleLimit;
|
||||
mixerRuntime.govenorExpectedThrottleLimit = MAX(mixerRuntime.govenorExpectedThrottleLimit, 1.0f);
|
||||
}
|
||||
|
||||
PIDOutput = MAX(PIDOutput,0.0f);
|
||||
|
||||
}
|
||||
if (motorConfig()->dev.useDshotTelemetry) {
|
||||
if (mixerRuntime.govenor_init) {
|
||||
if (mixerRuntime.rpmLinearization) {
|
||||
throttle = constrainf(-PIDOutput, 0.0f, 1.0f);
|
||||
} else {
|
||||
throttle = constrainf(throttle-PIDOutput, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
} else { //if dshot telemetry isn't enabled. Keep the throttle at zero
|
||||
throttle = 0.0f;
|
||||
}
|
||||
|
||||
mixerRuntime.govenor_init = true;
|
||||
|
||||
//update previous values for next loop
|
||||
mixerRuntime.prevAverageRPM = averageRPM;
|
||||
mixerRuntime.govenorPreviousSmoothedRPM = averageRPM_smoothed;
|
||||
mixerRuntime.govenorPreviousSmoothedRPMError = smoothedRPMError;
|
||||
mixerRuntime.govenorPreviousRPMLimit = RPM_GOVENOR_LIMIT;
|
||||
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 0, averageRPM);//unfiltered average rpm
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 1, averageRPM_smoothed); //filtered average rpm
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 2, smoothedRPMError); //P term
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 3, mixerRuntime.govenorI*100.0f); // I term
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 4, govenorD*10000.0f); // D term
|
||||
|
||||
/*DEBUG_SET(DEBUG_RPM_LIMITER, 0, mixerRuntime.afterburnerInitiated);
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 1, mixerRuntime.afterburnerTankPercent);
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 2, mixerRuntime.afterburnerTanksRemaining);
|
||||
DEBUG_SET(DEBUG_RPM_LIMITER, 3, maxRPMLimit);*/
|
||||
}
|
||||
}
|
||||
|
||||
static void applyMixToMotors(float motorMix[MAX_SUPPORTED_MOTORS], motorMixer_t *activeMixer)
|
||||
{
|
||||
// Now add in the desired throttle, but keep in a range that doesn't clip adjusted
|
||||
// roll/pitch/yaw. This could move throttle down, but also up for those low throttle flips.
|
||||
for (int i = 0; i < mixerRuntime.motorCount; i++) {
|
||||
float motorOutput = motorOutputMixSign * motorMix[i] + throttle * activeMixer[i].throttle;
|
||||
if (!mixerConfig()->govenor) {
|
||||
#ifdef USE_THRUST_LINEARIZATION
|
||||
motorOutput = pidApplyThrustLinearization(motorOutput);
|
||||
#endif
|
||||
}
|
||||
motorOutput = motorOutputMin + motorOutputRange * motorOutput;
|
||||
|
||||
#ifdef USE_SERVOS
|
||||
if (mixerIsTricopter()) {
|
||||
motorOutput += mixerTricopterMotorCorrection(i);
|
||||
|
@ -377,6 +570,8 @@ static void applyMixToMotors(float motorMix[MAX_SUPPORTED_MOTORS], motorMixer_t
|
|||
|
||||
// Disarmed mode
|
||||
if (!ARMING_FLAG(ARMED)) {
|
||||
mixerRuntime.govenorI = 0;
|
||||
mixerRuntime.govenor_init = false;
|
||||
for (int i = 0; i < mixerRuntime.motorCount; i++) {
|
||||
motor[i] = motor_disarmed[i];
|
||||
}
|
||||
|
@ -425,68 +620,47 @@ static void updateDynLpfCutoffs(timeUs_t currentTimeUs, float throttle)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void applyMixerAdjustmentLinear(float *motorMix, const bool airmodeEnabled)
|
||||
{
|
||||
float airmodeTransitionPercent = 1.0f;
|
||||
float motorDeltaScale = 0.5f;
|
||||
|
||||
if (!airmodeEnabled && throttle < 0.5f) {
|
||||
// this scales the motor mix authority to be 0.5 at 0 throttle, and 1.0 at 0.5 throttle as airmode off intended for things to work.
|
||||
// also lays the groundwork for how an airmode percent would work.
|
||||
airmodeTransitionPercent = scaleRangef(throttle, 0.0f, 0.5f, 0.5f, 1.0f); // 0.5 throttle is full transition, and 0.0 throttle is 50% airmodeTransitionPercent
|
||||
motorDeltaScale *= airmodeTransitionPercent; // this should be half of the motor authority allowed
|
||||
}
|
||||
|
||||
const float motorMixNormalizationFactor = motorMixRange > 1.0f ? airmodeTransitionPercent / motorMixRange : airmodeTransitionPercent;
|
||||
|
||||
const float motorMixDelta = motorDeltaScale * motorMixRange;
|
||||
|
||||
float minMotor = FLT_MAX;
|
||||
float maxMotor = FLT_MIN;
|
||||
static void applyMixerAdjustmentLinear(float *motorMix, const bool airmodeEnabled) {
|
||||
const float motorMixNormalizationFactor = motorMixRange > 1.0f ? motorMixRange : 1.0f;
|
||||
const float motorMixDelta = 0.5f * motorMixRange;
|
||||
|
||||
for (int i = 0; i < mixerRuntime.motorCount; ++i) {
|
||||
if (airmodeEnabled || throttle > 0.5f) {
|
||||
if (mixerConfig()->mixer_type == MIXER_LINEAR) {
|
||||
motorMix[i] = scaleRangef(throttle, 0.0f, 1.0f, motorMix[i] + motorMixDelta, motorMix[i] - motorMixDelta);
|
||||
} else {
|
||||
motorMix[i] = scaleRangef(throttle, 0.0f, 1.0f, motorMix[i] + fabsf(motorMix[i]), motorMix[i] - fabsf(motorMix[i]));
|
||||
motorMix[i] = scaleRangef(throttle, 0.0f, 1.0f, motorMix[i] + ABS(motorMix[i]), motorMix[i] - ABS(motorMix[i]));
|
||||
}
|
||||
}
|
||||
motorMix[i] /= motorMixNormalizationFactor;
|
||||
}
|
||||
motorMix[i] *= motorMixNormalizationFactor;
|
||||
|
||||
maxMotor = MAX(motorMix[i], maxMotor);
|
||||
minMotor = MIN(motorMix[i], minMotor);
|
||||
}
|
||||
|
||||
// constrain throttle so it won't clip any outputs
|
||||
throttle = constrainf(throttle, -minMotor, 1.0f - maxMotor);
|
||||
}
|
||||
|
||||
static void applyMixerAdjustment(float *motorMix, const float motorMixMin, const float motorMixMax, const bool airmodeEnabled)
|
||||
{
|
||||
static void applyMixerAdjustment(float *motorMix, const float motorMixMin, const float motorMixMax, const bool airmodeEnabled) {
|
||||
#ifdef USE_AIRMODE_LPF
|
||||
const float unadjustedThrottle = throttle;
|
||||
throttle += pidGetAirmodeThrottleOffset();
|
||||
float airmodeThrottleChange = 0.0f;
|
||||
float airmodeThrottleChange = 0;
|
||||
#endif
|
||||
float airmodeTransitionPercent = 1.0f;
|
||||
|
||||
if (!airmodeEnabled && throttle < 0.5f) {
|
||||
// this scales the motor mix authority to be 0.5 at 0 throttle, and 1.0 at 0.5 throttle as airmode off intended for things to work.
|
||||
// also lays the groundwork for how an airmode percent would work.
|
||||
airmodeTransitionPercent = scaleRangef(throttle, 0.0f, 0.5f, 0.5f, 1.0f); // 0.5 throttle is full transition, and 0.0 throttle is 50% airmodeTransitionPercent
|
||||
}
|
||||
|
||||
const float motorMixNormalizationFactor = motorMixRange > 1.0f ? airmodeTransitionPercent / motorMixRange : airmodeTransitionPercent;
|
||||
|
||||
if (motorMixRange > 1.0f) {
|
||||
for (int i = 0; i < mixerRuntime.motorCount; i++) {
|
||||
motorMix[i] *= motorMixNormalizationFactor;
|
||||
motorMix[i] /= motorMixRange;
|
||||
}
|
||||
// Get the maximum correction by setting offset to center when airmode enabled
|
||||
if (airmodeEnabled) {
|
||||
throttle = 0.5f;
|
||||
}
|
||||
} else {
|
||||
if (airmodeEnabled || throttle > 0.5f) {
|
||||
throttle = constrainf(throttle, -motorMixMin, 1.0f - motorMixMax);
|
||||
#ifdef USE_AIRMODE_LPF
|
||||
airmodeThrottleChange = constrainf(unadjustedThrottle, -motorMixMin, 1.0f - motorMixMax) - unadjustedThrottle;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
const float normalizedMotorMixMin = motorMixMin * motorMixNormalizationFactor;
|
||||
const float normalizedMotorMixMax = motorMixMax * motorMixNormalizationFactor;
|
||||
throttle = constrainf(throttle, -normalizedMotorMixMin, 1.0f - normalizedMotorMixMax);
|
||||
|
||||
#ifdef USE_AIRMODE_LPF
|
||||
airmodeThrottleChange = constrainf(unadjustedThrottle, -normalizedMotorMixMin, 1.0f - normalizedMotorMixMax) - unadjustedThrottle;
|
||||
pidUpdateAirmodeLpf(airmodeThrottleChange);
|
||||
#endif
|
||||
}
|
||||
|
@ -571,13 +745,12 @@ FAST_CODE_NOINLINE void mixTable(timeUs_t currentTimeUs)
|
|||
// reduce throttle to offset additional motor output
|
||||
throttle = pidCompensateThrustLinearization(throttle);
|
||||
#endif
|
||||
|
||||
applyRPMLimiter();
|
||||
// Find roll/pitch/yaw desired output
|
||||
// ??? Where is the optimal location for this code?
|
||||
float motorMix[MAX_SUPPORTED_MOTORS];
|
||||
float motorMixMax = 0, motorMixMin = 0;
|
||||
for (int i = 0; i < mixerRuntime.motorCount; i++) {
|
||||
|
||||
float mix =
|
||||
scaledAxisPidRoll * activeMixer[i].roll +
|
||||
scaledAxisPidPitch * activeMixer[i].pitch +
|
||||
|
|
|
@ -89,6 +89,22 @@ typedef struct mixerConfig_s {
|
|||
bool yaw_motors_reversed;
|
||||
uint8_t crashflip_motor_percent;
|
||||
uint8_t crashflip_expo;
|
||||
bool govenor;
|
||||
uint16_t govenor_p;
|
||||
uint16_t govenor_i;
|
||||
uint16_t govenor_d;
|
||||
uint16_t govenor_rpm_limit;
|
||||
uint16_t govenor_rpm_afterburner;
|
||||
uint8_t govenor_rpm_afterburner_duration;
|
||||
bool govenor_rpm_afterburner_reset;
|
||||
bool govenor_rpm_afterburner_hold_to_use;
|
||||
uint8_t govenor_rpm_afterburner_tank_count;
|
||||
uint16_t govenor_acceleration_limit;
|
||||
uint16_t govenor_deceleration_limit;
|
||||
uint16_t govenor_k_factor;
|
||||
bool govenor_rpm_linearization;
|
||||
uint16_t govenorThrottleLimitLearningTimeMS;
|
||||
uint16_t govenor_idle_rpm;
|
||||
uint8_t mixer_type;
|
||||
} mixerConfig_t;
|
||||
|
||||
|
@ -125,3 +141,6 @@ bool isFixedWing(void);
|
|||
|
||||
float getMotorOutputLow(void);
|
||||
float getMotorOutputHigh(void);
|
||||
|
||||
uint8_t getAfterburnerTanksRemaining(void);
|
||||
float getAfterburnerTankPercent(void);
|
|
@ -50,7 +50,22 @@ PG_RESET_TEMPLATE(mixerConfig_t, mixerConfig,
|
|||
.mixerMode = DEFAULT_MIXER,
|
||||
.yaw_motors_reversed = false,
|
||||
.crashflip_motor_percent = 0,
|
||||
.crashflip_expo = 35,
|
||||
.crashflip_expo = 0,
|
||||
.govenor = true,
|
||||
.govenor_p = 20.0f,
|
||||
.govenor_i = 17.0f,
|
||||
.govenor_d = 3.0f,
|
||||
.govenor_rpm_linearization = true,
|
||||
.govenor_idle_rpm = 60,
|
||||
.govenor_acceleration_limit = 60,
|
||||
.govenor_deceleration_limit = 60,
|
||||
.govenor_k_factor = 3000,
|
||||
.govenor_rpm_limit = 305.0f,
|
||||
.govenor_rpm_afterburner = 45,
|
||||
.govenor_rpm_afterburner_duration = 5,
|
||||
.govenor_rpm_afterburner_reset = false,
|
||||
.govenor_rpm_afterburner_hold_to_use = false,
|
||||
.govenor_rpm_afterburner_tank_count = 1,
|
||||
.mixer_type = MIXER_LEGACY,
|
||||
);
|
||||
|
||||
|
@ -310,6 +325,43 @@ void mixerInitProfile(void)
|
|||
mixerRuntime.motorOutputLow = DSHOT_MIN_THROTTLE; // Override value set by initEscEndpoints to allow zero motor drive
|
||||
}
|
||||
#endif
|
||||
mixerRuntime.govenorExpectedThrottleLimit = 1.0f;
|
||||
|
||||
//Street League spec settings
|
||||
|
||||
//Locked rpm settings
|
||||
mixerRuntime.govenorEnabled = true;
|
||||
mixerRuntime.rpmLinearization = true;
|
||||
mixerRuntime.motorPoleCount = 12;
|
||||
mixerRuntime.afterburnerReset = false;
|
||||
mixerRuntime.afterburnerHoldToBoost = false;
|
||||
|
||||
//Unlocked rpm settings
|
||||
// mixerRuntime.govenorEnabled = mixerConfig()->govenor;
|
||||
// mixerRuntime.rpmLinearization = mixerConfig()->govenor_rpm_linearization;
|
||||
// mixerRuntime.motorPoleCount = motorConfig()->motorPoleCount;
|
||||
// mixerRuntime.afterburnerReset = mixerConfig()->govenor_rpm_afterburner_reset;
|
||||
// mixerRuntime.afterburnerHoldToBoost = mixerConfig()->govenor_rpm_afterburner_hold_to_use;
|
||||
|
||||
|
||||
mixerRuntime.govenorPGain = mixerConfig()->govenor_p * 0.0000015f;
|
||||
mixerRuntime.govenorIGain = mixerConfig()->govenor_i * 0.0001f * pidGetDT();
|
||||
mixerRuntime.govenorDGain = mixerConfig()->govenor_d * 0.00000003f * pidGetPidFrequency();
|
||||
mixerRuntime.govenorAccelerationLimit = mixerConfig()->govenor_acceleration_limit * 1000.0f * pidGetDT();
|
||||
mixerRuntime.govenorDecelerationLimit = mixerConfig()->govenor_deceleration_limit * 1000.0f * pidGetDT();
|
||||
mixerRuntime.govenorKFactor = mixerConfig()->govenor_k_factor;
|
||||
mixerRuntime.afterburnerRPM = mixerConfig()->govenor_rpm_afterburner;
|
||||
mixerRuntime.afterburnerDuration = mixerConfig()->govenor_rpm_afterburner_duration;
|
||||
mixerRuntime.afterburnerTanksRemaining = mixerConfig()->govenor_rpm_afterburner_tank_count;
|
||||
mixerRuntime.RPMLimit = mixerConfig()->govenor_rpm_limit;
|
||||
|
||||
mixerRuntime.govenorI = 0;
|
||||
mixerRuntime.afterburnerTankPercent = 100.0f;
|
||||
mixerRuntime.afterburnerInitiated = false;
|
||||
mixerRuntime.govenorPreviousSmoothedRPMError = 0;
|
||||
mixerRuntime.govenorDelayK = mixerRuntime.govenorKFactor * pidGetDT() / 20.0f;
|
||||
mixerRuntime.govenorLearningThrottleK = 0.5 / (pidGetPidFrequency() * mixerConfig()->govenorThrottleLimitLearningTimeMS / 1000); // 0.5 = value ^ (4000 * time) 0.99^(4000*(20/1000))
|
||||
mixerRuntime.govenor_init = false;
|
||||
|
||||
#if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION)
|
||||
mixerRuntime.vbatSagCompensationFactor = 0.0f;
|
||||
|
@ -410,6 +462,7 @@ void mixerInit(mixerMode_e mixerMode)
|
|||
mixerRuntime.feature3dEnabled = featureIsEnabled(FEATURE_3D);
|
||||
|
||||
initEscEndpoints();
|
||||
|
||||
#ifdef USE_SERVOS
|
||||
if (mixerIsTricopter()) {
|
||||
mixerTricopterInit();
|
||||
|
@ -445,9 +498,11 @@ bool mixerModeIsFixedWing(mixerMode_e mixerMode)
|
|||
case MIXER_AIRPLANE:
|
||||
case MIXER_CUSTOM_AIRPLANE:
|
||||
return true;
|
||||
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,33 @@ typedef struct mixerRuntime_s {
|
|||
float vbatFull;
|
||||
float vbatRangeToCompensate;
|
||||
#endif
|
||||
float govenorExpectedThrottleLimit;
|
||||
float govenorAccelerationLimit;
|
||||
float govenorDecelerationLimit;
|
||||
float govenorKFactor;
|
||||
float prevAverageRPM;
|
||||
float govenorPreviousSmoothedRPMError;
|
||||
float minRPMDelayK;
|
||||
float afterburnerRPM;
|
||||
bool afterburnerReset;
|
||||
bool afterburnerHoldToBoost;
|
||||
float afterburnerDuration;
|
||||
float afterburnerTankPercent;
|
||||
bool afterburnerInitiated;
|
||||
uint8_t afterburnerTanksRemaining;
|
||||
bool rpmLinearization;
|
||||
float RPMLimit;
|
||||
int motorPoleCount;
|
||||
bool govenorEnabled;
|
||||
float govenorI;
|
||||
float govenorPGain;
|
||||
float govenorIGain;
|
||||
float govenorDGain;
|
||||
float govenorPreviousSmoothedRPM;
|
||||
float govenorPreviousRPMLimit;
|
||||
float govenorDelayK;
|
||||
float govenorLearningThrottleK;
|
||||
bool govenor_init;
|
||||
} mixerRuntime_t;
|
||||
|
||||
extern mixerRuntime_t mixerRuntime;
|
|
@ -100,7 +100,8 @@ static const box_t boxes[CHECKBOX_ITEM_COUNT] = {
|
|||
{ .boxId = BOXMSPOVERRIDE, .boxName = "MSP OVERRIDE", .permanentId = 50},
|
||||
{ .boxId = BOXSTICKCOMMANDDISABLE, .boxName = "STICK COMMANDS DISABLE", .permanentId = 51},
|
||||
{ .boxId = BOXBEEPERMUTE, .boxName = "BEEPER MUTE", .permanentId = 52},
|
||||
{ .boxId = BOXREADY, .boxName = "READY", .permanentId = 53}
|
||||
{ .boxId = BOXREADY, .boxName = "READY", .permanentId = 53},
|
||||
{ .boxId = BOXBOOST, .boxName = "BOOST", .permanentId = 54},
|
||||
};
|
||||
|
||||
// mask of enabled IDs, calculated on startup based on enabled features. boxId_e is used as bit index
|
||||
|
@ -187,6 +188,7 @@ void initActiveBoxIds(void)
|
|||
#define BME(boxId) do { bitArraySet(&ena, boxId); } while (0)
|
||||
BME(BOXARM);
|
||||
BME(BOXPREARM);
|
||||
BME(BOXBOOST);
|
||||
if (!featureIsEnabled(FEATURE_AIRMODE)) {
|
||||
BME(BOXAIRMODE);
|
||||
}
|
||||
|
|
|
@ -273,6 +273,7 @@ typedef enum {
|
|||
OSD_WARNING_RSSI_DBM,
|
||||
OSD_WARNING_OVER_CAP,
|
||||
OSD_WARNING_RSNR,
|
||||
OSD_WARNING_BOOST_ENGAGED,
|
||||
OSD_WARNING_COUNT // MUST BE LAST
|
||||
} osdWarningsFlags_e;
|
||||
|
||||
|
|
|
@ -146,6 +146,7 @@
|
|||
#include "flight/position.h"
|
||||
#include "flight/imu.h"
|
||||
#include "flight/mixer.h"
|
||||
#include "flight/mixer_init.h"
|
||||
#include "flight/pid.h"
|
||||
|
||||
#include "io/gps.h"
|
||||
|
@ -1270,10 +1271,13 @@ static void osdElementMainBatteryUsage(osdElementParms_t *element)
|
|||
{
|
||||
uint8_t remainingCapacityBars = 0;
|
||||
|
||||
if (batteryConfig()->batteryCapacity) {
|
||||
const float batteryRemaining = constrain(batteryConfig()->batteryCapacity - displayBasis, 0, batteryConfig()->batteryCapacity);
|
||||
remainingCapacityBars = ceilf((batteryRemaining / (batteryConfig()->batteryCapacity / MAIN_BATT_USAGE_STEPS)));
|
||||
}
|
||||
//if (batteryConfig()->batteryCapacity) {
|
||||
//const float batteryRemaining = constrain(batteryConfig()->batteryCapacity - displayBasis, 0, batteryConfig()->batteryCapacity);
|
||||
//remainingCapacityBars = ceilf((batteryRemaining / (batteryConfig()->batteryCapacity / MAIN_BATT_USAGE_STEPS)));
|
||||
const float maxBoost = 100.0f;
|
||||
const float batteryRemaining = constrainf(getAfterburnerTankPercent(), 0, maxBoost);
|
||||
remainingCapacityBars = ceilf((batteryRemaining / (maxBoost / MAIN_BATT_USAGE_STEPS)));
|
||||
//}
|
||||
|
||||
// Create empty battery indicator bar
|
||||
element->buff[0] = SYM_PB_START;
|
||||
|
@ -1284,7 +1288,12 @@ static void osdElementMainBatteryUsage(osdElementParms_t *element)
|
|||
if (remainingCapacityBars > 0 && remainingCapacityBars < MAIN_BATT_USAGE_STEPS) {
|
||||
element->buff[1 + remainingCapacityBars] = SYM_PB_END;
|
||||
}
|
||||
element->buff[MAIN_BATT_USAGE_STEPS+2] = '\0';
|
||||
//tfp_sprintf(element->buff, element->buff+"%d", mixerRuntime.afterburnerTanksRemaining);
|
||||
char tanksRemainingStr[1];
|
||||
tfp_sprintf(tanksRemainingStr, "%d", getAfterburnerTanksRemaining());
|
||||
element->buff[MAIN_BATT_USAGE_STEPS+2] = tanksRemainingStr[0];
|
||||
element->buff[MAIN_BATT_USAGE_STEPS+3] = '\0';
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "flight/gps_rescue.h"
|
||||
#include "flight/imu.h"
|
||||
#include "flight/mixer.h"
|
||||
#include "flight/mixer_init.h"
|
||||
#include "flight/pid.h"
|
||||
|
||||
#include "io/beeper.h"
|
||||
|
@ -369,7 +370,12 @@ void renderOsdWarning(char *warningText, bool *blinking, uint8_t *displayAttr)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mixerRuntime.afterburnerInitiated) {
|
||||
tfp_sprintf(warningText, "BOOST ENGAGED");
|
||||
*displayAttr = DISPLAYPORT_SEVERITY_WARNING;
|
||||
*blinking = true;
|
||||
return;
|
||||
}
|
||||
if (osdWarnGetState(OSD_WARNING_BATTERY_WARNING) && batteryState == BATTERY_WARNING) {
|
||||
tfp_sprintf(warningText, "LOW BATTERY");
|
||||
*displayAttr = DISPLAYPORT_SEVERITY_WARNING;
|
||||
|
@ -377,6 +383,7 @@ void renderOsdWarning(char *warningText, bool *blinking, uint8_t *displayAttr)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_RC_SMOOTHING_FILTER
|
||||
// Show warning if rc smoothing hasn't initialized the filters
|
||||
if (osdWarnGetState(OSD_WARNING_RC_SMOOTHING) && ARMING_FLAG(ARMED) && !rcSmoothingInitializationComplete()) {
|
||||
|
@ -420,6 +427,13 @@ void renderOsdWarning(char *warningText, bool *blinking, uint8_t *displayAttr)
|
|||
return;
|
||||
}
|
||||
|
||||
// Boost mode
|
||||
if (IS_RC_MODE_ACTIVE(BOXBOOST)) {
|
||||
tfp_sprintf(warningText, "BOOST ENGAGED");
|
||||
*displayAttr = DISPLAYPORT_SEVERITY_INFO;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // USE_OSD
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue