跳转到内容

协议说明

需要:uBotROS 0.1.x(含依赖 uBotCore、uBotSensor)

UE 与 ubot_ros_bridge 之间通过 TCP 协议(UBRB v1)通信,传输、帧格式、操作、会话与重连的要点如下,完整规范在 GitHub 上。

一次连接的过程可以按阶段看:

阶段 UE → 桥 桥 → UE
握手 HELLO HELLO_ACK
注册 全部 ADVERTISE,再全部 SUBSCRIBE;UE 不等 HELLO_ACK 订阅者数量变化时发 SUB_COUNT
数据 PUBLISH:发布者 id 加 CDR 字节 MESSAGE:订阅 id 加 CDR 字节
保活 PING,每 2 s 一次 PONG
出错 — ERROR
op 名称 方向 负载
1 HELLO UE → 桥 JSON:协议版本 protocol 和客户端名 client
2 HELLO_ACK 桥 → UE JSON:protocol、桥的版本 server、ros_distro
3 ADVERTISE UE → 桥 JSON:发布者 id、话题、类型、QoS
4 UNADVERTISE UE → 桥 JSON:id
5 PUBLISH UE → 桥 发布者 id(u32)加完整 CDR
6 SUBSCRIBE UE → 桥 JSON:订阅 id、话题、类型、QoS
7 UNSUBSCRIBE UE → 桥 JSON:id
8 MESSAGE 桥 → UE 订阅 id(u32)加完整 CDR
9 SUB_COUNT 桥 → UE JSON:每个发布者 id 当前的订阅者数量
10、11 PING、PONG 双向 空
12 ERROR 桥 → UE JSON:id(可为 null)、code、message

JSON 里的 QoS 是 {"reliability":"reliable","durability":"volatile","depth":10} 的形式,历史策略固定为 KEEP_LAST。

  • 桥监听 TCP(默认 0.0.0.0:8268),UE 来连接。一条 TCP 连接就是一个会话,UE 为每个 Game 或 PIE 世界开一条。
  • 所有整数都是小端。流里是一帧接一帧,帧之间没有别的字节;UE 开启 TCP_NODELAY,两端都可能把几帧合成一次发送,接收方要从字节流里重组帧。
  • 消息体是完整的 ROS 2 CDR 字节,包括 4 字节封装头,就是 rclpy.serialization.serialize_message 产生的字节。UE 自己序列化,桥不解码,用 Publisher.publish(bytes) 发布,用 create_subscription(..., raw=True) 订阅。
  • 协议是明文,没有认证和加密。

每帧是 12 字节的帧头加负载:

偏移 长度 字段 值
0 4 魔数 55 42 52 42,即 ASCII UBRB
4 1 版本 1
5 1 op 见上表
6 2 flags 保留:发送方写 0,接收方忽略
8 4 负载长度 N u32 小端
12 N 负载 N 可以是 0

收到 12 字节帧头后,接收方在读负载之前就检查它:魔数或版本不对,或 N 超过负载上限(默认 64 MiB),都是畸形帧头,接收方关闭连接,桥可能先回一个 protocol_error。桥对不带大数据的 op 还有更紧的上限,超过同样算畸形帧头。

  • 注册属于连接。连接结束时(任一方关闭、I/O 错误或空闲超时),桥释放这个连接注册的全部发布者和订阅。
  • 同一个话题在桥里只有一个 rclpy 发布者,被所有客户端共享并引用计数。同话题不同类型回 type_conflict,同类型不同 QoS 回 qos_conflict。
  • id 由 UE 选择,发布者和订阅各一个递增计数器,永不复用,重连后也不复用。
  • 每次连接成功后,UE 用同样的 id 重发全部注册:先是所有 ADVERTISE,再是所有 SUBSCRIBE,然后是断开期间排队的 reliable PUBLISH,最后是实时数据。
  • UE 每 2 s 发一次 PING;10 s 没有收到桥的任何数据,就断开并重连。桥对客户端有同样的规则,10 s 没收到数据就断开,还要求连接后 5 s 内完成 HELLO。
  • 失败或断开后,UE 每 Reconnect Interval Seconds(默认 2 s)重连,一次尝试最长 3 s。Disconnect 停止重连。
  • 锁存的数据不跨重连保留,所以 UE 在每次握手后重新发布合并的 /tf_static。
  • SUB_COUNT 在数量变化时发送,最快每 0.5 s 一次。UE 据此跳过没有订阅者的话题的编码;收到第一次报告之前数量未知,照常发布。
  • UE 的发送队列:best_effort 发布者只保留最新的一条待发消息,没有会话时直接丢弃;reliable 消息按顺序排队,重连期间也排,总量受 Max Queued Bytes 限制。
  • 桥的发送队列:每个客户端一个按字节计的队列,默认 128 MiB;队列满时先丢 best_effort 的 MESSAGE。慢客户端不会拖慢 ROS 执行器和其他客户端。

UE 把每个 ERROR 记成一条 Warning,并保持连接:

code 含义
unknown_type 消息类型的写法不对,不是已安装的 ROS 2 接口,或者加载失败
invalid_topic 话题不是合法的绝对 ROS 2 名称
type_conflict、qos_conflict 桥里同一个话题已经有另一种类型或 QoS
hello_required 在 HELLO 之前发了别的帧
unsupported_protocol HELLO 的 protocol 不是 1,桥随后关闭连接
too_many_clients 桥已达到 max_clients,随后关闭连接
message_too_large 订阅到的 ROS 消息超过负载上限,被丢弃
protocol_error 帧头畸形,桥随后关闭连接