ROS学习(八)ROS参数服务(2)

前言

在上一篇文章中,实现了ROS程序中的参数初始化,并且用一个while循环检测参数服务器中的参数变化,实现了参数的动态修改。显然这样实现动态修改参数并不是最优,要浪费许多系统资源来检测参数修改,还有一个更优更方便,动态修改参数的方法,那就是使用dynamic_reconfigure参数服务。先贴代码

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <ros/ros.h>
// Dynamic reconfigure includes.
#include <dynamic_reconfigure/server.h>
// Auto-generated from cfg/ directory.
#include <param_demo/dynamic_paramConfig.h>
using namespace std;
string s;
int num;
void paramConfigCallback(param_demo::dynamic_paramConfig &config, uint32_t level)
{
s = config.s;
num = config.num;
printf("\nstring_param: %s\n", s.c_str());
printf("int_param: %d\n\n", num);
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "dynamic_param_demo");
ros::NodeHandle pnh("~");
dynamic_reconfigure::Server<param_demo::dynamic_paramConfig> dr_srv;
dynamic_reconfigure::Server<param_demo::dynamic_paramConfig>::CallbackType cb;
cb = boost::bind(&paramConfigCallback, _1, _2);
dr_srv.setCallback(cb);
pnh.param<string>("string_param", s, "default_string");
pnh.param<int>("int_param", num, 2333);
printf("\nstring_param: %s\n", s.c_str());//输出初始化值
printf("int_param: %d\n\n", num);//输出初始化值
ros::spin();
}

代码分析

1
2
dynamic_reconfigure::Server<param_demo::dynamic_paramConfig> dr_srv;
dynamic_reconfigure::Server<param_demo::dynamic_paramConfig>::CallbackType cb;

留意<>中的参数,param_demo是package名称,dynamic_paramConfig我们添加的参数配置服务,添加方式下文详述。

1
2
cb = boost::bind(&paramConfigCallback, _1, _2);
dr_srv.setCallback(cb);

paramConfigCallback是回调函数的名称,在回调函数中获取变更后的参数值。

1
2
#include <dynamic_reconfigure/server.h>
#include <param_demo/dynamic_paramConfig.h>

要使用动态参数配置服务,要包含配置文件,第一个是ros中dynamic_reconfigure程序包中的头文件,第二个则是我们添加的配置文件,并通过编译后产生的,要编译产生这个文件,我们需要对package.xml和CMakeList.txt做一些改动。
package.xml

1
2
<build_depend>dynamic_reconfigure</build_depend>
<run_depend>dynamic_reconfigure</run_depend>

加入dynamic_reconfigure程序包作为依赖项

CMakeList.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
find_package(catkin REQUIRED COMPONENTS
...
dynamic_reconfigure
)
generate_dynamic_reconfigure_options(
cfg/dynamic_param.cfg
)
catkin_package(
...
CATKIN_DEPENDS ... dynamic_reconfigure
)

在上面几个地方加入对应的依赖项,其中cfg/dynamic_param.cfg是我们编写的配置文件,通过这个编译选项生成dynamic_paramConfig.h,dynamic_param.cfg位于cfg文件夹中,我们看下其中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#! /usr/bin/env python
PACKAGE='param_demo'
import roslib
roslib.load_manifest(PACKAGE)
from dynamic_reconfigure.parameter_generator_catkin import *
gen = ParameterGenerator()
# Name Type Level Description Default Min Max
gen.add("s", str_t, 0, "string_param.", "hello")
gen.add("num", int_t, 0, "int_param.", 1, -100, 100)
exit(gen.generate(PACKAGE, "param_demo", "param_demo"))

其中,名字s和num和我们参数表中的变量名称对应,虽然只要在回调函数中赋值的时候进行正确的匹配,名字不同也可以,但是保持相同的名称有利于我们程序的编写和对参数的管理,如何编写cfg文件,可以参考下面的网页
http://wiki.ros.org/dynamic_reconfigure/Tutorials/HowToWriteYourFirstCfgFile

1
2
3
4
5
6
void paramConfigCallback(param_demo::dynamic_paramConfig &config, uint32_t level)
{
s = config.s;
num = config.num;
...
}

完成这些工作后,编译并运行这个节点,当然现在我们尽量选择用launch文件来启动并初始化参数,会在终端看到以下信息

1
2
3
4
5
string_param: hello
int_param: 1
string_param: initialize from launch file
int_param: 999

这个信息有什么意义呢,也就是说,我们运行节点的时候,参数配置的回调函数会在初始化时被调用一次,改变了参数的值,这些初始化值可能与launch文件里给定的默认值不同,因此,我们要注意的是,将下面这个代码段

1
2
pnh.param<string>("string_param", s, "default_string");
pnh.param<int>("int_param", num, 2333);

放在初始化参数服务回调函数之后,这两句代码虽然对参数进行了初始化,但是如果从launch文件启动的话,其默认值由launcn文件中的参数值给定,这样,保证了我们运行节点初始化的参数值是确定的,这个对于一些对程序影响较大的参数的初始化很有意义。

现在,还剩下的问题是如何去改变我们希望动态配置的参数呢,有两种选择
1.使用命令行
具体参考下面网页
http://wiki.ros.org/dynamic_reconfigure
2.使用rqt_reconfigure
参考页面
http://wiki.ros.org/rqt_reconfigure
我们在终端输入命令

1
rosrun rqt_reconfigure rqt_reconfigure

可以看到一个参数更改页面,我们也可以将这个命令放到launch文件中和节点一起启动,在调试的时候十分方便。
完整工程代码查看
https://github.com/wenglihong/wlh_ros_demo/tree/master/param_demo