Nodes¶
Introduction¶
This tutorial explains the basics of ROS2 nodes, Launch files and the Parameters. You can use the given links in the documentation for further information.
Lines beginning with $ are terminal commands.
To open a new terminal → use the shortcut
Ctrl + Alt + T
.- To open a new tab inside an existing terminal → use the shortcut
Ctrl + Shift + T
.
To kill a process in a terminal → use the shortcut
Ctrl + C
.
Lines beginning with # indicate the syntax of the commands.
Code is separated in boxes.
Code is case sensitive.
Writing a Node in Python¶
A Python based ROS2 node has to be placed inside a ROS2 package.
Create a Python script inside the subdirectory of your package with the same name. This is necessary for ROS2 to find your Node later.
$ cd ~/robot_ws/src/myfirstpackage/
Start your Programming IDE from the current directory. We recommend to use Visual Studio Code, anyway you are free to use your favorite IDE or develop in a text editor or in a terminal based editor:
$ code .
This will start VS Code and create a project inside the current directory. Now create a new file inside the myfirstpackage sub-directory and name it myfirstnode.py:

Figure 1 Create a new file in VS Code¶
In case your IDE pop ups requests to install extensions, it is usually recommended to use them:

Figure 2 Install IDE extensions¶
Program a ROS2 node:
Given below is an example structure of a ROS2 node. These 11 lines are the most simple way to create a ROS2 node in Python! The coding is a bit ugly, but it will work.
Fill your file myfirstnode.py with the following example code.
#!/usr/bin/env python3
import rclpy
def main():
rclpy.init()
myfirstnode = rclpy.create_node('myfirstnode')
rclpy.spin(myfirstnode)
if __name__ == '__main__':
main()
Explanation¶
Let’s have a closer look to the single core lines that make the Python script being a ROS2 node. The following lines are only explanations of the given code structure. You do not need to enter them in the terminal!
#!/usr/bin/env python3
The first line makes sure the script is executed as a Python3 script. Every Python ROS2 Node must have this declaration at the top.
import rclpy
Imports the Python Client library for ROS2.
def main():
This line defines a function in Python. In this case the funtion is calles main and has no arguments that will be parsed to the function.
rclpy.init()
rclpy.init() will initialize the ROS client library and makes its functions available.
myfirstnode = rclpy.create_node('myfirstnode')
In line 7 we finally make this Python script a ROS2 node. The node is name myfirstnode and during runtime it will be known by this name from the ROS2 environment.
rclpy.spin(myfirstnode)
Line 8 keeps the node spinning forever. Usually the node will now wait for for any task to be done, but in this simple example there is nothing to do. This endless loop keeps running until the Node is shutdown.
if __name__ == '__main__':
This line is a Python specific line to define an entry point for the Python script. For more details read the Python documentation: https://docs.python.org/3/library/__main__.html
main()
Finally in this line the function main that we have defined, will be called and executed and we parse no arguments to it. Therefor it is called with empty round brackets ().
Before we can run our Node, we have to add it to our setup.py
file and build
our package so that the ros2 run
command is able to find the Node.
So let’s edit the setup.py file in the root of myfirstpackage. Initially it should look like this:
from setuptools import setup
package_name = 'myfirstpackage'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='mascor',
maintainer_email='mascor@fh-aachen.de',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
],
},
)
In line 23 we have to add the following code within the console_scripts
brackets of the
entry_points
field:
'myfirstnode = myfirstpackage.myfirstnode:main',
That is the only change, save the file now:
--- /mnt/c/Users/Patrick/Documents/academy-sphinx/_resources/code/tutorials/scripts/initial_setup.py
+++ /mnt/c/Users/Patrick/Documents/academy-sphinx/_resources/code/tutorials/scripts/node_setup.py
@@ -20,6 +20,7 @@
tests_require=['pytest'],
entry_points={
'console_scripts': [
+ 'myfirstnode = myfirstpackage.myfirstnode:main',
],
},
)
→ Now build and source your new built package.
$ cd ~/robot_ws
$ colcon build
$ source ~/robot_ws/install/setup.bash
→ Run the ROS2 node myfirstnode.
$ ros2 run myfirstpackage myfirstnode
→ Display information about the active nodes of the ROS2 system.
$ ros2 node list
This command will show all running nodes:
/myfirstnode
Let’s clean up the code in the Node now and let’s see why that should be done.
First we will add a try
statement around our spin function:
--- /mnt/c/Users/Patrick/Documents/academy-sphinx/_resources/code/tutorials/scripts/simple_python_node2.py
+++ /mnt/c/Users/Patrick/Documents/academy-sphinx/_resources/code/tutorials/scripts/simple_python_node2_with_try.py
@@ -5,8 +5,10 @@
def main():
rclpy.init()
myfirstnode = rclpy.create_node('myfirstnode')
- rclpy.spin(myfirstnode)
+ try:
+ rclpy.spin(myfirstnode)
+ except KeyboardInterrupt:
+ pass
if __name__ == '__main__':
main()
-
With this “trick” we can control what’s happening, if our node is
stopped using Ctrl+C in our terminal. The except
block will
be called, if Ctrl+C is pressed and forces our Python code to
pass
to the next line. Using this, we can make sure that our
Node will be shutdowned properly and not “crash”. So, let’s do that:
--- /mnt/c/Users/Patrick/Documents/academy-sphinx/_resources/code/tutorials/scripts/simple_python_node2_with_try.py
+++ /mnt/c/Users/Patrick/Documents/academy-sphinx/_resources/code/tutorials/scripts/simple_python_node2_with_shutdown.py
@@ -10,5 +10,8 @@
except KeyboardInterrupt:
pass
+ myfirstnode.destroy_node()
+ rclpy.shutdown()
+
if __name__ == '__main__':
main()
First we destroy the node so that the ROS2 environment knows that it does not exist anymore. We also shutdown the ROS Client Library cleanly.
That’s it! Congratulations, you do now have a very clean template for a ROS2 node with Python. Don’t forget to build the package and source the setup.bash file of your workspace in case you want to run your Node now again.