humanoidgrailhumanoidunitree-g1retargetingsmpl-xloco-manipulation

Retarget SMPL-X sang Unitree G1

Trace pipeline retarget của GRAIL: SMPL-X sang G1, hand action, table geometry, lift threshold và BPS.

Nguyễn Anh Tuấn7 tháng 6, 202615 phút đọc
Retarget SMPL-X sang Unitree G1

Trong bài 3, chúng ta đã có output 4D HOI: người SMPL-X, object pose 6-DoF, contact và visualization. Trong bài 4, chúng ta tách rõ hai kiểu dữ liệu: manipulation với object động và locomotion/sitting với scene tĩnh. Bài 5 là cầu nối từ thế giới "người trong reconstruction" sang "robot Unitree G1 trong motion library".

Điểm mấu chốt: policy downstream không train trực tiếp trên SMPL-X. SMPL-X là body model của người; Unitree G1 có topology, joint limit, tay, chân, khối lượng và kinematics khác. Vì vậy GRAIL cần một bước retargeting để biến trajectory người-object thành trajectory robot-object. Script trung tâm là grail/retargeting/scripts/retarget_pipeline.sh. Nó không làm phép màu trong một lệnh duy nhất; nó gọi ba stage rõ ràng: grail.retargeting.retarget, grail.retargeting.process, và grail.retargeting.compute_bps.

Nguồn kỹ thuật chính dùng trong bài:

Roadmap series

  1. Tạo asset 3D và terrain cho GRAIL: hai nhánh asset, prompt object, sharding và chuẩn bị file cho downstream.
  2. Sinh video 2D HOI bằng Blender và Kling: render frame điều kiện, camera/depth và video foundation model.
  3. Tái dựng 4D HOI: GEM, SAM2, MoGe: pose người, object tracking, optimization, filtering và visualization.
  4. Locomotion trên terrain tĩnh: curb, slope, stairs và sitting như static-scene 4D HOI.
  5. Retarget trajectory sang Unitree G1: bài hiện tại.
  6. Train policy và export dữ liệu: đóng gói demonstration, train tracker/policy và chuẩn bị sim-to-real.

Nếu bạn cần bối cảnh rộng hơn về whole-body policy, đọc thêm Humanoid loco-manipulationG1 đi terrain bằng reinforcement learning. Hai bài đó giải thích vì sao retargeting không chỉ là đổi tên joint: dữ liệu phải giữ được balance, timing, contact và quan hệ tay-object.

Mục tiêu của bài

Sau bài này, bạn sẽ biết:

  • Input hợp lệ từ 4dhoi_recon_valid nằm ở đâu và đi vào retarget_pipeline.sh như thế nào.
  • Vì sao stage 1 dùng grail.retargeting.retarget để chuyển SMPL-X sang unitree_g1.
  • Stage 2 grail.retargeting.process thêm hand_action_left, hand_action_right, table geometry và lọc contact theo frame lift ra sao.
  • --lift_threshold--grasp_anticipation_frames ảnh hưởng trực tiếp đến lúc tay đóng như thế nào.
  • Stage 3 grail.retargeting.compute_bps tạo object shape embedding trong bps/ để policy có observation cố định.
  • Khi nào nên dùng --zero_out_wrist và khi nào nên bỏ qua process.sh.

Lệnh end-to-end được tài liệu GRAIL khuyến nghị có dạng:

conda activate sonic
export DISPLAY=:1

bash grail/retargeting/scripts/retarget_pipeline.sh \
  data/genhoi/benchmark_v3/generation/4dhoi_recon_valid/Hunyuan \
  benchmark_v3_0203

Tham số thứ nhất là folder reconstruction hợp lệ. Tham số thứ hai là tên motion library dưới data/motion_lib/. Nếu đặt output là benchmark_v3_0203, stage đầu tiên sẽ ghi vào:

data/motion_lib/benchmark_v3_0203/
  robot/
  objects/
  object_usd/
  meta/

Sau stage process, GRAIL tạo thêm một bản đã có hand action:

data/motion_lib/benchmark_v3_0203_ha/
  robot/
  objects/
  meta/

Nếu dataset có nhiều object USD, stage BPS tạo:

data/motion_lib/benchmark_v3_0203/
  bps/
    _basis.npy
    <object_name>.npy

Bức tranh input-output

Từ góc nhìn beginner, hãy nghĩ pipeline này như một bộ chuyển đổi format có kiểm tra vật lý nhẹ. 4dhoi_recon_valid là output của reconstruction. Nó còn mang ngôn ngữ của human-object interaction: SMPL-X pose, object pose, mesh object, contact points và metadata scene. data/motion_lib/<name> là output robot-ready hơn: G1 joint trajectory, object trajectory đã đóng gói, USD asset dùng được trong Isaac Lab, và metadata mà task-general tracker cần đọc.

Giai đoạn Input chính Output chính Ý nghĩa
retarget 4dhoi_recon_valid/... data/motion_lib/<name>/{robot,objects,object_usd,meta} Chuyển pose người SMPL-X sang motion Unitree G1 và convert object mesh sang USD
process data/motion_lib/<name> data/motion_lib/<name>_ha/{robot,objects,meta} Thêm hand action, table pose/size, contact đã lọc, output per-motion
compute_bps data/motion_lib/<name>/object_usd/*.usd data/motion_lib/<name>/bps/*.npy Mã hóa shape object thành vector cố định cho policy observation

Tên motion_lib rất quan trọng. Đừng đặt tên chung chung như test nếu bạn chạy nhiều batch. Nên dùng tên chứa dataset, ngày hoặc config: comasset_pickup_g1_20260607, terrain_v6_zero_wrist, benchmark_v3_0203. Khi lỗi xảy ra, bạn sẽ biết folder nào đến từ config nào.

retarget_pipeline.sh thật sự làm gì?

Script retarget_pipeline.sh là wrapper ngắn nhưng hữu ích. Nó nhận:

bash grail/retargeting/scripts/retarget_pipeline.sh \
  <DATA_DIR> \
  <OUTPUT_FOLDER> \
  [extra_retarget_args...]

Trong đó:

  • DATA_DIR: folder chứa reconstruction đã qua filter, ví dụ data/genhoi/.../generation/4dhoi_recon_valid/Hunyuan.
  • OUTPUT_FOLDER: tên thư mục dưới data/motion_lib/, ví dụ benchmark_v3_0203.
  • Các argument còn lại thường được chuyển cho stage retarget.

Có một ngoại lệ đáng nhớ: --treat_hands_equally hoặc --treat-hands-equally được wrapper tách riêng và chuyển sang stage process, vì đây là flag xử lý hand action chứ không phải flag IK retargeting. Các flag khác, ví dụ --zero_out_wrist, đi vào grail.retargeting.retarget.

Flow rút gọn:

# 1. SMPL-X -> Unitree G1
bash grail/retargeting/scripts/retarget.sh \
  "${DATA_DIR}" "${OUTPUT_FOLDER}" "${RETARGET_ARGS[@]}"

# 2. Hand actions + table geometry
bash grail/retargeting/scripts/process.sh \
  "${OUTPUT_FOLDER}" "${PROCESS_ARGS[@]}"

# 3. BPS nếu có nhiều object USD
USD_COUNT=$(find "data/motion_lib/${OUTPUT_FOLDER}/object_usd" -name "*.usd" | wc -l)
if [[ "${USD_COUNT}" -gt 1 ]]; then
  bash grail/retargeting/scripts/compute_bps.sh "${OUTPUT_FOLDER}"
fi

Điều kiện USD_COUNT > 1 có chủ ý. Với dataset một object duy nhất, BPS không thêm nhiều tín hiệu phân biệt object vì shape không thay đổi giữa motions. Với dataset nhiều object như ComAsset hoặc RoboCasa-style data, policy cần biết nó đang pick object dạng chai, hộp, drill hay vật khác; BPS giúp đưa shape vào observation bằng vector cố định.

Stage 1: grail.retargeting.retarget

Stage đầu tiên được gọi qua retarget.sh:

python -m grail.retargeting.retarget \
  --data_dir "${DATA_DIR}" \
  --all \
  --robot unitree_g1 \
  --output_dir "data/motion_lib/${OUTPUT_FOLDER}" \
  --no_viewer

Nó đọc SMPL-X human pose và object trajectory từ output grail.pipelines.recon_4dhoi, rồi tạo motion library cho G1. Theo docstring của retarget.py, pipeline này có ba việc chính.

Thứ nhất, nó có thể bake tỷ lệ cơ thể G1 vào SMPL-X thông qua G1ProportionSMPLX. Đây là bước giúp giảm mismatch giữa người trong video và robot thật. Nếu một clip dùng human body quá cao, quá dài tay, hoặc tỷ lệ không giống G1, IK solver phải "đuổi theo" target không khả thi. GRAIL đã thiết kế các bước trước để character gần tỷ lệ G1 hơn, nên retargeting có đầu vào dễ chịu hơn so với video in-the-wild.

Thứ hai, nó gọi GMR thông qua grail.adapters.gmr. GMR là kinematics-based retargeting: lấy motion ở source embodiment, giải inverse kinematics trên target robot, rồi làm mượt theo thời gian để motion không giật. Với G1, target không chỉ là vị trí bàn tay. Retargeting phải giữ pelvis/root, chân, đầu gối, cổ chân, torso và tay ở quan hệ hợp lý để motion còn có thể được tracker bám trong simulation.

Thứ ba, nó convert object mesh sang USD và ghép object trajectory. Đây là phần dễ bị bỏ qua. Nếu chỉ có robot joint trajectory mà không có object USD, policy không biết object hình gì, ở đâu, và tương tác với gì. Output object_usd/ là asset sẵn sàng cho Isaac Lab; output objects/ giữ object motion 6-DoF theo thời gian.

Sau stage này, mỗi motion thường có ba loại pkl hoặc asset tương ứng:

data/motion_lib/<name>/
  robot/<motion_id>.pkl      # G1 root, pose_aa, dof, root_rot, smpl_joints, fps
  objects/<motion_id>.pkl    # object root_pos, root_quat, contact points, fps
  meta/<motion_id>.pkl       # object_name, table info nếu có
  object_usd/<object>.usd    # mesh object đã convert cho simulator

Bạn nên kiểm tra số lượng file trước khi đi tiếp:

find data/motion_lib/benchmark_v3_0203/robot -name "*.pkl" | wc -l
find data/motion_lib/benchmark_v3_0203/objects -name "*.pkl" | wc -l
find data/motion_lib/benchmark_v3_0203/meta -name "*.pkl" | wc -l
find data/motion_lib/benchmark_v3_0203/object_usd -name "*.usd" | wc -l

Nếu robot/ có ít file hơn kỳ vọng, lỗi nằm ở retargeting hoặc input reconstruction. Nếu object_usd/ rỗng, stage convert mesh không thành công hoặc input không có mesh object đúng chỗ.

--zero_out_wrist cho terrain và sitting

Không phải mọi dataset đều cần hand IK. Với pickup hoặc tabletop manipulation, wrist và hand contact là tín hiệu task chính. Nhưng với stairs, curb, slope và nhiều clip sitting, object/scene là môi trường lớn; tay không điều khiển object cầm nắm. Tài liệu GRAIL hướng dẫn dùng --zero_out_wrist cho terrain/sitting để skip hand IK:

bash grail/retargeting/scripts/retarget.sh \
  data/genhoi/results_terrain_v6/generation/4dhoi_recon_valid/Hunyuan \
  terrain_v6 \
  --zero_out_wrist

Với terrain, bạn cũng thường bỏ qua process.sh, vì không cần sinh hand_action_left/right. Policy locomotion quan tâm root, chân, terrain geometry và contact với mặt đất hơn là open/close hand. Nếu vẫn chạy process.sh, nó có thể tìm lift frame của object; terrain không "lift" khỏi vị trí ban đầu, nên --skip_no_lift sẽ loại motion hoặc làm bạn debug sai hướng.

Một quy tắc thực dụng:

Dataset Có cần wrist/hand action? Nên chạy gì?
Pick up object từ bàn retarget_pipeline.sh đầy đủ
Pick up object từ ground retarget_pipeline.sh đầy đủ, kiểm tra lift threshold
Carry/push object động Có, nhưng có thể cần config riêng Retarget + process, xem contact kỹ
Stairs/curb/slope Không retarget.sh --zero_out_wrist, bỏ process.sh
Sitting trên ghế đứng yên Thường không retarget.sh --zero_out_wrist, cân nhắc bỏ process.sh

Stage 2: grail.retargeting.process

Stage process biến motion đã retarget thành bản có hand action và table geometry sạch hơn:

python -m grail.retargeting.process \
  --input data/motion_lib/benchmark_v3_0203 \
  --output data/motion_lib/benchmark_v3_0203_ha \
  --meta_pkl data/g1_smplx/g1_skeleton_meta.pkl \
  --include_contact_points \
  --grasp_from_lift \
  --lift_threshold 0.02 \
  --grasp_anticipation_frames 10 \
  --skip_no_lift \
  --per_object

Tên _ha có thể hiểu là "hand action". Trong robot/<motion_id>.pkl, stage này thêm:

hand_action_left   # shape (T,), thường -1.0 là open, 1.0 là closed
hand_action_right  # shape (T,), thường -1.0 là open, 1.0 là closed

Với default legacy path, pipeline giả định pickup chủ yếu bằng tay phải. Nó zero out left arm bằng một pose mặc định, giữ hand_action_left mở, rồi đóng hand_action_right từ thời điểm grasp. Nếu bạn thêm --treat_hands_equally, pipeline giữ cả hai tay và suy ra hand action trái/phải đối xứng từ contact của từng tay. Flag này hữu ích khi video có hai tay cùng tham gia, hoặc khi data generation không đảm bảo object luôn được nhấc bằng tay phải.

So sánh nhanh:

Mode Left arm hand_action_left hand_action_right Khi dùng
Mặc định Bị đưa về pose mặc định Mở Đóng theo lift/contact Dataset pickup tay phải, legacy path
--treat_hands_equally Được giữ lại Suy ra từ contact tay trái Suy ra từ contact tay phải Bimanual, không chắc tay nào grasp, muốn giữ cả hai arm

Stage process cũng xử lý table geometry. Nếu meta/<motion_id>.pkltable_postable_quat, output _ha/meta/ sẽ giữ thêm table_pos, table_quat, table_size. Default --table_size trong parser là [1.5, 0.7, 0.04] mét. Code còn có logic điều chỉnh table để tránh leg intersection trong một số trường hợp: nó dùng forward kinematics từ skeleton metadata, tìm đoạn lower-body giao với mặt phẳng bàn, rồi dịch table theo trục y nếu cần. Với beginner, bạn không cần chỉnh phần này ngay; chỉ cần biết table không phải decoration. Nó là geometry dùng cho task pickup-from-table, nên sai table pose sẽ làm robot học grasp trên một mặt bàn không khớp object.

--lift_threshold: khi nào object được xem là đã nhấc?

Flag --lift_threshold mặc định trong wrapper là 0.02, tức 2 cm. Khi bật --grasp_from_lift, process.py đọc object_motion[f]["root_pos"][:, 0, 2], lấy z ban đầu, rồi tìm frame đầu tiên có:

obj_z > initial_z + lift_threshold

Frame đó là lift_frame. Nếu không có frame nào vượt ngưỡng và --skip_no_lift đang bật, motion bị skip. Đây là lý do log có thể báo "object never lifted above 0.02m".

Ngưỡng 2 cm là lựa chọn hợp lý cho pickup: nó tránh việc noise rất nhỏ ở object z bị hiểu nhầm là lift thật. Nhưng nó không phải hằng số cho mọi asset. Nếu object rất nhỏ, reconstruction hơi thấp, hoặc motion chỉ lăn/trượt chứ không nhấc lên, bạn cần hiểu task trước khi giảm threshold.

Triệu chứng Nguyên nhân có thể Cách xử lý
Nhiều motion bị skip "no lift" Object không được nhấc quá 2 cm hoặc reconstruction z bị thấp Xem video/recon, thử --lift_threshold 0.01 nếu task đúng là pickup nhẹ
Hand đóng quá muộn Lift frame xảy ra sau contact thật Tăng --grasp_anticipation_frames hoặc dùng contact trực tiếp với --treat_hands_equally
Hand đóng quá sớm Contact/lift bị detect sớm do noise Giữ threshold 0.02, giảm anticipation, kiểm tra contact points
Terrain bị skip Terrain không phải object được lift Không chạy process.sh; dùng --zero_out_wrist ở retarget

Đừng giảm --lift_threshold chỉ để tăng số lượng file output. Nếu object chưa bao giờ rời bàn, motion đó có thể không phải pickup thành công. Giữ dữ liệu lỗi sẽ làm policy học sai: tay đóng nhưng object không đi theo, hoặc object tự trượt mà không có grasp đáng tin.

--grasp_anticipation_frames: đóng tay trước lift

Robot không thể chờ object đã bay lên rồi mới đóng tay. Trong dữ liệu pickup, tay phải đóng trước hoặc gần thời điểm lift. --grasp_anticipation_frames giải quyết timing này. Wrapper process.sh lấy giá trị từ biến môi trường GRAIL_GRASP_ANTICIPATION_FRAMES, mặc định là 10.

Logic đơn giản là:

grasp_start_frame = lift_frame - grasp_anticipation_frames
hand_action_right[grasp_start_frame:] = 1.0

Nếu dùng --treat_hands_equally, mỗi tay có action riêng dựa trên contact của tay đó trước lift. Nếu một tay không có contact ở hoặc trước lift, tay đó có thể giữ open.

Ví dụ ở 50 FPS:

grasp_anticipation_frames Tương đương thời gian Tác động
0 0 ms Tay đóng đúng tại lift frame, thường quá muộn
5 100 ms Đóng hơi sớm, phù hợp motion ngắn
10 200 ms Default cân bằng cho pickup thường
20 400 ms Đóng sớm hơn, có thể hợp object lớn nhưng dễ tạo grasp giả

Với beginner, hãy bắt đầu bằng default 10. Chỉ chỉnh khi bạn xem visualization hoặc replay và thấy hand command lệch rõ so với contact.

Stage 3: grail.retargeting.compute_bps

BPS là viết tắt của Basis Point Set. Ý tưởng chung từ paper của Prokudin và cộng sự: thay vì đưa toàn bộ point cloud/mesh object vào policy, ta chọn một tập basis point cố định và mã hóa object bằng khoảng cách từ mỗi basis point đến bề mặt object gần nhất. GRAIL dùng phiên bản rất nhỏ: mặc định --num_basis 10, sample surface object, normalize quanh centroid và scale về unit bounding sphere, rồi lưu vector khoảng cách 10-D.

Lệnh wrapper:

python -m grail.retargeting.compute_bps \
  --object_usd_dir data/motion_lib/benchmark_v3_0203/object_usd \
  --output_dir data/motion_lib/benchmark_v3_0203/bps

Output:

data/motion_lib/benchmark_v3_0203/bps/
  _basis.npy
  cordless_drill.npy
  mug.npy
  bottle.npy
  ...

_basis.npy giúp tái lập cùng hệ basis point. Mỗi <object>.npy là embedding shape của object tương ứng. Trong training, policy có thể nhận object pose, robot state và BPS embedding để phân biệt vật nhỏ/dài/tròn/hộp mà không cần parse USD mesh trong vòng lặp RL.

BPS không thay thế collision geometry. USD vẫn cần cho simulation và contact. BPS là observation compact cho network. Nếu bạn chỉ có một object duy nhất trong dataset, wrapper skip stage này vì shape embedding không giúp phân biệt object giữa motions.

Debug theo folder

Khi pipeline lỗi, đừng đọc log từ trên xuống trong hoảng loạn. Hãy debug theo folder output:

Folder Nếu thiếu Nên kiểm tra
robot/ Retarget SMPL-X sang G1 thất bại Input 4dhoi_recon_valid, GMR install, --robot unitree_g1, viewer/display
objects/ Object trajectory không được đóng gói Reconstruction có object pose không, file input có đúng category không
object_usd/ Mesh conversion thất bại Mesh source, IsaacLab/USD dependencies, quyền ghi output
meta/ Thiếu metadata task Input meta từ reconstruction, table/object name
<name>_ha/robot/ Process skip motion --lift_threshold, --skip_no_lift, contact points
bps/ Không có BPS Có nhiều hơn một USD không, pxr/usd-core import được không

Một smoke test tốt là chọn 3-5 motions, chạy end-to-end, rồi mở pkl bằng joblib để kiểm tra key:

import joblib

motion = joblib.load("data/motion_lib/benchmark_v3_0203_ha/robot/<motion_id>.pkl")
key = list(motion.keys())[0]
print(motion[key].keys())

obj = joblib.load("data/motion_lib/benchmark_v3_0203_ha/objects/<motion_id>.pkl")
print(obj[key].keys())

Bạn muốn thấy robot có dof, pose_aa, root_trans_offset, root_rot, fps, và nếu là manipulation thì có hand_action_left/right. Object nên có root_pos, root_quat, fps, và nếu bật contact thì có contact points đã lọc.

Kết luận

retarget_pipeline.sh là điểm chuyển đổi quan trọng nhất trước training. Nó lấy output 4dhoi_recon_valid của reconstruction và tạo motion library theo layout mà tracker/policy hiểu được: robot, objects, object_usd, meta, và với multi-object dataset là bps. Stage retarget giải bài toán morphology SMPL-X sang Unitree G1. Stage process biến contact/lift thành hand_action_left/right, thêm table geometry và loại motion không có lift thật. Stage compute_bps biến object shape thành embedding cố định cho observation.

Quyết định quan trọng nhất là phân loại dataset trước khi chạy. Với pickup/manipulation, chạy full pipeline và kiểm tra --lift_threshold, --grasp_anticipation_frames, --treat_hands_equally. Với terrain/sitting, dùng --zero_out_wrist ở retarget và thường bỏ qua hand-action processing. Bước tiếp theo trong bài 6 là dùng motion library này để train tracker/policy và export dữ liệu phục vụ sim-to-real.

Bài viết liên quan

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

Train SONIC, export và đánh giá GRAIL
humanoid

Train SONIC, export và đánh giá GRAIL

7/6/202614 phút đọc
NT
Tái dựng 4D HOI: GEM, SAM2, MoGe
humanoid

Tái dựng 4D HOI: GEM, SAM2, MoGe

7/6/202616 phút đọc
NT
Tạo asset 3D và terrain cho GRAIL
humanoid

Tạo asset 3D và terrain cho GRAIL

7/6/202616 phút đọc
NT