ROS 动态参数配置Dynamic Reconfigure 使用的一系列问题
动态参数配置在机器人中使用还是挺频繁的,避免了ros中参数服务器的来回更改并且需要不断监听的缺陷,
动态参数配置就是ROS中的一个Service,需要改变参数的节点就充当一个服务器Server,调用改变参数的
rqt_reconfigure就相当于是客户端Client,这样配置参数相当方便。但是我在使用的过程中实在是遇到了
相当多的问题,果真还是我太菜了。下面详细说一下我遇到的一些问题。
动态参数的基本使用
在package.xml中配置依赖
1 | <build_depend>dynamic_reconfigure</build_depend> |
###创建一个配置文件
这个配置文件里主要就是确定需要动态改变的参数还有改变的方式以及默认参数、参数范围等。在package内建立一个文件夹cfg。$mkdir cfg
$torch (filename).cfg
1 | #!/usr/bin/env python |
我使用这个的原因是图像调整hsv的阈值用于颜色检测。我这里改成”hsv_”,最后就是 #include <ros_cutball/hsv_Config.h>
最后最好自己写个launch文件,把自己的参数yaml文件加入进去,方便调参。
这里一定要记得
1 | chmod a+x (your_package)/cfg/(filename).cfg |
将cfg文件设置可执行权限。
更改CmakeList.txt
在find_package下加入dynamic_reconfigure
,紧随着find_package,添加
1 | generate_dynamic_reconfigure_options( |
这个必须紧跟着,否则cmake会找不到文件中的配置文件然后报错。
cpp文件的编写
1 | #include <ros/ros.h> |
这段代码是写在主函数中的,我写在自定义的类中,所以f = boost::bind(&callback, _1, _2);中的callback需要加上类名解析。
我的callback是直接修改类中的一个成员的:nh_.getParam("H1",range_hsv[0]);
。
所以在callback中我直接使用nodehandle调取修改过的参数“H1”赋值给一个成员变量。
由于我们同时订阅与发布话题,所以主循环中使用ros::spinOnce()
还有一点,include中的dynamic_tutorials/TutorialsConfig.h,后面是在config文件中最后自己设置的名称。
但是这样写可能会失败
当编译后,运行节点,运行zed相机的launch文件,打开$rosrun rqt_reconfigure rqt_reconfigure
发现;里面没有我的节点的动态修改文件界面,查阅zed相机功能包的节点动态参数源代码的编写,将动态参数配置的代码改为
1 | //boost::shared_ptr<dynamic_reconfigure::Server<ros_cutball::hsv_Config>> mDynRecServer; |
然后就ok了。感觉应该是boost库的智能指针上操作的一些原因,目前不太了解,先留着问题。
效果如上
备注一下问题,第一次动态参数的bind中的回调函数没有指明正确,出现了有关boost一类的报错,最终查明。由于指针类型的原因,回调函数的参数格式一定要是教程中的类型。
继续探索
主要是cfg中设置了默认值的原因。由于我的图像部分代码是在无人机起飞前就要调整好图像的颜色阈值的,这样飞机上的机载RGB相机可以通过颜色识别来进行之后的一系列操作。但是由于设置了默认值的原因,通过我自己编写的launch文件每次启动节点颜色的阈值都是默认值,如果要修改默认值,必须重新编译整个package,这样反而大大降低效率,也不符合我使用ROS的初衷。所以进行了更多的探索。
想法一
通过在launch文件中更改参数服务器中的参数数值,以为会使默认值抵消。
前面说过,动态参数配置实际上是一个服务,我仅仅改变参数服务器的上的参数的之没有调用服务器的回调函数,然而我的使程序内阈值变化的代码是放在回调函数中的,所以,无论是手动改变参数还是launch文件启动时改变都无济于事,虽然参数服务器上的值变化了,但是程序内部的阈值没有。
想法二
然后去维基百科上查找文档,发现有通过命令行手动调用dynamic_reconfigure的方法修改参数并且调用回调函数,经过我的测试确实可以,于是将这个rosrun的调用写在了launch文件中,但是启动launch后还是无效。原因可能是launch文件内节点的启动顺序不一致造成的,可能我的节点在我配置参数后才启动,那么程序内的server启动后还是会使用默认值。
1 | $ rosrun dynamic_reconfigure dynparam set /node parameter_name value |
这个命令手动调用动态参数的回调函数。
多个参数可以这样
1 | $ rosrun dynamic_reconfigure dynparam set wge100_camera "{'camera_url':'foo', 'brightness':58}" |
后面是个yaml的字典。
想法三
继续查文档,看FAQ,里面提问我的需求的人很少,但是发现了有在C++程序内手动调用Client.Call的方法。如果在Server启动后就Call一下不久解决了吗,将前一次调好的阈值参数保存在一个文件中,然后在程序内加载就行了。我觉得这个方法应该可行,目前来不及测试,附上链接,待需要时查看。
- https://answers.ros.org/question/12276/is-there-a-c-api-for-a-dynamic-reconfigure-client/
- https://answers.ros.org/question/57498/notify-changes-to-dynamic_reconfigure/
想法四(采用)
将近看了四个小时,最后发现开发人员的一条回答。说dynamic_reconfigure在关闭后是将最后的参数保存下来的。
前提是没有关闭roscore, 我觉得这个简直是一句话解决我所有问题。
经过测试,首先启动roscore,然后启动动态参数配置节点的launch文件,更改参数后,然后关闭launch文件,再次启动launch文件,发现的确参数保持上次关闭时候的参数。不懈查找的结果
- 通过查阅FAQ与github的issue,他们在2013年的时候就添加了这个机制:动态参数服务建立时就会获取参数服务器上的需要动态更改的参数的信息,如果存在,则使用参数服务器上的值,否则使用默认的值。
这样,在每次进行项目时,先启动roscore后启动节点的调试模式修改参数,不能关闭roscore,然后重启节点进行生产模式后,执行业务。