ROS介绍
ROS是一个适用于机器人的开源的元操作系统。它不是一个真正的操作系统,但它提供了操作系统应用的服务,包括硬件抽象,底层设备控制,常用函数的实现,进程间消息传递,以及包管理。它也提供用于获取、 编译、编写、和跨计算机运行代码所需的工具和库函数。 它主要采用的是松耦合点对点进程网络。
设计思想
设计思想主要是分布式架构,将机器人的功能和软件,做成一个个节点,然后每个节点通过topic进行沟通,但你这些节点可以部署在同一台机器上,也可以部署在不同机器上,还可以部署在互联网上。
核心概念
1. Nodes(节点)
节点是各自独立的可执行文件,能够通过话题、服务或参数,与服务器或其他进程(节点)通信。ROS通过使用节点的方式将代码和功能解耦,提高了系统容错能力和可维护性,使系统简化。同时,节点允许了ROS系统能够布置在任意多个机器上并同时运行。关于节点需要注意的事项,节点在系统中必须有唯一的名称;节点可以使用不同的库进行编写,如roscpp和rospy,其中roscpp基于C++,rospy基于Python。
2. Messages and Topics(消息与话题)
节点之间通过topic机制进行通信,topic机制是一个一对多的Publish/Subscribe 模式: 同一个话题也可以有很多个订阅者,它的底层传输依靠的是TCP/IP,也可以是UDP。topic具体传输的message,具有一定的类型和数据结构,包括ROS提供的标准类型,和用户自定义类型。
3. Services and Parameters(服务与参数)
除了topic,ROS还提供另一种一对一的机制,也就是Service/Client,当你需要直接与节点通信并获得应答时,将无法通过话题实现,这时需要使用该服务。
4. ROS Master(ROS管理器)
Master向ROS系统中其他节点提供命名和注册服务,跟踪和记录话题的发布者和订阅者,使ROS 节点之间能够相互查找。一旦节点找到了彼此,就能建立一种点对点的通信方式。
5. Stacks and packages(堆栈与功能包)
那么如何组织代码呢?这主要依靠功能包(Package) ,ROS中软件组织的基本形式,用于创建ROS程序。功能包包含源代码和功能包清单(Manifest) 。功能包清单提供关于功能包、许可信息、依赖关系、编译标志等的信息。功能包清单是一个manifests.xml文件,通过这个文件能够实现对功能包的管理。
核心模块
ROS核心模块包括通信结构基础、机器人特性功能以及工具集,通信结构基础包括消息传递、记录和回放消息、远程过程调用、分布式参数系统;机器人特性功能包括标准机器人消息,机器人几何库,机器人描述语言,抢占式远程过程调用,诊断,位资估计、定位与导航;工具集包括命令式工具、可视化工具以及图形化接口。
安装配置ROS环境
- 安装直接跟着教程走就行,参见官方安装教程。
- 配置环境,就是将
source /opt/ros/<distro>/setup.bash
添加到~/.bashrc文件中就行。 - 创建ROS工作空间,有两种方式:catkin和rosbuild。catkin比较新且比较灵活,rosbuild比较旧但是简单。鉴于rosbuild慢慢可能不维护,还是选择用catkin比较好。
123456# 创建工作区mkdir -p ~/catkin_ws/srccd ~/catkincatkin_make# 执行后会在工作区下面生成了一个devel,build文件夹,以及在src中生成了一个CMakeLists.txt的链接文件
上面生成的devel文件夹中会有几个setup.*sh文件,source这些文件中对应的shell文件,就可以将当前工作区设置在ROS工作环境的最顶层,查看是否将当前工作区设置成最顶层(每次新开终端都source一下):
123456echo $ROS_PACKAGE_PATH# 没有执行source输出是下面这样的/opt/ros/kinetic/share# 执行source以后输出是下面这样的/home/lm/catkin_ws/src:/opt/ros/kinetic/share
至此,工作做环境搭建完成了。再来看一看什么是catkin工作区(catkin workspace)?catkin工作区就是一个文件夹,在这个文件夹里面modify,build和install catkin package,下面是catkin workspace的建议也是典型的工作区文件树:
12345678910111213141516171819202122232425262728293031323334353637workspace_folder/ -- WORKSPACEsrc/ -- SOURCE SPACECMakeLists.txt -- The 'toplevel' CMake filepackage_1/CMakeLists.txtpackage.xml...package_n/CATKIN_IGNORE -- Optional empty file to exclude package_n from being processedCMakeLists.txtpackage.xml...build/ -- BUILD SPACECATKIN_IGNORE -- Keeps catkin from walking this directorydevel/ -- DEVELOPMENT SPACE (set by CATKIN_DEVEL_PREFIX)bin/etc/include/lib/share/.catkinenv.bashsetup.bashsetup.sh...install/ -- INSTALL SPACE (set by CMAKE_INSTALL_PREFIX)bin/etc/include/lib/share/.catkinenv.bashsetup.bashsetup.sh...一个工作区最多可以包括四个不同的子工作区:
Source Space:用来存放源码;
Build Space:在Source Space中调用CMake构建catkin包;
Development (Devel) Space:在安装之前放置已构建的目标;
Install Space:build完成后可以调用安装到Install Space中,通常使用make install;
Result space:When ever referring to a folder which can either be a development space or an install space the generic term result space is used,贴上原文,应该用的不多。
ROS文件系统介绍
两个文件系统的概念:
- Packages: 软件包,是ROS应用程序代码的组织单元,每个软件包都可以包含程序库、可执行文件、脚本或者其它手动创建的东西。
- Manifest (package.xml): 清单,是对于’软件包’相关信息的描述,用于定义软件包相关元信息之间的依赖关系,这些信息包括版本、维护者和许可协议等。
几个文件系统的工具:rosbash
rospack
命令:rospack可以用来获取包的相关信息。这里只用到了rospack
中find
参数选项,该选项可以返回软件包的路径信息。
1234567# 用法rospack find [包名称]# 比如获取roscpp软件包的路径信息rospack find roscpp# 输出YOUR_INSTALL_PATH/share/roscpproscd
命令:roscd
是rosbash命令集中的一部分,它允许你直接切换工作目录到某个软件包或者软件包集当中。这个命令就像ubuntu中的cd命令,但是后面接的命令是包名。就像ROS中的其它工具一样,roscd只能切换到那些路径已经包含在ROS_PACKAGE_PATH环境变量中的软件包。
12roscd [本地包名称[/子目录]]rosls
命令:rosls
是rosbash命令集中的一部分,它允许你直接按软件包的名称而不是绝对路径执行ls命令(罗列目录)。用法和注意问题和roscd一样。
12rosls [本地包名称[/子目录]]
创建ROS程序包
一个catkin程序包由以下组成:
- 该程序包必须包含catkin compliant package.xml文件,这个package.xml文件提供有关程序包的元信息。
- 程序包必须包含一个catkin 版本的CMakeLists.txt文件,而Catkin metapackages中必须包含一个对CMakeList.txt文件的引用。
- 每个目录下只能有一个程序包,也就是说在同一个目录下不能有嵌套的或者多个程序包存在。
最简单的程序包看起来像下面这样:
1 2 3 4 |
my_package/ CMakeLists.txt package.xml |
在catkin工作空间中的程序包可能看着想下面这样:
1 2 3 4 5 6 7 8 9 10 11 |
workspace_folder/ -- WORKSPACE src/ -- SOURCE SPACE CMakeLists.txt -- 'Toplevel' CMake file, provided by catkin package_1/ CMakeLists.txt -- CMakeLists.txt file for package_1 package.xml -- Package manifest for package_1 ... package_n/ CMakeLists.txt -- CMakeLists.txt file for package_n package.xml -- Package manifest for package_n |
如何创建catkin程序包?
1 2 3 4 5 6 7 |
# 首先进入catkin工作区中的src目录下 cd ~/catkin/src # 然后执行下面的命令创建catkin程序包,前面是包名,后面是可选的包的相关依赖包 catkin_create_pkg <package_name> [depend1] [depend2] [depend3] # 比如下面创建一个名为'tutorials'依赖于std_msgs,roscpp,rospy的新程序包 catkin_create_pkg tutorials std_msgs rospy roscpp |
程序包之间的依赖
- 一级依赖,使用上面命令创建程序包时候指定的依赖都保存在package.xml中;可以使用rospack命令工具来查看程序包的一级依赖。
123456rospack depends1 tutorials# 输出std_msgsrospyroscpp - 间接依赖包,一个程序包可能依赖的包又依赖其他的包,可以用rospack命令递归的检测出所有依赖的包。
1234567891011121314151617181920rospack depends tutorials# 输出cpp_commonrostimeroscpp_traitsroscpp_serializationgenmsggenpymessage_runtimerosconsolestd_msgsrosgraph_msgsxmlrpcpproscpprosgraphcatkinrospackroslibrospy
自定义程序包
- 自定义生成的package.xml,弄清楚几个标签的含义。
- 自定义CMakeLists.txt
编译ROS程序包
用catkin make编译ROS程序包
传统CMake标准工作流程:
1 2 3 4 5 6 7 |
# 在一个CMake项目里 mkdir build cd build cmake .. make make install # (可选) |
而在ROS中多个catkin项目放在一个工作区中一起编译流程:
1 2 3 4 5 6 7 |
# In a catkin workspace 在默认src文件夹下 catkin_make catkin_make install # (可选) # 上面的命令会编译工作区src文件夹下所有工程,如果源码不在该目录下而在my_src中,则按如下编译 catkin_make --source my_src catkin_make install --source my_src # (可选) |
编译完成以后像上面创建工作区一样在目录下有三个文件夹:build,devel和src。build 目录是build space的默认所在位置,同时cmake 和 make也是在这里被调用来配置并编译你的程序包。devel 目录是devel space的默认所在位置, 同时也是在你安装程序包之前存放可执行文件和库文件的地方。
理解ROS节点
图概念概述:
- Nodes:节点,一个节点即为一个可执行文件,它可以通过ROS与其它节点进行通信。
- Messages:消息,消息是一种ROS数据类型,用于订阅或发布到一个话题。
- Topics:话Topics题,节点可以发布消息到话题,也可以订阅话题以接收消息。
- Master:节点管理器,ROS名称服务 (比如帮助节点找到彼此)。
- rosout: ROS中相当于stdout/stderr。
- roscore: 主机+ rosout + 参数服务器 (参数服务器会在后面介绍)。
主要介绍了3个命令:
roscore
命令:roscore是运行所有ROS程序之前首先要运行的命令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# 执行roscore命令 roscore # 输出 ... logging to ~/.ros/log/9cf88ce4-b14d-11df-8a75-00251148e8cf/roslaunch- machine_name-13039.log Checking log directory for disk usage. This may take awhile. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://machine_name:33919/ ros_comm version 1.4.7 SUMMARY ======== PARAMETERS * /rosversion * /rosdistro NODES auto-starting new master process[master]: started with pid [13054] ROS_MASTER_URI=http://machine_name:11311/ setting /run_id to 9cf88ce4-b14d-11df-8a75-00251148e8cf process[rosout-1]: started with pid [13067] started core service [/rosout] |
执行完看到下面的输出started core service表示启动了roscore,如果没有正常启动,可能是网络配置问题。
如果提示权限问题,可能是~/.ros文件夹是root用户的,将归属关系改成当前用户就行。
必须先正确启动roscore再运行其他的ros程序,并且运行roscore以后,让终端一直后台运行着。
rosnode
命令:节点相关的命令
1 2 3 4 5 6 7 |
# 显示当前运行的ROS节点 rosnode list # 返回一个节点的信息 rosnode info [节点名] # 比如查看rosout节点信息 rosnode info rosout |
rosrun
命令:允许你使用包名直接运行一个包内的节点(而不需要知道这个包的路径).
1 2 3 4 5 6 7 |
# 用法 rosrun [package_name] [node_name] # 运行一个小乌龟教程,这是这个系列教程上的一个节点,教程后面都是用这个小乌龟做例子 rosrun turtlesim turtlesim_node # 另外在运行是可以人为指定节点的名称,在roslist的时候就会是新的名称,比如 rosrun turtlesim turtlesim_node __name:=my_turtle |
ROS话题
先来一个用键盘移动小乌龟的例子:
1 2 3 4 5 6 7 |
# 开启roscore roscore # 运行一个小乌龟节点 rosrun turtlesim turtlesim_node # 运行一个键盘控制节点,然后可以用键盘的方向键控制小乌龟的运动 rosrun turtlesim turtle_teleop_key |
turtlesim_node节点和turtle_teleop_key节点之间是通过一个ROS话题来互相通信的。turtle_teleop_key在一个话题上发布按键输入消息,而turtlesim则订阅该话题以接收该消息。
rqt_graph
节点:创建一个显示当前系统运行情况的动态图形,rqt_graph是rqt程序包中的一部分,执行如下命令安装(早期的版本,rqt可能不可用,使用rxgraph
代替):
1 2 3 |
sudo apt-get install ros-<distro>-rqt sudo apt-get install ros-<distro>-rqt-common-plugins |
在终端中运行rqt_graph命令查看当前节点通信图:
1 2 |
rosrun rqt_graph rqt_graph |
然后会看到节点通信图:
上图表示turtlesim_node和turtle_teleop_key节点正通过一个名为 /turtle1/command_velocity的话题来互相通信。
ROS Topic
rostopic
命令:这个命令工具可以获取ROS话题有关的信息。
1 2 3 4 5 6 7 8 9 10 |
# 使用rostopic命令-h选项查看rostopic子命令 rostopic -h # 输出 rostopic bw display bandwidth used by topic rostopic echo print messages to screen rostopic hz display publishing rate of topic rostopic list print information about active topics rostopic pub publish data to topic rostopic type print topic type |
上面常用的几个命令:
1 2 3 4 5 |
# echo显示某个话题上发布的数据 rostopic echo [topic] # list列出所有当前订阅和发布的话题 rostopic list -v # 使用-v选项显示有关发布和订阅的话题及类型的详细信息 |
ROS Messages
话题中间的通信是通过节点之间的ROS消息实现的。对于发布器(turtle_teleop_key)和订阅器(turtulesim_node)之间的通信,发布器和订阅器之间必须发送和接收相同类型的消息。这意味着话题的类型是由发布在它上面的消息类型决定的。使用rostopic type命令可以查看发布在某个话题上的消息类型。
rostopic type
命令:用来显示所发布的话题的消息类型
1 2 3 4 5 6 7 8 9 |
# 用法 rostopic type [topic] # 查看上面的/turtle1/cmd_vel话题 rostopic type /turtle1/cmd_vel # 输出 geometry_msgs/Twist # 使用rosmsg命令查看小心详细情况 rosmsg show geometry_msgs/Twist |
rostopic pub
命令:可以把数据发布到当前某个正在广播的话题上
1 2 3 4 5 6 7 |
# 用法 rostopic pub [topic] [msg_type] [args] # 以下命令会发送一条消息给turtlesim,告诉它以2.0大小的线速度和1.8大小的角速度开始移动 rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]' # 下面这条命令和上面一样,但是小乌龟会一直移动,-r表示以频率为1Hz发布稳定的消息流 rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]' |
-1
:这个参数选项使rostopic发布一条消息后马上退出
/turtle1/cmd_vel
:这是消息所发布到的话题名称
geometry_msgs/Twist
:这是所发布消息的类型
--
:告诉命令选项解析器接下来的参数部分都不是命令选项
rostopic hz
命令:用来查看数据发布的频率
1 2 3 |
# 用法 rostopic hz [topic] |
rqt_plot
节点:可以实时显示一个发布到某个话题上的数据变化图形。
1 2 3 |
# 用法 rosrun rqt_plot rqt_plot |
理解ROS服务和参数
ROS Services
服务(services)是节点之间通讯的另一种方式。服务允许节点发送请求(request) 并获得一个响应(response).
rosservice
命令:rosservice提供了很多可以在topic上使用的命
1 2 3 4 5 6 7 |
# 使用方法 rosservice list 输出可用服务的信息 rosservice call 调用带参数的服务 rosservice type 输出服务类型 rosservice find 依据类型寻找服务find services by service type rosservice uri 输出服务的ROSRPC uri |
rosservice list
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 运行rosservice list rosservice list # 输出 /clear /kill /reset /rosout/get_loggers /rosout/set_logger_level /spawn /teleop_turtle/get_loggers /teleop_turtle/set_logger_level /turtle1/set_pen /turtle1/teleport_absolute /turtle1/teleport_relative /turtlesim/get_loggers /turtlesim/set_logger_level |
list
命令显示turtlesim节点提供了9个服务:重置(reset)
, 清除(clear)
, 再生(spawn)
, 终止(kill)
, turtle1/set_pen
, /turtle1/teleport_absolute
, /turtle1/teleport_relative
, turtlesim/get_loggers
, and turtlesim/set_logger_level
. 同时还有另外两个rosout
节点提供的服务: /rosout/get_loggers
and /rosout/set_logger_level
.
rosservice type
命令:
1 2 3 4 5 6 7 8 |
# 用法 rosservice type [service] # 查看clear服务的类型 rosservice type clear # 输出 std_srvs/Empty # 服务的类型为空(empty),这表明在调用这个服务是不需要参数(比如,请求不需要发送数据,响应也没有数据) |
rosservice call
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 用法 rosservice call [service] [args] # 调用clear服务,服务类型是空所以进行无参数调用 rosservice call clear # 调用该命令以后,之前小乌龟的运动轨迹都清楚了,小乌龟回到了起始位置 # 查看spawn服务的信息,了解带参数服务的调用 rosservice type spawn| rossrv show # 输出 float32 x float32 y float32 theta string name --- string name # 服务表明给定的位置和角度生成一只新的乌龟,名字参数是可选的,所以不人为指定让自动指定 rosservice call spawn 2 2 0.2 "" # 调用spawn服务后,在之前的小乌龟上面又有一个新的小乌龟 |
ROS Param
rosparam
使得我们能够存储并操作ROS 参数服务器(Parameter Server)上的数据。参数服务器能够存储整型、浮点、布尔、字符串、字典和列表等数据类型。rosparam使用YAML标记语言的语法。一般而言,YAML的表述很自然:1
是整型, 1.0
是浮点型, one
是字符串, true
是布尔, [1, 2, 3]
是整型列表, {a: b, c: d}
是字典。如下所示:
1 2 3 4 5 6 7 8 |
# 用法 rosparam set 设置参数 rosparam get 获取参数 rosparam load 从文件读取参数 rosparam dump 向文件中写入参数 rosparam delete 删除参数 rosparam list 列出参数名 |
rosparam list
命令:
1 2 3 4 5 6 7 8 9 10 |
# 查看当前参数服务器上有哪些参数 rosparam list # 输出 /background_b /background_g /background_r /roslaunch/uris/aqy:51932 /run_id # 上面turtlesim节点在参数服务器上有3个参数用于设定背景颜色 |
rosparam set
和rosparam get
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 用法 rosparam set [param_name] rosparam get [param_name] # 修改背景颜色的红色通道 rosparam set background_r 150 # 调用清除服务使得修改后的参数生效 rosservice call clear # 然后现在之前那个小乌龟的背景色变化了 # 获取背景的绿色通道的值 rosparam get background_g # 使用rosparam get /来显示参数服务器上的所有内容 rosparam get / # 输出 background_b: 255 background_g: 86 background_r: 150 roslaunch: uris: {'aqy:51932': 'http://aqy:51932/'} run_id: e07ea71e-98df-11de-8875-001b21201aa8 |
rosparam dump
和rosparam load
命令:存储这些参数服务器信息以备今后重新读取
1 2 3 4 5 6 7 8 9 10 11 12 |
# 用法 rosparam dump [file_name] rosparam load [file_name] [namespace] # 将所有的参数写入params.yaml文件 rosparam dump params.yaml # 将yaml文件重载入新的命名空间,比如说copy空间 rosparam load params.yaml copy # 检查background_b参数值 rosparam get copy/background_b # 输出 255 |
使用rqt_console和roslaunch
rqt_console
和rqt_logger_level
工具:这两个工具可以用来进行调试。
rqt_console属于ROS日志框架(logging framework)的一部分,用来显示节点的输出信息。
rqt_logger_level允许我们修改节点运行时输出信息的日志等级(logger levels)(包括 DEBUG、WARN、INFO和ERROR)。
1 2 3 4 5 |
# 用法 rosrun rqt_console rqt_console rosrun rqt_logger_level rqt_logger_level # 上面命令会打开两个窗口rqt_console和rqt_logger_level |
上面的调试工具,没有必要深究,先用起来再来弹调试,直接看下面的roslaunch命令。
roslaunch命令
roslaunch可以用来启动定义在launch文件中的多个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 用法 roslaunch [package] [filename.launch] # 下面用这个命令启动多个node # roscd到之前自己创建的roscd beginner_tutorialspackage下面 roscd beginner_tutorials # 如果roscd执行失败了,设置当前终端下的ROS_PACKAGE_PATH环境变量,然后就能roscd了 source ~/catkin_ws/devel/setup.bash export ROS_PACKAGE_PATH=~/<distro>_workspace/sandbox:$ROS_PACKAGE_PATH # 然后创建一个launch文件夹 mkdir launch cd launch |
创建Launch文件
创建一个名为turtlemimic.launch的launch文件并写入一下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<launch> <group ns="turtlesim1"> <node pkg="turtlesim" name="sim" type="turtlesim_node"/> </group> <group ns="turtlesim2"> <node pkg="turtlesim" name="sim" type="turtlesim_node"/> </group> <node pkg="turtlesim" name="mimic" type="mimic"> <remap from="input" to="turtlesim1/turtle1"/> <remap from="output" to="turtlesim2/turtle1"/> </node> </launch> |
现在分析一下上面的Launch文件
1 2 |
<launch> |
上面<launch>
标签表示这是一个launch文件
1 2 3 4 5 6 7 8 |
<group ns="turtlesim1"> <node pkg="turtlesim" name="sim" type="turtlesim_node"/> </group> <group ns="turtlesim2"> <node pkg="turtlesim" name="sim" type="turtlesim_node"/> </group> |
上面表示这里创建了两个节点分组并以’命名空间(namespace)’标签来区分,其中一个名为turtulesim1,另一个名为turtlesim2,两个组里面都使用相同的turtlesim节点并命名为’sim’。这样可以让我们同时启动两个turtlesim模拟器而不会产生命名冲突。
1 2 3 4 5 |
<node pkg="turtlesim" name="mimic" type="mimic"> <remap from="input" to="turtlesim1/turtle1"/> <remap from="output" to="turtlesim2/turtle1"/> </node> |
在这里启动模仿节点,并将所有话题的输入和输出分别重命名为turtlesim1和turtlesim2,这样就会使turtlesim2模仿turtlesim1。
roslaunching:现在开始用roslaunch命令来启动launch文件
1 2 3 4 5 6 7 8 |
# 用法 # 如果roslaunch失败记得像上面roscd失败一样,是指当前终端下的环境变量 roslaunch beginner_tutorials turtlemimic.launch # 执行完后,会启动两个小乌龟的窗口 # 执行下面的命令后,两个小乌龟同时做圆周运动 rostopic pub /turtlesim1/turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, -1.8]' # 可以使用rqt_graph工具来查看话题节点图,了解消息传递情况 |
使用rosed编辑ROS中的文件
rosed
是 rosbash的一部分。利用它可以直接通过package名来获取到待编辑的文件而无需指定该文件的存储路径。
1 2 3 4 5 6 |
# 用法 rosed [package_name] [filename] # 编辑roscpp package里的Logger.msg文件,如果不知道可以tab补全 rosed roscpp Logger.msg # 如果没有打开成功可能是因为没有安装vim |
设置编辑器,rosed默认的编辑器是vim,更改默认编辑器需要修改你的 ~/.bashrc 文件:
1 2 3 |
# 将默认编辑器设置成emacs export EDITOR='emacs -nw' |
创建ROS消息和ROS服务
msg文件存放在package的msg目录下,srv文件则存放在srv目录下。
msg文件实际上就是每行声明一个数据类型和变量名。可以使用的数据类型如下:
- int8, int16, int32, int64 (plus uint*)
- float32, float64
- string
- time, duration
- other msg files
- variable-length array[] and fixed-length array[C]
在ROS中有一个特殊的数据类型:Header
,它含有时间戳和坐标系信息。在msg文件的第一行经常可以看到Header header
的声明.
1 2 3 4 5 6 |
# 下面是一个msg文件的样例,它使用了Header,string,和其他另外两个消息类型 Header header string child_frame_id geometry_msgs/PoseWithCovariance pose geometry_msgs/TwistWithCovariance twist |
srv文件分为请求和响应两部分,由’—‘分隔。下面是srv的一个样例:
1 2 3 4 5 6 |
# A和B是请求,而Sum是响应。 int64 A int64 B --- int64 Sum |
使用msg
- 创建一个msg
123456# 在之前创建的package里定义新的消息cd ~/catkin_ws/src/beginner_tutorialsmkdir msgecho "int64 num" > msg/Num.msg# 上面是最简单的例子,在.msg文件中只有一行数据,可以多增几行定义更复杂的消息
接下来,还有关键的一步:我们要确保msg文件被转换成为C++,Python和其他语言的源代码:
123456# 查看package.xml, 确保它包含一下两条语句:<build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend># 如果没有,添加进去# 注意,在构建的时候,只需要"message_generation";在运行的时候,只需要"message_runtime"
用编辑器打开CMakeLists.txt
文件,在CMakeLists.txt
文件中,利用find_packag函数,在find_packag函数中增加对message_generation
的依赖,这样就可以生成消息了.
12345678# 直接在COMPONENTS的列表里增加message_generation# Do not just add this line to your CMakeLists.txt, modify the existing linefind_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation)
同样,需要在CMakeLists.txt
文件中设置运行依赖:
123456catkin_package(...CATKIN_DEPENDS message_runtime ......)# 添加的时候去掉上面的...
去掉注释符号#
,用你的.msg
文件替代Message*.msg
,就像下边这样:
12345add_message_files(FILESNum.msg)
添加.msg文件后,要确保CMake知道在什么时候重新配置project,保添加了如下函数(取消注释):
12generate_messages() - 使用rosmsg验证创建的msg上面的步骤完成后,使用
rosmsg show
命令检查ROS是否能够识别消息
1234567891011121314# 用法rosmsg show [message type]# 检查上面创建的消息rosmsg show beginner_tutorials/Num# 输出下面,表示人为创建的消息被ROS成功识别int64 num# 上面的消息包括两部分:消息所在的package(beginner_tutorials)和消息名(Num)# 如果忘记详细所在的package,可以省略package名rosmsg show Num# 输出[beginner_tutorials/Num]:int64 num
使用srv
- 创建一个srv
1234# 在刚刚那个package中创建一个服务roscd beginner_tutorialsmkdir srv
这里不手动创建服务,而是使用roscp
命令从其他package中复制一个服务。
12345# roscp用法roscp [package_name] [file_to_copy_path] [copy_path]# 从rospy_tutorials package中复制一个服务文件roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv
同样还有很关键的一步:我们要确保srv文件被转换成C++,Python和其他语言的源代码和上面一样,先在
CMakeLists.txt
文件中增加对message_generation
的依赖:1234567# Do not just add this line to your CMakeLists.txt, modify the existing linefind_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation)可以发现,
message_generation
对msg
和srv
都起作用。用你自己的srv文件名替换掉那些
Service*.srv
文件:12345add_service_files(FILESAddTwoInts.srv) - 用
rossrv
命令验证生成的service12345678910111213141516171819202122232425# 用法rossrv show <service type># 检查刚刚生成的srvrossrv show beginner_tutorials/AddTwoInts# 输出int64 aint64 b---int64 sum# 和rosmsg一样,也可以不指定具体的package名rossrv show AddTwoInts# 输出[beginner_tutorials/AddTwoInts]:int64 aint64 b---int64 sum[rospy_tutorials/AddTwoInts]:int64 aint64 b---int64 sum
msg和srv都需要的步骤
打开CMakeLists.txt文件,找到如下部分并去掉注释,并附加上所有消息文件所依赖的那些含有.msg
文件的package(上面这个例子是依赖std_msgs
,不要添加roscpp,rospy)
1 2 3 4 5 6 |
## Generate added messages and services with any dependencies listed here generate_messages( DEPENDENCIES std_msgs ) |
由于增加了新的消息,所以我们需要重新编译我们的package:
1 2 3 4 5 |
# In your catkin workspace $ cd ~/catkin_ws $ catkin_make # make报错,查看错误日志解决 $ cd - |
至此,所有在msg路径下的.msg文件都将转换为ROS所支持语言的源代码:
生成的C++头文件将会放置在~/catkin_ws/devel/include/beginner_tutorials/
;
Python脚本语言会在 ~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/msg
目录下创建;
lisp文件会出现在 ~/catkin_ws/devel/share/common-lisp/ros/beginner_tutorials/msg/
路径下。
编写简单的消息发布器和订阅器 (C++)
项目想用python来写,C++这部分不看了。
编写简单的消息发布器和订阅器 (Python)
编写发布器节点
节点(Node) 是指 ROS 网络中可执行文件。接下来,创建一个发布器节点(“talker”),它将不断的在 ROS 网络中广播消息。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 切换到之前创建的beginner_tutorials package 路径下 cd ~/catkin_ws/src/beginner_tutorials # 或者下面的命令也可以切换到该路径下 roscd beginner_tutorials # 在该路径下创建一个scripts文件夹来存放python scripts mkdir scripts cd scripts # 接下来下载python script wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001_talker_listener/talker.py # 并赋予可执行权限 chmod +x talker.py |
代码解释
可以使用rosed beginner_tutorials talker.py
命令或者其他编辑器查看源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env python # license removed for brevity import rospy from std_msgs.msg import String def talker(): pub = rospy.Publisher('chatter', String, queue_size=10) rospy.init_node('talker', anonymous=True) rate = rospy.Rate(10) # 10hz while not rospy.is_shutdown(): hello_str = "hello world %s" % rospy.get_time() rospy.loginfo(hello_str) pub.publish(hello_str) rate.sleep() if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass |
现在逐行解释上面代码:
1 2 |
#!/usr/bin/env python |
指定python脚本解释器,一般可执行文件的方式执行python时,在代码中指定python脚本解释器
1 2 3 |
import rospy from std_msgs.msg import String |
用python来编写节点首先要import rospy;然后import std_msgs.msg中的String消息类型用来发布消息
1 2 3 |
pub = rospy.Publisher('chatter', String, queue_size=10) rospy.init_node('talker', anonymous=True) |
这部分定义了这个消息发布器的接口。第一句表明节点使用消息类型String发布到chatter话题;这里的String实际上是类std_msgs.msg.String;queue_size指定当接收器来不及接受消息,在发送队列中存放消息的最大数量,旧的ROS发行版中没有这个参数。
1 2 |
rospy.init_node('talker', anonymous=True) |
这句很重要,它告诉rospy将要创建的节点名称,只有rospy直到这个消息以后节点才可以和ROS Master通信。这里表示向rospy说明将要创建的节点名称为:talker;anonymous=True表示通过添加一个随机的数给节点来唯一标示节点名。
1 2 |
rate = rospy.Rate(10) # 10hz |
创建rate对象来控制循环速率(通过sleep()方法实现),在这里表示每秒循环10次(只要每次处理时间不超过1/10秒)。
1 2 3 4 5 6 |
while not rospy.is_shutdown(): hello_str = "hello world %s" % rospy.get_time() rospy.loginfo(hello_str) pub.publish(hello_str) rate.sleep() |
这个循环是一个标准的rospy结构:先检查rospy.is_shutdown( )标志然后执行下面的工作,rospy.is_shutdown( )标志用来检查程序是否退出。ospy.loginfo(str)
有三个作用:消息被打印到屏幕,消息被写入Node的日志文件以及被写入rosout。rosout对于调试非常方便:可以使用rqt_console来提取消息,而不必使用Node的输出找到控制台窗口。pub.publish(hello_str)
向chatter话题广播一个字符串。rate.sleep()
用来调节时间以确保以上面设置的频率向话题发送消息,通用也可以使用rospy.sleep( )
来达到相同的目的。
std_msgs.msg.String
是一个非常简单的消息类型,也可以构造更复杂的消息类型。一般经验是构造函数args的顺序与.msg文件中的顺序相同。
1 2 3 4 5 6 |
# 可以不传入任何参数直接初始化 msg = String() msg.data = str # 也可以初始化部分字段,其他字段保留默认值 String(data=str) |
1 2 3 4 5 |
try: talker() except rospy.ROSInterruptException: pass |
当按下Ctrl-C或您的节点关闭时,可以通过rospy.sleep()和rospy.Rate.sleep()方法抛出rospy.ROSInterruptException
异常。
编写订阅器节点
现在,创建一个订阅器节点(“listener”),用来接收发布器节点(“talker”)广播的消息。
1 2 3 4 5 6 7 |
# 切换目录 roscd beginner_tutorials/scripts/ # 下载listener.py文件 wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001_talker_listener/listener.py # 赋予可执行权限 chmod +x listener.py |
代码解释
下载的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#!/usr/bin/env python import rospy from std_msgs.msg import String def callback(data): rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data) def listener(): # In ROS, nodes are uniquely named. If two nodes with the same # name are launched, the previous one is kicked off. The # anonymous=True flag means that rospy will choose a unique # name for our 'listener' node so that multiple listeners can # run simultaneously. rospy.init_node('listener', anonymous=True) rospy.Subscriber("chatter", String, callback) # spin() simply keeps python from exiting until this node is stopped rospy.spin() if __name__ == '__main__': listener() |
下面逐行解释代码:
1 2 3 4 5 6 7 |
rospy.init_node('listener', anonymous=True) rospy.Subscriber("chatter", String, callback) # spin() simply keeps python from exiting until this node is stopped rospy.spin() |
ROS中要求每个节点拥有唯一的名字,如果新建的节点名字和之前的重复,那么将会覆盖之前的节点,这个特性可以很容易的去除故障节点。第一句是创建’listener’节点,该节点像上面一样使用了anonymous=True
参数,表示生成唯一名称,以便轻松的运行多个’listener’节点。
第二句声明创建的节点订阅了std_msgs.msgs.String类型的chatter话题,收到新消息时,将调用回调,并将消息作为第一个参数。
最后一句,rospy.spin()是为了直到节点关闭才退出。与roscpp不同,rospy.spin()不会影响订阅者回调函数,因为它们有自己的线程。
编译节点
1 2 3 |
cd ~/catkin_ws catkin_make |
测试消息发布器和订阅器
启动发布器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 启动roscore roscore # 在运行节点之前先source一下setup.bash文件 cd ~/catkin_ws source ./devel/setup.bash # 运行上个教程创建的发布器节点 rosrun beginner_tutorials talker.py # 输出下面信息表示发布器启动成功启动 [INFO] [1560168906.875929]: hello world 1560168906.88 [INFO] [1560168906.975807]: hello world 1560168906.98 [INFO] [1560168907.075732]: hello world 1560168907.08 [INFO] [1560168907.175890]: hello world 1560168907.18 [INFO] [1560168907.275804]: hello world 1560168907.28 [INFO] [1560168907.375819]: hello world 1560168907.38 |
启动订阅器
1 2 3 4 5 6 7 8 9 |
# 运行上一个教程中创建的订阅器 rosrun beginner_tutorials listener.py # 输出下面信息表示订阅器成功启动 [INFO] [1560169074.477237]: /listener_29091_1560169017777I heard hello world 1560169074.48 [INFO] [1560169074.577244]: /listener_29091_1560169017777I heard hello world 1560169074.58 [INFO] [1560169074.677418]: /listener_29091_1560169017777I heard hello world 1560169074.68 [INFO] [1560169074.777438]: /listener_29091_1560169017777I heard hello world 1560169074.78 [INFO] [1560169074.877481]: /listener_29091_1560169017777I heard hello world 1560169074.88 |
编写简单的服务器和客户端 (C++)
项目想用python来写,C++这部分不看了。
编写简单的Service和Client (Python)
编写Service节点
这里将创建一个简单的service节点(“add_two_ints_server”),该节点将接收到两个整形数字,并返回它们的和。
1 2 3 |
# 先切换到beginner_tutorials roscd beginner_tutorials |
创建 scripts/add_two_ints_server.py文件,并粘贴下面的代码然后赋予可执行权限:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#!/usr/bin/env python from beginner_tutorials.srv import * import rospy def handle_add_two_ints(req): print "Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b)) return AddTwoIntsResponse(req.a + req.b) def add_two_ints_server(): rospy.init_node('add_two_ints_server') s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) print "Ready to add two ints." rospy.spin() if __name__ == "__main__": add_two_ints_server() |
代码解释
1 2 |
s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) |
上面这句代码表示使用AddTwoInts服务类型声明一个名为add_two_ints的新服务,handle_add_two_ints使用AddTwoIntsRequest的实例调用,并返回AddTwoIntsResponse的实例。
编写Client节点
创建scripts/add_two_ints_client.py文件,并粘贴下面的代码然后赋予可执行权限:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#!/usr/bin/env python import sys import rospy from beginner_tutorials.srv import * def add_two_ints_client(x, y): rospy.wait_for_service('add_two_ints') try: add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts) resp1 = add_two_ints(x, y) return resp1.sum except rospy.ServiceException, e: print "Service call failed: %s"%e def usage(): return "%s [x y]"%sys.argv[0] if __name__ == "__main__": if len(sys.argv) == 3: x = int(sys.argv[1]) y = int(sys.argv[2]) else: print usage() sys.exit(1) print "Requesting %s+%s"%(x, y) print "%s + %s = %s"%(x, y, add_two_ints_client(x, y)) |
代码解释
调用服务的客户端代码也很简单,对于客户端,您不必调用init_node()。我们先调用
1 2 |
rospy.wait_for_service('add_two_ints') |
这可以block一直到add_two_ints服务可用,接下来,我们创建一个用于调用服务的句柄:
1 2 |
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts) |
可以像普通函数一样使用这个句柄并调用它:
1 2 3 |
resp1 = add_two_ints(x, y) return resp1.sum |
因为已经将服务的类型声明为AddTwoInts,所以它会生成AddTwoIntsRequest对象(不用自己传递)。返回值是AddTwoIntsResponse对象,如果调用失败,可能会抛出rospy.ServiceException。
编译节点
1 2 3 |
cd ~/catkin_ws catkin_make |
测试简单的Service和Client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 运行Service rosrun beginner_tutorials add_two_ints_server.py # 输出 Ready to add two ints. # 另开一终端,运行Client rosrun beginner_tutorials add_two_ints_client.py 2 3 # Client输出 Requesting 2+3 2 + 3 = 5 # Service输出 Ready to add two ints. Returning [2 + 3 = 5] |
录制与回放数据
将ROS系统运行过程中的数据录制到一个.bag文件中,然后通过回放数据来重现相似的运行过程。
这部分和项目没有太大关系,粗略看一下。
roswtf入门
roswtf,名字怪怪的。。是一个用来检查ROS系统并尝试发现问题的工具。
现在也用不太招,粗略看一下。
探索ROS维基
说明怎么使用ROS维基网站,并在并在上面找问题。
教程注明已过时,不看了。
接下来做什么?
这个介绍了一些机器人模拟器和一些程序包,现在用不着,扫了一眼。至此,20个初级教程看完了。
Reference:
最新评论
感谢博主,让我PyTorch入了门!
博主你好,今晚我们下馆子不?
博主,你的博客用的哪家的服务器。
您好,请问您对QNN-MO-PYNQ这个项目有研究吗?想请问如何去训练自己的数据集从而实现新的目标检测呢?
where is the source code ? bomb1 188 2 8 0 0 hello world 0 0 0 0 0 0 1 1 9?5
在安装qemu的过程中,一定在make install 前加入 sudo赋予权限。
所以作者你是训练的tiny-yolov3还是yolov3...
很有用