Attention

This is older information related to ROS2 Humble. Newer versions, such as Jazzy, are part of the ongoing versions of this tutorial.

Using a Python Library from another package (for ament_python)

Let us create a package with a Node that uses the library we created in the prior example.

Note that we must add the python_package_with_a_library as a dependency to our new package. The easiest way to do so is through ros2 pkg create. We also add rclpy as a dependency so that our Node can do something useful.

cd ~/ros2_tutorial_workspace/src
ros2 pkg create python_package_that_uses_the_library \
--dependencies rclpy python_package_with_a_library \
--build-type ament_python \
--node-name node_that_uses_the_library

resulting in yet another version of our favorite wall of text

going to create a new package
package name: python_package_that_uses_the_library
destination directory: /home/murilo/ros2_tutorial_workspace/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['murilo <murilomarinho@ieee.org>']
licenses: ['TODO: License declaration']
build type: ament_python
dependencies: ['rclpy', 'python_package_with_a_library']
node_name: node_that_uses_the_library
creating folder ./python_package_that_uses_the_library
creating ./python_package_that_uses_the_library/package.xml
creating source folder
creating folder ./python_package_that_uses_the_library/python_package_that_uses_the_library
creating ./python_package_that_uses_the_library/setup.py
creating ./python_package_that_uses_the_library/setup.cfg
creating folder ./python_package_that_uses_the_library/resource
creating ./python_package_that_uses_the_library/resource/python_package_that_uses_the_library
creating ./python_package_that_uses_the_library/python_package_that_uses_the_library/__init__.py
creating folder ./python_package_that_uses_the_library/test
creating ./python_package_that_uses_the_library/test/test_copyright.py
creating ./python_package_that_uses_the_library/test/test_flake8.py
creating ./python_package_that_uses_the_library/test/test_pep257.py
creating ./python_package_that_uses_the_library/python_package_that_uses_the_library/node_that_uses_the_library.py

[WARNING]: Unknown license 'TODO: License declaration'.  This has been set in the package.xml, but no LICENSE file has been created.
It is recommended to use one of the ament license identitifers:
Apache-2.0
BSL-1.0
BSD-2.0
BSD-2-Clause
BSD-3-Clause
GPL-3.0-only
LGPL-3.0-only
MIT
MIT-0

The sample Node

Given that it was created from a template, the file python_package_that_uses_the_library/python_package_that_uses_the_library/node_that_uses_the_library.py is currently mostly empty. Let us replace its contents with

node_that_uses_the_library.py

 1import random
 2import string
 3
 4import rclpy
 5from rclpy.node import Node
 6from python_package_with_a_library.sample_python_library import SampleClass, sample_function_for_square_of_sum
 7
 8
 9class NodeThatUsesTheLibrary(Node):
10    """A ROS2 Node that prints to the console periodically."""
11
12    def __init__(self):
13        super().__init__('node_that_uses_the_library')
14        timer_period: float = 0.5
15        self.timer = self.create_timer(timer_period, self.timer_callback)
16
17    def timer_callback(self):
18        """
19        Method that is periodically called by the timer.
20        Prints out the result of sample_function_for_square_of_sum of two random numbers,
21        followed by the result of SampleClass.get_name() for an instance created with
22        a ten-character-long ascii string of random characters.
23        """
24        a: float = random.uniform(0, 1)
25        b: float = random.uniform(1, 2)
26        c: float = sample_function_for_square_of_sum(a, b)
27        self.get_logger().info(f'sample_function_for_square_of_sum({a},{b}) returned {c}.')
28
29        random_name_ascii: str = ''.join(random.choice(string.ascii_letters) for _ in range(10))
30        sample_class_with_random_name = SampleClass(name=random_name_ascii)
31        self.get_logger().info(f'sample_class_with_random_name.get_name() '
32                               f'returned {sample_class_with_random_name.get_name()}.')
33
34
35def main(args=None):
36    """
37    The main function.
38    :param args: Not used directly by the user, but used by ROS2 to configure
39    certain aspects of the Node.
40    """
41    try:
42        rclpy.init(args=args)
43
44        node_that_uses_the_library = NodeThatUsesTheLibrary()
45
46        rclpy.spin(node_that_uses_the_library)
47    except KeyboardInterrupt:
48        pass
49    except Exception as e:
50        print(e)
51
52
53if __name__ == '__main__':
54    main()

Indeed, the most difficult part is to make and configure the library itself. After that, to use it in another package, it is straightforward. We import the library.

import random
import string

import rclpy
from rclpy.node import Node
from python_package_with_a_library.sample_python_library import SampleClass, sample_function_for_square_of_sum

And then use the symbols we imported as we would with any other Python library.

    def timer_callback(self):
        """
        Method that is periodically called by the timer.
        Prints out the result of sample_function_for_square_of_sum of two random numbers,
        followed by the result of SampleClass.get_name() for an instance created with
        a ten-character-long ascii string of random characters.
        """
        a: float = random.uniform(0, 1)
        b: float = random.uniform(1, 2)
        c: float = sample_function_for_square_of_sum(a, b)
        self.get_logger().info(f'sample_function_for_square_of_sum({a},{b}) returned {c}.')

        random_name_ascii: str = ''.join(random.choice(string.ascii_letters) for _ in range(10))
        sample_class_with_random_name = SampleClass(name=random_name_ascii)
        self.get_logger().info(f'sample_class_with_random_name.get_name() '
                               f'returned {sample_class_with_random_name.get_name()}.')

Build and source

As always, this is needed so that our new package and node can be recognized by ros2 run.

cd ~/ros2_tutorial_workspace
colcon build
source install/setup.bash

Note

If you don’t remember why we’re building with these commands, see Always source after you build.

Run

Hint

Remember that you can stop the node at any time with CTRL+C.

ros2 run python_package_that_uses_the_library node_that_uses_the_library

Which outputs something similar to the shown below, but with different numbers and strings as they are randomized.

[INFO] [1683598288.149167944] [node_that_uses_the_library]: sample_function_for_square_of_sum(0.19395834493833486,1.3891603395040568) returned 2.506264769030609.
[INFO] [1683598288.149643378] [node_that_uses_the_library]: sample_class_with_random_name.get_name() returned qyOXLBEtzZ.
[INFO] [1683598288.616095880] [node_that_uses_the_library]: sample_function_for_square_of_sum(0.7387236329957096,1.7650481260672202) returned 6.2688730214810775.
[INFO] [1683598288.616604769] [node_that_uses_the_library]: sample_class_with_random_name.get_name() returned LCFNFyzwhk.
[INFO] [1683598289.116050219] [node_that_uses_the_library]: sample_function_for_square_of_sum(0.003813494022560704,1.7056916575839387) returned 2.9224078633691604.
[INFO] [1683598289.116553899] [node_that_uses_the_library]: sample_class_with_random_name.get_name() returned wrtTlOdanZ.