AutoLabor-ROS-Python 学习记录

AutoLabor-ROS-Python 学习记录
Penry引言
该文档主要基于赵虚左老师的课程【Autolabor初级教程】ROS机器人入门
,撰写一些 ROS Python 相关的重要知识点或者配置内容。
- 课程视频链接:【Autolabor初级教程】ROS机器人入门
- 课程电子书:ROS机器人入门课程《ROS理论与实践》零基础教程
- ROS noetic 配置文档:ROS配置
第一章 ROS概述与环境搭建
ROS 文件系统架构:
1 | WorkSpace --- 自定义的工作空间 |
1.0 ROS 演示
ROS 内置了一些小程序,可以通过运行这些小程序以检测 ROS 环境是否可以正常运行
- 首先启动三个命令行(ctrl + alt + T)
- 命令行1键入:
roscore
- 命令行2键入:
rosrun turtlesim turtlesim_node
(此时会弹出图形化界面) - 命令行3键入:
rosrun turtlesim turtle_teleop_key
(在3中可以通过上下左右控制2中乌龟的运动)
- 命令行1键入:
最终结果如下所示:
注意:光标必须聚焦在键盘控制窗口,否则无法控制乌龟运动。
1.1 集成开发环境_VsCode基本实现流程(为Python)
Python 文件:
1 | #! /usr/bin/env python |
由于一种编写 ROS-Python 习惯的沿袭,某些开源项目编写的 .py
文件没有经过 CMakeLists.txt
配置,导致编译时候出现如下报错:
1 | "python": 没有那个文件或目录 |
原因: 当前 noetic 版本中,python3 为默认 python 版本,而 python3 与 python2 不兼容
有三种解决方案:
- 直接声明解释器为 python3 (不建议)
/usr/bin/env python3
- 通过软连接将 python 连接到 python3 (建议)
- 备份现有链接:
sudo mv /usr/bin/python /usr/bin/python.bak
- 创建新链接:
sudo ln -s /usr/bin/python3 /usr/bin/python
- 验证链接:
ls -l /usr/bin/python
- 备份现有链接:
- 配置 CMakeLists.txt (推荐)
配置好软连接之后,即使不配置 CMakeLists.txt
,也可以按照以下步骤成功运行 .py
文件:
Ctrl+Shift+B
编译- 将
功能包/scripts
在终端打开 - 终端输入
chmod +x *.py
赋予所有.py
文件执行权限 - 终端输入
ll
查看可执行文件 - 新建两个终端:
- 第一个终端输入
roscore
启动 ROS 主进程 - 第二个终端输入先输入
source ./devel/setup.bash
配置 ROS 环境,再输入rosrun 包名 可执行文件名
运行节点.
- 第一个终端输入
第二章 ROS通信机制
机器人是一种高度复杂的系统性实现,在机器人上可能集成各种传感器(雷达、摄像头、GPS…)以及运动控制实现,为了解耦合,在ROS中每一个功能点都是一个单独的进程,每一个进程都是独立运行的。更确切的讲,ROS是进程(也称为Nodes)的分布式框架。 因为这些进程甚至还可分布于不同主机,不同主机协同工作,从而分散计算压力。不过随之也有一个问题: 不同的进程是如何通信的?也即不同进程间如何实现数据交换的?在此我们就需要介绍一下ROS中的通信机制了。
ROS 中的基本通信机制主要有如下三种实现策略:
- 话题通信(发布订阅模式)
- 服务通信(请求响应模式)
- 参数服务器(参数共享模式)
本章的主要内容就是是介绍各个通信机制的应用场景、理论模型、代码实现以及相关操作命令。本章预期达成学习目标如下:
- 能够熟练介绍ROS中常用的通信机制
- 能够理解ROS中每种通信机制的理论模型
- 能够以代码的方式实现各种通信机制对应的案例
- 能够熟练使用ROS中的一些操作命令
- 能够独立完成相关实操案例
案例演示
- 话题通信演示:
控制小乌龟做圆周运动
获取乌龟位姿
- 服务通信演示:
在指定位置生成乌龟
- 参数服务器演示:
改变乌龟窗口的背景颜色
2.1 话题通信
话题通信基于发布/订阅模型,是一种异步通信模式。一个节点发布消息,另一个节点订阅该消息。这种方式将信息的产生和使用双方解耦,常用于不断更新的、含有较少逻辑处理的数据通信。例如,雷达数据、里程计数据等。
话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布/订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。话题通信的应用场景也极其广泛,比如下面一个常见场景:
机器人在执行导航功能,使用的传感器是激光雷达,机器人会采集激光雷达感知到的信息并计算,然后生成运动控制信息驱动机器人底盘运动。
在上述场景中,就不止一次使用到了话题通信。
- 以激光雷达信息的采集处理为例,在 ROS 中有一个节点需要时时的发布当前雷达采集到的数据,导航模块中也有节点会订阅并解析雷达数据。
- 再以运动消息的发布为例,导航模块会根据传感器采集的数据时时的计算出运动控制信息并发布给底盘,底盘也可以有一个节点订阅运动信息并最终转换成控制电机的脉冲信号。
以此类推,像雷达、摄像头、GPS… 等等一些传感器数据的采集,也都是使用了话题通信,换言之,话题通信适用于不断更新的数据传输相关的应用场景。
概念:以发布订阅的方式实现不同节点之间数据交互的通信模式。
作用:用于不断更新的、少逻辑处理的数据传输场景。
2.1.1 理论模型
话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:
- ROS Master
- Talker
- Listener
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。
- Talker 注册:
- Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。
- Listener 注册:
- Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。
- ROS Master 实现信息匹配:
- ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。
- Listener向Talker发送请求:
- Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。
- Talker确认请求:
- Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。
- Listener与Talker件里连接:
- Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。
- Talker向Listener发送消息:
- 连接建立后,Talker 开始向 Listener 发布消息。
注意1: 上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议
注意2: Talker 与 Listener 的启动无先后顺序要求
注意3: Talker 与 Listener 都可以有多个
注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker 与 Listern 照常通信。
2.1.2 话题通信基本操作
需求:
编写发布订阅实现,要求发布方以10HZ(每秒10次)的频率发布文本消息,订阅方订阅消息并将消息内容打印输出。
分析:
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 发布方
- 订阅方
- 数据
流程:
- 编写发布方实现;
- 编写订阅方实现;
- Python 文件添加可执行权限;
- 编写配置文件;
- 编译并后自行。
2.1.2.1 发布方
1 | #! /usr/bin/env python |
验证:
2.1.2.2 订阅方
1 | #! /usr/bin/env python |
验证:
注意1:添加可执行权限,终端下进入 scripts 执行:
chmod +x *.py
;
注意2:配置CMakeLists.txt
文件
1 | catkin_install_python(PROGRAMS |
2.1.2.3 订阅费缺少消息的问题
如图所示,我将发布方发送频率设置为 ,明显发现接收方缺少了第一条消息:
原因解释:因为发布方首先要在 ROS Master 中注册,可能在注册过程中就已经发布了第一条消息,导致这个时候订阅方无法接收。
解决方案:确保发布方注册完毕后,再开始发送数据,在发布之前加一个休眠。
1 | #! /usr/bin/env python |
2.1.2.4 计算图查看Python发布订阅模型
Ctrl+Alt+T
快速创建四个终端:
- 第一个终端:
roscore
- 第二个终端:
rosrun 包名 demo01_sub.py
- 第三个终端:
rosrun 包名 demo01_pub.py
- 第四个终端:
rqt_graph
2.1.2.5 关键概念:解耦合
解耦是指系统中的组件或模块之间的依赖关系最小化,从而提高系统的灵活性、可维护性和可扩展性。在软件系统中,解耦通常通过引入中间件或消息队列等机制来实现。
解耦的实现方式:
- 发布/订阅模式:发布者将消息发布到主题(Topic),订阅者从主题订阅消息。发布者和订阅者之间没有直接依赖关系,它们通过消息队列进行通信。
- 服务/客户端模式:服务端提供服务,客户端请求服务。服务端和客户端之间也没有直接依赖关系,它们通过消息队列进行通信。
解耦合意味着即使我们的功能包中的两个源文件分别用 cpp
和 python
编写,也可以实现通信,这是因为它们通过消息队列进行解耦。
2.1.3 话题通信自定义msg
在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs
封装了一些原生的数据类型,比如:String
、Int32
、Int64
、Char
、Bool
、Empty
… 但是,这些数据一般只包含一个 data
字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如: 激光雷达的信息… std_msgs
由于描述性较差而显得力不从心,这种场景下可以使用自定义的消息类型。
msgs只是简单的文本文件,每行具有字段类型和字段名称,可以使用的字段类型有:
- int8, int16, int32, int64 (或者无符号类型: uint8, uint16, uint32, uint64)
- float32, float64
- string
- time
- duration
- other msg files
- variable-length array[] and fixed-length array[C]
ROS中还有一种特殊类型:Header
,标头包含时间戳和ROS中常用的坐标帧信息。会经常看到msg文件的第一行具有Header
标头。
需求:创建自定义消息,该消息包含人的信息:姓名、身高、年龄等。
流程:
- 按照固定格式创建 msg 文件
- 编辑配置文件
- 编译生成可以被 Python 调用的中间文件
2.1.3.1 定义 msg 文件
功能包下新建 msg
目录,添加文件 Person.msg
:
1 | string name |
2.1.3.2 编辑配置文件
- 在
package.xml
中添加编译依赖与执行依赖:
1 | <build_depend>message_generation</build_depend> |
- 在
CMakeLists.txt
编辑msg
相关配置:
1 | find_package(catkin REQUIRED COMPONENTS |
1 | ## 配置 msg 源文件 |
1 | # 生成消息时依赖于 std_msgs |
1 | #执行时依赖 |
2.1.3.3 编译并查看中间文件
Python 中间文件为:工作空间/devel/lib/python3/dist-packages/包名/msg/_Person.py
2.1.4 话题通信自定义msg调用
需求:
编写发布订阅实现,要求发布方以1HZ(每秒1次)的频率发布自定义消息,订阅方订阅自定义消息并将消息内容打印输出。
分析:
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 发布方
- 订阅方
- 自定义数据
流程:
- 编写发布方实现
- 编写订阅方实现
- 为 Python 文件赋予可执行权限
- 编辑配置文件
- 编译并执行
2.1.4.1 VsCode配置
为了方便代码提示以及误抛异常,需要先配置 VsCode,将前面生成的 python 文件路径配置进 settings.json
:
1 | { |
我的填写示例:
1 | { |
2.1.4.2 发布方
1 | #! /usr/bin/env python |
新建三个终端:
- 第一个终端:
roscore
- 第二个终端:
source ~/工作空间/devel/setup.bash
rosrun 包名 可执行文件
- 第三个终端:
source ~/工作空间/devel/setup.bash
rostopic echo 话题名
2.1.4.3 订阅方
1 | #! /usr/bin/env python |
新建四个终端:
- 第一个终端:
roscore
- 第二个终端发布方:
source ~/工作空间/devel/setup.bash
rosrun 包名 可执行文件
- 第三个终端订阅方:
source ~/工作空间/devel/setup.bash
rostopic echo 话题名
- 第四个终端:
rosnode list
计算图查看:rqt_graph
2.2 服务通信
服务通信基于请求/响应模型,是一种同步通信模式。一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。这种方式常用于数据量较小但有强逻辑处理的数据交换,例如拍照、语音识别等,如以下场景:
机器人巡逻过程中,控制系统分析传感器数据发现可疑物体或人… 此时需要拍摄照片并留存。
在上述场景中,就使用到了服务通信:一个节点需要向相机节点发送拍照请求,相机节点处理请求,并返回处理结果。
与上述应用类似的,服务通信更适用于对时时性有要求、具有一定逻辑处理的应用场景。
概念:以请求响应的方式实现不同节点之间数据交互的通信模式。
作用:用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。
案例:实现两个数字的求和,客户端节点,运行会向服务器发送两个数字,服务器端节点接收两个数字求和并将结果响应回客户端。
2.2.1 服务通信理论模型
服务通信较之于话题通信更简单些,理论模型如下图所示,该模型中涉及到三个角色:
- ROS master(管理者)
- Server(服务端)
- Client(客户端)
ROS Master 负责保管 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client ,帮助 Server 与 Client 建立连接,连接建立后,Client 发送请求信息,Server 返回响应信息。
2.2.1.1 Server注册
Server 启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含提供的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。
2.2.1.2 Client注册
Client 启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要请求的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。
2.2.1.3 ROS Master实现信息匹配
ROS Master 会根据注册表中的信息匹配Server和 Client,并通过 RPC 向 Client 发送 Server 的 TCP 地址信息。
2.2.1.4 Client发送请求
Client 根据步骤3 响应的信息,使用 TCP 与 Server 建立网络连接,并发送请求数据。
2.2.1.5 Server发送响应
Server 收到请求后,处理请求并将结果返回给 Client。
注意:
- 客户端请求被处理时,需要保证服务器已经启动,保证顺序;
- 服务端和客户端都可以存在多个。
2.2.2 服务通信自定义srv
需求:
服务通信中,客户端提交两个整数至服务端,服务端求和并响应结果到客户端,请创建服务器与客户端通信的数据载体。
流程:
srv 文件内的可用数据类型与 msg 文件一致,且定义 srv 实现流程与自定义 msg 实现流程类似:
- 按照固定格式创建 srv 文件
- 编辑配置文件
- 编译生成中间文件
2.2.2.1 定义 srv 文件
服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用---
分割,具体实现如下:
- 新建功能包:
atkin_create_pkg plumbing_server_client rospy roscpp std_msgs
- 在功能包下新建 srv 目录,添加
Addints.srv
文件,内容为:
1 | # 客户端请求时发送的两个数字 |
2.2.2.2 编辑配置文件
package.xml
中添加编译依赖于执行依赖:
1 | <build_depend>message_generation</build_depend> |
CMakeLists.txt
中编辑 srv 相关配置:
1 | find_package(catkin REQUIRED COMPONENTS |
1 | add_service_files( |
1 | generate_messages( |
1 | catkin_package( |
2.2.2.3 编译
- 使用快捷键
Ctrl+Alt+B
编译; - 在路径
/home/mpy/workspace_learn_ros/autolabor_python/devel/lib/python3/dist-packages/plumbing_server_client/srv/_Addints.py
下可以看到 Python 要调用的中间文件:
2.2.3 服务通信自定义 srv 调用
需求:
编写服务通信,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。
分析:
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 服务端
- 客户端
- 数据
流程:
- 编写服务端实现;
- 编写客户端实现;
- 为 Python 文件添加可执行权限;
- 编辑配置文件;
- 编译并执行。
2.2.3.1 VsCode 配置
需要像之前自定义 msg 实现一样配置settings.json 文件,如果以前已经配置且没有变更工作空间,可以忽略,如果需要配置,配置方式与之前相同:
1 | { |
2.2.3.2 服务端实现
1 | #! usr/bin/env python |
注意给 Python 文件赋权限,以及在 CMakeLists.txt
中添加配置。
启动三个终端:
- 第一个终端:
roscore
- 第二个终端:
rosrun plumbing_server_client server.py
- 第三个终端:
rosservice call Addints "{num1: 10, num2: 20}"
2.2.3.3 客户端实现
1 | #! usr/bin/env python |
注意给 Python 文件赋权限,以及在 CMakeLists.txt
中添加配置。
2.2.3.4 客户端优化
1 | #! usr/bin/env python |
注意给 Python 文件赋权限,以及在 CMakeLists.txt
中添加配置。
2.2.3.5 设置权限
1 | chmod +x *.py |
2.2.3.5 要求先启动客户端再启动服务端
问题:客户端优先于服务端启动,会抛出异常;
需要:客户端优先于服务端启动,不要抛出异常,而是挂起,等待服务端启动后再发送请求。
实现:ROS 中内置了相关函数,这些函数可以判断相关服务器状态,如果服务没启动,就让客户端挂起。
函数:rospy.wait_for_service("服务名")
或client.wait_for_service()
,这里的client
是创建的客户端对象。
作用:等待服务启动,参数为服务名,返回值为 None。
1 | #! usr/bin/env python |
如下图,先启动客户端,发现客户端已经挂起,没有报错:
再启动服务端,发现成功实现通信:
2.3 参数通信
参数服务器在 ROS 中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据,关于参数服务器的典型应用场景如下:
导航实现时,会进行路径规划,比如: 全局路径规划,设计一个从出发点到目标点的大致路径。本地路径规划,会根据当前路况生成时时的行进路径。
上述场景中,全局路径规划和本地路径规划时,就会使用到参数服务器:
- 路径规划时,需要参考小车的尺寸,我们可以将这些尺寸信息存储到参数服务器,全局路径规划节点与本地路径规划节点都可以从参数服务器中调用这些参数。
参数服务器,一般适用于存在数据共享的一些应用场景。
概念:以共享的方式实现不同节点之间数据交互的通信模式。
作用:存储一些多节点共享的数据,类似于全局变量。
案例:实现参数增删改查操作。
2.3.1 参数服务器理论模型
参数服务器实现是最为简单的,该模型如下图所示,该模型中涉及到三个角色:
- ROS Master(管理者)
- Talker(参数设置者)
- Listener(参数调用者)
ROS Master 作为一个公共容器保存参数,Talker 可以向容器中设置参数,Listener 可以获取参数。
2.3.1.1 Talker 设置参数
Talker 通过 RPC 向参数服务器发送参数(包括参数名与参数值),ROS Master 将参数保存到参数列表中。
2.3.1.2 Listener 获取参数
Listener 通过 RPC 向参数服务器发送参数查找请求,请求中包含要查找的参数名。
2.3.1.3 ROS Master 向 Listener 发送参数值
ROS Master 根据步骤2请求提供的参数名查找参数值,并将查询结果通过 RPC 发送给 Listener。
参数可使用数据类型:
- 32-bit integers
- booleans
- strings
- doubles
- iso8601 dates
- lists
- base64-encoded binary data
- 字典
注意:参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据
2.3.2 参数操作
需求:实现参数服务器参数的增删改查操作。
2.3.2.1 参数服务器新增(修改)参数
- 新增参数:
1 | #! /usr/bin/env python |
新建四个终端:
- 第一个终端:
roscore
- 第二个终端:
rosrun plumbing_param_server demo01_param_set_p.py
- 第三个终端:
rosparam list
,查看参数列表 - 第四个终端:
rosparam get 参数名
,获取参数值
- 修改参数:
1 | #! /usr/bin/env python |
很明显观察到参数 radius
被修改为 1.11
:
2.3.2.2 参数服务器获取参数
这里提供了多个实现方式:
get_params(键,默认值)
:获取指定键的参数值,如果键不存在,返回默认值。get_param_cached(键,默认值)
:获取指定键的参数值,如果键不存在,返回默认值。与get_param
不同,该方法会缓存参数值,因此在循环中调用时效率更高。get_param_names()
:获取所有参数的键列表。has_param(键)
:检查参数服务器中是否存在指定键。search_param(参数名)
:搜索参数名,返回第一个匹配的参数名。
2.3.2.2.1 get_param
1 | #! /usr/bin/env python |
新建四个终端:
- 第一个终端:
roscore
- 第二个终端:
rosrun plumbing_param_server demo01_param_set_p.py
- 第三个终端:
rosrun plumbing_param_server demo02_param_get_p.py
- 第四个终端:
rosparam list
,获取参数列表
2.3.2.2.2 get_params_cached
1 | #! /usr/bin/env python |
2.3.2.2.3 get_param_names
1 | #! /usr/bin/env python |
2.3.2.2.4 has_param
1 | #! /usr/bin/env python |
2.3.2.2.5 search_param
1 | #! /usr/bin/env python |
2.3.2.3 参数服务器删除参数
使用 rospy.delete_param("键")
删除参数:
- 键不存在时,会抛出异常
- 键存在时,删除成功
1 | #! /usr/bin/env python |
这里如果要删除的参数不存在,是会抛异常的,这里我们使用try...except
结构打印出来异常,如下图所示:
2.4 常用命令
机器人系统中启动的节点少则几个,多则十几个、几十个,不同的节点名称各异,通信时使用话题、服务、消息、参数等等都各不相同,一个显而易见的问题是: 当需要自定义节点和其他某个已经存在的节点通信时,如何获取对方的话题、以及消息载体的格式呢?
在 ROS 同提供了一些实用的命令行工具,可以用于获取不同节点的各类信息,常用的命令如下:
rosnode
: 操作节点rostopic
: 操作话题rosservice
: 操作服务rosmsg
: 操作消息rossrv
: 操作 srv 消息rosparam
: 操作参数
作用:和之前介绍的文件系统操作命令比较,文件操作命令是静态的,操作的是磁盘上的文件,而上述命令是动态的,在ROS程序启动后,可以动态的获取运行中的节点或参数的相关信息。
案例:本节将借助于 2.1、2.2 和 2.3 的通信实现介绍相关命令的基本使用,并通过练习ROS内置的小海龟例程来强化命令的应用。
参考文档:CommandLineTools
2.4.1 rosnode
rosnode
是用于获取节点信息的命令:
- rosnode ping 测试到节点的连接状态
- rosnode list 列出活动节点
- rosnode info 打印节点信息
- rosnode machine 列出指定设备上节点
- rosnode kill 杀死某个节点
- rosnode cleanup 清除不可连接的节点
rosnode list
rosnode ping /Publisher_Person
rosnode info /Subscriber_Person
rosnode machine mpy-vpc
rosnode kill /Publisher_Person
能看到右下角的进程被杀死了:
rosnode cleanup
能看到虽然 /turtlesim
已经被 Ctrl+C
关掉了,但是 rosnode list
显示该节点还活着,这时候需要用 rosnode cleanup
清理干净
rosnode cleanup
清理僵尸节点:
2.4.2 rostopic
打开话题通信模型。
rostopic
包含rostopic
命令行工具,用于显示有关 ROS 主题的调试信息,包括发布者,订阅者,发布频率和 ROS 消息。它还包含一个实验性Python库,用于动态获取有关主题的信息并与之交互。
- rostopic bw 显示主题使用的带宽
- rostopic delay 显示带有 header 的主题延迟
- rostopic echo 打印消息到屏幕
- rostopic find 根据类型查找主题
- rostopic hz 显示主题的发布频率
- rostopic info 显示主题相关信息
- rostopic list 显示所有活动状态下的主题
- rostopic pub 将数据发布到主题
- rostopic type 打印主题类型
2.4.2.1 rostpoic list
2.4.2.2 rostopic echo 话题名
打开四个终端,在执行 rostopic echo Person
的终端中,我们要依次执行如下步骤:
cd ~/工作空间
,即进入到该功能包所在的工作空间;source ./devel/setup.bash
,即加载该功能包下的所有环境变量;rostopic echo 话题名
,即可打印发布的话题消息。
2.4.2.3 rostopic pub
在执行 rostopic pub 话题名 消息类型 "消息内容"
之前,我们要依次执行如下步骤:
cd ~/工作空间
,即进入到该功能包所在的工作空间;source ./devel/setup.bash
,即加载该功能包下的所有环境变量;rostopic pub 话题名 消息类型 "消息内容"
,即可发布消息。- 其中输入完消息类型后点击
Tab
可以自动补齐并产生“消息内容”。
- 其中输入完消息类型后点击
也可以按照某种频率发送消息:
rostopic pub -r 10 话题名 消息类型 "消息内容"
,即按照10hz的频率发布消息。
2.4.2.4 rostopic info
查看当前话题具体信息:
- 类型;
- 发布者;
- 订阅者;
2.4.2.5 rostopic hz
显示话题发布频率:
2.4.3 rosservice
打开服务通信模型。
rosservice
包含用于列出和查询ROSServices
的rosservice
命令行工具。
调用部分服务时,如果对相关工作空间没有配置 path,需要进入工作空间调用 source ./devel/setup.bash
.
常用的命令:
- rosservice args 打印服务参数
- rosservice call 使用提供的参数调用服务
- rosservice find 按照服务类型查找服务
- rosservice info 打印有关服务的信息
- rosservice list 列出所有活动的服务
- rosservice type 打印服务类型
- rosservice uri 打印服务的 ROSRPC uri
2.4.3.1 rosservice list
查看所有服务:
2.4.3.2 rosservice call
调用服务,扮演客户端的角色:
- 语法:
rosservice call 服务名 服务参数
; - 示例:
rosservice call Addints 服务参数
。- 服务参数可以直接使用
Tab
键补齐
- 服务参数可以直接使用
可以看到我们并没有启动客户端,仅仅通过该命令行也实现了客户端的功能。
2.4.3.3 rosservice info
查看服务具体信息:
Node
:服务所在的节点;URI
:服务的 ROSRPC uri;Type
:服务的类型;Args
:服务的参数;
2.4.3.4 rosservice type
查看服务类型:
- 语法:
rosservice type 服务名
; - 示例:
rosservice type Addints
。
2.4.4 rosmsg
打开话题通信模型。
rosmsg是用于显示有关 ROS 消息类型的信息的命令行工具。
常用的命令:
- rosmsg show 显示消息描述
- rosmsg info 显示消息信息
- rosmsg list 列出所有消息
- rosmsg md5 显示 md5 加密后的消息
- rosmsg package 显示某个功能包下的所有消息
- rosmsg packages 列出包含消息的功能包
2.4.4.1 rosmsg list
rosmsg list
会列出 ROS 支持的所有消息列表:
rosmsg list | grep -i 消息名
可以根据消息名进行搜索:
2.4.4.2 rosmsg info
和 rosmsg show
二者均是展示消息的详细信息,展示内容一致:
2.4.5 rossrv
rossrv
是用于显示有关 ROS 服务类型的信息的命令行工具,与 rosmsg
使用语法高度雷同。
常用语法:
- rossrv show 显示服务消息详情
- rossrv info 显示服务消息相关信息
- rossrv list 列出所有服务信息
- rossrv md5 显示 md5 加密后的服务消息
- rossrv package 显示某个包下所有服务消息
- rossrv packages 显示包含服务消息的所有包
2.4.5.1 rossrv list
显示 ROS 中所有的服务消息:
rossrv list | grep -i 服务名
可以根据服务名进行搜索:
2.4.5.2 rossrv info
使用用法是:rossrv info 消息名称
,而对应的消息名称我们已经使用rossrv list | grep -i 服务名
抓取到了。
2.4.5.3 rossrv show
整体用法与 rossrv info
一致,显示内容也一致:
2.4.6 rosparam
rosparam
包含 rosparam
命令行工具,用于使用 YAML
编码文件在参数服务器上获取和设置 ROS 参数。
常用命令:
- rosparam list 列出所有参数
- rosparam get 获取参数值
- rosparam set 设置参数值
- rosparam delete 删除参数
- rosparam dump 导出所有参数为 YAML 文件
- rosparam load 从 YAML 文件导入参数
2.4.6.1 rosparam list
列举出所有参数:
rosparam list
可以列出所有的参数;rosparam list | grep -i 参数名
可以根据参数名进行搜索。
2.4.6.2 rosparam set
& rosparam get
用法:
rosparam set 参数名 参数值
配合 rosparam list
以及 rosparam get 参数名
观察效果:
2.4.6.3 rosparam delete
- 首先使用
rosparam set
多设置几个参数和对应的值; - 演示
rosparam delete 参数名
效果。
2.4.6.4 rosparam dump
& rosparam load
描述:
- 执行
rosparam dump params.yaml
,将所有参数导出为 YAML 文件; - 执行
rosparam load params.yaml
,将 YAML 文件中的参数导入参数服务器。
这里我们演示将参数序列化,即导出为 YAML 文件:
接着我们将 roscore
重启,会发现之前自定义的参数已经被释放了,此时我们选择将 params.yaml
中的参数导入参数服务器,进行反序列化:
2.5 通信机制实操
内容:本节主要是通过ROS内置的 turtlesim
案例,结合已经介绍ROS命令获取节点、话题、话题消息、服务、服务消息与参数的信息,最终再以编码的方式实现乌龟运动的控制、乌龟位姿的订阅、乌龟生成与乌龟窗体背景颜色的修改。
目的:熟悉、强化通信模式应用。
2.5.1 实操01_话题发布
需求描述:编码实现乌龟运动控制,让小乌龟做圆周运动。
结果演示:
实现分析:
- 乌龟运动控制实现,关键节点有两个,一个是乌龟运动显示节点
turtlesim_node
,另外一个是控制节点,二者是订阅发布模式实现通信的,乌龟运动显示节点直接调用即可,运动控制节点之前是使用的turtle_teleop_key
通过键盘控制,现在需要自定义控制节点。 - 控制节点自实现时,首先需要了解控制节点与显示节点通信使用的话题与消息,可以使用ros命令结合计算图来获取。
- 了解了话题与消息之后,通过 C++ 或 Python 编写运动控制节点,通过指定的话题,按照一定的逻辑发布消息即可。
实现流程:
- 通过计算图结合ros命令获取话题与消息信息。
- 编码实现运动控制节点。
- 启动 roscore、turtlesim_node 以及自定义的控制节点,查看运行结果。
2.5.1.1 话题与消息获取
准备: 先启动键盘控制乌龟运动案例。
2.5.1.1.1 话题获取
获取话题:/turtle/cmd_vel
使用命令rostopic list
:
结合命令 rqt_graph
:
我们得知控制话题是 /turtle1/cmd_vel
。
2.5.1.1.2 消息获取
我们可以使用 rostopic info /tuetle1/cmd_vel
获取消息类型:
我们接着可以使用 rosmsg info geometry_msgs/Twist
获取消息的详细信息:
消息解释:
- linear:线速度,包含x、y、z三个坐标,单位是m/s;
- angular:角速度,包含x、y、z三个坐标,单位是rad/s。
详细请见补充资料。
对于乌龟案例而言,只需要控制其线速度的 x
参数,角速度的 z
参数即可实现圆周运动,以下我们先进行一个速度消息验证。
我们在终端中通过 rostopic echo /turtle1/cmd_vel
可以查看发布的速度消息:
可以通过打印数据观察到在整个运动过程中,只有线速度的 x
参数和角速度的 z
参数改变。
2.5.1.2 节点实现
- 其实有一种极简的方法实现,就是使用以下命令:
1 | rostopic pub 10 -r 1 /turtle1/cmd_vel geometry_msgs/Twist "linear: |
这条命令,其中
10
表示发布的频率,-r
表示重复发布,1
表示重复发布的次数,/turtle1/cmd_vel
表示发布的话题,geometry_msgs/Twist
表示发布的消息类型,后面的字符串是发布的消息内容。
- 使用 Python 实现自定义节点:
- 我们首先新建功能包:
catkin_create_pkg plumbing_test rospy roscpp std_msgs geometry_msgs
; - 我们在
plumbing_test/scripts
目录下新test01_pub_twist_p.py
文件,编写发布方代码; - 赋予可执行权限:
chmod +x *.py
; - 编辑
CMakeLists.txt
配置文件; - 运行:
rosrun plumbing_test test01_pub_twist_p.py
。
- 我们首先新建功能包:
1 | #! /usr/bin/env python |
2.5.1.3 运行
创建四个终端:
- 第一个终端
roscore
; - 第二个终端
rosrun turtlesim turtlesim_node
; - 第三个终端
rosrun plumbing_test test01_pub_twist_p.py
; - 第四个终端
rostopic echo /turtle1/cmd_vel
。
2.5.1.4 补充资料
这里整体均参考为右手坐标系。
- 弧度:单位弧度定义为圆弧长度等于半径时的圆心角。
- 圆周长计算公式:
- 偏航、翻滚与俯仰:
- 坐标系图解:
- 偏航:绕 z 轴旋转,单位是弧度;
- 俯仰:绕 y 轴旋转,单位是弧度;
- 翻滚:绕 x 轴旋转,单位是弧度。
2.5.2 实操02_话题订阅
需求描述: 已知turtlesim
中的乌龟显示节点,会发布当前乌龟的位姿(窗体中乌龟的坐标以及朝向),要求控制乌龟运动,并时时打印当前乌龟的位姿。
结果演示:
实现分析:
- 首先,启动乌龟显示以及运动控制节点并控制乌龟运动;
- 要通过 ROS 命令,来获取乌龟位姿发布的话题以及消息;
- 编写订阅节点,订阅并打印乌龟的位姿。
实现流程:
- 通过 ROS 命令获取话题与消息信息;
- 编码实现位姿获取节点;
- 启动
roscore
、turtlesim_node
、控制节点以及位姿订阅节点,控制乌龟运动并输出乌龟的位姿。
2.5.2.0 准备工作
准备工作:编写乌龟GUI界面和键盘控制的
launch
文件。
1 | <!-- 启动乌龟GUI与键盘控制节点 --> |
2.5.2.1 话题与消息获取
- 话题获取:
- 新建三个终端:
- 第一个:
roscore
- 第二个:
roslaunch plumbing_test start_turtle.launch
- 第三个:
rostopic list
/turtle1/pose
- 第一个:
- 新建三个终端:
- 消息类型获取:
- 首先使用
rostopic info /turtle1/pose
命令获取话题的消息类型;- 消息类型:
turtlesim/Pose
- 消息类型:
- 再使用
rosmsg info /turtle1/Pose
命令获取消息的具体结构;- 消息结构:
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
- 消息结构:
- 首先使用
2.5.2.2 实现订阅节点
最简单的方式,不需要通过自定义节点,只需要打印 /turtle1/pose
的信息即可:
1 | rostopic echo /turtle1/pose |
创建功能包需要依赖的功能包:roscpp rospy std_msgs turtlesim
,我们这里仍然在功能包 plumbing_test
中追加 turtlesim
依赖即可:
- 修改
package.xml
:
1 | <build_depend>turtlesim</build_depend> |
- 修改
CMakeLists.txt
:
1 | find_package(catkin REQUIRED COMPONENTS |
- 新建
test02_sub_pose_p.py
:
1 | #! /usr/bin/env python |
这里我们还需要配置 CMakeLists.txt
:
1 | catkin_install_python(PROGRAMS |
注意:为
Python
文件赋予执行权限。
2.5.3 实操03_服务调用
需求描述:编码实现向 turtlesim
发送请求,在乌龟显示节点的窗体指定位置生成一乌龟,这是一个服务请求操作。
结果演示:
实现分析:
- 首先,需要启动乌龟显示节点;
- 要通过ROS命令,来获取乌龟生成服务的服务名称以及服务消息类;
- 编写服务请求节点,生成新的乌龟。
实现流程:
- 通过ros命令获取服务与服务消息信息;
- 编码实现服务请求节点;
- 启动
roscore
、turtlesim_node
、乌龟生成节点,生成新的乌龟。
2.5.3.1 服务名称与服务消息获取
- 获取服务名称:
- 使用
rosservice list
:/spawn
- 使用
- 获取消息类型:
- 使用
rosservice info /spawn
turtlesim/Spawn
- 使用
- 获取消息结构:
- 使用
rossrv info turtlesim/Spawn
float32 x
float32 y
float32 theta
string name
- 使用
2.5.3.2 服务客户端实现
最简单的方式是使用 rosservice call
命令来调用服务,具体如下:
1 | rosservice call /spawn "x: 1.0 |
注意:
- 服务名称前需要添加
/
;- 服务消息的
x
、y
、theta
数据类型为float32
,name
数据类型为string
,需要使用''
包裹;- 多个数据之间使用空格隔开。
创建功能包需要依赖的功能包: roscpp rospy std_msgs turtlesim
- 创建
test03_srv_spawn_p.py
:
1 | #! /usr/bin/env python |
- 编辑
CMakeLists.txt
文件,赋予 Python 文件可执行权限,并编译。 - 运行,建立三个终端:
- 第一个:
roscore
- 第二个:
roslaunch plumbing_test start_turtle.launch
- 第三个:
rosrun plumbing_test test03_srv_spawn_p.py 3.0 3.0 0.5 "turtle3"
- 第一个:
2.5.4 实操04_参数设置
需求描述:修改 turtlesim
乌龟显示节点窗体的背景色,已知背景色是通过参数服务器的方式以 rgb
方式设置的。
结果演示:
实现分析:
- 首先,需要启动乌龟显示节点;
- 要通过ROS命令,来获取参数服务器中设置背景色的参数;
- 编写参数设置节点,修改参数服务器中的参数值。
实现流程:
- 通过ros命令获取参数;
- 编码实现服参数设置节点;
- 启动 roscore、turtlesim_node 与参数设置节点,查看运行结果。
2.5.4.1 参数名获取
- 获取参数列表:
/turtlesim/background_b
/turtlesim/background_g
/turtlesim/background_r
2.5.4.2 获取参数值
- 获取参数值:
rosparam get /turtlesim/background_r
rosparam get /turtlesim/background_g
rosparam get /turtlesim/background_b
2.5.4.2 参数修改
最简单的方式是直接使用命令的方式修改:
rosparam set /turtlesim/background_r 255
rosparam set /turtlesim/background_g 0
rosparam set /turtlesim/background_b 0
然后重新启动节点 rosrun turtlesim turtlesim_node
,会发现背景变为亮红色了:
接下来我们编写参数设置节点,修改背景颜色为亮红色。
新建 test04_par_back_p.py
文件,并配置 CMakeLists.txt
文件:
1 | #! /usr/bin/env python |
注意:赋予 Python 文件可执行权限,之后在终端运行
rosrun plumbing_test test04_par_back_p.py
,并重启 turtlesim_node 节点,查看运行结果。
2.5.4.3 补充其他设置方式
2.5.4.3.1 方式1:修改小乌龟节点的背景色(命令行实现)
1 | rosparam set /turtlesim/background_b 自定义数值 |
修改相关参数后,重启 turtlesim_node 节点,背景色就会发生改变了。
2.5.4.3.2 方式2:启动节点时,直接设置参数
1 | rosrun turtlesim turtlesim_node _background_r:=100 _background_g=0 _background_b=0 |
2.5.4.3.3 方式3:通过launch文件传参
1 | <launch> |
2.6 通信机制比较
三种通信机制中,参数服务器是一种数据共享机制,可以在不同的节点之间共享数据,话题通信与服务通信是在不同的节点之间传递数据的,三者是ROS中最基础也是应用最为广泛的通信机制。
这其中,话题通信和服务通信有一定的相似性也有本质上的差异,在此将二者做一下简单比较。
二者的实现流程是比较相似的,都是涉及到四个要素:
要素编号 | 要素内容 | 实现形式 |
---|---|---|
要素1 | 消息的发布方/客户端 | Publisher/Client |
要素2 | 消息的订阅方/服务端 | Subscriber/Server |
要素3 | 话题名称 | Topic/Service |
要素4 | 数据载体 | msg/srv |
可以概括为: 两个节点通过话题关联到一起,并使用某种类型的数据载体实现数据传输。
二者的实现也是有本质差异的,具体比较如下:
通信模式 | Topic(话题) | Service(服务) |
---|---|---|
同步性 | 异步 | 同步 |
底层协议 | ROSTCP/ROSUDP | ROSTCP/ROSUDP |
缓冲区 | 有 | 无 |
实时性 | 弱 | 强 |
节点关系 | 多对多 | 一对多(一个 Server) |
通信数据 | msg | srv |
使用场景 | 连续高频的数据发布与接收:雷达、里程计 | 偶尔调用或执行某一项特定功能: 拍照、语音识别 |
2.7 本章小节
本章主要介绍了ROS中最基本的也是最核心的通信机制实现: 话题通信、服务通信、参数服务器。每种通信机制,都介绍了如下内容:
- 伊始介绍了当前通信机制的应用场景;
- 介绍了当前通信机制的理论模型;
- 介绍了当前通信机制的 Python 实现。
除此之外,还介绍了:
- ROS中的常用命令方便操作、调试节点以及通信信息;
- 通过实操又将上述知识点加以整合;
- 最后又着重比较了话题通信与服务通信的相同点以及差异。
掌握本章内容后,基本上就可以从容应对ROS中大部分应用场景了。
第三章 ROS 通信机制进阶
本章主要内容如下:
- ROS常用API介绍;
- ROS中自定义头文件与源文件的使用。
3.1 常用 API
API
解释:API
的全称是 Application Programming Interface
,即应用程序编程接口。它是不同软件组件之间进行交互的一套规则和协议,就像两个程序之间沟通的 “桥梁”,比如在 Python 中调用的各种库函数,本质上就是在使用该库提供的 API
。
建议参考官方API文档或参考源码:
- ROS节点的初始化相关API;
- NodeHandle 的基本使用相关API;
- 话题的发布方,订阅方对象相关API;
- 服务的服务端,客户端对象相关API;
- 时间相关API;
- 日志输出相关API。
参考文档:
3.1.1 初始化
1 | #! /usr/bin/env python |
3.1.1.1 argv
的使用
我们在命令行中使用如下命令行,可以传入参数A,并赋值10000:
1 | rosrun plumbing_apis demo01_apis_pub_p.py _A:=10000 |
我们还可以查看并获取参数A的值:
1 | rosparam list /api_pub/A |
3.1.1.2 anoymous
的使用
如下图所示,我们能清楚发现,如果同一个 python 文件在两个终端运行,由于节点名称相同,会导致第一个启动的 python 文件被强制暂停:
这时候我们使用 anoymous
就可以很好的解决:
1 | rosnode list |
3.1.2 话题与服务相关对象
3.1.2.1 发布对象
参数解读:重点解读参数 latch
的使用,该参数作用是如果设置为 True,可以将发布的最后一条数据保存,且后续当新的订阅对象订阅时,会立即收到最后一条数据。
应用场景:
- 发布机器人的速度指令;
- 发布发布地图数据。
API 定义:
1 | class Publisher(Topic): |
示例代码:
1 | #! /usr/bin/env python |
如下图所示,我们先运行:
1 | rosrun plumbing_apis demo01_apis_pub_p.py |
接下来我们用终端调用一个新的节点来订阅:
1 | rostopic echo /api_pub_test |
3.1.2.2 订阅对象
对象获取:
1 | class Subscriber(Topic): |
3.1.2.3 服务对象
对象获取:
1 | class Service(ServiceImpl): |
3.1.2.4 客户端对象
对象获取:
1 | class ServiceProxy(_Service): |
请求发送函数:
1 | def call(self, *args, **kwds): |
等待服务函数:
1 | def wait_for_service(service, timeout=None): |
3.1.3 回旋函数
1 | def spin(): |
3.1.4 时间
ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器…都与时间相关。
1 | #! /usr/bin/env python |
3.1.4.1 时刻
3.1.4.2 持续时间
3.1.4.3 持续时间与时刻运算
注意:
Time
和Time
不可以相加减。
3.1.4.4 设置运行频率
这里设置为 0.5Hz
,即每隔2秒发送一次。
3.1.4.5 定时器
定时器解读:
1 | #定时器设置 |
根据该API定义,回调函数需要接收参数类型是rospy.TimerEvent
,如下展示其具有的作用:
1 | # 回调函数 |
3.1.5 其他函数
这里我们主要介绍两类 API:
- 一类是与节点生命周期有关:
- 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
- 同名节点启动,导致现有节点退出;
- 程序中的其他部分调用了节点关闭相关的API(rospy.signal_shutdown())
- 一类是与日志相关:
- DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
- INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
- WARN(警告):提醒一些异常情况,但程序仍然可以执行;
- ERROR(错误):提示错误信息,此类错误会影响程序运行;
- FATAL(严重错误):此类错误将阻止节点继续运行。
1 | #! /usr/bin/env python |
3.2 ROS中Python模块导入
需求:首先新建一个Python文件A,再创建Python文件UseA,在UseA中导入A并调用A的实现。
实现:
- 新建两个Python文件,使用 import 实现导入关系;
- 添加可执行权限、编辑配置文件并执行UseA。
3.2.1 创建 tools.py
1 | #! /usr/bin/env python |
3.2.2 创建 demo04_module_p.py
1 | #! /usr/bin/env python |
3.2.3 尝试运行并纠错
- 此时我们仅仅完成
CMakeLists.txt
中如下配置:
1 | catkin_install_python(PROGRAMS |
chmod +x *.py
赋予可执行权限后,尝试运行:
执行命令 | 运行结果 |
---|---|
rosrun plumbing_apis demo04_module_p.py |
|
python3 demo04_module_p.py |
解释:这主要是由于
rosrun
指令默认的执行路径是工作空间下,而并没有聚焦到具体功能包的scripts
文件夹下,就导致rosrun
找不到指定文件,会报错,我们可以通过以下代码验证。
1 | #! /usr/bin/env python |
执行指令 | 运行结果 |
---|---|
rosrun plumbing_apis demo04_module_p.py |
|
python3 demo04_module_p.py |
解决:我们可以声明 python 的环境变量,当依赖某个模块时,先去指定的环境变量中查找依赖。
问题:这样虽然解决了找不到 my_tools.py
模块的问题,但是这样将路径写死并不好,会导致其他人用这个代码的时候,还需要修改路径,因此接下来我们提供一个更优选择,我们可以动态获取路径:
1 | #! /usr/bin/env python |
第四章 ROS运行管理
ROS是多进程(节点)的分布式框架,一个完整的ROS系统实现:
- 可能包含多台主机;
- 每台主机上又有多个工作空间(workspace);
- 每个的工作空间中又包含多个功能包(package);
- 每个功能包又包含多个节点(Node),不同的节点都有自己的节点名称;
- 每个节点可能还会设置一个或多个话题(topic)。
在多级层深的ROS系统中,其实现与维护可能会出现一些问题,比如,如何关联不同的功能包,繁多的ROS节点应该如何启动?功能包、节点、话题、参数重名时应该如何处理?不同主机上的节点如何通信?
本章主要内容介绍在ROS中上述问题的解决策略(见本章目录),预期达成学习目标也与上述问题对应:
- 掌握元功能包使用语法;
- 掌握launch文件的使用语法;
- 理解什么是ROS工作空间覆盖,以及存在什么安全隐患;
- 掌握节点名称重名时的处理方式;
- 掌握话题名称重名时的处理方式;
- 掌握参数名称重名时的处理方式;
- 能够实现ROS分布式通信。
4.1 ROS元功能包
场景:
- 完成ROS中一个系统性的功能,可能涉及到多个功能包,比如实现了机器人导航模块,该模块下有地图、定位、路径规划…等不同的子级功能包。那么调用者安装该模块时,需要逐一的安装每一个功能包吗?
- 显而易见的,逐一安装功能包的效率低下,在ROS中,提供了一种方式可以将不同的功能包打包成一个功能包,当安装某个功能模块时,直接调用打包后的功能包即可,该包又称之为元功能包(metapackage)。
概念:
- MetaPackage是Linux的一个文件管理系统的概念。是ROS中的一个虚包,里面没有实质性的内容,但是它依赖了其他的软件包,通过这种方法可以把其他包组合起来,我们可以认为它是一本书的目录索引,告诉我们这个包集合中有哪些子包,并且该去哪里下载。
- 例如
sudo apt install ros-noetic-desktop-full
命令安装ros时就使用了元功能包,该元功能包依赖于ROS中的其他一些功能包,安装该包时会一并安装依赖。 - 还有一些常见的MetaPackage:navigation moveit! turtlebot3 …
作用:
- 方便用户的安装,我们只需要这一个包就可以把其他相关的软件包组织到一起安装了。
实现:
- 基础描述:我目前已经有如图所示的五个功能包,我想要使用元功能包来管理这几个功能包。
- 创建一个功能包;
catkin_create_pkg plumbing_my
,不需要加依赖。
- 修改
package.xml
,内容如下,添加构建依赖:
1 | <exec_depend>plumbing_apis</exec_depend> |
- 修改
CMakeLists.txt
:
1 | cmake_minimum_required(VERSION 3.0.2) |
Ctrl+Shift+B
编译,编译通过。
CMakeLists.txt
中不可以有换行。
参考架构:ROS navigation
4.2 ROS 节点管理 launch 文件
一个程序中可能需要启动多个节点,比如:ROS 内置的小乌龟案例,如果要控制乌龟运动,要启动多个窗口,分别启动 roscore、乌龟界面节点、键盘控制节点。如果每次都调用 rosrun 逐一启动,显然效率低下,如何优化?
采用的优化策略便是使用 roslaunch 命令集合 launch 文件启动管理节点,并且在后续教程中,也多次使用到了 launch 文件。
概念:
- launch 文件是一个 XML 格式的文件,可以启动本地和远程的多个节点,还可以在参数服务器中设置参数。
作用:
- 简化节点的配置与启动,提高ROS程序的启动效率。
使用:
- 新建launch文件:
- 在功能包下添加 launch目录, 目录下新建
start_turtle.launch
文件,编辑 launch 文件。
- 在功能包下添加 launch目录, 目录下新建
1 | <launch> |
- 编译并新建一个终端窗口,输入
source ./devel/setup.bash
。
- 调用 launch 文件:
这里附上官方链接:ROS launch
4.2.1 launch文件标签——launch
<launch>
标签是所有 launch 文件的根标签,充当其他标签的容器。
属性:
deprecated = "弃用声明"
:如果该 launch 文件已经弃用,那么可以使用该属性进行说明。
1 | <launch deprecated = "此文件已过时,不建议使用!"> |
如下图所示,执行效果一样,只是会在终端输出日志:
1 | WARNING: [/home/mpy/workspace_learn_ros/autolabor_python/src/launch01_basic/launch/start_turtle.launch] DEPRECATED: 此文件已过时,不建议使用! |
4.2.2 launch文件标签——node
<node>
标签用于指定 ROS 节点,是最常见的标签,需要注意的是: roslaunch 命令不能保证按照 node 的声明顺序来启动节点(节点的启动是多进程的)
-
属性:
pkg="包名"
:节点所属的包type="nodeType"
:节点类型(与之相同名称的可执行文件)name="nodeName"
:节点名称(在 ROS 网络拓扑中节点的名称)args="xxx xxx xxx" (可选)
:将参数传递给节点machine="机器名"
:在指定机器上启动节点respawn="true | false" (可选)
:如果节点退出,是否自动重启respawn_delay=" N" (可选)
:如果 respawn 为 true, 那么延迟 N 秒后启动节点required="true | false" (可选)
:该节点是否必须,如果为 true,那么如果该节点退出,将杀死整个 roslaunchns="xxx" (可选)
:在指定命名空间 xxx 中启动节点clear_params="true | false" (可选)
:在启动前,删除节点的私有空间的所有参数output="log | screen" (可选)
:日志发送目标,可以设置为 log 日志文件,或 screen 屏幕,默认是 log
-
子级标签:
env
:环境变量设置remap
:重映射节点名称rosparam
:参数设置param
:参数设置
4.2.2.1 respawn
respawn
属性:如果节点退出,是否自动重启
respawn="true | false" (可选)
:如果节点退出,是否自动重启respawn_delay=" N" (可选)
:如果 respawn 为 true, 那么延迟 N 秒后启动节点
1 | <launch deprecated = "此文件已过时,不建议使用!"> |
如下演示所示,我们关闭窗口后会自动启动,那么如何彻底关闭节点呢?
- 在终端中使用
Ctrl+C
即可。
4.2.2.2 required
required="true | false" (可选)
:该节点是否必须,如果为 true,那么如果该节点退出,将杀死整个 roslaunch。
1 | <launch deprecated = "此文件已过时,不建议使用!"> |
如下演示过程:
如下可以观察到终端输出:REQUIRED process [my_turtle-2] has died!
4.2.2.3 ns
ns="xxx" (可选)
:在指定命名空间 xxx 中启动节点。
我们首先解释一下什么是命名空间,如下图所示,我们可以查看到节点列表名称:rosnode list
这时候如果我们想要在各个节点之前加前缀,我们可以在ns
属性中添加,如下:
1 | <launch deprecated = "此文件已过时,不建议使用!"> |
如下图所示:
ns
的使用可以很好的避免重名问题。
4.2.3 launch文件标签——include
include 标签用于将另一个 xml 格式的 launch 文件导入到当前文件。
-
属性:
file=$(find 包名)/xxx/xxx.launch
:要包含的文件路径;ns="xxx"(可选)
:在指定命名空间导入文件。
-
子级标签:
env
:环境变量设置;arg
:将参数传递给被包含的文件。
我们在launch01_basic
功能包下的launch
文件夹中新建start_turtle_use.launch
文件:
1 | <!-- 需要复用 start_turtle.launch 文件,使用 include 标签 --> |
注意:
launch
文件编写完毕后,不需要编译,可以直接执行。
1 | roslaunch launch01_basic start_turtle_use.launch |
4.2.4 launch文件标签——remap
remap
:用于话题重命名
- 属性:
from = "xxx"
:原始话题名称;to = "xxx"
:目标名称。
- 子级标签:无
这里我们首先补充下载一个控制功能包:
1 | sudo apt-get install ros-noetic-teleop-twist-keyboard |
然后我们就可以运行以下命令打开自带的键盘控制节点:
1 | rosrun teleop_twist_keyboard teleop_twist_keyboard.py |
从图中可以明显看出,自启动的键盘控制节点话题名称是/cmd_vel
,而乌龟仿真对应的节点话题名称是/turtle/cmd_vel
,如何使得两个话题名称一致从而可以正常通讯呢?就要使用我们的remap
标签了。
1 | <launch deprecated = "此文件已过时,不建议使用!"> |
4.2.5 launch文件标签——param
<param>
标签主要用于在参数服务器上设置参数,参数源可以在标签中通过 value 指定,也可以通过外部文件加载,在<node>
标签中时,相当于私有命名空间。
- 属性:
name="命名空间/参数名"
:- 参数名称,可以包含命名空间;
value="xxx"(可选)
:- 定义参数值,如果此处省略,必须指定外部文件作为参数源;
type="str | int | double | bool | yaml"(可选)
:- 指定参数类型,如果未指定,roslaunch 会尝试确定参数类型,规则如下:
- 如果包含
.
的数字解析为浮点型,否则为整型; - “true” 和 “false” 是 bool 值(不区分大小写);
- 其他是字符串。
- 如果包含
- 指定参数类型,如果未指定,roslaunch 会尝试确定参数类型,规则如下:
- 子级标签:无
- 格式:
launch
内,node
外;launch
内,node
内,此时会被视为私有。
1 | <launch deprecated = "此文件已过时,不建议使用!"> |
如下图如所示,使用rosparam list
可以观察到两个参数:
launch
内,node
外:/param_A
launch
内,node
内:/my_turtle/param_B
4.2.6 launch文件标签——rosparam
<rosparam>
标签可以从 YAML 文件导入参数,或将参数导出到 YAML 文件,也可以用来删除参数,<rosparam>
标签在<node>
标签中时被视为私有。
- 属性:
command = "load | dump | delete"
:load
:从 YAML 文件导入参数;dump
:将参数导出到 YAML 文件;delete
:删除参数。
file = "$(find xxx)/xxx/yyy/..."
:- 指定 YAML 文件路径。
param="参数名称"
ns="命名空间"(可选)
- 子级标签:无
- 格式:
launch
内,node
外;launch
内,node
内,此时会被视为私有。