为什么需要
在机器人开发中,会有很多参数和设置可以后期需要调整的,如果都放到源码里很难实现动态修改和管理,ROS2为了解决这一问题,提出了参数这一通信机制。
是什么
如何理解“参数”?
- ROS2的参数就是节点的设置
- 参数类似于ROS中的全局变量,由ROS Master进行管理,其通信机制较为简单,不涉及TCP/UDP的通信。
参数服务器是一种特殊的“通信方式”。特殊点在于参数服务器是节点存储参数的地方、用于配置参数、全局共享参数。
- 参数服务器使用互联网传输,在节点管理器中运行,实现整个通信过程
- 参数服务器,作为ROS中另外一种数据传输方式,有别于topic和service,它更加静态。
- 参数服务器维护者一个数据字典,字典里存储着各种参数和配置
- 参数服务器在ROS中主要用于实现不同节点之间的数据共享。一般用于存储一些多节点共享的数据,类似于全局变量。
- 参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据。
维护方式
参数服务器的维护方式非常的简单灵活,总的来讲有三种方式:
命令行维护
使用命令行来维护参数服务器,主要使用rosparam语句来进行操作的各种命令,如下表:
ROS1:rosparam 命令 | 作用 |
---|
rosparam set param_key param_value | 设置参数 |
rosparam get param_key | 显示参数 |
rosparam load file_name | 从文件加载参数 |
rosparam dump file_name | 保存参数到文件 |
rosparam delete | 删除参数 |
rosparam list | 列出参数名称 |
load&&dump文件
load和dump文件需要遵守YAML格式,一般格式如下:
key : value
具体示例如下:
name:'Zhangsan'
age:20
gender:'M'
score{Chinese:80,Math:90}
score_history:[85,82,88,90]
一般格式如下:
launch文件内读写
launch文件中有很多标签,而与参数服务器相关的标签只有两个,一个是< param>,另一个是< rosparam>
node源码
除了上述最常用的两种读写参数服务器的方法,还有一种就是修改ROS的源码,也就是利用API来对参数服务器进行操作。具体内容我们学习完后面章节再进行介绍。
ROS2:体验一下
(1)运行小乌龟模拟器节点和小乌龟控制节点
ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key
(2)查看参数
ros2 param list
![在这里插入图片描述](https://img-blog.csdnimg.cn/76f3f8808df04a909c446cd7b3b3f6a1.png)
- 看参数的详情信息:参数的名字,参数的描述,参数的类型,还有对参数的约束,最大值最小值等。
ros2 param describe <node_name> <param_name>
ros2 param describe /turtlesim background_b
![在这里插入图片描述](https://img-blog.csdnimg.cn/4cc37e47268647e4b22efe4ee8267669.png)
param list
ros2 param get /turtlesim background_b
![在这里插入图片描述](https://img-blog.csdnimg.cn/d58951fffe6241debe55f702ff9ad162.png)
ros2 param set <node_name> <parameter_name> <value>
ros2 param set /turtlesim background_r 44
ros2 param set /turtlesim background_g 156
ros2 param set /turtlesim background_b 10
- 把参数存起来:相当去把当前的参数值拍一张快照,然后保存下来,后面可以用于恢复参数到当前的数值。
ros2 param dump <node_name>
ros2 param dump /turtlesim
文件被保存成了yaml格式,用cat指令看一看
ros2 run turtlesim turtlesim_node
ros2 param load /turtlesim ./turtlesim.yaml
ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>
ros2 run turtlesim turtlesim_node --ros-args --params-file ./turtlesim.yaml
三个角色
- ROS Master (管理者):管理者作为一个公共的容器保存数据
- Talker (参数设置者):参数设置者往容器中存储数据
- Listener (参数调用者): 参数调用者读取容器中所需的数据
建立流程
![在这里插入图片描述](https://img-blog.csdnimg.cn/570a13011d274768b272523717873517.png)
- step1:
- 参数设置者使用RPC向参数服务器发送参数(包括参数名与参数值)
- ROS Master 将参数保存到参数列表中。
- step2:
- 参数调用者使用RPC向参数服务器发送参数查找请求,请求中包含要查找的参数名。
- step3:
- ROS Master 根据步骤2请求提供的参数名查找参数值,并使用RPC将查询结果发送给参数调用者。
参数组成成分
ROS2参数是由键值对组成的.
- 名字的数据类型是字符串
- 值的数据类型可以是:
- bool 和bool[],布尔类型用来表示开关,比如我们可以控制雷达控制节点,开始扫描和停止扫描。
- int64 和int64[],整形表示一个数字,含义可以自己来定义
- float64 和float64[],浮点型,可以表示小数类型的参数值
- string 和string[],字符串,可以用来表示雷达控制节点中真实雷达的ip地址
- byte[],字节数组,这个可以用来表示图片,点云数据等信息
代码实现
ROS2将日志分为五个级别,在RCLCPP中通过不同的宏可以实现不同日志级别日志的打印,例程如下
RCLCPP_DEBUG(this->get_logger(), "我是DEBUG级别的日志,我被打印出来了!");
RCLCPP_INFO(this->get_logger(), "我是INFO级别的日志,我被打印出来了!");
RCLCPP_WARN(this->get_logger(), "我是WARN级别的日志,我被打印出来了!");
RCLCPP_ERROR(this->get_logger(), "我是ERROR级别的日志,我被打印出来了!");
RCLCPP_FATAL(this->get_logger(), "我是FATAL级别的日志,我被打印出来了!");
有时候日志太多,会让人眼花缭乱找不到重要信息,所以我们需要对日志的级别进行过滤,比如只看INFO以上级别的,ROS2中可以通过已有的API设置日志的级别,RCLCPP中API如下:
this->get_logger().set_level(log_level);
目标:声明参数并实现动态修改打印的日志级别功能。
RCLCPP实现
创建功能包和节点
mkdir -p chapt4/chapt4_ws/
ros2 pkg create example_parameters_rclcpp --build-type ament_cmake --dependencies rclcpp --destination-directory src --node-name parameters_basic --maintainer-name "fishros" --maintainer-email "fishros@foxmail.com"
parameters_basic.cpp
#include <chrono>
#include "rclcpp/rclcpp.hpp"
class ParametersBasicNode : public rclcpp::Node {
public:
explicit ParametersBasicNode(std::string name) : Node(name) {
RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
}
private:
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<ParametersBasicNode>("parameters_basic");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
构建测试:
colcon build --packages-select example_parameters_rclcpp
source install/setup.bash
ros2 run example_parameters_rclcpp parameters_basic
参数API
在RCLCPP的API中,关于参数相关的函数比较多些,但都是围绕参数获取、参数设置、参数描述、列出参数、添加和移除参数回调事件。
使用参数控制节点日志级别
#include <chrono>
#include "rclcpp/rclcpp.hpp"
class ParametersBasicNode : public rclcpp::Node {
public:
explicit ParametersBasicNode(std::string name) : Node(name) {
RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
this->declare_parameter("rcl_log_level", 0);
this->get_parameter("rcl_log_level", log_level);
this->get_logger().set_level((rclcpp::Logger::Level)log_level);
using namespace std::literals::chrono_literals;
timer_ = this->create_wall_timer(
500ms, std::bind(&ParametersBasicNode::timer_callback, this));
}
private:
int log_level;
rclcpp::TimerBase::SharedPtr timer_;
void timer_callback() {
this->get_parameter("rcl_log_level", log_level);
this->get_logger().set_level((rclcpp::Logger::Level)log_level);
std::cout<<"======================================================"<<std::endl;
RCLCPP_DEBUG(this->get_logger(), "我是DEBUG级别的日志,我被打印出来了!");
RCLCPP_INFO(this->get_logger(), "我是INFO级别的日志,我被打印出来了!");
RCLCPP_WARN(this->get_logger(), "我是WARN级别的日志,我被打印出来了!");
RCLCPP_ERROR(this->get_logger(), "我是ERROR级别的日志,我被打印出来了!");
RCLCPP_FATAL(this->get_logger(), "我是FATAL级别的日志,我被打印出来了!");
}
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<ParametersBasicNode>("parameters_basic");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
上面set_level,设置日志级别,ROS2的日志级别定义在文件/opt/ros/humble/include/rcutils/rcutils/logging.h的167-175行
enum RCUTILS_LOG_SEVERITY
{
RCUTILS_LOG_SEVERITY_UNSET = 0,
RCUTILS_LOG_SEVERITY_DEBUG = 10,
RCUTILS_LOG_SEVERITY_INFO = 20,
RCUTILS_LOG_SEVERITY_WARN = 30,
RCUTILS_LOG_SEVERITY_ERROR = 40,
RCUTILS_LOG_SEVERITY_FATAL = 50,
};
编译测试
colcon build --packages-select example_parameters_rclcpp
source install/setup.bash
ros2 run example_parameters_rclcpp parameters_basic
运行后你会发现DEBUG级别的日志并没有被打印出来,原因在于我们将节点的日志级别设置为了0,0对应的日志级别为RCUTILS_LOG_SEVERITY_UNSET即未设置使用默认级别,节点默认的日志级别就是INFO级别的,所以只能打印INFO以上的日志信息。
运行节点的时候可以指定参数的值,我们尝试将log_level的值改成10即DEBUG级别。
ros2 run example_parameters_rclcpp parameters_basic --ros-args -p rcl_log_level:=10
除了在节点运行前通过CLI传递参数,在运动的过程中也可以动态的修改参数
#查看参数列表
ros2 param list
#设置参数级别
ros2 param set /parameters_basic rcl_log_level 10
![在这里插入图片描述](https://img-blog.csdnimg.cn/a079a0b22c984663a3f0951e585da66e.png)
补充
上面我们通过参数实现了动态控制节点日志级别的功能,其实像这样的功能ROS2早已为我们准备好了,在运行任意节点时候可以通过CLI传递日志级别配置。
ros2 run package-name node-name --ros-args --log-level debug
除了命令行设置参数和查看日志,通过rqt也可以可视化设置和查看
![在这里插入图片描述](https://img-blog.csdnimg.cn/0d76376e11ce4fc594ab4fce7b08aa4a.png)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)