关于ROS下的Dynamic Reconfig的学习探索

Dynamic Reconfig是ROS比较厉害的一个地方,可以在程序运行的时候动态调整参数,ROS官网关于Dynamic Reconfig的讲解主要有三处

1. http://wiki.ros.org/dynamic_reconfigure/Tutorials

2. http://wiki.ros.org/roscpp_tutorials/Tutorials 最下面

3. http://wiki.ros.org/ROSNodeTutorialC++ 最下面

个人感觉第一个好一点,另外两个比较杂乱。下面就介绍自己关于Dynamic Reconfig学习过程,这里假定大家都做完了,Beginner tutorials的学习,即写一个Publisher ,subscriber,自定义的service(两个数字的加法)以及Msg的学习。

主要参考:

http://wiki.ros.org/dynamic_reconfigure/Tutorials/SettingUpDynamicReconfigureForANode

我的最终的文件目录如图


1.定义cfg文件

首先在catkin_ws/src/beginner_tutorials目录下建立一个名为cfg的文件夹,放入一个名为MyStuff.cfg的文件,这个文件的作用就是告诉ROS 到时侯我们需要动态调整的参数有哪些。

#! /usr/bin/env python 
# Forearm camera configuration

PACKAGE='beginner_tutorials'
import roslib
roslib.load_manifest(PACKAGE)

from math import pi

from driver_base.msg import SensorLevels
from dynamic_reconfigure.parameter_generator import *

gen = ParameterGenerator()
angles = gen.add_group("Angles")
#       Name                    Type      Reconfiguration level
#       Description
#       Default  Min  Max
angles.add("min_ang",           double_t, SensorLevels.RECONFIGURE_STOP,
           "The angle of the first range measurement. The unit depends on ~ang_radians.",
           -pi/2,-pi, pi)
angles.add("max_ang",           double_t, SensorLevels.RECONFIGURE_STOP,
           "The angle of the first range measurement. The unit depends on ~ang_radians.",
            pi/2, -pi, pi)
gen.add("intensity",            bool_t,   SensorLevels.RECONFIGURE_STOP,
        "Whether or not the hokuyo returns intensity values.",
         False)
gen.add("cluster",              int_t,    SensorLevels.RECONFIGURE_STOP,
        "The number of adjacent range measurements to cluster into a single reading",
         1, 0, 99)
gen.add("skip",                 int_t,    SensorLevels.RECONFIGURE_STOP,
        "The number of scans to skip between each measured scan",
         0, 0,  9)
gen.add("port",                 str_t,    SensorLevels.RECONFIGURE_CLOSE,
        "The serial port where the hokuyo device can be found",
        "/dev/ttyACM0")
gen.add("calibrate_time",       bool_t,   SensorLevels.RECONFIGURE_CLOSE,
        "Whether the node should calibrate the hokuyo's time offset",
         True)
gen.add("frame_id",             str_t,    SensorLevels.RECONFIGURE_RUNNING,
        "The frame in which laser scans will be returned",
        "laser")
gen.add("time_offset",          double_t, SensorLevels.RECONFIGURE_RUNNING,
        "An offet to add to the timestamp before publication of a scan",
         0, -0.25, 0.25)
gen.add("allow_unsafe_settings",bool_t,   SensorLevels.RECONFIGURE_CLOSE,
        "Turn this on if you wish to use the UTM-30LX with an unsafe angular range. Turning this option on may cause occasional crashes or bad data. This option is a tempory workaround that will hopefully be removed in an upcoming driver version.",
         False)

exit(gen.generate(PACKAGE, "dynamic_reconfigure_node", "MyStuff"))

这里尤其要注意最后一句的第三个参数,必须与config文件同名,我们的config文件名为MyStuff.cfg所以这里为Mytuff。而且第三个参数也决定着我们的config头文件的名称,后面埋个伏笔,头文件生成的名称为MyStuffConfig.h也即,第三个参数加上Config.h.

修改cfg文件的权限

chmod a+x MyStuff.cfg

2.修改依赖项

在manifest.xml文件中加入依赖项即:

<build_depend>dynamic_reconfigure</build_depend>
<build_depend>driver_base</build_depend>


<run_depend>dynamic_reconfigure</run_depend>
<run_depend>driver_base</run_depend>

3.创建server主函数

在src文件夹下建立一个名为dynamic_reconfigure_node.cpp的文件,代码如下,尤其注意这里的第二个头文件的包含,因为我发现编译的时候,生成的config的头文件编译时放在了cfg文件夹下的cpp文件夹中,这个是catkin_make(catkin_make __force-cmake)的时候创建的,但是按照官网的路径它查找不到,我们干脆拷贝到src文件夹下

#include <dynamic_reconfigure/server.h>
#include "MyStuffConfig.h"
#include <ros/ros.h>

void callback(beginner_tutorials::MyStuffConfig &config, uint32_t level)
{
  ROS_INFO("Reconfigure request : %f %f %i %i %i %s %i %s %f %i",
           config.groups.angles.min_ang,
           config.groups.angles.max_ang,
           (int)config.intensity,
           config.cluster,
           config.skip,
           config.port.c_str(),
           (int)config.calibrate_time,
           config.frame_id.c_str(),
           config.time_offset,
           (int)config.allow_unsafe_settings);

  // do nothing for now
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "dynamic_reconfigure_node");
  dynamic_reconfigure::Server<beginner_tutorials::MyStuffConfig> srv;
  dynamic_reconfigure::Server<beginner_tutorials::MyStuffConfig>::CallbackType f;
  f = boost::bind(&callback, _1, _2);
  srv.setCallback(f);
  ROS_INFO("Starting to spin...");
  ros::spin();
  return 0;
}

4.关于CMakeList.txt的修改

这个文件相信大家不陌生,但是也是最头疼的地方,编译的错误好多都是设置不当引起的。我直接贴上来

cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
  dynamic_reconfigure
)

################################################
## Declare ROS messages, services and actions ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
##   * add a build_depend and a run_depend tag for each package in MSG_DEP_SET
##   * If MSG_DEP_SET isn't empty the following dependencies might have been
##     pulled in transitively but can be declared for certainty nonetheless:
##     * add a build_depend tag for "message_generation"
##     * add a run_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
##   * add "message_generation" and every package in MSG_DEP_SET to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * add "message_runtime" and every package in MSG_DEP_SET to
##     catkin_package(CATKIN_DEPENDS ...)
##   * uncomment the add_*_files sections below as needed
##     and list every .msg/.srv/.action file to be processed
##   * uncomment the generate_messages entry below
##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
 add_message_files(
  FILES
  Num.msg
 )

## Generate services in the 'srv' folder
 add_service_files(
   FILES
   AddTwoInts.srv
   
 )

## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )

## Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  std_msgs
 )

# for dynamic reconfigure
generate_dynamic_reconfigure_options(
  cfg/MyStuff.cfg
  
)

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if you package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package( CATKIN_DEPENDS message_runtime)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(${dynamic_reconfigure_PACKAGE_PATH}/cmake/cfgbuild.cmake)

include_directories(${catkin_INCLUDE_DIRS})

## Declare a cpp library
# add_library(beginner_tutorials
#   src/${PROJECT_NAME}/beginner_tutorials.cpp
# )

## Declare a cpp executable
# add_executable(beginner_tutorials_node src/beginner_tutorials_node.cpp)

add_executable(talker src/talker.cpp)
add_executable(listener src/listener.cpp)

# for dynamic reconfigure
add_executable(dynamic_reconfigure_node src/dynamic_reconfigure_node.cpp)


add_executable(add_two_ints_server src/add_two_ints_server.cpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)

## Add cmake target dependencies of the executable/library
## as an example, message headers may need to be generated before nodes
# add_dependencies(beginner_tutorials_node beginner_tutorials_generate_messages_cpp)

# for dynamic reconfigure
add_dependencies(dynamic_reconfigure_node beginner_tutorials_gencfg)
# for python dynamic client
add_dependencies(dynamic_reconfigure_node beginner_tutorials_generate_messages_cpp)

add_dependencies(talker beginner_tutorials_generate_messages_cpp)
add_dependencies(listener beginner_tutorials_generate_messages_cpp)

add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)

## Specify libraries to link a library or executable target against
# target_link_libraries(beginner_tutorials_node ${catkin_LIBRARIES} )

# for dynamic reconfigure
target_link_libraries(dynamic_reconfigure_node ${catkin_LIBRARIES})

target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(listener ${catkin_LIBRARIES})

target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# install(PROGRAMS
#   scripts/my_python_script
#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables and/or libraries for installation
# install(TARGETS beginner_tutorials beginner_tutorials_node
#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
#   FILES_MATCHING PATTERN "*.h"
#   PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
#   # myfile1
#   # myfile2
#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_beginner_tutorials.cpp)
# if(TARGET ${PROJECT_NAME}-test)
#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

关于dynamic reconfig的主要几句是

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
  dynamic_reconfigure
)

generate_dynamic_reconfigure_options(
  cfg/MyStuff.cfg
 
)


include_directories(${dynamic_reconfigure_PACKAGE_PATH}/cmake/cfgbuild.cmake)


add_executable(dynamic_reconfigure_node src/dynamic_reconfigure_node.cpp)这里生成可执行的node文件,node名字是dynamic_reconfigure_node


add_dependencies(dynamic_reconfigure_node beginner_tutorials_gencfg)也有写成${PROJECT_NAME}_gencfg的


target_link_libraries(dynamic_reconfigure_node ${catkin_LIBRARIES})生成可执行的node文件,node名字是dynamic_reconfigure_node

5.测试用的程序

测试用到的程序,其实我在这里仿照了http://wiki.ros.org/dynamic_reconfigure/Tutorials/中的client.py的写法,但是它的有个缺点,按照ros.rate(0.1)即0.1Hz也即10s一次,他会复位,就是你调整的参数10s后就由默认值替代了,我这里修改了一下,让他不会复位

在src中放入一个名为yakeclient.py的文件,这里就是客户端了

#!/usr/bin/env python

PACKAGE = 'beginner_tutorials'
import roslib;roslib.load_manifest(PACKAGE)
import rospy

import dynamic_reconfigure.client

def callback(config):
    rospy.loginfo("Config set to {min_ang}, {max_ang}, {intensity}, {cluster}, {skip}, {port}, {calibrate_time}, {frame_id}, {time_offset},{allow_unsafe_settings}".format(**config))
   

if __name__ == "__main__":
    rospy.init_node("yakeclient")

# Client first argument is the node names
    client = dynamic_reconfigure.client.Client("dynamic_reconfigure_node", timeout=30, config_callback=callback)

    r = rospy.Rate(0.1)
    #x = 0
    #b = False 
    while not rospy.is_shutdown():
        #x = x+1
        #if x>10:
        #    x=0
        #b = not b
       # client.update_configuration({"int_param":x, "double_param":(1/(x+1)), "str_param":str(rospy.get_rostime()), "bool_param":b, "size":1})
    	r.sleep()
6.编译并启动程序


7.对比官方教程

这里我们可以看到其实结构都是一样的,最主要的区别还是在于config文件的定义,而他是完全自由的,完全由设计者考虑,需要动态调整一些什么样子的参数。


从下面的这个详细的节点关系图,我们可以看出,其实rqt_reconfig也即图中的rqt_gui_py_node_9301,就是一个client。那个9507是rqt_graph

关掉我们自己的yakeclient如图所示。因此自己的那个client目前只有显示的作用,(即只能看到设定的值)。而rqt_reconfig则包含了设置与显示。所以一般情况下,我们所要做的就是写dynamic server,利用rqt_reconfig作为client来设定目标值。


8.总结

这些内容写起来容易,但是自己花了差不多两天才解决掉,其中主要卡在了cfg.h上,因为系统编译老是提示找不到它,如果你也有这样的问题,首先看看CMakeList.txt内容是否正确,二,就像我之前那样,生成的头文件居然放在cfg文件夹里了。还有就是Client.py文件的写法。我当时起得名字叫yakeclient.py,观察rqt_graph时的节点信息老是订阅不了topic


像上图中的一样,然后仔细查看才发现python中的文件第17行

这里我想当然理解为了package name其实它应该是publisher 的名称,也就是server的名称,这里我们的server节点是dynamic_reconfigure_node所以改完以后就Ok了。引以为戒。

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yaked19

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值