humanoidgrailhumanoidloco-manipulation4d-hoifoundationposesam2moge

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

Giải thích stack recon_4dhoi: pose người, mask/depth, FoundationPose, HOIOptimizer, filtering và visualization.

Nguyễn Anh Tuấn7 tháng 6, 202616 phút đọc
Tái dựng 4D HOI: GEM, SAM2, MoGe

Trong bài 1, chúng ta đã xem asset 3D và terrain như lớp dữ liệu có metric scale. Trong bài 2, Blender dựng frame điều kiện, camera, depth, rồi Kling tạo video 2D human-object interaction. Bài 3 đi vào bước khó nhất của nhánh manipulation: tái dựng 4D HOI từ video.

"4D" ở đây không có gì huyền bí: đó là 3D cộng thời gian. Output không chỉ là một mesh người ở một frame, mà là chuỗi pose người, pose tay, pose object và contact theo từng frame. Tài liệu GRAIL mô tả stage này là quá trình khôi phục full human-object interaction gồm SMPL-X body pose, MANO hand pose và object trajectory 6-DoF. Nói theo cách robotics: từ một video RGB ngắn, pipeline cần trả lời các câu hỏi downstream sẽ dùng để train robot: người đứng ở đâu, tay chạm vào đâu, object xoay/dịch chuyển thế nào, khi nào contact bắt đầu, và kết quả có đủ sạch để retarget sang Unitree G1 không.

GRAIL có lợi thế hơn các pipeline tái dựng video in-the-wild vì nó không bắt đầu từ video lạ. Trang dự án NVIDIA nhấn mạnh rằng GRAIL bắt đầu từ cấu hình 3D đã biết: geometry object, camera parameters, metric scale, environment depth và character có tỷ lệ phù hợp robot. Bước recon_4dhoi tận dụng chính "privileged context" này để giảm ambiguity về scale, camera và hình học object.

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: dùng curb, slope, stairs cho scene-centric motion.
  5. Retarget trajectory sang Unitree G1: chuyển motion người/object thành mục tiêu robot.
  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 muốn đặt bài này vào bối cảnh rộng hơn của whole-body policy, có thể đọc thêm NVIDIA GR00T/SONIC cho whole-body VLALeRobot v0.5 với humanoid G1. Hai bài đó giải thích vì sao dữ liệu motion sạch, có object và contact, quan trọng hơn một video đẹp.

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

Sau bài này, bạn sẽ hiểu sáu stage của grail.pipelines.recon_4dhoi, biết lệnh smoke test, biết tìm output ở đâu, biết đọc nhanh hoi_data/hoi_data.pkl, recon_result.mp4, recon_comparison.mp4, và biết chỉnh các trường quan trọng trong configs/recon_4dhoi/manip_smplx.yaml: opt_stage_specs.init_opt, contact_opt, filter_object_motion: dynamic_only, và pipeline.is_static_obj.

Lệnh cơ bản cho object manipulation:

python -m grail.pipelines.recon_4dhoi \
  --dataset ComAsset \
  --category cordless_drill \
  --results_dir results

Lệnh chạy một video cụ thể:

python -m grail.pipelines.recon_4dhoi \
  --video_id ComAsset/cordless_drill/<video_name> \
  --results_dir results

Nếu bạn đã chạy xong pose người, mask/depth và object pose, rồi chỉ muốn chạy lại optimization sau khi chỉnh config:

python -m grail.pipelines.recon_4dhoi \
  --dataset ComAsset \
  --category cordless_drill \
  --results_dir results \
  --skip_step1 \
  --skip_step2 \
  --skip_step3

Đừng chạy lệnh này từ thư mục blog. Bạn cần chạy từ root repo GRAIL, trong môi trường đã cài checkpoint. README chính thức dùng Docker, cài Blender và checkpoint, rồi chạy bash scripts/setup/download_checkpoints.sh để tải GEM-SMPL/GEM-SOMA/FoundationPose weights.

Bản đồ sáu stage

recon_4dhoi là một stack nhiều model. Beginner thường gặp lỗi vì nghĩ đây là một model end-to-end duy nhất. Thực tế, mỗi stage tạo cache cho stage sau:

# Stage Công cụ chính Input Output dùng tiếp
1 Human pose GEM-SMPL hoặc GEM-SOMA, WiLoR Video RGB .npz pose người, keypoint body/tay
2 Preprocess SAM2, MoGe, camera/depth cache Video, first-frame masks, camera mask object/người theo frame, depth/point cloud
3 Object pose FoundationPose RGB, mask object, mesh OBJ object 6-DoF pose theo frame
4 HOI optimization HOIOptimizer pose người, object pose, mesh, depth, masks hoi_data.pkl thô
5 Filter và post-process threshold, contact/motion checks hoi_data.pkl thô folder _valid, mesh_data, video so sánh
6 Visualization PyTorch3D, ScenePic HOI đã lọc MP4 overlay, top view, HTML 3D

Tài liệu GRAIL ghi thời gian tham khảo trên L40S: human pose khoảng 45 giây/video, SAM2 + MoGe khoảng 36 giây/video, FoundationPose khoảng 40 giây/video, còn HOI optimization là phần nặng nhất, khoảng 9-10 phút/video. Con số này không phải SLA; nó giúp bạn biết stage nào đáng shard, stage nào chỉ cần cache lại.

Stage 1: GEM-SMPL và WiLoR cho pose người

Stage 1 gọi run_human_pose_est. Với config mặc định manip_smplx.yaml, human_model.body_modelg1_smplx. Điều này nói với pipeline rằng output cần tương thích thân người SMPL-X đã được điều chỉnh cho G1, không phải một body model chung chung.

GEM là model human motion của NVIDIA cho monocular video. Model card GEM-X mô tả GEM như một hệ ước lượng 3D body pose từ video RGB, có global motion trajectory và local body kinematics. Trong GRAIL, tài liệu reconstruction nói stage này dùng GEM-SMPL body và WiLoR hands, rồi fuse per-frame. WiLoR là model 3D hand localization/reconstruction in-the-wild; paper và repo của WiLoR nhấn mạnh pipeline có hand localization real-time và 3D reconstruction dựa trên MANO. Với manipulation, phần tay không phải chi tiết phụ. Nếu wrist, finger hoặc hand keypoint lệch, optimizer có thể "ép" contact sai vào object.

Output chính của stage 1 thường nằm dưới:

results/generation/hmr_smplx/
results/generation/hmr_smplx_cache/

Trong code, stage này lưu cả motion_globalmotion_incam. Bạn có thể hiểu đơn giản:

  • motion_incam: pose trong hệ camera, hữu ích cho reprojection/keypoint tracking.
  • motion_global: pose đã có global trajectory, cần cho motion 4D theo thời gian.
  • cache keypoint: dùng để debug tay/chân có bám video không.

Lỗi beginner hay gặp: video Kling có người bị crop chân hoặc tay, nhưng bạn vẫn chạy reconstruction. Khi stage 1 thiếu keypoint ổn định, các stage sau không thể "sửa kỳ diệu". Nếu recon_result.mp4 cho thấy skeleton trượt khỏi người ngay từ đầu, hãy quay lại kiểm tra video 2D hoặc frame render ở bài 2.

Stage 2: SAM2 mask tracking và MoGe depth

Stage 2 làm hai việc: track mask và ước lượng depth. Trong code, preprocess_masks dùng mask object/human ở first frame từ output render/FoundationPose input, rồi track qua video. SAM2 phù hợp cho việc này vì Meta giới thiệu nó như một model segmentation thống nhất cho image và video, có thể segment object trong ảnh/video và theo dõi qua frame. Với GRAIL, SAM2 giúp biến video RGB thành hai tín hiệu rất thực dụng: vùng pixel của object và vùng pixel của người.

Sau mask là depth. GRAIL dùng MoGe cho monocular geometry estimation khi cần depth từ ảnh/video. MoGe là project của Microsoft cho open-domain monocular geometry; trong pipeline này, depth không dùng một mình, mà kết hợp với camera intrinsic, mask và mesh để tạo ràng buộc hình học cho optimizer.

Cache quan trọng:

results/generation/4dhoi_recon_cache/masks/<video_id>.npz
results/generation/4dhoi_recon_cache/depth/<video_id>.pt

Nếu --verbose, pipeline có thể xuất debug mask/depth. Bạn nên bật verbose khi thêm object mới, khi object bị occlude nhiều, hoặc khi video model tạo bàn tay che object quá lâu. Mask sai thường nhìn thấy ngay: object mask nhảy sang tay người, mất object trong vài frame, hoặc dính cả background. Depth sai khó thấy hơn, nhưng hậu quả là object/human bị kéo sai theo trục z.

Một checklist thực dụng:

Hiện tượng Nguyên nhân thường gặp Hành động
Object mask mất sau khi người chạm SAM2 bị occlusion hoặc object quá nhỏ Render camera gần hơn, tăng kích thước object, chọn video rõ hơn
Human mask dính object Object cùng màu/quá sát cơ thể Kiểm tra first-frame masks, giảm occlusion trong prompt video
Depth làm object nổi hoặc chìm Monocular depth thiếu anchor Dùng depth GT nếu có, kiểm tra camera/depth từ bài 2
Cache không đổi dù đã sửa video --skip_done dùng cache cũ Xóa cache liên quan hoặc tắt --skip_done

Stage 3: FoundationPose cho object 6-DoF

Stage 3 chạy FoundationPose để track pose object. FoundationPose là model NVIDIA cho 6D pose estimation và tracking of novel objects, hỗ trợ cả model-based setup khi có CAD/mesh object. GRAIL đúng là có mesh object từ bài 1, nên object tracking không cần đoán category từ zero.

6-DoF nghĩa là object có 3 bậc tự do translation và 3 bậc tự do rotation. Với robot manipulation, đây là dữ liệu cực quan trọng. Nếu drill được nâng lên nhưng tracker giữ nó đứng yên trên bàn, policy downstream sẽ học sai. Nếu rotation quanh trục dài bị flip, grasp/contact có thể không còn ăn khớp.

Stage 3 đọc mesh từ:

results/generation/mesh/<dataset>/<category>/*.obj

và output pose:

results/generation/foundation_pose_output/<video_id>/pose_estimation_output/poses_in_cam.pkl

Trong manip_smplx.yaml:

obj_pose_tracking:
  foundation_pose_debug: 2
  crop_image: false
  interpolation_factor: 1

foundation_pose_debug: 2 giữ mức debug khá hữu ích. crop_image: false nghĩa pipeline không crop image trước khi track; giữ toàn frame có thể ổn hơn khi camera/render đã sạch. interpolation_factor: 1 không nội suy thêm frame.

Trường cần hiểu là static/dynamic:

pipeline:
  is_static_obj: false

Với manipulation/pickup, object được kỳ vọng di chuyển, nên is_static_obj: false để FoundationPose chạy tracking. Với terrain hoặc sitting, object/scene thường tĩnh; config locomotion đặt static-object mode để bypass FoundationPose. Nếu bạn đặt sai, filter ở stage 5 sẽ loại kết quả hoặc giữ nhầm kết quả vô nghĩa.

Stage 4: Multi-stage HOIOptimizer

Đây là trái tim của reconstruction. Ba stage đầu chỉ cho estimate riêng lẻ: pose người, mask/depth, pose object. HOIOptimizer biến chúng thành một trajectory HOI nhất quán hơn bằng cách tối ưu nhiều biến cùng lúc.

Một bản rút gọn của config:

optimization:
  opt_stage_specs:
    init_opt:
      opt_vars:
        human_trans_global: { lr: 0.003 }
        human_trans_res: { lr: 0.003 }
        human_pose_res: { lr: 0.0001 }
        obj_t_res: { lr: 0.003 }
        obj_R_res: { lr: 0.0001 }
      niter: 400
      loss_cfg:
        keypoint_tracking: { weight: 0.3, beta: 0.1 }
        human_smoothness: { weight: 30.0, beta: 0.1 }
        human_pose_reg: { weight: 100.0 }
        human_foot_contact: { weight: 10.0 }
        obj_smoothness: { weight: 50.0 }
        depth_pointcloud:
          weight: 100.0
          num_gt_samples: 3000
          trim_pct: 0.2
          depth_tol: 0.02
    contact_opt:
      niter: 200
      loss_cfg:
        contact:
          weight: 30000.0
          depth_only: true
          max_contact_dist: 0.15

init_opt là stage kéo mọi thứ về cùng một hệ tọa độ hợp lý trước. Nó không nên quá hung hăng với pose residual, vì nếu pose người bị bẻ mạnh chỉ để fit object, motion sẽ mất tự nhiên. Các loss chính:

Loss Ý nghĩa
keypoint_tracking Giữ pose render lại bám keypoint 2D trong video
human_smoothness Tránh người giật frame-to-frame
human_pose_reg Không để pose lệch quá xa prior
human_foot_contact Giữ chân hợp lý với mặt đất khi cần
verts_tracking Dùng vertex/mask alignment để kéo mesh bám ảnh
obj_smoothness Tránh object trajectory giật
depth_pointcloud Bám depth/point cloud để giảm lỗi z-scale

contact_opt chạy sau khi có contact labels. Tài liệu GRAIL ghi stage 4 có OpenAI vision calls trong grail/core/contact_label.py để detect contact joints theo interval, mặc định dùng gpt-4o. Mục tiêu không phải để LLM "điều khiển robot"; nó chỉ giúp gán khoảng thời gian và joint nào có khả năng tiếp xúc object. Sau đó optimizer dùng loss contact để kéo điểm tay/cơ thể và object gần nhau trong những interval hợp lý.

Khi nào chỉnh init_opt?

Triệu chứng Gợi ý chỉnh
Người trượt khỏi video nhưng object ổn Tăng nhẹ keypoint_tracking, kiểm tra stage 1 trước
Motion người rung Tăng human_smoothness, không tăng quá cao
Object nhảy frame-to-frame Tăng obj_smoothness, kiểm tra FoundationPose
Object sai độ sâu Kiểm tra depth; chỉ tăng depth_pointcloud khi mask/depth đáng tin

Khi nào chỉnh contact_opt?

Triệu chứng Gợi ý chỉnh
Tay gần object nhưng không chạm Tăng contact weight hoặc max_contact_dist nhẹ
Tay bị hút xuyên object Giảm contact weight, kiểm tra penetration/filter
Contact bắt đầu sai thời điểm Chạy lại contact labeling hoặc kiểm tra video prompt
Object bị kéo theo tay khi chưa chạm Chú ý obj_precontact_reg và interval contact

Rule cho beginner: đừng sửa tất cả weight cùng lúc. Chọn một video, chạy lại từ stage 4, so sánh recon_comparison.mp4, rồi mới chỉnh tiếp.

Stage 5: Filtering và post-processing

Optimization luôn có thể trả ra một trajectory nhìn "có vẻ" hợp lý nhưng không đáng dùng cho training. Vì vậy stage 5 lọc kết quả. Tài liệu GRAIL liệt kê các threshold chính: human-position error, mask alignment, keypoint tracking, contact penalty, penetration và motion magnitude.

Trong manip_smplx.yaml:

filtering:
  camera_trans_thr: 0.1
  object_mask_tol: 0.5
  total_mask_tol: 0.3
  human_static_thr: 0.01
  min_frames: null
  filter_object_motion: "dynamic_only"
  object_static_thr: 0.02

filter_object_motion: dynamic_only là một chi tiết quan trọng. Bài này là manipulation/pickup, nên pipeline muốn loại những reconstruction trong đó object gần như không di chuyển. Nếu video chỉ có người đứng cạnh drill, hoặc FoundationPose thất bại và object trajectory bị static, kết quả đó không có ích cho manipulation policy. Ngược lại, với locomotion terrain ở bài 4, object/terrain tĩnh lại là đúng; config locomotion dùng static_only.

Khi một result pass filter, pipeline đóng gói vào:

results/generation/4dhoi_recon_smplx_valid/<dataset>/<category>/<video_id>/
  hoi_data/
    hoi_data.pkl
  mesh_data/
    model.obj
    model.mtl
    ...
  result_vis/
    input.mp4
    recon_result.mp4
    recon_result_top_view.mp4
    recon_comparison.mp4
    recon_result.html

Nếu result fail, bạn sẽ thấy marker invalid trong folder output thô. Đừng chỉ xóa marker để ép dữ liệu vào training. Filter fail thường có lý do: mask kém, pose người sai, object không động, contact phi vật lý hoặc motion quá ngắn.

Stage 6: ScenePic và PyTorch3D visualization

Visualization không phải phần phụ để làm đẹp. Nó là cách nhanh nhất để bạn quyết định trajectory có đáng retarget không. Tài liệu GRAIL nói stage visualize dùng PyTorch3D top-down và side-by-side renders, cùng ScenePic HTML. PyTorch3D cung cấp rendering 3D/differentiable rendering trong hệ PyTorch/CUDA; ScenePic là thư viện 3D visualization xuất HTML nhẹ, mở được bằng browser mà không cần Blender.

Ba file nên mở sau mỗi run:

File Bạn kiểm tra gì
result_vis/recon_result.mp4 Mesh người/object có bám video input không
result_vis/recon_comparison.mp4 So sánh input và reconstruction side-by-side
result_vis/recon_result.html Xoay scene 3D, nhìn top/side, kiểm tra contact và penetration

recon_result_top_view.mp4 đặc biệt hữu ích cho lỗi không thấy trong camera view. Một reconstruction có thể khớp ảnh 2D nhưng sai chiều sâu: nhìn từ camera thì ổn, nhìn top view thì tay cách object 30 cm. Đây là lý do GRAIL dùng camera/depth/mesh thay vì chỉ fit silhouette 2D.

Đọc nhanh hoi_data/hoi_data.pkl

hoi_data.pkl là artifact chính. Nó là pickle Python, nên bạn không nên mở bằng text editor. Dùng script nhỏ:

import pickle
from pathlib import Path

path = Path("results/generation/4dhoi_recon_smplx_valid/ComAsset/cordless_drill/<video_id>/hoi_data/hoi_data.pkl")

with path.open("rb") as f:
    hoi = pickle.load(f)

print(hoi.keys())
print(hoi["human_data"].keys())
print(hoi["object_data"].keys())
print(hoi["meta"].keys())

poses = hoi["human_data"]["poses"]
obj_pose = hoi["object_data"]["object_poses"]
print("num frames:", poses.shape[0])
print("human poses:", poses.shape)
print("object poses:", obj_pose.shape)

Tên key có thể thay đổi theo phiên bản code, nhưng pattern thường là:

  • human_data: pose, translation, body params, keypoints hoặc joints.
  • object_data: object pose/trajectory theo frame.
  • meta: đường dẫn mesh, file object pose thô, masks cache, render config.
  • contact: contact labels hoặc contact points nếu đã post-process.

Bạn đang tìm ba điều:

  1. Số frame hợp lý, không rỗng, không ngắn bất thường.
  2. Human/object arrays có cùng chiều thời gian.
  3. meta.obj_path, masks_cache_file, render_config_file trỏ đúng artifact của video.

Nếu hoi_data.pkl pass nhưng visualization fail, hãy ưu tiên visualization. Dataset tốt là dataset có thể nhìn lại và giải thích được, không chỉ là pickle có đủ key.

Chỉnh manip_smplx.yaml theo tình huống

Bốn field người mới nên nhớ:

Field Mặc định Khi nào đổi
optimization.opt_stage_specs.init_opt Stage tối ưu ban đầu Khi pose/object/depth đã đúng cache nhưng alignment chưa tốt
optimization.opt_stage_specs.contact_opt Stage tối ưu contact Khi tay/object gần đúng nhưng contact sai
filtering.filter_object_motion dynamic_only Giữ cho manipulation chỉ nhận object có chuyển động
pipeline.is_static_obj false Chỉ đổi thành true cho terrain/static object

Ví dụ giữ manipulation dynamic:

filtering:
  filter_object_motion: "dynamic_only"
  object_static_thr: 0.02

pipeline:
  is_static_obj: false

Ví dụ nếu bạn cố tình tái dựng một scene static để debug:

python -m grail.pipelines.recon_4dhoi \
  --dataset ComAsset \
  --category cordless_drill \
  --results_dir results \
  --is_static_obj

Nhưng đừng dùng static mode cho pickup thật. Nó có thể giúp debug mask/depth, nhưng không tạo object trajectory động đúng cho manipulation.

Debug theo thứ tự nào?

Đừng bắt đầu bằng việc tăng loss weight. Hãy debug theo thứ tự artifact:

  1. Mở video input trong results/generation/videos_kling.
  2. Kiểm tra stage 1 keypoint/body cache nếu người trượt.
  3. Kiểm tra mask .npz hoặc debug mask nếu object mất.
  4. Kiểm tra poses_in_cam.pkl bằng visualization FoundationPose nếu object pose sai.
  5. Chạy lại stage 4 sau khi chỉnh init_opt hoặc contact_opt.
  6. Chỉ nới filter khi bạn hiểu vì sao filter fail.
  7. Mở recon_comparison.mp4recon_result.html trước khi đưa dữ liệu sang retarget.

Với batch lớn, dùng sharding:

python -m grail.pipelines.recon_4dhoi \
  --dataset ComAsset \
  --results_dir results \
  --job_chunk_idx 0 \
  --num_job_chunks 8

Tài liệu GRAIL ghi một run 8 chunk có thể xử lý khoảng 24 video trong khoảng 35 phút wall-clock. Điểm quan trọng là cache: sau khi stage 1-3 ổn định, bạn có thể lặp stage 4-5 nhanh hơn nhiều so với chạy lại toàn bộ.

Kết luận

grail.pipelines.recon_4dhoi không chỉ "reconstruct video". Nó là cầu nối giữa video foundation model và dữ liệu robot học được. GEM-SMPL/WiLoR tạo prior người và tay. SAM2/MoGe biến RGB thành mask/depth. FoundationPose bám object 6-DoF bằng mesh đã biết. HOIOptimizer gom tất cả thành trajectory có contact. Filtering loại kết quả tệ. ScenePic/PyTorch3D giúp bạn kiểm tra trước khi retarget.

Điểm thực dụng nhất: hãy tin artifact hơn tin log. hoi_data.pkl cho bạn dữ liệu, nhưng recon_result.mp4, recon_comparison.mp4recon_result.html mới cho bạn biết dữ liệu đó có đáng dùng không. Khi reconstruction đã ổn, bài 5 sẽ chuyển trajectory người/object sang Unitree G1; còn bài 6 sẽ đóng gói motion này cho training và export.

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
Retarget SMPL-X sang Unitree G1
humanoid

Retarget SMPL-X sang Unitree G1

7/6/202615 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