wholebody-vlaopenwbtunitree-g1wholebody-vlateleoperationmujocoisaac-simwbc

OpenWBT: G1 teleop trong MuJoCo/Isaac

Dựng OpenWBT cho Unitree G1, kiểm tra policy ONNX, chạy teleop trong MuJoCo rồi mới thử robot thật qua eno1.

Nguyễn Anh Tuấn11 tháng 6, 202613 phút đọc
OpenWBT: G1 teleop trong MuJoCo/Isaac

Vì sao bắt đầu bằng OpenWBT?

Series này không bắt đầu bằng một VLA lớn. Nó bắt đầu bằng teleoperation whole-body chạy được, vì với humanoid, policy thông minh mà nền điều khiển không ổn định thì chỉ tạo ra log đẹp trong simulator. OpenWBT là một điểm vào thực tế cho Unitree G1/H1: repo chính thức mô tả hệ này là whole-body teleoperation dùng Apple Vision Pro, hỗ trợ cả robot thật lẫn môi trường mô phỏng, và chỉ cần một operator để điều khiển đi bộ, squat, cúi người, grasp và lift. Quan trọng hơn, OpenWBT tách bài toán thành hai phần dễ debug: lower-body control bằng joystick và upper-body control bằng inverse kinematics từ pose tay trong VR.

Bài này dựng stack theo đúng thứ tự nên làm trong lab: đọc installation.md, tạo môi trường, kiểm tra hai policy ckpts/loco.onnxckpts/squat.onnx, chạy MuJoCo bằng python -m deploy.run_teleoperation_mujoco --config run_teleoperation.yaml, sau đó mới nghĩ tới robot thật bằng python -m deploy.run_teleoperation_real --config run_teleoperation.yaml --net eno1. Nếu bạn đã đọc các bài cũ về GR00T N1 + G1 data collection hoặc UniFoLM-VLA + G1 deploy, hãy xem OpenWBT như lớp thực thi và thu demonstration ổn định hơn là một bài fine-tune VLA mới.

Roadmap series

  1. OpenWBT: G1 teleop trong MuJoCo/Isaac: dựng môi trường, kiểm tra ONNX policy, hiểu split lower-body joystick và upper-body IK.
  2. TWIST2: teleop imitation cho humanoid: biến teleop thành dữ liệu imitation có cấu trúc, tập trung vào đồng bộ toàn thân.
  3. EgoHumanoid: dữ liệu egocentric cho thao tác: nối camera góc nhìn người vận hành với action/pose của robot.
  4. VIRAL: retarget và kiểm tra kỹ năng: dùng motion source bên ngoài, retarget sang humanoid đích và đánh giá lỗi.
  5. FromW1: chuyển kỹ năng sang robot thật: đi từ sim policy sang real hardware với latency, contact và giới hạn actuator.
  6. CLONE: closed-loop whole-body teleop: nhìn closed-loop teleop như một stack thu dữ liệu dài hạn cho loco-manipulation.

Nguồn kỹ thuật cần đọc trước

Ba tài liệu nên mở song song khi làm bài này:

Tài liệu Dùng để làm gì Điểm cần ghi nhớ
OpenWBT README Hiểu mục tiêu hệ thống và command chạy Hỗ trợ G1/H1, robot thật, MuJoCo, Isaac Sim; lower-body dùng joystick, upper-body dùng IK
OpenWBT installation.md Dựng môi trường Ubuntu 20.04, Python 3.8, Pinocchio, ONNX Runtime, MuJoCo, ROS 2 Foxy, Unitree SDK/ROS 2
OpenWBT deploy configs Kiểm tra policy và tham số robot g1_loco.yaml trỏ tới ckpts/loco.onnx, g1_squat.yaml trỏ tới ckpts/squat.onnx

OpenWBT cũng có demo image trong repo, hữu ích để hình dung setup VR + humanoid:

OpenWBT demo từ repo chính thức
OpenWBT demo từ repo chính thức

Kiến trúc ở mức beginner

Đừng nhìn OpenWBT như một file Python đơn lẻ. Nó là một stack control có nhiều vòng lặp:

Apple Vision Pro / joystick
        |
        | wrist pose, hand pose, joystick command
        v
TeleVisionWrapper + joystick reader
        |
        +--> upper body: G1_29_ArmIK / ik_server.py
        |
        +--> lower body: loco/squat policy ONNX
                    |
                    v
             policy_unified.py
                    |
                    v
             target joint positions
                    |
                    v
        MuJoCo / Isaac Sim / Unitree G1

Lower-body và upper-body không bị nhồi chung thành một VLA end-to-end. Lower-body có hai chế độ chính. Chế độ locomotion dùng ckpts/loco.onnx, policy 12 action cho chân, observation khoảng 47 chiều trong config hiện tại, và có thêm clock input từ gait planner. Chế độ squat/body-pose dùng ckpts/squat.onnx, observation lớn hơn vì đọc 29 DoF, nhưng action chính vẫn tập trung vào phần chân. Upper-body không được predict bằng VLA trong bài này. Nó nhận pose cổ tay trái/phải từ VR, lấy joint hiện tại của hai tay, rồi giải IK để cập nhật phần action high-level trong runner.target_dof_pos.

Thiết kế này thực dụng. Khi robot mất thăng bằng, bạn debug policy chân, gait phase, command scale hoặc PD gain. Khi tay đi sai, bạn debug pose tracking, frame transform hoặc IK. Nếu một VLA vừa nhìn ảnh, vừa hiểu ngôn ngữ, vừa sinh joint toàn thân, bạn có thể có accuracy tốt trên benchmark nhưng rất khó biết lỗi nằm ở perception, policy, contact, latency hay retargeting.

Bước 1: clone và tạo môi trường

Theo installation.md, working directory mặc định là ~/OpenWBT, nhưng bạn có thể đặt ở workspace riêng. Bản hướng dẫn chính thức dùng Conda Python 3.8:

git clone https://github.com/GalaxyGeneralRobotics/OpenWBT.git
cd OpenWBT

conda create -n OpenWBT python=3.8
conda activate OpenWBT

Cụm dependency lõi gồm Pinocchio, MeshCat, CasADi, ONNX Runtime, PySerial và MuJoCo:

# Pinocchio trong installation.md yêu cầu version 3.1.0
conda install pinocchio -c conda-forge

pip install meshcat
pip install casadi
pip install onnxruntime
pip install pyserial
pip install mujoco
pip install -r requirements.txt

Với beginner, lỗi hay gặp nhất không phải policy, mà là môi trường bị trộn. Nếu terminal tự động source ROS 2 trong .bashrc, một số bước build CycloneDDS hoặc Unitree ROS 2 có thể lỗi. Tài liệu OpenWBT khuyến nghị comment dòng source /opt/ros/foxy/setup.bash trong .bashrc để tránh conflict, rồi source thủ công khi cần chạy teleoperation.

Bước 2: cài Unitree SDK và ROS 2 side

Nếu bạn chỉ chạy MuJoCo offline, bạn vẫn nên hiểu phần real robot để không nhầm --net eno1. OpenWBT dùng unitree_sdk2_pythonunitree_ros2 cho kênh giao tiếp robot. Installation guide yêu cầu clone SDK rồi cài editable:

git clone https://github.com/unitreerobotics/unitree_sdk2_python.git
cd unitree_sdk2_python
pip install -e .
cd ..

Phần ROS 2 trong hướng dẫn dùng Foxy, CMake 3.23.3, GCC 9.4.0, rmw_cyclonedds, và build unitree_ros2. Khi nối G1 thật, host cần cùng subnet với robot. Ví dụ hướng dẫn dùng interface eno1, IP host dạng 192.168.123.xxx, netmask 255.255.255.0, gateway 192.168.123.1. Sau đó chỉnh NetworkInterface name="eno1" trong ~/unitree_ros2/setup.sh đúng với card mạng của máy bạn.

Checklist trước khi real robot:

ip addr show eno1

source /opt/ros/foxy/setup.sh
source ~/unitree_ros2/setup.sh

ros2 topic list

Nếu ros2 topic list không thấy topic liên quan robot, dừng ở đây. Đừng chạy deploy.run_teleoperation_real chỉ để "thử xem sao". Với humanoid, lỗi mạng, mode robot sai, hoặc DDS sai interface đều có thể làm vòng điều khiển không nhận state đúng.

Bước 3: kiểm tra policy ONNX

OpenWBT để checkpoint trong ckpts/. Repo hiện có hai file cần quan tâm:

ckpts/loco.onnx
ckpts/squat.onnx

Hai config con trỏ tới hai file này:

# deploy/configs/g1_loco.yaml
policy_path: "ckpts/loco.onnx"
use_gait: True
gait_parameters:
  frequencies: 1.5
  phase_offset: 0.5
  stance_ratio: 0.6
control_dt: 0.02
control_decimation: 4
num_obs: 47
num_dof: 12
num_actions: 12
# deploy/configs/g1_squat.yaml
policy_path: "ckpts/squat.onnx"
use_gait: False
control_dt: 0.02
control_decimation: 4
num_obs: 78
num_dof: 29
num_actions: 12

Trước khi mở viewer, kiểm tra file tồn tại và ONNX Runtime load được:

ls -lh ckpts/loco.onnx ckpts/squat.onnx

python - <<'PY'
import onnxruntime as ort

for path in ["ckpts/loco.onnx", "ckpts/squat.onnx"]:
    sess = ort.InferenceSession(path, providers=["CPUExecutionProvider"])
    print(path)
    print("  inputs :", [(i.name, i.shape, i.type) for i in sess.get_inputs()])
    print("  outputs:", [(o.name, o.shape, o.type) for o in sess.get_outputs()])
PY

Bạn không cần output giống hệt máy khác, nhưng cần thấy session load thành công. Trong policy_unified.py, loader phân nhánh theo extension: .pt dùng TorchScript, .onnx dùng onnxruntime.InferenceSession. Hai policy hiện tại chạy ONNX và output action cộng hidden state. Nếu load ONNX fail, MuJoCo sẽ fail muộn hơn với traceback khó đọc hơn.

Bước 4: hiểu policy_unified.py

policy_unified.py có hai class chính: LocoLowLevelPolicySquatLowLevelPolicy.

Thành phần Loco policy Squat policy
Checkpoint ckpts/loco.onnx ckpts/squat.onnx
DoF quan sát chính 12 lower-body joints 29 joints
Gait clock Không
Command scale 3 chiều, thường là tiến/ngang/yaw 2 chiều, thường là squat/body pose
Vai trò Đi bộ, xoay, chuyển hướng Đứng, squat, chỉnh thân

Trong locomotion, observation gồm command đã scale, gravity orientation, angular velocity, joint position lệch so với default, joint velocity, action trước đó, và obs_clock từ gait planner. Gait clock này là lý do loco.onnx biết chân nào đang ở pha stance/swing. Trong squat, observation không cần clock vì robot không tạo chu kỳ bước chân.

Cả hai policy đều clip observation và action. Sau inference, action được scale bằng action_scale rồi cộng vào default_angles để thành target joint position. Nếu có upper_action, code concat action thân dưới với target thân trên. Đây là chỗ stack nối lower-body policy với upper-body IK mà không bắt policy chân phải học luôn chuyển động tay.

Bước 5: hiểu gait_planner.py

BipedalGaitPlanner rất nhỏ nhưng quan trọng. Nó giữ một biến gait_index, tăng theo dt * frequencies, offset hai chân bằng phase_offset, rồi tạo clock_inputs dạng sin. Với tham số mặc định trong g1_loco.yaml, tần số gait là 1.5, offset hai chân là 0.5, stance ratio là 0.6. Khi stop=True, planner reset cả hai chân về stance middle point để policy không tiếp tục "nghĩ" rằng robot đang bước.

Về trực giác, policy locomotion không chỉ nhận command "đi tới". Nó còn nhận nhịp đi. Nếu clock sai, robot có thể vẫn nhận command đúng nhưng chân trái/phải không phối hợp. Đây là điểm khác giữa một policy locomotion deployable và một đoạn script set joint đơn giản.

Bước 6: hiểu upper-body IK

Trong MuJoCo runner, hàm tv_arms() đọc dữ liệu từ TeleVisionWrapper:

head_rmat, left_wrist, right_wrist, left_hand, right_hand = tv_wrapper.get_data()
current_lr_arm_q = runner.qj.copy()[15:29]
current_lr_arm_dq = runner.dqj.copy()[15:29]
sol_q, sol_tauff = arm_ik.solve_ik(
    left_wrist,
    right_wrist,
    current_lr_arm_q,
    current_lr_arm_dq,
)

Sau đó sol_q bị clip quanh target hiện tại với biên rất nhỏ, rồi ghi vào runner.target_dof_pos[runner.config.action_hl_idx]. Ý nghĩa là tay không được nhảy tức thời theo pose VR. IK giải mục tiêu, nhưng vòng control vẫn giới hạn tốc độ thay đổi để giảm rung và giảm rủi ro tự va chạm.

deploy/helpers/ik_server.py phục vụ trường hợp Isaac Sim client cần gọi IK qua socket. Server nhận JSON gồm left_wrist, right_wrist, current_lr_arm_q, current_lr_arm_dq, gọi G1_29_ArmIK.solve_ik, rồi trả sol_q và cờ success. Nếu solver lỗi, server trả lại joint hiện tại thay vì sinh lệnh rỗng. Đó là một lựa chọn đúng cho safety: khi IK fail, giữ trạng thái hiện tại thường tốt hơn là gửi target không xác định.

Bước 7: chạy MuJoCo trước

OpenWBT README cho command MuJoCo:

source /opt/ros/foxy/setup.sh
source ~/unitree_ros2/setup.sh

python -m deploy.run_teleoperation_mujoco \
  --config run_teleoperation.yaml

run_teleoperation.yaml là config tổng. Nó trỏ squat_config tới g1_squat.yaml, loco_config tới g1_loco.yaml, dùng XML resources/robots/g1_description/g1_29dof_camera.xml, control_dt: 0.02, control_decimation: 4, và 29 DoF. Khi chạy, chương trình bắt đầu ở SQUAT mode, in hướng dẫn bấm Left_A để vào locomotion. Trong locomotion, runner gọi run_loco(manual=True). Trong squat, runner gọi tv_arms() trước rồi run_squat(manual=True).

Luồng kiểm tra nên làm chậm:

  1. Mở MuJoCo, chưa điều khiển gì, quan sát robot có đứng ổn không.
  2. Vào squat mode, thử chỉnh body pose rất nhẹ.
  3. Vào locomotion mode, gửi command nhỏ, ví dụ tiến rất chậm.
  4. Dừng locomotion, xem planner có reset ổn không.
  5. Chỉ sau đó mới bật VR/hand pose nếu phần lower-body không rơi.

Nếu bạn dùng Isaac Sim, README ghi rõ Isaac Sim V4.5.0. Với VR, chạy hai terminal:

bash start_ik_server.sh
bash start_isaacsim_vision.sh

Với non-VR:

bash start_isaacsim.sh

Điểm đáng nhớ là Isaac path dùng ik_server.py rõ hơn, còn MuJoCo path trong script hiện tại gọi G1_29_ArmIK trực tiếp trong process.

Bước 8: chỉ sau đó mới thử robot thật

Command real robot trong README:

source /opt/ros/foxy/setup.sh
source ~/unitree_ros2/setup.sh

python -m deploy.run_teleoperation_real \
  --config run_teleoperation.yaml \
  --net eno1

Thay eno1 bằng đúng interface nối robot. Nếu muốn debug không execute action từ policy, README có mode:

python -m deploy.run_teleoperation_real \
  --config run_teleoperation.yaml \
  --net eno1 \
  --debug

Trình tự robot trong README cũng rất cụ thể: bật robot, dùng Unitree remote để vào deployable mode, ready pose, damping mode, rồi mới chạy teleoperation. Joystick mặc định ở /dev/ttyACM0/dev/ttyACM1, nên có thể cần:

sudo chmod -R 777 /dev/ttyACM0
sudo chmod -R 777 /dev/ttyACM1

Với robot thật, hãy để emergency stop và khoảng trống vật lý là một phần của bài test, không phải ghi chú cuối. OpenWBT README cũng khuyến nghị chiến lược conservative cho lần deploy đầu: đứng xa robot, sẵn sàng kill policy, và dừng ngay nếu thấy hành vi bất thường.

OpenWBT khác gì GR00T/UniFoLM?

Tiêu chí OpenWBT GR00T/UniFoLM VLA
Mục tiêu chính Teleop whole-body ổn định Học policy thao tác từ dữ liệu đa nguồn
Input chính Joystick, VR wrist pose, robot state Camera, language instruction, robot state
Học mới cho bài này Không cần fine-tune VLA Thường cần post-training hoặc fine-tune theo embodiment/task
Điểm mạnh Debug được từng vòng control, sim-to-real gần Khả năng generalize task và language-conditioned behavior
Rủi ro beginner Mạng/ROS/DDS/IK/joystick Dataset, action normalization, checkpoint, inference latency

Các bài GR00T và UniFoLM cũ trên vnrobo.com tập trung vào dữ liệu, checkpoint, inference server và cách đưa VLA vào robot. OpenWBT đi theo hướng khác: làm cho người vận hành điều khiển được whole-body humanoid một cách ổn định, sau đó dùng hệ này làm nền thu demonstration hoặc benchmark WBC. Vì vậy bài này không cố thay thế VLA. Nó trả lời câu hỏi thấp hơn nhưng rất quan trọng: robot có thể đứng, squat, đi chậm, nhận wrist pose, giữ tay không giật, và chuyển mode không rơi không?

Nếu câu trả lời là chưa, fine-tune thêm VLA thường không phải thuốc đúng. Bạn cần debug controller, gait planner, IK, PD gain, network interface, hoặc calibration của VR trước.

Checklist lỗi thường gặp

Triệu chứng Khả năng cao Cách kiểm tra
ONNX load fail Thiếu onnxruntime hoặc file checkpoint sai Chạy script InferenceSession trước khi mở simulator
Robot rơi ngay trong MuJoCo Config XML/policy mismatch hoặc observation sai Kiểm tra run_teleoperation.yaml, g1_loco.yaml, g1_squat.yaml
Không nhận joystick Sai /dev/ttyACM* hoặc thiếu quyền ls /dev/ttyACM*, cấp quyền tạm thời
Real robot không có topic Sai ROS/DDS/interface ip addr, source ~/unitree_ros2/setup.sh, ros2 topic list
Tay giật khi teleop IK target nhảy, frame transform sai, latency VR Test upper-body riêng, giữ lower-body ở squat
Isaac Sim không nhận IK Chưa chạy server hoặc port/socket lỗi Chạy bash start_ik_server.sh, xem log kết nối

Kết luận

OpenWBT là bài mở đầu tốt cho series "Whole-body VLA humanoid 2026" vì nó buộc ta đặt nền control trước khi nói về model lớn. Cài đúng môi trường, kiểm tra loco.onnxsquat.onnx, chạy MuJoCo trước, hiểu rõ policy_unified.py, gait_planner.py, ik_server.py, rồi mới chuyển sang robot thật qua --net eno1. Khi stack này ổn, các bài sau về imitation, egocentric data, retargeting và closed-loop teleop sẽ có điểm tựa thực tế hơn.

Bài viết liên quan

  1. TWIST2: teleop imitation cho humanoid
  2. EgoHumanoid: dữ liệu egocentric cho thao tác
  3. CLONE: closed-loop whole-body teleop
NT

Nguyễn Anh Tuấn

Robotics & AI Engineer. Building VnRobo — sharing knowledge about robot learning, VLA models, and automation.

Khám phá VnRobo

Bài viết liên quan

TWIST2: PICO teleop và G1 sim2real
wholebody-vla

TWIST2: PICO teleop và G1 sim2real

11/6/202617 phút đọc
NT
VIRAL: RGB sim2real cho G1 loco-manip
wholebody-vla

VIRAL: RGB sim2real cho G1 loco-manip

11/6/202616 phút đọc
NT
CLONE: MoE teleop và chọn stack
wholebody-vla

CLONE: MoE teleop và chọn stack

11/6/202617 phút đọc
NT