Spaces:
Running on Zero
Running on Zero
File size: 3,637 Bytes
4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c f8dc0df 4cc0d6c 4417ac0 4cc0d6c f8dc0df 4cc0d6c f8dc0df 4cc0d6c 4417ac0 4cc0d6c 7980741 4cc0d6c f8dc0df 4cc0d6c f8dc0df 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | """
Gradio Demo: Human → G1 Robot Motion Retargeting
上传 AMASS 格式 NPZ 文件,推理并展示 3D 机器人动画,导出 bmimic 格式。
"""
import os
import sys
import tempfile
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import torch
# PyTorch 2.6 兼容 patch
_torch_load = torch.load
torch.load = lambda *args, **kwargs: _torch_load(*args, weights_only=kwargs.pop('weights_only', False), **kwargs)
import numpy as np
import gradio as gr
import spaces
from inference import load_all, infer_single
from visualize import create_skeleton_animation, XML_PATH
from convert_bmimic import convert_to_bmimic
# ---------- 启动时加载模型(CPU)----------
print("Loading model...")
model, smplx_model, betas, smplx_mean, smplx_std, g1_mean, g1_std, device = load_all()
print(f"Model loaded on {device}")
@spaces.GPU
def predict(input_file):
"""推理、可视化、导出 bmimic 格式。"""
if input_file is None:
return None, None, "Please upload an AMASS NPZ file."
file_path = input_file if isinstance(input_file, str) else input_file.name
if not file_path.endswith('.npz'):
return None, None, "Error: only .npz files are supported."
# ZeroGPU: 在 @spaces.GPU 函数内才有 GPU,需要将模型移到 CUDA
gpu_device = torch.device('cuda')
model.to(gpu_device)
smplx_model.to(gpu_device)
result, timing = infer_single(
file_path, model, smplx_model, betas,
smplx_mean, smplx_std, g1_mean, g1_std,
gpu_device, apply_filter=True,
)
if result is None:
return None, None, "Error: sequence too short (< 4 frames)."
# 转换为 bmimic npz 格式 (30fps → 50fps)
bmimic_data = convert_to_bmimic(result, XML_PATH, device, tgt_fps=50.0, src_fps=30.0)
output_path = os.path.join(tempfile.gettempdir(), 'g1_motion.npz')
np.savez(output_path, **bmimic_data)
# 渲染 3D 机器人动画视频
video_path = create_skeleton_animation(
result['dof'], result['root_rot_quat'], result['root_trans'],
)
T = result['dof'].shape[0]
T_bmimic = bmimic_data['joint_pos'].shape[0]
info = (
f"Frames: {T} ({T / 30:.1f}s @ 30 FPS) → bmimic: {T_bmimic} frames @ 50 FPS\n"
f"Preprocess: {timing['preprocess']:.2f}s | "
f"Inference: {timing['infer']:.2f}s | "
f"Postprocess: {timing['postprocess']:.2f}s | "
f"Total: {timing['total']:.2f}s\n"
f"Output keys: fps, joint_pos, joint_vel, body_pos_w, body_quat_w, "
f"body_lin_vel_w, body_ang_vel_w"
)
return video_path, output_path, info
# ---------- Gradio UI ----------
demo = gr.Interface(
fn=predict,
inputs=gr.File(label="Upload AMASS NPZ file", file_types=[".npz"]),
outputs=[
gr.Video(label="G1 Robot Animation"),
gr.File(label="Download Result (bmimic NPZ)"),
gr.Textbox(label="Info", lines=3),
],
title="Human → G1 Robot Motion Retargeting",
description=(
"Upload human motion capture data in AMASS format (.npz), "
"and get Unitree G1 humanoid robot motion in BeyondMimic format.\n\n"
"**Input**: AMASS NPZ with fields `trans/root_orient/pose_body` "
"(or `transl/global_orient/body_pose`), optionally `mocap_frame_rate`.\n\n"
"**Output**: bmimic NPZ (50 FPS) with `joint_pos`, `joint_vel`, "
"`body_pos_w`, `body_quat_w`, `body_lin_vel_w`, `body_ang_vel_w`."
),
examples=[["examples/sample_motion.npz"]] if os.path.exists("examples/sample_motion.npz") else None,
cache_examples=False,
)
if __name__ == '__main__':
demo.launch()
|