mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-07-24 15:27:41 +08:00
fix(battery): use unclamped dt for discharged_mah coulomb count (#27866)
Battery::updateDt() clamps _dt to a maximum of 2 seconds:
_dt = math::min((timestamp - _last_timestamp) / 1e6f, 2.f);
This same _dt is then used for two different purposes:
1. Battery::sumDischarged(), the coulomb count that accumulates
discharged_mah: discharged_mah_loop = current_a * dt.
2. The current average low-pass filter update
(_current_average_filter_a.update(current_a, _dt)).
The 2-second clamp makes sense for (2): a low-pass filter update
generally assumes a roughly steady sample interval and shouldn't see a
single very large dt distort its state. It does not make sense for
(1): the coulomb count is a plain physical integral (charge = current
x time), and clamping dt there just means an update that arrives less
often than every 2 seconds (e.g. some DroneCAN/UAVCAN battery devices,
as reported in #27797) has its actual elapsed time silently truncated
to 2 seconds, systematically underestimating discharged_mah for that
update - the error accumulates over the life of the battery.
Fix: track a second, unclamped dt (_dt_discharge) alongside the
existing clamped one, and use it specifically for the coulomb count in
sumDischarged(). The filter update keeps using the existing clamped
_dt unchanged.
Fixes #27797
Test plan:
- I don't have hardware with a >2-second-interval battery update
source to reproduce this end-to-end, so I verified the corrected
arithmetic with a standalone C reproduction of updateDt()/
sumDischarged(): a 5-second update interval at a constant 10A
before the fix accumulates 5.56 mAh (current x 2s, clamped) instead
of the physically correct 13.89 mAh (current x 5s); after the fix it
correctly accumulates 13.89 mAh.
- Verified a normal high-frequency update pattern (10 x 100ms updates
at 5A) is unaffected: both before and after the fix accumulate the
same, correct 1.39 mAh for that 1-second span.
Signed-off-by: yi chen <94xhn1@gmail.com>
Co-authored-by: yi chen <94xhn1@gmail.com>
This commit is contained in:
@@ -213,7 +213,17 @@ void Battery::updateAndPublishBatteryStatus(const hrt_abstime ×tamp)
|
||||
void Battery::updateDt(const hrt_abstime ×tamp)
|
||||
{
|
||||
if (_last_timestamp != 0) {
|
||||
_dt = math::min((timestamp - _last_timestamp) / 1e6f, 2.f); // guard to a maximum 2 seconds dt
|
||||
// _dt_discharge is the true, unclamped time delta: for the coulomb
|
||||
// count in sumDischarged() below, using the real elapsed time is
|
||||
// always more accurate than clamping it, even across an unusually
|
||||
// long gap between updates (e.g. an update source that reports
|
||||
// less often than every 2 seconds).
|
||||
_dt_discharge = (timestamp - _last_timestamp) / 1e6f;
|
||||
|
||||
// _dt is clamped to guard the numerical stability of the current
|
||||
// average filter below, which assumes a roughly steady sample
|
||||
// interval and should not see a single very large dt.
|
||||
_dt = math::min(_dt_discharge, 2.f);
|
||||
}
|
||||
|
||||
_last_timestamp = timestamp;
|
||||
@@ -221,10 +231,10 @@ void Battery::updateDt(const hrt_abstime ×tamp)
|
||||
|
||||
float Battery::sumDischarged(float current_a)
|
||||
{
|
||||
if (_dt > FLT_EPSILON && fabsf(current_a + 1.f) > FLT_EPSILON) {
|
||||
if (_dt_discharge > FLT_EPSILON && fabsf(current_a + 1.f) > FLT_EPSILON) {
|
||||
// mAh since last loop: (current[A] * 1000 = [mA]) * (dt[s] / 3600 = [h])
|
||||
// current = -1 means invalid current measurement
|
||||
_discharged_mah_loop = (current_a * 1e3f) * (_dt / 3600.f);
|
||||
_discharged_mah_loop = (current_a * 1e3f) * (_dt_discharge / 3600.f);
|
||||
_discharged_mah += _discharged_mah_loop;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +198,7 @@ private:
|
||||
float _scale{1.f};
|
||||
uint8_t _warning{battery_status_s::WARNING_NONE};
|
||||
float _dt{0.f};
|
||||
float _dt_discharge{0.f}; // unclamped dt, used for the discharged_mah coulomb count
|
||||
float _capacity_mah{0.f};
|
||||
hrt_abstime _last_timestamp{0};
|
||||
bool _armed{false};
|
||||
|
||||
Reference in New Issue
Block a user