3 ROS1通讯编程提高
3.3 动态重配参数
3.3.1 动态重配参数作用
参数服务器的数据被重新配置后时,如果节点不重新访问,那么就不能获取配置后的数据,针对这一特殊场景,ROS提出了动态重配参数(dynamic reconfigure),即参数一旦修改,能够使得节点读取修改后的数据,比如:在某些GUI界面中,可以向节点查询一组可重新配置的参数,包括它们的名称、类型和范围,并向用户呈现定制的效果。
3.3.2 动态重配参数基本编程
根据官网提供的教程:
3.3.2.1 创建动态参数项目
(一)创建功能包和ROS文件夹:
- 打开VS Code和工作空间,新建动作编程功能包并创建ROS文件夹结构。
-
- 打开VS Code和工作空间,在工作空间catkin_ws下输入命令code .右键工作空间catkin_ws的src目录,选择create catkin package然后在VS Code的提示下输入包名dy_reconfigure,回车之后输入依赖项roscpp rospy std_msgs即可。在功能包下新建cfg文件夹和scripts文件夹。
-
- 编写动态重配参数文件.cfg文件(类似于Python节点文件):
动态重配参数文件相当于一个客户端文件,文件格式类似于Python。
- 编写动态重配参数文件.cfg文件(类似于Python节点文件):
3.3.2.2 创建动态参数文件
- 编写自定义动态重配参数文件
myDyCfg.cfg
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# catkin_ws/src/dy_reconfigure/cfg/myDyCfg.cfg
# 指定功能包的位置
PACKAGE = "dy_reconfigure"
# 导入包
from dynamic_reconfigure.parameter_generator_catkin import *
# 2.创建动态重配参数生成器
gen = ParameterGenerator()
# 使用方法add(name, type, level, description, default=None, min=None, max=None, edit_method="")
# name:一个字符串,用于指定应存储此参数的名称
# type:定义存储的值的类型,可以是int_t、double_t、str_t或bool_t中的任何一个
# level:A bitmask which will later be passed to the dynamic reconfigure callback.
# description:描述参数的字符串
# default:指定默认值
# min:指定最小值(可选,不适用于字符串和布尔值)
# max:指定最大值(可选,不适用于字符串和布尔值)
gen.add("myint", int_t, 0, "整形式托拉条", 50, 0, 100)
# 创建枚举类型,在rqt-gui实现下拉操作
myListobj = 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("mylist", int_t, 0, "下拉式列表", 1, 0, 3, edit_method=myListobj)
# exit(gen.generate("功能包名", "节点名", "cfg文件名"))
exit(gen.generate(PACKAGE, "mydycfg_node", "myDyCfg"))
3.3.2.2 配置动态参数文件
(三)配置动态重配参数文件.cfg文件:
配置cfg文件主要包括给予cfg文件可执行权限,并且只需要在CMakeList.txt
进行依赖项添加
-
赋予cfg文件可执行权限,在功能包的cfg文件夹下,执行命令
sudo chmod +x *.cfg
即可。 -
打开功能包的CMakeList.txt文件,主要修改两项
-
- 添加编译依赖项功能包:
find_package(catkin REQUIRED COMPONENTS ..+ 依赖项功能包)
,大约在第10行,添加dynamic_reconfigure
:
- 添加编译依赖项功能包:
# catkin_ws/src/dy_reconfigure/CMakeLists.txt
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
dynamic_reconfigure
)
-
- 添加自定义cfg文件:
generate_dynamic_reconfigure_options (cfg/文件名)
,大约在第92行,如此时的文件名为myDyCfg.cfg
:
- 添加自定义cfg文件:
# catkin_ws/src/dy_reconfigure/CMakeLists.txt
generate_dynamic_reconfigure_options(
cfg/myDyCfg.cfg
)
(四)编译cfg文件,查看中间文件:
- 在工作空间下编译,此时在工作空间的devel目录会多出几个文件,如
- C++的中间文件在
/home/ubuntu/catkin_ws/devel/include/功能包名/文件夹
下, - Python的中间文件在
/home/ubuntu/catkin_ws/devel/lib/python2.7/dist-packages/功能包名cfg
3.3.2.3 编译C++代码作为服务端
(五)编译C++代码作为服务端:
- 创建C++程序作为服务端,并在CMakeList.txt配置C++文件。
-
- 创建客户端程序。创建客户端程序
dyconfig_cpp.cpp
。
- 创建客户端程序。创建客户端程序
// catkin_ws/src/dy_reconfigure/src/dyconfig_cpp.cpp
#include <ros/ros.h>
#include <dynamic_reconfigure/server.h>
#include <dy_reconfigure/myDyCfgConfig.h>
void callback(dy_reconfigure::myDyCfgConfig &config, uint32_t level) {
ROS_INFO("终端打印输出,整型数值是 %d,下拉条层级是 %d",config.myint,config.mylist);
}
int main(int argc, char **argv) {
setlocale(LC_ALL,"");
ros::init(argc, argv, "dy_configure");
dynamic_reconfigure::Server<dy_reconfigure::myDyCfgConfig> server;
dynamic_reconfigure::Server<dy_reconfigure::myDyCfgConfig>::CallbackType f;
f = boost::bind(&callback, _1, _2);
server.setCallback(f);
ros::spin();
return 0;
}
-
- 在CMakeList.txt文件中设置C++,主要包括三项
# catkin_ws/src/dy_reconfigure/CMakeLists.txt
add_executable(dyconfig_cpp_node src/dyconfig_cpp.cpp)
add_dependencies(dyconfig_cpp_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(dyconfig_cpp_node ${catkin_LIBRARIES})
-
- 编译运行,并且使用rqt-gui动态调整数值。启动客户端
rosrun dy_reconfigure dyconfig_cpp_node
,启动rqt工具实现动态重调参数rosrun rqt_gui rqt_gui -s rqt_reconfigure
- 编译运行,并且使用rqt-gui动态调整数值。启动客户端
3.3.2.4 编译Python代码作为服务端
(六)编译Python代码作为服务端:
- 编写Python文件实现客户端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# catkin_ws/src/dy_reconfigure/scripts/dyconfig_py.py
import rospy
from dynamic_reconfigure.server import Server
from dy_reconfigure.cfg import myDyCfgConfig
def callback(config, level):
rospy.loginfo("终端打印输出,整型数值是 %d,下拉条层级是 %d",config.myint,config.mylist)
return config
if __name__ == "__main__":
rospy.init_node("dynamic_myDyCfg", anonymous = False)
srv = Server(myDyCfgConfig, callback)
rospy.spin()
-
- 给Python文件赋予可执行权限,在script文件夹下输入命令
sudo chmod +x *.py
即可赋予所有Python可执行权限。
- 给Python文件赋予可执行权限,在script文件夹下输入命令
-
- 运行py文件,打开rqt工具
3.3.3 动态重配参数总结
查考文献:
- ROS-WIKI:dynamic_reconfigure/Tutorials
3.4 launch文件
launch文件能够在ROS中一次启动多个节点。下面主要介绍launch文件的常用启动参数和作用。
3.4.1 node标签
- node主标签:
启动文件的核心是启动ROS节点,采用标签定义,语法如下:
<node pkg="package-name" type="executable-name" name="node-name" />
从上面的定义规则可以看出,在启动文件中启动一个节点至少需要三个属性关键字: pkg
、type
和name
。
-
pkg
定义节点所在的功能包名称;
-
type
定义节点的可执行文件名称,这两个属性等同于在终端中使用rosrun命令执行节点时的输入参数;
-
name
属性用来定义节点运行的名称,将覆盖编程节点文件中init()赋予节点的名称。
-
output = "log/screen"
:将节点的标准输出打印到终端屏幕;
-
respawn = "true/false"
:复位属性,该节点停止运行,则会自动重启,默认为false;
-
required = "true/false"
:必要节点,当该节点终止时,launch文件中的其他节点也被终止;
-
ns = "namespace"
:命名空间,为节点内的相对名称添加命名空间前缀。
-
args = "arguments"
:节点需要的输入参数。
- remap子标签:
remap标签允许以更结构化的方式将名称重新映射参数传递给ROS节点,而不是直接设置的参数属性,使用用法如下所示:
<launch>
<node pkg="mypackage" type="mynodename" name="nodename" output="screen">
<remap from="A" to="B" />
</node>
</launch>
其中from="原始话题名称" to "目标名称"
- param子标签:
parameter是ROS系统运行中的参数,存储在参数服务器中。
在 launch文件中通过<param>
元素加载parameter
,launch文件执行后,parameter就加载到ROS的参数服务器上了。每个活跃的节点都可以通过ros::param:get()
接口来获取parameter的值,用户也可以在终端中通过rosparam
命令获得parameter的值。<param>
的使用方法如下:
<param name= " output_frame" value="odom" />
parameter的参数主要入下:
-
name="命名空间/参数名"
,参数名称,可以包含命名空间
-
value="xxx" (可选)
,定义参数值,如果此处省略,必须指定外部文件作为参数源
-
type="int" (可选)
,指定参数类型,如果未指定,roslaunch会尝试自动确定参数类型。
- rosparam子标签:
在很多复杂的系统中参数的数量很多,如果这样一个一个地设置会非常麻烦,ROS也为我们提供了另外一种类似的参数(<rosparam>
)加载方式帮助我们将一个YAML格式文件中的参数全部加载到ROS参数服务器中,需要设置command属性为“load”
,还可以选择设置命名空间“ns”
,如下所示:
<rosparam file="$ (find yourpackage) /config/xx. yam1 "command="load" ns="cc" />
3.4.2 其他标签
- arg:
argument
是另外一个概念,类似于launch 文件内部的局部变量,仅限于launch文件使用,便于launch文件的重构,与ROS节点内部的实现没有关系。
设置argument使用<arg>
标签元素,语法如下:
<arg name="arg-name" default= "arg-value" />
其中包括的参数有(rgument标签不存在子集标签):
-
name="参数名称"
-
default="默认值" (可选)
-
value="数值" (可选)
,不可以与 default 并存
-
doc="描述"
。
launch文件中需要使用到argument
时,可以使用如下方式调用:
<param name="foo" value="$(arg arg-name)"/>
<node name="node" pkg="package" type="type" args="$(arg arg-name)" />
- include标签:
include标签用于将另一个 xml 格式的 launch 文件导入到当前文件,例如
<include file="$(find yourpackage)/launch/yourlaunch.launch" >
<arg name="XX" value="XXX" />
</include>
其中属性包括:
-
file="$(find 包名)/xxx/xxx.launch"
,要包含的文件路径
-
ns="xxx" (可选)
,在指定命名空间导入文件
其中包含的子集标签有env环境变量和arg 将参数传递给被包含的文件。