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

仿真机器人MetaEngine

本文面向工程用户,详细说明如何将基于MetaEngine + ROS2 Gem的仿真机器人端到端接入R2C,包括:

  1. 概念与总体流程。
  2. 配置文件逐项解读(r2c_ur5e.yaml为范本)。
  3. 配置项含义与取值建议。
  4. 端到端接入步骤(环境 > MetaEngine仿真侧 > R2C配置 > 启动 > 联调验证)。

一、为什么仿真机器人也能接入R2C

R2C并不直接对接某个仿真器,而是对接标准ROS2接口。MetaEngine启用ROS2 Gem后,仿真中的机器人会把运动学、传感器、控制器以标准ROS2 Topics/Services/Actions对外暴露。

表1 ROS2接口示例

仿真侧(ROS2 Gem)

ROS2接口

R2C侧消费方式

ROS2RobotControl关节状态

/joint_states(sensor_msgs/JointState)

订阅

ROS2CameraSensor相机

/xxx/camera_image_color(sensor_msgs/Image)

订阅+编码

关节位置控制器

/arm_position_controller/commands(Float64MultiArray)

发布

夹爪控制器

/hand_position_controller/commands(Float64MultiArray)

发布

轨迹Action Server

FollowJointTrajectory Action

动作客户端

TF广播

/tf、/tf_static

可选,预留

因此R2C的Ros2HardwareAdapter对真实机器人与仿真机器人完全一视同仁:它只负责“订阅ROS2话题、按配置翻译成R2C观测;把R2C动作翻译回ROS2命令”。仿真与真实的差别只是对端是谁,配置范式完全相同。

相关参考:

二、端到端数据流总览

  • 观测上行:订阅ROS2 > streams > device_to_r2c > Observations > session.publish_observations() > Zenoh > 云端。
  • 控制下行:云端Actions > Zenoh > r2c_to_device > 命令dict > adapter.send_action() > 路由到ROS2发布器/服务/动作 > 仿真机器人执行。

三、端到端接入步骤

需要进行如下准备环境、MetaEngine(O3DE)仿真侧准备、编写R2C客户端通信配置、编写仿真机器人硬件配置以及启动并验证。

准备环境

表2 环境要求

配置项

要求

操作系统

Linux(推荐Ubuntu 22.04 / 24.04)

ROS2

Humble (22.04) / Jazzy (24.04)

Python

≥ 3.10

rclpy

随ROS2安装

message_filters

可选:pip install message_filters(多路观测时间同步)

执行如下命令,确认ROS2环境:

source /opt/ros/${ROS_DISTRO}/setup.bash 
python3 -c "import rclpy; print('rclpy OK')"

R2C端侧进程与MetaEngine仿真进程需在同一ROS2域(ROS_DOMAIN_ID 一致)才能互相发现话题。

MetaEngine(O3DE)仿真侧准备

  1. 在O3DE工程中启用ROS2 Gem(Project Manager > Gems > ROS2)。
  2. 导入机器人URDF:使用ROS2RobotImporter导入机器人模型,生成物理模型与关节。
  3. 为机器人挂载:
    • ROS2RobotControl/控制器 > 发布/joint_states、暴露关节命令话题。
    • ROS2CameraSensor(手/头/外置)> 发布sensor_msgs/Image。
    • 夹爪等执行器 > Float64MultiArray 命令话题。
  4. 运行仿真并验证话题可用:
ros2 topic list 
ros2 topic echo /joint_states --once
ros2 topic hz /joint_states 

记录下仿真侧实际发布的话题名与消息类型,这是后续配置subscriptions / command_publishers的依据。

编写R2C客户端通信配置

config/client_config.yaml(Zenoh通信):

project_id: "test-tenant"      # 与云端一致
device_id: "sim-ur5e-001"      # 设备标识
client_id: "sim-edge-001"
endpoints: []                  # 留空自动发现,或填 Router 地址
mode: "peer"                   # peer 或 client
protocol: "zenoh"

编写仿真机器人硬件配置

hardware.type: "ros2",配置主体为r2c_ur5e.yaml(详见四、配置文件逐项详解)。按仿真侧实际话题名/消息类型调整。

启动并验证

source /opt/ros/${ROS_DISTRO}/setup.bash
# 保险起见先用 dry_run 验证链路,再真正驱动
python -m r2c_sdk.cloudroboclient \
  --client-config config/client_config.yaml \
  --robot-config r2c_ur5e.yaml \
  --log-level INFO

启动后应看到日志:

  • Connecting ROS2 hardware adapter / ROS2 adapter connected (subscriptions=.., publishers=..)。
  • 观测正常发布、被云端接收并回推Actions。
  • init_joints将机械臂/夹爪摆到初始位。

四、配置文件逐项详解

以下以r2c_ur5e.yaml为主的仿真配置说明。

runtime:运行/控制循环

表3 配置项说明

配置项

说明

默认/建议

publish_hz

动作执行频率,也决定观测发布检查频率。

10–30 Hz

max_duration_s

最大运行时长(秒),0表示无限运行(Ctrl+C停止)。

0

dry_run

true时不执行Actions,但仍发观测,用于安全联调。

false

action_response_timeout_s

发布观测后等待Action的超时,超时则重发观测。

≥ 30(弱网/大模型)

max_enqueue_actions_per_chunk

每个chunk最多入队的Action步数,多余丢弃;-1全部采用。

-1

enable_action_chunk_alignment

是否启用chunk对齐,避免轨迹跳变。

false

skip_initial_observations

启动时跳过的观测数(让动作队列先填满)。

1

async_request.enabled

异步请求融合开关(一般false)。

false

keyboard_control.enabled

启用键盘控制(空格键:暂停、h键:回home、e键:退出)

true

hardware:ROS2硬件适配器

hardware:
  type: "ros2"
  config:
    node_name: "r2c_ur5e_ros"     # ROS2 节点名(与其他节点不重名)
    spin_timeout_sec: 0.05        # spin_once 超时(秒)
    auto_spin: True               # 是否自动启动 spin 线程
表4 配置项说明

配置项

说明

node_name

ROS2节点名。多台仿真机器人应使用不同节点名,避免冲突。

spin_timeout_sec

spin_once的处理超时,默认0.05s。

auto_spin

True时SDK自动启动后台spin线程处理回调。

hardware.config.init_joints:启动自摆位

连接成功、发布器就绪后,主动下发初始位(把机械臂/夹爪摆到安全初位)。_publish_init_joints 在 connect() 末尾调用。

init_joints:
  enabled: true            # 是否启用
  delay_sec: 3.0           # 连接后延迟多少秒再下发
  entries:
    - publisher: arm       # 对应 command_publishers 的一个发布器
      message:             # 直接构造该发布器类型消息
        data: [0.0, -1.9373155, 1.204277, -0.9075712, 4.694936, -0.0872665]
    - publisher: hand
      message:
        data: [0.0, 0.0]
表5 配置项说明

配置项

说明

enabled

是否在启动时执行初始摆位。

delay_sec

连接后延迟秒数,等待对端控制话题就绪。

entries[].publisher

指向command_publishers中的发布器名。

entries[].message

直接以dict构造该发布器消息(此处为 std_msgs/Float64MultiArray.data)。

entries[].joints(替代 message)

也可提供joints+joint_names,SDK自动构造 trajectory_msgs/JointTrajectory 单点。

hardware.config.subscriptions:观测订阅(上行)

每个订阅项把一个ROS2 Topic拉进streams。一个典型订阅项如下所示:

joint_states:                     # 订阅流名称(任意起名,用于 store_as)
  topic: /joint_states
  msg_type: sensor_msgs.msg.JointState
  qos: 10
  max_update_hz: 10               # 限频,避免高频洪泛
  store_as: joint_states          # 在 streams 中的键名
  include_fields: ["name", "position", "velocity", "effort"]
  field_aliases:
    name: names                    # 字段重命名 name > names
  transforms:                      # 订阅级转换链(可选)
    - select_joints_by_name:
        names: [... 8 个关节名 ...]
表6 配置项说明

配置项

说明

是否必需

topic

ROS2话题名。

是

msg_type

ROS2消息类型(点分路径,如 sensor_msgs.msg.Image)。

是

qos

QoS 队列深度。

否(默认 10)

store_as

写入streams的键名(默认用流名)。

否

max_update_hz

最大更新频率,用于降采样。

否

include_fields

只提取指定字段。

否

field_aliases

字段重命名映射(name>names)。

否

store_raw_message

true保留原始ROS消息(相机图像需用它)。

否

transforms

订阅级转换链(如 select_joints_by_name)。

否

相机订阅示例(UR5e):

head:
  topic: /head_camera/camera_image_color
  msg_type: sensor_msgs.msg.Image
  qos: 10
  store_as: head
  store_raw_message: true      # 保留 Image 对象,供下游 ros_image_to_jpeg 使用

hardware.config.command_publishers:命令发布(下行)

每个发布项把一个R2C命令映射到ROS2Topic:

command_publishers:
  arm:                             # 发布器名称(供 target/init_joints 引用)
    topic: "/arm_position_controller/commands"
    msg_type: std_msgs.msg.Float64MultiArray
    qos: 10
  hand:
    topic: "/hand_position_controller/commands"
    msg_type: std_msgs.msg.Float64MultiArray
    qos: 10
default_command_publisher: arm     # 未显式指定时的默认发布器
action_targets:                    # 语义 target > 发布器名
  arm: arm
  hand: hand
表7 配置项说明

配置项

说明

topic / msg_type / qos

目标话题、消息类型、QoS。

default_command_publisher

命令未携带publisher/target时的默认发布器。

action_targets

逻辑目标名 > 发布器名的映射,供r2c_to_device的target解析。

下行通道:

Topic 发布器(推荐,低延迟):command_publishers。

translator:数据翻译层

type: "configurable",由device_to_r2c(ROS2 > R2C)与r2c_to_device(R2C > ROS2)两组映射构成。

device_to_r2c: 组R2C观测

关节位置:

- target_key: "joint_states.names"            # 目标标准字段
  default: ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint",
            "wrist_1_joint", "wrist_2_joint", "wrist_3_joint", "gripper"]
- target_key: "joint_states.position"         # 来自订阅 streams
  source: streams.joint_states.position
  transforms:
    - array_to_list
    - slice: { start: 0, end: 6 }             # 前 6 维 = 机械臂
- target_key: "joint_states.position"         # 第 7 维 = 夹爪行程
  target_index: 6
  source: streams.joint_states.position
  transforms:
    - array_to_list
    - slice: { start: 6, end: 8 }             # 夹爪两指
    - my_gripper_fingers_to_stroke            # 两指求和 > 行程(米)

相机图像:

- target_key: "images.color.head"
  source_path: "streams.head"                 # 原始 Image 对象(store_raw_message)
  transforms: ros_image_to_jpeg               # 编码为 JPEG 字节
- target_key: "images.color.external"
  source_path: "streams.external"
  transforms: ros_image_to_jpeg

device_to_r2c映射项的字段:

表8 字段说明

字段

说明

target_key

R2C观测标准字段(joint_states.names/.position、images.color.xxx等)。

source / source_path

取数来源。streams.<store_as>.字段或streams.<store_as>

source_index / target_index

源/目标数组下标。

default

固定值(无source时用,如关节名表)。

transforms

转换链(array_to_list、slice、ros_image_to_jpeg、my_gripper_fingers_to_stroke等)。

target_key与task一起构成最终Observations(云端策略所需字段详见R2C观测模型)。

r2c_to_device:生成ROS2命令

机械臂(前6维 > arm发布器):

- target: "commands_by_publisher.arm.data"    # 写入哪个发布器的哪个字段
  source: "joint_states.position"
  use_assign_dotted: true                     # 用点号路径赋值
  required: true
  transforms:
    - slice: { start: 0, end: 6 }             # 6 维机械臂
    - list_to_ros_float64_multi_array         # 组装 Float64MultiArray

夹爪(第7维行程 > 展开两指 > hand发布器):

- target: "commands_by_publisher.hand.data"
  source: "joint_states.position"
  source_index: 6
  use_assign_dotted: true
  required: true
  transforms:
    - my_gripper_stroke_to_fingers            # 行程 > [S/2, S/2] 两指
    - list_to_ros_float64_multi_array

r2c_to_device映射项字段:

表9 字段说明

字段

说明

target

命令写入目标,如commands_by_publisher.<name>.data。

source

取自云端Action的字段,如joint_states.position。

source_index

源下标(取第7维夹爪行程)。

required

该字段是否必需。

use_assign_dotted

是否用点号路径完成嵌套赋值。

transforms

反向转换链(slice, my_gripper_stroke_to_fingers, list_to_ros_float64_multi_array …)。

最终翻译器输出commands_by_publisher(或commands_by_service/commands)给adapter.send_action(),由适配器按发布器名路由并发布到对应 Topic。

自定义转换器:r2c_to_device里可引用自定义转换器(如SO101的so101_gripper_value_to_joint)。参考Transformer使用指导。

五、常见ROS转换器速查

参考Transformer使用指导

来自core/transformers.py注册表,可在subscriptions.transforms / device_to_r2c.transforms / r2c_to_device.transforms中引用:

表10 转换器作用说明

转换器

作用

array_to_list

numpy或数组转换到list

slice {start,end}

截取数组片段。

ros_message_to_mapping

ROS消息转换到dict。

ros_image_to_jpeg/png/webp

sensor_msgs/Image转换到编码字节。

ros_compressed_image_to_jpeg/...

压缩图像转换到编码字节。

select_joints_by_name {names}

按关节名过滤出指定关节子集。

list_to_ros_float64_multi_array

list转换到std_msgs/Float64MultiArray。

list_to_ros_joint_state {names}

list转换到sensor_msgs/JointState。

list_to_ros_move_request

6维list转换到JAKA Move服务请求。

scalar_to_ros_float64

标量转换到std_msgs/Float64。

parallel_gripper_fingers_to_stroke / *_stroke_to_fingers

两指位置与行程换算(0/对称)。

my_gripper_fingers_to_stroke / my_gripper_stroke_to_fingers

UR5e夹爪行程换算变体。

六. 调试与验证

  1. 先配置dry_run验证链路,如下所示。

    runtime:  
      dry_run: true   # 只发观测、不真正驱动仿真机器人

    确认日志显示:ROS2订阅有数据、device_to_r2c成功、观测已发布、云端能收到。无误后再改false。

  2. 观测数据诊断。

    查看ROS2侧话题是否有数据:ros2 topic echo /joint_states、ros2 topic hz /head_camera/camera_image_color。

    如果translator.device_to_r2c因缺字段跳过:日志会打印缺失的source_path,对照store_as 与 include_fields检查。

  3. 执行诊断命令,确认command_publishers的话题名与仿真侧控制器话题完全一致(大小写、前导/)。

    ros2 topic echo /arm_position_controller/commands

    观察是否收到Float64MultiArray。

    多台仿真机器人保证node_name不同、话题带命名空间。

  4. 执行如下命令,联动Policy Server端到端。

    # 终端 1:云端推理
    python -m r2c_sdk.inference.r2c_lerobot_policy_server \
      --client-config config/client_config.yaml \
      --cloud-config config/cloud_ur5e_act.yaml \
      --policy-type act --device cpu
    # 终端 2:仿真机器人边缘端
    python -m r2c_sdk.cloudroboclient \
      --client-config config/client_config.yaml \
      --robot-config r2c_ur5e.yaml

七、常见问题处理

表11 常见问题处理方法

症状

可能原因

解决办法

ROS2 adapter connected ... 但没有观测

话题名/消息类型不对,或DDS域不一致。

ros2 topic list;export ROS_DOMAIN_ID=x两端一致。

ModuleNotFoundError: No module named 'rclpy'

未执行ROS2的setup.bash脚本。

source /opt/ros/${ROS_DISTRO}/setup.bash

No ROS2 command publisher found for 'arm'

commands_by_publisher.<name> 与发布器名不一致。

对齐r2c_to_device.target 与 command_publishers键名

KeyError: 'joint_states.position'

订阅数据未就绪或字段被过滤。

检查include_fields是否含position,订阅是否取到数。

动作不执行

dry_run: true /发布器话题不对。

改false;核对话题名与切片/转换。

图像为空

未store_raw_message: true或编码转换缺失。

相机订阅加 store_raw_message: true,device_to_r2c 用 ros_image_to_jpeg

多流时间不对齐

未开启观测同步。

启用observation_sync + 安装message_filters。

相关文档