How to import custom Python3 modules in a ROS 2 package

Written by Ernest Cheong

05/02/2024

What we are going to learn:

  1. How to set up the package architecture to have custom Python modules inside the ROS2 package
  2. How to configure setup.py so that the modules can be imported
  3. How to import modules and use them in a Python node script

 

If you want to learn ROS 2 Python in a practical, hands-on way, check out the course ROS 2 Basics in 5 Days: https://app.theconstruct.ai/courses/132

In this course, you’ll cover the foundational concepts needed to start working with ROS 2, as well as more advanced topics, all while engaging in hands-on practice.

Opening the rosject

In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject for that: https://app.theconstructsim.com/l/5e6f9418/

Just by copying the rosject (clicking the link above), you will have a setup already prepared for you.

After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject ( see this example).

After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.

In order to interact with ROS2, we need a terminal.

Let’s open a terminal by clicking the Open a new terminal button.

Open a new Terminal

 

Setup the package architecture

Create a standard ROS2 package using ament_python

With dependencies on rclpy:

cd ~/ros2_ws/src/
ros2 pkg create py_pkg –build-type ament_python –dependencies rclpy

Run the “tree . command to see the folder structure. If you don’t have the command installed, you can install it using:

sudo apt-get update
sudo apt-get install -y tree

You should be able to see this structure which you are already familiar with:

`– py_pkg
|– package.xml
|– py_pkg
| `– __init__.py
|– resource
| `– py_pkg
|– setup.cfg
|– setup.py
`– test
|– test_copyright.py
|– test_flake8.py
`– test_pep257.py

Add custom Python module(s) that you want to import

You will need to add module files under the directory with the same name as the package (in this case py_pkg):

cd py_pkg/py_pkg
touch module_to_import.py

For this tutorial for simplicity, we can just add this minimal function to the module_to_import.py file that just prints hello world:

def function_to_import():
print(“hello world”)


Create a scripts directory for your Python node(s)

Specify the directory as a Python package by creating an empty__init__.pyfile (essential for the folder to be recognized by Python as a package), then add a Python node:

cd ..
mkdir scripts
touch scripts/__init__.py
touch scripts/py_node.py
Here is a simple node implementation (to paste in py_node.py) that imports and uses the function from the module we just created:
import rclpy
from rclpy.node import Node
# Import a specific function/class from your module
from py_pkg.module_to_import import function_to_import
class MyPythonNode(Node):
    def __init__(self):
        super().__init__(“my_node_name”)
# Run the imported function
        function_to_import()
def main(args=None):
    # Initiate ROS communications
    rclpy.init(args=args)
    # Instantiate the node
    node = MyPythonNode()
    # Make the node spin
    rclpy.spin(node)
    # Destroy the node object
    node.destroy_node()
    # Shutdown ROS communications
    rclpy.shutdown()
if __name__ == ‘__main__’:
    main()

Final package architecture

Run the “tree. command to see the folder structure inside py_pkg:

|– package.xml
|– py_pkg
| |– __init__.py
| `– module_to_import.py
|– resource
| `– py_pkg
|– scripts
| |– __init__.py
| `– py_node.py
|– setup.cfg
|– setup.py
`– test
|– test_copyright.py
|– test_flake8.py
`– test_pep257.py

Configure the package and test

Modify setup.py

  1. Add scripts to the list of Python packages:

packages=[package_name, ‘scripts’],

This is so that later when you build with colcon, ROS2 knows to put the Python packages here ~/<workspace_name>/install/<package_name>/lib/<python_version>/site-packages/<package_name> (in our case <workspace_name> is ros2_ws, <python_version> ispython3.10, and <package_name> is py_pkg).

2. State the entry point:

entry_points={
‘console_scripts’: [
‘py_node_executable = scripts.py_node:main’
],

So your setup.py should look like this:

from setuptools import setup

package_name = ‘py_pkg’

setup(
name=package_name,
version=’0.0.0′,
packages=[package_name, ‘scripts’],
data_files=[
(‘share/ament_index/resource_index/packages’,
[‘resource/’ + package_name]),
(‘share/’ + package_name, [‘package.xml’]),
],
install_requires=[‘setuptools’],
zip_safe=True,
maintainer=’user’,
maintainer_email=’user@todo.todo’,
description=’TODO: Package description’,
license=’TODO: License declaration’,
tests_require=[‘pytest’],
entry_points={
‘console_scripts’: [
‘py_node_executable = scripts.py_node:main’
],
},
)

Compile the Package

cd ~/ros2_ws/
colcon build –packages-select py_pkg

There is a good chance that you will get a SetuptoolsDeprecationWarning:

Starting >>> py_pkg
— stderr: py_pkg
/usr/lib/python3/dist-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
warnings.warn(

Finished <<< py_pkg [1.88s]

Summary: 1 package finished [2.21s]
1 package had stderr output: py_pkg

If there is no warning then that’s great, but don’t worry if you get it as it is just a warning (not an error) and you will still be able to build successfully.

As of the time of writing this tutorial, the developers hope to have it fixed for Jazzy in May 2024, but for now, there are workarounds to remove the warning. The original workaround was to downgrade the version of setuptools that is currently installed by doing pip install setuptools==58.2.0 in the terminal. However, that is not recommended. Instead you should use the PYTHONWARNINGS environment variable to suppress that particular warning by adding this to the ~/.bashrc file:

PYTHONWARNINGS=”ignore:setup.py install is deprecated::setuptools.command.install”; export PYTHONWARNINGS

You could do this by entering this into the terminal:

echo “PYTHONWARNINGS=\”ignore:setup.py install is deprecated::setuptools.command.install\”; export PYTHONWARNINGS” >> ~/.bashrc

Don’t forget to source the .bashrc in the terminal before compiling the package to register the changes:

source ~/.bashrc # . ~/.bashrc

You should now be able to compile the package without the warning.

cd ~/ros2_ws/
colcon build –packages-select py_pkg

Run the node

In the terminal, source the ROS2 environment and start the Python node:

source ~/ros2_ws/install/setup.bash
ros2 run py_pkg py_node_executable

If you get the following output, that means it was successful!

hello world

Congratulations. You now know how to import custom Python3 modules in a ROS 2 package.

To learn more about ROS 2, have a look at the course below:

We hope this post was really helpful to you.

This tutorial is created by Robotics Ambassador Ernest.

Video Tutorial

Topics: python | ros2
Masterclass 2023 batch2 blog banner

Check Out These Related Posts

0 Comments

Submit a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Pin It on Pinterest

Share This