ROS学习笔记
笔记内容源自视频整理Autolabor课程,机器人工匠阿杰,和Autolabor课程文件。
Ubuntu终端窗口命令
基础指令
1 | cd 文件夹名/路径名 //进入当前目录的某个文件夹,或进入某个路径的文件夹 |
在输入命令式要熟练使用Tab键,自动补全文件名
文本编辑器gedit
1 | gedit 文件名.文件格式(例如.txt) //在当前路径下新建一个文本文件 |
source指令
1 | source command.sh //在Linux中常常吧一连串的指令写到.sh文件中,通过source指令加载运行 |
终端程启动脚本 ~/.bashrc
这是一个在主文件夹中的隐藏文件,在文件管理器中看不到,可以通过在终端中输入
1 | ls -a //查看所有文件,包括隐藏文件 |
.bashrc文件是终端程序的初始化脚本,在每次执行终端程序时,都会首先执行这个脚本,进行环境变量的配置工作。通过
1 | gedit ~/.bashrc |
对脚本进行编辑,如下图
sudo指令
以管理员权限执行本条执行,可以让我们完成安装等高权限操作。
比如,安装文件时可以通过
1 | sudo apt install 文件名 |
实现文件的安装操作
ROS系统
ROS安装步骤
- 首先进入ROS官网https://www.ros.org。
- 选择与Ubuntu系统版本对应的ROS系统名称,例如我安装的是Ubuntu20.04版本,对应的ROS系统名称叫Noetic。
- 根据官网的步骤,首先在终端程序中输入指令,完成相关配置。 可以通过选择国内的安装镜像,加快安装速度。
1
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
- 设置安装秘钥 如果网络波动较大,可能会出现安装失败的问题。
1
2
3sudo apt install curl # if you haven't already installed curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - - 下载安装ROS主体程序
- 运行
sudo apt update
从网上的各大应用商店更新APP索引列表。 - 从更新后的索引列表中安装ROS系统
sudo apt install ros-noetic-desktop-full
。
- 运行
- 环境参数设置
在新的终端中输入之后输入1
2
3echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrcroscore
检查ROS的核心是否可以运行。 - 最后对ROS的依赖包工具进行初始化,方便我们安装第三方的扩展软件包。 到这儿就基本完成安装了,如果出现了无法解决的问题,可以在B站上搜索机器人工匠阿杰观看详细视频。
1
2
3
4
5sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
sudo rosdep init
rosdep update
APT源,ROS的软件包应用商店
在浏览器中输入index.ros.org,可以在这里下载到ROS的软件包,资源库。
ROS架构
ROS文件系统指的是在硬盘上ROS源代码的组织形式,其大致结构如下:
1 | WorkSpace --- 自定义的工作空间 |
值得注意的点是,在package目录下的scripts目录下写py文件,在scr目录下写C++文件,launch目录下写launch文件。之后主要介绍package.xml文件和CMakeList.txt两个配置文件。
package.xml
该文件定义有关软件包的属性,比如软件包的名称,版本号,作者,维护者以及对其他catkin软件包的依赖信息等。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82<?xml version="1.0"?>
<!-- 格式: 以前是 1,推荐使用格式 2 -->
<package format="2">
<!-- 包名 -->
<name>demo01_hello_vscode</name>
<!-- 版本 -->
<version>0.0.0</version>
<!-- 描述信息 -->
<description>The demo01_hello_vscode package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<!-- 维护人员 -->
<maintainer email="xuzuo@todo.todo">xuzuo</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<!-- 许可证信息,ROS核心组件默认 BSD -->
<license>TODO</license>
<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/demo01_hello_vscode</url> -->
<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>message_generation</build_depend> -->
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<!-- 依赖的构建工具,这是必须的 -->
<buildtool_depend>catkin</buildtool_depend>
<!-- 指定构建此软件包所需的软件包 -->
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<!-- 指定根据这个包构建库所需要的包 -->
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>rospy</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<!-- 运行该程序包中的代码所需的程序包 -->
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>CMakelists.txt
文件CMakeLists.txt是CMake构建系统的输入,用于构建软件包。任何兼容CMake的软件包都包含一个或多个CMakeLists.txt文件,这些文件描述了如何构建代码以及将代码安装到何处。在编写ROS节点,话题等信息时,需要设置CMakelists.txt文件。具体的编写方法,可以观看机器人工匠阿杰或Autolabor初级教程,了解详细编写方法。
ROS文件系统相关命令
ROS文件系统本质上是操作系统文件,其本身提供了一些类似于Linux的命令。文件操作,就是增,删,改,查,执行,等5大主要操作。
- 增
1
2catkin_create_pkg 自定义包名 依赖包 //创建新的ROS包
sudo apt install xxx //安装ROS功能包 - 删
1
sudo apt perge xxx //删除某个功能包
- 改
1
2
3rosed 包名 文件名 //修改功能包文件
需要安装vim
比如:rosed turtlesim Color.msg - 查
1
2
3
4
5rospack list //列出所有功能包
rospack find 包名 //查找某个功能包是否存在,如果存在返回安装路径
roscd 包名 //进入某个功能包
rosls 包名 //列出某个包下的文件
apt search xxx //搜索某个功能包 - 执行
- roscore 这是ROS系统先决条件结点和程序的集合,必须在运行roscore之后才能使节点之间进行通信。roscore会启动 ros master, ros 参数服务器, rosout日志节点
1
2roscore //启动ros
roscore -p xxx //指定端口号 - rosrun 运行指定的ROS节点
1
2rosrun 包名 可执行文件名
rosrun turtlesim turtlesim_node //运行turtlesim包中的可执行文件turtlesim_node - roslaunch 执行某个包下的launch文件
1
roslaunch 包名 launch文件名 //执行某个包下的某个launch文件
- roscore 这是ROS系统先决条件结点和程序的集合,必须在运行roscore之后才能使节点之间进行通信。roscore会启动 ros master, ros 参数服务器, rosout日志节点
ROS计算图rqt_graph
在一个庞大的ros程序中,不同节点之间的通信非常复杂,ROS中提供了一个实用的工具:rqt_graph,可以显示当前系统运行的情况,各节点之间的通信过程。rqt_grah是rqt程序包中的一部分,在不同版本的ROS中,rqt程序包可能需要手动安装。比如,在Noetic版本下,安装输入
1 | sudo apt install ros-noetic-rqt |
在运行ros节点后,在新的终端中键入
1 | rqt_graph |
可以看到不同节点之间的关系。
ROS通信机制
在机器人中,可能集成各种传感器(摄像头,雷达等),在ROS中,每个功能点都是一个单独的进程,每个进程都是独立运行的,可以认为ROS进程(也叫Nodes,节点)的分布式框架。这些进程分布于不同的主机,不同主机协同工作,从而分散计算压力。但是随之而来的问题是,不同进程之间如何进行通信。
ROS中的基本通信机制有三种实现策略
- 话题通信(发布订阅模式)
- 服务通信(请求响应模式)
- 参数服务器(参数共享模式)
话题通信
话题通信是基于发布订阅模式的,即一个节点发布消息,另一个结点订阅该消息,是ROS中使用频率最高的通信模式。例如如下场景:
机器人在执行导航功能,使用的传感器是激光雷达,机器人会采集激光雷达感知到的信息并进行计算,然后生成运动控制信息驱动机器人的底盘
在上述场景中,不止一次使用了话题通信
- 激光雷达在采集信息并处理的时,ROS中有一个节点需要实时的发布当前雷达采集到的数据,导航模块中有结点会订阅并解析雷达数据。
- 在运动消息发布过程中,导航模块会根据传感器采集的数据实时计算出运动控制信息并发布给底盘,底盘也有一个节点订阅运动信息并转换成控制电机的脉冲信号。
话题通信适用于不断更新的数据传输应用场景
理论模型
话题通信模型中涉及到的是那个角色
- ROS Master(管理者)
- Talker(发布者)
- Subscriber(订阅者)
ROS Master负责管理Talker和Subscriber注册的信息,并匹配话题相同的Talker和Subscriber,帮助建立连接,在建立连接之后,Talker发布的信息可以被Subscriber订阅。
流程由一下步骤实现:
0)Talker注册
Talker在启动后,会通过RPC(远程调用地址,即图中的”foo:1234”)在ROS Master中注册自身信息,其中包含所发布消息的话题名称。ROS Master会将节点的注册信息加入到注册表中。1)Subscriber注册
Subscriber启动后,也会通过RPC在ROS Master中注册自身信息,包好需要订阅消息的话题名。ROS Master同样将节点注册信息加入到注册表中。2)ROS Master实现信息匹配
ROS Master会根据注册表中的信息匹配Talker和Subscriber,并通过RPC向Subscriber发送Talker的RPC地址信息(图中的foo:1234)3)Subscriber向Talker发送请求
Subscriber根据接收到的RPC地址,通过RPC向Talker发送连接请求,传输订阅的话题名称,消息类型以及通信协议(TCP/UDP)。4)Talker确认请求
Talker接受到Subscriber的请求后,也通过RPC向Subscriber确认连接信息,并发送自身的TCP地址信息(图中的TCP server:foo:2345)。5)Subscriber与Talker建立连接
Subscriber根据上一步返回的消息使用TCP与Talker建立网络连接。6)Talker向Subscriber发送消息
建立连接后,Talker开始向Subscriber发布消息
注意1:前5步通信过程中使用的是RPC协议,只有最后两步使用的是TCP协议
注意2:Talker和Subscriber之间的启动无先后顺序要求
注意3:Talker和Subscriber都可以有多个
注意4:Talker和Subscriber之间建立连接后,就不再需要ROS Master。即使关闭ROS Master,也可以正常通信
注意5:上述的连接过程在ROS中已经封装好了,在具体实现过程中可以直接使用。
话题通信基本操作(C++)
在具体实现过程中,ROS Master不需要实现,连接的建立也已经封装好了,需要注意的只有三点:
- 发布方
- 接收方
- 数据
流程:
- 新建功能包
- 编写发布方实现
- 编写订阅方实现
- 编辑配置文件
- 编译并执行(C++代码需要先编译再执行)
新建功能包
在工作区的src目录下新建功能包,比如我的工作目录为 ~/catkin_ws/src,可以在终端中首先进入src目录,然后新建一个功能包(可返回2.3查看)1
2cd ~/catkin_ws/src
catkin_create_pkg test_pub_sub roscpp rospy std_msgs通过上述命令新建一个名为 test_pub_sub 的功能包,其依赖项分别是 roscpp,rospy,std_msgs。
之后新建发布者节点 pub_node.cpp文件,新建订阅者节点 sub_node.cpp文件
发布方代码(C++)
编写发布方代码,其实有一个编写的框架,如下: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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56/*
发布方实现流程:
1. 包含头文件
2. 初始化 ROS 节点:命名(唯一)
3. 实例化ROS Master(调用ROS大管家)
4. 实例化 发布者对象
5. 组织被发布的数据,编写逻辑,发布数据
*/
//1. 编写头文件
int main(int argc, char *argv[]){
//设置编码,是之可以显示中文
setlocale(LC_ALL, "");
//2. 初始化ROS节点
ros::init(argc, argv, "pub_node");
//3. 实例化ROS Master
ros::NodeHandle nh; //这里封装了ROS中的许多功能
//4. 实例化发布者对象;
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);
//这句代码中nh.advertise后的内容为:
//<发布的消息类型>("发布到的话题名称", 队列中保存消息数的最大值)
//5. 组织被发布的数据,编写逻辑,发布数据
std_msgs::String msg; //消息发布的载体
std::String msg_front = "Hello 你好!"; //消息前缀
int count = 0; //消息计数器
//发送频率
ros::Rate r(10) //10Hz
whlie(ros::ok()){
//使用stringstream拼接字符串与编号
std::stringstream ss;
ss << msg_front << count;
msg.data = ss.str();
//发布消息
pub.publish(msg);
//打印发送的消息
ROS_INFO("消息: %s", msg.data.c_str());
//消息的内容在msg.data结构体的c_str()中。
r.sleep(); //根据之前设定的发送频率,进行休眠,休眠时间为1/频率
count++; //每次循环消息计数器+1
}
return 0;
}订阅方代码(C++)
与发布方类似,订阅方的大致框架如下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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46/*
订阅方实现流程:
1. 包含头文件
2. 初始化 ROS 节点
3. 实例化 ROS Master(即调用ROS大管家)
4. 实例化订阅对象
5. 处理订阅消息(需要编写回调函数)
6. 设置循环调用回调函数
*/
//1. 包含头文件
//5. 处理订阅消息(编写回调函数)
void callback(std_msgs::String msg){
ROS_INFO("订阅消息:%s", msg.data.c_str())
}
int main(int argc, char *argv[]){
//设置编码格式
setlocale(LC_ALL, "");
//2. 初始化 ROS 节点
ros::init(argc, argv, "sub_node");
//3. 实例化 ROS Master
ros::NodeHandle nh;
//4. 实例化 订阅者 对象
ros::Subscriber sub = nh.subscriber<std_msgs::String>("chatter", 10, callback);
//这句代码中nh.subscriber后的内容为:
//<发布的消息类型>("发布到的话题名称", 队列中保存消息数的最大值, 回调函数名称)
//6. 循环调用回调函数
while(ros::ok()){
ros::spinOnce(); //这里的spinOnce()可以让使在循环中的节点回过头检查是否有新消息包,防止节点错过消息包的接收。
}
//ros::spin();
//除了上面的使用while循环之外,还可以使用spin()函数,循环读取接受的数据,并调用回调函数
return 0;
}配置CMakeList.txt文件
在CMakeList.txt文件的结尾处添加编译规则和链接规则,不同的ROS版本,在添加时的写法可能有所不同,根据绿色字体的提示部分,进行编写,以我个人noetic(2022.10月版)版本为例(Ubuntu20.04)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
add_executable(pub_node src/pub_node.cpp)
target_link_libraries(pub_node
${catkin_LIBRARIES}
)
add_executable(sub_node src/pub_node.cpp)
target_link_libraries(sub_node
${catkin_LIBRARIES}
)执行
- 首先编译代码
- 可以在VSCode编译器的终端中使用Ctrl + shift + B 执行编译
- 在VSCode中可能出现问题导致无法编译,如果出现这种情况,可以打开一个终端,进入catkin_ws文件,使用指令
catkin_make
,对其目录下的所有文件进行编译,编写的代码如节点文件,message文件都在catkin_ws目录下的src文件中。
- 运行roscore,运行发布者和订阅者节点
- 首先编译代码
注意:
- main函数声明时,
int main(int argc, char const *argv[]){}
有时默认argv被const修饰,需要删除const修饰。 - 订阅时,第一条数据丢失是因为publisher还未在roscore注册完毕。可以通过加入休眠
ros::Duration(3.0).sleep()
延迟第一条数据的发送。 - 如果出现No such file or directory等报错时,需要先确认CMakeList.txt文件中的相关配置是否出现问题。
- main函数声明时,
话题通信基本操作(python)
python的实现与C++基本类似,但细节上有些不同。
流程:
- 新建功能包
- 编写发布方实现
- 编写订阅者实现
- 为py文件添加 可执行权限
- 编辑配置文件
- 执行
新建功能包
进入工作空间catkin_ws的src子目录中cd ~/catkin_ws/src/
,创建一个软件包catkin_create_pkg test_pub_sub rospy std_msgs
,名为test_pub_sub,依赖项为rospy和std_msgs。
在建好功能包之后,可以退回到catkin_ws工作空间,直接执行编译操作catkin_make
,这样做的目的是为了让这个新建的包进入到ROS的软件包列表,以便后续运行时,ROS能在列表中找到这个包。当然后续再进行编译也是可以的。(这里编译操作不是在编译python文件,因为python代码不需要编译)。
之后,在新建的包下,新建一个scripts文件夹,这个是用来装python节点的子目录。在scripts中新建节点文件,pub_node.py 和 sub_node.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
29
30
31
32
33
34
35
36
37
38
39
40
41"""
流程:
1. 导包
2. 初始化 ROS 节点
3. 实例化 发布者 对象
4. 组织被发布的数据,编写逻辑,发布数据
*/
"""
#! /usr/bin/env python3
#按照py的惯例,第一行是python的解释器,Ubuntu20.04默认是python3的版本
#Coding = utf-8
#1. 导包
import rospy #在python中rospy就相当于与C++中的ROS大管家 ros::NodeHandle。
from std_msgs.msg import String
if __name__ == "__main__":
#2. 初始化ROS节点
rospy.init_node("pub_node")
#3. 实例化发布者对象
pub = rospy.Publisher("chatter", String, queue_size = 10)
# 这里与C++不同,rospy.Publisher函数中的三个参数分别是 话题名称,话题中消息包的类型,消息包缓冲长度。写法相对于C++来说,更简单。
#4. 组织被发布的数据,编写逻辑,发布数据。
msg = String() #创建msg对象
msg_front = "Hello 你好"
count = 0 #计数器
#设置循环频率
rate = rospy.Rate(10)
while not rospy.is_shutdown():
#拼接字符串
msg.data = msg_front + str(count)
pub.publish(msg)
rate.sleep()
rospy.loginfo("发布的数据:%s", msg.data)
count += 1订阅方实现
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"""
流程:
1. 导包
2. 初始化 ROS 节点
3. 实例化 订阅者 对象
4. 处理订阅消息(编写回调函数)
5. 设置循环调用回调函数
"""
#! /usr/bin/env python3
#Coding = utf-8
#1. 导包
import rospy
from std_msgs.msg import String
#4. 编写回调函数
def callback(msg):
rospy.loginfo("订阅信息:%s", msg.data)
if __name__ == "__main__":
#2. 初始化 ROS 节点
rospy.init_node("sub_node")
#3. 实例化 订阅者 对象
sub = rospy.Subscriber("chatter", String, callback, queue_size = 10)
#rospy.Subscriber的四个参数分别是 话题名称,消息包类型,回调函数名,缓存队列长度。
#5. 设置循环调用毁掉函数
rospy.spin()添加可执行权限
在终端中进入功能包的scripts目录,执行chmod +x pub_node.py
,chomd +x sub_node.py
。如果没有任何提示,说明权限添加成功。配置CMakeLists.txt
1
2
3
4
5catkin_install_python(
scripts/pub_node.py
scripts/sub_node.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)执行
- 运行roscore
- 运行发布者节点,订阅者节点
自定义话题通信的message
在ROS的通信协议中,数据载体是较为重要的组成部分,ROS中自带了许多针对不同使用场景的消息包,类似于std_msgs这样的数据包有非常的多。但是,当官方给出的消息包已经不能满足我们的需求时,可以自己定义新的消息类型。
msgs其实就是简单的文本文件,想要自定义一个消息包的主要流程如下:
- 创建消息包
- 按照固定格式创建并编写msg文件
- 配置相关文件
- 编译生成可以被python或C++调用的中间文件
创建消息包
在 catkin_ws/src 路径下打开终端,使用
catkin_create_pkg qq_msgs roscpp rospy message_generation message_runtime
新建一个名为qq_msgs的功能包,其中的依赖项除了一直使用的 roscpp,rospy 之外,还包含了两个新的依赖项 message_generation 和 message_runtime,这两个是消息包生成和运行时所需要的依赖项。
之后,在新创建的功能包中,创建一个新的目录 msg,在 msg 文件夹中创建一个新的.msg消息类型的文件,假设命名为 Person.msg。编写msg文件
格式可以参考ros.index中的消息类型,为 数据类型 变量名。1
2
3string name
uini16 age
float64 height编写配置文件
CMakeLists.txt文件的配置
确定CMakeLists.txt文件中有 message_generation 和 message_runtime1
2
3
4
5
6
7find_package(catkin REQUIRED COMPONENTS
message_generation
message_runtime
roscpp
rospy
std_msgs
)取消add_message_files的注释,并更改其中的 .msg 文件为如下代码
1
2
3
4add_message_files(
FILES
Person.msg
)取消generate_messages的注释,这句代码表明新的消息类型需要依赖的其他消息包列表,这里我们只用到了std_msgs中的数据类型,所以只添加一个std_msgs就够了
1
2
3
4
5generate_messages(
DEPENDENCIES
std_msgs
)取消注释执行时的依赖项catkin_package
1
2
3
4
5
6catkin_package(
# INCLUDE_DIRS include
# LIBRARIES qq_msgs
CATKIN_DEPENDS message_generation message_runtime roscpp rospy std_msgs
# DEPENDS system_lib
)package.xml文件的配置
在package.xml文件的依赖项列表中添加编译依赖和执行依赖,确保 build_depend 和 exec_depend 都列出了 message_generation 和 message_runtime。缺失的话就补全1
2
3
4
5
6
7<build_depend>message_generation</build_depend>
<build_depend>message_runtime</build_depend>
·
·
·
<exec_depend>message_generation</exec_depend>
<exec_depend>message_runtime</exec_depend>编译
在工作空中执行catkin_make,没有报错则编译成功
检查是否进入ROS的消息列表:执行rosmsg show qq_msgs/Person
,如果能看到消息类型的显示,与自定义的结构一样,则成功。
服务通信
服务通信也是ROS中常用的通信模式,是基于请求响应模式的,是一种应答机制。例如,一个节点需要向相机节点发送拍照请求,相机节点处理请求,并返回处理结果。因此服务通信更适合于对实时性有要求,具有一定的逻辑处理的应用场景。相比话题通信,服务通信模型更简单。
- 服务通信是以请求响应的方式实现不同节点之间数据交互的通信模式。
- 用于偶然的,对实施性有要求,有一定逻辑处理需求的数据传输场景
服务通信的理论模型
服务通信涉及到的角色有三个
- ROS Master(管理员)
- Server(服务器)
- Client(客户端)
ROS Master负责保管Server和Client的注册信息,并匹配话题相同的Server和Client,帮助两者建立连接,之后Client发送请求信息,Server返回响应信息。
- 0)Server注册
Server启动后,会通过RPC在ROS Master中注册自身信息,其中包含提供的服务名称。ROS Master会将节点的注册信息加入到注册表中。 - 1)Client注册
Client启动后,也会通过RPC在ROS Master中注册自身信息,包含需要的请求的服务的名称。ROS Master会将节点的注册信息加入到注册表中。 - 2)ROS Master实现信息匹配
ROS Master会根据注册表中的信息匹配Server和Client,并通过RPC想Client发送Server的TCP地址信息。 - 3)Client发送请求
Client根据步骤2的响应信息,使用TCP与Server建立网络连接,并发送请求数据。 - 4)Server发送响应
Server接受,解析请求数据,并产生响应结果返回给Client。注意1:Client的请求被处理时,确保Server已启动。
注意2:Server和Client可以存在多个
服务通信自定义srv
假设有这样一个需求,客户端提交两个整数到服务端,服务端求和并响应结果到客户端,我们需要创建这样的一个数据载体。
srv与话题通信中的msg的可用数据类型一致,实现流程也类似:
- 按照固定格式创建srv文件
- 编辑配置文件
- 编译生成中间文件
定义srv文件
服务通信中,数据分成两部分,请求和响应,在srv文件中请求和响应的数据使用---
分隔符进行分隔。具体步骤为:先在功能包中新建一个srv目录,添加要编写的 xxx.srv 文件,文件的内容如下:1
2
3
4
5
6# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum编辑配置文件
package.xml中添加编译依赖和执行依赖1
2
3<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
CMakeLists.txt编辑 srv 相关配置
1 | find_package(catkin REQUIRED COMPONENTS |
1 | add_service_files( |
1 | generate_messages( |
1 | catkin_package( |
注意:在这里catkin_package中配置的是find_package中的依赖包的依赖包,官网中没有配置,但在这里配置了也没有问题。