Issue with Battery Status in PX4

Hi everyone, I’m having an issue with sending battery status data from a ROS node to QGroundControl using MAVLink.

What I’m trying to do:

  • I have a ROS node that reads battery data (voltage, current, percentage, capacity) and sends it to QGC using the BATTERY_STATUS MAVLink message at 0.5Hz.
  • The goal is to display the battery status correctly in QGC.

The problem:

  • In QGC, the battery percentage is displayed correctly, however, I still get a Vehicle Error: Failsafe activated due to low battery level, triggering RTL in 5 seconds. when trying to takeoff.
  • When clicking on the displayed battery, I notice that the time_remaining field always shows 00H:00M:01S, regardless of whether I: calculate the time remaining, set it to a fixed value or to invalid (0). Also it says battery 255, is this the ID? If so, it should be 1.

Relevant code (ROS node):

    def send_battery_status(self, drone_id, voltage, current, percentage, capacity):           
          # Create MAVLink battery status message
          msg = mavlink.MAVLink_battery_status_message(
              id=int(drone_id),  # set to 1 for drone1
              battery_function=1,
              type=1,
              temperature=32767,
              voltages=[int(voltage)] + [65535] * 9, 
              current_battery=int(current / 10),  # Convert current from mA to cA
              current_consumed=-1,  
              energy_consumed=-1, 
              battery_remaining=int(percentage)
          )
          raw_msg = msg.pack(mavlink.MAVLink(self.mavlink_system_id, self.mavlink_component_id))
          self.sock.sendto(raw_msg, (self.mavlink_target_ip, self.mavlink_target_port))

Additional details:

  • PX4 Firmware: v1.14.2
  • QGC Version: v4.4.3
  • Battery Monitoring Source (in QGC): Disabled
  • When checking the drone1/mavros/battery ROS topic and on the PX4 terminal using listener battery_status command the data seems to be correct but the error still triggers.
INFO  [commander] Ready for takeoff!
pxh> listener battery_status

TOPIC: battery_status
 battery_status
    timestamp: 1736438079006182 (0.003000 seconds ago)
    voltage_v: 22.76800
    voltage_filtered_v: 22.76800
    current_a: -1.17000
    current_filtered_a: -1.17000
    current_average_a: 0.00000
    discharged_mah: -1.00000
    remaining: 0.64000
    scale: 0.00000
    time_remaining_s: 0.00000
    temperature: 32767.00000
    voltage_cell_v: [22.76800, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000]
    max_cell_voltage_delta: 0.00000
    custom_faults: 0
    average_power: 0.00000
    available_energy: 0.00000
    full_charge_capacity_wh: 0.00000
    remaining_capacity_wh: 0.00000
    design_capacity: 0.00000
    nominal_voltage: 0.00000
    capacity: 0
    cycle_count: 0
    average_time_to_empty: 0
    serial_number: 0
    manufacture_date: 0
    state_of_health: 0
    max_error: 0
    interface_error: 0
    faults: 0
    average_time_to_full: 0
    over_discharge_count: 0
    connected: True
    cell_count: 1
    source: 0
    priority: 0
    id: 0
    is_powering_off: False
    is_required: False
    warning: 0
    mode: 0

pxh> commander takeoff
pxh> INFO  [commander] Armed by internal command	
INFO  [navigator] Using minimum takeoff altitude: 2.50 m	
INFO  [tone_alarm] arming warning
WARN  [failsafe] Failsafe activated, entering Hold for 5 seconds	
INFO  [tone_alarm] battery warning (fast)
INFO  [commander] Takeoff detected	
WARN  [failsafe] Failsafe activated	
INFO  [navigator] RTL HOME activated	
INFO  [navigator] RTL: landing at home position.	
INFO  [navigator] RTL: climb to 0 m (1 m above destination)	
INFO  [navigator] RTL: return at 0 m (1 m above destination)	
INFO  [navigator] RTL: descend to 0 m (1 m above destination)	
INFO  [navigator] RTL: land at destination	
INFO  [commander] Landing detected	
INFO  [commander] Disarmed by landing	
INFO  [tone_alarm] notify neutral
INFO  [logger] closed logfile, bytes written: 8381639

Hi @manudelu , could you share a flight log that contains the failsafe event? Hard to troubleshoot without, maybe there was a misconfiguration, maybe a PX4 bug.

Hi @sfuhrer , I’m not sure if this is the correct log, but I’ve downloaded the .ulg file and uploaded it on Flight Review. You can access it here.

So I had a look at the log, somehow the failsafe “battery_low_remaining_time” gets triggered on arming. You can find the code for it here. The condition that triggers it is battery_low_remaining_time < rtl_time_estimate, where in your case battery_low_remaining_time is all the time 0.
To why it is 0: Here I’m no longer certain, the only paths I see form looking at the code is:

  • you get the battery info through the UavcanBatteryBridge, and as the remaining_capacity_wh is 0 in your case the time_remaining in this line is also 0. This line contains an error, as the time_remaining field should be NAN if the battery capacity is not known.

I don’t understand yet how you receive the battery information at all on the PX4 side, given you have BAT1_SOURCE set to -1.

What I would recommend you is either to update to a new PX4 version, where you can disable this failsafe, or check if changing the line in the uavcan bridge fixes it.

@manudelu have you resolved this?
we have the same problem of our vehicle randomly entering rtl or loiter mode because of a low remaining flight time. We already use a version including the pr mentioned by @sfuhrer and have set the COM_FLTT_LOW_ACT parameter to None, but seems like it still enters failsafe when the remaining time is just one second.
The QGC Mavlink Inspector the battery status of a battery id 255, with remaining flight time 1s, but We never publish any mavlink message with this battery id, just one coming from a secondary component (26) with id 50 and the one from the power module with id 0. Both with remaining_time 0 (which is treated as nan according to mavlink documentation ). I suspect this code in the pixhawk firmware somehow messing with the uorb message (id flips from 0 to 255 due to -1 on an uint8 and the max() function sets the flight time remaining to 1 instead of 0. The faulty message gets published by component 1 (the pixhawk) but copies the voltage published by the secondary component under id 50.


(the emergency battery is just because I used a 15V psu instead of the expected 6S when on the bench)

We’re experiencing the exact same issue, and unfortunately, I haven’t managed to solve it either. I also suspect it’s related to some issue in the Pixhawk firmware code. I’ve put the idea on hold for now, but if you happen to find a solution and are willing to share it, it would be greatly appreciated!

is your PX4 firmware version the most recent?

Initially we were running selfcompiled 1.15.2 (crossfire support), I now tried 1.15.4 and the current state of the main branch and for both the problem still exists.
I also tried some changes to the code snipped I linked above:

// TODO: Determine how to better map between battery ID within the firmware and in MAVLink
				if (battery_status.id != 0) bat_msg.id = battery_status.id - 1;
				else bat_msg.id = battery_status.id + 1;
[...]
// MAVLink extension: 0 is unsupported, in uORB it's NAN
				bat_msg.time_remaining = (battery_status.connected && (PX4_ISFINITE(battery_status.time_remaining_s))) ?
							 /*math::max(*/(int)battery_status.time_remaining_s/*, 1)*/ : 0;

this removes the 1s time remaining, but I wasn’t able to field test this yet and not sure what other unwanted effects this has.

finally got to field test this yesterday, seems like the 1s remaining time doesn’t show up anymore in the mavlink inspector (the battery that was id 255 now shows up as id 1, but without a time_remaining value). But the vehicle still enters failsafe after a launch is detected and the uORB failsafe_flags topic (checked via the listener command) still shows the corresponding flag (battery_low_remaining_time) turn true. So our solution for now is setting the corresponding failsafe actions in /src/modules/commander/failsafe/failsafe.cpp to None. This seems to work, but permanently disables this failsafe. (@manudelu )