参数与动作是两种通信方式,与话题,服务构成四种通信方式。
参数
参数是一个配置值,可以认为就是对一个节点的设置。在实际应用之中可以用来动态地设置或改变节点的内容。
参数的组成部分有名字和值来组成,名字的数据类型是字符串,值的数据类型有bool,int64,float64,string,byte。
当我们打开一个节点时可以利用下列代码在另外一个终端中查看节点有那些参数(设置)
ros2 param list
当我们打开乌龟模拟器后输入这个命令后,可以看到里面的background_b,background_g;都是对该模拟器这个展示出来乌龟的这个界面的一些设置,也就是对乌龟模拟器节点的设置。输入下了代码可以查看参数对节点的设置。
ros2 param describe <node_name> <param_name>
可以详细的看到参数的名字,参数的描述,参数的类型,还有对参数的约束,最大值最小值等。
如果我们想看这个参数的值的话可以输入下列指令
ros2 param get /turtlesim background_b
想要修改值的话可以用下列指令
ros2 param set <node_name> <parameter_name> <value>
上面是对小乌龟模拟器的背景颜色进行了设置。
另外当我们修改完这个值后,并不能直接使用,只是临时修改了,下次再打开的时候,会恢复原本的设置。所以我们可以用下列指令将数据存储起来(数据被存成了yaml的格式)
ros2 param dump <node_name>
当我们下次打开的时候会发现还是与原来的状态相同,要想改成我们修改后的状态的话输入下列指令(此处时乌龟模拟器的示例)
ros2 param load /turtlesim ./turtlesim.yaml
要想我们下次打开的时候暴保持修改后的状态的话输入下列指令
ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>
参数的使用
在上次的王二卖书的实例中,如果我们想要动态的修改书的价格,我们就要用到参数,我们只需要在原有的代码中加上声明参数,获取参数两步即可。
在rclcpp::Node中,有包含参数的一些函数,可以直接使用,如下
函数名称 | 描述 |
---|---|
declare_parameter | 声明和初始化一个参数 |
declare_parameters | 声明和初始化一堆参数 |
get_parameter | 通过参数名字获取一个参数 |
get_parameters | 获取具有给定前缀的所有参数的参数值 |
set_parameters | 设置一组参数的值 |
这些函数都是些模板函数,只要填入对应的信息就可以完成函数的功能。
我们在wang2.cpp下将wang2节点继承rclcpp::Node,然后我们在wang2类中加入声明
//声明一下书的单价
unsigned int novel_price = 1;
然后将这个novel_price声明成参数,我们在构造函数中实现这一步
//声明参数
this->declare_parameter<std::int64_t>("novel_price", novel_price);
然后我们可以进行下一步操作,获取参数。根据我们每次完成一次买书后的价格可能会有波动,所以我们每完成一次操作后我们都要实时的获取书的价格,那么我们就要在回调函数中实现参数的获取。
//更新参数
this->get_parameter("novel_price",novel_price);
unsigned int novelsNum = int( request->money / novel_price ); //应给小说数量,一块钱一章
这样一来我们的书的价格就可以实时更新了。
class SignalDogNode : public rclcpp::Node
{
public:
// 构造函数
SignalDogNode(std::string name) : Node(name)
{
//声明参数
this->declare_parameter<std::int32_t>("novel_price", novel_price);
}
private:
//声明一下书的单价
unsigned int novel_price = 1;
// 声明一个回调函数,当收到买书请求时调用该函数,用于处理数据
void sell_book_callback(const village_interfaces::srv::SellNovel::Request::SharedPtr request,
const village_interfaces::srv::SellNovel::Response::SharedPtr response)
{
//更新参数
this->get_parameter("novel_price",novel_price);
unsigned int novelsNum = int( request->money / novel_price ); //应给小说数量,钱/小说章节单价=章节数
};
当我们将这些的代码编译后,运行起来,就可以在终端中去修改参数的数值,我们将买书节点和卖书运行起来,然后在另开一个终端输入下列指令
ros2 param set /wang2 novel_price 2
那么我们就完成了对书的价格的调整。
Action
当我们在向一个服务端给出命令时,我们无法知道服务端是否有收到指令,以及我们不知道我们的请求的指令进展如何,如果我们想要取消这个请求时,如何解决上述问题呢。这个时候就需要Action来解决这些问题。
Action三大组成部分:
- 目标:即Action客户端告诉服务端要做什么,服务端针对该目标要有响应。解决了不能确认服务端接收并处理目标问题
- 反馈:即Action服务端告诉客户端此时做的进度如何(类似与工作汇报)。解决执行过程中没有反馈问题
- 结果:即Action服务端最终告诉客户端其执行结果,结果最后返回,用于表示任务最终执行情况。
Action实际是由三个服务和两个话题组成的,因为单个话题或者服务不能满足我们使用的需求。三个服务分别是:1.目标传递服务 2.结果传递服务 3.取消执行服务 两个话题:1.反馈话题(服务发布,客户端订阅) 2.状态话题(服务端发布,客户端订阅)
在乌龟模拟器中的键盘控制节点中:
Use arrow keys to move the turtle.
Use G|B|V|C|D|E|R|T keys to rotate to absolute orientations. 'F' to cancel a rotation.
使用 G B V C D E R T 可以原地旋转小乌龟,F可以取消这个动作,这一系列指令就是通过Action完成的,当我们按下G或其他字母时,我们就是向服务端发出了请求目标,然后图层中的小乌龟旋转,是服务端通过反馈,展示出来小乌龟的状态,然后小乌龟停止运动,是服务端给出的结果,如果我们按下F,则是利用了取消执行的服务。
下面是一些action的一些指令
ros2 action list
该指令可以将目前系统中的action展示出来;在list后加上-t可以看到action的类型,知道接口类型之后我们又可以用接口的指令
ros2 interface show turtlesim/action/RotateAbsolute
得出下列结果:
# The desired heading in radians
float32 theta
---
# The angular displacement in radians to the starting position
float32 delta
---
# The remaining rotation in radians
float32 remaining
这里是用来调整方向的接口设置,都是以弧度制为单位。
info:用来看action中的客户端和服务端的名字的数量;
ros2 action info /turtle1/rotate_absolute
action send_goal:
将命令传送给服务端
ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 1.6}"
如此一来小乌龟的方向就会有改变。
在这个过程中服务端没有向客户端给出反馈,但是我们可以在上述指令后加上--feedback,那么就会收到来自服务端的反馈了。
-----------------------------------------------------------------------------------------------------------
本次学习的心得:ros2的通信方式是活用的,应对不同的情形,可以利用不同的通信方式,可以满足我们的需求,单向频繁的使用话题,双向的使用服务,参数是对节点的设置可以实现动态的改变一个东西的值,action是实时的了解操作的完成情况如何,以及能够停止某项操作;另外,ros2中有很多的模板函数,某处填入的东西都是有讲究的,例如:this->declare_parameter<std::int64_t>("novel_price", novel_price);这句话中的int64_t是参数的数据类型之一,如果填入的不是参数的数据类型之一,则会报错,就不能实现声明参数这一步骤了。所以要了解每一个模板函数的每个位置需要填入的是什么,不能搞错。
遇到的困难:经常会忘记前面学的一些函数,或作忘记一些指令怎么写,以及一些函数是怎么实现存在着难以寻找到答案的时候。还有就是知识面窄,一些新的东西从来没有听说过,以及不知道什么时候用什么函数,以及干什么。
解决的办法:多练习多记忆,学会去使用再尝试着去理解其深层的东西。暂时的方向还是以视屏为主,在视屏里的实例中去体会这种ros2语言的感觉,体会每一步骤的作用。