Building ROS 2 Packages with Python (rclpy)
Learning Objectives
By the end of this chapter, you will be able to:
- Create and structure ROS 2 packages using Python
- Implement nodes using the rclpy library
- Configure package dependencies and build files
- Build and run custom ROS 2 packages
- Debug common issues in Python-based ROS 2 packages
Prerequisites
- Understanding of ROS 2 architecture (covered in Week 3)
- Proficiency in Python programming
- Basic familiarity with package managers and build systems
Theory
ROS 2 packages are the fundamental building units of ROS 2 software. A package contains libraries, executables, scripts, or other artifacts. The rclpy library provides a Python client library for ROS 2, allowing developers to create ROS 2 nodes using Python.
Package Structure
A typical ROS 2 Python package follows this structure:
my_robot_package/
├── CMakeLists.txt # Build configuration (even for pure Python packages)
├── package.xml # Package manifest
├── setup.cfg # Installation configuration
├── setup.py # Python package setup
├── my_robot_package/ # Python module
│ ├── __init__.py
│ └── my_nodes.py
└── test/ # Test files
Key Components
package.xml: Contains package metadata including name, version, description, authors, maintainers, licenses, dependencies, and export tags.
setup.py: Standard Python packaging file that specifies how the package should be built and installed. For ROS 2 packages, this includes information about entry points and data files.
rclpy: The Python client library for ROS 2 that provides Python bindings to the ROS 2 client library (rcl). It allows creating nodes, publishers, subscribers, services, and actions.
Code Example 1: Creating a Basic ROS 2 Package Structure
# setup.py
# Purpose: Define the Python package structure for ROS 2
# Setup Instructions: Place in root of package directory
# Run: python setup.py install (or colcon build in workspace)
from setuptools import setup
import os
from glob import glob
package_name = 'my_robot_package'
setup(
name=package_name,
version='0.0.1',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
# Include all launch files
(os.path.join('share', package_name, 'launch'),
glob(os.path.join('launch', '*launch.[pxy][yma]*'))),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='Your Name',
maintainer_email='your.email@example.com',
description='A simple ROS 2 package for robot control',
license='Apache License 2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'simple_publisher = my_robot_package.simple_publisher:main',
'simple_subscriber = my_robot_package.simple_subscriber:main',
],
},
)
Expected Output:
# After running colcon build:
Starting >>> my_robot_package
Finished <<< my_robot_package [2.35s]
# Package can be sourced and executables run:
source install/setup.bash
ros2 run my_robot_package simple_publisher
Code Example 2: Python ROS 2 Node Implementation
# my_robot_package/simple_publisher.py
# Purpose: Implement a simple publisher node using rclpy
# Setup Instructions: Part of ROS 2 package structure
# Run: ros2 run my_robot_package simple_publisher
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
import sys
class SimplePublisher(Node):
def __init__(self):
super().__init__('simple_publisher')
# Create a publisher
self.publisher = self.create_publisher(
String,
'chatter',
10 # Queue size
)
# Create a timer for periodic publishing
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.counter = 0
self.get_logger().info('Simple Publisher has started')
def timer_callback(self):
"""Callback function for the timer"""
msg = String()
msg.data = f'Hello ROS 2 World: {self.counter}'
self.publisher.publish(msg)
self.get_logger().info(f'Published: {msg.data}')
self.counter += 1
def main(args=None):
"""Main function"""
rclpy.init(args=args)
try:
publisher_node = SimplePublisher()
# Spin the node to execute callbacks
rclpy.spin(publisher_node)
except KeyboardInterrupt:
print("\nShutting down node...")
finally:
# Cleanup
publisher_node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Expected Output:
[INFO] [1620123456.789] [simple_publisher]: Simple Publisher has started
[INFO] [1620123457.289] [simple_publisher]: Published: Hello ROS 2 World: 0
[INFO] [1620123457.789] [simple_publisher]: Published: Hello ROS 2 World: 1
[INFO] [1620123458.289] [simple_publisher]: Published: Hello ROS 2 World: 2
Hands-on Exercises
- Create a ROS 2 package that implements a temperature monitoring system with a publisher node and a subscriber node
- Build a ROS 2 package that provides a service to convert temperatures between Celsius and Fahrenheit
- Develop a ROS 2 package with multiple nodes that coordinate to perform a simple robot task
Summary
Building ROS 2 packages with Python provides a powerful way to develop robot applications. The rclpy library offers a clean Pythonic interface to ROS 2 concepts. Proper package structure and configuration are essential for creating reusable and maintainable robot software.
Hardware Requirements
This chapter can be completed using simulation environments. For physical implementation, the following hardware is recommended:
- Computer with ROS 2 installed (Ubuntu 22.04 with ROS 2 Humble Hawksbill)
- Python 3.8 or higher
- Appropriate robot hardware with ROS 2 compatibility (optional for basic exercises)