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