协议说明
需要: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,然后是断开期间排队的 reliablePUBLISH,最后是实时数据。 - 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 执行器和其他客户端。
常见的 ERROR 代码
Section titled “常见的 ERROR 代码”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 |
帧头畸形,桥随后关闭连接 |
- PROTOCOL.md(GitHub):完整规范:JSON 字段、名称规则、全部错误代码、队列、字节级示例,以及实现一个兼容的桥的清单。
- 连接与状态:UE 一侧的连接状态和排查。
- 准备 ROS 2 端:桥的参数和运行方式。
- 为自定义传感器添加 ROS 发布器:扩展 uBotROS,发布自己的传感器。