Walker C1 EDU 共创者:技术架构¶
对应代码:
ubt_IL/walker_c1/(LeRobot 插件lerobot_robot_walker_c1)与ubt_IL/scripts/deploy/walker_c1/
架构¶
双进程桥接(与 walker_s2 / tienkung 同模式),C1 与 Walker S2 完全解耦(独立包、独立注册名、独立端口):
LeRobot 推理 (Python 3.12) Bridge2 (Python 3.10)
ZMQ PUB → 5661 (actions) ──► ZMQ SUB ← 5661
ZMQ SUB ← 5662 (status) ◄── ZMQ PUB → 5662
ZMQ SUB ← 5663 (JPEG images) ◄── camera relay ← ROS2 /sensor/camera/head/color/raw
│
ROS2 DDS
│
mc_state_msgs / mc_task_msgs
│
Walker C1 EDU 真机 / ubt_sim
| 端口 | 用途 | 占用方 |
|---|---|---|
| 5661 | ZMQ 动作(PUB→SUB) | 本插件 / bridge |
| 5662 | ZMQ 状态(PUB→SUB) | bridge / 本插件 |
| 5663 | ZMQ 图像(JPEG base64) | camera relay / 本插件 |
| 5690 / 5691 | 常驻推理服务 REQ / PUB | inference_server |
不要复用 walker_s2 的 5561-5563 或 ubt_sim C1 桥接的 5655-5658。
26 维向量¶
[0-6] 左臂 7 L_shoulder_pitch/roll/yaw, L_elbow_pitch/yaw, L_wrist_pitch/roll
[7-13] 右臂 7 R_shoulder_pitch/roll/yaw, R_elbow_pitch/yaw, R_wrist_pitch/roll
[14-19] 左手 6 left_thumb_swing/thumb_mcp/index_mcp/middle_mcp/ring_mcp/little_mcp
[20-25] 右手 6 right_thumb_swing/thumb_mcp/index_mcp/middle_mcp/ring_mcp/little_mcp
- 顺序唯一来源:
lerobot_robot_walker_c1/constants.py::ACTION_ORDER - 训练数据集 names、部署
action_order、bridge 按名散射全部以此为准 - 头 2 / 腰 3 / 腿 12 不在策略中:bridge 不下发,机器人保持当前位;仿真侧
to_controller_data用_hold_joint_targets(锚定 HOME_POSE)填充全部 53 关节,未下发关节不会掉落
13 维变体(单臂)¶
[0-6] 右臂 7 R_shoulder_pitch/roll/yaw, R_elbow_pitch/yaw, R_wrist_pitch/roll
[7-12] 右手 6 right_thumb_swing/thumb_mcp/index_mcp/middle_mcp/ring_mcp/little_mcp
- 注册表键
walker_c1_13d(constants.ROBOT_MODELS),部署配置configs/walker_c1_13d.json body.groups仍保留left_arm/left_hand,inactive_fill(13 个键)把左臂 7 + 左手 6 填成 home 并下发——这样左臂不会被放松,也不会被策略干扰- 数据集
walker_c1_sim_13d_1RGB(DIM=13转换)
31 关节状态顺序(仅用于状态解析)¶
BODY_JOINT_NAMES(constants.py)= L 臂 0-6 / R 臂 7-13 / head_yaw 14 / head_pitch 15 / waist_yaw 16 / waist_pitch 17 / waist_roll 18 / 左腿 19-24 / 右腿 25-30。bridge 只按名取 14 个臂关节(body_groups 的 head/waist 为空列表)。
ZMQ 消息¶
动作(单步):
{"left_arm": [7], "right_arm": [7], "head": [], "waist": [],
"left_hand": [6], "right_hand": [6], "ts": 1700000000.0}
chunk(act_async):追加 n_points / fps / inference_time_sec / obs_time_sec / chunk_id,每组的值为 [C, n]。
{"hold": 1} 立即清空轨迹并停止下发 RobotCommand(C1 在 half_sdk 下由运控自行保持关节位置,桥接不再以 300 Hz 重复末帧,避免伺服抖动)。
状态(bridge → 插件):{"left_arm": [7], "right_arm": [7], "head": [], "waist": [], "left_hand": [6], "right_hand": [6], "ts": ...}。
ROS2 接口¶
| 方向 | 话题 | 类型 |
|---|---|---|
| sub | /mc/sdk/robot_command |
mc_task_msgs/RobotCommand(JointCmd.control_mode=MODE_POSITION=2) |
| sub | /mc/left_hand/command、/mc/right_hand/command |
mc_task_msgs/JointCommand(mode=[5],names+position) |
| pub | /mc/sdk/robot_state |
mc_state_msgs/RobotState(joint_states,31 关节) |
| pub | /mc/{left,right}_hand/joint_states |
sensor_msgs/JointState(SDK 6 关节序) |
| pub | /sim/cmd_reset |
std_msgs/Bool(仅仿真) |
| 相机(仿真) | /sensor/camera/head/color/raw |
sensor_msgs/Image(rgb8,BEST_EFFORT) |
| 相机(真机) | /sensor/camera/head_stereo_left/color/raw |
shm_msgs/Image6m(1920×1536 yuv422/UYVY,BEST_EFFORT) |
| 相机覆盖 | WALKER_C1_CAMERA_TOPIC / WALKER_C1_CAMERA_MSG_TYPE |
覆盖话题与类型;/compressed 自动推断为压缩图,shm_msgs/Image6m 必须显式指定 |
消息包 mc_task_msgs / mc_state_msgs 与 walker_s2 的 walker_sdk_ros2 工作空间字段一致,默认复用 /ubt_IL/walker_s2/walker_sdk_ros2/install;可用 WALKER_C1_ROS_WS 指向独立工作空间。
文件职责¶
| 文件 | 职责 |
|---|---|
constants.py |
关节名/限位/READY_POSE/ACTION_ORDER/ROBOT_MODELS/inactive_fill_for |
config_walker_c1.py |
WalkerC1RobotConfig(注册名 walker_c1)、JSON 加载与校验、to_bridge_config |
walker_c1.py |
WalkerC1Robot:connect / 状态线程 / get_observation / send_action / chunk / home / disconnect |
hand_utils.py |
手部 6 通道限位裁剪(限位经 to_bridge_config 单点传给 bridge) |
camera/* |
ZMQ 图像订阅(base64 JPEG → ndarray,按 W/H resize) |
ros2_walker_c1_bridge.py |
ZMQ↔ROS2、按名散射、chunk 融合/加密/执行期 300 Hz、stop 停发、单例守卫 |
walker_c1_camera_relay.py |
sensor_msgs/Image → resize → JPEG → ZMQ 5663 |
chunk_processor.py |
纯 numpy 融合/插值/滤波(与 walker_s2 相同,原样复用) |
关键约束¶
lock_joints语义 = 跳过不下发。C1 必须为空列表;_validate()会丢弃不在 body 组内的条目。CANONICAL_JOINT_NAMES= 26(不含头/腰/腿)。若改成像 walker_s2 那样包含非 group 关节,_inactive_fill生成的键无法归属到空组,send_action分组会出错。_inactive_fill仅在joint_config命中ROBOT_MODELS时生成;部署 JSON 也要传--robot.joint_config=walker_c1_26d。- 手部限位单点定义:插件
hand_joint_limits→to_bridge_config()→ bridge 裁剪;bridge 内不再硬编码 V4 限位表。 - 第 4 个臂关节 SDK 文档叫
elbow_roll、URDF 叫elbow_pitch;ubt_sim bridge 做别名容忍,真机部署前需现场核对符号。 - 相机只有头部单目(无腕部、无深度);relay QoS 必须 BEST_EFFORT。仿真发未压缩
sensor_msgs/Image,真机发shm_msgs/Image6m(1920×1536 yuv422/UYVY);relay 两者都能吃(含 CompressedImage 兼容),解码后统一 resize 再 JPEG 编码进 ZMQ。 - 与 walker_s2 的插件运行在同一容器内,注册名不同(
walker_c1vswalker),不要互相 import。
快速自检¶
# 1. 包注册
python /ubt_IL/walker_c1/init_walker_c1.py
# 2. 配置派生(26 维 + 1 相机)
PYTHONPATH=/ubt_IL/lerobot/src:/ubt_IL/walker_c1/lerobot_robot_walker_c1 python - <<'PY'
from lerobot_robot_walker_c1 import WalkerC1RobotConfig
cfg = WalkerC1RobotConfig()
print(len(cfg.all_joints), list(cfg.cameras), cfg.zmq_cmd_port, cfg.lock_joints)
PY
# 3. bridge 默认配置(无需 ROS)
/usr/bin/python3 -c "
import sys; sys.path.insert(0,'/ubt_IL/walker_c1')
import ros2_walker_c1_bridge as b
print(b._DEFAULT_CFG['robot_model'], len(b._DEFAULT_CFG['body_joint_names']))
"