ROS1 体系理解与工作区
(注意:本站所有博客均是记录本人学习过程中的理解,可能存在错误和不完善之处,请仔细甄别)
ROS的体系理解
思考一下,一个机器人操作系统,相比于普通的计算机操作系统,它要能做到哪些事?
机器人由许多的结构组成,比如机械臂,人型的有双手双脚,音箱,摄像头等等,它的工作需要各个组成部分搭配使用。人可以通过大脑来统筹管理自己的手脚,机器人也是一样。但是机器人没有人一样的生物电信号传输,协调工作需要手脚之间相互传递信息,同时各个部件都可能涉及到复杂的操作,要保证工作时能相互独立,并行进行,由此我们引出了节点。
节点就像是os当中的进程,各个节点的工作相对独立。我们可以自己编写节点,然后使用rosrun指令来运行。
那么节点之间是怎样协作的呢?我们举一个具体的例子:比如我们有一个“眼睛”节点,它的作用是连接摄像头,实时的获取图像。这里涉及了两个问题:
- 我要怎么把图像传给其它有需要的节点
- 我要怎么保证其它节点拿到我传过去的数据后,按和我一样的格式去识别它
为了解决问题1,我们规定了一种模式,即订阅者-发布者模式。这里我们就要引出话题(Topic)了:它有点像os中的管道,节点订阅需要一个话题,节点发布同样需要一个话题。编写的时候一般由发布的节点来初始化,具体的编写方法会在后面详述。
话题是什么呢?我现在更愿意把它理解成一个有名字的数据通道。发布者把消息发布到这个话题,订阅者只要订阅同一个话题就能收到消息。一个话题可以有多个发布者,也可以有多个订阅者;实际项目里通常会通过接口约定和命名来避免多个节点无意间向同一个话题发布互相冲突的数据。所以,如果我们需要整合来自多个节点的信息,就可以编写一个节点去订阅这些节点发布的话题,再做统一处理。
回到我们开始的例子:我们的“眼睛”节点,把发布了一个名叫/image_raw的话题,把图像数据都传了进去。然后我们有一个“中枢”节点,它内置了计算机视觉的模型,比如可以从图像里识别出里头的物品名称,那么它就订阅这个叫做/image_raw的话题,从里头拿出来图像数据,调用模型去识别,把结果打印在终端屏幕上。这样就实现了一个简单的工作流。
问题2就更好解决了。ROS 已经定义了很多常用消息类型,比如字符串可以用 std_msgs/String,图像通常使用 sensor_msgs/Image。在 Python 里可以写 from sensor_msgs.msg import Image,然后在发布和订阅时指定同一种消息类型。
ROS的大脑:ROS Master (roscore)
我们现在有了节点(员工)和话题(公告板),但一个新的员工来了,他怎么知道要去哪个公告板找信息,又该把自己的信息贴在哪里呢?
这就需要一个管理者,在ROS中,这个角色就是 ROS Master。
你可以把ROS Master想象成一个“中央登记处”或“总机”。在任何节点运行之前,必须先启动ROS Master。它的作用是:
- 节点注册:每个节点启动时,都会向Master报告:“你好,我是‘眼睛’节点,我将要发布一个叫/image_raw的话题。”
- 信息查询:“中枢”节点启动时会问Master:“你好,我想订阅/image_raw这个话题,请问谁在发布它?”
- 建立连接:Master告诉“中枢”节点“眼睛”节点的地址。之后,这两个节点就会建立直接的连接,图像数据会直接从“眼睛”节点流向“中枢”节点,不再经过Master(这很重要,Master只负责牵线,不负责传输数据,所以不会成为数据传输的瓶颈)。
启动ROS Master的命令非常简单,只需要打开一个独立的、长期运行的终端,输入:
1
roscore
这个终端会一直运行着roscore,为整个ROS系统提供管理服务。
查看系统状态的工具
所以,从宏观上看,ROS系统包含了一个ROS Master,一大堆节点,一大堆话题,以及节点与话题之间的发布和订阅关系。我们可以使用一些指令来查看这些信息:
1
2
3
4
5
6
7
8
9
10
# 在启动了roscore和若干节点后,打开一个新的终端,输入下面的指令
# 查看当前所有活跃的节点
rosnode list
# 查看当前所有活跃的话题
rostopic list
# 实时显示某个话题上发布的数据,例如/image_raw
rostopic echo /image_raw
输入上面的指令,就可以看到现在运行的节点,或是监听某个话题的信息,实现实时监控。
ROS的工作区
在编写代码之前,我们需要先有一个工作区。我们可以使用如下指令来初始化一个工作区:
1
2
3
4
5
6
7
8
# 在用户主目录下创建一个名为 catkin_ws 的文件夹,其中包含一个 src 文件夹
mkdir -p ~/catkin_ws/src
# 进入工作区根目录
cd ~/catkin_ws/
# 编译工作区,这会在工作区内生成 build 和 devel 文件夹
catkin_make
catkin_make 是一个便捷的工具,用于构建ROS工作区中的代码。 第一次运行它时,会在src目录下创建一个CMakeLists.txt的链接,并在devel目录下生成setup.bash等环境配置文件。
完成后,我们会发现根目录下多了一个文件夹,里头的结构为:
里面的src文件是我们编写代码的地方。我们打开它,里头长这样:
1
2
lori@LAPTOP-ETP28CAA:~/catkin_ws/src$ ls
CMakeLists.txt beginner_tutorials robot_vision_project
后面两个包都是我新增的,可以使用如下代码来初始化一个包:
1
2
3
4
5
6
7
# 首先进入src目录
cd ~/catkin_ws/src
# 使用 catkin_create_pkg 命令创建新包
# 格式为: catkin_create_pkg <包名> [依赖1] [依赖2] ...
# rospy, roscpp, std_msgs 是非常常用的依赖
catkin_create_pkg my_new_package rospy roscpp std_msgs
catkin_create_pkg 命令会自动生成一个功能包所需的基本文件结构,包括package.xml和CMakeLists.txt。
进入包内,以beginner_tutorials为例,结构如下:
1
2
lori@LAPTOP-ETP28CAA:~/catkin_ws/src/beginner_tutorials$ ls
CMakeLists.txt include launch model package.xml src training_scripts
里面还有一个src,这里的src就是我们存放.py文件的地方了。model和training_scripts文件夹是我新建的,里面的代码启动节点时不会被自动执行。
当我们修改了某个文件,我们需要更新配置:
1
2
3
4
5
6
7
8
# 回到工作区根目录
cd ~/catkin_ws
# 再次编译工作区以应用更改
catkin_make
# 更新当前终端的环境变量,让ROS能找到你的新包和节点
source devel/setup.bash
每次打开新的终端,或者修改了代码并重新编译后,都需要source一下setup.bash文件,这样才能确保ROS环境能正确找到你的工作区和功能包。
如果要启动包中的某个节点,打开一个Ubuntu终端,输入以下指令:
1
2
3
# 使用 rosrun 启动单个节点
# 格式为: rosrun <包名> <可执行文件名>
rosrun beginner_tutorials talker.py
如果要启动多个节点,用上面这种方式会很麻烦,所以我们可以先配置launch文件后再启动,流程如下:
创建launch文件:在你的功能包(例如beginner_tutorials)下创建一个名为launch的文件夹。
编写launch文件:在launch文件夹内创建一个以.launch结尾的文件,例如start_nodes.launch。文件内容是XML格式,示例如下:
1 2 3 4 5 6 7
<launch> <!-- 启动第一个节点 --> <node pkg="beginner_tutorials" type="talker.py" name="talker_node" output="screen" /> <!-- 启动第二个节点 --> <node pkg="beginner_tutorials" type="listener.py" name="listener_node" output="screen" /> </launch>
-
是根元素。 -
标签用来启动一个节点。 - pkg 指明节点所在的功能包。
- type 指明节点的可执行文件名。
- name 是为这个节点设定的唯一名称。
- output=”screen” 会将节点的日志信息打印到终端屏幕上。
-
启动launch文件:打开终端,输入以下指令:
1 2 3
# 使用 roslaunch 启动 # 格式为: roslaunch <包名> <launch文件名> roslaunch beginner_tutorials start_nodes.launch
roslaunch工具会自动检查roscore是否已运行,如果没有,它会先启动roscore,然后根据.launch文件的内容启动所有指定的节点。
