Table of Content
- Introduction
- Typical Setting
- ROS Project Structure
- Launch File
- Logging
- Parameters
- Services
- Graph Resource Names
- Commands
Introduction
This post is a quick overview of ROS. Most of the content is based on A Gentle Introduction to ROS.
ROS is a framework that makes the development of robotic applications easier. It provides the following building blocks
- a build system
- a set of graphical tools
- command-line tools
- a central server
- message passing interfaces
- playback functionality
The message passing interface is crucial because it hides all the details of actual communications between different devices. The computation unit of ROS is a node, which is a running process. Note that there is no mention of location in the definition of the node because where the process is running does not matter too much. All we care about is that nodes will communicate with each other using the message passing interface provided by ROS. It's interesting to notice that the node concept has nothing to do with robots, which means the implementation details such as controlling the robot, and making high-level decisions are encapsulated completely and this is the reason why ROS is super powerful.
An immediate consequence of using node as the abstraction level is that the system does not (need to) know if it's interacting with a real robot or a simulator. This makes debugging and experimenting much easier.
Typical Setting
The figure below shows a typical setup. There are three types of nodes
- local node
- remote node
- serial node
Local nodes are running on the host together with the ROS core. Ros core behaves like a server and is the brain of the ROS. Local nodes are used to control sensors and robotic parts connected to the host. Remote nodes are nodes that run remotely and there is no direct wired connection between this node and the ROS. The remote nodes need to know where to find the ROS core and we can provide this information in the environment variable ROS_MASTER_URI
. Serial nodes are nodes that are connected to ROS via serial communication. For example, we can connect Arduino to a Raspberry Pi host using a USB cable and use Arduino to control the motors. With this setting, Arduino becomes a node, similar to a local node that controls a sensor. The only difference is that the communication is done through a serial channel.
Again, from the ROS' point of view, all of them are just nodes.

ROS Project Structure
Typically, a ROS project is implemented in a workspace, which contains almost everything about the project. The code is stored in workspace/src
and is organized using packages. A ROS package is a library or a logical grouping of nodes. It's defined by a package manifest file package.xml
. A node is in turn represented by an executable file which is used to initiate the node it represents. The logical grouping also contains the information about the communication between the nodes therefore packages contain messages used by the nodes. A ROS package also contains other supporting files.
But how does ROS know which workspace to use? After we build the code, we need to execute source devel/setup.sh
in the workspace. This step will inform ROS that we want to use a specific workspace.
The diagram below shows a hello-world example of a ROS workspace.
my_ros_workspace ├── devel │ ├── cmake.lock │ ├── env.sh │ ├── lib │ │ └── pkgconfig │ │ └── agitr.pc │ ├── local_setup.bash │ ├── local_setup.sh │ ├── local_setup.zsh │ ├── setup.bash │ ├── setup.sh │ ├── _setup_util.py │ ├── setup.zsh │ └── share │ └── agitr │ └── cmake │ ├── agitrConfig.cmake │ └── agitrConfig-version.cmake └── src ├── agitr │ ├── CMakeLists.txt │ ├── hello_world.py │ ├── package.xml │ ├── publisher_example.py │ └── subscriber_example.py └── CMakeLists.txt -> /opt/ros/noetic/share/catkin/cmake/toplevel.cmake
Launch File
A better way to launch nodes is to use a launch file. The idea is similar to AWS cloudformation template: we specify the resources in a file. The ROS launch file has the following structure
<launch> <node pkg = package-name type = this-is-the-executable-file name = node-name required = boolean respawn = boolean launch-prefix = "" /> </launch>
pkg
and name
are straightforward. The type
argument is the executable file. Recall that when we use the rosrun
command, we need to provide both package name and executable file and the executable file is the type argument.
If required
is true, it means if this required node is terminated then ROS will terminate all other active nodes. If respawn
is true, it means if the node is terminated then ROS will try to re-initiate the node.
launch-prefix
is an advanced argument. It adds a prefix to the command generated from the execution of the launch file. One of the use cases is to create a new terminal window when using the launch file. In order to do so, we can set launch-prefix="xterm -e"
.
To use the launch file, we can execute the following command:
roslaunch package-name launch-file-name
Other options of the roslaunch
command:
-
--screen
Logging
We can find logging in three places
- console
- topic
rosout
with message type rosgraph_msgs/Log - log file
We are all familiar with the console. Regarding the topic, ROS publishes the logging messages to the rosout
topic and we can view the loggings just like view other ROS messages. The log file is located at ~/.ros/log/run_id/rosout.log
and to get the run id, use the following command
rosparam get /run_id
Parameters
There are four ways to access parameters:
- from command line
- from within code
- in launch files
- reading parameters from a file.
For example, we can set parameters in a launch file by setting:
<group ns="duck_color"> <param name="huey" value="red"/> <param name="dewey" value="blue"/> </group>
Or read parameters from a file by using
<rosparam command="load" file="path-to-param-file"/>
Services
ROS provides two communication mechanisms:
- many-to-many communication via pub/sub
- one-to-one communication via service
Service is less used compared to the publication and subscription pattern. A service is defined by
- service name
- service type
Service name is a string naming the service we want to call. This should be a relative name but can also be a global name. Service type is the name of the service object defined in the header file. Here is an example from ROS website:
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
bool add(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res)
{
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
add_two_ints
is the service name and the service type is beginner_tutorials/AddTwoInts
. The type is used when implementing the callback function, which is the service requester handler.
Note that a service can only handle/process one service type but a service type can be used in different services.
Graph Resource Names
All entities are (graph) resources in ROS, including
- Nodes
- Topics
- Services
- Parameters
Every resource is identified by a resource name. There are three types of resource names:
- global name
- relative name
- private name (a name that starts with a tilde (~) )
Strictly speaking, there is only global name. It's an equivalenet of the absolute file path in an operating system. Relative name and private name will be translated into a global name and it's this translation process that brings more flexibility.
When translating a relative name to a global name, ROS attaches a default name to it. The default namespace is tracked individually for each node and if we don't specify anything, the global namespace (/) is used.
There are three ways to set the default namespace:
- We can set the
ns
attribute in the launch file. - We can use the
__ns:=default-namespace
when launching a node using command line tool. - Use the environment variable
ROS_NAMESPACE
.
When translating a private name to a global name, ROS attaches the node's global name to it. For example, if we have a private name ~my-privaname
and the node is /robot/camera-node
, then the global name after translation is /robot/camera-node/my-private-name
. Therefore, private name is really a private attribute of a node.
Note that one of the differences between a relative name and a private name is that different relative names may share the same default namespace while private names are truly independent.
There is another type of name called anonymous name. It's used to make sure the node name is unique in ROS.
Note: To some extent, package is compile-time grouping of nodes while default namespace is runtime grouping of nodes. The namesapce can be set in the launch file but the launch file is executed at package level. In this sense, default namespace is a more granular control.
Commands
Command | Description |
---|---|
rospack list | List packages |
rospack find package-name | Find the directory of a single package |
rosls package-name | View the files in a package directory |
roscd package-name | Go to the package directory |
rosrun package-name executable-name | Start a node. To overwrite the node name, use __name:=node-name. |
roslaunch package-name launch-file-name | Use launch file to launch nodes. |
rosnode list | Listing running nodes |
rosnode info node-name | Inspect a node |
rosnode kill node-name | Kill a node. |
rosnode cleanup |
Purge the registration of any node that cannot be contacted immediately. Prints list of unreachable nodes which has to be confirmed. IMPORTANT: rosnode cleanup was meant as a temporary solution and its use was not encouraged in normal operation. Its benefit is aesthetic and it has the downside of potentially unregistering functioning nodes. |
rqt_graph | Display the message graph between nodes. |
rostopic list | List active topics. |
rostopic echo topic-name | Echo messages that are being published. |
rostopic hz topic-name | Measure the speed of message publication. |
rostopic bw topic-name | Measure the bandwidth of message publication. |
rostopic info topic-name | Inspect a topic. |
rosmsg show message-type-name | Inspect a message type. |
roswtf | This command is used for sanity checks. |
catkin_create_pkg package-name | Create a package. |
rosparam get /run_id | Query the run id. |
rosparam list | List parameters |
rosparam get parameter-name | Get the paramter values. |
rosparam get namespace | Get parameters in a namespace. |
rosparam set parameter-name paramter-value | Set parameter values. |
rosparam dump filename namespace | Dump parameters to a file. |
rosparam load filename namespace | Load parameters from a file. |
rosservice list | List all services |
rosnode info node-name | List services by node. |
rosservice node service-name | Find the node offering a service. |
rosservice info service-name | Find the data type of a service. |
rossrv show service-data-type-name | Inspect service datae types. |
----- END -----
©2019 - 2022 all rights reserved