Compare commits

...

2 Commits

Author SHA1 Message Date
Balduin 30e78141db fix(battery): Debounce battery connected state in battery library
This robustifies against some communication flukes where a single 0V
sample was given to the battery library, which would previously mark the
battery disconnected immediately. Now we tolerate a given time of 0V
samples (while not actually updating the voltage) before considering the
battery disconnected.
2026-03-19 10:15:34 +01:00
Balduin f8d9a3e68f feat(battery_simulator): single 0V sample failure injection
Use `failure battery intermittent` to trigger.
2026-03-19 10:15:34 +01:00
4 changed files with 28 additions and 4 deletions
+6 -2
View File
@@ -98,7 +98,10 @@ Battery::Battery(int index, ModuleParams *parent, const int sample_interval_us,
void Battery::updateVoltage(const float voltage_v)
{
_voltage_v = voltage_v;
if (voltage_v >= LITHIUM_BATTERY_RECOGNITION_VOLTAGE) {
_voltage_v = voltage_v;
_last_sufficient_voltage_timestamp = hrt_absolute_time();
}
}
void Battery::updateCurrent(const float current_a)
@@ -116,7 +119,8 @@ void Battery::updateBatteryStatus(const hrt_abstime &timestamp)
updateDt(timestamp);
// Require minimum voltage otherwise override connected status
if (_voltage_v < LITHIUM_BATTERY_RECOGNITION_VOLTAGE) {
// Tolerate a small number of low-voltage samples (common I2C comm failure) before disconnection
if (timestamp - _last_sufficient_voltage_timestamp > MAX_LOW_VOLTAGE_TIME_S * 1_s) {
_connected = false;
}
+4 -2
View File
@@ -134,8 +134,6 @@ public:
void updateDt(const hrt_abstime &timestamp);
protected:
static constexpr float LITHIUM_BATTERY_RECOGNITION_VOLTAGE = 2.1f;
struct {
param_t v_empty;
param_t v_charged;
@@ -202,6 +200,10 @@ private:
bool _vehicle_status_is_fw{false};
hrt_abstime _last_unconnected_timestamp{0};
static constexpr float LITHIUM_BATTERY_RECOGNITION_VOLTAGE = 2.1f;
static constexpr float MAX_LOW_VOLTAGE_TIME_S = 0.2f;
hrt_abstime _last_sufficient_voltage_timestamp{0};
// Internal Resistance estimation
void updateInternalResistanceEstimation(const float voltage_v, const float current_a);
void resetInternalResistanceEstimation(const float voltage_v, const float current_a);
@@ -109,6 +109,12 @@ void BatterySimulator::Run()
vbatt = _battery.empty_cell_voltage();
}
if (_zero_volt_spike) {
// Zero volt but only one sample. Replicates some I2C comm failures
vbatt = 0.0f;
_zero_volt_spike = false;
}
vbatt *= _battery.cell_count();
_battery.setConnected(true);
@@ -157,6 +163,17 @@ void BatterySimulator::updateCommands()
supported = true;
_force_empty_battery = true;
}
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_INTERMITTENT) {
handled = true;
PX4_WARN("CMD_INJECT_FAILURE, battery intermittent (single 0V sample)");
supported = true;
if (instance == 0) {
supported = true;
_zero_volt_spike = true;
}
}
}
@@ -89,6 +89,7 @@ private:
bool _armed{false};
bool _force_empty_battery{false};
bool _zero_volt_spike{false};
perf_counter_t _loop_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")};