更新时间:2026-09-07 GMT+08:00
分享

R2C协议扩展字段方式

R2C协议的Observations / Actions只有标准字段(joint_states、end_effector_poses等)。要传输深度图、语义指令、自定义信号这类标准字段无法承载的数据,不修改.proto(避免破坏协议兼容),通过extensions字段旁路扩展。

核心思想:自描述盒子 —— 每个扩展值自带dtype(类型)+ shape(维度),消费端拿到数据即可解析,零握手、无需外部schema。

Observations / Actions
├── joint_states / end_effector_poses / ...   ← 固定字段(proto 定义)
└── extensions:                               ← 自贴标签的盒子
      "depth_image"  → {dtype: FLOAT32, shape: [480,640], data: <bytes>}
      "language_goal"→ {dtype: STRING,  shape: [],       data: "pick cube"}

协议定义

DType枚举(common.proto)

enum DType {
  DTYPE_UNSPECIFIED = 0;
  FLOAT32 = 1; FLOAT64 = 2; INT32 = 3; INT64 = 4;
  UINT8 = 5; BOOL = 6; STRING = 7; BYTES = 8;
}

ExtensionValue消息(common.proto)

message ExtensionValue {
  DType dtype = 1;           // 元素类型(必填)
  repeated int32 shape = 2;  // 维度,[] = 标量,[H,W,C] = 图像
  bytes data = 3;            // flat row-major 原始数据
  string mime_type = 4;      // 可选,压缩/编码标注(如 "image/jpeg")
}

在消息中的位置

message Observations {  map<string, ExtensionValue> extensions = 12; }   // 观测
message Actions     {  map<string, ExtensionValue> extensions = 8;  }   // 动作
// ObservationsH264 同样携带 extensions = 12,行为一致

配置驱动用法

在mapper rule中添加extension块:

device_translator:
  mappings:
    # ── 完整示例:深度图(ndarray → bytes → 包装) ──
    - source: "camera.depth_frame"
      target: "extensions.depth_image"        # 必须 extensions. 前缀
      transforms:
        - ndarray_to_bytes                    # 可选:前置变换链
      extension:
        dtype: FLOAT32                        # 必填
        shape: [480, 640]                     # 可选,默认 [] = 标量
        mime_type: ""                         # 可选
    # ── 标量示例:语言指令 ──
    - source: "task.language_goal"
      target: "extensions.language_goal"
      extension:
        dtype: STRING
    # ── 常量示例:无 source,直接写 default ──
    - default: [1, 1, 1]
      target: "extensions.home_pose"
      extension:
        dtype: FLOAT64
        shape: [3]

执行流程(源码:config_mapper.py)

1. 从 source 读取原始值(或取 default)
2. 依次应用 transforms 变换
3. 应用 slice(如有)
4. _wrap_extension_value():按 dtype/shape/mime_type 打包为 ExtensionValue dict
5. 写入 output["extensions"]["<字段名>"]
6. 序列化时由 _parse_extensions() 转为 protobuf ExtensionValue

打包细节

表1 打包方式说明

输入值类型

打包方式

bytes / bytearray

直接作为data(配合ndarray_to_bytes等transform)。

str(dtype=STRING)

UTF-8编码进data。

标量/嵌套 list(数值 dtype)

递归flatten后逐元素struct.pack小端打包。

default 常量

与数值dtype同规则(如[1,1,1] + shape:[3] → 3 元素buffer)。

编程接口(写自定义translator/adapter时)

r2c_sdk.common.models.wrappers.common.ExtensionValue:

from r2c_sdk.common.models.wrappers.common import ExtensionValue
import numpy as np
# ── 写入端:工厂方法 ──
ExtensionValue.from_ndarray(np.zeros((480, 640), np.float32))
#   → dtype="FLOAT32", shape=[480,640], data=arr.tobytes()
ExtensionValue.from_string("pick cube")     # dtype=STRING
ExtensionValue.from_scalar(3.14)            # FLOAT64,struct "<d" 小端
ExtensionValue.from_scalar(True)            # BOOL,\x01/\x00
ExtensionValue.from_scalar(42)              # INT64,8 字节小端有符号
# 直接构造(等效)
ExtensionValue(dtype="UINT8", shape=[224,224,3], data=jpeg_bytes, mime_type="image/jpeg")
# ── 消费端:解析方法 ──
ev.to_ndarray()   # np.frombuffer(data, dtype).reshape(shape);标量 shape=[] 返回标量
ev.to_string()    # 仅 STRING,UTF-8 解码
ev.to_scalar()    # 仅标量数值/BOOL
# STRING/BYTES 也可直接读 ev.data

消费端解析(云端/任意第三方)

import numpy as np
ext = msg.extensions["depth_image"]
if ext.mime_type:                       # 先按 mime 解码(如 JPEG)
    arr = cv2.imdecode(np.frombuffer(ext.data, np.uint8), cv2.IMREAD_UNCHANGED)
else:
    dtype = {"FLOAT32": np.float32, "FLOAT64": np.float64, "INT32": np.int32,
             "INT64": np.int64, "UINT8": np.uint8, "BOOL": np.bool_}[ext.dtype]
    arr = np.frombuffer(ext.data, dtype=dtype).reshape(ext.shape)

下发方向(r2c_to_device)同理:云端把扩展字段写进Actions.extensions边端mapper从extensions.<名字>读取。

相关文档