本仓库为双臂 Piper 机械臂提供两套相互独立的 OpenPI JAX action chunk 执行方案。策略模型从 JAX Orbax checkpoint 恢复,GPU 服务器负责策略推理,Piper 端 client 负责采集观测、请求动作并执行双臂控制。
- RTC(Real-Time Chunking):重新推理时保留上一段完整轨迹,通过 hard/soft 时间约束减少相邻 action chunk 切换时的不连续,同时保持短期计划的一致性。
- TE(Temporal Ensemble):持续请求相互重叠的 action chunk,将不同 chunk 中对应同一真实控制时刻的动作对齐后加权平均,用于减小局部预测噪声。
本仓库中的 client 是双臂 client,不是单臂 client,并保留了 prompt、策略双臂顺序和强制交换双臂动作块的入口。
这是一套 Piper/OpenPI 集成代码,不是适用于所有机器人的通用配置。真实部署前,checkpoint、OpenPI config、norm stats、相机键名、动作维度、双臂顺序以及 Piper 驱动必须与训练设置一致。
openpi-jax-rtc-te/
├── rtc/
│ ├── openpi_piper_jax_rtc_policy_server.py
│ ├── openpi_piper_dual_arm_jax_rtc_client.py
│ ├── start_jax_rtc_server.sh
│ └── start_jax_rtc_dual_arm_client.sh
├── temporal_ensemble/
│ ├── openpi_piper_jax_temporal_ensemble_policy_server.py
│ ├── openpi_piper_dual_arm_jax_temporal_ensemble_client.py
│ ├── start_jax_temporal_ensemble_server.sh
│ └── start_jax_temporal_ensemble_dual_arm_client.sh
├── configs/
│ ├── policy.example.yaml
│ ├── server.example.env
│ ├── client.example.env
│ ├── rtc.example.env
│ └── temporal_ensemble.example.env
├── policy/
│ └── README.md
├── references/
│ ├── original_jax_orbax_policy_server.py
│ ├── piper_inference_dependency.py
│ ├── piper_motor_driver.py
│ └── piper_robot_driver.py
├── DEPENDENCIES.md
└── CHECKSUMS.sha256
rtc/:RTC JAX server、双臂 client 和启动脚本。temporal_ensemble/:独立的 TE server、双臂 client 和启动脚本。configs/:不含真实信息的 policy manifest,以及可复制为本机文件的 server/client/RTC/TE 环境变量模板。policy/README.md:Policy Adapter 的请求、返回、metadata 和替换 policy 接口约定。references/:本项目依赖的原始 JAX/Piper/LeRobot 集成参考代码。发布前需要检查上游许可证。DEPENDENCIES.md:server/client 依赖边界以及复现实验时需要记录的版本。CHECKSUMS.sha256:用于确认发布的源文件、启动脚本、配置模板和接口说明没有发生意外变化。
仓库不会包含实际 policy 权重。使用者通过 configs/policy.example.yaml 确认兼容条件,再复制 .env 示例并填写自己的 checkpoint、norm stats、OpenPI 路径和设备配置:
cd /path/to/openpi-jax-rtc-te
cp configs/server.example.env configs/server.local.env
cp configs/client.example.env configs/client.local.env.local.env 已被 Git 忽略,不应上传。
| 项目 | RTC | Temporal Ensemble |
|---|---|---|
| 默认端口 | 8011 |
8012 |
| 默认执行 chunk | 25 actions | 50 actions |
| 新旧轨迹关系 | 新轨迹受到上一完整轨迹的 hard/soft 约束 | 每个 chunk 独立生成,client 端再做加权 |
| 时间处理 | 冻结立即需要执行的动作,soft 约束重叠区 | 按绝对 control step 对齐多个 chunk |
| 主要参数 | delay、execution horizon、soft decay、guidance scale | inference interval、decay、max chunks |
| 更适合 | 需要动作计划连贯性的长时序、双臂搬运任务 | 各 chunk 计划兼容、主要问题是局部预测噪声的任务 |
不要把 TE client 连接到启用了 RTC 的 server。TE client 会主动拒绝这种组合,因为普通时间加权可能破坏 RTC 已经冻结或约束的动作。
当前实现基于以下约定:
- Piper 双臂状态和执行动作均为 14D。
- 前 7D 和后 7D 分别表示一只机械臂的六个关节与一个夹爪。
- 物理顺序通常为
left(can0), right(can1)。 - server 可以处理原生 14D Piper 输出,也可以处理 80D unified policy 输出。
- 对 80D 输出使用明确的 Piper slot mapping,不使用不安全的
action[:14]。 - server 将模型输出恢复为绝对 14D Piper target,client 再根据双臂顺序完成映射并发送命令。
当前双臂 client 会初始化并读取四路 RealSense:
| Client key | 视角 | 当前 JAX policy adapter 是否使用 |
|---|---|---|
head |
头部或全局视角 | 是 |
left_wrist |
左腕视角 | 是 |
right_wrist |
右腕视角 | 是 |
front_view |
额外正面视角 | 否 |
因此,当前代码的准确描述是:四路相机被 client 采集并发送,其中三路进入模型。
使用现有代码时必须提供四个相机序列号:
export HEAD_CAMERA_SERIAL='<HEAD_CAMERA_SERIAL>'
export LEFT_WRIST_CAMERA_SERIAL='<LEFT_WRIST_CAMERA_SERIAL>'
export RIGHT_WRIST_CAMERA_SERIAL='<RIGHT_WRIST_CAMERA_SERIAL>'
export FRONT_VIEW_CAMERA_SERIAL='<FRONT_VIEW_CAMERA_SERIAL>'也可以直接提供完整 JSON:
export ROBOT_CAMERAS_JSON='<YOUR_CAMERA_JSON>'如果需要真正的 3-camera client,需要同时修改 client 的 build_remote_observation() 和 launcher 中的相机配置,使 front_view 不再初始化和读取。只删除 FRONT_VIEW_CAMERA_SERIAL 会导致当前启动脚本直接报缺少变量。
- Linux 与可用的 CUDA GPU。
- Python 3.11。
- OpenPI 源码目录,其中至少存在
src/和packages/openpi-client/src/。 - 与该 OpenPI checkout 匹配的 JAX/Python 环境。
- 完整的 Orbax/OCDBT checkpoint,至少包含:
_CHECKPOINT_METADATAparams/manifest.ocdbtparams/array_metadatas
- 与 checkpoint 和数据配置匹配的
norm_stats.json。 - 能被本地 cotrain config 代码解析的
CONFIG_NAME。
- Python 3.11 与 OpenPI WebSocket client 依赖。
- 兼容的 LeRobot-Piper checkout。
- 对应的 Piper SDK、RealSense 和机器人驱动依赖。
- 两路已配置 CAN,通常左臂为
can0,右臂为can1。 - 当前 launcher 需要四个可见的 RealSense 设备。
- Piper 电脑能够访问 GPU server 的策略端口。
仓库不包含 checkpoint、norm stats、数据集、机器人视频、真实路径、SSH 配置、凭据或相机序列号。
| 变量 | 含义 | 占位示例 |
|---|---|---|
OPENPI_ROOT |
OpenPI checkout | /path/to/openpi |
CHECKPOINT_DIR |
需要测试的准确 checkpoint step 目录 | /path/to/checkpoints/checkpoint_step |
NORM_STATS_PATH |
对应的 norm stats | /path/to/assets/norm_stats.json |
OPENPI_PYTHON_BIN |
server 端 Python 3.11 绝对路径 | /path/to/python3.11 |
CONFIG_NAME |
OpenPI 配置名称 | your_config_name |
PROMPT |
当前任务指令 | your task instruction |
POLICY_HOST |
监听地址;远程 client 通常使用 0.0.0.0 |
0.0.0.0 |
POLICY_PORT |
WebSocket 端口 | RTC 8011、TE 8012 |
启动脚本会检查 Python 路径,因此建议提供 Python 可执行文件的绝对路径,而不是只填写 python3。
| 变量 | 含义 | 占位示例 |
|---|---|---|
OPENPI_ROOT |
client 可访问的 OpenPI checkout | /path/to/openpi |
LEROBOT_PIPER_ROOT |
LeRobot-Piper checkout | /path/to/lerobot-piper |
CLIENT_PYTHON_BIN |
client Python 3.11 绝对路径 | /path/to/python3.11 |
POLICY_HOST |
GPU server 地址 | <SERVER_IP> |
POLICY_PORT |
与 server 完全一致的端口 | RTC 8011、TE 8012 |
PROMPT |
发送给策略的任务指令 | your task instruction |
LEFT_CAN_NAME |
左臂 CAN | can0 |
RIGHT_CAN_NAME |
右臂 CAN | can1 |
RTC 与 TE 应作为两组独立实验。一次只启动一组匹配的 server 和 client。
启动前确认:
CHECKPOINT_DIR是需要测试的准确 checkpoint step。NORM_STATS_PATH来自相同数据集和动作表示。CONFIG_NAME能恢复相同的模型结构和 transforms。- 模型 action horizon 不小于
ACTIONS_PER_INFERENCE。 - 相机 key 和 prompt 格式与训练一致。
- checkpoint 的 14D 双臂顺序已经确认。
只更换 checkpoint 路径并不能自动修复 config、norm stats 或动作映射不匹配。
在 GPU server 上准备公共变量:
cd /path/to/openpi-jax-rtc-te
export OPENPI_ROOT='/path/to/openpi'
export CHECKPOINT_DIR='/path/to/checkpoints/checkpoint_step'
export NORM_STATS_PATH='/path/to/assets/norm_stats.json'
export OPENPI_PYTHON_BIN='/path/to/python3.11'
export CONFIG_NAME='your_config_name'
export PROMPT='your task instruction'
export POLICY_HOST='0.0.0.0'验证 RTC:
export POLICY_PORT='8011'
export ACTIONS_PER_INFERENCE='25'
export RTC_DELAY_STEPS='3'
export RTC_EXECUTION_HORIZON='25'
export RTC_SOFT_MASK_DECAY='0.6'
export RTC_GUIDANCE_SCALE='1.0'
export VALIDATE_ONLY='true'
bash rtc/start_jax_rtc_server.shRTC validate-only 会加载 checkpoint,执行合成观测推理,检查可执行 action shape、完整 action horizon 以及 hard/soft/free mask。验证完成后程序退出,不会持续监听端口。
验证 TE:
export POLICY_PORT='8012'
export ACTIONS_PER_INFERENCE='50'
export VALIDATE_ONLY='true'
bash temporal_ensemble/start_jax_temporal_ensemble_server.shTE 的时间加权发生在 client 端。server validate-only 负责确认相同的 checkpoint 与 JAX 推理路径能够工作。
RTC server:
cd /path/to/openpi-jax-rtc-te
export POLICY_HOST='0.0.0.0'
export POLICY_PORT='8011'
export ACTIONS_PER_INFERENCE='25'
export RTC_DELAY_STEPS='3'
export RTC_EXECUTION_HORIZON='25'
export RTC_SOFT_MASK_DECAY='0.6'
export RTC_GUIDANCE_SCALE='1.0'
export VALIDATE_ONLY='false'
bash rtc/start_jax_rtc_server.shTE server:
cd /path/to/openpi-jax-rtc-te
export POLICY_HOST='0.0.0.0'
export POLICY_PORT='8012'
export ACTIONS_PER_INFERENCE='50'
export VALIDATE_ONLY='false'
bash temporal_ensemble/start_jax_temporal_ensemble_server.sh如果 client 在另一台机器上,需要允许对应端口通过 server 防火墙。不要把策略服务直接暴露到不可信网络。
在 Piper 电脑上:
cd /path/to/openpi-jax-rtc-te
export OPENPI_ROOT='/path/to/openpi'
export LEROBOT_PIPER_ROOT='/path/to/lerobot-piper'
export CLIENT_PYTHON_BIN='/path/to/python3.11'
export POLICY_HOST='<SERVER_IP>'
export PROMPT='your task instruction'
export LEFT_CAN_NAME='can0'
export RIGHT_CAN_NAME='can1'
export HEAD_CAMERA_SERIAL='<HEAD_CAMERA_SERIAL>'
export LEFT_WRIST_CAMERA_SERIAL='<LEFT_WRIST_CAMERA_SERIAL>'
export RIGHT_WRIST_CAMERA_SERIAL='<RIGHT_WRIST_CAMERA_SERIAL>'
export FRONT_VIEW_CAMERA_SERIAL='<FRONT_VIEW_CAMERA_SERIAL>'
export FPS='30'
export STEPS='100'STEPS=0 表示持续运行直到按下 Ctrl+C。首次验证时建议使用有限步数。
代码中有两种不同的交换入口:
FORCE_SWAP_ARMS:修改策略 14D 动作中两个 7D block 的解释顺序。COMMAND_ARM_ORDER:在动作发送到物理机械臂之前,再交换两个 7D block。
正常物理命令顺序应保持:
export COMMAND_ARM_ORDER='left-right'
export COMMAND_SWAP_MODE='absolute'根据 checkpoint 训练时的 action order 设置:
# Policy 输出为 [left 7D, right 7D]
export FORCE_SWAP_ARMS='false'
# Policy 输出为 [right 7D, left 7D]
export FORCE_SWAP_ARMS='true'不要因为 policy order 相反就同时设置 COMMAND_ARM_ORDER=right-left。FORCE_SWAP_ARMS 已经负责把 policy order 恢复到物理左右臂顺序;command-level swap 是独立的兼容或诊断入口。
RTC 与 TE launcher 当前的 FORCE_SWAP_ARMS 默认值不同,所以真实部署时应显式设置,不要依赖默认值。
Dry-run 仍然会连接 Piper、读取双臂状态和相机、连接策略 server 并计算最终命令,但不会调用 robot.send_action()。
RTC dry-run:
cd /path/to/openpi-jax-rtc-te
export POLICY_PORT='8011'
export ACTIONS_PER_INFERENCE='25'
export PREFETCH_THRESHOLD='4'
export RTC_DELAY_STEPS='3'
export RTC_EXECUTION_HORIZON='25'
export RTC_SOFT_MASK_DECAY='0.6'
export RTC_GUIDANCE_SCALE='1.0'
export FORCE_SWAP_ARMS='false'
export ENABLE_ARM='false'
export DRY_RUN='true'
bash rtc/start_jax_rtc_dual_arm_client.sh \
--debug-action-details \
--print-command-countsTE dry-run:
cd /path/to/openpi-jax-rtc-te
export POLICY_PORT='8012'
export ACTIONS_PER_INFERENCE='50'
export ENSEMBLE_INFERENCE_INTERVAL='4'
export ENSEMBLE_DECAY='0.25'
export ENSEMBLE_MAX_CHUNKS='4'
export FORCE_SWAP_ARMS='false'
export ENABLE_ARM='false'
export DRY_RUN='true'
bash temporal_ensemble/start_jax_temporal_ensemble_dual_arm_client.shRTC debug 输出包含 observation、model action、delta、final action 和可选的电机命令计数。TE 输出包含最终 14D action,以及当前控制步使用的 chunk index 和权重。启用真机运动前,应确认左右臂动作块对应正确、没有意外全零、NaN 或 infinite value。
RTC:
cd /path/to/openpi-jax-rtc-te
export STEPS='0'
export ENABLE_ARM='true'
export DRY_RUN='false'
export DISABLE_ARM_ON_EXIT='false'
bash rtc/start_jax_rtc_dual_arm_client.shTE:
cd /path/to/openpi-jax-rtc-te
export STEPS='0'
export ENABLE_ARM='true'
export DRY_RUN='false'
export DISABLE_ARM_ON_EXIT='false'
bash temporal_ensemble/start_jax_temporal_ensemble_dual_arm_client.shlauncher 当前将 action scale 设为 1.0、额外平滑 alpha 设为 1.0、deadband 设为 0,并把 client delta 上限设置为近似开放值,因此不会额外加入保守动作滤波。真实行为主要由策略输出、RTC/TE controller、Piper driver 和底层机械臂限制决定。
按 Ctrl+C 停止。如果设置 DISABLE_ARM_ON_EXIT=true,退出时还会请求关闭机械臂使能。
当前 RTC 设计保留模型完整的 50-action horizon,同时只返回前 25 个 action 作为可执行窗口:
full_actions:保存完整模型轨迹,用于下一次 RTC 请求。actions:返回给 client 的可执行前缀。RTC_DELAY_STEPS:新推理完成前,需要保持不变的立即执行动作数量。RTC_EXECUTION_HORIZON:一次允许进入执行队列的最大有效动作数。RTC_SOFT_MASK_DECAY:soft overlap 第一个位置的权重及后续指数衰减。RTC_GUIDANCE_SCALE:RTC guided sampling 的贡献比例。
server 和 client 的 ACTIONS_PER_INFERENCE、delay、execution horizon、soft decay 与 guidance scale 应保持一致。client 在 chunk 切换时会打印 skip、hard、soft、free、queued 和 full_prior,可用于确认完整 prior 被保留且 soft 区域不是空的。
默认配置:
model horizon: 50
ACTIONS_PER_INFERENCE: 25
FPS: 30
PREFETCH_THRESHOLD: 4
RTC_DELAY_STEPS: 3
RTC_EXECUTION_HORIZON: 25
RTC_SOFT_MASK_DECAY: 0.6
RTC_GUIDANCE_SCALE: 1.0
如果 checkpoint 原生输出 50 actions,不要为了执行 25 actions 就把模型完整 horizon 截断为 25。RTC 需要保留 50-action prior 才能在 hard 区域之后形成有效的 soft overlap。
TE client 使用下式对齐动作:
action_index = control_step - request_step
只有指向当前绝对 control step 的动作会参与加权。旧 chunk 在当前时刻对应更大的 action_index,因此会获得更低的指数权重。
默认配置:
ACTIONS_PER_INFERENCE: 50
FPS: 30
ENSEMBLE_INFERENCE_INTERVAL: 4
ENSEMBLE_DECAY: 0.25
ENSEMBLE_MAX_CHUNKS: 4
- 减小 inference interval 会增加 chunk 重叠和 server 负载。
- 增大 decay 会让新 chunk 的权重更高。
- 增大 max chunks 会保留更多历史计划,可能降低局部噪声,也可能在任务阶段切换时造成犹豫或原地抖动。
使用 Python 可执行文件的绝对路径:
export OPENPI_PYTHON_BIN='/absolute/path/to/python3.11'
export CLIENT_PYTHON_BIN='/absolute/path/to/python3.11'检查:
- server 是否设置
VALIDATE_ONLY=false。 - 远程连接时 server 是否设置
POLICY_HOST=0.0.0.0。 - server/client 端口是否一致。
- 防火墙是否允许对应端口。
- 是否存在旧 server 进程占用端口。
检查准确的 checkpoint 目录、Orbax metadata、Python 3.11 环境、CONFIG_NAME 和匹配的 NORM_STATS_PATH。来自不同动作表示或数据配置的 checkpoint 不能只通过更换路径解决。
确认四个序列号在 Piper 电脑上均可见且没有重复。使用 ROBOT_CAMERAS_JSON 时,每个相机条目必须包含数值形式的 serial_number 和兼容的 FPS/分辨率。
先保持 COMMAND_ARM_ORDER=left-right,只切换 FORCE_SWAP_ARMS 并检查 dry-run 日志。只有确定需要交换物理输出 block 时,才使用 command-level swap。
确认 server 返回完整模型 horizon 的 full_actions,可执行 prefix 小于完整 horizon,并且 server/client 参数一致。client chunk-switch 日志中应出现非零 soft 数量。
TE 可能正在平均语义上不兼容的旧计划和新计划。可以减少保留 chunk 的数量或存活时间、增大 decay 让新计划占主导,或者改用 RTC。不要在 RTC 之后再叠加普通 TE。
本仓库已将机器路径、设备序列号、主机名、数据集标识和任务 prompt 替换为占位符。可使用以下命令检查复制的源文件:
cd /path/to/openpi-jax-rtc-te
shasum -a 256 -c CHECKSUMS.sha256发布到 GitHub 前:
- 检查 OpenPI、LeRobot、Piper SDK、RealSense 以及
references/中代码的许可证。 - 添加仓库许可证和上游 attribution。
- 固定已经验证的依赖版本或 commit。
- 不要提交 checkpoint、norm stats、数据集、机器人视频、SSH key、凭据或真实相机序列号。
- Shell/Python 语法检查、合成验证和 dry-run 只能证明对应路径可运行,不能证明真实机械臂行为安全或任务一定成功。
This repository provides two independent action-chunk execution methods for a dual-arm Piper robot using an OpenPI policy restored from a JAX Orbax checkpoint:
- RTC (Real-Time Chunking): replans while preserving part of the previous action chunk through hard and soft temporal constraints. It is intended to reduce discontinuities between consecutive plans while retaining a coherent short-term trajectory.
- Temporal Ensemble (TE): requests overlapping action chunks, aligns their predictions to the same control step, and computes a weighted average. It can reduce small prediction noise, but may average incompatible plans near semantic stage transitions.
The implementation is split into a GPU policy server and a robot-side client. The client in this repository is dual-arm only and retains explicit controls for policy arm order and forced action-block swapping.
This is integration code, not a universal Piper configuration. Your checkpoint, OpenPI config, normalization statistics, camera layout, action dimensions, arm order, and robot drivers must match each other before real-hardware execution.
openpi-jax-rtc-te/
├── rtc/
│ ├── openpi_piper_jax_rtc_policy_server.py
│ ├── openpi_piper_dual_arm_jax_rtc_client.py
│ ├── start_jax_rtc_server.sh
│ └── start_jax_rtc_dual_arm_client.sh
├── temporal_ensemble/
│ ├── openpi_piper_jax_temporal_ensemble_policy_server.py
│ ├── openpi_piper_dual_arm_jax_temporal_ensemble_client.py
│ ├── start_jax_temporal_ensemble_server.sh
│ └── start_jax_temporal_ensemble_dual_arm_client.sh
├── configs/
│ ├── policy.example.yaml
│ ├── server.example.env
│ ├── client.example.env
│ ├── rtc.example.env
│ └── temporal_ensemble.example.env
├── policy/
│ └── README.md
├── references/
│ ├── original_jax_orbax_policy_server.py
│ ├── piper_inference_dependency.py
│ ├── piper_motor_driver.py
│ └── piper_robot_driver.py
├── DEPENDENCIES.md
└── CHECKSUMS.sha256
The configs/ directory contains a policy compatibility manifest and sourceable deployment templates. policy/README.md defines the adapter request/response contract. The references/ directory records upstream integration points used by the clients; the launchers do not automatically install or replace the corresponding OpenPI, LeRobot, RealSense, or Piper SDK dependencies.
Actual policy weights are intentionally excluded. Users verify compatibility with configs/policy.example.yaml, then copy the environment templates and fill in their own checkpoint, norm stats, paths, and devices:
cd /path/to/openpi-jax-rtc-te
cp configs/server.example.env configs/server.local.env
cp configs/client.example.env configs/client.local.envThe .local.env files are ignored by Git and should not be uploaded.
| Item | RTC | Temporal Ensemble |
|---|---|---|
| Server port default | 8011 |
8012 |
| Executable chunk default | 25 actions | 50 actions |
| Replanning behavior | Generates a new chunk with constraints from the previous full trajectory | Generates independent overlapping chunks |
| Transition handling | Hard-freezes the immediate prefix and softly constrains the overlap | Averages all valid predictions for the same control step |
| Main client parameters | delay, execution horizon, soft-mask decay, guidance scale | inference interval, decay, maximum retained chunks |
| Recommended use | Long-horizon or bimanual tasks where plan commitment matters | Tasks where local prediction noise is the main problem and overlapping plans remain compatible |
Do not run the TE client against an RTC-enabled server. The TE client explicitly rejects this combination because ordinary temporal averaging can overwrite the trajectory commitment introduced by RTC.
This implementation assumes:
- A dual-arm Piper observation/action has 14 values: 7 for the left arm and 7 for the right arm.
- Each 7D block contains six arm joints and one gripper command.
- Physical robot order is normally
left(can0), right(can1). - The server supports either a native 14D Piper policy output or an 80D unified policy output.
- For an 80D unified policy, the Piper slots are mapped explicitly; the code does not use a generic
action[:14]slice. - The checkpoint is a JAX Orbax/OCDBT checkpoint and the normalization statistics belong to the same training configuration.
The server converts model outputs into absolute 14D Piper targets. The client then applies the selected arm mapping before sending commands to the robot.
The current dual-arm client initializes four RealSense streams:
| Client key | Intended view | Used by the current JAX model adapter |
|---|---|---|
head |
head or global view | Yes |
left_wrist |
left wrist | Yes |
right_wrist |
right wrist | Yes |
front_view |
additional front view | No |
The client currently captures and sends all four images, while the server-side policy adapter consumes only head, left_wrist, and right_wrist. Therefore, the checked-in launcher is operationally a four-camera client feeding a three-camera policy.
To use the repository unchanged, provide all four camera serial numbers:
export HEAD_CAMERA_SERIAL='<HEAD_CAMERA_SERIAL>'
export LEFT_WRIST_CAMERA_SERIAL='<LEFT_WRIST_CAMERA_SERIAL>'
export RIGHT_WRIST_CAMERA_SERIAL='<RIGHT_WRIST_CAMERA_SERIAL>'
export FRONT_VIEW_CAMERA_SERIAL='<FRONT_VIEW_CAMERA_SERIAL>'Alternatively, provide the complete camera object yourself:
export ROBOT_CAMERAS_JSON='<YOUR_CAMERA_JSON>'Changing to a true three-camera client requires editing build_remote_observation() and the launcher camera configuration so that the unused front_view camera is neither initialized nor read. Simply omitting FRONT_VIEW_CAMERA_SERIAL will make the current launcher stop with a missing-variable error.
- Linux machine with a CUDA-capable GPU.
- Python 3.11.
- An OpenPI source checkout containing:
src/packages/openpi-client/src/
- The Python/JAX environment required by that OpenPI checkout.
- A complete Orbax checkpoint containing at least:
_CHECKPOINT_METADATAparams/manifest.ocdbtparams/array_metadatas
- A matching
norm_stats.json. - A valid OpenPI configuration name resolvable by your local cotrain configuration code.
- Python 3.11 with the OpenPI WebSocket client dependencies.
- A compatible LeRobot-Piper checkout.
- Piper SDK and RealSense dependencies required by that checkout.
- Two configured CAN interfaces, normally
can0for the left arm andcan1for the right arm. - Four visible RealSense devices for the checked-in launcher.
- Network access to the selected server port.
This repository does not include checkpoints, datasets, normalization files, robot calibration, SSH configuration, API credentials, or camera serial numbers.
| Variable | Meaning | Example placeholder |
|---|---|---|
OPENPI_ROOT |
OpenPI checkout | /path/to/openpi |
CHECKPOINT_DIR |
Exact Orbax checkpoint step directory | /path/to/checkpoints/checkpoint_step |
NORM_STATS_PATH |
Matching normalization JSON | /path/to/assets/norm_stats.json |
OPENPI_PYTHON_BIN |
Absolute path to the server Python 3.11 executable | /path/to/python3.11 |
CONFIG_NAME |
OpenPI training/inference config name | your_config_name |
PROMPT |
Task instruction | your task instruction |
POLICY_HOST |
Bind address; use 0.0.0.0 for a remote client |
0.0.0.0 |
POLICY_PORT |
WebSocket server port | RTC 8011, TE 8012 |
Use an absolute Python path. The launchers validate executable paths before starting, so a bare command such as python3 may not pass the path check.
| Variable | Meaning | Example placeholder |
|---|---|---|
OPENPI_ROOT |
OpenPI checkout available on the client | /path/to/openpi |
LEROBOT_PIPER_ROOT |
Compatible LeRobot-Piper checkout | /path/to/lerobot-piper |
CLIENT_PYTHON_BIN |
Absolute path to client Python 3.11 | /path/to/python3.11 |
POLICY_HOST |
IP or hostname of the GPU server | <SERVER_IP> |
POLICY_PORT |
Must match the selected server | RTC 8011, TE 8012 |
PROMPT |
Must describe the same task intended for the policy | your task instruction |
LEFT_CAN_NAME |
Left-arm CAN interface | can0 |
RIGHT_CAN_NAME |
Right-arm CAN interface | can1 |
| Camera variables | Four serial numbers, or ROBOT_CAMERAS_JSON |
placeholders above |
Use RTC and TE as separate experiments. Start one matching server/client pair at a time.
Before starting either method, verify that:
CHECKPOINT_DIRpoints to the exact checkpoint step you intend to test.NORM_STATS_PATHwas produced for the same dataset/action representation.CONFIG_NAMEreconstructs the same model architecture and transforms.- The model action horizon is at least the requested
ACTIONS_PER_INFERENCE. - The camera keys and prompt format match training.
- The policy arm order matches the 14D action layout used by the checkpoint.
Changing only the checkpoint path is insufficient when its config, normalization statistics, or action mapping differs.
On the GPU server:
cd /path/to/openpi-jax-rtc-te
export OPENPI_ROOT='/path/to/openpi'
export CHECKPOINT_DIR='/path/to/checkpoints/checkpoint_step'
export NORM_STATS_PATH='/path/to/assets/norm_stats.json'
export OPENPI_PYTHON_BIN='/path/to/python3.11'
export CONFIG_NAME='your_config_name'
export PROMPT='your task instruction'
export POLICY_HOST='0.0.0.0'Validate RTC:
export POLICY_PORT='8011'
export ACTIONS_PER_INFERENCE='25'
export RTC_DELAY_STEPS='3'
export RTC_EXECUTION_HORIZON='25'
export RTC_SOFT_MASK_DECAY='0.6'
export RTC_GUIDANCE_SCALE='1.0'
export VALIDATE_ONLY='true'
bash rtc/start_jax_rtc_server.shRTC validation loads the checkpoint, runs synthetic inference, verifies the executable action shape and full action horizon, and checks that the RTC hard/soft/free mask regions are internally consistent. It does not start the WebSocket service.
Validate Temporal Ensemble:
export POLICY_PORT='8012'
export ACTIONS_PER_INFERENCE='50'
export VALIDATE_ONLY='true'
bash temporal_ensemble/start_jax_temporal_ensemble_server.shTE validation loads the same server-side policy path and checks a synthetic inference result. Temporal ensembling itself runs on the client.
After validation succeeds, keep the same checkpoint/config/norm-stat variables and disable validation-only mode.
Start RTC server:
cd /path/to/openpi-jax-rtc-te
export POLICY_HOST='0.0.0.0'
export POLICY_PORT='8011'
export ACTIONS_PER_INFERENCE='25'
export RTC_DELAY_STEPS='3'
export RTC_EXECUTION_HORIZON='25'
export RTC_SOFT_MASK_DECAY='0.6'
export RTC_GUIDANCE_SCALE='1.0'
export VALIDATE_ONLY='false'
bash rtc/start_jax_rtc_server.shStart TE server instead:
cd /path/to/openpi-jax-rtc-te
export POLICY_HOST='0.0.0.0'
export POLICY_PORT='8012'
export ACTIONS_PER_INFERENCE='50'
export VALIDATE_ONLY='false'
bash temporal_ensemble/start_jax_temporal_ensemble_server.shAllow the selected TCP port through the server firewall if the robot client runs on another machine. Do not expose the policy server directly to an untrusted network.
On the robot computer:
cd /path/to/openpi-jax-rtc-te
export OPENPI_ROOT='/path/to/openpi'
export LEROBOT_PIPER_ROOT='/path/to/lerobot-piper'
export CLIENT_PYTHON_BIN='/path/to/python3.11'
export POLICY_HOST='<SERVER_IP>'
export PROMPT='your task instruction'
export LEFT_CAN_NAME='can0'
export RIGHT_CAN_NAME='can1'
export HEAD_CAMERA_SERIAL='<HEAD_CAMERA_SERIAL>'
export LEFT_WRIST_CAMERA_SERIAL='<LEFT_WRIST_CAMERA_SERIAL>'
export RIGHT_WRIST_CAMERA_SERIAL='<RIGHT_WRIST_CAMERA_SERIAL>'
export FRONT_VIEW_CAMERA_SERIAL='<FRONT_VIEW_CAMERA_SERIAL>'
export FPS='30'
export STEPS='100'STEPS=0 means run continuously until Ctrl+C. Use a finite value during initial checks.
There are two different swap controls:
FORCE_SWAP_ARMSchanges how the two policy action blocks are interpreted.COMMAND_ARM_ORDERchanges the blocks immediately before they are sent to the physical arms.
Normal physical command order is:
export COMMAND_ARM_ORDER='left-right'
export COMMAND_SWAP_MODE='absolute'Choose FORCE_SWAP_ARMS from the checkpoint's training action order:
# Policy output is [left 7D, right 7D]
export FORCE_SWAP_ARMS='false'
# Policy output is [right 7D, left 7D]
export FORCE_SWAP_ARMS='true'Do not use COMMAND_ARM_ORDER=right-left merely because the policy order is reversed. FORCE_SWAP_ARMS already converts between policy order and physical left/right order. The command-level swap is a separate diagnostic/compatibility override.
The RTC and TE launchers currently have different FORCE_SWAP_ARMS defaults. Set it explicitly for both methods rather than relying on those defaults.
Dry-run still connects to the robot, captures cameras and state, contacts the policy server, and computes final commands, but it does not call robot.send_action().
RTC dry-run:
cd /path/to/openpi-jax-rtc-te
export POLICY_PORT='8011'
export ACTIONS_PER_INFERENCE='25'
export PREFETCH_THRESHOLD='4'
export RTC_DELAY_STEPS='3'
export RTC_EXECUTION_HORIZON='25'
export RTC_SOFT_MASK_DECAY='0.6'
export RTC_GUIDANCE_SCALE='1.0'
export FORCE_SWAP_ARMS='false'
export ENABLE_ARM='false'
export DRY_RUN='true'
bash rtc/start_jax_rtc_dual_arm_client.sh \
--debug-action-details \
--print-command-countsTE dry-run:
cd /path/to/openpi-jax-rtc-te
export POLICY_PORT='8012'
export ACTIONS_PER_INFERENCE='50'
export ENSEMBLE_INFERENCE_INTERVAL='4'
export ENSEMBLE_DECAY='0.25'
export ENSEMBLE_MAX_CHUNKS='4'
export FORCE_SWAP_ARMS='false'
export ENABLE_ARM='false'
export DRY_RUN='true'
bash temporal_ensemble/start_jax_temporal_ensemble_dual_arm_client.shThe RTC debug flags print observation, model action, delta, final action, and optional motor-command counts. The TE client logs its final 14D action and the aligned chunk indices/weights used at each step. Before enabling motion, confirm that neither arm is unexpectedly all zeros, verify that left/right action blocks follow the intended physical arms, and check for NaN or infinite values.
Only after the server validation and client dry-run match the intended setup:
RTC:
cd /path/to/openpi-jax-rtc-te
export STEPS='0'
export ENABLE_ARM='true'
export DRY_RUN='false'
export DISABLE_ARM_ON_EXIT='false'
bash rtc/start_jax_rtc_dual_arm_client.shTemporal Ensemble:
cd /path/to/openpi-jax-rtc-te
export STEPS='0'
export ENABLE_ARM='true'
export DRY_RUN='false'
export DISABLE_ARM_ON_EXIT='false'
bash temporal_ensemble/start_jax_temporal_ensemble_dual_arm_client.shThe launchers intentionally keep action scaling, additional smoothing, command deadbands, and client-side delta limiting disabled or effectively open. They do not add conservative motion filters. Real-hardware behavior therefore depends directly on the policy outputs, RTC/TE controller, robot driver, and physical controller limits.
Press Ctrl+C to stop. If DISABLE_ARM_ON_EXIT=true, the client also requests arm disable during shutdown after the configured settle period.
The intended RTC setup uses a model horizon of 50 actions while returning a 25-action executable prefix:
full_actions: retains the full model trajectory for the next RTC request.actions: contains the executable prefix returned to the client.RTC_DELAY_STEPS: estimates how many immediate actions must remain fixed while a new inference is in flight.RTC_EXECUTION_HORIZON: limits how many non-expired returned actions are queued for execution.RTC_SOFT_MASK_DECAY: sets the first soft-overlap weight and its exponential decay across the remaining overlap.RTC_GUIDANCE_SCALE: controls the RTC-guided sampling contribution.
Server and client values for ACTIONS_PER_INFERENCE, delay, execution horizon, soft-mask decay, and guidance scale should match. The client logs skip, hard, soft, free, queued, and full_prior at chunk switches; use these fields to confirm that the full prior is retained and the soft region is not empty.
The default deployment values are:
model horizon: 50
ACTIONS_PER_INFERENCE: 25
FPS: 30
PREFETCH_THRESHOLD: 4
RTC_DELAY_STEPS: 3
RTC_EXECUTION_HORIZON: 25
RTC_SOFT_MASK_DECAY: 0.6
RTC_GUIDANCE_SCALE: 1.0
Do not set the server to 25 model-horizon actions if the checkpoint natively predicts 50. RTC needs the full prior trajectory to form a non-empty soft overlap even though only 25 actions are exposed for execution.
The TE client aligns every action using:
action_index = control_step - request_step
Only predictions targeting the current absolute control step are combined. Older predictions receive lower weight according to the configured exponential decay.
The default deployment values are:
ACTIONS_PER_INFERENCE: 50
FPS: 30
ENSEMBLE_INFERENCE_INTERVAL: 4
ENSEMBLE_DECAY: 0.25
ENSEMBLE_MAX_CHUNKS: 4
A smaller inference interval creates more overlap and more server load. A larger decay favors newer predictions more strongly. Increasing ENSEMBLE_MAX_CHUNKS retains more historical plans, which may smooth local noise but can also increase hesitation when old and new chunks represent different task stages.
Set the Python variable to an absolute executable path:
export OPENPI_PYTHON_BIN='/absolute/path/to/python3.11'
export CLIENT_PYTHON_BIN='/absolute/path/to/python3.11'Check that:
VALIDATE_ONLY=falseon the server.- Server uses
POLICY_HOST=0.0.0.0when accepting remote connections. - Client and server use the same port.
- The GPU server firewall allows that port.
- No older server process is still occupying the port.
Verify the exact checkpoint directory, required Orbax metadata files, Python 3.11 environment, CONFIG_NAME, and matching NORM_STATS_PATH. A checkpoint from another action representation or dataset cannot be repaired by changing only the path.
Confirm that all four serial numbers are visible on the Piper computer and unique. If using ROBOT_CAMERAS_JSON, each camera entry must include a numeric serial_number and compatible FPS/resolution fields.
First keep COMMAND_ARM_ORDER=left-right. Then toggle only FORCE_SWAP_ARMS and inspect dry-run action logs. Use command-level swapping only when you intentionally need to cross the physical output blocks.
Confirm that the server returns the complete model horizon as full_actions, that the executable prefix is shorter than the full horizon, and that server/client RTC parameters match. The client chunk-switch log should contain nonzero soft steps.
TE may be averaging chunks that encode different semantic plans. Reduce the number/age of retained chunks, increase decay so newer plans dominate, or compare against RTC. Do not stack ordinary TE on top of RTC.
The source tree has been sanitized: machine paths, device serial numbers, hostnames, dataset identifiers, and task prompts use placeholders. CHECKSUMS.sha256 covers the published source files, launchers, configuration templates, and interface documentation.
To verify them:
cd /path/to/openpi-jax-rtc-te
shasum -a 256 -c CHECKSUMS.sha256Before publishing or redistributing this repository:
- Review the licenses of OpenPI, LeRobot, Piper SDK, RealSense, and all code retained in
references/. - Add an appropriate repository license and upstream attribution.
- Pin tested dependency versions or commits.
- Do not commit checkpoints, datasets, robot videos, norm statistics, SSH keys, credentials, or real device serial numbers.
- Treat syntax checks, synthetic validation, and dry-run as limited evidence; they do not prove safe real-hardware behavior.