天工行者 Dex:机器人本体部署(Thor / Jazzy,无容器)¶
本页说明如何把策略直接跑在机器人本体的 ARM 板(NVIDIA Thor,Ubuntu 24.04 + ROS 2 Jazzy + Python 3.12)上,不需要 Docker。数据转换与训练仍建议在工作站/容器内完成,本体上只做部署。
脚本位置:ubt_IL/scripts/deploy/tienkung_dex/arm_64/。
1. 运行架构(双 Python 栈,ZMQ 解耦)¶
层 A $VENV (3.12):lerobot-rollout / lerobot-train
│ 5559 动作 ▼ ▲ 5560 状态 ▲ 5558 JPEG(640×360)
层 B 系统 python3 (3.12) + Jazzy:ros2_deploy_bridge.py
│ /arm/cmd、/right_hand/set_motor_multi ▲ /robot_state(≈200Hz)
└ /ob_camera_head/color/image_raw (1280×720 → 缩放 640×360 → JPEG)
层 C 系统 python3 + Jazzy:robot_ready.py(预备位,与层 B 互斥)
| 层 | 解释器 | 跑什么 | 端口 / 话题 |
|---|---|---|---|
| A | $PROJECT_ROOT/.venv-dex(3.12) |
lerobot-rollout / lerobot-train |
5559 发动作 / 5560 收状态 |
| B | /usr/bin/python3 + Jazzy |
ros2_deploy_bridge.py |
5558 JPEG、/arm/cmd、手指令 |
| C | /usr/bin/python3 + Jazzy |
robot_ready.py(与层 B 互斥) |
/arm/cmd |
2. 为什么不用 Docker / conda¶
| 方案 | 结论 |
|---|---|
| docker | 仓库 Dockerfile.arm64 是 JetPack6 / 22.04 / Humble / Orin sm_87,与 Thor(JetPack7 / 24.04 / Jazzy / sm_110)不兼容;且当前用户不在 docker 组、无免密 sudo |
| conda | 宿主 torch 来自已删除的本地 wheel,PyPI 的 aarch64 torch 无 CUDA,jetson-ai-lab jp7/cu130 索引查不到 cp312 轮子 → 拿不到可用 torch |
venv(--system-site-packages) |
直接继承宿主 sm_110 torch;实测 numpy 2.2.6 下 torch/torchvision/cv2 均正常(Orin 上必须钉 numpy<2 的坑在这里不存在) |
3. 文件清单¶
| 文件 | 作用 |
|---|---|
env.sh |
公共常量(PROJECT_ROOT/VENV/BRIDGE_SCRIPT)+ dex_source_ros(显式 source Jazzy) |
setup_env.sh |
建 venv、装 LeRobot/插件(--no-deps 保 torch)、自检 13D 契约 |
robot_ready_host.sh |
预备位(系统 python + Jazzy) |
rollout_host.sh |
策略推理(venv),带维度预检 |
bridge_smoke.sh |
只读冒烟:验证 状态/图像/急停 三条链路,不动机器人 |
camera_preview.sh |
头相机预览:实时窗口 / --snapshot 存图 / --size raw 看 720p |
hold_test.py |
控制通路零位移验收(目标 = 当前实测角度,不产生动作) |
staged_test.sh |
分步微动:头 / 手 / 臂各 ±0.05 rad(手 80 码)偏出再回位,带实测核对 |
robot_home_host.sh |
回站立位:按预备位四段航点倒序走(手全程在桌面以上),目标为 HOME_POSE + 真机微偏移 |
4. 环境构建(首次)¶
仓库(含 lerobot 子模块)需同步到本体——PROJECT_ROOT 由脚本位置推导为仓库内的 ubt_IL/。LFS 大文件(ubt_sim 仿真资源)不需要。
前置检查:
python3 -V # 3.12
ls /opt/ros/jazzy/setup.bash
/usr/bin/python3 -c "import rclpy, ros2_bridge_msgs, bodyctrl_msgs, inspire_hand_msgs, brainco_hand_msgs"
/usr/bin/python3 -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_arch_list())"
厂商消息包默认装在系统里;若装在自定义工作空间,设 TIENKUNG_DEX_MSGS_WS=<ws>(默认 /opt/tienkung_dex_msgs_ws)。ROS_SETUP 可显式指定 ROS 发行版,默认依次找 /opt/ros/jazzy、/opt/ros/humble。
建环境:
setup_env.sh 幂等(重复跑会跳过建 venv、重装依赖),做的事:
| 步骤 | 说明 |
|---|---|
| 建 venv | python3 -m venv --system-site-packages $PROJECT_ROOT/.venv-dex;不装 torch,继承宿主 sm_110 |
| numpy | 装 numpy==2.2.6(LeRobot 要求 >=2.0,<2.3) |
| 运行时依赖 | draccus / gymnasium / einops / safetensors / huggingface-hub / datasets / av / pyzmq 等;不装 opencv(用宿主带 GUI 的 cv2) |
| numpy2-ABI 包 | pandas / pyarrow / matplotlib / scikit-learn 用 --ignore-installed,否则宿主 ~/.local 里按 numpy1 编译的包会报 numpy.dtype size changed |
| editable 安装 | --no-deps -e lerobot + -e tienkung_dex(--no-deps 防止 pip 把 torch 换成 PyPI 的 CPU 版) |
| 自检 | torch CUDA / sm_110、numpy 版本、三种 JOINT_CONFIG 的维度与手型契约、lerobot-rollout --help |
跑完应看到 cuda=True、archs 含 sm_110、numpy 2.2.6,以及:
joint_config=tienkung_dex_brainco_13 dim=13 hand=brainco mode=rad13 ...
joint_config=tienkung_dex_20 dim=20 hand=inspire mode=rad13 ...
joint_config=tienkung_dex_inspire_13 dim=13 hand=inspire mode=pico6 ...
dex_camera 已注册 | 13D 契约: ...
[setup] lerobot-rollout OK
[setup] 完成。
失败点集中在两处:cuda=False 说明 venv 没继承到宿主 torch;numpy.dtype size changed 说明 ABI 包没装进 venv(重跑 setup_env.sh)。
5. 首轮联调(不接策略)¶
cd ubt_IL/scripts/deploy/tienkung_dex/arm_64
bash bridge_smoke.sh # 只读体检:status ≥30 Hz、image ≥10 fps、estop=false
bash camera_preview.sh --snapshot # 抓一张策略视角(/tmp/dex_camera_preview.png)
source ./env.sh && dex_source_ros # 层 B 用系统 python + Jazzy
/usr/bin/python3 hold_test.py --groups arm # 零位移通路验收
bash staged_test.sh --step all # 头/手/臂微动再回位,确认方向与回位
bash robot_ready_host.sh --dry-run # 先看四段目标
bash robot_ready_host.sh --real # 实发预备位(人盯急停)
因时 r13 链路:HAND_VARIANT=inspire HAND_ACTION_MODE=pico6 bash bridge_smoke.sh(状态里的 right_hand 应为 6 维)。camera_preview.sh 会自己起一个只订阅的 Bridge2,--size raw 看原始 1280×720,--port 5558 连已有桥接。
6. 部署推理¶
POLICY_PATH=/abs/path/pretrained_model \
JOINT_CONFIG=tienkung_dex_brainco_13 \
CAMERA_PORT=5558 IMAGE_ENABLED=1 FPS=30 \
DURATION=10 MAX_RELATIVE_TARGET=0.01 \
bash rollout_host.sh
| 变量 | 默认 | 说明 |
|---|---|---|
POLICY_PATH |
无(必填) | checkpoint 的 pretrained_model 目录 |
JOINT_CONFIG |
tienkung_dex_brainco_13 |
强脑 6 电机;因时 20D 用 tienkung_dex_20;因时 r13 数据集用 tienkung_dex_inspire_13 |
HAND_ACTION_MODE |
空(自动) | 留空由 JOINT_CONFIG 推导;tienkung_dex_inspire_13 → pico6 |
FPS |
15 |
必须 = 数据集帧率 |
CAMERA_PORT / IMAGE_ENABLED |
5558 / 1 |
真机走 Bridge2 转码 |
CAMERA_WIDTH/HEIGHT |
640×360 |
同时用于 Bridge2 缩放与 dex_camera 裁剪窗口 |
MAX_RELATIVE_TARGET |
空 | 首轮 0.01(rad/步) |
DISPLAY_CAM |
true |
本机无 X(SSH)时设 false |
7. 与容器版对照¶
| 容器版 | 本体版 | 说明 |
|---|---|---|
robot_ready.sh --real |
robot_ready_host.sh --real |
系统 python3 + Jazzy |
rollout.sh |
rollout_host.sh |
venv 内跑,自动拉起 Bridge2 |
preview_camera.py |
camera_preview.sh |
本体上由 Bridge2 直接转码 |
inference_server.sh |
暂无 | 常驻推理服务仍需容器版 |
8. 注意事项与排错¶
- 相机尺寸不匹配不会报错:720p 原样送入时
dex_camera只按offset_x+width切列,会拿到「左半幅」,策略照样输出、只是画面分布全错。已由 Bridge2 的image_width/image_height在编码前缩放修正,首次务必camera_preview.sh --snapshot确认是完整视野。 bash -lc不 source.bashrc:Bridge2 靠父进程继承 ROS 环境,脚本里用dex_source_ros显式 source Jazzy;手工起桥接时同样要先 source。/arm/cmd无人消费:机器人必须处于话题控制模式(遥控器切半身策略并上拨),否则「策略在跑但机器人不动」。robot_ready与 Bridge2 互斥:两者都写/arm/cmd,顺序永远是先 ready 再 rollout。- 急停:Bridge2 监听
/power/board/key_status的is_estop;已下发目标仍在驱动器里,必要时要按物理急停。 wrist_roll_l40 Hz 摩擦极限环:真机 home/预备位已由constants.DEPLOY_HOME_OFFSETS叠加 +0.03 rad;换机后若右腕同样嗡嗡,往该表加一条。cuda=False:venv 没继承到宿主 torch,确认python3 -m venv --system-site-packages创建的是$PROJECT_ROOT/.venv-dex。numpy.dtype size changed:ABI 包没装进 venv,重跑setup_env.sh。
完整文件清单与实测记录见仓库 ubt_IL/scripts/deploy/tienkung_dex/arm_64/README.md。