In this tutorial, I will show you how to create a simulated battery state publisher in ROS 2. My goal is to publish a sensor_msgs/BatteryState message to a topic named /battery_status.
Real-World Application
The application that we will develop in this tutorial can be used as a template for a number of real-world robotic applications…the most important being:
- Autonomous docking at a charging station once the battery level gets below a certain threshold.
Prerequisites
- ROS 2 Foxy Fitzroy installed on Ubuntu Linux 20.04 or newer. I am using ROS 2 Galactic, which is the latest version of ROS 2 as of the date of this post.
- You have already created a ROS 2 workspace. The name of our workspace is “dev_ws”, which stands for “development workspace.”
- You have Python 3.7 or higher.
You can find the files for this post here on my Google Drive.
In this implementation, I want the float32 percentage variable of the sensor_msgs/BatteryState message to start off as 1.0 and gradually decrease. 1.0 indicates a full battery at 100% charge.
In a real-life application, we could have a condition plugin that listens to the /battery_status topic and returns SUCCESS when the battery percentage is lower than a specified value, and FAILURE otherwise.
Create the Battery State Publisher Node
Open a terminal window, and move to your package.
cd ~/dev_ws/src/two_wheeled_robot/scripts
Create a folder for battery state.
mkdir battery_state
cd battery_state
Open a new Python program called battery_state_pub.py.
gedit battery_state_pub.py
Add this code.
#!/usr/bin/env python3 
"""
Description:
Publish the battery state at a specific time interval
-------
Publishing Topics:
/battery_status – sensor_msgs/BatteryState
-------
Subscription Topics:
None
-------
Author: Addison Sears-Collins
Website: AutomaticAddison.com
Date: November 10, 2021
"""
 
import rclpy # Import the ROS client library for Python
from rclpy.node import Node # Enables the use of rclpy's Node class
from sensor_msgs.msg import BatteryState # Enable use of the sensor_msgs/BatteryState message type
 
class BatteryStatePublisher(Node):
  """
  Create a BatteryStatePublisher class, which is a subclass of the Node class.
  The class publishes the battery state of an object at a specific time interval.
  """
  
  def __init__(self):
    """
    Class constructor to set up the node
    """
   
    # Initiate the Node class's constructor and give it a name
    super().__init__('battery_state_pub')
     
    # Create publisher(s)  
     
    # This node publishes the state of the battery.
    # Maximum queue size of 10. 
    self.publisher_battery_state = self.create_publisher(BatteryState, '/battery_status', 10)
     
    # Time interval in seconds
    timer_period = 5.0 
    self.timer = self.create_timer(timer_period, self.get_battery_state)
    
    # Initialize battery level
    self.battery_voltage = 9.0 # Initialize the battery voltage level
    self.percent_charge_level = 1.0  # Initialize the percentage charge level
    self.decrement_factor = 0.99 # Used to reduce battery level each cycle
     
  def get_battery_state(self):
    """
    Callback function.
    This function gets called at the specific time interval.
    We decrement the battery charge level to simulate a real-world battery.
    """
    msg = BatteryState() # Create a message of this type 
    msg.voltage = self.battery_voltage 
    msg.percentage = self.percent_charge_level
    self.publisher_battery_state.publish(msg) # Publish BatteryState message 
     
    # Decrement the battery state 
    self.battery_voltage = self.battery_voltage * self.decrement_factor
    self.percent_charge_level = self.percent_charge_level * self.decrement_factor
   
def main(args=None):
 
  # Initialize the rclpy library
  rclpy.init(args=args)
 
  # Create the node
  battery_state_pub = BatteryStatePublisher()
 
  # Spin the node so the callback function is called.
  # Publish any pending messages to the topics.
  rclpy.spin(battery_state_pub)
 
  # Destroy the node explicitly
  # (optional - otherwise it will be done automatically
  # when the garbage collector destroys the node object)
  battery_state_pub.destroy_node()
 
  # Shutdown the ROS client library for Python
  rclpy.shutdown()
 
if __name__ == '__main__':
  main()
Save the code, and close the file.
Change the access permissions on the file.
chmod +x battery_state_pub.py
Open CMakeLists.txt.
cd ~/dev_ws/src/two_wheeled_robot
gedit CMakeLists.txt
scripts/battery_state/battery_state_pub.py
Build the Package
Now we build the package.
cd ~/dev_ws/
colcon build
Run the Node
Open a new terminal, and run the node:
ros2 run two_wheeled_robot battery_state_pub.py
Check out the current ROS topics.
ros2 topic list
Check the output on the /battery_status topic by opening a new terminal window and typing:
ros2 topic echo /battery_status
Here is the output:

That’s it! Keep building!
 
					