Creating a Python Node from scratch (for ament_python)
TL;DR Making an ament_python Node
Modify
package.xml
with any additional dependencies.Create the Node.
Modify the
setup.py
file.
Let us add an additional Node to our ament_python package that actually uses ROS2 functionality. These are the steps that must be taken, in general, to add a new Node.
Handling dependencies (package.xml
)
The package.xml
was automatically generated by ros2 pkg create and holds basic information about the package.
One important role of package.xml
is to declare dependencies with other ROS2 packages. It is common for new Nodes to have additional dependencies, so we will cover that here. For any ROS2 package, we must modify the package.xml
to add new dependencies.
In this toy example, let us add rclpy
as a dependency because it is the Python implementation of the RCL. All Nodes that use anything related to ROS2 will directly or indirectly depend on that library.
By no coincidence, the package.xml
has the .xml
extension, meaning that it is written in XML.
Let us add the dependency between the <license>
and <test_depend>
tags. This is not a strict requirement but is where it commonly is for standard packages.
~/ros2_tutorial_workspace/src/python_package_with_a_node/package.xml
1<?xml version="1.0"?>
2<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3<package format="3">
4 <name>python_package_with_a_node</name>
5 <version>0.0.0</version>
6 <description>TODO: Package description</description>
7 <maintainer email="murilomarinho@ieee.org">murilo</maintainer>
8 <license>TODO: License declaration</license>
9
10 <depend>rclpy</depend>
11
12 <test_depend>ament_copyright</test_depend>
13 <test_depend>ament_flake8</test_depend>
14 <test_depend>ament_pep257</test_depend>
15 <test_depend>python3-pytest</test_depend>
16
17 <export>
18 <build_type>ament_python</build_type>
19 </export>
20</package>
After you modify the workspace, build it once
After you add a new dependency to package.xml
, nothing really changes in the workspace unless a new build is performed.
In addition, when programming with new dependencies, unless you rebuild the workspace, PyCharm will not recognize the libraries, and autocomplete will not work.
So,
close PyCharm.
- Run (in the terminal you used to run PyCharm before)
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.
- Re-open pycharm
pycharm_ros2
Creating the Node
In the directory src/python_package_with_a_node/python_package_with_a_node
, create a new file called print_forever_node.py
. Copy and paste the following contents into the file.
1import rclpy
2from rclpy.node import Node
3
4
5class PrintForever(Node):
6 """A ROS2 Node that prints to the console periodically."""
7
8 def __init__(self):
9 super().__init__('print_forever')
10 timer_period: float = 0.5
11 self.timer = self.create_timer(timer_period, self.timer_callback)
12 self.print_count: int = 0
13
14 def timer_callback(self):
15 """Method that is periodically called by the timer."""
16 self.get_logger().info(f'Printed {self.print_count} times.')
17 self.print_count = self.print_count + 1
18
19
20def main(args=None):
21 """
22 The main function.
23 :param args: Not used directly by the user, but used by ROS2 to configure
24 certain aspects of the Node.
25 """
26 try:
27 rclpy.init(args=args)
28
29 print_forever_node = PrintForever()
30
31 rclpy.spin(print_forever_node)
32 except KeyboardInterrupt:
33 pass
34 except Exception as e:
35 print(e)
36
37
38if __name__ == '__main__':
39 main()
By now, this should be enough for you to be able to run the node in PyCharm. You can right-click it and choose Debug print_forever_node. This will output
[INFO] [1683009340.877110693] [print_forever]: Printed 0 times.
[INFO] [1683009341.336559942] [print_forever]: Printed 1 times.
[INFO] [1683009341.836334639] [print_forever]: Printed 2 times.
[INFO] [1683009342.336555088] [print_forever]: Printed 3 times.
To finish, press the Stop button or press CTRL+F2 on PyCharm. The node will exit gracefully with
Process finished with exit code 0
Making ros2 run work
Even though you can run the new node in PyCharm, we need an additional step to make it deployable in a place where ros2 run can find it.
To do so, we modify the console_scripts
key in the entry_points
dictionary defined in setup.py
, to have our new node, as follows
Hint
console_scripts
expects a list
of str
in a specific format. Hence, follow the format properly and don’t forget the commas to separate elements in the list
.
~/ros2_tutorial_workspace/src/python_package_with_a_node/setup.py
1from setuptools import setup
2
3package_name = 'python_package_with_a_node'
4
5setup(
6 name=package_name,
7 version='0.0.0',
8 packages=[package_name],
9 data_files=[
10 ('share/ament_index/resource_index/packages',
11 ['resource/' + package_name]),
12 ('share/' + package_name, ['package.xml']),
13 ],
14 install_requires=['setuptools'],
15 zip_safe=True,
16 maintainer='murilo',
17 maintainer_email='murilomarinho@ieee.org',
18 description='TODO: Package description',
19 license='TODO: License declaration',
20 tests_require=['pytest'],
21 entry_points={
22 'console_scripts': [
23 'sample_python_node = python_package_with_a_node.sample_python_node:main',
24 'print_forever_node = python_package_with_a_node.print_forever_node:main'
25 ],
26 },
27)
The format is straightforward, as follows
|
The name of the node when calling it through ros2 run. |
|
The name of the package. |
|
The name of the script, without the |
|
The function, within the script, that will be called. In general, |
Once again, we have to refresh the workspace so we 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.
And, with that, we can run
ros2 run python_package_with_a_node print_forever_node
which will output, as expected
[INFO] [1683010987.130432622] [print_forever]: Printed 0 times.
[INFO] [1683010987.622780292] [print_forever]: Printed 1 times.
[INFO] [1683010988.122731296] [print_forever]: Printed 2 times.
[INFO] [1683010988.622735422] [print_forever]: Printed 3 times.
To stop, press CTRL+C on the terminal and the Node will return gracefully.