Publisher and subscriber

Now that you know how is composed a catkin package let's create one!

cd root/catkin_ws/src/
mdkir ros_hello_erle
cd ros_hello_erle/
touch CMakeLists.txt package.xml
mkdir src
cd src/

Create this two files:

touch listener.cpp talker.cpp

Use your favourite text editor to write on the files you just have created.

The package.xml should look like this:

<?xml version="1.0"?>
<package>
  <name>ros_hello_erle</name>
  <version>0.1.0</version>
  <description>The beginner_tutorials package</description>

  <maintainer email="you@yourdomain.tld">Your Name</maintainer>
  <license>BSD</license>
  <url type="website">http://wiki.ros.org/beginner_tutorials</url>
  <author email="you@yourdomain.tld">Jane Doe</author>

  <buildtool_depend>catkin</buildtool_depend>

  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>

  <run_depend>roscpp</run_depend>
  <run_depend>rospy</run_depend>
  <run_depend>std_msgs</run_depend>

</package>

The CMakeLists.txt should look like this:

cmake_minimum_required(VERSION 2.8.3)
project(ros_hello_erle)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)

## Declare a catkin package
catkin_package()

## Build talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker ros_hello_erle_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener ros_hello_erle_generate_messages_cpp)

The talker.cpp should look like this:

#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)
{
  /**
    * The ros::init() function needs to see argc and argv so that it can perform
    * any ROS arguments and name remapping that were provided at the command line.
    * For programmatic remappings you can use a different version of init() which takes
    * remappings directly, but for most command-line programs, passing argc and argv is
    * the easiest way to do it.  The third argument to init() is the name of the node.
    *
    * You must call one of the versions of ros::init() before using any other
    * part of the ROS system.
    */
  ros::init(argc, argv, "talker");

  /**
    * NodeHandle is the main access point to communications with the ROS system.
    * The first NodeHandle constructed will fully initialize this node, and the last
    * NodeHandle destructed will close down the node.
    */
  ros::NodeHandle n;

  /**
    * The advertise() function is how you tell ROS that you want to
    * publish on a given topic name. This invokes a call to the ROS
    * master node, which keeps a registry of who is publishing and who
    * is subscribing. After this advertise() call is made, the master
    * node will notify anyone who is trying to subscribe to this topic name,
    * and they will in turn negotiate a peer-to-peer connection with this
    * node.  advertise() returns a Publisher object which allows you to
    * publish messages on that topic through a call to publish().  Once
    * all copies of the returned Publisher object are destroyed, the topic
    * will be automatically unadvertised.
    *
    * The second parameter to advertise() is the size of the message queue
    * used for publishing messages.  If messages are published more quickly
    * than we can send them, the number here specifies how many messages to
    * buffer up before throwing some away.
    */
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

  ros::Rate loop_rate(10);

  /**
    * A count of how many messages we have sent. This is used to create
    * a unique string for each message.
    */
  int count = 0;
  while (ros::ok())
  {
    /**
      * This is a message object. You stuff it with data, and then publish it.
      */
    std_msgs::String msg;

    std::stringstream ss;
    ss << "Hello Erle! " << count;
    msg.data = ss.str();

    ROS_INFO("%s", msg.data.c_str());

    /**
      * The publish() function is how you send messages. The parameter
      * is the message object. The type of this object must agree with the type
      * given as a template parameter to the advertise<>() call, as was done
      * in the constructor above.
      */
    chatter_pub.publish(msg);

    ros::spinOnce();

    loop_rate.sleep();
    ++count;
  }

  return 0;
}

The listener.cpp should look like this:

#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)
{
  /**
    * The ros::init() function needs to see argc and argv so that it can perform
    * any ROS arguments and name remapping that were provided at the command line.
    * For programmatic remappings you can use a different version of init() which takes
    * remappings directly, but for most command-line programs, passing argc and argv is
    * the easiest way to do it.  The third argument to init() is the name of the node.
    *
    * You must call one of the versions of ros::init() before using any other
    * part of the ROS system.
    */
  ros::init(argc, argv, "listener");

  /**
    * NodeHandle is the main access point to communications with the ROS system.
    * The first NodeHandle constructed will fully initialize this node, and the last
    * NodeHandle destructed will close down the node.
    */
  ros::NodeHandle n;

  /**
    * The subscribe() call is how you tell ROS that you want to receive messages
    * on a given topic.  This invokes a call to the ROS
    * master node, which keeps a registry of who is publishing and who
    * is subscribing.  Messages are passed to a callback function, here
    * called chatterCallback.  subscribe() returns a Subscriber object that you
    * must hold on to until you want to unsubscribe.  When all copies of the Subscriber
    * object go out of scope, this callback will automatically be unsubscribed from
    * this topic.
    *
    * The second parameter to the subscribe() function is the size of the message
    * queue.  If messages are arriving faster than they are being processed, this
    * is the number of messages that will be buffered up before beginning to throw
    * away the oldest ones.
    */
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

  /**
    * ros::spin() will enter a loop, pumping callbacks.  With this version, all
    * callbacks will be called from within this thread (the main one).  ros::spin()
    * will exit when Ctrl-C is pressed, or the node is shutdown by the master.
    */
  ros::spin();

  return 0;
}

Create this two files:

touch listener.py talker.py

#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    rate = rospy.Rate(10) # 10hz
    while not rospy.is_shutdown():
        hello_str = "hello world %s" % rospy.get_time()
        rospy.loginfo(hello_str)
        pub.publish(hello_str)
        rate.sleep()

if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException:
        pass

#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)

def listener():
    rospy.init_node('listener', anonymous=True)

    rospy.Subscriber("chatter", String, callback)

    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()

if __name__ == '__main__':
    listener()

To build the package you just have created run:

catkin_make_isolated  --pkg ros_hello_erle

and then:

source devel_isolated/ros_hello_erle/setup.bash

If you want to build all the existing packages, run instead:

catkin_make

and then:

source devel/setup.bash

When you first want to run a ROS package you have to call the master (remember that without the master node, nodes would not be able to communicate with each other).

To do that roscore is called, however, because in Erle-Brain the autopilot is always running there is already a master node and this step is not necessary (although you can kill it if you want).

So just let's run the listener and talker nodes by running in one shell rosrun ros_hello_erle listener and in the other rosrun ros_hello_erle talker (remember to always source the environment if you have not changed the .bashrc).

By now you have created your own package! But what about all the concepts we talked about? Well, you have created two nodes: the listener and the talker and they are communicating with each other thanks to the master node.

The talker node is publishing info in a given topic called /chatter and the listener node is subscribing to it. The messages received are passed to a callback function.

You can find more in this cheat sheets.