ROS Nodes
Creating nodes
Motivation
A toy example
What is a Node
ROS Node
A node is a process that performs computation, and uses a client library to communicate with other nodes. Nodes are combined together into a graph and communicate with one another using streaming topics, RPC services, and the Parameter Server.
Sources: About Nodes - ROS2 Docs ↗ , Nodes - ROS Wiki ↗ .
Benefits of nodes:
- Fault tolerance: as crashes are isolated to individual nodes (remember that nodes are individual processes).
- Reduce code complexity: is reduced in comparison to monolithic systems.
- Expose a minimal API: Implementation details are also well hidden for other nodes.
- Language agnostic
- Modularity
- Code reuse
Nodes can communicate with other nodes within:
- the same process
- in a different process
- on a different machine.
Important:
Ideally each node should do one logical thing.
Namespace: prefix to apply to entities associated with the node (node name, topics, etc).
Creating a Bare Minimum Node
A bare minimum node without any ROS entity.
import rclpy
def main(args=None) -> None:
rclpy.init(args=args)
node = rclpy.create_node("bare_node")
rclpy.spin(node)
rclpy.shutdown()
if __name__ == "__main__":
main()
Remember that packages of type ament_python
are structured like a standard python packages using setuptools
↗ . So, to make your node usable as a console script
add an entry_points
kwarg inside the setup()
function within the setup.py
file of your package. Check Setuptools - Entry Points ↗ for more details.
...
entry_points={
'console_scripts': [
'bare = python_basics.bare:main',
],
},
...
Node entry points structure description.
ENTRY_POINT_NAME = PACKAGE_NAME.MODULE_NAME:ENTRY_POINT
Running Nodes
You can run ROS nodes using the ROS cli
with the following format:
ros2 run <package_name> <node_entry_point_name>
In this case:
ros2 run python_basics bare
erros?… here’s a list of common mistakes you may be making:
ros2 command not found
(assuming you have ROS correctly installed using debian packages) Have you already source the underlying workspace?source /opt/ros/humble/setup.bash
Package 'python_basics' not found
(assuming you have the package in thesrc
folder in your workspace)- remember that every time you have new code files that you want to execute you must build the workspace, have you already done it? in the root directory of your workspace
colcon build --symlink-install
- have you already activated the overlaying workspace (your worksapce)?
source ~/ros2_ws/install/local_setup.bash
- remember that every time you have new code files that you want to execute you must build the workspace, have you already done it? in the root directory of your workspace
clearly not all possible error cases can be covered, so it is advisable to read the error message and try to understand it (I know, sometimes, just sometimes, error messages are not very useful).
In case you haven’t wondered yet. You: “Wait!?, my node is python, why do I need to build my workspace if python is interpreted and does not require compiling.?”. Let me tell you that is an excellent question, colcon build
does not only compile code, it has the potential to do many more things that we will not mention here (but you can see colcon docs ↗ ), in the case of ament_python
packages colcon does not compile the code, but instead copies it from your package to the lib
directory inside the install
folder of the workspace. However this copy alone is not enough, you must source the workspace in a way that lets ROS know that there are new files in the install folder.
The --symlink-install
directive tells colcon not to perform a hard-copy (default behavior) but to perform a symlink of the files, this option is extremely useful when you are developing with python as it allows you to make changes to your file and because it is just a symbolic link the changes are automatically reflected from the file perspective in the install
folder, which means that if you use --symlink-install
you don’t have to do colcon build
for every time you change the file (example bare.py
), but you have to redo colcon build --symlink-install
once every time there is a new file, this is for the purpose of creating the symlink of the new file. e.g. a new node called new.py
and then you can just keep changing the file since the symlink already exist.
The symlink concept is not exclusive for nodes, but also applies to any type of shared resource such as launch files, configurations, urdfs etc. Just keep in mind that currently (January 2024) there are no mechanisms to create symlinks for the resources specified in the . You can symlink data files in packages of type setup.py
section data_files
of your ament_python
ament_python
, thanks to the PR Implement a custom distutils command to symlink data_files #592 ↗
Coding a Minimum Logger
coding a logger (logger.py
) in the package python_basics
, code here ↗
import rclpy
def main(args=None) -> None:
rclpy.init(args=args)
node = rclpy.create_node("logger_node")
rate = node.create_rate(0.5)
counter = 0
while rclpy.ok():
node.get_logger().info(f"hello {counter}")
counter += 1
rclpy.spin_once(node)
rate.sleep()
if __name__ == "__main__":
main()
Don’t forget to add the entry point in the setup.py
: logger = python_basics.logger:main
Using Classes (Recommended)
coding a logger using classes (class.py
) in the package python_basics
, code here ↗
import rclpy
from rclpy.node import Node
class MyNode(Node):
def __init__(self) -> None:
super().__init__("my_node")
self.count = 0
timer = self.create_timer(0.5, self.callback)
def callback(self) -> None:
self.get_logger().info(f"Hello {self.count}")
self.count += 1
def main(args=None) -> None:
rclpy.init(args=args)
node = MyNode()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == "__main__":
main()
Don’t forget to add the entry point in the setup.py
: class = python_basics.class:main
Capabilities
The real power of nodes in ROS lies in their ability to contain various ROS entities:
- Publisher: this entity allows a node to publish information.
- Subscriber: allows a node to subscribe information.
- Services
- service client: allows a node to request a service (from a service server).
- service server: allows a node to response to a request.
- Actions
- action client: allows a node to request an ACTION service.
- action server: allows a node to response to an ACTION request.
- Configurable parameters (run-time)
A ROS node can be composed of none, one or several ROS entities, each with their respective interfaces. We will discuss each ros entity and their respective interfaces later in this serie of notes.