Create a ROS2 Python package

In this tutorial you’ll learn how to create and setup a ROS2 Python package.

I’ll show you every step, and explain the relation between files, where to write your nodes, how to add launch files, etc.

Let’s get to it!

Setup your ROS2 Python package

Before you can create a ROS2 Python package, make sure you have :

  • correctly installed ROS2,
  • setup your environment (add source /opt/ros/ROS_VERSION/setup.bash in your .bashrc – don’t forget to replace “ROS_VERSION”),
  • and created a ROS2 workspace ($ mkdir -p ~/ros2_ws/src && cd ros2_ws/ && colcon build).

Now, to create a Python package:

$ cd ~/ros2_ws/src/
$ ros2 pkg create my_python_pkg --build-type ament_python 
going to create a new package
package name: my_python_pkg
destination directory: /home/user/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['Name <your@email.com>']
licenses: ['TODO: License declaration']
build type: ament_python
dependencies: []
creating folder ./my_python_pkg
creating ./my_python_pkg/package.xml
creating source folder
creating folder ./my_python_pkg/my_python_pkg
creating ./my_python_pkg/setup.py
creating ./my_python_pkg/setup.cfg
creating folder ./my_python_pkg/resource
creating ./my_python_pkg/resource/my_python_pkg
creating ./my_python_pkg/my_python_pkg/__init__.py
creating folder ./my_python_pkg/test
creating ./my_python_pkg/test/test_copyright.py
creating ./my_python_pkg/test/test_flake8.py
creating ./my_python_pkg/test/test_pep257.py

Use ros2 pkg create followed by the name of your package. Then add the option --build-type ament_python to precise that you’re building a package specifically for Python.


You are learning ROS2...

As a complete beginner? Check out ROS2 For Beginners and learn ROS2 in 1 week.

As a ROS1 developer? Check out Learn ROS2 as a ROS1 Developer and Migrate Your ROS Projects.


A bunch of files will be created inside the new package.

my_python_pkg/
├── my_python_pkg
│   └── __init__.py
├── package.xml
├── resource
│   └── my_python_pkg
├── setup.cfg
├── setup.py
└── test
    ├── test_copyright.py
    ├── test_flake8.py
    └── test_pep257.py

Explanation of files inside a ROS2 Python package

Here’s a quick explanation for each file, and what you have to do to set them up.

package.xml

This file provides some information and required dependencies for the package.

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>my_python_pkg</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="your@email.com">Name</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_python</buildtool_depend>

  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>

You need to manually edit lines 5-8. Everything will work if you don’t do it, but if you decide to share or publish your package, then those info are mandatory.

  • version.
  • description: a brief description of what your package does.
  • maintainer: name and email of current maintainer. You can add multiple maintainer tags. Also, you can add some author tags (with name and email) if you want to make the distinction between authors and maintainers.
  • license: if you ever want to publish your package you’ll need a license (for example BSD, MIT, GPLv3).

setup.py

If you know what a CMakeLists.txt file is, well the setup.py is basically the same but for Python. When you compile your package it will tell what to install, where to install it, how to link dependencies, etc.

from setuptools import setup

package_name = 'my_python_pkg'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='Name',
    maintainer_email='your@email.com',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)

We’ll come back to this file later in this tutorial. For now you can see that the 4 lines we had to setup in the package.xml are also here. Modify those lines if you intent to share or publish the package.

setup.cfg

This file will tell where the scripts will be installed. Right now you have nothing to change.

[develop]
script-dir=$base/lib/my_python_pkg
[install]
install-scripts=$base/lib/my_python_pkg

<package_name>/ folder

This folder will be different every time, because it will always have the same name as your package. In this case the name of the package is “my_python_pkg”, so the name of the folder is also “my_python_pkg”.

You will create all your ROS2 Python nodes in this folder. Note that it already contains an empty __init__.py file.

resource/<package_name> file

This is needed for ROS2 to find your package. For our example the file name is “resource/my_python_pkg”.

Nothing to change here for now.

test/ folder

This folder, as its name suggests, is for testing. When you create a package it already contains 3 Python files.

Compile your package

To compile your package, go into your workspace directory and execute colcon build. We’ll tell ROS2 to only build our Python package with the option --packages-select.

$ cd ~/ros2_ws
$ colcon build --packages-select my_python_pkg 
Starting >>> my_python_pkg
Finished <<< my_python_pkg [0.52s]          

Summary: 1 package finished [0.65s]

Note: When working with Python, you may think that you don’t need to compile anything. That’s true, you can directly execute the Python files that you create without colcon build. But compiling a package is much more than that: it will install the scripts in a place where they can find other modules from other packages, where they can be found by other scripts. It will also allow you to start a node with ros2 run, add it in a launch file, pass parameters to it, etc.

Now that you know how to create and compile a package, let’s make a few examples to see what you can do with this package.

Build a Python node inside a ROS2 Python package

Let’s see how to build, install, and use a Python node, with our freshly created ROS2 Python package.

Create a file named my_python_node.py in the my_python_pkg/ folder.

$ cd ~/ros2_ws/src/my_python_pkg/my_python_pkg/
$ touch my_python_node.py

Here’s a simple Python code you can use for testing purposes.

import rclpy
from rclpy.node import Node

class MyPythonNode(Node):
    def __init__(self):
        super().__init__("my_node_name")
        self.get_logger().info("This node just says 'Hello'")

def main(args=None):
    rclpy.init(args=args)
    node = MyPythonNode()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == "__main__":
    main()

The node will just print a message on startup, and then it will spin indefinitely until you kill the node. If you want to know more about the code, check out how to write a ROS2 Python node.

Now that we have a Python file, we need to add an entry point in the setup.py file.

...
entry_points={
    'console_scripts': [
        'test = my_python_pkg.my_python_node:main'
    ],
...

Find the “entry_points” dictionary and add one line in the “console_scripts” array.

Some explanations:

  • “test” will be the name of the executable after the script is installed.
  • “my_python_pkg.my_python_node:main” means: execute the main() function inside the my_python_node.py file, inside the my_python_pkg. So, the entry point is the main(). If you want to start your node with a different function, make sure to set the function name accordingly in setup.py.
  • Don’t mix everything: executable name != file name != node name. Those are 3 different things. In our example: “test” is the executable, “my_python_node” is the file, and “my_node_name” is the node name. Note that you can also choose to use the same name for all 3.
  • The executable script will be installed in ~/ros2_ws/install/my_python_pkg/lib/my_python_pkg/. This is the folder specified in the setup.cfg file.

One more thing you need to do: add a <depend>rclpy</depend> tag in package.xml, because we use a dependency to rclpy in our code.

...
<buildtool_depend>ament_python</buildtool_depend>

<depend>rclpy</depend>
...

You only need to do this once per dependency for the whole package. If you create another node you’ll need to update setup.py, but not package.xml if you don’t have any new dependency.

And now you can compile your package with colcon build --packages-select my_python_pkg. Then, open a new terminal, source your ROS2 workspace and execute the node with ros2 run.

$ ros2 run my_python_pkg test 
[INFO] [my_node_name]: This node just says 'Hello'

Install other files in a ROS2 Python package

You can virtually put everything you want in a ROS2 package. There is no hard rule about what to do, but some conventions make it easier for you. Let’s see how to install launch files and YAML config files. Those are among the most common things you’ll add to packages when you develop your ROS2 application.

Launch files

Create a launch/ folder at the root of your package. You’ll put all your launch files inside this folder.

$ cd ~/ros2_ws/src/my_python_pkg/ 
$ mkdir launch

Now, to install those launch files, you need to modify setup.py.

import os
from glob import glob
from setuptools import setup
...
data_files=[
    ('share/ament_index/resource_index/packages',
        ['resource/' + package_name]),
    ('share/' + package_name, ['package.xml']),
    (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
],
...

For our example, with package name “my_python_pkg”, this will install all launch files from the launch/ folder, into ~/ros2_ws/install/my_python_pkg/share/my_python_pkg/launch/.

Note: you only need to modify setup.py once. After that, every time you add a launch file you’ll just need to compile your package so that the file is installed, that’s it.

Then, to start a launch file: ros2 launch package_name launch_file_name.

YAML config files

You can follow the same technique to install YAML config files.

Create a config/ folder at the root of your package. You’ll put all your YAML files here.

$ cd ~/ros2_ws/src/my_python_pkg/
$ mkdir config

To install YAML files, again, modify setup.py. Add a new line in the “data_files” array:

...
data_files=[
    ('share/ament_index/resource_index/packages',
        ['resource/' + package_name]),
    ('share/' + package_name, ['package.xml']),
    (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
    (os.path.join('share', package_name, 'config'), glob('config/*.yaml')),
],
...

Still with the “my_python_pkg” example, the YAML files will be installed into ~/ros2_ws/install/my_python_pkg/share/my_python_pkg/config/.

You can follow this technique to add any other folder into the install/ folder of your ROS2 workspace.

ROS2 Python package: going further

In this tutorial you have seen how to setup a ROS2 Python package, and how to make it grow with nodes, launch files, YAML files.

Here’s the final package architecture after all the additions we made:

my_python_pkg/
├── config
│   └── some_params.yaml
├── launch
│   └── my_application.launch.py
├── my_python_pkg
│   ├── __init__.py
│   └── my_python_node.py
├── package.xml
├── resource
│   └── my_python_pkg
├── setup.cfg
├── setup.py
└── test
    ├── test_copyright.py
    ├── test_flake8.py
    └── test_pep257.py

Understanding how to work with ROS2 packages is important so that you’re not stuck whenever you want to add something to your application.

To go further from here, check out how to:

Want to learn how to program with ROS2?

Don't miss this opportunity:

ROS2 For Beginners - Step by Step Course


>> Learn ROS2 in 1 Week <<

...or are you already a ROS1 Developer?

ROS2 For ROS1 Developers and Migrate Your ROS Projects


>> Learn ROS2 as a ROS1 Developer and Migrate Your ROS Projects <<

LEARN HOW TO PROGRAM ROBOTS

Did you find this tutorial useful?

Do you want to become better at programming robots, with Arduino, Raspberry Pi, or ROS2?

If yes, subscribe to receive exclusive content and special offers!