2 * This file is part of INAV.
4 * INAV is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * INAV is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with INAV. If not, see <http://www.gnu.org/licenses/>.
24 #if defined(USE_POWER_LIMITS)
26 #include "flight/power_limits.h"
28 #include "build/debug.h"
30 #include "common/filter.h"
31 #include "common/maths.h"
32 #include "common/utils.h"
34 #include "config/parameter_group.h"
35 #include "config/parameter_group_ids.h"
37 #include "drivers/time.h"
39 #include "fc/settings.h"
43 #include "sensors/battery.h"
45 #define LIMITING_THR_FILTER_TCONST 50
47 PG_REGISTER_WITH_RESET_TEMPLATE(powerLimitsConfig_t
, powerLimitsConfig
, PG_POWER_LIMITS_CONFIG
, 1);
49 PG_RESET_TEMPLATE(powerLimitsConfig_t
, powerLimitsConfig
,
50 .piP
= SETTING_LIMIT_PI_P_DEFAULT
,
51 .piI
= SETTING_LIMIT_PI_I_DEFAULT
,
52 .attnFilterCutoff
= SETTING_LIMIT_ATTN_FILTER_CUTOFF_DEFAULT
, // Hz
55 static float burstCurrentReserve
; // cA.µs
56 static float burstCurrentReserveMax
; // cA.µs
57 static float burstCurrentReserveFalldown
; // cA.µs
58 static int32_t activeCurrentLimit
; // cA
59 static float currentThrAttnIntegrator
= 0;
60 static pt1Filter_t currentThrAttnFilter
;
61 static pt1Filter_t currentThrLimitingBaseFilter
;
62 static bool wasLimitingCurrent
= false;
65 static float burstPowerReserve
; // cW.µs
66 static float burstPowerReserveMax
; // cW.µs
67 static float burstPowerReserveFalldown
; // cW.µs
68 static int32_t activePowerLimit
; // cW
69 static float powerThrAttnIntegrator
= 0;
70 static pt1Filter_t powerThrAttnFilter
;
71 static pt1Filter_t powerThrLimitingBaseFilter
;
72 static bool wasLimitingPower
= false;
75 void powerLimiterInit(void) {
76 if (currentBatteryProfile
->powerLimits
.burstCurrent
< currentBatteryProfile
->powerLimits
.continuousCurrent
) {
77 currentBatteryProfileMutable
->powerLimits
.burstCurrent
= currentBatteryProfile
->powerLimits
.continuousCurrent
;
80 activeCurrentLimit
= currentBatteryProfile
->powerLimits
.burstCurrent
;
82 uint16_t currentBurstOverContinuous
= currentBatteryProfile
->powerLimits
.burstCurrent
- currentBatteryProfile
->powerLimits
.continuousCurrent
;
83 burstCurrentReserve
= burstCurrentReserveMax
= currentBurstOverContinuous
* currentBatteryProfile
->powerLimits
.burstCurrentTime
* 1e6
;
84 burstCurrentReserveFalldown
= currentBurstOverContinuous
* currentBatteryProfile
->powerLimits
.burstCurrentFalldownTime
* 1e6
;
86 pt1FilterInit(¤tThrAttnFilter
, powerLimitsConfig()->attnFilterCutoff
, 0);
87 pt1FilterInitRC(¤tThrLimitingBaseFilter
, LIMITING_THR_FILTER_TCONST
, 0);
90 if (currentBatteryProfile
->powerLimits
.burstPower
< currentBatteryProfile
->powerLimits
.continuousPower
) {
91 currentBatteryProfileMutable
->powerLimits
.burstPower
= currentBatteryProfile
->powerLimits
.continuousPower
;
94 activePowerLimit
= currentBatteryProfile
->powerLimits
.burstPower
;
96 uint16_t powerBurstOverContinuous
= currentBatteryProfile
->powerLimits
.burstPower
- currentBatteryProfile
->powerLimits
.continuousPower
;
97 burstPowerReserve
= burstPowerReserveMax
= powerBurstOverContinuous
* currentBatteryProfile
->powerLimits
.burstPowerTime
* 1e6
;
98 burstPowerReserveFalldown
= powerBurstOverContinuous
* currentBatteryProfile
->powerLimits
.burstPowerFalldownTime
* 1e6
;
100 pt1FilterInit(&powerThrAttnFilter
, powerLimitsConfig()->attnFilterCutoff
, 0);
101 pt1FilterInitRC(&powerThrLimitingBaseFilter
, LIMITING_THR_FILTER_TCONST
, 0);
105 static uint32_t calculateActiveLimit(int32_t value
, uint32_t continuousLimit
, uint32_t burstLimit
, float *burstReserve
, float burstReserveFalldown
, float burstReserveMax
, timeDelta_t timeDelta
) {
106 int32_t continuousDiff
= value
- continuousLimit
* 10;
107 float spentReserveChunk
= continuousDiff
* timeDelta
;
108 *burstReserve
= constrainf(*burstReserve
- spentReserveChunk
, 0, burstReserveMax
);
110 if (currentBatteryProfile
->powerLimits
.burstCurrentFalldownTime
) {
111 return scaleRangef(MIN(*burstReserve
, burstReserveFalldown
), 0, burstReserveFalldown
, continuousLimit
, burstLimit
) * 10;
114 return (*burstReserve
? burstLimit
: continuousLimit
) * 10;
117 void currentLimiterUpdate(timeDelta_t timeDelta
) {
118 activeCurrentLimit
= calculateActiveLimit(getAmperage(),
119 currentBatteryProfile
->powerLimits
.continuousCurrent
, currentBatteryProfile
->powerLimits
.burstCurrent
,
120 &burstCurrentReserve
, burstCurrentReserveFalldown
, burstCurrentReserveMax
,
125 void powerLimiterUpdate(timeDelta_t timeDelta
) {
126 activePowerLimit
= calculateActiveLimit(getPower(),
127 currentBatteryProfile
->powerLimits
.continuousPower
, currentBatteryProfile
->powerLimits
.burstPower
,
128 &burstPowerReserve
, burstPowerReserveFalldown
, burstPowerReserveMax
,
133 void powerLimiterApply(int16_t *throttleCommand
) {
136 if (!activeCurrentLimit
&& !activePowerLimit
) {
140 if (!activeCurrentLimit
) {
145 static timeUs_t lastCallTimestamp
= 0;
146 timeUs_t currentTimeUs
= micros();
147 timeDelta_t callTimeDelta
= cmpTimeUs(currentTimeUs
, lastCallTimestamp
);
149 int16_t throttleBase
;
150 int16_t currentThrottleCommand
;
152 int16_t powerThrottleCommand
;
155 int16_t current
= getAmperageSample();
157 uint16_t voltage
= getVBatSample();
158 int32_t power
= (int32_t)voltage
* current
/ 100;
162 int32_t overCurrent
= current
- activeCurrentLimit
;
164 if (lastCallTimestamp
) {
165 currentThrAttnIntegrator
= constrainf(currentThrAttnIntegrator
+ overCurrent
* powerLimitsConfig()->piI
* callTimeDelta
* 2e-7f
, 0, PWM_RANGE_MAX
- PWM_RANGE_MIN
);
168 float currentThrAttnProportional
= MAX(0, overCurrent
) * powerLimitsConfig()->piP
* 1e-3f
;
170 uint16_t currentThrAttn
= lrintf(pt1FilterApply3(¤tThrAttnFilter
, currentThrAttnProportional
+ currentThrAttnIntegrator
, callTimeDelta
* 1e-6f
));
172 throttleBase
= wasLimitingCurrent
? lrintf(pt1FilterApply3(¤tThrLimitingBaseFilter
, *throttleCommand
, callTimeDelta
* 1e-6f
)) : *throttleCommand
;
173 uint16_t currentThrAttned
= MAX(PWM_RANGE_MIN
, (int16_t)throttleBase
- currentThrAttn
);
175 if (activeCurrentLimit
&& currentThrAttned
< *throttleCommand
) {
176 if (!wasLimitingCurrent
&& getAmperage() >= activeCurrentLimit
) {
177 pt1FilterReset(¤tThrLimitingBaseFilter
, *throttleCommand
);
178 wasLimitingCurrent
= true;
181 currentThrottleCommand
= currentThrAttned
;
183 wasLimitingCurrent
= false;
184 pt1FilterReset(¤tThrAttnFilter
, 0);
186 currentThrottleCommand
= *throttleCommand
;
191 int32_t overPower
= power
- activePowerLimit
;
193 if (lastCallTimestamp
) {
194 powerThrAttnIntegrator
= constrainf(powerThrAttnIntegrator
+ overPower
* powerLimitsConfig()->piI
* callTimeDelta
/ voltage
* 2e-5f
, 0, PWM_RANGE_MAX
- PWM_RANGE_MIN
);
197 float powerThrAttnProportional
= MAX(0, overPower
) * powerLimitsConfig()->piP
/ voltage
* 1e-1f
;
199 uint16_t powerThrAttn
= lrintf(pt1FilterApply3(&powerThrAttnFilter
, powerThrAttnProportional
+ powerThrAttnIntegrator
, callTimeDelta
* 1e-6f
));
201 throttleBase
= wasLimitingPower
? lrintf(pt1FilterApply3(&powerThrLimitingBaseFilter
, *throttleCommand
, callTimeDelta
* 1e-6)) : *throttleCommand
;
202 uint16_t powerThrAttned
= MAX(PWM_RANGE_MIN
, (int16_t)throttleBase
- powerThrAttn
);
204 if (activePowerLimit
&& powerThrAttned
< *throttleCommand
) {
205 if (!wasLimitingPower
&& getPower() >= activePowerLimit
) {
206 pt1FilterReset(&powerThrLimitingBaseFilter
, *throttleCommand
);
207 wasLimitingPower
= true;
210 powerThrottleCommand
= powerThrAttned
;
212 wasLimitingPower
= false;
213 pt1FilterReset(&powerThrAttnFilter
, 0);
215 powerThrottleCommand
= *throttleCommand
;
218 *throttleCommand
= MIN(currentThrottleCommand
, powerThrottleCommand
);
220 *throttleCommand
= currentThrottleCommand
;
223 lastCallTimestamp
= currentTimeUs
;
226 bool powerLimiterIsLimiting(void) {
228 return wasLimitingPower
|| wasLimitingCurrent
;
230 return wasLimitingCurrent
;
234 bool powerLimiterIsLimitingCurrent(void) {
235 return wasLimitingCurrent
;
239 bool powerLimiterIsLimitingPower(void) {
240 return wasLimitingPower
;
245 float powerLimiterGetRemainingBurstTime(void) {
246 uint16_t currentBurstOverContinuous
= currentBatteryProfile
->powerLimits
.burstCurrent
- currentBatteryProfile
->powerLimits
.continuousCurrent
;
247 float remainingCurrentBurstTime
= burstCurrentReserve
/ currentBurstOverContinuous
/ 1e7f
;
250 uint16_t powerBurstOverContinuous
= currentBatteryProfile
->powerLimits
.burstPower
- currentBatteryProfile
->powerLimits
.continuousPower
;
251 float remainingPowerBurstTime
= burstPowerReserve
/ powerBurstOverContinuous
/ 1e7f
;
253 if (!currentBatteryProfile
->powerLimits
.continuousCurrent
) {
254 return remainingPowerBurstTime
;
257 if (!currentBatteryProfile
->powerLimits
.continuousPower
) {
258 return remainingCurrentBurstTime
;
261 return MIN(remainingCurrentBurstTime
, remainingPowerBurstTime
);
263 return remainingCurrentBurstTime
;
268 uint16_t powerLimiterGetActiveCurrentLimit(void) {
269 return activeCurrentLimit
;
274 uint16_t powerLimiterGetActivePowerLimit(void) {
275 return activePowerLimit
;