Skip to content

Commit

Permalink
AP_BattMonitor: support logging state-of-health percentage
Browse files Browse the repository at this point in the history
Only DroneCAN backend implements this feature for now
  • Loading branch information
rmackay9 committed Jan 9, 2024
1 parent 511659e commit 58a2274
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 6 deletions.
9 changes: 9 additions & 0 deletions libraries/AP_BattMonitor/AP_BattMonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,15 @@ uint32_t AP_BattMonitor::get_mavlink_fault_bitmask(const uint8_t instance) const
return drivers[instance]->get_mavlink_fault_bitmask();
}

// return true if state of health (as a percentage) can be provided and fills in soh_pct argument
bool AP_BattMonitor::get_state_of_health_pct(uint8_t instance, uint8_t &soh_pct) const
{
if (instance >= _num_instances || drivers[instance] == nullptr) {
return false;
}
return drivers[instance]->get_state_of_health_pct(soh_pct);
}

// Enable/Disable (Turn on/off) MPPT power to all backends who are MPPTs
void AP_BattMonitor::MPPT_set_powered_state_to_all(const bool power_on)
{
Expand Down
5 changes: 5 additions & 0 deletions libraries/AP_BattMonitor/AP_BattMonitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ class AP_BattMonitor
bool powerOffNotified; // only send powering off notification once
uint32_t time_remaining; // remaining battery time
bool has_time_remaining; // time_remaining is only valid if this is true
uint8_t state_of_health_pct; // state of health (SOH) in percent
bool has_state_of_health_pct; // state_of_health_pct is only valid if this is true
uint8_t instance; // instance number of this backend
const struct AP_Param::GroupInfo *var_info;
};
Expand Down Expand Up @@ -274,6 +276,9 @@ class AP_BattMonitor
// Returns mavlink fault state
uint32_t get_mavlink_fault_bitmask(const uint8_t instance) const;

// return true if state of health (as a percentage) can be provided and fills in soh_pct argument
bool get_state_of_health_pct(uint8_t instance, uint8_t &soh_pct) const;

static const struct AP_Param::GroupInfo var_info[];

#if AP_BATTERY_SCRIPTING_ENABLED
Expand Down
10 changes: 10 additions & 0 deletions libraries/AP_BattMonitor/AP_BattMonitor_Backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ void AP_BattMonitor_Backend::update_resistance_estimate()
_state.voltage_resting_estimate = _state.voltage + _state.current_amps * _state.resistance;
}

// return true if state of health can be provided and fills in soh_pct argument
bool AP_BattMonitor_Backend::get_state_of_health_pct(uint8_t &soh_pct) const
{
if (!_state.has_state_of_health_pct) {
return false;
}
soh_pct = _state.state_of_health_pct;
return true;
}

float AP_BattMonitor_Backend::voltage_resting_estimate() const
{
// resting voltage should always be greater than or equal to the raw voltage
Expand Down
3 changes: 3 additions & 0 deletions libraries/AP_BattMonitor/AP_BattMonitor_Backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ class AP_BattMonitor_Backend
// return true if cycle count can be provided and fills in cycles argument
virtual bool get_cycle_count(uint16_t &cycles) const { return false; }

// return true if state of health (as a percentage) can be provided and fills in soh_pct argument
bool get_state_of_health_pct(uint8_t &soh_pct) const;

/// get voltage with sag removed (based on battery current draw and resistance)
/// this will always be greater than or equal to the raw voltage
float voltage_resting_estimate() const;
Expand Down
20 changes: 17 additions & 3 deletions libraries/AP_BattMonitor/AP_BattMonitor_DroneCAN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,20 @@ AP_BattMonitor_DroneCAN* AP_BattMonitor_DroneCAN::get_dronecan_backend(AP_DroneC

void AP_BattMonitor_DroneCAN::handle_battery_info(const uavcan_equipment_power_BatteryInfo &msg)
{
update_interim_state(msg.voltage, msg.current, msg.temperature, msg.state_of_charge_pct);
update_interim_state(msg.voltage, msg.current, msg.temperature, msg.state_of_charge_pct, msg.state_of_health_pct);

WITH_SEMAPHORE(_sem_battmon);
_remaining_capacity_wh = msg.remaining_capacity_wh;
_full_charge_capacity_wh = msg.full_charge_capacity_wh;

// consume state of health
if (msg.state_of_health_pct != UAVCAN_EQUIPMENT_POWER_BATTERYINFO_STATE_OF_HEALTH_UNKNOWN) {
_interim_state.state_of_health_pct = msg.state_of_health_pct;
_interim_state.has_state_of_health_pct = true;
}
}

void AP_BattMonitor_DroneCAN::update_interim_state(const float voltage, const float current, const float temperature_K, const uint8_t soc)
void AP_BattMonitor_DroneCAN::update_interim_state(const float voltage, const float current, const float temperature_K, const uint8_t soc, uint8_t soh_pct)
{
WITH_SEMAPHORE(_sem_battmon);

Expand All @@ -145,6 +151,12 @@ void AP_BattMonitor_DroneCAN::update_interim_state(const float voltage, const fl
update_consumed(_interim_state, dt_us);
}

// state of health
if (soh_pct != UAVCAN_EQUIPMENT_POWER_BATTERYINFO_STATE_OF_HEALTH_UNKNOWN) {
_interim_state.state_of_health_pct = soh_pct;
_interim_state.has_state_of_health_pct = true;
}

// record time
_interim_state.last_time_micros = tnow;
_interim_state.healthy = true;
Expand Down Expand Up @@ -185,7 +197,7 @@ void AP_BattMonitor_DroneCAN::handle_mppt_stream(const mppt_Stream &msg)
// convert C to Kelvin
const float temperature_K = isnan(msg.temperature) ? 0 : C_TO_KELVIN(msg.temperature);

update_interim_state(voltage, current, temperature_K, soc);
update_interim_state(voltage, current, temperature_K, soc, UAVCAN_EQUIPMENT_POWER_BATTERYINFO_STATE_OF_HEALTH_UNKNOWN);

if (!_mppt.is_detected) {
// this is the first time the mppt message has been received
Expand Down Expand Up @@ -283,6 +295,8 @@ void AP_BattMonitor_DroneCAN::read()
_state.time_remaining = _interim_state.time_remaining;
_state.has_time_remaining = _interim_state.has_time_remaining;
_state.is_powering_off = _interim_state.is_powering_off;
_state.state_of_health_pct = _interim_state.state_of_health_pct;
_state.has_state_of_health_pct = _interim_state.has_state_of_health_pct;
memcpy(_state.cell_voltages.cells, _interim_state.cell_voltages.cells, sizeof(_state.cell_voltages));

_has_temperature = (AP_HAL::millis() - _state.temperature_time) <= AP_BATT_MONITOR_TIMEOUT;
Expand Down
2 changes: 1 addition & 1 deletion libraries/AP_BattMonitor/AP_BattMonitor_DroneCAN.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class AP_BattMonitor_DroneCAN : public AP_BattMonitor_Backend
private:
void handle_battery_info(const uavcan_equipment_power_BatteryInfo &msg);
void handle_battery_info_aux(const ardupilot_equipment_power_BatteryInfoAux &msg);
void update_interim_state(const float voltage, const float current, const float temperature_K, const uint8_t soc);
void update_interim_state(const float voltage, const float current, const float temperature_K, const uint8_t soc, uint8_t soh_pct);

static bool match_battery_id(uint8_t instance, uint8_t battery_id);

Expand Down
6 changes: 5 additions & 1 deletion libraries/AP_BattMonitor/AP_BattMonitor_Logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ void AP_BattMonitor_Backend::Log_Write_BAT(const uint8_t instance, const uint64_
temperature_cd = temperature * 100.0;
}

uint8_t soh_pct = 0;
IGNORE_RETURN(get_state_of_health_pct(soh_pct));

const struct log_BAT pkt{
LOG_PACKET_HEADER_INIT(LOG_BAT_MSG),
time_us : time_us,
Expand All @@ -28,7 +31,8 @@ void AP_BattMonitor_Backend::Log_Write_BAT(const uint8_t instance, const uint64_
temperature : temperature_cd,
resistance : _state.resistance,
rem_percent : percent,
health : _state.healthy
health : _state.healthy,
state_of_health_pct : soh_pct
};
AP::logger().WriteBlock(&pkt, sizeof(pkt));
}
Expand Down
4 changes: 3 additions & 1 deletion libraries/AP_BattMonitor/LogStructure.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// @Field: Res: estimated battery resistance
// @Field: RemPct: remaining percentage
// @Field: H: health
// @Field: SH: state of health percentage. 0 if unknown
struct PACKED log_BAT {
LOG_PACKET_HEADER;
uint64_t time_us;
Expand All @@ -32,6 +33,7 @@ struct PACKED log_BAT {
float resistance;
uint8_t rem_percent;
uint8_t health;
uint8_t state_of_health_pct;
};

// @LoggerMessage: BCL
Expand Down Expand Up @@ -61,6 +63,6 @@ struct PACKED log_BCL {

#define LOG_STRUCTURE_FROM_BATTMONITOR \
{ LOG_BAT_MSG, sizeof(log_BAT), \
"BAT", "QBfffffcfBB", "TimeUS,Inst,Volt,VoltR,Curr,CurrTot,EnrgTot,Temp,Res,RemPct,H", "s#vvAaXOw%-", "F-000C0?000" , true }, \
"BAT", "QBfffffcfBBB", "TimeUS,Inst,Volt,VoltR,Curr,CurrTot,EnrgTot,Temp,Res,RemPct,H,SH", "s#vvAaXOw%-%", "F-000C0?0000" , true }, \
{ LOG_BCL_MSG, sizeof(log_BCL), \
"BCL", "QBfHHHHHHHHHHHH", "TimeUS,Instance,Volt,V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12", "s#vvvvvvvvvvvvv", "F-0CCCCCCCCCCCC" , true },

0 comments on commit 58a2274

Please sign in to comment.