Creating Your First ROS Package

Let’s start hands on activities!

Create Package

A ROS package is simply a folder contains executables. It must have a CMakeLists.txt and package.xml files to define dependencies include it as a ROS package to the ROS environment. CMakeLists.txt is a build script, it does not explicitly express the run dependencies of the targets that get built. package.xml contains meta-data (in addition to the dependency declarations you identified): things not necessarily needed for building (or even running) a package, but still nice to have around in a central place. Examples would be author, maintainer, url, description and license. More about these files: CMakeLists.txt and package.xml.

Apart from these two files, the folders are up to you (a.k.a. the developer). However, to have a consistency with all the ROS community, you should not place your files unintendedly. A mid-size ROS package has generally these folders:

../_images/folders.png

Basically, a /src folder for source codes (nodes), a /launch folder for launch files (written in xml with .launch extension), an /include folder for header files, a /config or /cfg folder for configuration parameters (mostly .yaml files), a /worlds for Gazebo world files (for .world extension) if the package is aimed to use a simulated robot, a /urdf folder where robot models (written in xml, .urdf, .xacro, .sdf extensions), and finally a /models folder to keep any custom models drawn in some CAD programs (mostly .stl files in this folder). Except for these, depending on the package purpose, there can be some other folders as well.

Create Publisher and Subscriber

Publishers and Subscribers are what we have been telling so far as nodes. Publishers provide data to the ROS server and subscribers retrieve data from the server. They are run using the following command:

rosrun package_name node_name

rosrun package_name node_name.py

rosrun package_name node_name.cpp

Note

You need to have the ROS master running to start a node. (Remember roscore).

Simple Publisher

#!/usr/bin/env python
# license removed for brevity
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

Simplified publisher:

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

if __name__ == '__main__':
   pub = rospy.Publisher('chatter', String, queue_size=10)
   rospy.init_node('talker')
   rate = rospy.Rate(10)
   while not rospy.is_shutdown():
      hello_str = "hello world"
      pub.publish(hello_str)
      rate.sleep()

Simple Subscriber

#!/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():

   # In ROS, nodes are uniquely named. If two nodes with the same
   # name are launched, the previous one is kicked off. The
   # anonymous=True flag means that rospy will choose a unique
   # name for our 'listener' node so that multiple listeners can
   # run simultaneously.
   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()

Simplified subscriber:

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

def callback(data):
   print data.data

if __name__ == '__main__':
   rospy.init_node('listener')
   rospy.Subscriber("chatter", String, callback)
   rospy.spin()

See also

Complete the ROS tutorials on simple publisher and subscriber in the following link.

Understanding ROS Topics

Please see the comprehensive ROS topics tutorials on the official guide.

Practical

We are ready to play around now. Let’s control the turtlesim with our own publisher.

Let’s first start a GUI form of a turtlebot (well, it is turtlesim) using the following command: rosrun turtlesim turtlesim_node

To control the turtle, here is the code for you. Don’t celebrate it immediately for that you have the source code, there are some empty lines for you!

#!/usr/bin/env python

import rospy
from geometry_msgs.msg import Twist


def GoForward():
    # initiliaze
    rospy.init_node('GoForwardNode', anonymous=False)

    # tell user how to stop TurtleBot
    rospy.loginfo("To stop TurtleBot CTRL + C")

    # Create a publisher which can "talk" to TurtleBot and tell it to move
    pub = rospy.Publisher('TOPICNAME', Twist, queue_size=10)

    #TurtleBot will stop if we don't keep telling it to move.  How often should we tell it to move? 10 HZ
    r = rospy.Rate(10);

    # Twist is a datatype for velocity
    move_cmd = Twist()
    # let's go forward at 0.2 m/s
    move_cmd.linear.x = 0.2
    # let's turn at 0 radians/s
    move_cmd.angular.z = 0

    # as long as you haven't ctrl + c keeping doing...
    while not rospy.is_shutdown():
        # publish the velocity
        pub.publish(move_cmd)
        # wait for 0.1 seconds (10 HZ) and publish again
        r.sleep()

def GoUpward():
    # Fill yourself
    pass

def GoCircularClockwise():
    # Fill yourself
    pass


if __name__ == '__main__':
    try:
        GoForward()
    except:
        rospy.loginfo("GoForward node terminated.")

Hint: To find out which topic name you should publish your message, use rostopic list.

See also

Can you make your turtlesim to draw a square by using your own publisher?

Create Launcher

A launcher file is an XML file which can start ROS master, multiple nodes with required arguments, set parameters and start the system in a desired state using only one command:

roslaunch package_name launch_file_name.launch

Note

You don’t need to start ROS master to execute a launch file. (No need roscore). If there is a ROS master running, then the launcher starts the inside nodes on this ROS master. If there is not a ROS master running, then the launch file starts the ROS master before starting any nodes.

A simple launch file looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<launch>

   <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find my_pkg)/urdf/my_robot.xacro'"/>

   <!-- Combine joint values -->
   <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"/>

   <!-- Show in Rviz   -->
   <node name="rviz" pkg="rviz" type="rviz" args="-d $(find my_pkg)/launch/config.rviz"/>

   <!-- publish joint state-->
   <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
      <param name="use_gui" value="True"/>
   </node>

   <node name="a_random_node_name" pkg="package_name" type="node_name" output="screen" >
      <param name="speed" type="int" value="100" />
      <rosparam command="load" file="$(find my_package)/config/parameters.yaml" />
   </node>

</launch>

So our launch file will look like this:

<?xml version="1.0" encoding="UTF-8"?>

<launch>

   <node name="my_publisher" pkg="beginner_tutorials" type="publisher.py"/>
   <node name="my_subscriber" pkg="beginner_tutorials" type="subscriber.py"/>

</launch>

See also

Check out the roslaunch tutorial starting from 2.2 in the given link.

Services and Parameters

Services are another way that nodes can communicate with each other. Services allow nodes to send a request and receive a response.

../_images/params-and-services.png

Note

ROS services are not going to be a main target for now, so we will not hold any hands on activities about them. For those who would like to get more information about ROS services, please follow the Services and rosservice links.

Parameters, on the other hand, are very useful to store and manipulate data in the ROS server. They can be set in a launch file manually or they can be loaded (again in a launch file) by simply calling a script file mostly with .yaml extension.

Let’s first start the turtlesim again: rosrun turtlesim turtlesim_node

A single turtle on a console window appeared. Try to solve the steps below by yourself:

  1. See which nodes are running

  2. See which topics are available

  3. See wich parameters are loaded

  4. Get the value of /turtlesim/background_r

  5. Set the value 255 of /turtlesim/background_r

Extra

ROS world is big. There are lots of things to touch uppon but we aimed to give a target-based and condense information so that you can use in your course project. Custom message types, action-clients, various ROS commands are waiting to be explored by enthusiasts. Please check the official ROS tutorials.

Questions

  1. What is a launch file? How to run?

  2. What is a node? How to run?

  3. What is a parameter in parameter server?