How to import custom Python3 modules in a ROS2 package

Written by Ernest Cheong

05/02/2024

This tutorial is created by Rosbotics Ambassador 010 Ernest

Rosbotics Ambassador Program https://www.theconstruct.ai/rosbotics-ambassador/)

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

List of resources used in this post:

  1. Use the rosject: https://app.theconstructsim.com/l/5e6f9418/
  2. Question from the Robotics StackExchange that is answered in this tutorial post: https://robotics.stackexchange.com/questions/98212/using-python3-modules-in-ros2-packages
  3. This tutorial is based on: https://answers.ros.org/question/367793/including-a-python-module-in-a-ros2-package/ and https://get-help.robotigniteacademy.com/t/ros2-python-package-convention/22399/2 and https://stackoverflow.com/questions/57426715/import-modules-in-package-in-ros2
  4. More information about the SetuptoolsDeprecationWarning: https://github.com/ament/ament_cmake/issues/382 and https://answers.ros.org/question/396439/setuptoolsdeprecationwarning-setuppy-install-is-deprecated-use-build-and-pip-and-other-standards-based-tools/

Overview

  • In this tutorial, I’ll show you how to import custom Python modules placed inside a standard ROS2 package built using the ament_python system
  • I’ll be using The Construct’s ROS development studio and doing it in a new ROS2 Humble workspace called ros2_ws.

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

Video Tutorial

Topics: python | ros2
Masterclass 2023 batch2 blog banner

Check Out These Related Posts

129. ros2ai

129. ros2ai

I would like to dedicate this episode to all the ROS Developers who believe that ChatGPT or...

read more

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