Overview and the most important commands of ROS2

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen


Abb 1: Communication in ROS2 [1]


Absolute basics

  • Each robot-functionality is managed in one entity known as a ROS 2 Node, [here is a great illustration]. ROS2 takes care of the running Nodes: timing, communication,...
  • Communication between nodes is managed via interfaces, which are described using a specific ROS2-format [ROS2].
  1. Topics: a "Publisher" sends messages and one or many "Subsriber" gets them
  2. Service: a "Client" requests informations and a "Server" sends a response
  3. Action: ??
  • Organization in ROS2:
    • A package contains one or more programs (in C++ or Python). "Entry-Points" in their setup.py show ROS2 how to execute them.
    • Every "program" will start at least one Node. Each Node handles a specific robot function and ROS2 takes care of the running Nodes (timing, messaging,...)
    • A Launch-File launches a number of Nodes (from one or more packages)
  • In ROS2 we use multiple terminals
  • At the beginning of a terminal, use this so that the linux-terminal recognises all ROS2-commands

source /SomeWorkspace/install/setup.bash, e. g. source ~/ros2_ws/install/setup.bash

  • Run execubale files ros2 run <package_name> <executable_file>
  • ROS2-workspace: Folder for your ROS2-project, e. g. ros2_ws
  • Launch a launch-file ros2 launch <package_name> <launch_file>
  • Show all running nodes ros2 node list


Step-by-Step

  • Find out your ROS2-Version for the next command
  • Turn on the ROS2-environment via source /opt/ros/<ROS2-Version>/setup.bash
  • Set up your workspace source ~/ros2_ws/install/setup.bash
  • put this command in ~/.bashrc (~ means home-directory in Linux), so that it will be executed each time we start a terminal gedit ~/.bashrc

ROS-packages [ROS2-docs]

What is a package

Every program that we want to execute or create is organized in ROS2-packages. They are either created in C/C++ or Python.

A Python-package consists of:

  1. package.xml - meta-information (maintainer, dependencies, etc.)
  2. setup.py - instructions how to compile the package
  3. setup.cfg - instructions how to install the package
  4. <package_name> - folder whith the name of your package. All our Python scripts etc. go here. Contains an empty __init__.py file at the beginning.

Show ROS2-packages

show all: ros2 pkg list

show only my "package_name": ros2 pkg list | grep <package_name>

Step-by-Step [go here for example-files]

  1. Go to the src-directory of your workspace-folder: cd/ros2_ws/src
  2. Create your package <package_name>: ros2 pkg create --build-type ament_python --license Apache-2.0 <package_name> --dependencies <package_dependency_1> <package_dependency_2>
  3. Create a launch-folder: create folder with mkdir launch so that you get src/<package_name>/launch
  4. Create a launch-file: This step can be skipped. Launch-files allow to start different excecutables in one command. This launch-file can be started via ros2 launch <package_name> <launch_file>
    from launch import LaunchDescription
    from launch_ros.actions import Node
    
    def generate_launch_description():
        return LaunchDescription([
            Node(
                package='<package_name>',# name of the package that contains the ROS2 program to execute
                executable='<executable_of_my_package>', # executable=name of the Python entry point defined in the setup.py.
                output='screen', # type of output channel for printing the program's output
                emulate_tty=True), # enable colors in the terminal
        ])
    
  1. Modify setup.py: In order for ROS2 to find the .launch.py-files, we make sure the data_files in setup.py looks like
    # this is for finding
    import os
    from glob import glob
    #...
    data_files=[
            ('share/ament_index/resource_index/packages',
                ['resource/' + package_name]),
            ('share/' + package_name, ['package.xml']),
            (os.path.join('share', package_name), glob('launch/*.launch.py'))
        ],
    
  2. Create a program: Create MyFirstPythonROSProgram.py, see Example of a Python-File for ROS2 with Important Basic Principles
  3. Create a shortcut for the methods in a package: They are called executables/entry points/... Enter the name of the package in "entry_points" in the setup.py of your package
        entry_points={
            'console_scripts': [
                <name_of_executable> = <package_name>.<NewPythonScript>:<SomeMethod>,
                '<executable_of_my_package> = somepackage.MyFirstPythonROSProgram:main',
            ],
        },
    
  4. Build package: Execute this command in your workspace-folder (e. g. ros2_ws) to compile all packages colcon build or a specific one colcon build --packages-select <package_name>
  5. Refresh: After package-creation always do source ~/ros2_ws/install/setup.bash to let ROS2 recognize all updates
  6. Test: ros2 run <package_name> <name_of_executable>

Topics

  • Print all available topics ros2 topic list
  • Search for specific topic ros2 topic list | grep <topic_name>
  • Get information about a topic ros2 topic info <topic_name>
    • Get more details including the Quality of Service (QoS) ros2 topic info <topic_name> -v
    • "Type" is the interface. Show it via ros2 interface show <Type>
    • This gets us a list of "fields" which are the data fields / variables a topic transmits.
  • Listen to a topic (stop with Cntrl+C) ros2 topic echo <topic_name>
  • Listen to a topic once ros2 topic echo <topic_name> --once
  • Listen to a topic and write the output to a csv-file ros2 topic echo <topic_name> --csv > <file_name>.csv
  • Listen to a field of a topic ros2 topic echo <topic_name> --field <field_name>
  • Show camera data in an extra window ros2 run rqt_image_view rqt_image_view <topic_name>/.../image
  • Publish a message to a topic ros2 topic pub <topic_name> <interface_name> <message>
  • Publish a message ros2 topic pub --once /SomeMessageName std_msgs/msg/String "data: 'Hello World'"
  • This topic should show here ros2 topic list
  • Get more information ros2 topic info /SomeMessageName . This way we get the Type=interface std_msgs/msg/String
  • Getting morge info about the interface ros2 interface show std_msgs/msg/String . This gets us at the end all variables, here it's only one string data

Graphic Tools for Topics

  • RQT: start via rqt , select Plugins/Topics/TopicMonitor and activate the required topic
  • not testet yet: rqt_graph, PlotJuggler, Foxglove Studio


ROS2: Template to create a subscriber

Click on the link above to jump to the template.

Interfaces

  • show all ros2 interface list
  • an interface has to be created in a CMake-package (there is no way in a Python-package yet)
  • an interface is used by messges (msg), servers (srv),...

Step-by-Step

  1. Go to src-folder of your workspace ~/ros2_ws/src
  2. Create package ros2 pkg create --build-type ament_cmake --license Apache-2.0 <InterfaceCPackage>
  3. Create a msg/srv-directory inside the <InterfaceCPackage> interfaces-folder and a msg/srv-file
    cd ~/ros2_ws/src/<InterfaceCPackage>
    mkdir msg
    touch msg/<SomeInterface>.msg
    
  4. Modify the msg-file:
    geometry_msgs/Point center # This custom message uses a message from another message package (geometry_msgs/Point in this case).
    float64 radius # Some new variable
    
  5. Add some stuff to the CMakeLists.txt file:
    find_package(geometry_msgs REQUIRED)
    find_package(rosidl_default_generators REQUIRED)
    
    rosidl_generate_interfaces(${PROJECT_NAME}
      "msg/Num.msg"
      "msg/Sphere.msg"
      "srv/AddThreeInts.srv"
      DEPENDENCIES geometry_msgs # Add packages that above messages depend on, in this case geometry_msgs for Sphere.msg
    )
    
  6. Add some stuff to the packages.xml file:
    <depend>geometry_msgs</depend>
    <buildtool_depend>rosidl_default_generators</buildtool_depend>
    <exec_depend>rosidl_default_runtime</exec_depend>
    <member_of_group>rosidl_interface_packages</member_of_group>
    
  7. Go to the main workspace folder cd ~/ros2_ws
  8. Compile in the workspace-folder: colcon build --packages-select custom_interfaces
  9. Source (to let ros2 know all changes) source install/setup.bash
  10. Chek if it worked ros2 interface show <InterfaceCPackage>/srv/<Interface-Name-of-msg-or-srv>

Services

Services use a call-response model. A service only provides data when called explicitly by a client. Multiple clients can use the same service-server, but there is only one server for one service.

Both topics and services use interfaces.

  • Show a list of services ros2 service list
  • Get the type of service ros2 service type <name_of_service>
  • Get details of the variables in the interface, e. g. ros2 interface show std_srvs/srv/Trigger
  • Get the exact format ros2 interface proto std_srvs/srv/Trigger
  • Call a service ros2 service call <name_of_service> <name_of_interface> <interface_request_message> e. g. ros2 service call /some_self_made_service std_srvs/srv/Trigger "{}"

ROS2: Template to program a Server

Click on the link above to jump to the template.

ROS2: Template to program a Client

Click on the link above to jump to the template.


Multithreading

Important basics:

  • ROS2-Nodes can only execute one callback at a time
  • A node excecutes the callbacks "First In First Out" (FIFO), so that the next callback has to wait until the first one is finished

Comparison

Feature MutuallyExclusiveCallbackGroup ReentrantCallbackGroup
Core Idea Only one callback from this group runs at a time Multiple callbacks from this group can run in parallel
Thread Safety Assumption Assumes callbacks are NOT thread-safe Assumes callbacks ARE thread-safe
Concurrency Inside the Group ❌ No parallel execution within the same group ✅ Callbacks in the same group may execute simultaneously
Concurrency Across Different Groups ✅ Yes (if executor has multiple threads) ✅ Yes
Same Callback Re-entering Itself ❌ Never happens ✅ Possible (e.g., timer fires again before previous run finished)
Best For • Shared mutable state
• Hardware drivers
• State machines
• Non-thread-safe libraries
• Pure computation
• Async service calls
• Action clients
• Callbacks that wait on futures
Risk Level Safer, but can block other work Higher risk if you forget locking
Deadlock Risk ⚠️ Possible if callback waits on another callback in the same group Lower (group doesn’t block itself), but still possible with bad locking
Typical Use Case Sensor callback updates shared data used by a timer A callback that makes async service requests and waits for results
Performance Lower parallelism Higher parallelism
Default Mental Model “Like a single-threaded section” “Like a thread pool section”
Works Well With MultiThreadedExecutor to isolate other groups MultiThreadedExecutor to actually get parallelism
What It Prevents Race conditions inside the group Nothing — you must handle synchronization

Tips

If your callback… Use
Touches shared state without locks MutuallyExclusive
Calls async services / actions Reentrant
Might block or wait Reentrant (or move to different group)
Talks to hardware MutuallyExclusive
Is pure math / stateless Reentrant

Coordinate Systems

coordinate system = coordinate/reference frame = transform = transformation frame (TF)

xyz = red green blue = RGB

View coordinate systems

Show a graph in a pdf-document to analyze the coordinate frame structure of a project:

ros2 run tf2_tools view_frames

Get the same graph, but in a window which you can refresh and see changes in real-time:

ros2 run rqt_tf_tree rqt_tf_tree

Show the link between 2 coordinate systems

ros2 run tf2_ros tf2_echo reference_frame get_frame

View coordinate systems via RViz2

  • open RViz rviz2
  • set the fixed frame via Global Options/Fixed Frame
  • add the frame view via Add/TF
  • Add/Image and set up "Description Topic" and "Quality of Service QoS" [in which quality the signal is to be received]
  • Add/RobotModel and set up "Description Topic"
  • and save the rviz2-config!

Create a coordinate system "NewSystem"

ros2 run tf2_ros static_transform_publisher --x 0 --y 4.0 --z 0 --roll 0 --pitch 0 --yaw 0 --frame-id SomeParentSystem--child-frame-id NewSystem