ROS学习链接:
- 英文:ROS/Tutorials - ROS Wiki
中文:cn/ROS/Tutorials - ROS Wiki
ROS Wiki通常仅适用于ROS 1! 如果已安装ROS 2,请使用ROS 2文档网站。ROS 2 developer guide - 推荐书籍:《ROS机器人开发实践》胡春旭
- ROS探索总结 – 古月居 https://www.guyuehome.com/category/column/ros-exploring
- ROS探索总结-3.ROS新手教程 - 创客智造 https://www.ncnynl.com/archives/201609/840.html
wiki上的新手教程是一定要先熟悉的,后面的内容在后续学习过程中可以一边做一边学习。
目录
ROS Melodic学习笔记
注:和ROS版本相关的命令,要改成实际版本。具体参考官网。此处均为Melodic。
创建与编译
创建一个ROS工作空间
解析:创建一个ROS工作空间(ROS Workspace) - Jason_hjx - 博客园
先在主目录的Documents目录下新建一个文件夹work1存放所有工作空间的文档.
工作空间(Workspace)是一个存放工程开发相关文件的文件夹,高版本ROS默认用Catkin编译系统,典型的结构:
1)src:存储源代码
2)build:存储编译过程中产生的缓存信息和中间文件
3)devel:开发空间,放置编译生成的可执行文件
4)install:安装空间,编译成功后,可用make install命令将可执行文件安装到该空间,运行该空间中的环境变量脚本,即可在终端中运行可执行文件。(非必须)
# ubuntu18.04
# ros melodic
# source
# work1中出现文件夹devel, devel_isolated, el, el_isolated
source /opt/ros/melodic/setup.bash
# 生成文件夹
# catkin_ws和src
mkdir -p ~/Documents/work1/catkin_ws/src
# 转到目录catkin_ws
cd ~/Documents/work1/catkin_ws/
# 生成catkin文件夹中的文件
catkin_make
基本的代码构建工具是CMake(more).
- 每一个创建的功能包的顶层目录都必须存在CMakeLists.txt .这样 catkin_make才能够利用 CMake 强大的跨平台特性来编译所创建的package
- 每一个功能包都必须有一个Makefile.
- 那些没有经过构建步骤的功能包不需要任何的构建文件。
创建一个catkin程序包
切换到之前通过创建catkin工作空间教程创建的catkin工作空间中的src目录下,创建程序包
# 转换路径
cd ~/Documents/work1/catkin_ws/src
# 创建程序包beginner_tutorials
# 依赖包std_msgs,rospy,roscpp
# 创建后得到文件夹beginner_tutorials,内部有CMakeLists.txt,package.xml和两个空文件夹
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
查看一级依赖包depends1
rospack depends1 beginner_tutorials
出现报错:
[rospack] Error: could not call python function ‘rosdep2.rospack.init_rospack_interface’
解决:更新ROS的依赖
sudo rosdep fix-permissions
rosdep update
查看间接依赖包(depends递归检测所有依赖包)
rospack depends beginner_tutorials
添加路径,编译程序包(永久设置source)
事先用source命令使你的环境配置(setup)生效,在Ubuntu中的操作指令如下:
cd ~/Documents/work1/catkin_ws/devel
source setup.bash
为确保环境变量生效,可用命令echo $ROS_PACKAGE_PATH命令检查,若打印的路径包含当前路径,则成功。
(source设置的环境变量只对当前终端有效,是临时的,打开新的终端运行还需要source)
命令env|grep ros可以查看所有与ros相关的环境变量。
永久设置source的方法:
在终端修改bashrc。
gedit ~/.bashrc
在尾端加上source ~/catkin_ws_txt/devel/setup.bash,保存。
命令source ~/.bashrc使之生效。
在work1文件夹下搜索setup.bash找到3个同名同内容文件:
catkin_make是在CMake标准工作流程中依次调用了cmake 和 make。
# 在catkin工作空间中:
cd ~/Documents/work1/catkin_ws
# 编译src文件夹下的所有catkin工程
# build files写入了catkin_ws文件夹下的build中。
catkin_make
若源代码不在src文件夹中,而在my_src中,则可以这样:
catkin_make --source my_src
- build 目录是build space的默认所在位置,同时cmake 和 make也是在这里被调用来配置并编译你的程序包。
- devel 目录是devel space的默认所在位置, 同时也是在你安装程序包之前存放可执行文件和库文件的地方。
节点、话题、消息
ROS客户端库允许使用不同编程语言编写的节点之间互相通信:
rospy = python 客户端库
roscpp = c++ 客户端库
ROS对publisher和subscriber的启动顺序没有要求。
ROS的元功能包common_msgs中提供了许多不同消息类型的功能包,如std_msgs(标准数据类型)、geometry_msgs(几何学数据类型)和sensor_msgs(传感器数据类型)等。
msg文件是ROS中定义消息类型的文件
roscore
roscore 是你在运行所有ROS程序前首先要运行的命令,它会检查日志文件(log),launch ROS命令,开启节点管理器(Master),设置运行id。
roscore
终止循环:Ctrl+C
若roscore初始化失败,可能是网络配置问题(见官网),或者尝试修改文件夹~/.ros的所有者:
# 网络配置
export ROS_HOSTNAME=localhost
export ROS_MASTER_URI=http://localhost:11311
# 修改文件夹所属
sudo chown -R <your_username> ~/.ros
补充:
- chown [选项]… [所有者][:[组]] 文件…
- -R:处理指定目录以及其子目录下的所有文件
- 通过chown改变文件的拥有者和群组。在更改文件的所有者或所属群组时,可以使用用户名称和用户识别码设置。普通用户不能将自己的文件改变成其他的拥有者。其操作权限一般为管理员
界面:
以乌龟为例,运行节点和键盘控制节点
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
rosnode
打开一个新的终端,可以用rosnode查看节点运行内容。
打开新的终端时~/.bashrc会复原。
需要添加一些环境设置文件到你的~/.bashrc或者手动重新配置他们。
# 转换路径到catkin_ws
cd ~/Documents/work1/catkin_ws
# 显示当前正在运行的节点
rosnode list
# 显示
# /rosout
因为节点rosout用于收集和记录节点调试输出信息,所以它总是在运行的。
# 返回节点rosout的具体信息
rosnode info /rosout
Ping是工作在 TCP/IP网络体系结构中应用层的一个服务命令, 主要是向特定的目的主机发送 ICMP(Internet Control Message Protocol 因特网报文控制协议)Echo 请求报文,测试目的站是否可达及了解其有关状态
rosnode ping可检查网络连接量:Ctrl+C结束
# 测试物理路径
rosnode ping /rosout
如果执行ping成功而网络仍无法使用,那么问题很可能出在网络系统的软件配置方面,ping成功只保证当前主机与目的主机间存在一条连通的物理路径。
输入rosnode后反馈的:
rosnode is a command-line tool for printing information about ROS Nodes.
Commands:
rosnode ping test connectivity to node
rosnode list list active nodes
rosnode info print information about node
rosnode machine list nodes running on a particular machine or list machines
rosnode kill kill a running node
rosnode cleanup purge registration information of unreachable nodes
Type rosnode <command> -h for more detailed usage, e.g. 'rosnode ping -h'
rosrun
rosrun允许你使用包名直接运行一个包内的节点(而不需要知道这个包的路径)
# 首先打开新的终端
rosrun [package_name] [node_name]
下面是一个例子,关闭 turtlesim 窗口可以停止运行节点,再看rosnode list会少一项节点。若在终端1按Ctrl+C中止,则终端2 rosnode list不会少。所以还是建议关闭串口来停止节点运行。
# 例子
# 在终端1打开
rosrun turtlesim turtlesim_node
# 在终端2打开
rosnode list
rqt_graph
rqt_graph能够创建一个显示当前系统运行情况的动态图形。rqt_graph是rqt程序包中的一部分。
- 如果你没有安装Qt工具,请通过以下命令来安装:
$ sudo apt-get install ros--rqt
$ sudo apt-get install ros--rqt-common-plugins
请使用你的ROS版本名称(比如fuerte、groovy、hydro等)来替换掉。
sudo apt-get install ros-melodic-rqt
sudo apt-get install ros-melodic-rqt-common-plugins
打开
rosrun rqt_graph rqt_graph
鼠标放上去就可以看到ROS节点(蓝色和绿色)和话题(红色)的高亮显示。
rostopic、rosmsg
rostopic可获得话题信息
rostopic -h命令可看到详细子命令
例,打开新终端,查看turtle1话题的信息
rostopic echo /turtle1/cmd_vel
在进行话题相关操作的时候,传输的信息会显示出来
# 查看所有话题的发布者和订阅者数量等详细信息
rostopic list -v
话题之间的通信是通过在节点之间发送ROS消息实现的。对于发布器(turtle_teleop_key)和订阅器(turtulesim_node)之间的通信,发布器和订阅器之间必须发送和接收相同类型的消息。这意味着话题的类型是由发布在它上面的消息类型决定的。使用rostopic type命令可以查看发布在某个话题上的消息类型。
# 查看话题/turtle1/cmd_vel的消息类型
rostopic type /turtle1/cmd_vel
# 显示
# geometry_msgs/Twist
# 查看消息的详细信息
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
注:可用命令rostopic type /turtle1/cmd_vel | rosmsg show代替上述两命令
注:查看具体服务的信息时也有类似的命令。如spawn服务,rosservice type spawn| rossrv show
rostopic pub可以把数据发布到当前某个正在广播的话题上。
rostopic pub [topic] [msg_type] [args]
例:以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]'
-1:使rostopic发布一条消息后马上退出
# 以1Hz的频率发送命令流:即乌龟不停得绕圈
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'
在新端口,用rosrun rqt_graph rqt_graph查看节点和话题的关系:
rostopic hz命令可以用来查看数据发布的频率
我们看一下turtlesim_node发布/turtle/pose时有多快:
rostopic hz /turtle1/pose
# 显示
# average rate: 62.498
# min: 0.014s max: 0.018s std dev: 0.00060s window: 2495
turtlesim向turtle平均发送频率62.5Hz
查看话题信息:
rostopic type /turtle1/pose | rosmsg show
# 显示
#float32 x
#float32 y
#float32 theta
#float32 linear_velocity
#float32 angular_velocity
rqt_plot
rqt_plot命令可以实时显示一个发布到某个话题上的数据变化图形。这里我们将使用rqt_plot命令来绘制正在发布到/turtle1/pose话题上的数据变化图形。首先,在一个新终端中运行rqt_plot命令
rosrun rqt_plot rqt_plot
这会弹出一个新窗口,在窗口左上角的一个文本框里面你可以添加需要绘制的话题。在里面输入/turtle1/pose/x后之前处于禁用状态的加号按钮将会被使能变亮。按一下该按钮,并对/turtle1/pose/y重复相同的过程。现在你会在图形中看到turtle的x-y位置坐标图。
没有图显示,窗口不能拉大解决:
pip install -U matplotlib
ROS服务与参数
resservice
话题提供的服务可用rosservice list命令查看。
# 查看话题可用的服务
rosservice list
# 查看具体某服务:
# rosservice type [service]
#如:
rosservice type clear # 返回std_srvs/Empty,说明参数为空
# 调用某服务:
# rosservice call [service] [args]
#如:
rosservice call clear
# 查看服务spawn的信息:
rosservice type spawn| rossrv show
# 调用再生服务,生成新乌龟
rosservice call spawn 2 2 0.2 ""
rosparam
rosparam使得我们能够存储并操作ROS 参数服务器(Parameter Server)上的数据。参数服务器能够存储整型、浮点、布尔、字符串、字典和列表等数据类型。rosparam使用YAML标记语言的语法。一般而言,YAML的表述很自然:1 是整型, 1.0 是浮点型, one是字符串, true是布尔, [1, 2, 3]是整型列表, {a: b, c: d}是字典. rosparam有很多指令可以用来操作参数,如下所示:
使用方法:
- rosparam set 设置参数
rosparam get 获取参数
rosparam load 从文件读取参数
rosparam dump 向文件中写入参数
rosparam delete 删除参数
rosparam list 列出参数名
# 查看
rosparam list
# rosparam set [param_name] [arg]
rosparam set /turtlesim/background_r 150
# rosparam get [param_name]
rosparam get /turtlesim/background_g
# 修改参数的值后要调用清除服务使其生效
rosservice call clear
# 显示参数服务器上所有内容
rosparam get /
存储参数dump和加载load:
rosparam dump [file_name]
rosparam load [file_name] [namespace]
# 将所有的参数写入params.yaml文件:
rosparam dump params.yaml
# 将yaml文件重载入新的命名空间,比如说copy空间:
rosparam load params.yaml copy
# 查看存储的参数
rosparam get /copy/turtlesim/background_g
使用Qt工具,rqt_console和rqt_logger_level
rqt_console属于ROS日志框架(logging framework)的一部分,用来显示节点的输出信息。rqt_logger_level允许我们修改节点运行时输出信息的日志等级(logger levels)(包括 DEBUG、WARN、INFO和ERROR)。
保持乌龟一直转圈的状态,打开两个新终端,分别运行下面命令,弹出两个窗口。console窗口没有信息,关闭乌龟节点,重新打开后才有信息。
# 终端
rosrun rqt_console rqt_console
# 另一终端
rosrun rqt_logger_level rqt_logger_level
日志等级按以下优先顺序排列:
Fatal
Error
Warn
Info
Debug
Fatal是最高优先级,Debug是最低优先级。通过设置日志等级你可以获取该等级及其以上优先等级的所有日志消息。比如,将日志等级设为Warn时,你会得到Warn、Error和Fatal这三个等级的所有日志消息。
启动多节点roslaunch
启动定义在launch文件中的多个节点。
roslaunch [package] [filename.launch]
launch文件是描述一组节点及其话题重映射和参数的XML文件。
# 转到beginner_tutorials工作空间
cd ~/Documents/work1/catkin_ws
# 新建文件夹launch
mkdir launch
cd launch
在文件夹launch中创建文件turtlemimic.xml
(真机中新建txt,把下面内容复制粘贴后,修改文件名及扩展名,复制到虚拟机的launch文件夹下)
<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>
在终端继续输入
roslaunch turtlemimic.xml
将会有两个turtlesims被启动(两个窗口),然后我们在一个新终端中使用rostopic命令发送速度设定消息:
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来更好的理解在launch文件中所做的事情。运行rqt并在主窗口中选择Node Graph:
$ rqt
或者直接运行:
$ rqt_graph
文件编辑rosed
rosed 是 rosbash 的一部分。利用它可以直接通过package名来获取到待编辑的文件而无需指定该文件的存储路径了。
使用方法:
$ rosed [package_name] [filename]
例子: 编辑roscpp package里的Logger.msg文件
rosed roscpp Logger.msg
注意如果要使用rosed来编辑,那退出前要记得保存。
- vim一些基本操作
1。打开后按i,进入插入(编辑)模式
2。编辑完成后按esc退出编辑,但并没有保存,如果要保存并退出,先按“:”,再按wq,回车即可。
如果该实例没有运行成功,那么很有可能是你没有安装vim编辑器。
sudo apt install vim
创建消息rosmsg
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的声明.
创建消息步骤:
- 在功能包package下建立文件夹msg,并新建.msg文件,将消息类型写好。如:
# 新建文件夹msg,创建消息Num.msg
cd ~/Documents/work1/catkin_ws/src/beginner_tutorials
mkdir msg
echo "int64 num" > msg/Num.msg
msg文件夹中的文件Num.msg中有一行int64 num。
(也可用命令touch Num.msg创建文件,再打开,直接编辑文件)
msg文件中的语言不是C++也不是python,后续编译时会自动编译为这些语言。
- 我们要确保msg文件被转换成为C++,Python和其他语言的源代码,需要在package.xml中添加功能包依赖:
查看package.xml, 确保它包含一下两条语句:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
如果你的电脑安装了浏览器,xml文件可能会由浏览器默认打开,不能编辑。可以在这个目录下打开命令行,可以输入以下指令打开
gedit package.xml
如果没有,添加进去。 注意,在构建的时候,我们只需要"message_generation"。然而,在运行的时候,我们只需要"message_runtime"
- 在 CMakeLists.txt文件中,利用find_packag函数,增加对message_generation的依赖,这样就可以生成消息了。 你可以直接在COMPONENTS的列表里增加message_generation,就像这样:
# 不要只是将此行添加到您的CMakeLists.txt中,可以修改现有行
find_package(catkin REQUIRED COMPONENTS
roscpp rospy std_msgs message_generation)
设置运行依赖:
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
去掉注释符号#,用你的.msg文件替代Message*.msg:
add_message_files(
FILES
Num.msg
)
手动添加.msg文件后,我们要确保CMake知道在什么时候重新配置我们的project。 去掉注释并附加上所有你消息文件所依赖的那些含有.msg文件的package。确保添加了如下代码:(Num.msg没有依赖的package,所以没有参数)
generate_messages()
- 如果你要使用在其他package里定义的消息类型,不要忘记添加以下语句到 package.xml:(用功能包名替换name_of_package_containing_custom_msg)
<build_depend>name_of_package_containing_custom_msg</build_depend>
<run_depend>name_of_package_containing_custom_msg</run_depend>
- 用命令检查是否创建消息成功:
rosmsg show beginner_tutorials/Num
或
rosmsg show Num
如果不能正常查看,显示Unable to load msg… … with paths,则添加source:
cd ~/Documents/work1/catkin_ws/devel
source setup.bash
# 路径转到devel
注: 永久设置source的方法: 添加环境setup.bash到bashrc
# 打开文件
gedit ~/.bashrc
# 修改!加上source ~/catkin_ws_txt/devel/setup.bash
# 使配置生效
source ~/.bashrc
在尾端加上source ~/catkin_ws_txt/devel/setup.bash,保存。
再命令source ~/.bashrc使之生效。
注:echo “source ~/catkin_ws/devel/setup.sh” >> ~/.bashrc即可完成将source加入bashrc的过程。再生效即可。
-
catkin_make尝试编译,编译成功后,.msg文件会编译为.h文件,在devel/include 中可以看到头文件。
-
接下来可以生成自己的消息源代码。引用和输出消息类型:
消息类型都被归属到与package相对应的域名空间下,例如:
C++
#include <std_msgs/String.h>
std_msgs::String msg;
Python
from std_msgs.msg import String
msg = String()
创建服务rossrv、roscp
在刚刚那个package中创建一个服务:
在终端输入
roscd beginner_tutorials
mkdir srv
# 复制服务
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv
在~/Documents/work1/catkin_ws/src/beginner_tutorials/srv路径中可以找到AddTwoInts.srv
在CMakeLists.txt中找到find_package(),添加message_generation。
修改add_service_files:
add_service_files(
FILES
AddTwoInts.srv
)
修改generate_messages:去掉注释并附加上所有你消息文件所依赖的那些含有.msg文件的package。
generate_messages(
DEPENDENCIES
std_msgs
)
检查ROS能否识别该服务:
# rossrv show <service type>
rossrv show beginner_tutorials/AddTwoInts
# 也可不指定package名
rossrv show AddTwoInts
接下来可以生成自己的服务源代码。
最后,由于增加了新的消息,所以我们需要重新编译我们的package:
在catkin目录下打开终端,运行catkin_make命令
所有在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/ 路径下.
安装依赖包package
rosdep install [package]
例:
rosdep install turtlesim
命令行工具
rospack = ros+pack(age) : provides information related to ROS packages获取功能包信息
rosstack = ros+stack : provides information related to ROS stacks
roscd = ros+cd : changes directory to a ROS package or stack功能包目录跳转
rosls = ros+ls : lists files in a ROS package列出功能包文件
roscp = ros+cp : copies files from/to a ROS package复制功能包中的文件
rosmsg = ros+msg : provides information related to ROS message definitions消息类型
rossrv = ros+srv : provides information related to ROS service definitions服务类型
rosmake = ros+make : makes (compiles) a ROS package编译功能包
catkin_create_pkg:创建功能包
catkin_make:编译功能包
rosdep:自动安装功能包依赖的其他包
rosed:编辑功能包中的文件
rosrun:运行功能包中的可执行文件
roslaunch:运行启动文件
发布者与订阅者
Publisher和Subscriber是ROS系统最常用的通信方式,即通过话题传播消息。
Publisher作用:针对指定话题发布特定数据类型的消息。
Subscriber作用:订阅Publisher发布的话题,对消息做处理。
编写Publisher节点(C++)
『节点』(Node) 是指 ROS 网络中可执行文件。接下来,我们将会创建一个Publisher节点(“talker”),它将不断的在 ROS 网络中广播消息。
在之前创建的 beginner_tutorials package的src文件夹中新建C++文件talker.cpp,复制官方代码到其中。
初始化ROS节点,向ROS Master注册节点信息,包括发布的话题名称和话题中的消息类型,每过100ms发布一次话题。
// talker.cpp
#include "ros/ros.h"
// 消息类型
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
// 初始化ROS节点,名称talker
ros::init(argc, argv, "talker");
// 创建节点句柄
ros::NodeHandle n;
// 创建Publisher,发布话题名chatter,消息类型std_msgs::String
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// 10Hz的频率循环运行
ros::Rate loop_rate(10);
int count = 0;
// 进入节点主循环,非异常情况下会一直循环,异常时ros::ok()返回false
while (ros::ok())
{
// 初始化消息
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
// 打印日志信息
ROS_INFO("%s", msg.data.c_str());
// 初始化即将发布的消息msg。
// 消息发布后,Master会查找订阅话题的节点,建立节点之间的连接,完成传输
chatter_pub.publish(msg);
// 循环等待回调函数
ros::spinOnce();
// 调用休眠函数,按循环频率延时100ms
loop_rate.sleep();
++count;
}
return 0;
}
退出节点主循环的异常情况:
- 收到SIGINT信号(Ctrl+C)
- 被相同名称的节点踢掉线
- 节点调用了关闭函数ros::shutdown()
- 所有ros::NodeHandles句柄被销毁
编写Subscriber节点(C++)
在 beginner_tutorials package 目录下创建 src/listener.cpp 文件
初始化节点,订阅话题,循环等待消息,回调函数处理。
当有消息到达时,会自动以消息指针作为参数调用回调函数,完成对消息内容的处理。
// listenser.cpp
#include "ros/ros.h"
// 消息类型
#include "std_msgs/String.h"
// 消息回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
// 打印接收到的消息
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "listener");
// 创建节点句柄
ros::NodeHandle n;
// 创建Subscriber,订阅话题名为chatter,在ROS Master中注册回调函数chatterCallback
// Master会关注系统中是否存在发布话题的节点,如果存在则建立连接,完成数据传输。
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
// 循环等待,当有消息到达,就调用回调函数完成处理
ros::spin();
return 0;
}
ros::spin()在ros::ok()返回false时退出。如果没有消息到达,它不会占用很多 CPU,所以不用担心。一旦 ros::ok() 返回 false,ros::spin() 就会立刻跳出自循环。这有可能是 ros::shutdown() 被调用,或者是用户按下了 Ctrl-C,使得 master 告诉节点要终止运行。也有可能是节点被人为关闭的。
C++是需要编译的语言,因此需要修改CMakeLists.txt,并用catkin_make编译。
(在之前修改的基础上增加)
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
# 发布者
# 需要编译的代码和文件
add_executable(talker src/talker.cpp)
# 设置链接库,此处没有使用其他库,链接默认库
target_link_libraries(talker ${catkin_LIBRARIES})
# 为可执行文件添加对生成的消息文件的依赖:
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
# 订阅者同理
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
这会生成两个可执行文件, talker 和 listener, 默认存储到 devel space 目录下,具体是在~/catkin_ws/devel/lib/< package name> 中
最后,在catkin工作空间下,命令catkin_make 。
注意:如果你是添加了新的 package,你需要通过 --force-cmake 选项告诉 catkin 进行强制编译
编写Publisher节点(python)
在之前创建的 beginner_tutorials package的文件夹中新建文件夹scripts和python文件talker.py,复制官方代码到其中。
# 新建文件夹
cd ~/Documents/work1/catkin_ws
mkdir scripts
cd scripts
# 下载官方文档
wget https://raw.github.com/ros/ros_tutorials/melodic-devel/rospy_tutorials/001_talker_listener/talker.py
# 更改权限为可执行文件
chmod +x talker.py
在CMakeLists.txt中增加/修改
(路径:~/Documents/work1/catkin_ws/src/beginner_tutorials)
catkin_install_python(PROGRAMS scripts/talker.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
# talker.py
import rospy
from std_msgs.msg import String
def talker():
# 节点发布话题chatter,消息类型为std.msgs.msg.String,限制消息队列中的消息数
pub = rospy.Publisher('chatter', String, queue_size=10)
# 初始化节点talker,在此之前,rospy不能与ROS Master通信
# anonymous=True,在节点名称后加随机数保证不重名
rospy.init_node('talker', anonymous=True)
# 与sleep共同实现10Hz循环
rate = rospy.Rate(10) # 10hz
# 程序中断时退出循环,比如Ctrl+C
while not rospy.is_shutdown():
hello_str = "hello world %s" % rospy.get_time()
# 打印消息,将消息写入节点日志,将消息写到rosout
rospy.loginfo(hello_str)
# 调用回调函数pub.publish(hello_str)给话题chatter发布string
pub.publish(hello_str)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
rosout可以方便的通过rqt_console进行调试。
编写Subscriber节点(python)
# 下载listener.py
wget https://raw.github.com/ros/ros_tutorials/melodic-devel/rospy_tutorials/001_talker_listener/listener.py
# 更改权限为可执行文件
chmod +x listener.py
修改CMakeLists.txt
catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
# listener.py
import rospy
from std_msgs.msg import String
def callback(data):
# 打印消息,将消息写入节点日志,将消息写到rosout
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.
# 初始化节点listener,在名称末尾加随机数
rospy.init_node('listener', anonymous=True)
# 声明订阅者chatter,消息类型为std_msgs.msg.String,有消息时调用callback函数
rospy.Subscriber('chatter', String, callback)
# spin() simply keeps python from exiting until this node is stopped
# 循环等待
rospy.spin()
if __name__ == '__main__':
listener()
对于python节点,也要在catkin工作空间用catkin_make编译,以保证对消息和服务自动生成python代码。
catkin_make报错:找不到文件,CMakeLists中默认路径是/Documents/work1/catkin_ws/src/beginner_tutorials/,但是实际scripts在/Documents/work1/catkin_ws/中,所以找不到文件。把scripts文件夹移到~/Documents/work1/catkin_ws/src/beginner_tutorials/中,再编译就没问题了。
报错:/usr/bin/env: “python\r”: 没有那个文件或目录
解决:在python所在目录打开终端,用vim命令打开报错的py文件,输入“:set ff=unix”回车,再输入“:wq”回车。(link)
注:vim启动进入普通模式,处于插入模式或命令行模式时只需要按Esc或者Ctrl+[(这在vim课程环境中不管用)即可进入普通模式。普通模式中按i(插入)或a(附加)键都可以进入插入模式,普通模式中按:进入命令行模式。命令行模式中输入wq回车后保存并退出vim。
(link)
启动Publisher
打开两个终端,如果使用catkin,确保你在调用catkin_make后,在运行你自己的程序前,已经source了catkin工作空间下的setup.sh文件。
# 启动ROS Master
roscore
# 设置环境变量
cd ~/Documents/work1/catkin_ws
source ./devel/setup.bash
# 运行Publisher
# C++:
rosrun beginner_tutorials talker
# python:
# rosrun beginner_tutorials talker.py
# 运行Subscriber
# C++:
rosrun beginner_tutorials listener
# python
# rosrun beginner_tutorials listener.py
两个终端正常输出信息,说明Publisher和Subscriber正常。(注,上述代码在多个终端执行,都要source环境)
服务器与客户端
首先要完成前面创建服务数据类型的文件的过程,并修改CMakeLists.txt和Package.xml文件,修改后catkin_make编译功能包。编译后,在服务的service节点和client节点的代码中调用定义好的服务消息,实现相加的功能。
AddTwoInts.srv
int64 a
int64 b
---
int64 sum
AddTwoInts.h在devel/include/beginner_tutorials/AddTwoInts.h
以下以C++源码为例,python见官网。
编写service节点
路径~/Documents/work1/catkin_ws/devel/include/beginner_tutorials下,可找到AddTwoInts.h,是由编译系统自动根据我们先前创建的srv文件生成的对应该srv文件的头文件。
初始化ROS节点,创建Service,循环等待服务请求,进入回调函数完成服务功能的处理,反馈应答数据给Client。
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
// service回调函数,输入参数req,输出参数res
// 数据类型都定义在srv文件内部,函数返回一个boolean值
bool add(beginer_tutorials::AddTwoInts::Request &req,
beginer_tutorials::AddTwoInts::Response &res)
{
// 将输入参数中的请求数据相加,放到应答变量中
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}
int main(int argc, char **argv)
{
// 初始化节点
ros::init(argc, argv, "add_two_ints_server");
// 创建节点句柄
ros::NodeHandle n;
// 创建服务器add_two_ints,注册回调函数add
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
// 循环等待
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
编写Client节点
在beginner_tutorials包中创建src/add_two_ints_client.cpp文件
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "add_two_ints_client");
// 从终端命令行获取两个加数
if (argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
// 创建节点句柄
ros::NodeHandle n;
// 创建client,请求add_two_ints服务
// service的消息类型:beginer_tutorials::AddTwoInts
ros::ServiceClient client = n.serviceClient<beginer_tutorials::AddTwoInts>("add_two_ints");
// 创建消息srv
beginner_tutorials::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
// 发布service请求,等待应答结果
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}
return 0;
}
client.call(srv)进行服务调用,该过程会阻塞,成功后返回true,访问srv.response即可得到结果。
最后,还要修改CMakeLists.txt,加上
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
这段代码将生成两个可执行程序"add_two_ints_server"和"add_two_ints_client",这两个可执行程序默认被放在你的devel space下的包目录下,默认为~/catkin_ws/devel/lib/share/< package name>。你可以直接调用可执行程序,或者使用rosrun命令去调用它们。
最后,要在catkin_ws下运行catkin_make命令.
catkin_main出现错误:add_two_ints_server.cpp和add_two_ints_client.cpp中的拼写都有错误,beginner写成了beginer
另外,检查CMakeLists.txt,补上了find_package(catkin REQUIRED COMPONENTS
geometry_msgs)
修改后编译成功。
运行
成功后启动例程,设置环境变量:
cd ~/Documents/work1/catkin_ws
source ./devel/setup.bash
用下列命令检查服务消息类型AddTwoInts.msg是否可被识别:
rossrv show beginner_tutorials/AddTwoInts
# 运行service
# C++:
rosrun beginner_tutorials add_two_ints_server
# python:
rosrun beginner_tutorials add_two_ints_server.py
# 显示
# Ready to add two ints.
# 运行client
# C++:
rosrun beginner_tutorials add_two_ints_client 1 3
# python:
rosrun beginner_tutorials add_two_ints_client.py 1 3
# 显示
# request: x=1, y=3
# sending back response: [4]
报错:
[rosrun] Couldn’t find executable named add_two_ints_server below /home/xcapecjh1804/catkin_ws/src/beginner_tutorials
解决:
设置环境变量
报错:
[ERROR] [1597828930.825444201]: [registerPublisher] Failed to contact master at [localhost:11311]. Retrying…
解决:
roscore
记录与回放话题
记录rosbag record
首先运行节点:
roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
# 查看所有话题
rostopic list -v
录制所有话题数据:
cd ~/Documents/work1/catkin_ws/src/beginner_tutorials/
mkdir bagfiles
cd bagfiles
rosbag record -a
-a表示将所有话题的数据保存于bag文件中。
录制指定话题数据:
rosbag record -O subset /turtle1/command_velocity /turtle1/pose
-O参数告诉rosbag record将数据记录保存到名为subset.bag的文件中,同时后面的话题参数告诉rosbag record只能录制这两个指定的话题。然后通过键盘控制turtle随处移动几秒钟,最后按Ctrl-C退出rosbag record命令。
在运行rosbag record命令的窗口中按Ctrl-C退出该命令。现在检查看~/bagfiles目录中的内容,你应该会看到一个以年份、日期和时间命名并以.bag作为后缀的文件。这个就是bag文件,它包含rosbag record运行期间节点发布的话题。
如:2020-08-19-02-44-05.bag
查看记录rosbag info
使用rosbag info检查看它的内容,使用rosbag play命令回放出来。接下来我们首先会看到在bag文件中都录制了哪些东西。我们可以使用info命令,该命令可以检查看bag文件中的内容而无需回放出来。在bag文件所在的目录下执行以下命令
rosbag info < your bagfile>
如:
rosbag info 2020-08-19-02-44-05.bag
文件名在rosbag record的返回信息中找。
回放记录rosbag play
rosbag play < your bagfile>
如:
rosbag play 2020-08-19-02-44-05.bag
最终/turtle1/command_velocity话题将会被发布,同时在turtuelsim虚拟画面中turtle应该会像之前你通过turtle_teleop_key节点控制它一样开始移动。从运行rosbag play到turtle开始移动时所经历时间应该近似等于之前在本教程开始部分运行rosbag record后到开始按下键盘发出控制命令时所经历时间。
rosbag play参数
- -s 让rosbag play命令等待一段时间跳过bag文件初始部分后再真正开始回放。
- -r 改变消息发布速率,若为2,则以两倍的速度通过按键发布控制命令
- -d 设置等待时间,默认模式下,rosbag play命令在公告每条消息后会等待一小段时间(0.2秒)后才真正开始发布bag文件中的内容
例:
rosbag play -r 2 2020-08-19-02-44-05.bag
要知道有哪些话题,可rosopic list:
# 新终端,列出话题:
rostopic list
# 新终端,显示话题内容:
rostopic echo /turtle1/pose
注意:rosbag受制于其本身的性能无法完全复制录制时的系统运行行为,rosplay也一样。对于像turtlesim这样的节点,当处理消息的过程中系统定时发生极小变化时也会使其行为发生微妙变化
检查ROS系统
安装检查roswtf
以下检查要求确保roscore没在运行!
- 运行ps -ef | grep -i rosmaster,返回类似的内容:
xcapecj+ 11186 3321 0 03:18 pts/0 00:00:00 grep --color=auto -i rosmaster。
执行下面命令:
roscd rosmaster
roswtf
返回
Static checks summary:
No errors or warnings
运行时检查roswtf
先在新终端roscore,再执行
roscd
roswtf
检查过程的长短取决于正在运行的ROS节点数量,可能会花费很长时间才能完成。正如你看到的,这一次出现了警告。roswtf发出警告说rosout节点订阅了一个没有节点向其发布的话题。在本例中,这正是所期望看到的,因为除了roscore没有任何其它节点在运行,所以我们可以忽略这些警告。
错误roswtf
roswtf会对一些系统中看起来异常但可能是正常的运行情况发出警告。也会对确实有问题的情况报告错误。
接下来我们在ROS_PACKAGE_PATH 环境变量中设置一个 bad值,并退出roscore以简化检查输出信息。
roscd
ROS_PACKAGE_PATH=bad:$ROS_PACKAGE_PATH roswtf
返回:Found 1 error(s).
roswtf发现了一个有关ROS_PACKAGE_PATH设置的错误。
roswtf还可以发现很多其它类型的问题。如果你发现自己被一个编译或者通信之类的问题困扰的时候,可以尝试运行roswtf看能否帮你解决。
给你一台运行ROS的机器人,你应该能够运用所学知识来列出机器人上发布和订阅的各种话题(topic),查看话题中发布的消息,然后编写你自己的节点(node)来处理传感器数据,最后让机器人在真实环境中动起来。
在python中使用C++类
cn/ROS/Tutorials/Using a C++ class in Python - ROS Wiki(全英文)
Keywords: C++, Python, bindings