BetaFlight模块设计之三十三:Pid模块分析

2023-05-16

BetaFlight模块设计之三十三:Pid模块分析

  • Pid模块
    • 1. Pid_audio子模块
    • 2. Pid_init子模块
    • 3. Pid算法子模块
      • 3.1 TPA模式
      • 3.2 飞行模式
      • 3.3 Launch模式
      • 3.4 AcroTrainer模式
      • 3.5 antiGravity模式
      • 3.6 pidController函数

基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。

Pid模块

描述:目前主要用于文件系统的相关维护例程。

从代码资源角度Pid模块可以分为三个子模块:

  1. Pid_init子模块
  2. Pid_audio子模块
  3. Pid算法子模块
Pid模块代码
 ├──> src\main\flight
 │   ├──> Pid.c
 │   └──> Pid.h
 │   ├──> Pid_init.c
 │   └──> Pid_init.h
 └──> src\main\io
     ├──> Pidaudio.c
     └──> Pidaudio.h

1. Pid_audio子模块

根据PID的工作状态,FC在耳机上增加一个声音,让飞控操作人员更加了解PID运行状态。

  1. PIDSUM_X: Roll
  2. PIDSUM_Y: Pitch
  3. PIDSUM_XY: (Roll + Pitch) / 2
typedef enum {
    PID_AUDIO_OFF = 0,
    PID_AUDIO_PIDSUM_X,
    PID_AUDIO_PIDSUM_Y,
    PID_AUDIO_PIDSUM_XY,
} pidAudioModes_e;
void pidAudioUpdate(void);
void pidAudioInit(void);
pidAudioModes_e pidAudioGetMode(void);
void pidAudioSetMode(pidAudioModes_e mode);

以下机型使用该子模块:

./src/main/target/SPRACINGF7DUAL/target.h:183:#define USE_PID_AUDIO
./src/main/target/SPRACINGH7EXTREME/target.h:171:#define USE_PID_AUDIO

注1:SPRACINGF7DUAL机型,BetaFlight支持情况介绍
注2:SPRACINGH7EXTREME机型

2. Pid_init子模块

Pid算法初始化pidInit函数(包含了pidInitFilters和pidInitConfig)。

void pidInit(const pidProfile_t *pidProfile);
void pidInitFilters(const pidProfile_t *pidProfile);
void pidInitConfig(const pidProfile_t *pidProfile);

针对USE_RC_SMOOTHING_FILTER下pt3Filter_t feedforwardPt3[XYZ_AXIS_COUNT]进行pt3FilterGain(截止频率和dT)更新。

void pidInitFeedforwardLpf(uint16_t filterCutoff, uint8_t debugAxis);
void pidUpdateFeedforwardLpf(uint16_t filterCutoff);

处理CMS菜单复制Pid Profile的执行函数。

void pidCopyProfile(uint8_t dstPidProfileIndex, uint8_t srcPidProfileIndex);

3. Pid算法子模块

BetaFlight模块设计之二十八:MainPidLoop任务分析中最核心的是Pid算法。

通过Pid_init子模块对算法进行了全局(变量)初始化,在MainPidLoop任务中,通过subTaskPidController子任务调用pidController算法函数。

// Betaflight pid controller, which will be maintained in the future with additional features specialised for current (mini) multirotor usage.
// Based on 2DOF reference design (matlab)
void pidController(const pidProfile_t *pidProfile, timeUs_t currentTimeUs);

注:这里也提到了一点“specialised for current (mini) multirotor usage”,对大机架的支持也许并没有那么好,比如:F450

3.1 TPA模式

TPA 是Throttle PID Attenuation的缩写。

tpaFactor主要将会影响PD参量,减少油门越大带入的干扰越大的情况。
TPA效果图

typedef enum {
    TPA_MODE_PD,  //影响PD参数
    TPA_MODE_D    //仅影响D参数(默认配置)
} tpaMode_e;

pidUpdateTpaFactor
 ├──> tpaBreakpoint = (currentControlRateProfile->tpa_breakpoint - 1000) / 1000.0f;
 ├──> tpaRate = currentControlRateProfile->tpa_rate / 100.0f;
 ├──> <throttle > tpaBreakpoint><throttle < 1.0f>
 │   └──> tpaRate *= (throttle - tpaBreakpoint) / (1.0f - tpaBreakpoint);  //超过tpaBreakpoint时,随着油门增加,tpaFactor逐渐线性降低至1-tpaRate
 ├──> <!(throttle > tpaBreakpoint)>  //没有超过tpaBreakpoint时,tpaFactor=1
 │   └──> tpaRate = 0.0f;
 └──> pidRuntime.tpaFactor = 1.0f - tpaRate;

3.2 飞行模式

从代码角度分析,实际飞控存在以下三种飞行模式:

  1. LEVEL_MODE_OFF:手动(Acro)
  2. LEVEL_MODE_R:Pitch手动,Roll自稳(NFE level race mode)
  3. LEVEL_MODE_RP:Pitch + Roll自稳(Angle, Horizon)
typedef enum {
    LEVEL_MODE_OFF = 0,
    LEVEL_MODE_R,
    LEVEL_MODE_RP,
} levelMode_e;

pidLevel  // LEVEL_MODE_RP: Angle, Horizon
 ├──> angle = pidProfile->levelAngleLimit * getLevelModeRcDeflection(axis);
 ├──> <USE_GPS_RESCUE>
 │   └──> angle += gpsRescueAngle[axis] / 100; // ANGLE IS IN CENTIDEGREES
 ├──> angle = constrainf(angle, -pidProfile->levelAngleLimit, pidProfile->levelAngleLimit);
 ├──> errorAngle = angle - ((attitude.raw[axis] - angleTrim->raw[axis]) / 10.0f);
 ├──> <FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(GPS_RESCUE_MODE)>
 │   └──> currentPidSetpoint = errorAngle * pidRuntime.levelGain; // ANGLE mode - control is angle based
 ├──> <!(FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(GPS_RESCUE_MODE))>
 │   ├──> horizonLevelStrength = calcHorizonLevelStrength();
 │   └──> currentPidSetpoint = currentPidSetpoint + (errorAngle * pidRuntime.horizonGain * horizonLevelStrength);
 └──> return currentPidSetpoint;

3.3 Launch模式

主要用于方向性的可控起飞。

typedef enum {
    LAUNCH_CONTROL_MODE_NORMAL = 0,
    LAUNCH_CONTROL_MODE_PITCHONLY,
    LAUNCH_CONTROL_MODE_FULL,
    LAUNCH_CONTROL_MODE_COUNT // must be the last element
} launchControlMode_e;


#define LAUNCH_CONTROL_MAX_RATE 100.0f
#define LAUNCH_CONTROL_MIN_RATE 5.0f
#define LAUNCH_CONTROL_ANGLE_WINDOW 10.0f  // The remaining angle degrees where rate dampening starts

// Use the FAST_CODE_NOINLINE directive to avoid this code from being inlined into ITCM RAM to avoid overflow.
// The impact is possibly slightly slower performance on F7/H7 but they have more than enough
// processing power that it should be a non-issue.
static FAST_CODE_NOINLINE float applyLaunchControl(int axis, const rollAndPitchTrims_t *angleTrim)
{
    float ret = 0.0f;

    // Scale the rates based on stick deflection only. Fixed rates with a max of 100deg/sec
    // reached at 50% stick deflection. This keeps the launch control positioning consistent
    // regardless of the user's rates.
    if ((axis == FD_PITCH) || (pidRuntime.launchControlMode != LAUNCH_CONTROL_MODE_PITCHONLY)) {
        const float stickDeflection = constrainf(getRcDeflection(axis), -0.5f, 0.5f);
        ret = LAUNCH_CONTROL_MAX_RATE * stickDeflection * 2;
    }

#if defined(USE_ACC)
    // If ACC is enabled and a limit angle is set, then try to limit forward tilt
    // to that angle and slow down the rate as the limit is approached to reduce overshoot
    if ((axis == FD_PITCH) && (pidRuntime.launchControlAngleLimit > 0) && (ret > 0)) {
        const float currentAngle = (attitude.raw[axis] - angleTrim->raw[axis]) / 10.0f;
        if (currentAngle >= pidRuntime.launchControlAngleLimit) {
            ret = 0.0f;
        } else {
            //for the last 10 degrees scale the rate from the current input to 5 dps
            const float angleDelta = pidRuntime.launchControlAngleLimit - currentAngle;
            if (angleDelta <= LAUNCH_CONTROL_ANGLE_WINDOW) {
                ret = scaleRangef(angleDelta, 0, LAUNCH_CONTROL_ANGLE_WINDOW, LAUNCH_CONTROL_MIN_RATE, ret);
            }
        }
    }
#else
    UNUSED(angleTrim);
#endif

    return ret;
}

Launch control相关设置目前只能通过Cli命令行,详见链接。

set launch_control_mode = PITCHONLY
set launch_trigger_allow_reset = ON
set launch_trigger_throttle_percent = 30
set launch_angle_limit = 8
set osd_warn_launch_control = ON

3.4 AcroTrainer模式

该模式主要为了用于训练Acro手动模式,避免炸机的一种模式。

pidAcroTrainerInit
 ├──> pidRuntime.acroTrainerAxisState[FD_ROLL] = 0;
 └──> pidRuntime.acroTrainerAxisState[FD_PITCH] = 0;

pidSetAcroTrainerState
 └──> <pidRuntime.acroTrainerActive != newState>
     ├──> <newState>
     │   └──> pidAcroTrainerInit
     └──> pidRuntime.acroTrainerActive = newState;

// Acro Trainer - Manipulate the setPoint to limit axis angle while in acro mode
// There are three states:
// 1. Current angle has exceeded limit
//    Apply correction to return to limit (similar to pidLevel)
// 2. Future overflow has been projected based on current angle and gyro rate
//    Manage the setPoint to control the gyro rate as the actual angle  approaches the limit (try to prevent overshoot)
// 3. If no potential overflow is detected, then return the original setPoint
applyAcroTrainer
 ├──> <!FLIGHT_MODE(ANGLE_MODE) && !FLIGHT_MODE(HORIZON_MODE) && !FLIGHT_MODE(GPS_RESCUE_MODE)>
 │   ├──> setpointSign = acroTrainerSign(setPoint);
 │   ├──> angleSign = acroTrainerSign(currentAngle);
 │   ├──> currentAngle = (attitude.raw[axis] - angleTrim->raw[axis]) / 10.0f;
 │   ├──> <(pidRuntime.acroTrainerAxisState[axis] != 0) && (pidRuntime.acroTrainerAxisState[axis] != setpointSign>
 │   │   └──> pidRuntime.acroTrainerAxisState[axis] = 0;
          ----- 角度超过允许最大值:重置I
 │   ├──> <(fabsf(currentAngle) > pidRuntime.acroTrainerAngleLimit) && (pidRuntime.acroTrainerAxisState[axis] == 0)><angleSign == setpointSign>
 │   │   ├──> pidRuntime.acroTrainerAxisState[axis] = angleSign;
 │   │   └──> resetIterm = true;
          ----- 角度超过允许最大值:纠偏返回setpoint
 │   ├──> <pidRuntime.acroTrainerAxisState[axis] != 0>
 │   │   └──> ret = constrainf(((pidRuntime.acroTrainerAngleLimit * angleSign) - currentAngle) * pidRuntime.acroTrainerGain, -ACRO_TRAINER_SETPOINT_LIMIT, ACRO_TRAINER_SETPOINT_LIMIT);
          ----- 角度预测:若超出重置I,返回更新后的setpoint;反之返回原始setpoint。
 │   ├──> <pidRuntime.acroTrainerAxisState[axis] == 0> 
 │   │   ├──> checkInterval = constrainf(fabsf(gyro.gyroADCf[axis]) / ACRO_TRAINER_LOOKAHEAD_RATE_LIMIT, 0.0f, 1.0f) * pidRuntime.acroTrainerLookaheadTime;
 │   │   ├──> projectedAngle = (gyro.gyroADCf[axis] * checkInterval) + currentAngle;
 │   │   ├──> projectedAngleSign = acroTrainerSign(projectedAngle); //若条件不满足,则满足训练条件
 │   │   └──> <(fabsf(projectedAngle) > pidRuntime.acroTrainerAngleLimit) && (projectedAngleSign == setpointSign)>
 │   │       ├──> ret = ((pidRuntime.acroTrainerAngleLimit * projectedAngleSign) - projectedAngle) * pidRuntime.acroTrainerGain;
 │   │       └──> resetIterm = true;
 │   └──> <resetIterm>
 │       └──> pidData[axis].I = 0;
 └──> return setPoint

3.5 antiGravity模式

主要是为了解决突然加速和减速对飞机稳定性的影响:

  1. 在processRcCommand处理中,checkForThrottleErrorResetState会根据Rc命令变化量,在ANTI_GRAVITY_STEP模式下调整pidRuntime.itermAccelerator;
  2. 在subTaskMotorUpdate的mixTable中,pidUpdateAntiGravityThrottleFilter在ANTI_GRAVITY_SMOOTH调整更新pidRuntime.antiGravityPBoost;
  3. pidController将agGain叠加影响到I-Term上,影响稳定性;
typedef enum {
    ANTI_GRAVITY_SMOOTH,  //默认状态
    ANTI_GRAVITY_STEP
} antiGravityMode_e;

checkForThrottleErrorResetState
 ├──> throttleVelocityThreshold = (featureIsEnabled(FEATURE_3D)) ? currentPidProfile->itermThrottleThreshold / 2 : currentPidProfile->itermThrottleThreshold;
 ├──> rcCommandThrottlePrevious[index++] = rcCommand[THROTTLE];
 ├──> rcCommandSpeed = rcCommand[THROTTLE] - rcCommandThrottlePrevious[index];
 └──> <currentPidProfile->antiGravityMode == ANTI_GRAVITY_STEP>
     ├──> <ABS(rcCommandSpeed) > throttleVelocityThreshold>
     │   └──> pidSetItermAccelerator(CONVERT_PARAMETER_TO_FLOAT(currentPidProfile->itermAcceleratorGain));
     └──> <!(ABS(rcCommandSpeed) > throttleVelocityThreshold)>
         └──> pidSetItermAccelerator(0.0f);


pidUpdateAntiGravityThrottleFilter
 └──> <pidRuntime.antiGravityMode == ANTI_GRAVITY_SMOOTH>
     ├──> antiGravityThrottleLpf = pt1FilterApply(&pidRuntime.antiGravityThrottleLpf, throttle);
     ├──> <throttle < 0.5f>
     │   └──> pidRuntime.antiGravityPBoost = 0.5f - throttle;  // focus P boost on low throttle range only
     ├──> <!(throttle < 0.5f)>
     │   └──> pidRuntime.antiGravityPBoost = 0.0f;
     ├──> <antiGravityThrottleLpf < throttle>
     │   └──> pidRuntime.antiGravityPBoost *= 0.5f;  // use lowpass to identify start of a throttle up, use this to reduce boost at start by half
     ├──> pidRuntime.antiGravityThrottleHpf = fabsf(throttle - antiGravityThrottleLpf);
     ├──> pidRuntime.antiGravityPBoost = pidRuntime.antiGravityPBoost * pidRuntime.antiGravityThrottleHpf;  // high-passed throttle focuses boost on faster throttle changes
     └──> pidRuntime.antiGravityPBoost = pt1FilterApply(&pidRuntime.antiGravitySmoothLpf, pidRuntime.antiGravityPBoost);  // smooth the P boost at 3hz to remove the jagged edges and prolong the effect after throttle stops

3.6 pidController函数

PID运算主要在pidController这个函数中实现,但由于历史及全局变量诸多问题,一上来就从整体来看是比较复杂的。

这里做了一些精简和排序,并将部分特性相关的内容抽取出来介绍,希望后续进一步结合实际效果来看每个特性对PID的细节影响。

PID控制算法外,还增加了以下特性:

  1. FeedForward特性
  2. antiGravity特性
  3. TPA特性
  4. AutoLevel特性(Race Mode, Angle Mode, Horizon Mode)
  5. Launch特性
pidController
 ├──> [ -----Basic status and settings ] 
 │   ├──> angleTrim = &accelerometerConfig()->accelerometerTrims;
 │   ├──> <USE_YAW_SPIN_RECOVERY>
 │   │   └──> yawSpinActive = gyroYawSpinDetected();
 │   ├──> launchControlActive = isLaunchControlActive();
 │   └──> <USE_ACC>
 │       ├──> gpsRescueIsActive = FLIGHT_MODE(GPS_RESCUE_MODE);
 │       ├──> <FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE) || gpsRescueIsActive>
 │       │   ├──> <pidRuntime.levelRaceMode && !gpsRescueIsActive>
 │       │   │   └──> levelMode = LEVEL_MODE_R;
 │       │   └──> <!(pidRuntime.levelRaceMode && !gpsRescueIsActive)>
 │       │       └──> levelMode = LEVEL_MODE_RP;
 │       ├──> <!(FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE) || gpsRescueIsActive)>
 │       │   └──>levelMode = LEVEL_MODE_OFF;
 │       ├──> <levelMode><(levelModeStartTimeUs == 0) || (gpsRescueIsActive && !gpsRescuePreviousState)>
 │       │   └──> levelModeStartTimeUs = currentTimeUs;
 │       ├──> <!levelMode>
 │       │   └──> levelModeStartTimeUs = 0;
 │       └──> gpsRescuePreviousState = gpsRescueIsActive;
 │
 ├──> [ -----Throttle PID Attenuation for P ] // pidUpdateTpaFactor update with throttle position
 │   ├──> <USE_TPA_MODE>
 │   │   └──> tpaFactorKp = (currentControlRateProfile->tpaMode == TPA_MODE_PD) ? pidRuntime.tpaFactor : 1.0f;
 │   └──> <!USE_TPA_MODE>
 │       └──> tpaFactorKp = pidRuntime.tpaFactor;
 │
 ├──> [ -----calculate dynamic i component ] // boost I for antigravity
 │   ├──> <(pidRuntime.antiGravityMode == ANTI_GRAVITY_SMOOTH) && pidRuntime.antiGravityEnabled>
 │   │   ├──> pidRuntime.itermAccelerator = pidRuntime.antiGravityThrottleHpf * 0.01f * pidRuntime.itermAcceleratorGain;
 │   │   ├──> pidRuntime.antiGravityPBoost *= pidRuntime.itermAcceleratorGain;  // users AG Gain changes P boost
 │   │   ├──> pidRuntime.itermAccelerator += pidRuntime.antiGravityPBoost * 0.05f; // add some percentage of that slower, longer acting P boost factor to prolong AG effect on iTerm
 │   │   └──> pidRuntime.antiGravityPBoost *= 0.02f;
 │   ├──> <!((pidRuntime.antiGravityMode == ANTI_GRAVITY_SMOOTH) && pidRuntime.antiGravityEnabled)>
 │   │   └──> pidRuntime.antiGravityPBoost = 0.0f;
 │   └──> agGain = pidRuntime.dT * pidRuntime.itermAccelerator * AG_KI;
 │
 ├──> dynCi = pidRuntime.dT;
 ├──> <pidRuntime.itermWindupPointInv > 1.0f>
 │   └──> dynCi *= constrainf((1.0f - getMotorMixRange()) * pidRuntime.itermWindupPointInv, 0.0f, 1.0f);
 ├──> <for (int axis = FD_ROLL; axis <= FD_YAW; ++axis)>  //Precalculate gyro deta for D-term
 │   ├──> gyroRateDterm[axis] = gyro.gyroADCf[axis];
 │   ├──> gyroRateDterm[axis] = pidRuntime.dtermNotchApplyFn((filter_t *) &pidRuntime.dtermNotch[axis], gyroRateDterm[axis]);
 │   ├──> gyroRateDterm[axis] = pidRuntime.dtermLowpassApplyFn((filter_t *) &pidRuntime.dtermLowpass[axis], gyroRateDterm[axis]);
 │   └──> gyroRateDterm[axis] = pidRuntime.dtermLowpass2ApplyFn((filter_t *) &pidRuntime.dtermLowpass2[axis], gyroRateDterm[axis]);
 ├──> rotateItermAndAxisError();
 ├──> <USE_RPM_FILTER>
 │   └──> rpmFilterUpdate
 ├──> <USE_FEEDFORWARD>
 │   ├──> newRcFrame = false;
 │   └──> <getShouldUpdateFeedforward()>
 │       └──> newRcFrame = true;
 │
 ├──> <for (int axis = FD_ROLL; axis <= FD_YAW; ++axis) // ----------PID controller----------
 │   ├──> [ -----calculate errorRate ]
 │   │   ├──> currentPidSetpoint = getSetpointRate(axis);
 │   │   ├──> <pidRuntime.maxVelocity[axis]>
 │   │   │   └──> currentPidSetpoint = accelerationLimit(axis, currentPidSetpoint);
 │   │   ├──> <USE_ACC>
 │   │   │   ├──> <LEVEL_MODE_OFF>
 │   │   │   │   └──> break;
 │   │   │   ├──> <LEVEL_MODE_R><axis == FD_PITCH>
 │   │   │   │   └──> break;
 │   │   │   ├──> <LEVEL_MODE_RP><axis == FD_YAW>
 │   │   │   │   └──> break;
 │   │   │   └──> currentPidSetpoint = pidLevel(axis, pidProfile, angleTrim, currentPidSetpoint); // LEVEL_MODE_RP: Angle, Horizon
 │   │   ├──> <USE_ACRO_TRAINER><(axis != FD_YAW) && pidRuntime.acroTrainerActive && !pidRuntime.inCrashRecoveryMode && !launchControlActive>
 │   │   │   └──> currentPidSetpoint = applyAcroTrainer(axis, angleTrim, currentPidSetpoint);
 │   │   ├──> <USE_LAUNCH_CONTROL><launchControlActive>
 │   │   │   ├──> <USE_ACC>
 │   │   │   │   └──> currentPidSetpoint = applyLaunchControl(axis, angleTrim);
 │   │   │   └──> <!USE_ACC>
 │   │   │       └──> currentPidSetpoint = applyLaunchControl(axis, NULL);
 │   │   ├──> <USE_YAW_SPIN_RECOVERY><(axis == FD_YAW) && yawSpinActive>
 │   │   │   └──> currentPidSetpoint = 0.0f;
 │   │   ├──> gyroRate = gyro.gyroADCf[axis]; // Process variable from gyro output in deg/sec
 │   │   ├──> errorRate = currentPidSetpoint - gyroRate; // r - y
 │   │   ├──> <USE_ACC>
 │   │   │   └──> handleCrashRecovery(pidProfile->crash_recovery, angleTrim, axis, currentTimeUs, gyroRate, &currentPidSetpoint, &errorRate);
 │   │   ├──> previousIterm = pidData[axis].I;
 │   │   ├──> itermErrorRate = errorRate;
 │   │   ├──> <USE_ABSOLUTE_CONTROL>
 │   │   │   └──> uncorrectedSetpoint = currentPidSetpoint;
 │   │   ├──> <USE_ITERM_RELAX><!launchControlActive && !pidRuntime.inCrashRecoveryMode>
 │   │   │   ├──> applyItermRelax(axis, previousIterm, gyroRate, &itermErrorRate, &currentPidSetpoint);
 │   │   │   └──> errorRate = currentPidSetpoint - gyroRate;
 │   │   └──> <USE_ABSOLUTE_CONTROL>
 │   │      └──> setpointCorrection = currentPidSetpoint - uncorrectedSetpoint;
 │   │
 │   ├──> <USE_FEEDFORWARD>
 │   │   └──> pidSetpointDelta = feedforwardApply(axis, newRcFrame, pidRuntime.feedforwardAveraging);
 │   ├──> pidRuntime.previousPidSetpoint[axis] = currentPidSetpoint;
 │   │
 │   ├──> [ -----calculate P component ]
 │   │   ├──> pidData[axis].P = pidRuntime.pidCoefficient[axis].Kp * errorRate * tpaFactorKp;
 │   │   └──> <axis == FD_YAW>
 │   │       └──> pidData[axis].P = pidRuntime.ptermYawLowpassApplyFn((filter_t *) &pidRuntime.ptermYawLowpass, pidData[axis].P);
 │   │
 │   ├──> [ -----calculate I component ]
 │   │   ├──> <launchControlActive>
 │   │   │   ├──> Ki = pidRuntime.launchControlKi;
 │   │   │   └──> axisDynCi = dynCi;
 │   │   ├──> <!launchControlActive>
 │   │   │   ├──> Ki = pidRuntime.pidCoefficient[axis].Ki;
 │   │   │   └──> axisDynCi = (axis == FD_YAW) ? dynCi : pidRuntime.dT; // only apply windup protection to yaw
 │   │   └──> pidData[axis].I = constrainf(previousIterm + (Ki * axisDynCi + agGain) * itermErrorRate, -pidRuntime.itermLimit, pidRuntime.itermLimit);
 │   │
 │   ├──> [ -----calculate D component ]
 │   │   ├──> <(pidRuntime.pidCoefficient[axis].Kd > 0) && !launchControlActive>
 │   │   │   ├──> delta = - (gyroRateDterm[axis] - previousGyroRateDterm[axis]) * pidRuntime.pidFrequency;
 │   │   │   ├──> preTpaD = pidRuntime.pidCoefficient[axis].Kd * delta;
 │   │   │   ├──> <USE_ACC><cmpTimeUs(currentTimeUs, levelModeStartTimeUs) > CRASH_RECOVERY_DETECTION_DELAY_US>
 │   │   │   │   └──> detectAndSetCrashRecovery(pidProfile->crash_recovery, axis, currentTimeUs, delta, errorRate);
 │   │   │   ├──> <USE_D_MIN>
 │   │   │   │   ├──> dMinFactor = 1.0f;
 │   │   │   │   ├──> <pidRuntime.dMinPercent[axis] > 0>
 │   │   │   │   └──> preTpaD *= dMinFactor;
 │   │   │   └──> pidData[axis].D = preTpaD * pidRuntime.tpaFactor;
 │   │   ├──> <!((pidRuntime.pidCoefficient[axis].Kd > 0) && !launchControlActive)>
 │   │   │   └──> pidData[axis].D = 0;
 │   │   └──> pidData[axis].D = 0;previousGyroRateDterm[axis] = gyroRateDterm[axis];
 │   ├──> <USE_ABSOLUTE_CONTROL>
 │   │   ├──> pidSetpointDelta += setpointCorrection - pidRuntime.oldSetpointCorrection[axis];
 │   │   └──> pidRuntime.oldSetpointCorrection[axis] = setpointCorrection;
 │   │
 │   ├──> [ -----calculate feedforward component ]
 │   │   ├──> feedforwardGain = launchControlActive ? 0.0f : pidRuntime.pidCoefficient[axis].Kf;
 │   │   ├──> <feedforwardGain > 0>
 │   │   │   ├──> feedforwardGain *= FLIGHT_MODE(ANGLE_MODE) ? 0.5f : 1.0f;
 │   │   │   ├──> feedForward = feedforwardGain * pidSetpointDelta * pidRuntime.pidFrequency;
 │   │   │   ├──> <USE_FEEDFORWARD>
 │   │   │   │   └──> pidData[axis].F = shouldApplyFeedforwardLimits(axis) ? applyFeedforwardLimit(axis, feedForward, pidRuntime.pidCoefficient[axis].Kp, currentPidSetpoint) : feedForward;
 │   │   │   ├──> <!USE_FEEDFORWARD>
 │   │   │   │   └──> pidData[axis].F = feedForward;
 │   │   │   └──> <USE_RC_SMOOTHING_FILTER>
 │   │   │       └──> pidData[axis].F = applyRcSmoothingFeedforwardFilter(axis, pidData[axis].F);
 │   │   └──> <!(feedforwardGain > 0)>
 │   │       └──> pidData[axis].F = 0;
 │   ├──> <USE_YAW_SPIN_RECOVERY><yawSpinActive>
 │   │   ├──> pidData[axis].I = 0;  // in yaw spin always disable I
 │   │   └──> <axis <= FD_PITCH>
 │   │       └──> pidData[axis].P = 0;pidData[axis].D = 0;pidData[axis].F = 0;
 │   ├──> <USE_LAUNCH_CONTROL><launchControlActive>
 │   │   ├──> launchControlYawItermLimit = (pidRuntime.launchControlMode == LAUNCH_CONTROL_MODE_FULL) ? LAUNCH_CONTROL_YAW_ITERM_LIMIT : 0;
 │   │   └──> pidData[FD_YAW].I = constrainf(pidData[FD_YAW].I, -launchControlYawItermLimit, launchControlYawItermLimit);
 │   │       └──> <pidRuntime.launchControlMode == LAUNCH_CONTROL_MODE_PITCHONLY>
 │   │           └──> pidData[FD_ROLL].P = 0;pidData[FD_ROLL].I = 0;pidData[FD_YAW].P = 0;pidData[FD_PITCH].I = MAX(0.0f, pidData[FD_PITCH].I);
 │   │
 │   └──> [ -----calculate pid sum ]
 │       ├──> agBoostAttenuator = fabsf(currentPidSetpoint) / 50.0f;
 │       ├──> agBoostAttenuator = MAX(agBoostAttenuator, 1.0f);
 │       ├──> agBoost = 1.0f + (pidRuntime.antiGravityPBoost / agBoostAttenuator);
 │       ├──> <axis != FD_YAW>
 │       │   └──> pidData[axis].P *= agBoost;
 │       ├──> pidSum = pidData[axis].P + pidData[axis].I + pidData[axis].D + pidData[axis].F;
 │       ├──> <USE_INTEGRATED_YAW_CONTROL>
 │       │   ├──> <axis == FD_YAW && pidRuntime.useIntegratedYaw>
 │       │   │   ├──> pidData[axis].Sum += pidSum * pidRuntime.dT * 100.0f;
 │       │   │   └──> pidData[axis].Sum -= pidData[axis].Sum * pidRuntime.integratedYawRelax / 100000.0f * pidRuntime.dT / 0.000125f;
 │       │   └──> <!(axis == FD_YAW && pidRuntime.useIntegratedYaw)>
 │       │       └──> pidData[axis].Sum = pidSum;
 │       └──> <!USE_INTEGRATED_YAW_CONTROL>
 │           └──> pidData[axis].Sum = pidSum;
 │
 ├──> <!pidRuntime.pidStabilisationEnabled || gyroOverflowDetected()>
 │   └──> <for (int axis = FD_ROLL; axis <= FD_YAW; ++axis)>
 │       └──> pidData[axis].P = 0;pidData[axis].I = 0;pidData[axis].D = 0;pidData[axis].F = 0;pidData[axis].Sum = 0;
 └──> <pidRuntime.zeroThrottleItermReset>
     └──>pidResetIterm
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BetaFlight模块设计之三十三:Pid模块分析 的相关文章

  • PI闭环的FPGA实现

    PID闭环的FPGA实现 1 原理分析 最近小张同学在做项目的时候发现PI闭环的FPGA学习资料很少 秉持着 既然没有轮子 那么自己就造一个的原则 于是乎自己写了个PI的Verilog程序 FPGA中实现PI闭环与DSP STM32 arm
  • setns对当前进程无效问题的排查(getpid获取值不变)

    1 复现流程及lxc的处理 demo1程序与执行结果如下 此时在容器内部看不到执行的程序 int main int ret fd pid printf father pid old d n getpid fd open dev ns O R
  • 单相Boost功率因数校正电路(PFC)设计与仿真(Simulink & Saber):第一章 PFC基础知识与电路参数设计

    写在前面 教程是根据Mathworks公司的有源功率因数校正教程 点这里跳转 和那日沙等老师编著的 电力电子 电机控制系统的建模及仿真 改写的 设计思路基本与之一致 嫌看文章麻烦的同学可以直接跳转看视频和查阅相关书籍 Simulink仿真部
  • 关于ARM_math数学库的使用

    关于ARM math数学库的使用 ARM math好强大的好吧 话不多说 请看截图 各种数学库看到没有 好强大的说 其实使用这些写函数 最快上手的方法就是看官方手册 话不多说 上官方链接arm math库的官网 举个栗子 一般步骤 1 首先
  • PID控制器开发笔记之九:基于前馈补偿的PID控制器的实现

    对于一般的时滞系统来说 设定值的变动会产生较大的滞后才能反映在被控变量上 从而产生合理的调节 而前馈控制系统是根据扰动或给定值的变化按补偿原理来工作的控制系统 其特点是当扰动产生后 被控变量还未变化以前 根据扰动作用的大小进行控制 以补偿扰
  • 端口被占用怎么办?关闭占用端口的进程

    当你发现某个端口被占用时 但不知道是哪一个进程占用了端口 需要关闭占用该端口的进程 1 启动系统命令行 windows系统 win r 2 输入命令 netstat ano 可查看所有端口的使用情况 netstat aon findstr
  • 【抗扰PID控制】干扰抑制PID控制器研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 Simulink 文章讲解 1 概述 文献来源 抗扰PI
  • 单端反激——隔离型DC/DC变换器的设计及仿真

    单端反激 隔离型DC DC变换器的设计及仿真 技术指标 1 原理分析 2 参数设计 3 仿真验证 技术指标 输入电压 V s m i n
  • Android : 通过pid获取app包名

    方法一 这个方法用在app主线程的activity或service里 因为要有context获取am private String getAppName int pid String pkgName ActivityManager am A
  • docker和主机之间的PID映射

    docker 命名空间与主机命名空间有何不同以及 pid 如何在这两者之间映射 谁能给我一个想法 有助于使用源代码在主机和 docker 之间映射 pid 的简单方法 您可以在中找到映射 proc PID status文件 它包含这样一行
  • 如何获取生成的 java 进程的 PID

    我正在编写几个 java 程序 在完成我想做的任何事情后 需要在单独的 JVM 中杀死 清理 为此 我需要获取我正在创建的 java 进程的 PID jps l可在 Windows 和 Unix 上运行 您可以使用 java 程序调用此命令
  • 什么是 .pid 文件以及它包含什么?

    我最近遇到了一个扩展名为 pid 的文件 并查看了它的内部 但没有找到太多内容 这文档 says Pid 文件是包含进程标识号 pid 的文件 该文件存储在文件系统的明确定义位置 从而允许其他程序找到正在运行的脚本的 pid 任何人都可以对
  • 以编程方式获取另一个进程的父进程pid?

    我尝试谷歌 但发现getppid 它获取的父pidcurrent过程 我需要类似的东西getppid some other pid 有这样的事吗 基本上获取某个进程的 pid 并返回父进程的 pid 我认为最简单的事情是打开 proc 并解
  • 有没有办法确定 Linux PID 是否暂停?

    我有一个 python 脚本 它使用 SIGSTOP 和 SIGCONT 命令与 os kill 来暂停或恢复进程 有没有办法判断相关PID是处于暂停状态还是恢复状态 您可以从进程的 proc 目录 proc
  • 如何在shell脚本中从fork子进程获取PID

    我相信我可以从父进程派生出 10 个子进程 下面是我的代码 bin sh fpfunction n 1 while n lt 20 do echo Hello World n times sleep 2 echo Hello World2
  • 确定监听某个端口的进程pid

    正如标题所示 我正在运行多个游戏服务器 并且每个服务器都有相同的name但不同PID和port数字 我想匹配PID正在监听某个端口的服务器 然后我想终止这个进程 我需要它来完成我的 bash 脚本 这可能吗 因为在网上还没有找到解决方案 您
  • 如何查看linux中特定进程每5秒的内存消耗情况

    我只是想知道如何找到特定进程在特定时间 比如5秒 的内存消耗 我是linux新手 因此 详细的步骤将不胜感激 Use top p PID其中 PID 是进程 ID 应显示有关进程的信息 包括使用的系统内存百分比 类型d以及一个以秒为单位的整
  • 查看用户最近执行的Android任务

    我想查看我的 Android 手机最近的任务 我尝试了一些来自互联网的代码 但没有一个能正常工作 我只想获取用户最后执行的应用程序的PID和名称 例如 如果我执行计算器应用程序 然后执行我创建的最近任务应用程序 则该应用程序应该能够告诉我类
  • 如何在Windows中通过端口查找PID并使用java杀死找到的任务

    我需要通过进程端口在java代码中杀死进程 我可以在 cmd 中手动执行此操作 例如 C gt netstat a n o findstr 6543 TCP 0 0 0 0 6543 0 0 0 0 0 LISTENING 1145 TCP
  • mysql.server 启动时出现 PID 错误?

    我刚刚尝试使用自制程序 在 Mac OS X 10 6 上 安装 MySQL 但我在第一个障碍时遇到了问题 当尝试手动启动服务器 mysql server start 时 出现以下错误 ERROR Manager of pid file q

随机推荐

  • 链游玩家:浅谈链游开发平台,千里之行始于足下

    链游玩家 出品 区块链游戏行业 xff0c 截止目前已有近千款的产品 xff0c 往常我们只知道这些游戏好不好玩 xff0c 以及怎么玩 xff0c 却不知道这些游戏都是用什么开发出来的 今天 xff0c 就来带大家盘点一下 xff0c 几
  • 解决部分网页打不开的方法(特别是CSDN)

    近来遇到好几次CSDN网站连不上的状况 起初是在学校因为是连的校园网 xff0c 突然有一天连着校园网就打不开CSDN的相关网页了 xff0c 问了周围的同学 xff0c 问了几个她们也都打不开 xff0c 但是用流量就能打开CSDN网页
  • MTK平台Android项目开发框架搭建

    前言 xff1a 不同的项目配置存在差异 xff0c 原生的SDK无法兼容多个项目作业 xff0c 按照原生框架创建项目比较繁琐 xff0c 如果采用GIT分支形式来管理每个项目同样过于繁琐 因此一套代码多个项目框架是有必要的 1 编译脚本
  • windows平台 Anaconda及相关插件安装

    1 Anaconda官网 https www anaconda com products individual 2 Anaconda安装 3 创建python3 7虚拟环境 conda create n 名字 python 61 3 7 如
  • python paramiko 远程登陆主机并获取主机信息

    usr bin python encoding utf 8 filename host information get py author gaohaixiang writetime 20200409 import paramiko def
  • rk3568 android11 AP6275s调试

    一 AP6275S的32 768k波形要求 xff1a 1 43 25ppm xff0c 计算的范围是 xff1a 32767 1808 32768 8192Hz 2 占空比 xff1a 30 70 3 峰峰值 xff1a 1 8v 43
  • OpenCV如何获取视频当前的一帧图像

    xff08 OpenCV读取视频 OpenCV提取视频每一帧 每一帧图片合成新的AVI视频 xff09 CvCapture 是视频获取结构 被用来作为视频获取函数的一个参数 比如 CvCapture cap IplImage cvQuery
  • iptables raw表

    1 什么是raw表 xff1f 做什么用的 xff1f iptables有5个链 PREROUTING INPUT FORWARD OUTPUT POSTROUTING 4个表 filter nat mangle raw 4个表的优先级由高
  • NSMutableParagraphStyle & NSAttributedString 文本样式设置

    今天做工作时 xff0c 用到了 NSMutableParagraphStyle amp NSAttributedString xff0c 由于用C 语言 写 xff0c 一开始比较生疏 xff0c 有些语法和oc语言还是有点区别的 xff
  • 【计算机毕业设计】医院管理系统源码

    一 系统截图 xff08 需要演示视频可以私聊 xff09 一 xff0e 摘要 目前各医疗机构中 xff0c 绝大部分中小型医疗机构内部没有实现任何信息化管理 xff0c 医院临床信息 xff0c 业务流程的数据依然采取纸质记录 xff0
  • 【计算机毕业设计】88.人事工资管理系统源码

    一 系统截图 xff08 需要演示视频可以私聊 xff09 摘 要 本论文主要论述了如何使用 JAVA 语言开发一个 人事管理系统 xff0c 本系统将严格按照软件开发流程进行各个阶段的工作 xff0c 采用 B S 架构 xff0c 面向
  • 【计算机毕业设计】012基于springboot的社区团购系统设计

    一 系统截图 xff08 需要演示视频可以私聊 xff09 摘 要 本课题是根据用户的需要以及网络的优势建立的一个社区团购系统 xff0c 来满足用户 团购 的需求 本社区团购系统应用 Java 技术 xff0c MY SQL数据库存储数据
  • 四轴飞控DIY调试起飞简明步骤

    四轴飞控DIY调试起飞简明步骤 调试起飞简明步骤Step1 xff1a 飞控配置Step2 xff1a 试飞目标测试内容坐标系 Step3 xff1a 试飞方法1 升降 xff08 Throttle xff09 2 偏航 xff08 yaw
  • BetaFlight开源工程结构简明介绍

    BetaFlight开源工程结构简明介绍 Step1 获取开源代码开源代码版本克隆开源代码 Step2 了解工程情况支持模型类型 xff1a 多旋翼 amp 固定翼支持特性 amp 功能安装 amp 文档链接配置工具下载其他介绍 xff08
  • 四轴FPV无人机手动操作简明介绍

    四轴FPV无人机手动操作简明介绍 通常航拍机都是有自稳算法 43 GPS导航 43 辅助功能 避障 的支持 xff0c 从而保证飞手能够相对容易且稳定的操作模型飞机 xff0c 通常通过阅读说明书都能很快上手 xff0c 这里就不在赘述 本
  • BetaFlight开源代码框架简介

    BetaFlight开源代码框架简介 1 框架设计分析考量2 框架分析前提条件3 主程序框架4 调度框架5 模块方法6 典型任务 amp 模块6 1 典型任务6 2 典型模块6 3 传感模块 7 回顾 amp 分析8 分析模板 1 框架设计
  • 四轴飞控DIY集成FPV功能

    四轴飞控DIY集成FPV功能 1 功能需求2 概念介绍2 1 制式2 2 显示分辨率2 3 摄像头线数高于700线低于700线 3 需求分析4 组件选择5 接线组装5 1 摄像头接线5 2 图传接线 6 组装位置7 FPV功能调试7 1 摄
  • MFC拷贝文件及进度条显示

    参考 xff1a 封装CopyFileEx函数 xff0c 实现文件复制中的暂停 xff0c 控速 xff0c 获取进度 http blog csdn net career2011 article details 6844513 实例讲解C
  • BetaFlight模块设计之三十二:MSP协议模块分析

    BetaFlight模块设计之三十二 xff1a MSP协议模块分析 1 MSP协议模块1 1 MSP描述1 2 MSP版本优缺点1 3 MSP代码资源 2 MSP报文解析2 1 MSP收包状态机2 2 MSP报文格式 3 MSP报文处理3
  • BetaFlight模块设计之三十三:Pid模块分析

    BetaFlight模块设计之三十三 xff1a Pid模块分析 Pid模块1 Pid audio子模块2 Pid init子模块3 Pid算法子模块3 1 TPA模式3 2 飞行模式3 3 Launch模式3 4 AcroTrainer模