机器人操作系统简介:终极机器人应用框架
已发表: 2022-03-11机器人操作系统(ROS) 不是一个实际的操作系统,而是一个框架和一组工具,可在异构计算机集群上提供操作系统的功能。 它的用途不仅限于机器人,而且提供的大多数工具都专注于使用外围硬件。
ROS 分为 2000 多个包,每个包都提供专门的功能。 连接到框架的工具数量可能是它最大的力量。
为什么要使用机器人操作系统?
ROS 为硬件抽象、设备驱动程序、多台机器上的进程之间的通信、测试和可视化工具等提供功能。
ROS 的关键特性是软件的运行方式和通信方式,允许您在不知道某些硬件如何工作的情况下设计复杂的软件。 ROS 提供了一种将进程(节点)网络与中央集线器连接的方法。 节点可以在多个设备上运行,并且它们以各种方式连接到该集线器。
创建网络的主要方式是提供可请求的服务,或定义与其他节点的发布者/订阅者连接。 两种方法都通过指定的消息类型进行通信。 一些类型由核心包提供,但消息类型可以由单独的包定义。
开发人员可以通过连接小问题的现有解决方案来组装一个复杂的系统。 该系统的实施方式允许我们:
即时更换具有相似接口的组件,无需停止系统进行各种更改
将多个组件的输出复用到另一个组件的一个输入中,允许并行解决各种问题
只需在消息系统中实现适当的连接器,即可连接以各种编程语言制作的组件,通过连接来自不同开发人员的现有模块,可以轻松开发软件
在设备网络上创建节点,无需担心代码在哪里运行并实现进程间通信 (IPC) 和远程过程调用 (RPC) 系统
通过使用前两个要点,直接从远程硬件按需连接到源,而无需编写任何额外的代码
我们计划通过迭代开发一个简单的解决方案来展示它的有用性。 与其他方法相比,有几个关键优势。 ROS 具有多平台支持,并允许通过在后台处理的点对点连接在多个设备上的进程之间进行连接。 该设计允许通过包装 C++ 通信类或手动开发语言接口类来支持任何语言。
ROS 是由它自己的社区制作的,是为了它的社区。 几年后,由于系统的架构,这导致了大量易于集成的可重用包。
MRPT、CARMEN、LCM、Player、Microsoft RDS 等替代方法提供了其中一些功能,但不是全部。 大多数情况下,设计失败是语言支持限制、进程之间未优化的通信或缺乏对各种设备的支持,这可以说是最难解决的问题。
我们要建造什么?
由于我们的重点是框架而不是特定问题的实际算法,因此给定的问题将相当简单。 我们的目标是为车载计算机构建软件,使我们能够远程控制和监控通过 Wi-Fi 连接到我们的机器人,方法是使用我们计算机上的游戏手柄和安装在机器人上的摄像头提供信息。
首先,我们将一个简单的程序连接到一个简单的模拟,只是为了演示 ROS 的基本原理。 我们将把游戏手柄连接到计算机上,并尝试设计一个好的控制方案,将游戏手柄输入转化为机器人的控制信号。
编写 ROS 代码的主要语言是 C++ 和 Python,由于性能更好,首选 C++。 由于代码中的样板较少并且不需要显式构建,我们将用 Python 解释我们的示例。
安装和配置
ROS 版本是按名称引用的。 截至目前,最新版本是Jade Turtle和最新的 LTS 版本Indigo Igloo 。 最好选择 LTS 版本,并且在 ROS 中不保证向后兼容性,因此所有示例都将针对Indigo编写。
ROS 可在各种 *NIX 平台上使用。 官方支持的版本在 Ubuntu 上。 社区支持 OS X、Arch Linux、Debian、Raspbian 和 Android 版本。
我们将在桌面上完成 Ubuntu 14.04 的安装过程。 所有支持的版本和平台的流程都可以在官方网站上找到。 也可以使用安装了 ROS 的虚拟机。
安装取决于平台(并且大多数平台都提供了软件包),但所有平台的工作区配置都是相同的。
在 Ubuntu 上安装
ROS 提供了自己的存储库。 第一步是添加它们。
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt-key adv --keyserver hkp://pool.sks-keyservers.net --recv-key 0xB01FA116 sudo apt-get update
之后,您将拥有适用于您的 Ubuntu 版本的所有 ROS 版本的所有托管包。 例如,Ubuntu 14.04 支持indigo
和jade
。
在桌面上安装基础包有以下三个选项之一:
sudo apt-get install ros-indigo-ros-base
进行最小安装sudo apt-get install ros-indigo-desktop
以获得基本的附加 GUI 工具sudo apt-get install ros-indigo-desktop-full
拥有所有官方功能,包括用于导航和感知的各种模拟器和库
为获得最佳工作体验,建议使用完整选项。 对于仅用于运行节点的设备上的安装,基本版本就足够了。 无论您选择什么选项,您都可以通过运行以下命令安装任何需要的名为package_name
的包:
sudo apt-get install ros-indigo-<package-name>
下划线被最终名称中的连字符替换,因此stage_ros
将位于包ros-indigo-stage-ros
中。
下一步是初始化rosdep
。 ROS 中的包可以声明它们所依赖的组件。 rosdep
允许您编译这些包而无需过多的手动依赖处理。 要初始化它,请调用:
sudo rosdep init rosdep update
ROS 有几个环境变量供其工具使用。 在默认安装中,初始化它们的 bash 脚本位于/opt/ros/indigo/setup.bash
中。 变量需要在每个 bash 会话中初始化,因此最好的解决方案是将它们添加到~/.bashrc
。
echo "source /opt/ros/indigo/setup.bash" >> ~/.bashrc source ~/.bashrc
一些软件包通过rosinstall
安装外部依赖项,它作为一个软件包提供并通过sudo apt-get install python-rosinstall
。
这是在 Ubuntu 上安装的结束。 以下是安装工作区的简短介绍。
配置
从Groovy Galapagos开始,ROS 工作空间就通过catkin
进行管理。 我们需要为我们托管的所有包定义一个目录。 在该目录中,我们创建一个src
文件夹,并在其中调用catkin_init_workspace
表单。 这将创建到当前来源的 ROS 版本的各种符号链接。 下一步是将此工作区也添加到环境变量中。
要执行整个工作区配置,请选择一个空目录并执行以下命令:
mkdir src cd src catkin_init_workspace cd .. catkin_make echo "source $(pwd)/devel/setup.bash" >> ~/.bashrc source ~/.bashrc
您现在已经创建了一个工作区,您可以在其中创建自己的 ROS 包。
熟悉工具
创建任何代码都是一个巨大的飞跃。 让我们首先熟悉一些在幕后运行的系统。 我们的第一步将是运行基本的 GUI 并查看它生成的消息。
要在 ROS 中运行任何东西,需要启动一个核心进程。 就像打开一个新的终端窗口并输入一样简单:
roscore
在您连接的整个设备网络中, roscore
只需在将托管中央集线器以进行通信调度的设备上启动一次。
roscore
的主要作用是告诉节点它们应该连接到哪些其他节点,以及以何种方式(无论是通过网络端口还是共享内存)。 目标是让节点只关心他们想要知道的数据,而不是他们想要连接的节点,同时最大限度地减少执行所有通信所需的时间和带宽。
rqt
运行roscore
后,我们可以启动 ROS 的主要 GUI 工具: rqt
。 我们看到的是非常平淡无奇的——一个空窗。 rqt
拥有各种各样的插件,可以配置为任何可视化配置和任意数量的预定义视图。
首先,让我们在Plugins > Robot Tools > Robot Steering
中选择它来运行Robot Steering插件。 我们得到的是两个滑块,代表我们希望机器人具有的线性和旋转运动。 在插件的顶部,我们看到一个带有/cmd_vel
的文本框。 我们可以将其重命名为我们想要的任何名称。 它表示转向发布到的主题的名称。 终端工具是查看后台发生的事情的最佳位置。
终端工具
ROS 有几个强大的工具来检查系统中发生的事情。 我们要介绍的第一个工具是rostopic
。 它允许我们检查节点可以订阅和发布的主题。 运行rostopic list
将产生:
/cmd_vel /rosout /rosout_agg
后两个主题始终在运行,并且与中央 ROS 系统相关。 /cmd_vel
主题正在由我们的指导发布。 在转向中重命名主题也会在此处重命名。 现在,我们对主题内发生的事情感兴趣。 运行rostopic echo /cmd_vel
不会向我们显示任何内容(除非您修改了滑块)。 该过程一直运行,直到我们取消它。 现在让我们将垂直滑块移动到 20 m/s。 查看回声,我们可以看到以下内容一遍又一遍地重复:
linear: x: 0.2 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0
它多久发送一次垃圾邮件? rostopic hz /cmd_vel
以 10 Hz 的平均速率表示。 好吧,我可以通过慢速 Wi-Fi 连接运行多少这样的主题? rostopic bw /cmd_vel
检测到平均 480 B/s。
现在一切都很好,但是我们谈到了消息类型。 这些数据对人类有好处,但应用程序需要原始数据,并且需要知道消息类型以便解释数据。 类型可以用rostopic type /cmd_vel
来确定,告诉我们它是一个geometry_msgs/Twist
。 所有不带任何参数调用的 ROS 终端工具都会返回标准帮助消息。
ROS Wiki 足以对这个字符串进行网络搜索,从而在 Wiki 中解释它包含的内容以及它的结构。 但我们不必依赖它。 rosmsg
是消息类型的通用工具。 运行rosmsg show geometry_msgs/Twist
将返回:
geometry_msgs/Vector3 linear float64 x float64 y float64 z geometry_msgs/Vector3 angular float64 x float64 y float64 z
该消息由两个 3D 矢量组成,分别代表 3D 空间中的线速度和角速度。
如果我们想要一个节点连接到哪些主题, rosnode info <node-name>
将为我们提供有关该节点的详细数据。 rostopic
、 rosmsg
和rosnode
工具是检查原始 ROS 功能的主要工具。 ROS 有更多的 GUI 和终端工具,但这些超出了我们本次介绍的范围。
运行 ROS 节点的主要工具是rusrun
和roslaunch
。 rosrun
可以通过rosrun <package_name> <node_name>
运行节点,而roslaunch
基于启动文件运行节点,我们会稍微熟悉一下,因为它们是 ROS 自动化中最复杂的元素。
我们可以关闭我们运行的所有东西,开始处理我们的第一个代码。 为了将来参考,不用说运行任何与 ROS 相关的东西都需要一个活动的roscore
实例。 你遇到的很多问题都可以通过关闭运行roscore
的终端窗口来解决,然后打开一个新窗口重新启动它。 这会更新bash
和roscore
中需要重新加载的所有依赖项。
创建游戏手柄远程操作
我们的第一个目标是通过创建一个节点来模仿Robot Steering
的功能,该节点根据游戏手柄输入将geometry_msgs/Twist
数据发布到/cmd_vel
。 我们的第一站是joy
套餐。
joy
礼包
joy
包为游戏杆和游戏手柄提供了通用的 ROS 驱动程序。 它不包含在默认安装中,因此需要通过以下方式安装:
sudo apt-get install ros-indigo-joy
安装完成后,我们可以运行rosrun joy joy_node
。 这会将我们连接到默认的操纵杆或游戏手柄。 运行rostopic list
显示我们有一个名为/joy
的主题。 通过rostopic echo
收听它会向我们显示以下格式的消息(请注意,您必须与游戏手柄或操纵杆交互才能发布消息)。
header: seq: 4156 stamp: secs: 1450707466 nsecs: 204517084 frame_id: '' axes: [0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0] buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
您现在可以忽略标题。 除此之外,我们还有axes
和buttons
,很好地解释了它们代表什么。 移动轴和按下控制器上的按钮将导致这些数字发生变化。 使用我们的工具,我们可以确定消息类型为sensor_msgs/Joy
,格式为:
std_msgs/Header header uint32 seq time stamp string frame_id float32[] axes int32[] buttons
创建我们的远程操作
编写代码的第一步是制作一个包。 在工作区的src
文件夹中,运行:
catkin_create_pkg toptal_tutorial rospy joy geometry_msgs sensor_msgs
在这里,我们说明我们正在创建的包的名称,然后是我们计划依赖的包。 不用担心,以后可以手动更新依赖项。

我们现在有一个toptal_tutorial
文件夹。 在文件夹中,创建一个包含所有 Python 脚本的scripts
文件夹。
让我们创建一个名为teleop.py
的文件,并在其中设置:
#!/usr/bin/env python import rospy from sensor_msgs.msg import Joy def joy_callback(data): print data def main(): rospy.init_node('teleop') rospy.Subscriber('joy', Joy, joy_callback) while not rospy.is_shutdown(): pass if __name__ == '__main__': main()
我们还需要设置chmod +x teleop.py
以便脚本可以运行。 在一个终端运行rosrun joy joy_node
并在另一个终端运行 rosrun rosrun toptal_tutorial teleop.py
将导致teleop.py
的终端输出充满 Joy 消息。
让我们检查一下代码的作用。
首先,我们导入 rospy,它托管用于与 ROS 框架交互的库。 每个定义消息的包都有一个msg
子包,其中包含消息定义。 我们正在导入Joy
来处理输入。 除非我们想明确提及它们,否则无需导入嵌入的消息类型(例如Joy
消息中的std_msgs.msg
中的Header
)。
我们的第一步是初始化一个具有特定名称的节点(在这种情况下,我们称之为“teleop”)。 之后,我们创建一个订阅者,订阅sensor_msgs.msg.Joy
类型的“joy”主题,并通过调用joy_callback
函数来处理每条消息。 回调接收一个参数,即消息中的数据。 访问数据成员很简单。 如果我们想打印第一个轴的状态,如果我们回忆消息类型,我们会调用print data.axes[0]
,它会是一个浮点数。 最后的循环循环直到 ROS 被关闭。
我们的下一步是以某种方式处理我们的数据。 我们应该创建一个根据输入变化的 Twist 消息,然后我们会将其发布到cmd_vel
主题。
#!/usr/bin/env python import rospy from sensor_msgs.msg import Joy from geometry_msgs.msg import Twist # new from functools import partial # new def joy_callback(pub, data): # modified cmd_vel = Twist() # new cmd_vel.linear.x = data.axes[1] # new cmd_vel.angular.z = data.axes[0] # new pub.publish(cmd_vel) # new def main(): rospy.init_node('teleop') pub = rospy.Publisher('cmd_vel', Twist, queue_size=1000) # new rospy.Subscriber('joy', Joy, partial(joy_callback, pub)) # modified while not rospy.is_shutdown(): pass if __name__ == '__main__': main()
首先,我们添加Twist
消息,并通过functools.partial
添加对绑定函数参数的支持。 我们创建一个发布者pub
,它向cmd_vel
发布Twist
类型的消息。 我们将该发布者绑定到回调,并使其在每个输入上发布一条 Twist 消息,其中速度由前两个轴表示。 这段代码符合我们的预期,我们可以通过rostopic echo /cmd_vel
查看结果输出。
我们还有一个问题。 /joy
主题可以以很高的速度发布。 如果我们监控rostopic hz /cmd_vel
并将模拟摇杆绕圈移动,我们可以看到大量消息。 这不仅会导致大量通信,而且接收这些消息的进程必须处理每一个消息。 没有必要如此频繁地发布这些数据,我们最好以 10 Hz 的稳定速率发布。 我们可以用下面的代码来完成。
#!/usr/bin/env python import rospy from sensor_msgs.msg import Joy from geometry_msgs.msg import Twist from functools import partial def joy_callback(cmd_vel, data): # modified cmd_vel.linear.x = data.axes[1] cmd_vel.angular.z = data.axes[0] # moved pub.publish(cmd_vel) to main loop def main(): rospy.init_node('teleop') cmd_vel = Twist() # new pub = rospy.Publisher('cmd_vel', Twist, queue_size=1000) rospy.Subscriber('joy', Joy, partial(joy_callback, cmd_vel)) # modified rate = rospy.Rate(10) # new while not rospy.is_shutdown(): pub.publish(cmd_vel) # new rate.sleep() # new if __name__ == '__main__': main()
我们修改了回调以接收可变的Twist
对象并在循环中对其进行修改。 rospy.Rate
的sleep
功能保持稳定的输出频率。
最终代码将导致/cmd_vel
主题以 10 Hz 获得速度命令,模仿Robot Steering rqt
插件的输出。
运行模拟系统
模拟世界
我们的第一个目标是创建一个可以模拟我们想要实现的场景的环境。 stage_ros
包中的节点stageros
允许我们在通过图像定义的 2D 阶段内运行一个机器人。 在世界文件的stage_ros
包中描述了完整的语法以及如何生成它们。 这相当简单,但超出了我们的范围。 幸运的是,该软件包附带了几个演示世界。 首先,让我们运行以下命令进入文件目录:
roscd stage_ros cd world
在文件夹中有几个文件。 让我们运行一个。
rosrun stage_ros stageros willow-erratic.world
这创造了几个话题。 它们中的每一个的含义也记录在包中。 重要的是它有cmd_vel
。
在显示的舞台内,有一个蓝色方块,代表您控制的机器人。 通过使用我们的代码或Robot Steering ,我们可以控制这个机器人。 试试看。
通过启动文件设置我们的系统
让我们在我们的包中创建一个launch
文件夹,并在其中创建一个名为teleop.launch
的文件。 最终的文件夹结构应如下所示:
toptal_tutorial/ ├── CMakeLists.txt ├── launch │ └── teleop.launch ├── package.xml ├── scripts │ └── teleop.py └── src
在teleop.launch
文件中,我们将定义一组节点及其互连。
<launch> <arg name="world_file" default="$(find stage_ros)/world/willow-four-erratics-multisensor.world" /> <node pkg="stage_ros" type="stageros" name="simulated_world" args="$(arg world_file)"></node> <group ns="robot_0"> <node pkg="joy" type="joy_node" name="joy_input"></node> <node pkg="toptal_tutorial" type="teleop.py" name="joy_convert"></node> </group> </launch>
新世界由四个机器人组成,每个主题都有一个前缀robot_<n>
。 因此,0 号机器人有一个名为robot_0/cmd_vel
的速度命令主题。 这就是为什么我们将控件放在名为robot_0
的命名空间中,以将它们的名称调整为新形式。 从这个意义上说,您可以将主题名称视为文件系统中的文件夹。
要运行启动文件,不需要roscore
。 从某种意义上说, roscore
只是一个什么都不做的启动文件的特例。 如果缺少roscore
,则只有启动的第一个启动文件将运行核心,而其余的将连接到它。 现在,我们使用以下命令运行启动:
roslaunch toptal_tutorial teleop.launch
如果一切正确,这将产生一个有 4 个机器人的模拟器,其中一个由我们的游戏手柄或操纵杆控制。 这个世界比以前的世界有更多的东西。 四个机器人中的每一个都具有:
/robot_<n>/base_pose_ground_truth /robot_<n>/base_scan_0 /robot_<n>/base_scan_1 /robot_<n>/camera_info_0 /robot_<n>/camera_info_1 /robot_<n>/cmd_vel /robot_<n>/depth_0 /robot_<n>/depth_1 /robot_<n>/image_0 /robot_<n>/image_1 /robot_<n>/odom
我们将<n>
替换为 0、1、2 或 3。这将我们带到了最后一个主题。
使用rqt
查看我们的数据
我们没有深入rqt
,但它是查看更复杂数据的完美工具。 您可以尝试所有主题,但我们将重点关注image_0
、 image_1
、 depth_0
和depth_1
主题。
让我们启动rqt
并删除所有打开的插件。 现在我们将打开 4 个图像可视化工具( Plugins > Visualization > Image View
),并将它们以 2x2 网格形式放置。 最后,在每个视图的左上角,让我们为robot_0
选择四个陈述的主题之一。
我们得到的是具有深度感知的立体视觉,带有低分辨率相机。 请记住,如果没有输入系统,我们甚至可以得到这个结果。 如果我们只是运行它(从stage_ros/world
文件夹中):
rosrun stage_ros stageros willow-four-erratics-multisensor.world
并添加带有名为/robot_0/cmd_vel
主题的机器人转向插件,如果控件是屏幕上的滑块,我们会得到相同的结果。
将结果应用于实际系统
许多硬件都完全支持 ROS,通常由第三方志愿者提供。 许多机器人平台具有生成这些类型消息的驱动程序,而 ROS 具有接收网络摄像头并发布图像馈送的节点。
虽然最后一个结果是我们想要实现的模拟,但同样可以通过以下修改来实现:
- 在机器人的板载计算机上安装 ROS
- 为将 ROS 连接到底层平台和所有高级传感器(如相机、激光测距仪等)的车载计算机创建一个启动文件。 所需的节点可能已经存在,或者可以通过在一侧创建 ROS 的发布者/订阅者和在另一侧创建串行通信驱动程序来实现
- 让启动文件在启动时运行
- 在您的远程计算机上,将
export ROS_MASTER_URI=http://<robot_hostname>:11311/
添加到您的 bash 启动中,使远程计算机在给定的主机名和端口上查找roscore
- 启动
rqt
和/或任何用于监视和控制机器人的脚本
这真正归结为只是在远程设备上导出正确的环境变量,其余的自行处理。 在计算机集群上运行 ROS 只需要为每台机器完成一个步骤。
结论
我们已经展示了如何通过很少的编码就可以拥有一个复杂的变量系统,您可以随心所欲地对其进行操作。 简单的发布者/订阅者系统允许您快速开发在计算机集群中处理数据的软件管道,而无需担心某些元素的底层实现。
虽然我们使用了一个简单的模拟器,但更复杂的模拟器(如gazebo
)(也包含在完整的桌面版本中)允许您使用物理和复杂的传感器创建 3D 世界,并且可以在最终结果和产品开发之前就让您体验它。
这个介绍是一个非常基本的介绍,但希望您对使用这个多功能框架更加感兴趣。