Note: This tutorial assumes that you have completed the previous tutorials: understanding ROS services and parameters. |
![]() |
Writing a Simple Publisher and Subscriber (C++)
Description: This tutorial covers how to write a publisher and subscriber node in C++.Tutorial Level: BEGINNER
Next Tutorial: Examining the simple publisher and subscriber
Contents
Writing the Publisher Node
"Node" is the ROS term for an executable that is connected to the ROS network. Here we'll create a publisher ("talker") node which will continually broadcast a message.
Change directory into the beginner_tutorials package, you created previously in the creating a rosbuild package tutorial:
roscd beginner_tutorials
Change directories to your beginner_tutorials package you created in your catkin workspace previous tutorials:
roscd beginner_tutorials
The Code
Create a src directory in the beginner_tutorials package directory:
mkdir -p src
This directory will contain any source files for our beginner_tutorials package.
Create the src/talker.cpp file within the beginner_tutorials package and paste the following inside it:
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
The Code Explained
Now, let's break the code down.
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
The name used here must be a base name, ie. it cannot have a / in it.
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
NodeHandle::advertise() returns a ros::Publisher object, which serves two purposes: 1) it contains a publish() method that lets you publish messages onto the topic it was created with, and 2) when it goes out of scope, it will automatically unadvertise.
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
In this case we tell it we want to run at 10Hz.
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
ros::ok() will return false if:
- a SIGINT is received (Ctrl-C)
- we have been kicked off the network by another node with the same name
ros::shutdown() has been called by another part of the application.
all ros::NodeHandles have been destroyed
Once ros::ok() returns false, all ROS calls will fail.
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp':
Here's the condensed version of what's going on:
- Initialize the ROS system
Advertise that we are going to be publishing std_msgs/String messages on the chatter topic to the master
Loop while publishing messages to chatter 10 times a second
Now we need to write a node to receive the messsages.
Writing the Subscriber Node
The Code
Create the src/listener.cpp file within the beginner_tutorials package and paste the following inside it:
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp':
The Code Explained
Now, let's break it down piece by piece, ignoring some pieces that have already been explained above.
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp':
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp':
NodeHandle::subscribe() returns a ros::Subscriber object, that you must hold on to until you want to unsubscribe. When the Subscriber object is destructed, it will automatically unsubscribe from the chatter topic.
There are versions of the NodeHandle::subscribe() function which allow you to specify a class member function, or even anything callable by a Boost.Function object. The roscpp overview contains more information.
Could not fetch external code from 'https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp':
There are other ways of pumping callbacks, but we won't worry about those here. The roscpp_tutorials package has some demo applications which demonstrate this. The roscpp overview also contains more information.
Again, here's a condensed version of what's going on:
- Initialize the ROS system
Subscribe to the chatter topic
- Spin, waiting for messages to arrive
When a message arrives, the chatterCallback() function is called
Building your nodes
roscreate-pkg will create a default Makefile and CMakeLists.txt for your package.
$ rosed beginner_tutorials CMakeLists.txt
It should look something like this:
cmake_minimum_required(VERSION 2.4.6) include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake) # Set the build type. Options are: # Coverage : w/ debug symbols, w/o optimization, w/ code-coverage # Debug : w/ debug symbols, w/o optimization # Release : w/o debug symbols, w/ optimization # RelWithDebInfo : w/ debug symbols, w/ optimization # MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries #set(ROS_BUILD_TYPE RelWithDebInfo) rosbuild_init() #set the default path for built executables to the "bin" directory set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) #set the default path for built libraries to the "lib" directory set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) #uncomment if you have defined messages #rosbuild_genmsg() #uncomment if you have defined services #rosbuild_gensrv() #common commands for building c++ executables and libraries #rosbuild_add_library(${PROJECT_NAME} src/example.cpp) #target_link_libraries(${PROJECT_NAME} another_library) #rosbuild_add_boost_directories() #rosbuild_link_boost(${PROJECT_NAME} thread) #rosbuild_add_executable(example examples/example.cpp) #target_link_libraries(example ${PROJECT_NAME})
Adding the following at the bottom:
rosbuild_add_executable(talker src/talker.cpp) rosbuild_add_executable(listener src/listener.cpp)
This will create two executables, talker and listener, which by default will go into the "bin" directory.
For more information on using CMake with ROS, see CMakeLists Now run make:
$ make
Building your nodes
You used catkin_create_pkg in a previous tutorial which created a package.xml and a CMakeLists.txt file for you.
The generated CMakeLists.txt should look like this (with modifications from the Creating Msgs and Srvs tutorial and unused comments and examples removed):
Could not fetch external code from 'https://raw.github.com/ros/catkin_tutorials/master/create_package_modified/catkin_ws/src/beginner_tutorials/CMakeLists.txt':
Don't worry about modifying the commented (#) examples, simply add these few lines to the bottom of your CMakeLists.txt:
add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_dependencies(talker beginner_tutorials_generate_messages_cpp) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES}) add_dependencies(listener beginner_tutorials_generate_messages_cpp)
Your resulting CMakeLists.txt file should look like this:
Could not fetch external code from 'https://raw.github.com/ros/catkin_tutorials/master/create_package_pubsub/catkin_ws/src/beginner_tutorials/CMakeLists.txt':
This will create two executables, talker and listener, which by default will go into package directory of your devel space, located by default at ~/catkin_ws/devel/lib/<package name>.
Note that you have to add dependencies for the executable targets to message generation targets:
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
This makes sure message headers of this package are generated before being used. If you use messages from other packages inside your catkin workspace, you need to add dependencies to their respective generation targets as well, because catkin builds all projects in parallel. As of *Groovy* you can use the following variable to depend on all necessary targets:
target_link_libraries(talker ${catkin_LIBRARIES})
You can invoke executables directly or you can use rosrun to invoke them. They are not placed in '<prefix>/bin' because that would pollute the PATH when installing your package to the system. If you wish for your executable to be on the PATH at installation time, you can setup an install target, see: catkin/CMakeLists.txt
For more detailed discription of the CMakeLists.txt file see: catkin/CMakeLists.txt
Now run catkin_make:
# In your catkin workspace $ cd ~/catkin_ws $ catkin_make
Note: Or if you're adding as new pkg, you may need to tell catkin to force making by --force-cmake option. See catkin/Tutorials/using_a_workspace#With_catkin_make.
Now that you have written a simple publisher and subscriber, let's examine the simple publisher and subscriber.
Additional Resources
Here are some additional resources contributed by the community:
Video Tutorial
The following video presents a small tutorial explaining how to write and test a publisher and subscriber in ROS with C++ and Python based on the talker/listener example above