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

Working With ROS actionlib in ROS Noetic

What is a ROS Action?

ROS services are all about request/reply communication. A Service Client node sends a request (typically for some data) to a Service Server node. The Service Server node then replies to that request.

However, there may be some situations when the Service Client wants to cancel a request. This situation could occur when it is taking too long to receive a reply from the Service Server.

There are also some situations when the Service Client wants to get regular updates on the processing status of the request. 

Fortunately, ROS has a solution for this. It is called the ROS’s actionlib package. The ROS actionlib package enables cancellation of requests and periodic feedback on the status of requests.

What is the Difference Between a ROS Service and a ROS Action?

Here is an analogy to understand the difference between ROS Services and ROS Actions.

Let’s say you’re hungry. You order Chinese food from your local Chinese restaurant. Here are two ways communication might work during the ordering process between you (the client) and the restaurant (the server).

chinese_takeout_t_png

Option 1: Go to the Chinese Restaurant to Order Takeout

  1. Place your order.
  2. Wait for your order.
  3. Receive your order.

Option 2: Order Chinese Food via an App on Your Smartphone (for Delivery)

  1. Place your order.
  2. Receive an order confirmation.
  3. Consider cancelling your order.
  4. Receive periodic updates on the status of your order.
  5. Do some household tasks while you wait for your order.
  6. Chinese food is delivered.

A ROS Service is like Option 1. It is a synchronous form of communication. After the client sends a request to a server, the client has to wait for a response from the server in order to continue doing other things. 

Synchronous communication is also like talking on the telephone. When you’re talking on the telephone, you’re not free to do other things. You have to wait for the call to end to resume doing other things.

A ROS Action is like Option 2. It is an asynchronous form of communication. The client is free to do other things while it is waiting for a response from the server.

Asynchronous communication is like communicating via text message or e-mail. You send a request and then wait for a reply. In the meantime, you can do other things besides waiting for a response.

Here’s how ROS actions work. An ActionClient sends a request (i.e. a Goal) to an ActionServer. The goal might be, for example, for the robot to move to a particular location in the environment.

The ActionServer gives the ActionClient feedback on the progress it is making towards the execution of that goal (e.g. the robot’s most recent position along the path towards the goal location).

When the ActionServer completes the goal, it sends a result message to the ActionClient.

How to Create an ActionServer

Let’s create an ActionServer. The ActionServer’s job is to generate a Fibonacci sequence of a certain order (i.e. length). Don’t be scared if you’ve never heard of Fibonacci before.

A Fibonacci sequence is just a series of numbers in which the next number is found by adding up the two numbers before it, starting from 0 and 1. For example, here is a Fibonacci sequence:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …

Notice that after 0 and 1, each number is the sum of the two numbers before it.

1 + 2 = 3 … 2 + 3 = 5, 3 + 5 = 8, etc.

A Fibonacci sequence of order 5, means the first 5 numbers of the sequence after the two seed numbers, which are 0 and 1:

0, 1, 1, 2, 3, 5, 8

In the next section of this tutorial, we will create an ActionClient. The ActionClient must send a goal to the ActionServer. The goal in this example will be the ActionClient’s desired order of the Fibonacci sequence (e.g. 5). 

The ActionServer will take that order number as input (e.g. 5) and then reply to the ActionClient with the result (e.g. 0, 1, 1, 2, 3, 5, 8). 

While the ActionServer is busy generating the requested Fibonacci sequence, it will send feedback to the ActionClient on its progress towards completing the goal.

Open up a new terminal window.

Move to the src folder of your workspace.

cd ~/catkin_ws/src

Create a package named actionlib_basics. Everything below is a single command that you type on one line in the terminal. 

catkin_create_pkg actionlib_basics actionlib message_generation roscpp rospy std_msgs actionlib_msgs

Now, let’s create an action. In this action, we’ll define the goal, result, and feedback messages. Create a new folder named action inside the actionlib_basics package.

cd actionlib_basics
mkdir action
cd action
gedit Fibonacci.action

Type the following code inside the file.

#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback
int32[] sequence

Click Save.

Close the editor.

Open the CMakeLists.txt file.

cd ..
gedit CMakeLists.txt

Make sure your find_package macro looks like this.

7b-find-package-macro

Uncomment and modify the add_action_files macro:

add_action_files(
  DIRECTORY action
  FILES Fibonacci.action
)
8-add-action-filesJPG

Uncomment and modify the generate_messages macro:

generate_messages(
  DEPENDENCIES actionlib_msgs std_msgs  # Or other packages containing msgs
)
8b-generate-messages-macroJPG

Make the catkin_package macro look like this. If you scroll down, you’ll see it further down in your CMakeLists.txt file.

catkin_package(
  CATKIN_DEPENDS actionlib_msgs
)
9-uncomment-catkin-packageJPG

Click Save and close CMakeLists.txt.

Now, open your “manifest file.” This file is also known as package.xml.

gedit package.xml

Under where you see all the <exec_depend> declarations, add the following line:

<exec_depend>message_generation</exec_depend>
10-exec-dependJPG

Click Save. 

Close package.xml.

Now, let’s generate the msg files out of the action files. Type the following commands in your terminal.

cd ~/catkin_ws/
catkin_make

Now let’s see all the messages that were created.

ls devel/share/actionlib_basics/msg/

You should see 7 files in there.

11-seven-files-in-thereJPG

Let’s take a look at the header files.

ls devel/include/actionlib_basics/
12-header-filesJPG

Now that we’ve set up the message types, let’s create a program for the ActionServer.

Move to the src folder of the package.

roscd actionlib_basics/src

Create a new C++ program called fibonacci_server.cpp.

Type the following code inside it.

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <actionlib_basics/FibonacciAction.h>

class FibonacciAction
{
protected:

  ros::NodeHandle nh_;
  actionlib::SimpleActionServer<actionlib_basics::FibonacciAction> as_; // NodeHandle instance must be created before this line. Otherwise strange error occurs.
  std::string action_name_;
  // create messages that are used to published feedback/result
  actionlib_basics::FibonacciFeedback feedback_;
  actionlib_basics::FibonacciResult result_;

public:

  FibonacciAction(std::string name) :
    as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),
    action_name_(name)
  {
    as_.start();
  }

  ~FibonacciAction(void)
  {
  }

  void executeCB(const actionlib_basics::FibonacciGoalConstPtr &goal)
  {
    // helper variables
    ros::Rate r(1);
    bool success = true;

    // push_back the seeds for the fibonacci sequence
    feedback_.sequence.clear();
    feedback_.sequence.push_back(0);
    feedback_.sequence.push_back(1);

    // publish info to the console for the user
    ROS_INFO("%s: Executing, creating fibonacci sequence of order %i with seeds %i, %i", action_name_.c_str(), goal->order, feedback_.sequence[0], feedback_.sequence[1]);

    // start executing the action
    for(int i=1; i<=goal->order; i++)
    {
      // check that preempt has not been requested by the client
      if (as_.isPreemptRequested() || !ros::ok())
      {
        ROS_INFO("%s: Preempted", action_name_.c_str());
        // set the action state to preempted
        as_.setPreempted();
        success = false;
        break;
      }
      feedback_.sequence.push_back(feedback_.sequence[i] + feedback_.sequence[i-1]);
      // publish the feedback
      as_.publishFeedback(feedback_);
      // this sleep is not necessary, the sequence is computed at 1 Hz for demonstration purposes
      r.sleep();
    }

    if(success)
    {
      result_.sequence = feedback_.sequence;
      ROS_INFO("%s: Succeeded", action_name_.c_str());
      // set the action state to succeeded
      as_.setSucceeded(result_);
    }
  }


};


int main(int argc, char** argv)
{
  ros::init(argc, argv, "fibonacci");

  FibonacciAction fibonacci("fibonacci");
  ros::spin();

  return 0;
}

You can find a full explanation of what is going on inside the code at this page on the ROS website.

Click Save.

Close the editor.

Now let’s compile the code.

In the same terminal window, go back to the actionlib_basics package.

cd ..

Open CMakeLists.txt.

gedit CMakeLists.txt

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

add_executable(fibonacci_server src/fibonacci_server.cpp)

target_link_libraries(
  fibonacci_server
  ${catkin_LIBRARIES}
)

add_dependencies(
  fibonacci_server
  ${actionlib_basics_EXPORTED_TARGETS}
)

Click Save.

Close the file.

Now, let’s build everything again using the usual process.

cd ~/catkin_ws/
catkin_make

If it builds successfully, you should see this message in the terminal window.

13-success-fibonacciJPG

Now let’s run the server.

In a fresh terminal window, type the following:

roscore

Open a new terminal tab, and type this command:

rosrun actionlib_basics fibonacci_server

You shouldn’t see any output. The ActionServer (i.e. fibonacci_server) is patiently waiting to receive a goal to execute.

Let’s check to see if the actions are running properly by listing the current topics.

rostopic list -v
14-rostopic-listJPG

Press CTRL+C on all terminal windows to shutdown ROS.

That’s it. Now let’s create an ActionClient that can send the ActionServer (that we just created) some goals.

How to Create an ActionClient

Here is the official tutorial for creating an ActionClient, but I’ll run through the process below.

Move to the src folder of the package.

roscd actionlib_basics/src

Create a new C++ program called fibonacci_client.cpp.

Type the following code inside it.

#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <actionlib_basics/FibonacciAction.h>

int main (int argc, char **argv)
{
  ros::init(argc, argv, "test_fibonacci");

  // create the action client
  // true causes the client to spin its own thread
  actionlib::SimpleActionClient<actionlib_basics::FibonacciAction> ac("fibonacci", true);

  ROS_INFO("Waiting for action server to start.");
  // wait for the action server to start
  ac.waitForServer(); //will wait for infinite time

  ROS_INFO("Action server started, sending goal.");
  // send a goal to the action
  actionlib_basics::FibonacciGoal goal;
  goal.order = 20;
  ac.sendGoal(goal);

  //wait for the action to return
  bool finished_before_timeout = ac.waitForResult(ros::Duration(30.0));

  if (finished_before_timeout)
  {
    actionlib::SimpleClientGoalState state = ac.getState();
    ROS_INFO("Action finished: %s",state.toString().c_str());
  }
  else
    ROS_INFO("Action did not finish before the time out.");

  //exit
  return 0;
}

You can find a full explanation of what is going on inside the code at this page on the ROS website.

Click Save.

Close the editor.

Now let’s compile the code.

In the same terminal window, go back to the actionlib_basics package.

cd ..

Open CMakeLists.txt.

gedit CMakeLists.txt

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

add_executable(fibonacci_client src/fibonacci_client.cpp)

target_link_libraries( 
  fibonacci_client
  ${catkin_LIBRARIES}
)

add_dependencies(
  fibonacci_client
  ${actionlib_basics_EXPORTED_TARGETS}
)

Click Save.

Close the file.

Now, let’s build everything again using the usual process.

cd ~/catkin_ws/
catkin_make

If it builds successfully, you should see this message in the terminal window.

15-fibonacci-client-successful-buildJPG

Now let’s run the client.

In a fresh terminal window, type the following:

roscore

Open a new terminal tab, and type this command:

rosrun actionlib_basics fibonacci_client

You should see a message that says “Waiting for action server to start.” Going back to our Chinese restaurant analogy, the customer is waiting for the restaurant to open.

16-waiting-for-actionserverJPG

Let’s check to see the current topics. In a new terminal window, type:

rostopic list -v
17-current-topicsJPG

Press CTRL+C on all terminal windows to shutdown ROS.

That’s it. Now let’s see how to run both the ActionClient and ActionServer at the same time.

How to Run an ActionClient and ActionServer

Here is the official tutorial on how to run the ActionClient and ActionServer, but let’s run through this together.

In a new terminal window, launch ROS.

roscore

In a new terminal tab, type:

rosrun actionlib_basics fibonacci_server
18-fibonacci-serverJPG

In another terminal tab, type:

rosrun actionlib_basics fibonacci_client
19-fibonacci-client-outputJPG

Let’s see the feedback from the ActionServer while it is in the middle of executing the goal. In a new tab, type:

rostopic echo /fibonacci/feedback
20-feedback-outputJPG

Let’s check out the action result. In a new terminal tab, type:

rostopic echo /fibonacci/result
20a-result-outputJPG

To see the communication lines between the ActionServer and ActionClient, in a new terminal tab, type:

rqt_graph
21-node-graphJPG

If you didn’t get the same output, try again, typing in the commands above quickly. It doesn’t take long (~10 seconds on my computer) for the ActionServer to execute the goal in this case (i.e. creating a Fibonacci sequence of order 20).

In a real robotics project, you won’t need to type in all the commands so fast because your robot will most likely be operating for longer than 10 seconds.

When you’re done, type CTRL+C on the window where you launched the ROS Master (i.e. roscore).

How to Create an ActionClient in Python

For the last part of this tutorial on ROS Actions, let’s look at how to create an ActionClient in Python.

Go to your package.

roscd actionlib_basics

Create a new folder called scripts.

mkdir scripts

Move inside that folder

cd scripts

Create a new Python program called fibonacci_client.py.

gedit fibonacci_client.py

Type the following code inside it.

#! /usr/bin/env python3
from __future__ import print_function
import rospy


# Brings in the SimpleActionClient
import actionlib

# Brings in the messages used by the fibonacci action, including the
# goal message and the result message.
import actionlib_basics.msg

def fibonacci_client():
    # Creates the SimpleActionClient, passing the type of the action
    # (FibonacciAction) to the constructor.
    client = actionlib.SimpleActionClient('fibonacci', actionlib_basics.msg.FibonacciAction)

    # Waits until the action server has started up and started
    # listening for goals.
    client.wait_for_server()

    # Creates a goal to send to the action server.
    goal = actionlib_basics.msg.FibonacciGoal(order=20)

    # Sends the goal to the action server.
    client.send_goal(goal)

    # Waits for the server to finish performing the action.
    client.wait_for_result()

    # Prints out the result of executing the action
    return client.get_result()  # A FibonacciResult

if __name__ == '__main__':
    try:
        # Initializes a rospy node so that the SimpleActionClient can
        # publish and subscribe over ROS.
        rospy.init_node('fibonacci_client_py')
        result = fibonacci_client()
        print("Result:", ', '.join([str(n) for n in result.sequence]))
    except rospy.ROSInterruptException:
        print("program interrupted before completion", file=sys.stderr)

Click Save.

Close the editor.

Make the file executable.

chmod +x fibonacci_client.py

Now type the following commands to build the node:

cd ~/catkin_ws
catkin_make

Now let’s run our ActionServer (created using C++) and our ActionClient (create using Python).

In a new terminal window, launch ROS.

roscore

In a new terminal tab, type:

rosrun actionlib_basics fibonacci_server

In another terminal tab, type:

rosrun actionlib_basics fibonacci_client.py

Let’s see the feedback from the ActionServer while it is in the middle of executing the goal. In a new tab, type:

rostopic echo /fibonacci/feedback

Let’s check out the action result. In a new terminal tab, type:

rostopic echo /fibonacci/result

To see the communication lines between the ActionServer and ActionClient, in a new terminal tab, type:

rqt_graph

Here is the node graph:

22-node-graph-pythonJPG

Here is the output of the /fibonacci/result tab we opened up.

23-result-python-action-clientJPG

To run the client again, go back to the terminal where you ran fibonacci_client.py, and type the up arrow on your keyboard. Then press Enter to run this command again:

rosrun actionlib_basics fibonacci_client.py

When you’re done, press CTRL+C to close down everything.

ROS Parameters and the dynamic_reconfigure Package in ROS Noetic

What are ROS Parameters?

Parameters are globally available values such as integers, floats, strings or booleans. Any node in ROS can access and modify these values. You should use parameters for data that you do not expect to change a lot over time.

Nodes (i.e. programs in C++/Python that exist inside ROS packages), can use parameters during runtime.

Parameters are stored inside a centralized Parameter Server. The Parameter Server is like a dictionary that contains the name of the parameter and the corresponding value of the parameter.

The Parameter Server is part of the ROS Master, so it launches automatically when you launch either roscore or roslaunch.

You can learn more about the Parameter Server on the ROS website. There is also a quick tutorial on ROS parameters here if you’d like to dive deep.

Let’s look at some basic commands now.

Most Common Parameter Commands

To get a list of the currently active parameters on your system, open a new terminal window, and type:

roscore
rosparam list

Here we see the name of the active parameters.

1-name-of-active-parametersJPG

If you want to see the value of the parameter, you use this syntax:

rosparam get <name_of_parameter>

For example, type:

rosparam get /rosdistro
2-name-of-ros-distroJPG

We see that the value of the /rosdistro (i.e. “ROS distribution”) is noetic, which is exactly what we would expect.

If you want to see the values of all parameters at once, you type this command:

rosparam get /

To assign a value to a parameter, you use this command.

rosparam set <name_of_parameter> <desired_value>

Followed by this command to complete the update of the parameter value.

rosservice call /clear

How to Update Parameters Using the dynamic_reconfigure Package

Imagine we have two nodes written in either Python or C++. One node sets the value of a particular parameter on the Parameter Server. The other node uses the value of that parameter to perform some computation. This latter node typically reads the value of the parameter only when the node is first launched. How will this latter node know if the value of the parameter has changed during runtime?

Fortunately, ROS has an efficient solution for this. To update the value of a parameter dynamically (i.e. “on-the-fly”) while a node is running, we can use the dynamic_reconfigure package. This package enables us to update parameters on the Parameter Server at runtime and push that change out to a given active node that needs the most current value of that parameter.

This is cool because it means we don’t have to restart a node in order for it to recognize the change in the value of a parameter.

A common use case for the dynamic_reconfigure package is when you’ve built a control system that needs to precisely control the speed of a motor. You might need to continuously tune the control parameters to make the robot move how you want it to move. Doing so requires continuous updates. The dynamic_reconfigure package provides an efficient means of doing this.

Let’s take a look at an example of how to use the dynamic_reconfigure package. The official ROS tutorial for this package is here (for both C++ and Python). We’ll run through the process below for C++.

How to Write a .cfg File

First, let’s create a package called parameter_server_basics.

In a new terminal window, move to the src (source) folder of your workspace.

cd ~/catkin_ws/src

Now create the package (everything below goes on one line in the terminal)

catkin_create_pkg parameter_server_basics std_msgs roscpp rospy dynamic_reconfigure

Now, let’s build the package.

cd ~/catkin_ws/
catkin_make

Now, move inside the package.

roscd parameter_server_basics

Create a cfg directory. This is where all the cfg files will be.

mkdir cfg

Move inside the cfg directory you just created.

cd cfg

Now let’s create a file named parameter_server_basics.cfg.

gedit parameter_server_basics.cfg

Write the following code:

#!/usr/bin/env python3

PACKAGE = "parameter_server_basics"

from dynamic_reconfigure.parameter_generator_catkin import *

gen = ParameterGenerator()

gen.add("int_param",    int_t,    0, "An Integer parameter", 50,  0, 100)
gen.add("double_param", double_t, 0, "A double parameter",    .5, 0,   1)
gen.add("str_param",    str_t,    0, "A string parameter",  "Hello World")
gen.add("bool_param",   bool_t,   0, "A Boolean parameter",  True)

size_enum = gen.enum([ gen.const("Small",      int_t, 0, "A small constant"),
                       gen.const("Medium",     int_t, 1, "A medium constant"),
                       gen.const("Large",      int_t, 2, "A large constant"),
                       gen.const("ExtraLarge", int_t, 3, "An extra large constant")],
                     "An enum to set size")

gen.add("size", int_t, 0, "A size parameter which is edited via an enum", 1, 0, 3, edit_method=size_enum)

exit(gen.generate(PACKAGE, "parameter_server_basics", "parameter_server_"))

To understand what the code is all about, check out this post.

Save the file and close the editor.

Make the code executable:

chmod +x parameter_server_basics.cfg

Now go back to the directory of the package.

roscd parameter_server_basics

Open the CMakeLists.txt file.

gedit CMakeLists.txt

Add these lines to the lines just underneath the “find_package()” block:

#add dynamic reconfigure api
#find_package(catkin REQUIRED dynamic_reconfigure)
generate_dynamic_reconfigure_options(
  cfg/parameter_server_basics.cfg
  #...
)
3-under-find-package-blockJPG

Add these lines to the very bottom of the file.

# make sure configure headers are built before any node using them
add_dependencies(parameter_server_basics ${PROJECT_NAME}_gencfg)

Click Save and close the editor window.

How to Make a Node Dynamically Reconfigureable in C++

Now we need to create a node that uses the cfg file. We will create a node in C++, but you can also create a node using Python.

Go to your parameter_server_basics package.

roscd parameter_server_basics 

Go inside the src folder.

cd src

Open a new C++ file named 

parameter_server_basics.cpp

Add the code below into the file. For a detailed explanation of what is going on inside the code, check out this post.

#include <ros/ros.h>

#include <dynamic_reconfigure/server.h>
#include <parameter_server_basics/parameter_server_Config.h>

void callback(parameter_server_basics::parameter_server_Config &config, uint32_t level) {
  ROS_INFO("Reconfigure Request: %d %f %s %s %d", 
            config.int_param, config.double_param, 
            config.str_param.c_str(), 
            config.bool_param?"True":"False", 
            config.size);
}

int main(int argc, char **argv) {
  ros::init(argc, argv, "parameter_server_basics");

  dynamic_reconfigure::Server<parameter_server_basics::parameter_server_Config> server;
  dynamic_reconfigure::Server<parameter_server_basics::parameter_server_Config>::CallbackType f;

  f = boost::bind(&callback, _1, _2);
  server.setCallback(f);

  ROS_INFO("Spinning node");
  ros::spin();
  return 0;
}

Let’s edit the CMakeLists.txt file for the parameter_server_basics package.

Open a new terminal window, and type this command:

roscd parameter_server_basics
gedit CMakeLists.txt

Now add these lines to the bottom of the CMakeLists.txt file. Make sure the add_dependencies line you added in the last section is between add_executable… and target_link_libraries:

add_executable(parameter_server_basics src/parameter_server_basics.cpp)
target_link_libraries(parameter_server_basics ${catkin_LIBRARIES})
3b-is-between-codeJPG

Now save and close the CMakeLists.txt file.

Open a new terminal window, and type the following commands to build all the nodes in the parameter_server_basics package:

cd ~/catkin_ws
catkin_make 

If everything goes, well, here is the message you should see:

4-here-is-the-message-you-should-seeJPG

We need to modify the rqt_reconfigure node so that it runs with Python 3.

Open up a new terminal window, and type:

sudo ln -s /usr/bin/python3 /usr/bin/python

This will make a symbolic link from /usr/local/bin/python to /usr/bin/python3.

Now that everything is setup, let’s run the node and the Dynamic Reconfigure Graphical User Interface.

Open a new terminal window to launch ROS.

roscore

In a new terminal tab, type:

rosrun parameter_server_basics parameter_server_basics

In another terminal tab, type:

rosrun rqt_reconfigure rqt_reconfigure

Click on parameter_server_basics on the left-hand side.

Here is what you should see:

5-here-is-what-you-should-seeJPG

To modify the parameters, drag any of the sliders. You can also uncheck the boolean checkbox there.

Each time you make a change to one of the parameters, you’ll see the change printed out to the terminal window where you ran rosrun parameter_server_basics parameter_server_basics.

7-modify-parametersJPG

When you’re finished, type Ctrl + C on all terminal windows to shutdown ROS.