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:
-
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.
-
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.
-
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 roslaunch
command, 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