第3章 ROS2通信机制补充
第2章介绍了ROS2通信机制的核心内容,核心内容更偏向于粗粒度框架的介绍。本章主要介绍关于通信机制的补充内容,比如:分布式框架搭建、重名问题处理、常用API、通信机制工具等等,这些补充内容的知识点比较零散但是每个知识点都不复杂,另外,本章最后还会通过若干练习来强化对ROS2通信机制的认识。
本章概览
案例演示
案例1:双向奔赴的小乌龟。
案例2:生成一只新的乌龟,并计算与原生乌龟的距离。
案例3:生成一只新的乌龟作为“目标点”,原生乌龟可以运动至该目标点。
案例4:动态修改乌龟窗口的背景颜色。
3.1 分布式
场景
在许多机器人相关的应用场景中都涉及到多台ROS2设备协作,比如:无人车编队、无人机编队、远程控制等等,那么不同的ROS2设备之间是如何实现通信的呢?
概念
分布式通信是指可以通过网络在不同主机之间实现数据交互的一种通信策略。
ROS2本身是一个分布式通信框架,可以很方便的实现不同设备之间的通信,ROS2所基于的中间件是DDS,当处于同一网络中时,通过DDS的域ID机制(ROS_DOMAIN_ID)可以实现分布式通信,大致流程是:在启动节点之前,可以设置域ID的值,不同节点如果域ID相同,那么可以自由发现并通信,反之,如果域ID值不同,则不能实现。默认情况下,所有节点启动时所使用的域ID为0,换言之,只要保证在同一网络,你不需要做任何配置,不同ROS2设备上的不同节点即可实现分布式通信。
作用
分布式通信的应用场景是较为广泛的,如上所述:机器人编队时,机器人可能需要获取周边机器人的速度、位置、运行轨迹的相关信息,远程控制时,则可能需要控制端获取机器人采集的环境信息并下发控制指令...... 这些数据的交互都依赖于分布式通信。
实现
多机通信时,可以通过域ID对节点进行分组,组内的节点之间可以自由通信,不同组之间的节点则不可通信。如果所有节点都属于同一组,那么直接使用默认域ID即可,如果要将不同节点划分为多个组,那么可以在终端中启动节点前设置该节点的域ID(比如设置为6),具体执行命令为:
上述指令执行后,该节点将被划分到ID为6的域内。
如果要为当前设备下的所有节点设置统一的域ID,那么可以执行如下指令:
执行完毕后再重新启动终端,运行的所有节点将自动被划分到ID为6的域内。
演示
注意
在设置ROS_DOMAIN_ID的值时并不是随意的,也是有一定约束的:
-
建议ROS_DOMAIN_ID的取值在[0,101] 之间,包含0和101;
-
每个域ID内的节点总数是有限制的,需要小于等于120个;
-
如果域ID为101,那么该域的节点总数需要小于等于54个。
DDS 域 ID 值的计算规则
域ID值的相关计算规则如下:
-
DDS是基于TCP/IP或UDP/IP网络通信协议的,网络通信时需要指定端口号,端口号由2个字节的无符号整数表示,其取值范围在[0,65535]之间;
-
端口号的分配也是有其规则的,并非可以任意使用的,根据DDS协议规定以7400作为起始端口,也即可用端口为[7400,65535],又已知按照DDS协议默认情况下,每个域ID占用250个端口,那么域ID的个数为:(65535-7400)/250 = 232(个),对应的其取值范围为[0,231];
-
操作系统还会设置一些预留端口,在DDS中使用端口时,还需要避开这些预留端口,以免使用中产生冲突,不同的操作系统预留端口又有所差异,其最终结果是,在Linux下,可用的域ID为[0,101]与[215-231],在Windows和Mac中可用的域ID为[0,166],综上,为了兼容多平台,建议域ID在[0,101] 范围内取值。
-
每个域ID默认占用250个端口,且每个ROS2节点需要占用两个端口,另外,按照DDS协议每个域ID的端口段内,第1、2个端口是Discovery Multicast端口与User Multicast端口,从第11、12个端口开始是域内第一个节点的Discovery Unicast端口与User Unicast,后续节点所占用端口依次顺延,那么一个域ID中的最大节点个数为:(250-10)/2 = 120(个);
-
特殊情况:域ID值为101时,其后半段端口属于操作系统的预留端口,其节点最大个数为54个。
上述计算规则了解即可。
附:
3.2 工作空间覆盖
场景
同一工作空间下不允许出现功能包重名的情况,但是当存在多个工作空间时,不同工作空间下的功能包是可以重名的,那么当功能包重名时,会调用哪一个呢?
概念
所谓工作空间覆盖,是指不同工作空间存在重名功能包时,重名功能包的调用会产生覆盖的情况。
作用
没什么用,这种情况是需要极力避免出现的。
演示
1.分别在不同的工作空间下创建turtlesim功能包。
终端下进入ws00_helloworld的src目录,新建功能包:
为了方便查看演示结果,将默认生成的 turtlesim_node.cpp 中的打印内容修改为:ws00_helloworld turtlesim\n
终端下进入ws01_plumbing的src目录,新建功能包:
为了方便查看演示结果,将默认生成的 turtlesim_node.cpp 中的打印内容修改为:ws01_plumbing turtlesim\n
2.在 ~/.bashrc 文件下追加如下内容:
修改完毕后,保存并关闭文件。
3.新建终端,输入如下指令:
输出结果为:ws01_plumbing turtlesim,也即执行的是 ws01_plumbing 功能包下的 turtlesim,而 ws00_helloworld 下的 turtlesim 与内置的 turtlesim 被覆盖了。
原因
这与~/.bashrc中不同工作空间的setup.bash文件的加载顺序有关:
1.ROS2 会解析 ~/.bashrc 文件,并生成全局环境变量 AMENT_PREFIX_PATH 与 PYTHONPATH,两个环境变量取值分别对应了 ROS2 中 C++ 和 Python 功能包,环境变量的值由功能包名称组成;
2.两个变量的值的设置与 ~/.bashrc 中的 setup.bash 的配置顺序有关,对于自定义的工作空间而言,后配置的优先级更高,主要表现在后配置的工作空间的功能包在环境变量值组成的前部,而前配置工作空间的功能包在环境变量值组成的后部分,如果更改两个自定义工作空间在 ~/.bashrc 中的配置顺序,那么变量值也将相应更改,但是 ROS2 系统工作空间的配置始终处于最后。
3.调用功能包时,会按照 AMENT_PREFIX_PATH 或 PYTHONPATH 中包配置顺序从前往后依次查找相关功能包,查找到功能包时会停止搜索,也即配置在前的会优先执行。
隐患
前面提到,工作空间覆盖的情况是需要极力避免出现的,因为导致一些安全隐患:
-
可能会出现功能包调用混乱,出现实际调用与预期调用结果不符的情况;
-
即便可以通过 ~/.bashrc 来配置不同工作空间的优先级,但是经过测试,修改 ~/.bashrc 文件之后不一定马上生效,还需要删除工作空间下build与install目录重新编译,才能生效,这个过程繁琐且有不确定性。
综上,在实际工作中,需要制定明确的包命名规范,避免包重名情况。
3.3 元功能包
场景
显而易见的,逐一安装功能包的效率低下,在ROS2中,提供了一种方式可以将不同的功能包打包成一个功能包,当安装某个功能模块时,直接调用打包后的功能包即可,该包又称之为元功能包(metapackage)。
概念
MetaPackage是Linux的一个文件管理系统的概念。是 ROS2 中的一个虚包,里面没有实质性的内容,但是它依赖了其他的软件包,通过这种方法可以把其他包组合起来,我们可以认为它是一本书的目录索引,告诉我们这个包集合中有哪些子包,并且该去哪里下载。
例如:
作用
方便用户的安装,我们只需要这一个包就可以把其他相关的软件包组织到一起安装了。
实现
1.新建一个功能包
2.修改 package.xml 文件,添加执行时所依赖的包:
3.文件CMakeLists.txt内容如下:
3.4 节点重名
问题描述
在 ROS2 中不同的节点可以有相同的节点名称,比如可以启动多个 turtlesim_node 节点,这些节点的名称都是 turtlesim。节点重名虽然是被允许的,但是开发者应该主动避免这种情况,因为节点重名时可能会导致操作上的混淆,仍以启动了多个 turtlesim_node 节点为例,当使用计算图(rqt_graph)查看节点运行状态时,由于他们的节点名称一致,那么虽然实际有多个节点,但是在计算图上显示一个。并且节点名称也会和话题名称、服务名称、动作名称、参数等产生关联,届时也可能会导致通信逻辑上的混乱。
那么在 ROS2 中如何避免节点重名呢?
解决思路
避免重名问题,一般有两种策略:
-
名称重映射,也即为节点起别名;
-
命名空间,是为节点名称添加前缀,可以有多级,格式:/xxx/yyy/zzz。
这也是在 ROS2 中解决重名问题的常用策略。
解决方案
上述两种策略的实现途径主要有如下三种:
-
ros2 run 命令实现;
-
launch 文件实现;
-
编码实现。
本节将逐一演示上述三种方案的实现语法。
3.4.1 ros2 run设置节点名称
1.ros2 run设置命名空间
1.1设置命名空间演示
语法:ros2 run 包名 节点名 --ros-args --remap __ns:=命名空间
示例:
1.2运行结果
使用ros2 node list查看节点信息,显示结果:
2.ros2 run名称重映射
2.1为节点起别名
语法: ros2 run 包名 节点名 --ros-args --remap __name:=新名称
或
ros2 run 包名 节点名 --ros-args --remap __node:=新名称
示例:
2.2运行结果
使用ros2 node list查看节点信息,显示结果:
3.ros2 run命名空间与名称重映射叠加
3.1设置命名空间同时名称重映射
语法: ros2 run 包名 节点名 --ros-args --remap __ns:=新名称 --remap __name:=新名称
3.2运行结果
使用ros2 node list查看节点信息,显示结果:
3.4.2 launch设置节点名称
在ROS2中launch文件可以由Python、XML或YAML三种语言编写(关于launch文件的基本使用可以参考4.1 启动文件launch简介),每种实现方式都可以设置节点的命名空间或为节点起别名。
1.Python方式实现的launch文件设置命名空间与名称重映射
在 Python 方式实现的 launch 文件中,可以通过类 launch_ros.actions.Node来创建被启动的节点对象,在对象的构造函数中提供了 name 和 namespace 参数来设置节点的名称与命名空间,使用示例如下:
2.XML方式实现的launch文件设置命名空间与名称重映射
在 XML 方式实现的 launch 文件中,可以通过 node 标签中 name 和 namespace 属性来设置节点的名称与命名空间,使用示例如下:
3.YAML方式实现的launch文件设置命名空间与名称重映射
在 YAML 方式实现的 launch 文件中,可以通过 node 属性中 name 和 namespace 属性来设置节点的名称与命名空间,使用示例如下:
4.测试
上述三种方式在设置命名空间与名称重映射时虽然语法不同,但是实现功能类似,都是启动了三个 turtlesim_node 节点,第一个节点设置了节点名称,第二个节点设置了命名空间,第三个节点既设置了命名空间又设置了节点名称,分别执行三个launch文件,然后使用ros2 node list查看节点信息,显示结果都如下所示:
3.4.3 编码设置节点名称
在 rclcpp 和 rclpy 中,节点类的构造函数中,都分别提供了设置节点名称与命名空间的参数。
1.rclcpp中的相关API
rclcpp中节点类的构造函数如下:
构造函数1中可以直接通过node_name设置节点名称,构造函数2既可以通过node_name设置节点名称也可以通过namespace_设置命名空间。
2.rclpy中的相关API
rclpy中节点类的构造函数如下:
构造函数中可以使用node_name设置节点名称,namespace设置命名空间。
3.5 话题重名
问题描述
节点名称可能出现重名的情况,同理话题名称也可能重名,不过与节点重名不同的是,有些场景下需要避免话题重名的情况,但有些场景下又需要将不同的不同的话题名称修改为相同。
那么如何修改话题名称呢?
解决思路
与节点重名的解决思路类似的,为了避免话题重名问题,一般有两种策略:
-
名称重映射,也即为话题名称起别名;
-
命名空间,是为话题名称添加前缀,可以有多级,格式:/xxx/yyy/zzz。
需要注意的是,通过命名空间设置话题名称时,需要保证话题是非全局话题。
解决方案
与节点重名解决方案类似的,修改话题名称的方式主要有如下三种:
-
ros2 run 命令实现;
-
launch 文件实现;
-
编码实现。
本节将逐一演示上述三种方案的实现语法。
3.5.1 ros2 run 修改话题名称
1.ros2 run设置命名空间
该实现与3.4.1 ros2 run设置节点名称中演示的语法使用一致。
1.1设置命名空间演示
语法:ros2 run 包名 节点名 --ros-args --remap __ns:=命名空间
示例:
1.2运行结果
使用ros2 topic list查看节点信息,显示结果:
节点下的话题已经添加了命名空间前缀。
2.ros2 run话题名称重映射
2.1为话题起别名
语法: ros2 run 包名 节点名 --ros-args --remap 原话题名称:=新话题名称
示例:
2.2运行结果
使用ros2 topic list查看节点信息,显示结果:
节点下的话题/turtle1/cmd_vel已经被修改为了/cmd_vel。
注意:
当为节点添加命名空间时,节点下的所有非全局话题都会前缀命名空间,而重映射的方式只是修改指定话题。
3.5.2 launch 文件修改话题名称
1.Python方式实现的launch文件修改话题名称
在 Python 方式实现的 launch 文件中,可以通过类 launch_ros.actions.Node的构造函数中的参数 remappings 修改话题名称,使用示例如下:
2.XML方式实现的launch文件修改话题名称
在 XML 方式实现的 launch 文件中,可以通过 node 标签的子标签 remap(属性from取值为被修改的话题名称,属性to的取值为修改后的话题名称) 修改话题名称,使用示例如下:
3.YAML方式实现的launch文件修改话题名称
在 YAML 方式实现的 launch 文件中,可以通过 node 属性中 remap(属性from取值为被修改的话题名称,属性to的取值为修改后的话题名称) 修改话题名称,使用示例如下:
4.测试
上述三种方式在修改话题名称时虽然语法不同,但是实现功能类似,都是启动了两个turtlesim_node节点,一个节点添加了命名空间,另一个节点将话题从/turtle1/cmd_vel映射到了/cmd_vel。使用ros2 topic list查看节点信息,显示结果:
添加命名空间的节点对应的话题为:
重映射的节点对应的话题为:
3.5.3 编码设置话题名称
话题分类
话题的名称的设置是与节点的命名空间、节点的名称有一定关系的,话题名称大致可以分为三种类型:
-
全局话题(话题参考ROS系统,与节点命名空间平级);
-
相对话题(话题参考的是节点的命名空间,与节点名称平级);
-
私有话题(话题参考节点名称,是节点名称的子级)。
总之,以编码方式设置话题名称是比较灵活的。本节将介绍如何在 rclcpp 和 rclpy 中分别设置不同类型的话题。
准备
请先分别创建 C++ 与 Python 相关的功能包以及节点,且假定在创建节点时,使用的命名空间为 xxx,节点名称为 yyy。
话题设置
1.1全局话题
格式:定义时以/开头的名称,和命名空间、节点名称无关。
rclcpp示例:publisher_ = this->create_publisher<std_msgs::msg::String>("/topic/chatter", 10);
rclpy示例:self.publisher_ = self.create_publisher(String, '/topic/chatter', 10)
话题:话题名称为 /topic/chatter,与命名空间 xxx 以及节点名称 yyy 无关。
1.2相对话题
格式:非/开头的名称,参考命名空间设置话题名称,和节点名称无关。
rclcpp示例:publisher_ = this->create_publisher<std_msgs::msg::String>("topic/chatter", 10);
rclpy示例:self.publisher_ = self.create_publisher(String, 'topic/chatter', 10)
话题:话题名称为 /xxx/topic/chatter,与命名空间 xxx 有关,与节点名称 yyy 无关。
1.3私有话题
格式:定义时以~/开头的名称,和命名空间、节点名称都有关系。
rclcpp示例:publisher_ = this->create_publisher<std_msgs::msg::String>("~/topic/chatter", 10);
rclpy示例:self.publisher_ = self.create_publisher(String, '~/topic/chatter', 10)
话题:话题名称为 /xxx/yyy/topic/chatter,使用命名空间 xxx 以及节点名称 yyy 作为话题名称前缀。
综上,话题名称设置规则在rclcpp与rclpy中基本一致,且上述规则也同样适用于ros2 run指令与launch文件。
3.6 时间相关API
在前面案例中我们已经使用了 ROS2 中的诸多 API,本节主要介绍另一类比较常见的API:时间相关API。
3.6.1 Rate
第2章话题通信案例中,要求话题发布方按照一定的频率发布消息,我们实现时是通过定时器来控制发布频率的,其实,除了定时器之外,ROS2 中还提供了 Rate 类,通过该类对象也可以控制程序的运行频率。
1.rclcpp 中的 Rate
示例:周期性输出一段文本。
2.rclpy 中的 Rate
rclpy 中的 Rate 对象可以通过节点创建,Rate 对象的 sleep() 函数需要在子线程中执行,否咋会阻塞程序。
示例:周期性输出一段文本。
3.6.2 Time
1.rclcpp 中的 Time
示例:创建 Time 对象,并调用其函数。
2.rclpy 中的 Time
示例:创建 Time 对象,并调用其函数。
3.6.3 Duration
1.rclcpp 中的 Duration
示例:创建 Duration 对象,并调用其函数。
2.rclpy 中的 Duration
示例:创建 Duration 对象,并调用其函数。
3.6.4 Time 与 Duration 运算
1.rclcpp 中的运算
示例:Time 以及 Duration 的相关运算。
2.rclpy 中的运算
示例:Time 以及 Duration 的相关运算。
3.7 通信机制工具
场景
上一章内容,我们学习了ROS2中的多种通信机制,了解了不同通信模型的实现流程、相关API以及各自的特点,接下来我们再介绍一些实际开发当中可能会遇到的一些问题:
在ROS2中提供了一些工具,可以方便快捷的解决上述问题,本部分内容就主要介绍这些工具的使用。
概念
在ROS2中,通信机制相关的工具有两种类型,分别是命令行工具和图形化工具(rqt),前者是一系列终端命令的集合,后者则是ROS2基于QT框架,针对机器人开发的一系列可视化工具的集合。
作用
可以方便的实现程序调试,提高开发效率,优化用户体验。
3.7.1 命令工具
ROS2中常用的命令如下:
-
ros2 node:节点相关命令行工具
-
ros2 interface:接口(msg、srv、action)消息相关的命令行工具
-
ros2 topic:话题通信相关的命令行工具
-
ros2 service:服务通信相关的命令行工具
-
ros2 action:动作通信相关的命令行工具
-
ros2 param:参数服务相关的命令行工具
关于命令的使用一般都会提供帮助文档,帮助文档的获取方式如下:
-
可以通过命令 -h 或 命令 --help的方式查看命令帮助文档,比如:ros2 node -h或 ros2 node --help。
-
命令下参数的使用也可以通过命令 参数 -h 或 命令 参数 --help的方式查看帮助文档,比如:ros2 node list -h或 ros2 node list --help。
1.ros2 node
ros2 node的基本使用语法如下:
2.ros2 interace
ros2 interace的基本使用语法如下:
3.ros2 topic
ros2 topic的基本使用语法如下:
4.ros2 service
ros2 service的基本使用语法如下:
5.ros2 action
ros2 action的基本使用语法如下:
6.ros2 param
ros2 param的基本使用语法如下:
3.7.2 rqt工具箱
本节主要介绍ROS2中rqt工具箱的使用,比如:rqt的安装、启动与插件使用等。
1.安装
-
一般只要安装的是desktop版本就会默认安装rqt工具箱;
-
如果需要安装可以以如下方式安装
2.启动
常用的rqt启动命令有:
-
方式1:rqt
-
方式2:ros2 run rqt_gui rqt_gui
3.插件使用
启动rqt之后,可以通过plugins添加所需的插件:
在plugins中包含了话题、服务、动作、参数、日志等等相关的插件,我们可以按需选用,方便的实现ROS2程序调试。使用示例如下。
1.topic 插件
添加topic插件并发送速度指令控制乌龟运动。
2.service插件
添加 service 插件并发送请求,在制定位置生成一只乌龟。
3.参数插件
通过参数插件动态修改乌龟窗体背景颜色。
3.8 通信机制实操(期中大作业)
本节主要介绍通信机制相关的一些练习,这些练习基于turtlesim功能包,练习类型覆盖了话题、服务、动作、参数这四种通信机制。
准备
终端下进入工作空间的src目录,调用如下命令创建C++功能包。
功能包下新建launch目录以备用。
3.8.1 话题通信案例分析
1.案例需求
需求:启动两个turtlesim_node节点,节点2中的乌龟自动调头180°,我们可以通过键盘控制节点1中的乌龟运动,但是不能控制节点2的乌龟,需要自实现功能:可以根据乌龟1的速度生成并发布控制乌龟2运动的速度指令,最终两只乌龟做镜像运动。
2.案例分析
在上述案例中,主要需要关注的问题有三个:
-
如何创建两个turtlesim_node节点,且需要具有不同的节点名称、话题名称。
-
如何控制乌龟掉头?
-
核心实现是如何订阅乌龟1的速度并生成发布控制乌龟2运动的速度指令的?
思路:
-
问题1我们可以通过为turtlesim_node设置namespace解决;
-
问题2可以通过调用turtlesim_node内置的action功能来实现乌龟航向的设置;
-
问题3是整个案例的核心,需要编码实现,需要订阅乌龟1的位姿相关话题来获取乌龟1的速度,并且按照“镜像运动”的需求生成乌龟2的速度指令,并且该节点需要在掉头完毕后启动。
-
最后,整个案例涉及到多个节点,我们可以通过launch文件集成这些节点。
3.流程简介
主要步骤如下:
-
编写速度订阅与发布实现;
-
编写launch文件集成多个节点;
-
编辑配置文件;
-
编译;
-
执行。
3.8.2 话题通信实现
1.速度订阅与发布
功能包cpp07_exercise的src目录下,新建C++文件exe01_pub_sub.cpp,并编辑文件,输入如下内容:
2.launch文件
功能包cpp07_exercise的launch目录下,新建launch文件exe01_pub_sub.launch.py,并编辑文件,输入如下内容:
3.编辑配置文件
1.package.xml
在创建功能包时,所依赖的功能包已经自动配置了,配置内容如下:
2.CMakeLists.txt
CMakeLists.txt 中发布和订阅程序核心配置如下:
4.编译
终端中进入当前工作空间,编译功能包:
5.执行
当前工作空间下,启动终端输入如下指令:
指令执行后,将生成两个turtlesim_node节点对应的窗口,并且其中一个窗口的乌龟开始调头。
再启动一个终端,输入如下指令:
待乌龟调头完毕,就可以通过键盘控制乌龟运动了,最终运行结果与演示案例类似。
3.8.3 服务通信案例分析
1.案例需求
需求:在turtlesim_node节点的窗体中在指定位置生成一只新乌龟并可以输出两只乌龟之间的直线距离。
2.案例分析
在上述案例中,需要关注的问题有两个:
-
如何在指定位置生成一只新乌龟?
-
计算两只乌龟的距离应该使用何种通信模式又如何实现?
思路:
-
问题1可以通过调用turtlesim_node内置的名称为/spawn的服务功能来实现新乌龟的创建;
-
问题2可以通过服务通信来实现,客户端发送新生成的乌龟的位姿到服务端,服务端根据该坐标以及原生乌龟的坐标计算距离并响应。当然如果使用服务通信,还需要自定义服务接口。
-
最后,整个案例涉及到多个节点,我们可以通过launch文件集成这些节点。
3.流程简介
主要步骤如下:
-
编写服务接口文件;
-
编写服务端实现;
-
编写客户端实现;
-
编写launch文件;
-
编辑配置文件;
-
编译;
-
执行。
3.8.4 服务通信实现
1.服务接口文件
功能包base_interfaces_demo的srv目录下,新建srv文件Distance.srv,并编辑文件,输入如下内容:
2.服务端实现
功能包cpp07_exercise的src目录下,新建C++文件exe02_server.cpp,并编辑文件,输入如下内容:
3.动作客户端实现
功能包cpp07_exercise的src目录下,新建C++文件exe05_action_client.cpp,并编辑文件,输入如下内容:
4.launch文件
该案例需要分别为动作服务端和动作客户端创建launch文件。
功能包cpp07_exercise的launch目录下,首先新建动作服务端launch文件exe04_action_server.launch.py,编辑文件,输入如下内容:
然后新建动作客户端launch文件exe05_action_client.launch.py,编辑文件,输入如下内容:
5.编辑配置文件
此处需要编辑base_interfaces_demo和cpp07_exercise两个功能包下的配置文件。
1.base_interfaces_demo下的CMakeLists.txt
和前面服务通信一样,只需要修改CMakeLists.txt中的rosidl_generate_interfaces 函数即可,修改后的内容如下:
2.cpp07_exercise下的CMakeLists.txt
CMakeLists.txt 文件需要添加如下内容:
文件中 install 修改为如下内容:
6.编译
终端中进入当前工作空间,编译功能包:
7.执行
当前工作空间下,启动两个终端。
终端1输入如下指令:
指令执行后,将生成turtlesim_node节点对应的窗口,并且会启动乌龟导航的动作服务端。
终端2输入如下指令:
指令执行后,会生成一只新的乌龟,并且原生乌龟会以新乌龟为目标点向其运动,运动过程中,动作客户端会接收服务端连续反馈的剩余距离消息,最终运行结果与演示案例类似。
3.8.7 参数服务案例分析
1.案例需求
需求:动态修改乌龟窗口的背景颜色。
2.案例分析
在上述案例中,只需要修改背景色相关参数即可。
3.流程简介
主要步骤如下:
-
编写参数客户端实现;
-
编写launch文件;
-
编辑配置文件;
-
编译;
-
执行。
3.8.8 参数服务实现
1.参数客户端实现
功能包cpp07_exercise的src目录下,新建C++文件exe06_param.cpp,并编辑文件,输入如下内容:
2.launch文件
功能包cpp07_exercise的launch目录下,新建launch文件exe06_param.launch.py,并编辑文件,输入如下内容:
3.编辑配置文件
package.xml 无需修改,CMakeLists.txt 文件需要添加如下内容:
文件中 install 修改为如下内容:
4.编译
终端中进入当前工作空间,编译功能包:
5.执行
当前工作空间下,启动终端输入如下指令:
指令执行后,将生成turtlesim_node节点对应的窗口,窗口背景色会动态改变,最终运行结果与演示案例类似。
3.9 本章小结
本章主要介绍了通信机制相关的一些补充内容,相关知识点如下:
-
分布式环境搭建;
-
各种重名问题(包重名、节点重名、话题重名);
-
元功能包;
-
时间相关API;
-
通信机制常用工具;
ROS2中的分布式环境搭建极其便捷,只需要保证不同的ROS2设备在同一网络下,默认不同设备之间即可正常通信;关于重名问题,不同工作空间下功能包重名应该是尽力避免重名的,节点重名与话题重名的问题则可以通过两种策略、三种途径解决;元功能包是一个特殊的功能包,相当于是一个包目录索引,可以将具有内在关联的功能包关联到一起;时间相关的API则介绍了在ROS2中一些常用的定时器、频率控制和持续时间相关的一些API,这些API都是经常使用的;通信机制工具主要介绍了通信相关的命令行工具以及图形化的rqt工具箱,通过这些工具可以提高开发者的开发、调试效率。