Robot hình người học từ video của người nghe có vẻ tự nhiên: con người đi vào phòng, nhìn đồ vật, nhặt đồ, đặt đồ, mở cửa, kéo xe, dọn bàn. Nếu robot nhìn thấy đủ nhiều ví dụ như vậy, ta hy vọng nó học được hành vi cấp cao mà không cần bắt robot thật làm từng động tác hàng nghìn lần. Nhưng có một vấn đề rất cụ thể: mắt người và mắt robot không ở cùng một nơi.
Người trưởng thành thường có camera đeo ở đầu hoặc ngực cao hơn camera của nhiều humanoid robot. Cơ thể người che khung hình theo cách khác với vỏ kim loại và bàn tay robot. Khi người cúi xuống, đầu người xoay, rung, lệch theo sinh học; khi robot di chuyển, camera đi theo cơ cấu cơ khí, độ cao và hướng nhìn khác. Nếu lấy video egocentric của người đưa thẳng vào policy robot, model có thể học nhầm: cái bàn trông cao hơn, đồ vật nằm ở vị trí ảnh khác, vùng nền phía sau thay đổi, và khoảng cách tay-đồ vật không còn giống lúc robot thật thực thi.
Bài 1 của series, bản đồ sở hữu dữ liệu robot hình người, đã đặt câu hỏi "ai sở hữu dữ liệu?" ở tầng chiến lược. Bài 2, VR teleoperation và dữ liệu thao tác, đi vào dữ liệu do con người điều khiển robot trực tiếp. Bài này đi vào tầng kỹ thuật khó hơn: khi một video của người được biến đổi bằng depth warping và inpainting để trông giống camera robot, nó còn là "video của người" nữa không, hay đã trở thành một tài sản dữ liệu phái sinh?
Anchor kỹ thuật của bài là script trong EgoHumanoid:
cd data_alignment/view_alignment
python viewport_transform_batch_h5.py \
--h5_dir /path/to/h5_directory \
--image_key observation_image_left \
--trajectory down \
--movement_distance 0.07 \
--batch_size 32 \
--num_gpus 4 \
--output_dir /path/to/output
Sau bài này, bạn nên có thể giải thích được bốn ý: vì sao cần view alignment, depth warping làm gì với từng pixel, inpainting lấp vùng thiếu như thế nào, và tại sao frame đã biến đổi làm quyền sở hữu dữ liệu trở nên rối hơn rất nhiều.
Vấn đề: cùng một hành động, hai camera khác nhau
Hãy tưởng tượng một người đeo camera egocentric và nhặt một món đồ chơi trên bàn. Trong video của người, camera có thể nằm ở độ cao mắt người, nhìn từ trên xuống. Tay người có da, ngón tay mềm, cổ tay linh hoạt. Khi người tiến lại gần bàn, góc nhìn có cảm giác "cao", thấy nhiều mặt bàn hơn, thấy đồ vật từ trên xuống nhiều hơn.
Robot humanoid như Unitree G1 có camera đặt theo thiết kế đầu robot. Camera này có thể thấp hơn, lệch hơn, và đi theo quỹ đạo thân robot khác. Khi robot nhìn cùng cái bàn, nó thấy cạnh bàn ở vị trí khác, đồ vật có thể lớn hơn hoặc nhỏ hơn trên ảnh, và vùng phía sau đồ vật có thể lộ ra hoặc bị che theo cách khác.
Trong học robot, đây không chỉ là khác biệt thẩm mỹ. Policy thường nhận ảnh RGB làm observation, sau đó dự đoán hành động. Nếu observation ở train khác observation ở deploy, policy bị domain gap. Với manipulation, vài chục pixel lệch trên ảnh có thể làm grasp point sai. Với loco-manipulation, sai góc nhìn còn ảnh hưởng cách robot tiếp cận bàn, né vật cản, và chọn tư thế đứng.
EgoHumanoid gọi phần này là view alignment: biến quan sát egocentric của người thành quan sát xấp xỉ góc nhìn robot. Paper mô tả pipeline gồm ước lượng depth từ ảnh đơn, chiếu lại điểm 3D sang camera target, rồi dùng inpainting để lấp vùng trống. Repo triển khai công cụ đó trong data_alignment/view_alignment/viewport_transform_batch_h5.py.
Điểm quan trọng cho beginner: ta không "crop" hay "resize" đơn giản. Crop chỉ cắt ảnh 2D. View alignment cố dựng lại cấu trúc 3D thô của scene, di chuyển camera ảo, rồi render lại ảnh từ vị trí mới.
File HDF5 và observation_image_left là gì?
Trong nhiều pipeline robot learning, một episode được lưu thành file HDF5. HDF5 giống một container chứa nhiều mảng dữ liệu: ảnh camera, trạng thái robot, action, timestamp, lệnh ngôn ngữ, trạng thái tay, command locomotion. Thay vì lưu từng frame thành từng file JPEG rời rạc, HDF5 giúp giữ dữ liệu đồng bộ và dễ đọc theo batch.
Tham số:
--image_key observation_image_left
nói cho script biết ảnh RGB nằm ở key nào trong HDF5. Tên observation_image_left thường gợi ý đây là luồng ảnh camera trái hoặc ảnh chính trong cặp stereo/egocentric. Script EgoHumanoid có default cũng là key này. Khi đọc frame, script kiểm tra key có tồn tại không, lấy frame theo index, decode nếu dữ liệu đang nén JPEG, rồi đổi BGR sang RGB để xử lý bằng PyTorch và PIL.
Bảng sau tóm tắt các tham số quan trọng trong lệnh anchor:
| Tham số | Giá trị trong bài | Ý nghĩa thực tế |
|---|---|---|
--h5_dir |
/path/to/h5_directory |
Xử lý cả thư mục chứa nhiều episode .h5 hoặc .hdf5 |
--image_key |
observation_image_left |
Dataset ảnh nguồn trong mỗi file HDF5 |
--trajectory |
down |
Di chuyển camera ảo xuống dưới, phù hợp khi robot camera thấp hơn người |
--movement_distance |
0.07 |
Độ dịch chuyển danh nghĩa của camera ảo |
--batch_size |
32 |
Số frame xử lý mỗi batch |
--num_gpus |
4 |
Số GPU worker để chạy depth, warp, inpaint song song |
--output_dir |
/path/to/output |
Nơi lưu ảnh kết quả hoặc HDF5 mới nếu bật --save_h5 |
Ở chế độ thư mục, script gom các file HDF5, chia frame thành batch, đưa batch vào worker GPU. Mỗi worker tải MoGe model để dự đoán depth và Stable Diffusion Inpainting để lấp hole. Với --num_gpus 4, mục tiêu là tránh việc xử lý hàng nghìn frame nối tiếp trên một GPU duy nhất.
Pipeline ba bước: depth, warp, inpaint
Để hiểu view alignment, hãy đi từng bước theo luồng dữ liệu của một frame.
Bước 1: ước lượng depth từ một ảnh RGB
Đầu vào chỉ là ảnh RGB egocentric của người. Script dùng MoGe để dự đoán depth và intrinsics từ ảnh đơn. Nói đơn giản, model cố đoán mỗi pixel cách camera bao xa và camera có tiêu cự/điểm chính như thế nào. Kết quả không phải depth metric hoàn hảo như LiDAR, nhưng đủ để dựng một point map tương đối.
Trong script, ảnh được resize về kích thước thuận lợi cho MoGe, sau đó chuyển thành tensor [C, H, W]. Model trả về:
output = moge_model.infer(image_tensor)
depth = output["depth"]
intrinsics = output["intrinsics"]
mask = output.get("mask")
Nếu có pixel không hợp lệ theo mask, script đẩy depth của pixel đó về giá trị rất xa để tránh phá phép render. Sau đó intrinsics được scale lại về kích thước ảnh gốc.
Với beginner, có thể hình dung depth map là một bản đồ nổi: pixel của cái cốc gần camera có depth nhỏ, pixel của bức tường phía sau có depth lớn. Khi biết depth và intrinsics, ta có thể nâng mỗi pixel 2D thành một điểm 3D tương đối trong không gian camera.
Bước 2: warp ảnh bằng camera ảo
Sau khi có điểm 3D, script tạo một camera ban đầu và một camera target. Tham số:
--trajectory down
--movement_distance 0.07
nói rằng camera target sẽ dịch xuống dưới một khoảng danh nghĩa 0.07. Repo còn có --movement_distance_noise, default 0.02, để thêm nhiễu độc lập mỗi sample. Vì vậy khoảng dịch thực tế từng frame có thể hơi khác nhau. Điều này giúp policy không quá phụ thuộc vào đúng một độ cao camera duy nhất.
Phần warp gọi generate_camera_trajectory() để sinh pose camera mới và dùng Cache3D_Buffer để render point cloud sang viewpoint target. Kết quả gồm:
warped_rgb, mask = warp_image_3d(
image_rgb,
depth,
intrinsics,
args.trajectory,
actual_movement_distance,
device,
)
warped_rgb là ảnh sau khi chiếu lại. mask cho biết pixel nào có dữ liệu thật từ ảnh nguồn và pixel nào là hole. Hole xuất hiện vì hai lý do chính:
| Nguyên nhân hole | Giải thích |
|---|---|
| Depth invalid | MoGe không tin vào depth của một số vùng, đặc biệt vùng phản chiếu, texture lặp, motion blur |
| Disocclusion | Khi camera ảo dịch xuống, nó nhìn thấy vùng trước đó bị che; ảnh gốc không có dữ liệu cho vùng mới này |
Đây là điểm rất quan trọng: khi đổi viewpoint, không thể chỉ kéo pixel 2D là xong. Camera mới có thể thấy mặt cạnh của đồ vật, chân bàn, nền phía sau, hoặc khoảng trống dưới mép bàn mà camera người ban đầu không thấy. Những vùng đó không tồn tại trong ảnh gốc, nên phải được dự đoán.
Bước 3: inpainting lấp vùng thiếu
Script dùng Stable Diffusion Inpainting nếu cài được diffusers; nếu không, fallback sang OpenCV inpainting. Với Stable Diffusion, mask bị đảo lại vì quy ước khác nhau: mask của warp dùng 255 = có nội dung, 0 = hole; inpainting model lại cần 255 = vùng cần lấp.
Pseudo-code:
inpaint_mask = 255 - mask
result = sd_pipeline(
prompt=prompt,
negative_prompt="blurry, low quality, distorted",
image=image_pil,
mask_image=mask_pil,
num_inference_steps=20,
guidance_scale=7.5,
)
Output cuối cùng là một ảnh RGB hoàn chỉnh, trông giống như robot nhìn scene từ góc thấp hơn. Paper EgoHumanoid cho biết các ảnh reprojected thường có vùng đen do disocclusion và depth invalid; inpainting lấp các vùng đó để tạo quan sát RGB hoàn chỉnh gần với input egocentric của robot.
Chạy single file và chạy thư mục khác nhau thế nào?
Nếu bạn chỉ muốn thử một episode:
python viewport_transform_batch_h5.py \
--h5_file /path/to/input.h5 \
--image_key observation_image_left \
--trajectory down \
--movement_distance 0.07 \
--output_dir ./output
Chế độ này phù hợp để debug. Script có thể lưu ảnh frame_000123_result.jpg và frame_000123_comparison.jpg. File comparison thường ghép ngang ba ảnh: original, warped, result. Đây là cách nhanh nhất để xem viewpoint shift có hợp lý không.
Khi đã kiểm tra ổn, bạn chuyển sang xử lý cả thư mục:
python viewport_transform_batch_h5.py \
--h5_dir /path/to/h5_directory \
--image_key observation_image_left \
--trajectory down \
--movement_distance 0.07 \
--batch_size 32 \
--num_gpus 4 \
--output_dir /path/to/output
Với dataset lớn, batch và multi-GPU không chỉ là tối ưu tốc độ. Nó còn quyết định khả năng vận hành pipeline. Depth model và diffusion model đều nặng. Nếu batch quá lớn, GPU hết VRAM. Nếu batch quá nhỏ, overhead load/dispatch cao. --batch_size 32 là một cấu hình hợp lý để bắt đầu, nhưng team thực tế vẫn phải đo theo GPU, resolution và số inference steps.
Nếu bật --save_h5, script có logic copy file HDF5 gốc rồi thay dataset ảnh tại image_key bằng ảnh đã alignment. Chi tiết này có hệ quả pháp lý và quản trị dữ liệu: file mới có thể giữ nguyên action, timestamp, metadata, nhưng ảnh observation đã được biến đổi bởi model khác.
Vì sao trajectory down đặc biệt quan trọng với humanoid?
Trong EgoHumanoid, mục tiêu là học loco-manipulation từ demo người và demo robot. Sự khác biệt độ cao camera là một domain gap lớn. Nếu camera người cao hơn camera robot, vật thể trên bàn xuất hiện theo góc từ trên xuống; robot thấp hơn sẽ thấy vật thể từ hướng ngang hơn. Vì vậy trajectory down tạo dịch chuyển nhất quán: đưa camera ảo của người xuống gần camera robot hơn.
Đừng hiểu down là một phép biến đổi tuyệt đối đúng cho mọi robot. Nó là một giả định hình học. Với robot khác, setup camera khác, hoặc người đeo camera ở ngực thay vì đầu, bạn có thể cần forward, up, hoặc distance khác. EgoHumanoid hỗ trợ các hướng left, right, up, down, forward, backward chính vì alignment không có một công thức duy nhất cho mọi embodiment.
Một cách kiểm tra thực tế:
| Dấu hiệu | Có thể ổn | Cần xem lại |
|---|---|---|
| Mép bàn dịch vị trí hợp lý | Có | Mép bàn bị bẻ cong hoặc trôi quá mạnh |
| Vật thể chính còn nhận ra được | Có | Vật thể bị biến dạng hoặc bị inpaint đè |
| Hole tập trung ở vùng mới lộ ra | Có | Hole phủ lên vùng thao tác chính |
| Result gần robot view hơn original | Có | Result chỉ là ảnh đẹp nhưng sai hình học |
Trong robot learning, "ảnh đẹp" chưa chắc là ảnh tốt. Ảnh tốt là ảnh làm policy học đúng tương quan giữa observation và action khi deploy trên robot thật.
Dữ liệu phái sinh: frame này thuộc về ai?
Bây giờ đến câu hỏi sở hữu dữ liệu. Một frame sau view alignment có ít nhất năm lớp đóng góp:
| Lớp | Ai có thể claim quyền/lợi ích? | Vì sao phức tạp |
|---|---|---|
| Video gốc của người | Người biểu diễn, công ty thu thập, chủ địa điểm | Có hình ảnh người, nhà riêng, đồ vật, môi trường thật |
| Metadata và action | Team thu thập dữ liệu | Timestamp, pose, hand status, command có giá trị huấn luyện |
| Depth ước lượng | Model MoGe và pipeline xử lý | Depth không phải sensor gốc, mà là suy luận từ model |
| Warped frame | Team chạy alignment | Viewpoint mới được tạo bằng phép biến đổi 3D |
| Inpainted pixels | Model diffusion, prompt, seed, pipeline | Một phần ảnh không tồn tại trong video gốc, được sinh mới |
Nếu policy được train trên frame phái sinh, giá trị thương mại đến từ toàn bộ chuỗi này, không chỉ từ video ban đầu. Một công ty có thể nói: "Chúng tôi sở hữu dataset alignment vì chúng tôi tạo ra pipeline và compute." Người biểu diễn có thể nói: "Không có hành động và môi trường của tôi thì không có frame nào để biến đổi." Nhà cung cấp model foundation có thể đặt điều kiện license lên output hoặc lên cách sử dụng model trong pipeline. Nếu video được quay trong nhà máy khách hàng, khách hàng có thể claim môi trường, quy trình thao tác, layout và bí mật vận hành.
Điểm khó là frame cuối cùng vừa giống dữ liệu gốc, vừa không còn là dữ liệu gốc. Nó giữ object, scene và hành vi của người, nhưng perspective và một số pixel đã bị sinh lại. Vì vậy, trong quản trị dữ liệu robot, ta không nên chỉ lưu "source = human video". Cần lưu provenance chi tiết:
source_episode: human_demo_00042.h5
source_image_key: observation_image_left
alignment_script: data_alignment/view_alignment/viewport_transform_batch_h5.py
trajectory: down
movement_distance: 0.07
movement_distance_noise: 0.02
batch_size: 32
num_gpus: 4
depth_model: Ruicheng/moge-vitl
inpaint_model: stabilityai/stable-diffusion-2-inpainting
prompt: ""
negative_prompt: "blurry, low quality, distorted"
seed_policy: seed + frame_index
output_type: derived_observation
Provenance này không giải quyết hết tranh chấp pháp lý, nhưng giúp team biết chính xác dữ liệu nào là raw, dữ liệu nào là transformed, và transformed bằng phiên bản pipeline nào.
Checklist cho team muốn dùng view alignment
Nếu bạn đang xây dataset humanoid nhỏ, đừng bắt đầu bằng câu hỏi "có nên inpaint không?" Hãy bắt đầu bằng câu hỏi "robot của tôi cần thấy gì để hành động đúng?"
- Xác định camera robot thật: độ cao, FOV, hướng nhìn, resolution, latency.
- Xác định camera người: đầu, ngực, kính AR/VR, ZED, điện thoại, hay camera cầm tay.
- Lấy một cặp scene giống nhau: người quay một task, robot đứng ở pose deploy và chụp ảnh cùng scene.
- Chạy alignment với vài
movement_distance: ví dụ0.03,0.07,0.10. - So sánh original, warped, result với ảnh robot thật, không chỉ bằng mắt mà theo vị trí object quan trọng.
- Kiểm tra vùng inpaint có đụng vào object thao tác không. Nếu model sinh lại cái cốc hoặc tay, dữ liệu có thể gây hại.
- Lưu provenance cho từng file output.
- Tách raw dataset và derived dataset trong storage, naming và license.
- Train ablation: robot-only, human raw, human aligned, human aligned + robot.
Ablation đặc biệt quan trọng. EgoHumanoid báo cáo view alignment giúp giảm mismatch viewpoint và cải thiện co-training, nhất là khi object height thay đổi. Nhưng dataset của bạn có thể khác. Nếu robot chỉ cần navigation corridor, alignment có thể ít quan trọng hơn. Nếu robot phải nhặt đồ trên kệ thấp, alignment có thể quyết định thành bại.
Liên hệ với bài tiếp theo
Bài này tập trung vào việc biến video người thành observation robot-compatible. Bài 4, dữ liệu mô phỏng và synthetic data, sẽ đi tiếp một bước: nếu frame có thể được sinh một phần bằng inpainting, liệu ta có thể sinh toàn bộ scene hoặc trajectory bằng simulation không? Bài 5, human video mining, sẽ quay lại nguồn dữ liệu web-scale: video người trên internet có thể đóng vai trò gì khi quyền riêng tư, license và embodiment gap cùng xuất hiện?
Nếu bạn muốn nối với các bài ngoài series, đọc thêm GROOT N1 và quy trình thu thập dữ liệu G1 để thấy một pipeline robot data thực tế, và teleoperation trong WholeBodyVLA để hiểu vì sao data từ operator vẫn là xương sống của nhiều hệ humanoid.
Kết luận
View alignment là một lớp "dịch thuật thị giác" giữa con người và robot. Nó không biến video người thành dữ liệu robot thật, nhưng làm giảm khoảng cách quan sát đủ để co-training có cơ hội hoạt động. Về kỹ thuật, pipeline gồm depth estimation, 3D reprojection, mask hole và inpainting. Về dữ liệu, pipeline tạo ra một loại tài sản mới: frame phái sinh có nguồn gốc từ người, được xử lý bởi model, và được tối ưu cho robot.
Trong cuộc đua dữ liệu humanoid năm 2026, người thắng không chỉ là người có nhiều video nhất. Người thắng là người biết biến raw video thành training signal có provenance rõ, license rõ, và tương thích với embodiment thật. EgoHumanoid cho ta một ví dụ rất cụ thể: chỉ một lệnh viewport_transform_batch_h5.py đã kéo theo cả hình học 3D, diffusion model, compute pipeline, và câu hỏi sở hữu dữ liệu.
Nguồn kỹ thuật
- EgoHumanoid GitHub repository
- EgoHumanoid paper on arXiv
- View alignment script:
viewport_transform_batch_h5.py - View alignment README