Getting Started With Gazebo in ROS Noetic

In this section, we’ll take a quick look at how to get started with Gazebo. Gazebo is a robot simulator that enables you to create realistic simulations of robots in complex indoor and outdoor environments.

If you already have ROS on your system, the stand-alone version of Gazebo is already installed. Let’s check that out now.

Open up a new terminal window, and type the following command. It normally takes a while to launch the first time, so just sit back and relax while Gazebo does its thing:

gazebo

Here is what your screen should look like. You should see an empty world.

33-gazebo-empty-worldJPG

Press CTRL+C in the terminal window to close it.

Gazebo is an independent framework, so what you saw above is the stand-alone version of this program. Since our main framework for robotics development will be ROS, we need to learn how to use Gazebo’s plugins (which we discussed earlier in this tutorial) so that you can access the Gazebo functionality when you’re working with ROS.

There are six types of plugins in Gazebo:

  1. World
  2. Model
  3. Sensor
  4. System
  5. Visual
  6. GUI

Each plugin type is responsible for providing your simulated robot with different functionality. For example, if you want to model sensors (e.g. IMU), you would use the Sensor plugin. The World plugin gives you control over the physics engine and the lighting of the environment the simulated robot is in.

Create a Gazebo Plugin

Let’s develop and load a minimalist “hello world” plugin that is based on the World plugin type mentioned above. This plugin will only consist of a class and a few functions. 

We’ll follow the official tutorial here at the Gazebo website.

Let’s install the Gazebo development files. You can see what version of gazebo you have on your system by typing the following command:

apt list --installed

I have Gazebo 11, so we need to install the Gazebo development files for Gazebo 11.

34-gazebo-versionJPG

Open a new terminal window, and type the following command (note, it might already be installed):

sudo apt-get install libgazebo11-dev

Now, we need to make a new directory.

mkdir ~/gazebo_plugin_tutorial

Move into that folder.

cd ~/gazebo_plugin_tutorial

Open a new C++ file.

gedit hello_world.cc

Type the following code.

// Includes a core set of basic Gazebo functions
#include <gazebo/gazebo.hh>

// All plugins must be in the gazebo namespace
namespace gazebo
{
  // Each plugin has to inherit from a plugin type.
  // In this case, we are inheriting from the WorldPlugin type.
  class WorldPluginTutorial : public WorldPlugin
  {
    public: WorldPluginTutorial() : WorldPlugin()
            {
              printf("Hello World!\n");
            }

    // This function is mandatory. It receives an element in 
	// Simulation Description Format (SDF) that contains the elements
	// and attributes that are specified in the loaded SDF file.
    public: void Load(physics::WorldPtr _world, sdf::ElementPtr _sdf)
            {
            }
  };
  // Register the plugin with the simulator.
  // Note that there are matching register macros for the six types
  // of plugins in Gazebo.
  GZ_REGISTER_WORLD_PLUGIN(WorldPluginTutorial)
}

Save it, and close the editor.

Compile the Plugin

Type the following command:

gedit ~/gazebo_plugin_tutorial/CMakeLists.txt

Add the following to this file.

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)

find_package(gazebo REQUIRED)
include_directories(${GAZEBO_INCLUDE_DIRS})
link_directories(${GAZEBO_LIBRARY_DIRS})
list(APPEND CMAKE_CXX_FLAGS "${GAZEBO_CXX_FLAGS}")

add_library(hello_world SHARED hello_world.cc)
target_link_libraries(hello_world ${GAZEBO_LIBRARIES})

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GAZEBO_CXX_FLAGS}")

Click Save, and close the editor.

Create a new directory called build.

mkdir ~/gazebo_plugin_tutorial/build
cd ~/gazebo_plugin_tutorial/build

Compile the code:

cmake ../
make

You should now see this message:

35-should-see-this-messageJPG

Now, add your library path to the GAZEBO_PLUGIN_PATH:

export GAZEBO_PLUGIN_PATH=${GAZEBO_PLUGIN_PATH}:~/gazebo_plugin_tutorial/build

So that we don’t have to execute the command above each time we open a new terminal window, let’s add this line to the bottom of our bashrc file.

gedit ~/.bashrc
36-add-to-bottom-of-bashrc-fileJPG

Click Save, and close the editor.

Use the Plugin

Now that we have our compiled plugin (~/gazebo_plugin_tutorial/build/libhello_world.so), we can attach it to a file in Simulation Description Format. The way it works on startup is as follows:

  1. Gazebo parses the SDF file
  2. Gazebo locates the plugin you made
  3. Gazebo loads the code in the plugin

Let’s create the SDF file.

Type the following command to open up a new file:

gedit ~/gazebo_plugin_tutorial/hello.world

Add the following code:

<?xml version="1.0"?>
<sdf version="1.4">
  <world name="default">
    <plugin name="hello_world" filename="libhello_world.so"/>
  </world>
</sdf>

Click Save, and close the editor.

Lastly, open the file using this command:

gzserver ~/gazebo_plugin_tutorial/hello.world --verbose

Here is the output:

37-output-of-gazebo-pluginJPG

Working With ROS nodelets in ROS Noetic

In this section, we’ll learn how to work with ROS nodelets.

What is a ROS nodelet?

A node in ROS is a program that runs a specific process. These nodes communicate back and forth with each other on the ROS network via special ROS constructs, known as topics. The disadvantage of this framework is that message passing can get pretty slow if there is a large volume of data that needs to be passed (e.g. data from 3D sensor). 

Fortunately, ROS has something called a nodelet. A nodelet is a special type of ROS node that enables you to run multiple nodes in a single process on a single computer. These nodes are actually separate threads that can communicate with each other directly without going through the traditional ROS communication network middleman. 

The cool thing about nodelets is that they can load as plugins (see the last section of this tutorial), which means that you can add them to your application or remove them from your application without having to make major modifications to your main source code.

The disadvantage of a nodelet is that if a nodelet goes down, all the running processes within it go down as well. Also, the nodelet concept is only implemented in C++, so you can’t use Python.

Bottom Line: If you need a quick and efficient way to pass large amounts of data from one node to another, consider using a nodelet. Otherwise, always use nodes because they are more flexible.

With that background, let’s take a look now at how to create a ROS nodelet. I will loosely follow the official tutorial on the ROS website.

How to Create a ROS Nodelet

The nodelet that we will create will subscribe to a topic (/ros_in). It will then receive a message via that topic and then republish that message to another topic (/ros_out).

Open a new terminal window.

Type the following command:

cd ~/catkin_ws/src

Create a new package called nodelet_basics

catkin_create_pkg nodelet_basics nodelet roscpp std_msgs

The nodelet package we included in the command above provides the tools to build a nodelet.

Move to the src folder of the package you just created.

cd nodelet_basics/src

Create a new file. This file will have the code for the nodelet.

gedit hello_world.cpp

Type the following lines of code:

/*
Author: Addison Sears-Collins

This ROS nodelet will subscribe to a topic (/ros_in). 
It will then receive a message via that topic and then 
republish that message to another topic (/ros_out). 

Date: 6/19/2020

ROS Version: ROS Noetic Ninjemys
*/

// Add the necessary includes
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <nodelet/nodelet.h>
#include <pluginlib/class_list_macros.h>
#include <stdio.h>


namespace nodelet_basics
{
	/*********
	* Nodelet
	**********/

	// This code here means the class needs to inherit from 
	// nodelet::Nodelet
	class Hello_World : public nodelet::Nodelet
	{
	
		public:
			Hello_World()
			{
			}

		private:
			// The onInit method is called by nodelet manager. 
			// It is responsible for initializing the nodelet.
			virtual void onInit()
			{
				// Create a NodeHandle object
				ros::NodeHandle& private_nh = getPrivateNodeHandle();
				NODELET_DEBUG("Initializing nodelet...");
			
				// Create a publisher topic
				pub = private_nh.advertise<std_msgs::String>("ros_out",10); 
			
				// Create a subscriber topic
				sub = private_nh.subscribe("ros_in",10, &Hello_World::callback, this);	
			}

			ros::Publisher pub;
			ros::Subscriber sub;
	
			// Display messages from /ros_in topic to the terminal window.
			// Publish to /ros_out topic
			void callback(const std_msgs::String::ConstPtr& input)
			{

				std_msgs::String output;
				output.data = input->data;
				NODELET_DEBUG("msg data = %s",output.data.c_str());
				ROS_INFO("msg data = %s",output.data.c_str());
				pub.publish(output);		
			}
	};
	// Export the Hello_World class as a plugin using the
	// PLUGINLIB_EXPORT_CLASS macro.
	PLUGINLIB_EXPORT_CLASS(nodelet_basics::Hello_World, nodelet::Nodelet);
}

Save and close the editor.

Now, let’s create an xml file that describes the plugin.

roscd nodelet_basics
gedit nodelet_plugins.xml

Add these lines. 

<library path="lib/libnodelet_basics">
  <class name="nodelet_basics/Hello_World" 
         type="nodelet_basics::Hello_World" 
		 base_class_type="nodelet::Nodelet">
	<description>
		This nodelet receives a message and publishes it.
	</description>
  </class>
</library>

This nodelet receives a message and publishes it.

Save and close the editor.

Open the package.xml file.

gedit package.xml

Make sure you add this line between the <export></export> tags.

<nodelet plugin="${prefix}/nodelet_plugins.xml" />

Save and close the file.

Now let’s edit CMakeLists.txt.

gedit CMakeLists.txt

Add these lines to the bottom of the CMakeLists.txt file.

## Declare C++ library
add_library(nodelet_basics src/hello_world.cpp)

## Link libraries
target_link_libraries(nodelet_basics ${catkin_LIBRARIES}) 

Click Save and close the editor.

Build the package.

cd ~/catkin_ws/
catkin_make

If the build is successful, you’ll see a shared object named libnodelet_basics.co. This file is the plugin.

26-thats-our-pluginJPG

Now let’s create a launch file.

roscd nodelet_basics
mkdir launch
cd launch
gedit hello_world_launch.launch
<launch>
  <node pkg="nodelet" 
        type="nodelet" 
        name="standalone_nodelet"  
        args="manager" 
        output="screen"
  />

  <node pkg="nodelet" 
        type="nodelet" 
        name="Hello_World" 
        args="load nodelet_basics/Hello_World standalone_nodelet" 
        output="screen"
  />
</launch>

Save and close the editor.

Now, launch the nodelet. Remember when you use the roslaunch command, you don’t have to launch the ROS master using roscore.

roslaunch nodelet_basics hello_world_launch.launch

Here is the output.

27-ran-launch-fileJPG

In another terminal, check out the list of active nodes.

rosnode list
28-rosnode-listJPG

In another terminal, check out the list of topics.

rostopic list
29-rostopic-listJPG

Let’s publish a string message to the /ros_in topic. In a new terminal, type the following

rostopic echo /Hello_World/ros_out

This code above is now ready for you to publish your string. It will republish the string it receives. Let’s type the string now.

rostopic pub /Hello_World/ros_in std_msgs/String "Hi Automatic Addison"

Here is what you should see on the /Hello_World/ros_in topic terminal window.

30-publish-messageJPG

Here is what you should see on the /Hello_World/ros_out terminal window.

31-rosout-republish-messageJPG

Let’s check out the node graph.

rqt_graph
32-node-graphJPG

That’s it! You now know how to create a nodelet. 

Working With ROS pluginlib in ROS Noetic

In this tutorial, we’ll learn how to use ROS pluginlib.

What is ROS pluginlib?

Over the course of your work with ROS, there may be some situations where you want an application to have added functionality. However, you want that functionality to remain independent from the main application so that you can “plug it in” when you need it and unplug it when you don’t need it.

In short, the job of a plugin is to add some specific feature to an existing program.

The ROS pluginlib is a C++ library that enables you to load and unload plugins from within a package in ROS, giving your robot added capabilities.

How to Create and Load a Plugin

Let’s see how to create and load a plugin in ROS. We will follow the tutorial given on the ROS website.

What we’ll do is 

  • Create a base class for a polygon. This base class will be called RegularPolygon, and it will serve as the template for the plugins we’ll create.
  • Use that base class to create two new classes, Triangle and Square. These two classes inherit the properties of the RegularPolygon class.
  • Use pluginlib to register the Triangle and Square classes as plugins. 
  • Build the Plugin library using CMakeLists.txt.
  • Make the plugins available to ROS using a package.xml file.
  • Create a C++ program to actually use the plugins (Triangle and Square).
  • Run the code.

So those are all the steps. Let’s go through each step-by-step.

Open up a new terminal window, and create a new package.

cd ~/catkin_ws/src/
catkin_create_pkg pluginlib_tutorials_ roscpp pluginlib

Move into the pluginlib_tutorials_ package.

cd  pluginlib_tutorials_ 

Move into the include/pluginlib_tutorials_ folder.

Move into the directory.

cd include/pluginlib_tutorials_ 

Let’s create the RegularPolygon class.

gedit polygon_base.h

Copy and paste the following code into it.

#ifndef PLUGINLIB_TUTORIALS__POLYGON_BASE_H_
#define PLUGINLIB_TUTORIALS__POLYGON_BASE_H_

namespace polygon_base
{
  class RegularPolygon
  {
    public:
      virtual void initialize(double side_length) = 0;
      virtual double area() = 0;
      virtual ~RegularPolygon(){}

    protected:
      RegularPolygon(){}
  };
};
#endif

Save and close the editor.

Now let’s create our two plugins, Triangle and Square.

Open a new file, and copy and paste the code into it.

gedit polygon_plugins.h
#ifndef PLUGINLIB_TUTORIALS__POLYGON_PLUGINS_H_
#define PLUGINLIB_TUTORIALS__POLYGON_PLUGINS_H_
#include <pluginlib_tutorials_/polygon_base.h>
#include <cmath>

namespace polygon_plugins
{
  class Triangle : public polygon_base::RegularPolygon
  {
    public:
      Triangle(){}

      void initialize(double side_length)
      {
        side_length_ = side_length;
      }

      double area()
      {
        return 0.5 * side_length_ * getHeight();
      }

      double getHeight()
      {
        return sqrt((side_length_ * side_length_) - ((side_length_ / 2) * (side_length_ / 2)));
      }

    private:
      double side_length_;
  };

  class Square : public polygon_base::RegularPolygon
  {
    public:
      Square(){}

      void initialize(double side_length)
      {
        side_length_ = side_length;
      }

      double area()
      {
        return side_length_ * side_length_;
      }

    private:
      double side_length_;

  };
};
#endif

Click Save and close the editor.

Now let’s register the plugins we just created.  

Go to the src folder of the package.

roscd pluginlib_tutorials_/src

Create a new file named polygon_plugins.cpp.

gedit polygon_plugins.cpp

Copy and paste the following code into it.

#include <pluginlib/class_list_macros.h>
#include <pluginlib_tutorials_/polygon_base.h>
#include <pluginlib_tutorials_/polygon_plugins.h>

PLUGINLIB_EXPORT_CLASS(polygon_plugins::Triangle, polygon_base::RegularPolygon)
PLUGINLIB_EXPORT_CLASS(polygon_plugins::Square, polygon_base::RegularPolygon)

Save and close the editor.

Now let’s build the plugin library.

Go to your CMakeLists.txt file.

roscd pluginlib_tutorials_
gedit CMakeLists.txt

Add these two lines.

include_directories(include)
add_library(polygon_plugins src/polygon_plugins.cpp)

Save and close the editor.

Now compile the package.

cd ~/catkin_ws/
catkin_make

Let’s make the plugins available to ROS.

Go to your package.

roscd pluginlib_tutorials_

Create a new file.

gedit polygon_plugins.xml

Copy and paste the following code inside it.

<library path="lib/libpolygon_plugins">
  <class type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon">
    <description>This is a triangle plugin.</description>
  </class>
  <class type="polygon_plugins::Square" base_class_type="polygon_base::RegularPolygon">
    <description>This is a square plugin.</description>
  </class>
</library>

Save and close the editor.

Go to the package.xml file.

gedit package.xml

Add this line between the <export> </export> tags at the bottom of the package.xml file.

  <pluginlib_tutorials_ plugin="${prefix}/polygon_plugins.xml" />

Save and close the editor.

Let’s check that everything is working properly. Type this command in your terminal window.

rospack plugins --attrib=plugin pluginlib_tutorials_

Here is what your output should look like:

24-here-is-what-output-should-look-likeJPG

Now, let’s create a C++ program that uses the plugin.

Go to the src file.

cd src

Open a new file.

gedit polygon_loader.cpp

Copy and paste the following code inside it.

#include <pluginlib/class_loader.h>
#include <pluginlib_tutorials_/polygon_base.h>

int main(int argc, char** argv)
{
  pluginlib::ClassLoader<polygon_base::RegularPolygon> poly_loader("pluginlib_tutorials_", "polygon_base::RegularPolygon");

  try
  {
    boost::shared_ptr<polygon_base::RegularPolygon> triangle = poly_loader.createInstance("polygon_plugins::Triangle");
    triangle->initialize(10.0);

    boost::shared_ptr<polygon_base::RegularPolygon> square = poly_loader.createInstance("polygon_plugins::Square");
    square->initialize(10.0);

    ROS_INFO("Triangle area: %.2f", triangle->area());
    ROS_INFO("Square area: %.2f", square->area());
  }
  catch(pluginlib::PluginlibException& ex)
  {
    ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
  }

  return 0;
}

Save and close the editor.

Now go to the CMakeLists.txt file.

cd ..
gedit CMakeLists.txt

Add these two lines to the bottom of the file.

add_executable(polygon_loader src/polygon_loader.cpp)
target_link_libraries(polygon_loader ${catkin_LIBRARIES})

Save and close.

Now we need to compile the code.

cd /~catkin_ws/
catkin_make

Let’s run the executable.

In a new terminal window, type:

roscore

In a new terminal tab, type:

rosrun pluginlib_tutorials_ polygon_loader

Here is the output:

25-here-is-the-outputJPG