3.2 掌握参数服务器和动态参数

在前一章中,我们了解到参数服务器是节点管理器(Master)的一部分,并且允许ROS系统将数据或配置信息保存在关键位置。所有的节点可以获取这些数据来配置、改变自己的状态。

我们之前已经有过使用rosparam工具的经验,所以现在使用参数服务器也不成问题了。在节点执行期间,我们可以使用dynamic_reconfigure功能包来动态地改变它所使用的参数。在下一节中,我们将详细了解ROS中的动态参数工具。

3.2.1 准备工作

通常我们所编写节点程序的变量值是在初始化的时候就确定好了,之后这些值只能在节点的内部进行修改。如果我们需要从运行节点外部来动态地修改这些值,就需要用到参数服务器、服务或者话题。

举例来说,如果我们正在操作一个节点,这个节点可以通过PID控制器来控制电动机的最佳运动速度,这通常需要调整PID的3个参数,即k1、k2和k3。不过,ROS中提供了一个效率更高的工具“Dynamic Reconfigure”(动态配置)来完成这些功能。

在接下来的一节中,我们将学习如何在一个基本实例节点中启用这个工具。首先必须在CMakeLists.txt和package.xml文件中添加几行代码。

3.2.2 如何完成

1.如果你希望使用“Dynamic Reconfigure”工具,首先要编写一个配置文件,并将其保存在预期功能包的cfg文件夹中。

2.在parameter_server_tutorials功能包中创建一个cfg文件夹,然后在其中再创建一个parameter_server_tutorials.cfg文件。这个过程如下所示:

$ roscd parameter_server_tutorials
$ mkdir cfg
$ vim parameter_server_tutorials

3.将如下所示的代码添加到parameter_server_tutorials.cfg文件中:

#!/usr/bin/env python
PACKAGE = "parameter_server_tutorials"
from dynamic_reconfigure.parameter_generator_catkin import *
gen = ParameterGenerator()
gen.add("BOOL_PARAM", bool_t, 0,"A Boolean parameter", True)
gen.add("INT_PARAM", int_t, 0, "An Integer Parameter", 1, 0,
100)
gen.add("DOUBLE_PARAM", double_t, 0, "A Double Parameter",
0.01, 0, 1)
gen.add("STR_PARAM", str_t, 0, "A String parameter", "Dynamic
Reconfigure")
size_enum = gen.enum([ gen.const("Low", int_t, 0, "Low : 0"),
                       gen.const("Medium", int_t, 1,"Medium :
1"),
                       gen.const("High", int_t, 2, "Hight
:2")],
                       "Selection List")
gen.add("SIZE", int_t, 0, "Selection List", 1, 0, 3,
edit_method=size_enum)
exit(gen.generate(PACKAGE, "parameter_server_tutorials",
"parameter_server_"))

4.要完成参数生成器的初始化并定义需要动态配置的参数,我们需要添加以下代码:

gen = ParameterGenerator()
gen.add("BOOL_PARAM", bool_t, 0,"A Boolean parameter", True)
gen.add("INT_PARAM", int_t, 0, "An Integer Parameter", 1, 0,
100)
gen.add("DOUBLE_PARAM", double_t, 0, "A Double Parameter",
0.01, 0, 1)
gen.add("STR_PARAM", str_t, 0, "A String parameter", "Dynamic
Reconfigure")
size_enum = gen.enum([gen.const("Low", int_t, 0, "Low : 0"),
                      gen.const("Medium", int_t, 1, "Medium :
1"),
                      gen.const("High", int_t, 2, "Hight
:2")],"Selection List")
gen.add("SIZE", int_t, 0, "Selection List", 1, 0, 3,
edit_method=size_enum)

5.这些行中添加了不同的参数类型并且设置默认值(default)、描述(description)和取值范围等。生成参数生成器add()的格式如下所示:

gen.add(name, type, level, description, default, min, max)

参数生成器add()中每个参数的含义如下所示。


•name:参数的名称。

•type:定义存储值的类型。

•level:需要传入参数动态配置回调函数中的掩码。

•description:描述参数作用的字符串。

•default:设置节点启动时参数的默认值。

•min:设置参数的最小值。

•max:设置参数的最大值。


上面程序中的最后一行代码用于生成所需的文件并退出程序。我们可以看到这个.cfg文件是使用Python语言编写的。虽然本书是针对C++语言的,但是在某些特定的场合,或者需要解释某个概念的时候,还是会使用到Python语言。

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

然后,我们必须修改parameter_server_tutorials.cfg文件的权限(为其添加可执行权限),这是因为需要ROS系统将其作为一个Python脚本来执行。

$ chmod a+x cfg/ parameter_server_tutorials.cfg

6.为了调用编译,我们需要将下面这些行的内容添加到CMakeLists.txt文件中。

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
  dynamic_reconfigure
)
generate_dynamic_reconfigure_options(
  cfg/parameter_server_tutorials.cfg
)
add_dependencies(parameter_server_tutorials
parameter_server_tutorials_gencfg)

7.接下来我们需要在“Dynamic Reconfigure”的帮助下来创建一个示例节点。

$ roscd parameter_server_tutorials
$ vim src/ parameter_server_tutorials.cpp

我们需要将下面的代码片段添加到node文件中:

#include <ros/ros.h>
#include <dynamic_reconfigure/server.h>
#include <parameter_server_tutorials/parameter_server_Config.h>
void
callback(parameter_server_tutorials::parameter_server_Config
&config, uint32_t level) {
  ROS_INFO("Reconfigure Request: %s %d %f %s %d",
            config.BOOL_PARAM?"True":"False",
            config.INT_PARAM,
            config.DOUBLE_PARAM,
            config.STR_PARAM.c_str(),
            config.SIZE);
}
int main(int argc, char **argv) {
  ros::init(argc, argv, "parameter_server_tutorials");
dynamic_reconfigure::Server<parameter_server_tutorials::paramet
er_server_Config> server;
dynamic_reconfigure::Server<parameter_server_tutorials::paramet
er_server_Config>::CallbackType f;
  f = boost::bind(&callback, _1, _2);
  server.setCallback(f);
  ROS_INFO("Spinning");
  ros::spin();
  return 0;
}

像往常一样,这几行代码引入了ROS的头文件、参数服务器以及我们之前创建的配置文件:

#include <ros/ros.h>
#include <dynamic_reconfigure/server.h>
#include <parameter_server_tutorials/parameter_server_Config.h>

下面的代码中显示了函数callback()的内容,当客户端请求修改参数时,它就会将修改之后的参数值打印输出。需要注意的是,参数的名字必须和parameter_server_tutorials.cfg文件中配置的相一致。

void
callback(parameter_server_tutorials::parameter_server_Config
&config, uint32_t level) {
  ROS_INFO("Reconfigure Request: %s %d %f %s %d",
            config.BOOL_PARAM?"True":"False",
            config.INT_PARAM,
            config.DOUBLE_PARAM,
            config.STR_PARAM.c_str(),
            config.SIZE);
}

另外,在主函数中是使用parameter_server_Config configuration文件来初始化服务器的。当服务器收到重新配置的请求时,就会跳转到callback函数进行处理。

dynamic_reconfigure::Server<parameter_server_tutorials::paramet
er_server_Config> server;
dynamic_reconfigure::Server<parameter_server_tutorials::paramet
er_server_Config>::CallbackType f;
  f = boost::bind(&callback, _1, _2);
  server.setCallback(f);

8.最后,我们需要将下面的代码添加到ROS构建系统的文件CMakeLists.txt中来完成编译。

add_executable(parameter_server_tutorials
src/parameter_server_tutorials.cpp)
add_dependencies(parameter_server_tutorials
parameter_server_tutorials_gencfg)
target_link_libraries(parameter_server_tutorials
${catkin_LIBRARIES}

现在我们已经完成代码部分的开发了,接下来需要编译并运行这个节点,同时启动ROS提供的可视化参数动态配置工具(Dynamic ReconfigureGUI),执行的命令如下所示:

$ roscore
$ rosrun parameter_server_tutorials parameter_server_tutorials
$ rosrun rqt_reconfigure rqt_reconfigure

9.在图3-1所示的可视化界面中,可以动态地修改节点参数。

图3-1 可视化参数动态配置工具

当用户通过滑块、复选框等修改参数时,我们可以在节点运行的命令行(shell)中看到修改的信息(见图3-2)。

图3-2 可视化参数动态配置工具的回显

ROS中提供的“Dynamic Reconfigure”工具非常有用,我们通过它可以更快、更有效地完成对与硬件连接节点的调整与验证。我们将在接下来的章节中了解这方面的更多内容。