comp.nus.edu.sg/~guoyi / tutorial / cg2111a / ros-topics

Tutorial: Robot Operating System for EPP2


Engineering Principles and Practice II [CG2111A]

Author: Boyd Anderson; Chen Guoyi
Supervisors: Henry Tan; Nguyen Tien Khoa; Ravi S/O Suppiah; Sean Tan Rui Xiang; Tan Keng Yan, Colin.

This tutorial is sponsored by the NUS School of Computing and brought to you by the AY23/24 CG2111A Teaching Team.

Talker & Listener (Optional)

This tutorial serves as an additional exercise for students to learn the very fundamental and important functions (see section 0.1 Objectives) of ROS using "Talker & Listener" as example. While the CG2111A module does not require you to fully understand these methodologies, gaining knowledge in these areas can be immensely beneficial for your future modules that utilize ROS, or even in your career.

It's important to note that in CG2111A, the teaching team will handle these functions for you. Therefore, if you're not interested in the details behind some commands, you may choose to skip this entire tutorial. This flexibility allows you to focus on the aspects of ROS that are most relevant and interesting to you.

Each step in this tutorial is crucial, with later steps building upon the successful completion of earlier ones. Therefore, after finishing each step, please make sure to check the corresponding checkbox in this tutorial. This action will change the status from "Waiting Completion" to "Marked as Completed." This simple practice ensures you're progressing correctly through the material and helps prevent any oversight that could impact later stages.

0. Introduction

In this exercise, students will develop two nodes within the ROS framework: one node will publish a value, and another node will echo the published value. Communication between nodes in ROS is facilitated through topics, akin to channels in which messages are broadcasted and received. To understand this concept, consider the following analogy:

  • Imagine Tom and Jerry, representing two nodes in a network. Tom wishes to send information to Jerry using two different methods: email and telegram, analogous to two separate topics in ROS.
  • In this scenario, Tom acts as a publisher who sends information, while Jerry acts as a subscriber who receives it. For Jerry to receive the information sent via email, he must have an email account—similarly, a node must subscribe to a topic to receive messages broadcasted on that topic.
  • The information relayed through these topics is referred to as a message. For instance, a message could be "I saw 3 students in location 1". If the information pattern repeats with slight variations (e.g., "I saw 4 students in location 2"), the messages can be simplified to just the varying elements, like "3 and 1" followed by "4 and 2".
  • The structure and meaning of these messages are defined in a .msg file, a text file that describes the fields of a message, effectively outlining the data structure contained within a topic.

In this project, your workspace should be named cg2111a_exercise_ws, and your package should be cg2111a_talker_listener.

0.1 Objectives

By the end of this project, students should be able to:

  1. Create a workspace and a package.
  2. Develop nodes and define messages.
  3. Utilize nodes and messages to facilitate one-way communication.
  4. Design and implement a launch file.

For your reference, the following tree structure represents the organization of files and directories within the cg2111a_exercise_ws ROS workspace. This includes the ROS package cg2111a_talker_listener, along with its nodes, launch file, and configuration files.

cg2111a_exercise_ws/
├── build/
├── devel/
│   ├── ...
│   └── setup.bash
├── src/
│   └── cg2111a_talker_listener/
│       ├── CMakeLists.txt  (modified to include talker and listener executables)
│       ├── package.xml  (modified to include necessary dependencies)
│       ├── launch/
│       │   └── cg2111a_talker_listener.launch  (launch file for starting talker and listener nodes)
│       └── src/
│           ├── talker.cpp  (publisher node source code)
│           └── listener.cpp  (subscriber node source code)
└── (other ROS workspace directories and files)
Click here to expand the whole file tree.
~/cg2111a_exercise_ws $ tree
.
├── build
│   ├── atomic_configure
│   │   ├── env.sh
│   │   ├── local_setup.bash
│   │   ├── local_setup.sh
│   │   ├── local_setup.zsh
│   │   ├── setup.bash
│   │   ├── setup.sh
│   │   ├── _setup_util.py
│   │   └── setup.zsh
│   ├── catkin
│   │   └── catkin_generated
│   │       └── version
│   │           └── package.cmake
│   ├── catkin_generated
│   │   ├── env_cached.sh
│   │   ├── generate_cached_setup.py
│   │   ├── installspace
│   │   │   ├── env.sh
│   │   │   ├── local_setup.bash
│   │   │   ├── local_setup.sh
│   │   │   ├── local_setup.zsh
│   │   │   ├── setup.bash
│   │   │   ├── setup.sh
│   │   │   ├── _setup_util.py
│   │   │   └── setup.zsh
│   │   ├── order_packages.cmake
│   │   ├── order_packages.py
│   │   ├── setup_cached.sh
│   │   └── stamps
│   │       └── Project
│   │           ├── interrogate_setup_dot_py.py.stamp
│   │           ├── order_packages.cmake.em.stamp
│   │           ├── package.xml.stamp
│   │           └── _setup_util.py.stamp
│   ├── CATKIN_IGNORE
│   ├── catkin_make.cache
│   ├── cg2111a_talker_listener
│   │   ├── catkin_generated
│   │   │   ├── cg2111a_talker_listener-msg-extras.cmake.develspace.in
│   │   │   ├── cg2111a_talker_listener-msg-extras.cmake.installspace.in
│   │   │   ├── installspace
│   │   │   │   ├── cg2111a_talker_listenerConfig.cmake
│   │   │   │   ├── cg2111a_talker_listenerConfig-version.cmake
│   │   │   │   ├── cg2111a_talker_listener-msg-extras.cmake
│   │   │   │   ├── cg2111a_talker_listener-msg-paths.cmake
│   │   │   │   └── cg2111a_talker_listener.pc
│   │   │   ├── ordered_paths.cmake
│   │   │   ├── package.cmake
│   │   │   ├── pkg.develspace.context.pc.py
│   │   │   ├── pkg.installspace.context.pc.py
│   │   │   └── stamps
│   │   │       └── cg2111a_talker_listener
│   │   │           ├── package.xml.stamp
│   │   │           ├── pkg-genmsg.cmake.em.stamp
│   │   │           └── pkg.pc.em.stamp
│   │   ├── cmake
│   │   │   ├── cg2111a_talker_listener-genmsg.cmake
│   │   │   └── cg2111a_talker_listener-genmsg-context.py
│   │   ├── CMakeFiles
│   │   │   ├── cg2111a_talker_listener_gencpp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_generate_messages_cpp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_generate_messages.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_generate_messages_eus.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_generate_messages_lisp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_generate_messages_nodejs.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_generate_messages_py.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_geneus.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_genlisp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_gennodejs.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── cg2111a_talker_listener_genpy.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── CMakeDirectoryInformation.cmake
│   │   │   ├── listener.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── CXX.includecache
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   ├── flags.make
│   │   │   │   ├── link.txt
│   │   │   │   ├── progress.make
│   │   │   │   └── src
│   │   │   │       └── listener.cpp.o
│   │   │   ├── progress.marks
│   │   │   ├── roscpp_generate_messages_cpp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── roscpp_generate_messages_eus.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── roscpp_generate_messages_lisp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── roscpp_generate_messages_nodejs.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── roscpp_generate_messages_py.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── rosgraph_msgs_generate_messages_cpp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── rosgraph_msgs_generate_messages_eus.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── rosgraph_msgs_generate_messages_lisp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── rosgraph_msgs_generate_messages_nodejs.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── rosgraph_msgs_generate_messages_py.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   └── progress.make
│   │   │   ├── std_msgs_generate_messages_cpp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── std_msgs_generate_messages_eus.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── std_msgs_generate_messages_lisp.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── std_msgs_generate_messages_nodejs.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   ├── std_msgs_generate_messages_py.dir
│   │   │   │   ├── build.make
│   │   │   │   ├── cmake_clean.cmake
│   │   │   │   ├── DependInfo.cmake
│   │   │   │   ├── depend.internal
│   │   │   │   ├── depend.make
│   │   │   │   └── progress.make
│   │   │   └── talker.dir
│   │   │       ├── build.make
│   │   │       ├── cmake_clean.cmake
│   │   │       ├── CXX.includecache
│   │   │       ├── DependInfo.cmake
│   │   │       ├── depend.internal
│   │   │       ├── depend.make
│   │   │       ├── flags.make
│   │   │       ├── link.txt
│   │   │       ├── progress.make
│   │   │       └── src
│   │   │           └── talker.cpp.o
│   │   ├── cmake_install.cmake
│   │   ├── CTestTestfile.cmake
│   │   └── Makefile
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.16.3
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── a.out
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── a.out
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       └── tmp
│   │   ├── clean_test_results.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── DependInfo.cmake
│   │   │   └── progress.make
│   │   ├── cmake.check_cache
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeError.log
│   │   ├── CMakeOutput.log
│   │   ├── CMakeRuleHashes.txt
│   │   ├── CMakeTmp
│   │   ├── download_extra_data.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── DependInfo.cmake
│   │   │   └── progress.make
│   │   ├── doxygen.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── DependInfo.cmake
│   │   │   └── progress.make
│   │   ├── Makefile2
│   │   ├── Makefile.cmake
│   │   ├── progress.marks
│   │   ├── run_tests.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── DependInfo.cmake
│   │   │   └── progress.make
│   │   ├── TargetDirectories.txt
│   │   └── tests.dir
│   │       ├── build.make
│   │       ├── cmake_clean.cmake
│   │       ├── DependInfo.cmake
│   │       └── progress.make
│   ├── cmake_install.cmake
│   ├── CTestConfiguration.ini
│   ├── CTestCustom.cmake
│   ├── CTestTestfile.cmake
│   ├── gtest
│   │   ├── CMakeFiles
│   │   │   ├── CMakeDirectoryInformation.cmake
│   │   │   └── progress.marks
│   │   ├── cmake_install.cmake
│   │   ├── CTestTestfile.cmake
│   │   ├── googlemock
│   │   │   ├── CMakeFiles
│   │   │   │   ├── CMakeDirectoryInformation.cmake
│   │   │   │   ├── gmock.dir
│   │   │   │   │   ├── build.make
│   │   │   │   │   ├── cmake_clean.cmake
│   │   │   │   │   ├── DependInfo.cmake
│   │   │   │   │   ├── depend.make
│   │   │   │   │   ├── flags.make
│   │   │   │   │   ├── link.txt
│   │   │   │   │   ├── progress.make
│   │   │   │   │   └── src
│   │   │   │   ├── gmock_main.dir
│   │   │   │   │   ├── build.make
│   │   │   │   │   ├── cmake_clean.cmake
│   │   │   │   │   ├── DependInfo.cmake
│   │   │   │   │   ├── depend.make
│   │   │   │   │   ├── flags.make
│   │   │   │   │   ├── link.txt
│   │   │   │   │   ├── progress.make
│   │   │   │   │   └── src
│   │   │   │   └── progress.marks
│   │   │   ├── cmake_install.cmake
│   │   │   ├── CTestTestfile.cmake
│   │   │   ├── gtest
│   │   │   │   ├── CMakeFiles
│   │   │   │   │   ├── CMakeDirectoryInformation.cmake
│   │   │   │   │   ├── gtest.dir
│   │   │   │   │   │   ├── build.make
│   │   │   │   │   │   ├── cmake_clean.cmake
│   │   │   │   │   │   ├── DependInfo.cmake
│   │   │   │   │   │   ├── depend.make
│   │   │   │   │   │   ├── flags.make
│   │   │   │   │   │   ├── link.txt
│   │   │   │   │   │   ├── progress.make
│   │   │   │   │   │   └── src
│   │   │   │   │   ├── gtest_main.dir
│   │   │   │   │   │   ├── build.make
│   │   │   │   │   │   ├── cmake_clean.cmake
│   │   │   │   │   │   ├── DependInfo.cmake
│   │   │   │   │   │   ├── depend.make
│   │   │   │   │   │   ├── flags.make
│   │   │   │   │   │   ├── link.txt
│   │   │   │   │   │   ├── progress.make
│   │   │   │   │   │   └── src
│   │   │   │   │   └── progress.marks
│   │   │   │   ├── cmake_install.cmake
│   │   │   │   ├── CTestTestfile.cmake
│   │   │   │   └── Makefile
│   │   │   └── Makefile
│   │   ├── lib
│   │   └── Makefile
│   ├── Makefile
│   └── test_results
├── devel
│   ├── cmake.lock
│   ├── env.sh
│   ├── lib
│   │   ├── cg2111a_talker_listener
│   │   │   ├── listener
│   │   │   └── talker
│   │   ├── pkgconfig
│   │   │   └── cg2111a_talker_listener.pc
│   │   └── python3
│   │       └── dist-packages
│   │           └── cg2111a_talker_listener
│   │               └── __init__.py
│   ├── local_setup.bash
│   ├── local_setup.sh
│   ├── local_setup.zsh
│   ├── setup.bash
│   ├── setup.sh
│   ├── _setup_util.py
│   ├── setup.zsh
│   └── share
│       ├── cg2111a_talker_listener
│       │   └── cmake
│       │       ├── cg2111a_talker_listenerConfig.cmake
│       │       ├── cg2111a_talker_listenerConfig-version.cmake
│       │       ├── cg2111a_talker_listener-msg-extras.cmake
│       │       └── cg2111a_talker_listener-msg-paths.cmake
│       └── roseus
│           └── ros
│               └── cg2111a_talker_listener
│                   └── manifest.l
└── src
    ├── cg2111a_talker_listener
    │   ├── CMakeLists.txt
    │   ├── include
    │   │   └── cg2111a_talker_listener
    │   ├── package.xml
    │   └── src
    │       ├── listener.cpp
    │       └── talker.cpp
    └── CMakeLists.txt -> /opt/ros/noetic/share/catkin/cmake/toplevel.cmake

92 directories, 305 files

This structure outlines the key components of the ROS workspace:

  • build/: Contains intermediate files generated during the build process.
  • devel/: Includes executables, libraries, and the setup.bash script for environment setup.
  • src/: The source directory for ROS packages. For this tutorial, it houses the cg2111a_talker_listener package, which includes:
    • CMakeLists.txt: Lists build instructions for the package's nodes.
    • package.xml: Defines the package's dependencies and metadata.
    • launch/: Contains ROS launch files for starting nodes.
    • src/: Source code for the publisher (talker.cpp) and subscriber (listener.cpp) nodes.

This hierarchical view helps in understanding the organization and the relationships between different components of the ROS workspace.

Before we start! 

Please check if you successfully implemented the ROS Noetic on your Raspberry Pi or on your Ubuntu 20.04 PC. If not, please navigate to Tutorial 1: Setup ROS on RPi 4 to set up the ROS Noetic environment.


1. Steps for ROS Workspace and Package Creation

  1. Create a workspace and build it.

    If you have not already created a workspace called cg2111a_exercise_ws, create one and build it using the following commands:

    cd ~
    mkdir cg2111a_exercise_ws
    cd cg2111a_exercise_ws
    mkdir src
    cd ~/cg2111a_exercise_ws
    catkin_make

    This step sets up a ROS workspace, which is a directory where you can develop ROS projects. The catkin_make command is used to build the workspace, compiling any source files and preparing the environment for ROS nodes.

  2. Check Your Work Now 

    Please check, your terminal looks like this:

    and you received: Build files have been written to: /home/pi/cg2111a_exercise_ws/build at last.


  3. Create a package.

    Inside ~/cg2111a_exercise_ws/src, create a package called cg2111a_talker_listener using the command:

    cd ~/cg2111a_exercise_ws/src
    catkin_create_pkg cg2111a_talker_listener std_msgs roscpp

    This command creates a new ROS package with the specified name and dependencies. The package will include two important files, CMakeLists.txt and package.xml, which are used for building the package and specifying its metadata, respectively.

  4. Check Your Work Now 

    Please check, your terminal looks like this:

    and you received: Successfully created files in /home/pi/cg2111a_exercise_ws/src/cg2111a_talker_listener. Please adjust the values in package.xml. at last.


  5. Build the workspace and source the setup file.

    Like in Step 1, build the workspace as follows:

    cd ~/cg2111a_exercise_ws
    catkin_make

    Then, source the setup file:

    source ./devel/setup.bash

    Building the workspace compiles all packages within it, including the newly created package. Sourcing the setup file updates your environment to include paths to the workspace's binaries and scripts, making ROS nodes and tools from this workspace available for use.

  6. Check Your Work Now 

    Please check, your terminal looks like this:

    and you received: Build files have been written to: /home/pi/cg2111a_exercise_ws/build at last.


  7. Check the first-order dependencies.

    Before we check the dependencies, we need to install a Python library rosdep:

    sudo pip3 install -U rosdep

    Check the first-order dependencies of the package using the command:

    rospack depends1 cg2111a_talker_listener

    This command lists the direct dependencies of the specified package, reflecting the dependencies declared when the package was created. Understanding these dependencies is crucial for ensuring your package has access to the necessary ROS libraries and messages for its operation.

  8. Check Your Work Now 

    Please check, your terminal's results are:

    roscpp
    std_msgs


The result of checking the dependencies should align with the specified dependencies when creating the package. This ensures that your package can interact with ROS correctly and utilize necessary message types and functionalities.

  1. Create a new node talker.cpp, which will be a publisher.

    Inside ~/cg2111a_exercise_ws/src/cg2111a_talker_listener/src, create and open a file talker.cpp using the following commands:

    cd ~/cg2111a_exercise_ws/src/cg2111a_talker_listener/src
    touch talker.cpp
    nano talker.cpp

    Write the following code into talker.cpp:

    #include "ros/ros.h"
    #include "std_msgs/String.h"
    #include <sstream>
    /**
    * This tutorial demonstrates simple sending of messages over the ROS system.
    */
    int main(int argc, char **argv)
    {
      ros::init(argc, argv, "talker");
      ros::NodeHandle n;
      ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
      ros::Rate loop_rate(10);
      int count = 0;
      while (ros::ok())
      {
        std_msgs::String msg;
        std::stringstream ss;
        ss << "I met student number " << count;
        msg.data = ss.str();
        ROS_INFO("%s", msg.data.c_str());
        chatter_pub.publish(msg);
        ros::spinOnce();
        loop_rate.sleep();
        ++count;
      }
    }

    Save the file. This code snippet creates a ROS node that publishes a sequence of messages, each containing a string with a counter value. It demonstrates how to send messages within the ROS system and can be used to understand the publishing mechanism.

  2. Create a new node listener.cpp, which will be a subscriber.

    Inside ~/cg2111a_exercise_ws/src/cg2111a_talker_listener/src, create and open a file listener.cpp using the same method as before:

    touch listener.cpp
    nano listener.cpp

    For the listener.cpp, you'll need to write a corresponding subscriber code that listens to the messages published by talker.cpp. The detailed code for this will involve setting up a subscriber to the "chatter" topic and defining a callback function to process incoming messages.

    Write the following code into listener.cpp:

    #include "ros/ros.h"
    #include "std_msgs/String.h"
    /**
    * This tutorial demonstrates simple receipt of messages over the ROS system.
    */
    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;
    }

    Save the file. This code sets up a ROS node that subscribes to the "chatter" topic and prints the messages it receives, illustrating how to receive and process messages within the ROS system.

  1. Modify CMakeLists.txt.

    Next, open the CMakeLists.txt inside ~/cg2111a_exercise_ws/src/cg2111a_talker_listener:

    cd ~/cg2111a_exercise_ws/src/cg2111a_talker_listener
    nano CMakeLists.txt
    and modify it as detailed below to include the new nodes and their dependencies:

    cmake_minimum_required(VERSION 3.0.2)
    project(cg2111a_talker_listener)
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      std_msgs
      message_generation
      genmsg
    )
    generate_messages(
      DEPENDENCIES
      std_msgs
    )
    catkin_package(
      CATKIN_DEPENDS roscpp std_msgs message_runtime
    )
    include_directories(
      include
      ${catkin_INCLUDE_DIRS}
    )
    add_executable(talker src/talker.cpp)
    target_link_libraries(talker ${catkin_LIBRARIES})
    add_dependencies(talker cg2111a_talker_listener_generate_messages_cpp)
    add_executable(listener src/listener.cpp)
    target_link_libraries(listener ${catkin_LIBRARIES})
    add_dependencies(listener cg2111a_talker_listener_generate_messages_cpp)

    Save the file. These modifications specify the dependencies of your project, ensuring that ROS knows how to correctly build and link the talker and listener nodes within your package.

  1. Modify package.xml.

    Open the package.xml file inside ~/cg2111a_exercise_ws/src/cg2111a_talker_listener:

    cd ~/cg2111a_exercise_ws/src/cg2111a_talker_listener
    nano package.xml
    and include the following dependencies:

    <?xml version="1.0"?>
    <package format="2">
      <name>cg2111a_talker_listener</name>
      <version>0.0.0</version>
      <description>The cg2111a_talker_listener package</description>
      <maintainer email="your_email@example.com">Your Name</maintainer>
      <license>TODO</license>
      <buildtool_depend>catkin</buildtool_depend>
      <build_depend>roscpp</build_depend>
      <build_depend>std_msgs</build_depend>
      <build_depend>message_generation</build_depend>
      <build_export_depend>roscpp</build_export_depend>
      <build_export_depend>std_msgs</build_export_depend>
      <exec_depend>roscpp</exec_depend>
      <exec_depend>std_msgs</exec_depend>
      <exec_depend>message_runtime</exec_depend>
    </package>

    Save the file. This update includes essential dependencies for building and executing your ROS package, ensuring the package manager recognizes the necessary components for compilation and runtime.

  2. Build the workspace and source it.

    Refer back to Step 3 for the commands, which are:

    cd ~/cg2111a_exercise_ws
    catkin_make

    This process compiles all the packages within the workspace, including any changes made, and updates the environment to recognize the newly built executables.

  3. Check Your Work Now 

    Please check, your terminal looks like this:

    and you received: [100%] Built target listener at last.


  4. Running the Publisher (Talker) and Subscriber (Listener).

    To simulate the talker and listener communicating, follow these steps:

    1. Open one terminal and navigate to your workspace: cd ~/cg2111a_exercise_ws.
    2. Run source ./devel/setup.bash and roscore. This starts the ROS master node.
    3. In a new terminal, navigate to workspace: cd ~/cg2111a_exercise_ws and source the setup file with source ./devel/setup.bash. Run the talker node with rosrun cg2111a_talker_listener talker.
    4. In a new terminal, navigate to workspace: cd ~/cg2111a_exercise_ws and source the setup file with source ./devel/setup.bash. Run the talker node with rosrun cg2111a_talker_listener listener.

    You should see the terminal running the talker node publishing a series of messages, and the terminal running the listener node echoing those messages:

    Experiment by stopping the listener node (using Ctrl+C) while keeping the talker node running, and observe the behavior. Similarly, stop the talker node while keeping the listener running, and note any differences.

  1. Create a launch file. (Optional)

    Creating a launch file simplifies the process of starting multiple nodes by using a single command, roslaunch. Follow these steps to create a launch file:

    mkdir -p ~/cg2111a_exercise_ws/src/cg2111a_talker_listener/launch
    touch ~/cg2111a_exercise_ws/src/cg2111a_talker_listener/launch/cg2111a_talker_listener.launch
    nano ~/cg2111a_exercise_ws/src/cg2111a_talker_listener/launch/cg2111a_talker_listener.launch

    Insert the following XML content into the cg2111a_talker_listener.launch file:

    <?xml version="1.0"?>
    <launch>
      <node pkg="cg2111a_talker_listener" type="talker" name="talker" output="screen" launch-prefix="gnome-terminal --" />
      <node pkg="cg2111a_talker_listener" type="listener" name="listener" output="screen" launch-prefix="gnome-terminal --" />
    </launch>

    This file defines a launch configuration that starts both the talker and listener nodes in separate terminals, facilitating easier management of the ROS nodes.

  2. Build the workspace and source it. (Optional)

    As done previously, rebuild the workspace and source the setup file to apply the changes:

    cd ~/cg2111a_exercise_ws
    catkin_make
    source ./devel/setup.bash
  3. Launch the file. (Optional)

    In ~/cg2111a_exercise_ws, launch the nodes using the following command:

    roslaunch cg2111a_talker_listener cg2111a_talker_listener.launch

    This command starts the ROS nodes as defined in the launch file, displaying the talker and listener nodes' outputs in separate terminals. You should observe the same behavior as when manually running the nodes in separate terminals, but with the convenience of a single command.

· End of Tutorial ·


★ Bonus Time! 

You have done 0 out of 6!

Hip hip hooray! You have successfully learned how to:

  1. Create a workspace and a package.
  2. Develop nodes and define messages.
  3. Utilize nodes and messages to facilitate one-way communication.
  4. Design and implement a launch file.

You are now have a better understanding of what we do in ROS. Do you want to build up Talkers and Listeners between different devices? Please navigate to Tutorial 4: ROS Networking!



References:

  • Dr. Andi Sudjana Putra, Prof. Prahlad Vadakkepat; EE3305/ME3243, AY22/23 Semester I;

< Prev  Tutorial 1: Setup ROS on Raspberry Pi 4    |    Tutorial 3: Raspberry Pi LiDAR & SLAM  Next >