ROS通信模型

ROS节点构成了一个计算图(computation graph),它是一个p2p的网络结构,节点之间的拓扑结构为 mesh(网状)。节点必须与其他节点通讯,否则就是个普通程序。ROS通讯中,节点通过XML-RPC远程调用来实现建立连接,传输数据。远程调用负责管理节点对计算图中信息的获取与更改,还有一些全局的设置, 但RPC不直接支持数据的流传输(而是通过 TCPROS与 UDPROS)

每个ROS节点运行一个XML-RPC server,ROS的通讯模块在ros_comm

topic通信模型

我运行了两个发布者节点:Pub和second,一个订阅者节点:Sub,话题名为topic,详细情况如下:


上面列出了三个节点的端口号,IP都是相同的,因为都在本机上,roscore的uri为http://192.168.0.106:11311,整个通信模型如下:

发布者节点通过一个名为registerPublisher的远程调用(XML-RPC),注册它们的话题和消息类型到主节点。同样地,订阅者通过registerSubscriber()远程调用,注册到主节点,这样主节点就获知了双方的uri、话题名称、消息类型。如果仅仅话题名一样,消息类型不一样,也是不能通信的。

订阅者注册时,主节点会回复注册同一话题的所有发布者,然后订阅者就直接与发布者通信,基于TCPROS协议传输消息,这就跟主节点无关了。当又有新的发布者注册同一个话题到主节点时,主节点执行publisherUpdate()给订阅者,回复一个新的发布者list。

二者通信是基于TCPROS协议,过程与三次握手类似,订阅者与发布者建立第一次连接,传输topic信息,然后再根据发布者返回的topic 信息,建立第二次连接,发布者开始传输具体的数据。

对于unregistration,发布者执行远程调用unregisterPublisher(),订阅者执行unregisterSubscriber()远程调用。

举个例子,我们可以看一个节点的信息:
rosnode info.png
话题tf_static是本节点传递给amcl节点的,方向outbound说明了这一点,也就是本节点负责发布,消息传递方式是TCPROS;话题joint_states的传递方向是inbound,也就是本节点从节点joint_state_publisher订阅了此话题

TCPROS中的重点:

  • Md5:如果两个话题名一样而消息类型不同,会无法通信成功。TCPROS为了保证两边传输数据类型一致,会在协议头中给出话题名的md5 hash算法处理过的值,而每次你生成新的msg时,md5的值都会因为你内部数据类型的变动而改变。

  • Subscriber 选项tcp_nodelay :如果是“1” 则给socket 设置 TCP_NODELAY 选项,降低延迟,可能会降低传输效率。

  • Service client 选项:persistent 设置为1,则service的链接会一直开放给多个 service request

使用netstat和wireshark研究通信过程

但是我在使用ROS时, 发现ROS命令显示的端口号并不准确,使用netstatwireshark是找不到的,最好以后两者为准

启动了一对发布订阅者的程序,用netstat查看如下,排除rosout节点:

1
2
# 同IP所有建立通信的节点,排除rosout和SSH
netstat -anop | grep 0.109 | grep ESTA | grep -v rosout | grep -v 109:22

结果还需要去掉3个通信,就是sub-rosout, pub-rosout, python-rosout。python对应的进程是roscore
netstat的结果
显然pub和sub的通信端口不是rosnode info显示的,不知道这算不算ROS的bug

wireshark抓到的发布和订阅TCPROS通信
两者之间的TCP通信只有一问一答:Publisher发PSH和ACK,要求Subscriber数据尽快达到应用层,Subscriber发回ACK,从wireshark里看不到传递的消息,但ROS又不是加密的,这点我没明白什么意思

参考:ROS的弊端