话题通的模型中涉及三类角色:

  1. ROS Master

  2. Talker

  3. Listener

ROS Master负责Talker和Listener的注册信息,并匹配话题(topic)相同的Talker和Listener建立连接,连接建立后,Talker发布的消息可以被Listener接收。建立连接后,可以不再需要ROS Master。

1、理论流程

  1. Talker注册

Talker会通过RPC在ROS Master中注册信息,包括自身发布消息的话题名称、自己的RPC地址

  1. Listener注册

同上,Listener会注册自身需要订阅的话题名和自己的RPC地址

  1. ROS Master实现信息匹配

ROS Master会根据注册表中的信息匹配Talker和Listener,并通过RPC先Listener发送Talker的RPC地址

  1. Listener先Talker发送请求

Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)

  1. Talker确认请求

Talker接受到请求后,通过RPC先Listener确认连接信息,并发送自己的TCP地址

  1. Listener与Talker建立连接

Listener根据返回到的TCP地址与Talker建立网络连接

  1. Talker向Listener发送消息

连接建立后,Talker可以开始向Listener发布消息

2、通俗理解

  1. 女方(打电话)找媒婆

  2. 男方(打电话)找媒婆

  3. 媒婆匹配信息,把女方信息(打电话)给男方

  4. 男方给女方打电话

  5. 女方确认男方信息,并把自己的微信给男方

  6. 男方通过微信联系女方

  7. 男方和女方的后续沟通使用微信继续,而不再用媒婆传话

在通俗理解中,打电话即RCP通信协议,微信即TCP通信协议

3、注意事项

  1. 使用的通信协议有RPC和TCP,前期与ROS Master相关的通信是RCP协议,后面Talker与Listener的直接通信使用TCP协议

  2. 步骤0和步骤1的顺序先后无所谓

  3. talker和listener可以同时存在多个

4、话题通信应用时的关注点

  1. 大部分实现已被封装
  2. 话题的设置
  3. 发布者和订阅放的实现
  4. 信息载体

5、代码实现

上面介绍的流程中的大部分工作都有ROS系统抽象封装好了,我们只需要简单调用即可。

  1. 实例化ROS句柄:

ros::NodeHandle nh;

  1. 发布方的注册:

ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);

  1. 订阅方的注册:

ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter", 10, doMsg);

我们可以发现,双方的匹配是根据注册函数中的第一个参数(chatter)决定的,即话题名称。

双方注册自身的信息后,接下来的工作由ROS系统完成。

发布方发布信息:

pub.publish(msg);

订阅方注册好信息后,就一直处于待接收状态,我们需要将订阅方阻塞即可:ros::spin();

循环读取接收的数据,并调用回调函数处理

6、自定义msg

std_msgs包括:

  • int8, int16, int32, int64 (或者无符号类型: uint*)
  • float32, float64
  • string
  • time, duration
  • other msg files
  • variable-length array[] and fixed-length array[C]

我们可以自定义复合类型的msg类型,比如创建一个自定义消息,该消息包含人的信息:姓名、身高、年龄。

自定义msg流程:

  1. 定义msg文件

在package新建目录msg,添加Person.msg

内容为:

string name
uint16 age
float64 height
  1. 修改配置文件

package.xml中添加编译依赖与执行依赖

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

CMakeLists.txt编辑 msg 相关配置

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs
## 配置 msg 源文件
add_message_files(
  FILES
  Person.msg
)
# 生成消息时依赖于 std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)
#执行时依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

需要注意的是,上述语句的顺序不能搞错,否则会报错

  1. 编译

执行命令cmake_make

C++ 需要调用的中间文件(.../工作空间/devel/include/包名/xxx.h)

Python 需要调用的中间文件(.../工作空间/devel/lib/python3/dist-packages/包名/msg)

  1. 调用的注意事项

为了方便代码提示以及避免误抛异常,需要先配置 vscode,将前面生成的 头文件和python文件路径配置进vscode的json配置文件

cpp:c_cpp_properties.json 的 includepath

python:settings.json的python.autoComplete.extraPaths

  1. 调用

发布者调用:

ros::Publisher pub=nh.advertise<current_msgs::Person>("chatter_person", 1000);

订阅者调用:

ros::Subscriber sub = nh.subscribe<current_msgs::Person>("chatter_person", 10, doMsg);

可以发现,函数后面的尖括号包含了我们自定义的信息类型,这个是C++的语法知识,叫做模板,后面会详细补充相关知识。

其他与发布订阅标准信息类型一样。