====== Lab 01 ======
During this lab, you should learn how to use the //Apptainer//, the //tmux//, the //terminal commands of Robot Operating System (ROS)//, and how to //program a simple python subscriber/publisher node//.
Slides used in this lab are available here: https://docs.google.com/presentation/d/1rLQQllG8z78JBPfMsZ-KJiQbpIWFphqULLs1VxF0gbQ/edit?usp=sharing (they are not standalone, they only make sense with the live lab program; use Faculty google account to access them).
===== Apptainer =====
Apptainer is software for virtualization via containers that allows you to program and test ARO homeworks and semestral work without installing all ROS dependencies on your computer.
You can learn how to use Apptainer on Lab PCs and/or details how to install/build it on your computer [[courses:aro:tutorials:ros|here]].
* **For now with your notebook with Linux OS, you can install Apptainer, download the image, and run the image using: **
git clone https://gitlab.fel.cvut.cz/robolab/deploy.git
./deploy/scripts/install_apptainer
./deploy/scripts/download_apptainer_image
./deploy/scripts/start_apptainer_aro
**The start_apptainer_aro script not only starts the Apptainer container, but also creates appropriate ROS workspace, clones the student packages to the workspace, builds the workspace, and sources the workspace.** Details of this process are described [[courses:aro:tutorials:ros#behind_the_scenes|here]].
After starting the Apptainer container, try to run the simulation which will be part of your first homework using command:
# in 1st terminal window:
ros2 run rmw_zenoh_cpp rmw_zenohd
# keep zenohd running and call this in the 2nd terminal:
ros2 launch aro_reactive_control hw01_sim.launch.xml
===== Terminal =====
You will need to be able to use terminal in Linux. Here are a few tips for what you will need:
* ''cd dir'' - changes current directory
* ''pwd'' - prints current directory
* ''mkdir dir'' - creates directory ''dir'' in current directory
* ''chmod +x file.py'' - make ''file.py'' executable
* ''./file.py'' - execute (run) ''file.py'' using the program mentioned on the first line of the file (the **shebang**)
* typing just ''file.py'' will not work!
* **Ctrl-C** - terminate currently running program
* Specifically with ROS, sometimes not all processes are cleanly stopped when stopping launched nodes. The following command will stop all ROS-related processes: ''pkill -f "(ros|workspace|aro_|gazebo|ruby)"'' (beware that it also kills the zenohd which you need to launch again).
* ''htop'' - view list of running processes (use F9 to terminate selected process)
* ''sudo'' - this is a program that gives you administrator (root) permission. If you are not eligible, you will not get them! ''sudo'' does not work inside Apptainer containers.
* To launch a terminal, you can try Ctrl-Alt-T. If it doesn't work on your distro, press the Windows button and type ''terminal''. It will launch a terminal app like ''gnome-terminal'', ''konsole'', ''xterm'' or similar.
* Pressing Ctrl-S freezes the current terminal and you cannot do anything in it. You can escape this by pressing Ctrl-Q.
* ''nano'', ''emacs'', ''vim'' or ''nvim'' are console text editors.
The following key shortcuts are super cool quality-of-life helpers, give them a try!
* Arrow keys up/down go through command history.
* '''' (one or two presses) shows you autocompletion options (filenames, packages, topics...)
* **Ctrl-R** allows you to search through command history (first press Ctrl-R, then type a substring from the command you search for, then press either Tab if you found it, Enter to directly execute it, or Ctrl-R again to search backwards in history with the same substring)
We've noticed some setups in which Tab completion doesn't work. If you think this problem affects you, please tell us on the forum a little bit more about your setup.
===== Terminal multiplexer (tmux) =====
**Tmux allows you to switch easily between several windows in one terminal.**, detach them (they keep running in the background) and reattach them to a different terminal.
To start Tmux use:
* ''tmux'' - starts a new tmux session
* ''tmux a'' - attaches to an existing session
Inside tmux session, press //$prefix// (''ctrl+b'') followed by any of the keys below to control the session:
* //$prefix//''+ d'' - detach from the session while it keeps running
* //$prefix//''+ c'' - create new window
* //$prefix//''+ w'' - list windows with option to select one to see
* //$prefix//''+ n'' - next window
* //$prefix//''+ p'' - previous window
* //$prefix//''+ ,'' - name window
* //$prefix//''+ %'' - split vertically
* //$prefix//''+ "'' split horizontally
===== Robot Operating System (ROS) =====
Robot Operating System (ROS) [[https://www.ros.org/|https://www.ros.org/]] is a set of software libraries that allows you to build a complex robotic system.
It is **not an operating system** but rather a very **convenient middleware for sharing data** (messages) between different parts of the robot's software (nodes).
When using ROS you can leverage the work of others and use an already very large code base of implemented packages for, e.g., mapping, localization, control, planning, and sensor interfacing.
We teach ROS 2 (version Kilted Kaiju). You can still find many links and references to ROS 1 on the web (versions e.g. Melodic Morenia or Noetic Ninjemys). ROS 1 and 2 are not compatible at all, although the core concepts are similar. When searching the web for resources, always check if you found a ROS 2 or ROS 1 answer (ROS 2 started being used around year 2020, so anything older is probably ROS 1).
Key aspects/buzzwords to know are:
* **ROS architecture** - ROS 2 uses decentralized architecture where nodes (programs) that want to communicate announce themselves using a decentralized protocol. After this **discovery phase**, all other nodes are known and the communication can start happening peer-to-peer.
* **RMW (ROS MiddleWare)** - these are the actual communication layer implementations. Most are based on DDS, but we use another RMW called ''rmw_zenoh_cpp'' which is not based on DDS (and is thus a little simpler to setup). The default RMW is currently rmw_fastrtps_cpp (which is based on DDS).
* **DDS (Data Distribution Service)** - is an old distributed communication standard used e.g. in automotive and other industrial settings. Its default network layer is based on UDP and multicast data sending. We do not use DDS in ARO.
* **zenohd** - rmw_zenoh_cpp uses a centralized discovery approach instead of the default decentralized on. ''zenohd'' is the central discovery broker. You have to have ''zenohd'' running for ROS 2 nodes to see each other, even on the same computer.
* **programming** - We will use //Python 3//, but ROS 2 also supports programming in //C%%++%%//, //C//, //Rust// and many more languages.
* **RCL (ROS Client Library)** - the actual libraries for using ROS in some programming language are always called ''RCL-something''.
* Python has ''rclpy''.
* C++ has ''rclcpp''
* C has ''rclc''
* Rust has ''rclrs''
* **version** - We will use //ROS 2 Kilted Kaiju// inside Ubuntu 24.04 inside Apptainer image.
* **package** - Software in ROS is organized in packages. Usually, they contain individual nodes with different purposes.
* **node** - Executable program that runs some part of the robot's software and uses ROS to communicate with other nodes. Multiple instances with different names can run simultaneously.
* **message type** - ROS data type used to send information between //nodes//.
* A more general term is //interface//. That contains messages (defined in ''.msg'' files), services (''.srv'' files) and actions (''.action'' files).
* You can also meet the term IDL (Interface Definition Language). This is a language used by DDS RMWs to describe interface structure (''.idl'' files). The ROS Interface definitions (''.msg'') can be automatically converted to IDL (''.idl'') files.
* **message** - an instance of a message type (i.e. a particular data object).
* **topic** - Named communication channel for particular message types, e.g., camera image topic "/camera/image" for messages of type ''sensor_msgs/msg/Image'', that the nodes use to send/receive data from.
* **publisher** - Part of a node that sends a //messages// of some type to a particular //topic//. There can be multiple publishers on the same topic.
* **subscriber** - Part of a node that receives all //messages// of a particular //topic//. There can be multiple subscribers on the same topic.
* **QoS (Quality of Service)** - configuration of publishers and subscribers that defines how reliable the message sending/delivery has to be.
* **launch file** - Recipe that can start multiple nodes with given parameters simultaneously.
* **workspace** - Place for your project with multiple ROS packages.
* **colcon** - The build system that builds your ROS workspace packages. It understands both CMake and Python packages. Also, it auto-generates specified messages based on their definition.
* **rosbag** - File format in ROS for storing ROS message data allowing to log all transmitted messages within the robotic system.
* rosbag is actually a folder with a metadata YAML file and one or more actual data files
* **MCAP** - The particular storage format used by rosbag data files (''*.mcap'' files). There was also an older one based on SQLite (''*.db3'' files) but it's not suggested anymore.
The code for your projects with ROS is stored in a workspace.
Your workspace is created, built, and sourced automagically using the deploy/scripts/start_apptainer_aro script. So no need to create your workspace manually.
**In case you would like to create your own workspace in some folder, you can do it by running the following commands in the started Apptainer container:**
You do not need to run these commands to use the default ARO workspace because we have already created it for you.
cd
mkdir -p new_ws/src
cd new_ws
# Now source the "base workspace" that your workspace extends
source /opt/ros/aro/setup.bash
colcon build --symlink-install
source install/setup.bash
This code initializes your workspace as an extension of the default ARO workspace.
Then it calls the colcon tool to build the workspace.
Finally, it sources the workspace to //load// information about all built packages.
**Now you can place any of your packages into the src folder and after repeating colcon build and sourcing parts you can use them.**
Please note that even when you are using Python, which normally does not require a build step, ROS requires you to re-build and re-source the workspace in some cases - most notably when you add a new package, add a new file to a package, or change the setup.py or CMakeLists.txt.
It is very important to call ''colcon build'' in the correct folder. You have to call it in the workspace folder (i.e. the folder that contains ''src'' and ''install''. We made a convenience script for you that handles this and a few other tricky parts: call ''./build.sh'' instead of ''colcon build'' in your ARO workspace.
==== ROS in terminal ====
ROS has many handy command line programs that you can learn. You won't use them to create the actual ROS programs (nodes), but you will be using them routinely when inspecting and debugging running ROS systems.
Please note that due to the distributed architecture of ROS, some commands can output incomplete information - most notably when run for the first time.
* ''ros2 run rmw_zenoh_cpp rmw_zenohd'' - starts the zenohd central broker for rmw_zenoh_cpp. **This needs to be running in some terminal all the time!**
* ''ros2 node list'' - shows all running nodes
* ''ros2 node info '' - gives you information about a specified node
* ''ros2 topic list'' - shows all currently available topic names
* ''ros2 topic info '' - shows information about a given topic (pass ''-v'' parameter to get more info)
* ''ros2 topic hz '' - shows frequency of messages published on a topic
* ''ros2 topic echo '' - prints all messages on a given topic (you usually want ''--flow-style'')
* ''ros2 topic type '' - shows message type of the topic
* ''ros2 topic pub '' - publishes a message to a topic
* writing the correct syntax of '''' is difficult; you should use Tab completion. After typing the topic type, type these two characters: ''\''' and then press the Tab key (once). After that, use only left/right arrows to navigate in the completed data structure. Using up/down arrows will make it disappear!
* ''ros2 interface list'' - shows all available interfaces (messages/services/actions)
* ''ros2 interface show '' - displays the definition of a given interface type
* ''ros2 interface package'' - lists interfaces of a certain package
* ''ros2 launch '' - launches the of a given
* ''ros2 bag record'' - record a rosbag
* ''ros2 bag play'' - play existing rosbag (pass the path to the folder, not the MCAP file)
* ''ros2 bag info'' - gives you information what is inside a rosbag.
* ''ros2 run '' - starts node from package .
* ''colcon build'' - build workspace you are in (you usually want ''--symlink-install'' for local development).
**Test the following individual commands. Use the tab for suggestions of node names, topic names, message types, etc.**
- Start tmux and run ''ros2 run rmw_zenoh_cpp rmw_zenohd'' .
- Start turtlesim: ''ros2 run turtlesim turtlesim_node'' .
- Create new tmux window and test all ''ros2 node'' commands on **/turtlesim** node.
- Test ''ros2 topic'' (including ''echo'') commands on the listed topics in ''/turtle1'' namespace in the same tmux window.
- Test listing all available messages using ''ros2 interface list --only-msgs'' and find out what messages are in ''turtlesim_msgs'' package. Show the definition of one of the message types.
- Create a new tmux window (//Ctrl+B+C//) and run an example publisher: ''ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist \' # Now press Tab key to complete the message body and use left/right arrow keys to navigate in it and change values.''
==== ROS for Python ====
In ROS Kilted we will use Python 3.12. The most important library that enables interfacing with ROS is //rclpy//.
The typical content of a package folder in, e.g., ''workspace/src/my_package/'' is:
* ''launch/'' - folder with launch files
* ''msg/'' - folder with message definitions
* ''my_package/'' - folder with source codes where you can put your nodes
* ''resource/my_package'' - an empty file that just has to be there for ROS to work
* ''setup.py'' + ''setup.cfg'' - build files defining the available Python packages and scripts (entrypoints)
* ''package.xml'' - ROS file with information about the package used by the colcon
** Always make sure the python node ''.py'' file starts with the 'shebang' line ''#!/usr/bin/env python3''.**
** Always make sure to ''source install/setup.bash'' in your workspace after you build your workspace with ''colcon build'' or ''./build.sh''**. You need to build the workspace if you have new package, if you change c++ code or, e.g., message definitions. Changing Python code or YAML config contents does not require rebuilding.
You can code in your favorite IDE, but the IDE needs to be run from inside the Apptainer container. See [[ros#using_ides_with_apptainer|Using IDEs with Apptainer]].
Do not set up any kind of **virtual environment** when developing with ROS. No venv, no conda, no uv or poetry! The often installed conda autostart of the base env can interfere with ROS. Please, check [[ros#common_problems|ROS/Common Problems]] to see how to fix it.
\\
=== Initializing a node and running it ===
At the beginning of a Python script (actually usually at the bottom of the file) you need to initialize ROS with ''rclpy.init()'' command.
Afterwards you can initialize all your subscribers, publishers, threaded workers, etc.
Finally you need to run ''rclpy.spin()'' which internally runs the messaging system of the node (especially important for subscribers to run callbacks for new messages).
The actual startup code looks like this:
import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node
class MyNode(Node):
def __init__(self):
super().__init__('my_node')
# Continue with setting up publishers, subscribers etc.
def main():
try:
with rclpy.init(), MyNode() as node:
rclpy.spin(node)
except (KeyboardInterrupt, ExternalShutdownException):
# exit nicely if the program is normally terminated
pass
if __name__ == '__main__':
main()
\\
=== Printing ===
You can print messages to console and log within your package with different severity levels using:
* ''self.get_logger().info('your message')''
* ''self.get_logger().warning('your warning message')''
* ''self.get_logger().error('your error message')''
Find more logging capabilities on https://docs.ros.org/en/kilted/Concepts/Intermediate/About-Logging.html#apis .
\\
=== Messages ===
Messages are data structures used to send data over topics.
They are composed of either simple or complex data types.
Simple types are, e.g.: bool, int, uint, float (with %%N being {8, 16, 32, 64}%%), String, Time, Duration (see standard messages ''ros2 interface package std_msgs'').
Complex data types are composed of simple ones or their arrays (see, e.g., ''ros2 interface info std_msgs/Header '').
Custom messages can be defined in message files, and their respective C++ headers and Python objects are generated during build.
Available message definitions can also be found on the [[https://index.ros.org/|ROS Index]], e.g. [[https://index.ros.org/p/std_msgs|std_msgs]] package. Click [[https://docs.ros.org/en/kilted/p/std_msgs/|API docs]] in the upper right corner to get a nice view of the message definitions.
\\
{{ :courses:aro:tutorials:ros_index.jpg?direct |}}
All released message definitions can be found in the list on [[https://index.ros.org/p/rosidl_default_runtime/#kilted-deps|index.ros.org]].
\\
=== Publisher ===
Publisher is an object assigned to a specific topic and message type that you can use to send messages from your node to other nodes.
You can create publisher object using:
''publisher = self.create_publisher(std_msgs.msg.String, 'message', 10)''.
Then every time you want to send a message you create a new message object
''msg = String()''
, then fill it with your data (''msg.data="ahoj"'') and publish it to ROS using
''publisher.publish(msg)''.
Also try to publish to topic ''/turtle1/cmd_vel'' and control the turtle in turtlesim!
\\
=== Subscriber ===
Subscriber is an object that basically assigns a callback function to received messages of a given topic name and message type.
You can create the subscriber using:
''subscriber = self.create_subscription(std_msgs.msg.String, 'message', callback, 10)''
, where the ''callback'' is a custom function (''def callback(msg):'') that handles the received message.
Also try to subscribe topic ''/turtle1/pose'' and print the pose of the turtle!
===== Lab task =====
Implement simple publisher and subscriber nodes as specified below.
You can get inspiration on how to write such Python nodes in the following [[https://docs.ros.org/en/kilted/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html | ROS tutorial]].
Both can be placed inside the aro_reactive_control/scripts of the student-packages.
- Run turtlesim using ''ros2 run turtlesim turtlesim_node''
- Implement publisher inside ''workspace/src/student-packages/aro_reactive_control/aro_reactive_control/publisher.py'' that publishes String message from std_msgs package on topic "message" every one second and also publishes cmd_vel for the turtle.
- Add an entry for the publisher in ''workspace/src/student-packages/aro_reactive_control/setup.py'' .
- Build the workspace: ''workspace/build.sh'' (or ''./build.sh'' if you are in the workspace directory).
- Source the workspace: ''source workspace/install/setup.bash''
- Launch the publisher using ''ros2 run aro_reactive_control publisher''.
- Implement subscriber inside ''subscriber.py'' that receives the String messages and prints them in the terminal. It should also listen to ''turtle1/pose'' and print the received values.
- Again, add ''subscriber.py'' to ''setup.py'', rebuild and source the workspace.
- Launch the subscriber using ''ros2 run aro_reactive_control subscriber.py''.
===== Homework 01 assignment =====
Follow the assignment of the homework [[courses:aro:tutorials:homework01|HW01]] which requires only some additional work compared to the lab task above.