humanoidgrailhumanoidloco-manipulation2d-hoiblenderklingsimulation

Sinh video 2D HOI bằng Blender và Kling

Chạy gen_2dhoi cho cordless_drill: Bullet settling, scale/prompt refinement, render scene và video Kling.

Nguyễn Anh Tuấn7 tháng 6, 202615 phút đọc
Sinh video 2D HOI bằng Blender và Kling

Trong bài 1, chúng ta đã xem asset 3D và terrain như lớp dữ liệu đầu vào của GRAIL. Bài 2 đi sang stage kế tiếp: sinh video 2D human-object interaction (2D HOI) từ một scene 3D đã biết. Đây là bước nối giữa asset tĩnh và reconstruction 4D ở bài 3: Blender dựng frame điều kiện, camera và depth; Kling tạo video người tương tác với object; metadata render được giữ lại để các bước sau không phải đoán lại scale/camera từ video tự do.

Theo trang dự án GRAIL của NVIDIA, pipeline này khác cách "lấy video internet rồi reconstruct" ở chỗ nó bắt đầu từ cấu hình 3D có thông tin đầy đủ: geometry object, camera, metric scale, environment depth và character có tỷ lệ phù hợp robot. Tài liệu 2D HOI chính thức mô tả stage này là chuỗi physics simulation, Blender multi-view rendering và video foundation model, mặc định dùng Kling AI. Nói ngắn gọn: video 2D không phải output cuối, mà là prior hình ảnh có kiểm soát cho 4D HOI.

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

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

Sau bài này, bạn sẽ chạy được smoke test với ComAsset/cordless_drill, hiểu các tham số quan trọng trong configs/gen_2dhoi/manipulation.yaml, biết skip_step1skip_step2 ảnh hưởng gì, biết đọc các thư mục initial_states, asset_renders, cameras, depth_maps, videos_kling, và biết khi nào nên đổi sang terrain_stairs.yaml, sitting.yaml, pickup_table.yaml hoặc pickup_ground.yaml.

Lệnh trung tâm là:

python -m grail.pipelines.gen_2dhoi \
  --dataset ComAsset \
  --category cordless_drill \
  --character kid \
  --results_dir results \
  --video_model_api kling-ai

Bạn nên chạy từ root repo GRAIL. Tài liệu chính thức nhấn mạnh stage này không có wrapper script ở root; hãy gọi package entrypoint bằng python -m grail.pipelines.gen_2dhoi. Nếu chạy trong Docker của GRAIL, flow chuẩn là pull image, mount repo, cài Blender/checkpoint, conda activate grail, rồi source .env để nạp OPENAI_API_KEY, KLING_ACCESS_KEY, KLING_SECRET_KEY và các token cần thiết.

manipulation.yaml đang nói gì?

File configs/gen_2dhoi/manipulation.yaml là config mặc định cho tabletop/handheld manipulation. Phần đầu file đặt character, thư mục asset, output root và object config:

character: kid
character_dir: data/characters
texture_dir: data/characters
results_dir: results_collection/manipulation001
object_config: configs/objects/comasset.yaml
verbose: false
skip_step1: false
skip_step2: true
skip_step3: false
skip_step4: false
skip_done: false

Điểm quan trọng nhất cho beginner là skip_step2: true. Điều này không có nghĩa scale không quan trọng. Nó có nghĩa phần lớn object trong configs/objects/comasset.yaml đã có obj_scale thủ công. Ví dụ cordless_drill dùng scale [2.5, 2.5, 2.5] và scene indoor2-manipulation. Vì vậy smoke test không cần mở vòng OpenAI đánh giá scale, đỡ tốn API và thời gian.

Ngược lại, skip_step1: false nghĩa là mặc định pipeline vẫn chạy Blender Bullet settling để tìm orientation ổn định cho object. Với object cầm tay như drill, hammer, frypan, axe, một mesh có thể được xuất ra ở pose tùy ý. Nếu render ngay, object có thể lơ lửng, xuyên bàn hoặc nằm trên cạnh rất thiếu tự nhiên. Step 1 thả object từ độ cao nhỏ, để Bullet settle vài giây, rồi lưu trạng thái cuối vào cache.

Bảng dưới đây là các tham số bạn nên biết trước khi chỉnh:

Tham số Mặc định trong manipulation.yaml Ý nghĩa thực tế
skip_step1 false Có chạy Bullet settling để lấy orientation ổn định hay không
skip_step2 true Có bỏ qua scale optimization bằng render + OpenAI vision hay không
rendering.num_rand_scenes 3 Số biến thể camera/lighting random cho mỗi object
rendering.samples 32 Samples render Blender, cân bằng tốc độ và noise
rendering.width / height 1280 / 720 Kích thước frame điều kiện cho video
video.kling_model_name kling-v2-5-turbo Model Kling dùng cho image-to-video
video.duration "5" Độ dài video segment, tính theo giây
video.kling_mode pro Chế độ Kling, mặc định ưu tiên chất lượng
video.video_max_retries 100 Số lần retry nếu API chưa trả video hợp lệ
video.video_retry_wait 30 Thời gian chờ giữa các retry, tính bằng giây

Nếu bạn chỉ muốn render frame và kiểm tra scene, có thể bỏ qua step 4:

python -m grail.pipelines.gen_2dhoi \
  --dataset ComAsset \
  --category cordless_drill \
  --character kid \
  --skip_step1 \
  --skip_step2 \
  --skip_step4 \
  --results_dir results

Lệnh này hữu ích khi bạn chưa có Kling key hoặc đang debug camera/scale. Nó vẫn tạo ảnh render, camera và depth nếu step 3 chạy thành công.

Bước 1: Blender Bullet settling

Step 1 gọi script Blender simulation để tạo initial state. Trong config mặc định:

simulation:
  drop_height: 0.1
  settling_time: 5.0
  initial_rotation_perturbation: 2.0
  seed: 42
  save_usd: false
  use_initial_state: true

drop_height: 0.1 nghĩa là object được thả từ cao hơn vị trí đặt một chút, không phải thả từ trần nhà. settling_time: 5.0 cho Bullet thời gian để vật thể rơi, va chạm, xoay và ngừng dao động. initial_rotation_perturbation: 2.0 tạo nhiễu rotation ban đầu; mục tiêu là tránh pose quá nhân tạo nhưng vẫn kiểm soát được.

Kết quả step này nằm trong:

results/generation/initial_states/

Bạn có thể hiểu folder này như cache "object nằm ổn định ra sao". Nếu chạy lại cùng object và muốn tiết kiệm thời gian, dùng:

python -m grail.pipelines.gen_2dhoi \
  --dataset ComAsset \
  --category cordless_drill \
  --character kid \
  --skip_step1 \
  --skip_step2 \
  --results_dir results

Chỉ dùng --skip_step1 khi bạn tin rằng cache initial state đã tồn tại và hợp lệ. Nếu đổi mesh, đổi object rotation, đổi scene contact hoặc xóa cache, hãy chạy lại step 1. Lỗi thường gặp là object render bị xuyên mặt bàn hoặc đứng ở orientation kỳ lạ vì bạn bỏ qua settling khi không nên bỏ qua.

Bước 2: OpenAI scale evaluation

Step 2 là scale optimization: pipeline render một scene tạm, gửi ảnh cho chat-vision, yêu cầu trả đúng một trong ba nhãn small, big, correct, rồi cập nhật scale theo kiểu nhân đôi/chia đôi và binary search. Config mặc định giới hạn:

scale:
  max_iterations: 5

Trong manipulation.yaml, step này bị skip vì configs/objects/comasset.yaml đã chứa scale cho object. Ví dụ:

objects:
  cordless_drill:
    obj_scale: [2.5, 2.5, 2.5]
    scene: indoor2-manipulation

Khi nào nên bỏ --skip_step2 hoặc đặt skip_step2: false? Khi bạn thêm object mới chưa có scale, khi mesh xuất từ Hunyuan3D trông quá nhỏ/lớn so với character, hoặc khi object thuộc loại không giống các object ComAsset có sẵn. Khi chạy step 2, bạn cần OPENAI_API_KEY. Nếu không có key, pipeline không thể dùng vision model để đánh giá scale.

Với beginner, quy tắc thực dụng là:

Tình huống Nên làm
Chạy smoke test cordless_drill Giữ skip_step2: true
Thay đổi camera/render nhưng giữ object config Giữ skip_step2: true
Thêm object mới chưa có scale Chạy step 2 một lần, xem obj_scales
Scale sai rõ ràng nhưng OpenAI trả correct Sửa configs/objects/*.yaml thủ công và render lại
Muốn chạy offline không gọi API Bắt buộc phải dùng scale có sẵn và skip_step2

Step 2 ghi kết quả vào:

results/generation/obj_scales/

Dù yêu cầu bài này tập trung các output folder initial_states, asset_renders, cameras, depth_maps, videos_kling, bạn vẫn nên biết obj_scales tồn tại vì nó giải thích vì sao skip_step2 an toàn hay không.

Bước 3: Render scene, camera và depth

Step 3 là phần Blender render thật cho video foundation model và reconstruction sau này:

rendering:
  samples: 32
  width: 1280
  height: 720
  num_rand_scenes: 3
  gpu: true
  no_rand_seed: false
  render_start_end: false

rendering.num_rand_scenes: 3 tạo ba biến thể random scene, thường có tên seed như rand00001, rand00002, rand00003. Mỗi biến thể có camera/lighting khác nhau nhưng vẫn bám scene/object/character. Đây là cách rẻ để tăng đa dạng video: cùng cordless_drill, bạn có ba frame điều kiện khác nhau trước khi gọi Kling.

Output quan trọng:

results/generation/asset_renders/
results/generation/cameras/
results/generation/depth_maps/

asset_renders chứa PNG scene đã render. Đây là thứ bạn mở đầu tiên để debug: object có nằm đúng vị trí không, character có nhìn về phía object không, camera có thấy toàn thân không, object có đủ lớn để Kling hiểu không. Nếu ảnh render đã sai, đừng gọi video model; sửa scene/scale trước.

cameras chứa camera parameters, thường ở dạng pickle. Đây là dữ liệu hình học cho reconstruction, không phải file trang trí. GRAIL có lợi thế vì biết camera trước khi sinh video; bài 3 sẽ dùng ý tưởng này để giảm ambiguity khi khôi phục 4D HOI.

depth_maps chứa depth ground truth từ scene Blender. Với video tự do trên internet, depth thường phải ước lượng từ ảnh. Với GRAIL, depth ban đầu đến từ scene đã biết, giúp pipeline có anchor metric tốt hơn.

Một checklist trước khi sang Kling:

  • PNG trong asset_renders rõ object và người.
  • Object không xuyên bàn/sàn.
  • Character không bị crop đầu, chân hoặc tay.
  • Camera đủ tĩnh và đủ xa để video không mất target.
  • Depth/camera file có số lượng khớp với render.
  • Nếu num_rand_scenes tăng từ 3 lên 10, bạn hiểu chi phí Kling cũng tăng theo.

Bước 4: Prompt refinement và Kling video

Step 4 lấy ảnh render làm input image, chọn một base prompt, tùy chọn refine prompt bằng OpenAI vision/chat, rồi gọi Kling image-to-video. Trong manipulation.yaml:

video:
  num_videos: 1
  num_video_segments: 1
  model_api: kling-ai
  kling_model_name: kling-v2-5-turbo
  duration: "5"
  kling_mode: pro
  skip_prompt_refinement: false
  base_prompt:
    - The person interacts with the object. The camera should remain static.
    - The person moves the object (pull or push). The camera should remain static.
  video_max_retries: 100
  video_retry_wait: 30

video.kling_model_name là model cụ thể được truyền xuống adapter Kling. Nếu Kling API đổi model hoặc account của bạn chỉ có quyền dùng model khác, đây là nơi cần sửa. video_retry_wait: 30 nghĩa là mỗi lần chưa có video hợp lệ, pipeline chờ 30 giây trước khi retry. Với video_max_retries: 100, một render xấu hoặc API queue nghẽn có thể làm job chờ rất lâu. Khi debug, hãy giữ num_rand_scenes nhỏ và num_videos: 1.

Prompt refinement có hai mục tiêu. Thứ nhất, prompt phải cụ thể với object trong ảnh, ví dụ "a person picks up a cordless drill and keeps holding it" thay vì "interacts with object". Thứ hai, prompt phải nhắc camera static. Tài liệu GRAIL dùng camera tĩnh vì reconstruction sau đó cần nhất quán hình học; camera bay lung tung có thể làm HOI đẹp mắt hơn nhưng phá 4D recovery.

Output video nằm trong:

results/generation/videos_kling/

Với smoke test, bạn kỳ vọng thấy .mp4 dưới nhánh dataset/category, ví dụ:

results/
  generation/
    videos_kling/
      ComAsset/
        cordless_drill/
          kid_indoor2-manipulation_rand00001.mp4
          kid_indoor2-manipulation_rand00002.mp4
          kid_indoor2-manipulation_rand00003.mp4

Tên file thực tế có thể khác một chút tùy scene key và số video, nhưng pattern chính là character, scene và random seed. Nếu videos_kling trống, kiểm tra theo thứ tự: có PNG trong asset_renders chưa, OPENAI_API_KEY có thiếu không, KLING_ACCESS_KEY/KLING_SECRET_KEY có nạp từ .env chưa, và log có báo retry hay refusal prompt không.

Smoke test ComAsset/cordless_drill

Một smoke test tốt nên rẻ, dễ nhìn lỗi và không đòi chỉnh nhiều config. cordless_drill phù hợp vì nó là object cầm tay, bất đối xứng, có scale sẵn trong comasset.yaml, scene mặc định là indoor2-manipulation, và video prompt "người tương tác/di chuyển object" có khả năng sinh hành động dễ hiểu.

Chạy đầy đủ:

source .env
python -m grail.pipelines.gen_2dhoi \
  --dataset ComAsset \
  --category cordless_drill \
  --character kid \
  --results_dir results \
  --video_model_api kling-ai

Nếu đây là lần đầu trong container, hãy kiểm tra Blender trước. Code GRAIL tìm Blender tại imports/blender/blender; nếu thiếu, lỗi sẽ nhắc chạy bash scripts/setup/install_env_docker.sh. Đây là lỗi setup, không phải lỗi object.

Sau khi chạy, kiểm tra theo thứ tự:

find results/generation/initial_states -maxdepth 3 -type f | head
find results/generation/asset_renders -maxdepth 4 -name '*.png' | head
find results/generation/cameras -maxdepth 4 -type f | head
find results/generation/depth_maps -maxdepth 4 -type f | head
find results/generation/videos_kling -maxdepth 4 -name '*.mp4' | head

Không cần mở ngay tất cả video. Hãy mở render trước. Nếu render sai, video Kling chỉ khuếch đại lỗi. Nếu render đúng nhưng video sai, lúc đó mới chỉnh prompt, model name, duration hoặc thử seed khác.

Khi nào tăng num_rand_scenes?

num_rand_scenes là núm tăng diversity nhưng cũng tăng chi phí. Với 3, bạn có ba camera/lighting variant cho một object. Với 10, bạn có nhiều cơ hội hơn để Kling sinh video tốt, nhưng cũng nhân số request video. Nếu mỗi video có thể retry 100 lần, tăng từ 3 lên 10 không phải thay đổi nhỏ.

Một chiến lược thực tế:

Giai đoạn num_rand_scenes Lý do
Setup smoke test 1 hoặc 3 Tìm lỗi Blender/API nhanh
Kiểm tra object mới 3 Đủ xem scale/camera có ổn định không
Tạo batch nhỏ 5 Cân bằng đa dạng và chi phí
Tạo dataset thật 10+ Chỉ sau khi prompt, scene và skip cache đã ổn

Bạn có thể override config bằng CLI nếu parser hỗ trợ tham số tương ứng, nhưng cách ít gây nhầm lẫn cho beginner là copy config sang file riêng, đổi rendering.num_rand_scenes, rồi chạy bằng --config.

Chuyển sang stairs, sitting và pickup

Khi smoke test manipulation đã chạy, bạn có thể đổi config:

# Terrain stairs
python -m grail.pipelines.gen_2dhoi \
  --config configs/gen_2dhoi/terrain_stairs.yaml \
  --results_dir results

# Sitting
python -m grail.pipelines.gen_2dhoi \
  --config configs/gen_2dhoi/sitting.yaml \
  --results_dir results

# Pickup from table
python -m grail.pipelines.gen_2dhoi \
  --config configs/gen_2dhoi/pickup_table.yaml \
  --results_dir results

# Pickup from ground
python -m grail.pipelines.gen_2dhoi \
  --config configs/gen_2dhoi/pickup_ground.yaml \
  --results_dir results

Các config này không chỉ khác prompt. terrain_stairs.yaml dùng object config cho synthetic stairs, skip_step1: true, skip_step2: true, num_rand_scenes: 10, duration "10"kling_model_name: kling-v3. Lý do là terrain không cần thả Bullet như object cầm tay; cầu thang đã là scene geometry ổn định. Prompt cũng cẩn thận hơn: người phải leo chậm, luôn ở trong frame, không đi xuyên trần hoặc biến khỏi cầu thang. Chi tiết này nghe như prompt engineering, nhưng thực ra là ràng buộc dữ liệu cho reconstruction và locomotion.

sitting.yaml dùng configs/objects/chair_sitting.yaml, samples: 128, num_rand_scenes: 10, no_rand_seed: true, prompt "walk over and sit down on the chair naturally". Sitting khác manipulation vì contact cuối là thân người với ghế, không chỉ tay với object. Bạn cần render rõ seat/back/leg và đảm bảo character tiếp cận đúng hướng.

Pickup configs tách tabletop và ground-level pickup. Pickup table thường gần manipulation nhưng mục tiêu action rõ hơn: tiếp cận, grasp, nâng object. Pickup ground khó hơn vì người phải cúi hoặc squat, nên nếu render crop chân hoặc object quá nhỏ, video dễ hỏng.

Debug theo output folder

Khi pipeline lỗi, đừng debug từ cuối về đầu bằng cảm tính. Hãy đọc folder theo thứ tự stage:

Folder Nếu thiếu hoặc sai Nguyên nhân hay gặp
initial_states Không có state, object pose kỳ lạ Blender setup lỗi, skip_step1 sai, mesh/collision xấu
asset_renders Không có PNG hoặc PNG sai scene Object config sai, scene key sai, scale sai, camera crop
cameras Thiếu camera pickle Render step chưa hoàn tất hoặc bị skip nhầm
depth_maps Thiếu depth Blender render_scene lỗi hoặc path output sai
videos_kling Không có MP4 Kling credentials, prompt refinement, API retry, render input không hợp lệ

Một lỗi phổ biến là chạy lại với --skip_done nhưng quên rằng output cũ sai. skip_done chỉ kiểm tra file tồn tại; nó không biết render/video đó có chất lượng tốt hay không. Khi bạn sửa scale hoặc prompt, hãy dùng results dir mới hoặc xóa đúng output lỗi bằng tay trước khi chạy lại. Với yêu cầu production, nên lưu log seed/config cùng output để truy vết.

GRAIL cần video 2D để làm gì?

Nếu đã có scene 3D, vì sao không trực tiếp optimize motion trong simulator? Câu trả lời là VFM mang prior về chuyển động người: cách người tiếp cận object, đặt tay, nâng, đẩy, ngồi, leo cầu thang. GRAIL không tin video một cách mù quáng; nó dùng video như nguồn motion prior rồi ràng buộc lại bằng geometry đã biết. Đây là lý do stage 2D HOI vừa phải "đẹp" để VFM tạo hành động tự nhiên, vừa phải "kỷ luật" để reconstruction sau đó còn recover được metric motion.

Trong bài 4, nhánh terrain sẽ dùng cùng triết lý này cho locomotion qua curb/slope/stairs. Trong bài 5, motion người/object được retarget sang Unitree G1, lúc đó lỗi scale/camera từ stage này sẽ trở thành lỗi chân tay robot. Vì vậy bài 2 là chỗ bạn nên làm chậm lại: kiểm tra render, kiểm tra prompt, chạy ít scene trước, rồi mới fan-out.

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á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