什么是ROS
-
中间件/类操作系统:
- 硬件抽象
- 底层设备控制
- 常用函数的实现
- 进程间消息传递
- 包管理
-
官方的定义:ROS = 框架 + 工具 + 功能 + 社区
- 框架
- 分布式
- 节点(进程)管理
- 节点(进程)间通信
- 工具
- 仿真
- 数据可视化
- 图形界面
- 数据记录
- 功能
- 控制
- 规划
- 视觉
- 建图
- 社区
- 软件包管理
- 文档
- 教程
- 框架
Catkin工作空间和编译系统
ROS1通信架构:
Master & Node
Node
在ROS的世界里,最小的进程单元就是节点(node)。一个软件包里可以有多个可执行文件,可执行文件在运行之后就成了一个进程(process),这个进程在ROS中就叫做节点。 从程序角度来说,node就是一个可执行文件(通常为C++编译生成的可执行文件、Python脚本)被执行,加载到了内存之中;从功能角度来说,通常一个node负责者机器人的某一个单独的功能。
- 节点(node)相当一个个程序:
- ROS中的进程
- pkg中可执行文件运行的实例
- node节点启动
rosrun [--prefix cmd] [--debug] pkg_name node_name
示例
假设你有一个名为 my_package 的 ROS 包,里面有一个名为 my_node 的节点,运行这个节点,并传递一些参数给它:
rosrun my_package my_node arg1 arg2
在调试模式下运行这个节点,输出更多信息:
rosrun --debug my_package my_node arg1 arg2
- 常用命令
rosnode命令 作用 rosnode list 列出当前运行的node信息 rosnode info node_name 显示出node的详细信息 rosnode kill node_name 结束某个node
Master
master在整个网络通信架构里管理着各个node。node首先在master处进行注册,之后master会将该node纳入整个ROS程序中。node之间的通信也是先由master进行“牵线“。才能两两的进行点对点通信。当ROS程序启动时,第一步首先启动master,由节点管理器处理依次启动node。
- master相当于一个管家,用来管理各个节点
- 每个 node 启动时都要向 master 注册
- 管理 node 之间的通信
- 启动 roscore
- roscore:启动 ros master
- master:节点管理器
- rosout:日志输出
- parameter server:参数服务器
通常运行ROS,按照上述顺序启动,节点太多时,选择用launch文件来启动。 Master、Node之间以及Node之间的关系如下图所示:
launch文件
- 简介
机器人是一个系统工程,通常一个机器人运行操作时要开启很多个node,对于一个复杂的机器人的启动操作,并不需要每个节点依次进行rosrun,ROS提供了一个命令能一次性启动master和多个node。该命令是:
roslaunch pkg_name file_name.launch
roslaunch命令首先会自动进行检测系统的roscore有没有运行,也即是确认节点管理器是否在运行状态中,如果master没有启动,那么roslaunch就会首先启动master,然后再按照launch的规则执行。launch文件里已经配置好了启动的规则。 所以roslaunch就像是一个启动工具,能够一次性把多个节点按照预先的配置启动起来,减少在终端中一条条输入指令的麻烦。
-
写法与格式
launch文件同样也遵循着xml格式规范,是一种标签文本,它的格式包括以下标签:
参考链接 -
示例
launch文件的写法和格式看起来内容比较复杂,我们先来介绍一个最简单的例子如下:
<launch>
<node name="talker" pkg="rospy_tutorials" type="talker"/>
</launch>
注:
pkg
功能包的名字
type
功能包下你想启动节点的名字
name
对这个节点再命个名,我们取一样的就好
output
结果输出在屏幕上,没有这个参数默认记录在log里
这是官网给出的一个最小的例子,它启动了一个单独的节点talker,该节点是包rospy_tutorials软件包中的节点。
实际中的launch文件要复杂很多,我们以Ros-Academy-for-Beginners中的robot_sim_demo为例:
<launch>
<!--arg是launch标签中的变量声明,arg的name为变量名,default或者value为值-->
<arg name="robot" default="xbot2"/>
<arg name="debug" default="false"/>
<arg name="gui" default="true"/>
<arg name="headless" default="false"/>
<!-- Start Gazebo with a blank world -->
<include file="$(find gazebo_ros)/launch/empty_world.launch"> <!--include用来嵌套仿真场景的launch文件-->
<arg name="world_name" value="$(find robot_sim_demo)/worlds/ROS-Academy.world"/>
<arg name="debug" value="$(arg debug)"/>
<arg name="gui" value="$(arg gui)"/>
<arg name="paused" value="false"/>
<arg name="use_sim_time" value="true"/>
<arg name="headless" value="$(arg headless)"/>
</include>
<!-- Oh, you wanted a robot? --> <!--嵌套了机器人的launch文件-->
<include file="$(find robot_sim_demo)/launch/include/$(arg robot).launch.xml"/>
<!--如果你想连同RViz一起启动,可以按照以下方式加入RViz这个node-->
<!--node name="rviz" pkg="rviz" type="rviz" args="-d $(find robot_sim_demo)/urdf_gazebo.rviz" /-->
</launch>
这个launch文件的内容稍微有些复杂,它的作用是:启动gazebo模拟器,导入参数内容,加入机器人模型。
Topic
简介
ROS的通信方式是ROS最为核心的概念,ROS系统的精髓就在于它提供的通信架构。ROS的通信方式有以下四种:
- Topic 主题
- Service 服务
- Parameter Service 参数服务器
- Actionlib 动作库
Topic
ROS中的通信方式中,topic是常用的一种。对于实时性、周期性的消息,使用topic来传输是最佳的选择。topic是一种点对点的单向通信方式,这里的“点”指的是node,也就是说node之间可以通过topic方式来传递信息。topic要经历下面几步的初始化过程: - 首先,publisher节点和subscriber节点都要到节点管理器进行注册
- 然后,publisher会发布topic
- subscriber在master的指挥下会订阅该topic,从而建立起sub-pub之间的通信。
注意整个过程是单向的。其结构示意图如下:
注: 我在实操中一般用到
roscore &
:命令中的&符号表示将roscore命令放在后台执行。这意味着你可以在启动roscore的同时,在同一终端窗口中执行其他命令或操作。这对于需要同时运行多个ROS节点或程序的情况非常有用。
Subscriber接收消息会进行处理,一般这个过程叫做回调(Callback)。所谓回调就是提前定义好了一个处理函数(写在代码中),当有消息来就会触发这个处理函数,函数会对消息进行处理。
上图就是ROS的topic通信方式的流程示意图。topic通信属于一种异步的通信方式。下面通过一个示例来了解如何使用topic通信。
通信示例
参考下图,以摄像头画面的发布、处理、显示为例讲讲topic通信的流程。在机器人上的摄像头拍摄程序是一个node(圆圈表示,记作node1),当node1运行启动之后,它作为一个Publisher就开始发布topic。比如它发布了一个topic(方框表示),叫做/camera_rgb,是rgb颜色信息,即采集到的彩色图像。同时,node2假如是图像处理程序,它订阅了/camera_rgb这个topic,经过节点管理器的介绍,它就能建立和摄像头节点(node1)的连接。
怎么样来理解“异步”这个概念呢?在node1每发布一次消息之后,就会继续执行下一个动作,至于消息是什么状态、被怎样处理,它不需要了解;而对于node2图像处理程序,它只管接收和处理/camera_rgb上的消息,至于是谁发来的,它不会关心。所以node1、node2两者都是各司其责,不存在协同工作,称这样的通信方式是异步的。
ROS是一种分布式的架构,一个topic可以被多个节点同时发布,也可以同时被多个节点接收。比如在这个场景中用户可以再加入一个图像显示的节点,我们想看看摄像头节点的画面,则可以用笔记本连接到机器人上的节点管理器,然后在电脑上启动图像显示节点。
这就体现了分布式系统通信的好处:扩展性好、软件复用率高。
- 总结三点:
- topic通信方式是异步的,发送时调用publish()方法,发送完成立即返回,不用等待反馈。
- subscriber通过回调函数的方式来处理消息。
- topic可以同时有多个subscribers,也可以同时有多个publishers。ROS中这样的例子有:/rosout、/tf等等。
操作命令
在实际应用中,应该熟悉topic的几种使用命令,下表详细的列出了各自的命令及其作用。
命令 作用
rostopic list | 列出当前所有的topic |
---|---|
rostopic info topic_name | 显示某个topic的属性信息 |
rostopic echo topic_name | 显示某个topic的内容 |
rostopic pub topic_name … | 向某个topic发布内容 |
rostopic bw topic_name | 查看某个topic的带宽 |
rostopic hz topic_name | 查看某个topic的频率 |
rostopic find topic_type | 查找某个类型的topic |
rostopic type topic_name | 查看某个topic的类型(msg) |
可以通过rostopic help或rostopic command -h查看具体用法。
Message
-
简介
topic有很严格的格式要求,比如上节的摄像头进程中的rgb图像topic,它就必然要遵循ROS中定义好的rgb图像格式。这种数据格式就是Message。==Message按照定义解释就是topic内容的数据类型,也称之为topic的格式标准。==这里和我们平常用到的Massage直观概念有所不同,这里的Message不单单指一条发布或者订阅的消息,也指定为topic的格式标准。 -
结构与类型
基本的msg包括bool、int8、int16、int32、int64(以及uint)、float、float64、string、time、duration、header、可变长数组array[]、固定长度数组array[C] 。那么具体的一个msg是怎么组成的呢?我们用一个具体的msg来了解,例如上例中的msg sensor_msg/image,位置存放在sensor_msgs/msg/image.msg里,它的结构如下:
std_msg/Header header
uint32 seq
time stamp
string frame_id
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data
上面msg的定义,是不是很类似C语言中的结构体呢?通过具体的定义图像的宽度,高度等等来规范图像的格式。所以这就解释了Message不仅仅是我们平时理解的一条一条的消息,而且更是ROS中topic的格式规范。或者可以理解msg是一个“类”,那么我们每次发布的内容可以理解为“对象”,这么对比来理解可能更加容易。 我们实际通常不会把Message概念分的那么清,通常说Message既指的是类,也是指它的对象。而msg文件则相当于类的定义了。
- 操作命令
rosmsg的命令相比topic就比较少了,只有两个如下:
rosmsg命令 | 作用 |
---|---|
rosmsg list | 列出系统上所有的msg |
rosmsg show msg_name | 显示某个msg的内容 |
- 小结
topic的通信方式是ROS中比较常见的单向异步通信方式,它在很多时候的通信是比较易用且高效的。但是有些需要交互的通信时该方式就显露出自己的不足之处了,后续我们会介绍双向同步的通信方式service。