Introduction to ROS

The three levels of concepts of ROS are nicely presented at wiki.ros.org/ROS/Concepts with a short summary in the following paragraphs:

  1. ROS Filesystem Level

    The basic unit for software code organization in ROS is a package. It can contain code for processes, liberaries, datasets, configuration files and custom communication types definitions. Packages can be sensibly grouped into metapackages. Each package contains a package manifest (package.xml) that provides the metadata about the package.

     

  2. ROS Computation Graph Level

    In ROS, processes run as nodes in a peer-to-peer network processing data together. This network is called ROS Computation Graph and it is supported by the name registration and lookup services of the ROS Master, by the parameter storing and serving services of the Parameter Server, and by the ROS logging mechanism. ROS nodes communicate by excanging data using messages in communication models called topics, services and actions.

     

  3. ROS Community Level

    There is a large number of ROS developers and robotic systems developers using ROS worldwide. Separate communities can exchange software and knowledge using ROS distributions, code repositories, the ROS Wiki and ROS Answers forums, blog and mailing lists. To get the participants of this course acquainted with the availabe resources, reading through some of the ROS Wiki topics will be encouraged by providing links to the ROS Wiki contents.

Creating a catkin workspace

Catkin is the official build system of ROS and it is used to build ROS packages. Packages are placed (created or installed) in a catkin workspace folder were they can be modified and built.

Creating a workspace is actually creating a new folder using the mkdir shell command. In the workspace folder there has to be a folder called src where all the packages will be put.

The workspace folder and the src folder can be created together using the -p option of the mkdir command:

mkdir -p ~/catkin_ws/src

After creation, the worksapce needs to be initialised as ROS workspace. During the initialisation done by running the catkin_make command a CMakelists.txt file and the folders build and devel are created in the workspace folder.

cd ~/catkin_ws/

catkin_make

To use the packages in the workspace in the ROS system, the workspace needs to be made visible to the ROS system. This is done by running the setup script in the devel folder:

source ~/catkin_ws/devel/setup.bash

# or
# source devel/setup.bash

This script adds the path of the workspace to the ROS_PACKAGE_PATH environmental variable used by the terminal. This change of the environment setting is valid only during the current session in the terminal and has to be repeated every time a new terminal is open. The content of the ROS_PACKAGE_PATH variable can be viewed using echo ROS_PACKAGE_PATH or env | grep ROS_PACKAGE_PATH.

Creating a new package

Source code for the packages is placed in the src folder of the workspace. For a package to be considered a catkin package it must contain a (catkin compliant) package manifest file (package.xml), and CMakeLists.txt file and it must have its own folder.

This is the structure of the simplest possible package:

ros_intro/
    CMakeLists.txt
    package.xml

The package can be created manually or using a catkin_create_pkg script. The script creates the skeletton for a catkin package, running it requires specifying the package name. Optionally a list of dependencies, on which the package depends, can be given.

For creating a new package called ros_intro with dependencies std_msgs and roscpp use:

$ catkin_create_pkg ros_intro std_msgs roscpp

# catkin_create_pkg <package_name> [depend1] [depend2] [depend3]

After creating a new package (and after every modification of the package code) the workspace containig the package needs to be built:

cd ~/catkin_ws
catkin_make

If the workspace has not been added to the ROS environment (in the current terminal), the generated setup file needs to be sourced (see above).

Installing a package

Installation of existing packages can be done in two ways. Binary packages can be installed and managed as debian packages using the apt-get command.

Example of installing a binary package turtlesim:

$ sudo apt-get install ros-melodic-turtlesim

# sudo apt-get install ros-<distribution>-<package_name>

The second way of installation is building the packages from source. In case of installation from a code repository, the code must be downloaded first.

Example of installing a build-from-source metapackage ros_tutorials (a metapackage is a collection of packages):

$ cd ~/catkin_ws
$ git clone -b kinetic-devel https://github.com/ros/ros_tutorials.git

# git clone -b <branch> <address>

After installation the workspace with all the containing packages needs to be built with the catkin_make command run from the workspace folder.

Running ROS nodes

A ROS node is a process of the ROS computation graph. A node is started by running an executable that is part of a ROS package. Before starting a node, the ROS Master has to be running.

The ROS Master provides naming and registration services to the rest of the ROS system, which enables individual ROS nodes to locate one another. It also tracks publishers and subscribers to topics and services and provides the Parameter Server.

ROS Master can be started using the roscore command.

After starting ROS Master in one terminal, another node can be started in a separate terminal using rosrun.

ROS Command-line (CLI) tools

ROS provides some useful tools for ROS system developers that are used by invoking them in the terminal. ROS tools are used to start and manage the running of the ROS system, to interact and inspect the running system, facilitating debugging, to install and build packages, and to simplify working with the filesystem.

To use a ROS tool, a command is called in the terminal. Two from the group of tools were already mentioned, namely roscore and rosrun for starting the ROS system. The ROS system with nodes can also be started with the roslaunch command. These and a few other tools are explained in the following part of the section while some other few will be introduced later together with use cases.

The roscore command starts a ROS Master, a ROS Parameter Server and a rosout logging node which are the pre-requisites of a ROS system. Every other node can only run in the system if the roscore is running and reachable. Read more on roscore on wiki.ros.org/roscore.

rosrun runs a node or set of nodes from an executable from any ROS package. The general syntax is rosrun package executable and an example for running one of the ROS graphical tool, rqt_graph is:

rosrun rqt_graph rqt_graph

To start the next node after running the previous one in one terminal, a new terminal has to be open which sometimes also requires configuring some environmental variables. Running multiple nodes with one command is facilitated by the roslaunchcommand, that launches a set of nodes from an XML configuration file. The syntax for roslaunch is roslaunch package launch_file.

Building executable code

As an example of building executable code for ROS we will use the writing publisher and subscriber example from the ROS wiki.

The task

In the following exercise we will write the code for two nodes:

  • the taker node: publishes a message on a topic (called chatter)
  • the listener node: subscribes to the (chatter) topic and prints the contents of each received message in the terminal

However, we will use the ROS wiki tutorial in slightly modified version as follows:

Creating an empty file for the talker

Create a .cpp file in the src folder of the ros_intro package we created earlier.

This can be done in varius ways, if you choose to do it via terminal, this is the procedure:

cd ~/catkin_ws/src/ros_intro/src
touch talker.cpp

Open the created file in Visual Studio Code and follow the next step.

Writing the code for the talker

Put the following code in the talker.cpp file:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char** argv)
{
    // run ros
    ros::init(argc, argv, "talker");
    // create node handler object
    ros::NodeHandle n;
    // create publisher
    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
    // create timer
    ros::Rate loop_rate(10);

    int count = 0;
    while(ros::ok())
    {
        //create a message object
        std_msgs::String msg;
        std::stringstream ss;
        ss << "hello world " << count;
        msg.data = ss.str();
        ROS_INFO("%s", msg.data.c_str());
        //send message
        chatter_pub.publish(msg);
        ros::spinOnce();
        loop_rate.sleep();
        ++count;
    }
    return 0;
}

Creating a file and writing the code for the listener

As above, create a file named listener.cpp and add the following code:

#include "ros/ros.h"
#include "std_msgs/String.h"

void chatterCallback(const std_msgs::String::ConstPtr& msg) 
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "listener");
    ros::NodeHandle n;
    ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
    ros::spin();
    return 0;
}

Building the nodes

Before building the executable code for the created nodes, the CMakeLists.txt file needs to be updated.

Open the CMakeLists.txt file from the package folder and add the following two lines to the line number 136, under the section Declare a C++ executable, followed by a blank line:

add_executable(talker src/talker.cpp)
add_executable(listener src/listener.cpp)

Add the following two lines to the line number (cca.) 153, under the section Specify libraries to link a library or executable target against, followed by a blank line:

target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(listener ${catkin_LIBRARIES})

In the terminal, go to the workspace folder, build it, and source the setup.bash file:

cd ~/catkin_ws/
catkin_make
source devel/setup.bash

Running the nodes

In the first terminal:

roscore

In the second terminal:

cd ~/catkin_ws/
source devel/setup.bash
rosrun ros_intro talker

In the third terminal:

cd ~/catkin_ws/
source devel/setup.bash
rosrun ros_intro listener

Creating a launch file for launching the talker and the listener

First, we will create a launch folder inside our package:

cd ~/catkin_ws/src/ros_intro
mkdir launch

In the new folder, create an empty launch file named launch_chatter.launch:

cd ~/catkin_ws/src/ros_intro/launch
touch launch_chatter.launch

In the file place the following code:

?xml version="1.0" encoding="UTF-8"?>
<launch>
    <node name="talker_node" pkg="ros_intro" type="talker" output="screen" />
    <node name="listener_node" pkg="ros_intro" type="listener" output="screen"/>
</launch>

To launch the talker and listener with the created launch file, run the following code in the terminal:

cd ~/catkin_ws/
source devel/setup.bash
roslaunch ros_intro launch_chatter.launch