Skip to content

Latest commit

 

History

History

ex01_first_package

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

ex01_first_package

ROS is short for Robot Operating System. It is a framework/middleware for the quick development of software related to robotics. It provides the infrastructure for sensor and actuator communications, sensor data handling, serialization, visualizations, and software packaging. All these things don't change for different types of robots. Roboticists are happy

  • to not implement everything from scratch for every robot they have,
  • that they can use other software that was already implemented somewhere in the world.

ROS1 was invented in 2007. It was mostly used in research and is completely open-source. Some design choices were considered bad. In addition, it had no guarantees in real-time executions nor in how safe communications were. Recently ROS2 was released in 2017, seeking to overcome those and other issues.

With the following set of repositories, we try to explain the basic ROS concepts by example.

ROS2

To continue a computer with natively installed Ubuntu 22.04 is required. Come back if you find one.

Great to see you have found one. Now install ROS2 humble with the following steps (official instructions: https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debians.html)

Open a terminal:

Useful packages

sudo apt-get install vim

Preparation

locale  # check for UTF-8

sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

locale  # verify settings
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

Refresh the package sources

sudo apt update

Upgrade all packages you already have

sudo apt upgrade

Installation

Now start the ROS2-humble installation:

sudo apt install ros-humble-desktop

Some additional dev tools:

sudo apt install ros-dev-tools

Environment Variables

In many situations, ROS2 will need additional configuration via setting environment variables. A good way is to add additional environment variables to the ~/.bashrc file (~ is the path to your home folder). Then they will be set every time a new terminal is started. Open the file with Vim (yes with vim) and add the following line to the end of the file:

vi ~/.bashrc

Try to insert this at the end of the file:

source /opt/ros/humble/setup.bash

Exit Vim: https://stackoverflow.com/questions/11828270/how-do-i-exit-vim. The sooner you learn Vim the better. As soon as you restart your terminal this command will set up everything you will need for your global ROS2. Test your installation via

ros2 run demo_nodes_cpp talker

open another terminal (Ctrl+Shift+T) and execute:

ros2 run demo_nodes_py listener

The listener should hear what the talker is saying, even though they are not the same process, and not written in the same programming language. How this works comes soon.

Package

A package is a piece of software that has a closed purpose. For example, one package could be a driver for a sensor, another package could handle the detection of objects in images. Now you will create your very first package. But where to put it? Into a ROS workspace.

It doesn't matter how you call it. We mostly name the workspace after the robots we are using. Since we are later using the "ceres" robot we are calling the workspace ceres_ws:

user@pc:~$ mkdir ceres_ws

all packages will be placed within a src folder inside of this package:

ceres_ws/
  src/
    package1/
      package.xml
      CMakeLists.txt
      ...
    package2/
      package.xml
      ...
    folder/
      package3/
        package.xml
        CMakeLists.txt
        ...
      package4/
        package.xml
        ...
    ...
user@pc:~$ cd ceres_ws
user@pc:~/ceres_ws$ mkdir src 

Source your new workspace by adding the following line to the end of your .bashrc:

source ~/ceres_ws/install/setup.bash

Then create a package:

user@pc:~/ceres_ws$ cd src
user@pc:~/ceres_ws/src$ mkdir ex01_first_package
user@pc:~/ceres_ws/src$ cd ex01_first_package

A folder becomes a ROS package once you put a package.xml into it.

user@pc:~/ceres_ws/src/ex01_first_package$ touch package.xml 
user@pc:~/ceres_ws/src/ex01_first_package$ vi package.xml

Open the package.xml file of this repository to see its contents.

Everything until license are tags providing meta-information about the package. The rest describes what the package needs to compile successfully. The rclcpp package contains everything we need to write C++ code for a robot. So if we decide to write our software in C++, we usually depend on rclcpp. The second package is std_msgs. It is a package that only consists of message definitions.

Messages & Topics

In ROS-Messages we can store and transfer data to other nodes. For example, the message std_msgs/msg/String is defined here.

It is a rather simple message because it only contains one field. The description of a message is compiled into types that can be used in any supported programming language. Like this, we can use the same message in C++ or Python and communicate between programs written in Python and C++. For example, we can write something into the message using C++ and send it

#include <std_msgs/msg/string.hpp>

...

std_msgs::msg::String message;
message.data = "Hello!";
// send message ... (publisher)

and read the data using Python:

import std_msgs.msg.String

...

msg = std_msgs.msg.String()
# receive message ... (subscriber)
print(msg.data)

Communications between nodes are not done directly but with so-called ROS-topics in the middle. So a Publisher is publishing a message on a topic. One ore multiple nodes can subscribe on the topic to receive the ROS-Message that was published on the topic before. This scheme is exceptionally well suited for sensor data: An example from reality: ROS-sensor drivers usually access the sensor on low-level and then publish the sensor data to a topic. For example, a USB webcam driver is accessing the USB interface of the camera and reads the images. The driver doesn't care about who exactly needs the images, so it just publishes the image data to a topic. Then one node wants to detect cats in the images, so it subscribes on the topic, another node wants to filter the image so that only edges remain, so it subscribes on the topic. Maybe in the future, someone implements some new nodes doing cool things with the images, so they will subscribe to the image topic. But the image driver never needs to be changed for that. And on the other side, the image processing nodes will never know there is a USB-interface existing.

Another example is a Radio station where someone is broadcasting something without caring about who is actually receiving it:

radio-ros

Publisher

The next task deals with implementing our own publisher node for our package. It should publish a std_msgs/String on a topic called important_messages. So the Node acts similarly to the sensor-driver-Node described before (except accessing a USB-interface).

Read src/publisher.cpp and try to replicate the contents into your own node. Try without copy-paste.

After writing this file we need to add some instructions to compile the C++ project. ROS internally using CMake for that. So create a CMakeLists.txt at the base folder of your package and add the following contents:

cmake_minimum_required(VERSION 3.8)
project(ex01_first_package)

###########################
# Not important to know start

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# Not important to know end
###########################

# 'find_package' trys to find packages
# usually all dependencies of the package.xml file 
# should occour here too
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

# Compile our node!
add_executable(publisher src/publisher.cpp)

######
# Properly add ROS dependencies
# - header files become available in code
# - libraries are linked
ament_target_dependencies(publisher 
  rclcpp
  std_msgs)

#######
# at execution time ROS2 only knows about things exist
# in the install directory 'ceres_ws/install/...'
# So we install our executables to 'ceres_ws/install/...'
# via
install(TARGETS
    publisher
  DESTINATION lib/${PROJECT_NAME})

# and finish with do ament_package cleanup
ament_package()

The comments explain roughly what is going on during compilation. If you are interested you can read them. But from my experience, you will be good if you just copy-paste those commands. If you understand everything else you can come back to it. Now go to your workspace's base directory and call

user@pc:~/ceres_ws$ colcon build

If no errors occur the compilation has been successful. After that, you can run your node by calling

ros2 run ex01_first_package publisher

Open a second terminal (Ctrl + Shift + T) and enter

ros2 topic list

to list all the topics that are available. The topic important_messages should appear. With

ros2 topic echo /important_messages

You can print the (important) messages from the topic.

Subscriber

Now we want to receive the message in a self-written program. Imagine you want to write a Node that subscribes to an Image to detect cats. But in this example it is a stream of strings:

Read src/subscriber.cpp and try to replicate the contents into your own node. Try without copy-paste.

Afterward, try adding your cpp file to your CMakeLists.txt for compilation. Then call colcon build again. If you are not manage to fill out the CMakeLists.txt correctly, get yourself help by reading the CMakeLists.txt from this repository.

When it compiles, test your program by calling

First Terminal:

ros2 run ex01_first_package publisher

Second Terminal:

ros2 run ex01_first_package subscriber

The subscriber should always print the most recent messages that were sent by the publisher. Even after starting ending and restarting the subscriber.

Better Publisher & Subscriber

Further examples are in src folder of this repository. All publishers are doing exactly the same. As well as all subscribers are doing exactly the same. publisher_class.cpp and subsriber_class.cpp are the recommended styles of writing nodes. It's done by inheriting from rclcpp::Node.

Read it, understand it, execute it.

Last Words

One node does not need to be a subscriber or publisher alone. A node can also be composed of multiple subscribers and publishers. ROS has more useful tools like ros2 topic list or ros2 topic echo. To see them just type ros2 topic and press enter. Test the commands with your topics.

Data Distribution Services

Important: This step is required when working with our robots!!!

The default DDS that is used by ROS is FastDDS. In our tests, it tends to be unstable in networks. Luckily, we can switch to another implementation called CylconeDDS by installing it:

sudo apt install ros-humble-rmw-cyclonedds-cpp

And enabling it by adding the following lines to your .bashrc:

# export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp

Then restart your computer or the ROS daemon

ros2 daemon stop
ros2 daemon start

Now try if your nodes are still working.

Not required but just in case: If you want to recover the default FastDDS use:

export RMW_IMPLEMENTATION=rmw_fastrtps_cpp

Addition: CPP tutorial

In this package, I placed another method of writing a publisher. With src/my_publisher.cpp and include/ex01_first_package/my_publisher.hpp a library is built named my_publisher. This library is installed and can be used by every other package. As an example, the main node src/publisher_lib.cpp includes the header and links to the library.