Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Second PT1 on DTerm #5427

Closed
wants to merge 38 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4fc43bf
- fixed TBS BST "Rates" was missing problem
larryho5 Mar 7, 2018
e8e61b9
Second PT1 on DTerm
Mar 7, 2018
f00575e
Merge pull request #5407 from larryho5/betaflight_3_3_0_R3
blckmn Mar 9, 2018
d3014ff
Update names, old defaults, fix whitespace
Mar 9, 2018
c3f192e
Remove underscore in lowpass_2, add hz to setpoint for lowpass
Mar 9, 2018
1755544
completed replacing lpf with lowpass, added _hz to all lowpass set po…
Mar 9, 2018
decc92f
fix whitespace
Mar 9, 2018
72676fb
whitespace attempt #57
Mar 9, 2018
d7fc099
change lpf to lowpass where appropriate elsewhere
Mar 9, 2018
4844e9b
second attempt at a simple PT1 implementation
Mar 9, 2018
aa42a69
Revert "Rewritten F7 dshot to LL (draft)" (#5430)
hydra Mar 9, 2018
416495e
Convert BEEPER to USE_ scheme (#5433)
jflyper Mar 10, 2018
0a2e5a5
CF/BF - Update DSP_Lib and STM32F7/Drivers/CMSIS to CMSIS 5.3.0. (#5431)
hydra Mar 11, 2018
2abcaeb
Reimplement strtol/strtoul/atoi (#5400)
ledvinap Mar 12, 2018
35f5e50
Fix OSD "DISARMED" blanking the display when armed (#5384)
etracer65 Mar 12, 2018
141d6ec
Higher-order gyro filter (#5257)
ledvinap Mar 14, 2018
27bc23c
Suppress OSD stats screen if runaway takeoff triggered the disarm (#5…
etracer65 Mar 14, 2018
0d3ca7f
add a wiki link in the release notes for the update of runcam in BF3.…
azolyoung Mar 14, 2018
31b3959
Update Building in Ubuntu.md (#5408)
ileou Mar 14, 2018
eee15e7
Implement minimalistic strcasestr (#5411)
ledvinap Mar 14, 2018
f674b01
Change ACC health check to use averaged rather than only the last (#5…
etracer65 Mar 14, 2018
85b9223
MSP_RAW_IMU to convert accADC from float to integer (#5417)
jflyper Mar 14, 2018
dd25358
Merge pull request #3028 from cleanflight/improve-osd-slave-comms (#5…
mikeller Mar 14, 2018
b286c06
New target CrazyBeeF3FR (#5420)
githubDLG Mar 14, 2018
c2898a4
review of #4986 (#5422)
adrianmiriuta Mar 14, 2018
b0e3da5
Merge pull request #3031 from cleanflight/fix-osd-slave-settings (#5424)
mikeller Mar 14, 2018
509d386
Merge pull request #3030 from cleanflight/fix-osd-vtx-power (#5423)
mikeller Mar 14, 2018
abe5b61
IMU attitude estimate accelerated convergence after disarming (#5425)
etracer65 Mar 15, 2018
6056ee1
Uniformly reduce UART buffer sizes to 128 bytes (#5432)
jflyper Mar 15, 2018
5be60dd
Second PT1 on DTerm
Mar 7, 2018
1a22d45
Rebase
Mar 15, 2018
c9c26a0
Remove underscore in lowpass_2, add hz to setpoint for lowpass
Mar 9, 2018
b5a2805
completed replacing lpf with lowpass, added _hz to all lowpass set po…
Mar 9, 2018
2e2b0c4
fix whitespace
Mar 9, 2018
fc6c0f8
whitespace attempt #57
Mar 9, 2018
be960ef
change lpf to lowpass where appropriate elsewhere
Mar 9, 2018
df382d8
second attempt at a simple PT1 implementation
Mar 9, 2018
b849b24
rebase
Mar 15, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 53 additions & 37 deletions src/main/flight/pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ void resetPidProfile(pidProfile_t *pidProfile)
.pidSumLimitYaw = PIDSUM_LIMIT_YAW,
.yaw_lpf_hz = 0,
.dterm_lpf_hz = 100, // filtering ON by default
.dterm_lpf2_hz = 0, // second Dterm LPF OFF by default
.dterm_notch_hz = 260,
.dterm_notch_cutoff = 160,
.dterm_filter_type = FILTER_BIQUAD,
Expand Down Expand Up @@ -167,28 +168,30 @@ void pidStabilisationState(pidStabilisationState_e pidControllerState)

const angle_index_t rcAliasToAngleIndexMap[] = { AI_ROLL, AI_PITCH };

static FAST_RAM filterApplyFnPtr dtermNotchFilterApplyFn;
static FAST_RAM void *dtermFilterNotch[2];
static FAST_RAM filterApplyFnPtr dtermLpfApplyFn;
static FAST_RAM void *dtermFilterLpf[2];
static FAST_RAM filterApplyFnPtr ptermYawFilterApplyFn;
static FAST_RAM void *ptermYawFilter;
static FAST_RAM filterApplyFnPtr dtermNotchApplyFn;
static FAST_RAM void *dtermNotch[2];
static FAST_RAM filterApplyFnPtr dtermLowpassApplyFn;
static FAST_RAM void *dtermLowpass[2];
static FAST_RAM filterApplyFnPtr dtermLowpass2ApplyFn;
static FAST_RAM void *dtermLowpass2[2];
static FAST_RAM filterApplyFnPtr ptermYawLowpassApplyFn;
static FAST_RAM void *ptermYawLowpass;

typedef union dtermFilterLpf_u {
typedef union dtermLowpass_u {
pt1Filter_t pt1Filter[2];
biquadFilter_t biquadFilter[2];
firFilterDenoise_t denoisingFilter[2];
} dtermFilterLpf_t;
} dtermLowpass_t;

void pidInitFilters(const pidProfile_t *pidProfile)
{
BUILD_BUG_ON(FD_YAW != 2); // only setting up Dterm filters on roll and pitch axes, so ensure yaw axis is 2

if (targetPidLooptime == 0) {
// no looptime set, so set all the filters to null
dtermNotchFilterApplyFn = nullFilterApply;
dtermLpfApplyFn = nullFilterApply;
ptermYawFilterApplyFn = nullFilterApply;
dtermNotchApplyFn = nullFilterApply;
dtermLowpassApplyFn = nullFilterApply;
ptermYawLowpassApplyFn = nullFilterApply;
return;
}

Expand All @@ -207,55 +210,67 @@ void pidInitFilters(const pidProfile_t *pidProfile)

if (dTermNotchHz != 0 && pidProfile->dterm_notch_cutoff != 0) {
static biquadFilter_t biquadFilterNotch[2];
dtermNotchFilterApplyFn = (filterApplyFnPtr)biquadFilterApply;
dtermNotchApplyFn = (filterApplyFnPtr)biquadFilterApply;
const float notchQ = filterGetNotchQ(dTermNotchHz, pidProfile->dterm_notch_cutoff);
for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) {
dtermFilterNotch[axis] = &biquadFilterNotch[axis];
biquadFilterInit(dtermFilterNotch[axis], dTermNotchHz, targetPidLooptime, notchQ, FILTER_NOTCH);
dtermNotch[axis] = &biquadFilterNotch[axis];
biquadFilterInit(dtermNotch[axis], dTermNotchHz, targetPidLooptime, notchQ, FILTER_NOTCH);
}
} else {
dtermNotchFilterApplyFn = nullFilterApply;
dtermNotchApplyFn = nullFilterApply;
}

static dtermFilterLpf_t dtermFilterLpfUnion;

//2nd Dterm Lowpass Filter
static pt1Filter_t dtermLowpass2State;
if (pidProfile->dterm_lpf2_hz == 0 || pidProfile->dterm_lpf2_hz > pidFrequencyNyquist) {
dtermLowpass2ApplyFn = nullFilterApply;
} else {
dtermLowpass2ApplyFn = (filterApplyFnPtr)pt1FilterApply;
for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) {
dtermLowpass2[axis] = &dtermLowpass2State;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This falls short of using the same state for all axes, just like @fujin's PR before

pt1FilterInit(dtermLowpass2[axis], pidProfile->dterm_lpf2_hz, dT);
}
}

static dtermLowpass_t dtermLowpassUnion;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, this effectively makes state shared for all axes

Copy link
Member Author

@ctzsnooze ctzsnooze Mar 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I just did some re-naming, that's all, to 'dtermLowpass2State' from previous name of 'pt1FilterD', as suggested by Fujin. The code itself is copied from yaw p lowpass further down.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked, I've just used exactly the same method as for the yaw code and the other filter code as in release 3.3. Hope there's nothing fundamentally wrong, but if there is, we will need to fix it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yaw is just one axis, so it's not shared by definition.
In your code roll&pitch get shared Dterm state for lowpass.

Copy link
Member

@DieHertz DieHertz Mar 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a look at how it's done in Fujin's PR for gyro lowpass, you need to create state for PID filters in advance. I can help when I get home

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK sorry I have to wait for actual code suggestion, too dumb to know how to do that.

Copy link
Member Author

@ctzsnooze ctzsnooze Mar 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work better?

//2nd Dterm Lowpass Filter      
if (pidProfile->dterm_lowpass2_hz == 0 || pidProfile->dterm_lowpass2_hz > pidFrequencyNyquist) {
	dtermLowpass2ApplyFn = nullFilterApply;
} else {
	static pt1Filter_t dtermLowpass2State[2];  
    dtermLowpass2ApplyFn = (filterApplyFnPtr)pt1FilterApply;
    for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) {
        dtermLowpass2[axis] = &dtermLowpass2State[axis];
        pt1FilterInit(dtermLowpass2[axis], pidProfile->dterm_lowpass2_hz, dT);
    }        
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dtermLowpass_t contains 2-element arrays for filter state (roll+pitch)
pt1Filter_t is single state

Use static pt1Filter_t dtermLowpass2State[2]; or define type

Copy link
Member Author

@ctzsnooze ctzsnooze Mar 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed this, hope it is now OK? Basically same structure as the preceding dterm notch.

//2nd Dterm Lowpass Filter   
static pt1Filter_t dtermFilterLowpass2[2];     
if (pidProfile->dterm_lowpass2_hz == 0 || pidProfile->dterm_lowpass2_hz > pidFrequencyNyquist) {
	dtermLowpass2ApplyFn = nullFilterApply;
} else {
    dtermLowpass2ApplyFn = (filterApplyFnPtr)pt1FilterApply;
    for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) {
        dtermLowpass2[axis] = &dtermFilterLowpass2[axis];
        pt1FilterInit(dtermLowpass2[axis], pidProfile->dterm_lowpass2_hz, dT);
    }        
}

if (pidProfile->dterm_lpf_hz == 0 || pidProfile->dterm_lpf_hz > pidFrequencyNyquist) {
dtermLpfApplyFn = nullFilterApply;
dtermLowpassApplyFn = nullFilterApply;
} else {
switch (pidProfile->dterm_filter_type) {
default:
dtermLpfApplyFn = nullFilterApply;
dtermLowpassApplyFn = nullFilterApply;
break;
case FILTER_PT1:
dtermLpfApplyFn = (filterApplyFnPtr)pt1FilterApply;
dtermLowpassApplyFn = (filterApplyFnPtr)pt1FilterApply;
for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) {
dtermFilterLpf[axis] = &dtermFilterLpfUnion.pt1Filter[axis];
pt1FilterInit(dtermFilterLpf[axis], pidProfile->dterm_lpf_hz, dT);
dtermLowpass[axis] = &dtermLowpassUnion.pt1Filter[axis];
pt1FilterInit(dtermLowpass[axis], pidProfile->dterm_lpf_hz, dT);
}
break;
case FILTER_BIQUAD:
dtermLpfApplyFn = (filterApplyFnPtr)biquadFilterApply;
dtermLowpassApplyFn = (filterApplyFnPtr)biquadFilterApply;
for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) {
dtermFilterLpf[axis] = &dtermFilterLpfUnion.biquadFilter[axis];
biquadFilterInitLPF(dtermFilterLpf[axis], pidProfile->dterm_lpf_hz, targetPidLooptime);
dtermLowpass[axis] = &dtermLowpassUnion.biquadFilter[axis];
biquadFilterInitLPF(dtermLowpass[axis], pidProfile->dterm_lpf_hz, targetPidLooptime);
}
break;
case FILTER_FIR:
dtermLpfApplyFn = (filterApplyFnPtr)firFilterDenoiseUpdate;
dtermLowpassApplyFn = (filterApplyFnPtr)firFilterDenoiseUpdate;
for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) {
dtermFilterLpf[axis] = &dtermFilterLpfUnion.denoisingFilter[axis];
firFilterDenoiseInit(dtermFilterLpf[axis], pidProfile->dterm_lpf_hz, targetPidLooptime);
dtermLowpass[axis] = &dtermLowpassUnion.denoisingFilter[axis];
firFilterDenoiseInit(dtermLowpass[axis], pidProfile->dterm_lpf_hz, targetPidLooptime);
}
break;
}
}

static pt1Filter_t pt1FilterYaw;
static pt1Filter_t pt1LowpassYaw;
if (pidProfile->yaw_lpf_hz == 0 || pidProfile->yaw_lpf_hz > pidFrequencyNyquist) {
ptermYawFilterApplyFn = nullFilterApply;
ptermYawLowpassApplyFn = nullFilterApply;
} else {
ptermYawFilterApplyFn = (filterApplyFnPtr)pt1FilterApply;
ptermYawFilter = &pt1FilterYaw;
pt1FilterInit(ptermYawFilter, pidProfile->yaw_lpf_hz, dT);
ptermYawLowpassApplyFn = (filterApplyFnPtr)pt1FilterApply;
ptermYawLowpass = &pt1LowpassYaw;
pt1FilterInit(ptermYawLowpass, pidProfile->yaw_lpf_hz, dT);
}
}

Expand Down Expand Up @@ -494,7 +509,7 @@ void pidController(const pidProfile_t *pidProfile, const rollAndPitchTrims_t *an
// -----calculate P component and add Dynamic Part based on stick input
axisPID_P[axis] = Kp[axis] * errorRate * tpaFactor;
if (axis == FD_YAW) {
axisPID_P[axis] = ptermYawFilterApplyFn(ptermYawFilter, axisPID_P[axis]);
axisPID_P[axis] = ptermYawLowpassApplyFn(ptermYawLowpass, axisPID_P[axis]);
}

// -----calculate I component
Expand All @@ -509,10 +524,11 @@ void pidController(const pidProfile_t *pidProfile, const rollAndPitchTrims_t *an
// -----calculate D component
if (axis != FD_YAW) {
// apply filters
float gyroRateFiltered = dtermNotchFilterApplyFn(dtermFilterNotch[axis], gyroRate);
gyroRateFiltered = dtermLpfApplyFn(dtermFilterLpf[axis], gyroRateFiltered);
float gyroRateFiltered = dtermNotchApplyFn(dtermNotch[axis], gyroRate);
gyroRateFiltered = dtermLowpassApplyFn(dtermLowpass[axis], gyroRateFiltered);
gyroRateFiltered = dtermLowpass2ApplyFn(dtermLowpass2[axis], gyroRateFiltered);

const float rD = dynCd * MIN(getRcDeflectionAbs(axis) * relaxFactor, 1.0f) * currentPidSetpoint - gyroRateFiltered; // cr - y
const float rD = dynCd * MIN(getRcDeflectionAbs(axis) * relaxFactor, 1.0f) * currentPidSetpoint - gyroRateFiltered; // cr - y
// Divide rate change by deltaT to get differential (ie dr/dt)
float delta = (rD - previousRateError[axis]) / deltaT;

Expand Down
1 change: 1 addition & 0 deletions src/main/flight/pid.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ typedef struct pidProfile_s {

uint16_t yaw_lpf_hz; // Additional yaw filter when yaw axis too noisy
uint16_t dterm_lpf_hz; // Delta Filter in hz
uint16_t dterm_lpf2_hz; // Extra PT1 Filter on D in hz
uint16_t dterm_notch_hz; // Biquad dterm notch hz
uint16_t dterm_notch_cutoff; // Biquad dterm notch low cutoff
uint8_t dterm_filter_type; // Filter selection for dterm
Expand Down
3 changes: 2 additions & 1 deletion src/main/interface/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,8 @@ const clivalue_t valueTable[] = {

// PG_PID_PROFILE
{ "dterm_lowpass_type", VAR_UINT8 | PROFILE_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_LOWPASS_TYPE }, PG_PID_PROFILE, offsetof(pidProfile_t, dterm_filter_type) },
{ "dterm_lowpass", VAR_INT16 | PROFILE_VALUE, .config.minmax = { 0, 16000 }, PG_PID_PROFILE, offsetof(pidProfile_t, dterm_lpf_hz) },
{ "dterm_lowpass_hz", VAR_INT16 | PROFILE_VALUE, .config.minmax = { 0, 16000 }, PG_PID_PROFILE, offsetof(pidProfile_t, dterm_lpf_hz) },
{ "dterm_lowpass2_hz", VAR_INT16 | PROFILE_VALUE, .config.minmax = { 0, 16000 }, PG_PID_PROFILE, offsetof(pidProfile_t, dterm_lpf2_hz) },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there's a redundant indent :)

{ "dterm_notch_hz", VAR_UINT16 | PROFILE_VALUE, .config.minmax = { 0, 16000 }, PG_PID_PROFILE, offsetof(pidProfile_t, dterm_notch_hz) },
{ "dterm_notch_cutoff", VAR_UINT16 | PROFILE_VALUE, .config.minmax = { 0, 16000 }, PG_PID_PROFILE, offsetof(pidProfile_t, dterm_notch_cutoff) },
{ "vbat_pid_gain", VAR_UINT8 | PROFILE_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_PID_PROFILE, offsetof(pidProfile_t, vbatPidCompensation) },
Expand Down