Bạn nghĩ robot hình người bưng ly cà phê đi qua hành lang mà không sánh ra ngoài là chuyện hiển nhiên? Trong thực tế đây là một trong những bài toán khó nhất của humanoid control: mỗi bước chân là một cú sốc truyền lên tay, khay nghiêng vài độ là ly đổ. Nhóm ArcLab (UC San Diego) vừa phát hành SteadyTray — framework RL cho phép Unitree G1 bưng khay chứa ly, cốc, dụng cụ phẫu thuật qua địa hình gồ ghề, chịu cả va đập bên ngoài, với tỉ lệ thành công 96.9%.
Bài viết này sẽ mổ xẻ paper, giải thích tại sao "residual policy" là chìa khóa, rồi hướng dẫn bạn reproduce lại kết quả trên IsaacLab từ đầu đến khi deploy policy lên MuJoCo.
Tại sao bưng khay khó đến vậy?
Với con người, bưng khay là kỹ năng vô thức. Nhưng robot humanoid phải đối mặt với ba mâu thuẫn kinh điển:
- Locomotion sinh perturbation: Mỗi bước chân tạo ra gia tốc theo phương z (landing impact) và rung lắc theo phương xy. Gia tốc này lan qua hông, lưng, vai, cuối cùng truyền vào khay.
- End-to-end RL collapse: Nếu train một policy duy nhất học cả đi lẫn giữ khay, agent thường "chọn dễ" — đứng yên hoặc bước rất chậm để giảm perturbation, kết quả là không track được command velocity.
- Sim-to-real gap tích lũy: Sai số simulation nhỏ (friction, inertia, latency) khi truyền qua chuỗi khớp dài sẽ khuếch đại ở đầu khay. Policy học trong sim thường không sống sót trên robot thật.
SteadyTray giải quyết cả ba bằng một kiến trúc gọi là ReST-RL (REsidual STabilization RL).
Ý tưởng cốt lõi: Tách bạch locomotion và stabilization
Thay vì học từ đầu, ReST-RL đóng băng một policy locomotion đã train sẵn (có thể tracking velocity tốt), rồi huấn luyện một residual module chỉ phụ trách việc sinh ra các "hiệu chỉnh nhỏ" để giữ khay thăng bằng.
Công thức tổng thể của biến thể Residual Action:
a_final = α_base · a_base(s) + α_residual · a_residual(s, s_priv) + q_default
a_base(s): action từ locomotion policy (frozen)a_residual(·): action hiệu chỉnh từ module mớiq_default: joint position mặc địnhα_base, α_residual: hệ số pha trộn (thườngα_base = 1,α_residualnhỏ)
Biến thể thứ hai — Residual FiLM — còn tinh vi hơn: không cộng vào action, mà điều biến feature của policy gốc bằng scale/shift (Feature-wise Linear Modulation):
y'_i = y_i · (1 + γ_{t,i}) + β_{t,i}
Với γ, β sinh bởi residual encoder. Kết quả: FiLM học nhanh hơn và ổn định hơn action-adapter, vì nó không cần "đánh lại" hành vi locomotion đã đúng.
Kiến trúc chi tiết
Observation space
| Thành phần | Kích thước | Mô tả |
|---|---|---|
| Proprioception | ℝ²⁹ | Joint pos, joint vel, base angular vel, gravity projection |
| Object pose (camera) | ℝ⁷ | Position 3D + quaternion 4D (từ AprilTag) |
| Privileged (train only) | ℝ²⁴ | Base lin vel, tray/object vel, CoM offset |
| Goal | ℝ³ | Velocity command (v_x, v_y, ω_z) |
| History window | H=32 | Context 32 timestep gần nhất |
Lưu ý privileged obs chỉ có trong training. Lúc deploy, student policy chỉ nhìn thấy proprioception + object pose qua camera — nhờ vào một pha distillation mà ta sẽ bàn sau.
Action space & control
29 DoF (đủ cho Unitree G1 phiên bản full): 12 chân, 1 torso, 14 tay (bao gồm 2 cổ tay điều khiển nghiêng khay). Policy output là target joint position, đưa xuống PD controller chạy 1 kHz, policy step tại 50 Hz.
Reward function
Reward chia thành 3 nhóm:
Nhóm locomotion (giữ từ base policy):
- Linear XY tracking:
exp(-λ_vxy · ||v_xy - v̂_xy||²) - Torso stability:
exp(-λ_ω · ||ω_xy||²) - Foot impact smoothness
Nhóm tray stabilization (mới):
- Object upright:
exp(-λ_up · ||P_xy(g_obj)||²)— giữ trọng lực object vuông góc mặt khay - Tray orientation:
exp(-λ · ||P_xy(g_tray)||²)— khay phải nằm ngang - Object-tray contact:
𝟙{contact}— object không rời khỏi khay
Nhóm penalty:
- Action rate, torque, joint limit — các penalty chuẩn trong Isaac Lab humanoid env.
Training 3 giai đoạn + distillation
| Stage | Task | Khởi tạo | Output |
|---|---|---|---|
| 1 | Locomotion only | Scratch | loc_policy.pt |
| 2 | + Tray balance (không có object) | Load Stage 1 | tray_policy.pt |
| 3 | + Object stabilization (residual teacher, dùng privileged obs) | Freeze base, train residual | teacher.pt |
| 4 | Distillation sang student | Student = proprio + camera only | student.pt (deploy) |
Distillation loss kết hợp match cả context latent và action:
L = ||z_teacher - z_student||² + ||a_teacher - a_student||²
Với learning rate thấp 5×10⁻⁵ — đủ để student bắt chước mà không "quên" những gì đã học trong locomotion.
Cài đặt môi trường
Bạn cần một máy có GPU NVIDIA (RTX 3090 trở lên khuyến nghị), CUDA 11.8+ và Docker. Paper train trên 2× RTX A6000 với 8192 parallel envs — bạn có thể giảm xuống 2048–4096 envs trên 1 GPU.
Bước 1 — Clone IsaacLab fork và SteadyTray
SteadyTray yêu cầu IsaacLab fork đặc biệt của tác giả (thêm asset khay + object randomization):
# Tạo workspace
mkdir -p ~/steadytray_ws && cd ~/steadytray_ws
# Clone IsaacLab fork
git clone https://github.com/AllenHuangGit/IsaacLab_SteadyTray.git
# Clone SteadyTray repo
git clone https://github.com/AllenHuangGit/steadytray.git
cd steadytray
git lfs pull # tải checkpoint model/
Bước 2 — Build Docker image
Khuyến nghị dùng Docker vì IsaacLab có rất nhiều dependency phiền phức (OpenUSD, Kit SDK):
cd ~/steadytray_ws/IsaacLab_SteadyTray
./docker/container.py start
./docker/container.py enter
Sau khi vào container, mount SteadyTray:
# Trong container
cd /workspace/steadytray
python -m pip install -e source/steadytray
apt-get update && apt-get install -y git-lfs tmux
Bước 3 — Kiểm tra env
Chạy visualization để xác nhận asset load đúng:
python scripts/rsl_rl/play.py \
--task G1-Steady-Tray-Pre-Locomotion \
--num_envs 4 --video
Bạn sẽ thấy 4 robot G1 đứng sẵn, khay gắn trên hai tay, sẵn sàng train.
Training từng stage
Stage 1 — Locomotion cơ bản
python scripts/rsl_rl/train.py \
--task G1-Steady-Tray-Pre-Locomotion \
--num_envs 4096 --headless \
--max_iterations 3000
Thời gian: ~4 giờ trên 1× A6000. Reward curve sẽ plateau khi robot đi vững ở mọi velocity trong [-1, 1] m/s.
Stage 2 — Giữ khay (chưa có object)
python scripts/rsl_rl/train.py \
--task G1-Steady-Tray-Balance \
--num_envs 4096 --headless \
--resume --load_run <stage1_run_name> \
--max_iterations 2000
Ở stage này, khay đã gắn nhưng chưa có object trên đó. Policy học cách giữ tay thăng bằng trong lúc đi.
Stage 3 — Train residual teacher với privileged obs
python scripts/rsl_rl/train.py \
--task G1-Steady-Object-Teacher \
--num_envs 4096 --headless \
--resume --load_run <stage2_run_name> \
--residual.mode film \
--max_iterations 4000
Chú ý --residual.mode film để dùng biến thể FiLM (kết quả tốt hơn action-adapter trong hầu hết scenario). Nếu có 2 GPU:
python scripts/rsl_rl/train.py \
--task G1-Steady-Object-Teacher \
--num_envs 8192 --headless \
--distributed --nproc_per_node=2 \
--resume --load_run <stage2_run_name>
Stage 4 — Distillation sang student
python scripts/rsl_rl/train.py \
--task G1-Steady-Object-Distillation \
--num_envs 4096 --headless \
--resume --load_run <stage3_run_name> \
--distill.lr 5e-5 \
--max_iterations 2000
Student chỉ nhìn proprioception + object pose từ camera, nhưng học được hành vi của teacher.
Domain randomization — chìa khoá sim-to-real
Đây là phần dễ bị bỏ qua nhất nhưng lại quyết định policy có survive trên robot thật hay không. Config trong source/steadytray/tasks/.../randomization_cfg.py:
| Tham số | Range | Lý do |
|---|---|---|
| Robot friction | [0.3, 1.0] |
Bề mặt thật khác carpet sim |
| Object mass | [0.05, 1.0] kg |
Từ ly cà phê rỗng tới bình nước |
| Tray mass | [0.3, 0.7] kg |
Dung sai vật liệu |
| Object radius | [0.7, 1.5] × 0.03m |
Ly nhỏ tới cốc lớn |
| Object height | [0.75, 2.0] × 0.1m |
Chai thấp tới chai cao |
| Torso CoM offset | ±[2.5, 5, 5] cm |
Lỗi calibration thật |
| Push robot | ±0.5 m/s mỗi [3, 5]s |
Va chạm hành lang |
| Push object | ±0.3 m/s mỗi [2, 4]s |
Vật trượt trên khay |
| Observation delay | 10–30 ms | Camera latency |
Nếu thiếu bất cứ dòng nào trong bảng, sim-to-real hầu như chắc chắn fail.
Inference: chạy policy đã train
Play trong Isaac Lab
python scripts/rsl_rl/play.py \
--task G1-Steady-Object-Distillation \
--num_envs 8 \
--checkpoint model/model_9999.pt \
--video --video_length 500
Sim2Sim sang MuJoCo (trước khi chạy robot thật)
Thư mục deploy/deploy_mujoco/ chứa script chuyển policy sang MuJoCo environment — đây là bước bắt buộc trước khi dám chạy trên hardware:
cd deploy/deploy_mujoco
python run_mujoco.py --policy ../../exported/student_film.onnx
Nếu policy stable 30s trong MuJoCo với object nặng nhất và push mạnh nhất, xác suất cao nó sẽ chạy trên G1 thật. Nếu fail, quay lại tăng domain randomization.
Kết quả từ paper
| Method | Command Track Success | Tilt (rad) | Vel Error (m/s) |
|---|---|---|---|
| Base policy (frozen loco) | 47.4% | 0.179 | 0.110 |
| End-to-End RL | 89.1% | 0.046 | 0.116 |
| ReST-RL (FiLM) | 96.9% | 0.046 | 0.106 |
Trong test push robot (đẩy mạnh vào hông), ReST-RL (FiLM) đạt 84.6% success so với 9.1% của base policy — khoảng cách lên tới 9×. Tương tự với push object (đánh thẳng vào cốc trên khay): ReST-RL 74.6% vs base 25.2%.
Thí nghiệm robot thật: G1 bưng ly rượu đầy nước, dụng cụ phẫu thuật trên khay, đi qua sàn không đều, có người cố tình đẩy vai — không rơi, không tràn.
Ứng dụng thực tế
Đây không chỉ là bài toán đồ chơi. Cùng một framework có thể triển khai cho:
- Dịch vụ nhà hàng/khách sạn: Humanoid bưng khay thức ăn, đồ uống.
- Y tế: Robot phụ tá vận chuyển mẫu bệnh phẩm, dụng cụ tiệt trùng.
- Kho vận: Bưng thùng hàng chưa cố định qua khu vực có bậc.
- Gia đình: Robot gia đình cầm chén đĩa mà không rơi vỡ.
Thậm chí ReST-RL framework có thể generalize cho bất kỳ task nào có hai tầng objective xung đột — ví dụ robot mobile đeo camera và phải đi nhanh mà giữ camera ổn định, hay drone giữ payload trong bão.
Những bẫy thường gặp
- Bỏ qua observation delay — policy trong sim phản ứng tức thời, robot thật có latency 10-30ms. Không randomize delay → policy dao động điên cuồng khi deploy.
- Residual coefficient quá lớn — nếu
α_residual = 1.0từ đầu, residual ghi đè locomotion policy → robot ngã trong 10 step đầu. Nên warmup từ 0.1 lên 1.0. - Skip Stage 2 — nhiều người nóng lòng nhảy thẳng từ locomotion sang object. Kết quả: policy không biết khay tồn tại, action cổ tay loạn xạ.
- Train distillation quá ít step — student cần ~2000 iteration để bắt kịp teacher, không nên dừng ở 500.
- Dùng action-adapter thay FiLM — action-adapter dễ học hơn nhưng ceiling thấp hơn 5–7% success rate so với FiLM.
Bài viết liên quan
- RL cho Humanoid — Tập 1: Giới thiệu tổng quan
- RL cho Humanoid — Tập 8: Loco-manipulation
- Fine-tune GR00T N1 trên Isaac Lab