Spaces:
Running on Zero
Running on Zero
File size: 2,813 Bytes
4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 4cc0d6c 4417ac0 | 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 | """
MuJoCo 离屏渲染:DOF + root pose → 带 mesh 的机器人动画视频 (MP4)。
"""
import os
# 设置 MuJoCo 离屏渲染后端 (必须在 import mujoco 之前)
# EGL 用于有 GPU 的环境, osmesa 用于纯 CPU 环境 (如 HF Spaces)
if 'MUJOCO_GL' not in os.environ:
try:
import ctypes
ctypes.cdll.LoadLibrary('libEGL.so.1')
os.environ['MUJOCO_GL'] = 'egl'
except OSError:
os.environ['MUJOCO_GL'] = 'osmesa'
import tempfile
import numpy as np
import mujoco as mj
import imageio
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
XML_PATH = os.path.join(BASE_DIR, 'assets', 'g1_mocap_29dof.xml')
# inference 输出 DOF 顺序 → MuJoCo XML joint 顺序的映射
JOINT_MAPPING = [
0, 6, 12,
1, 7, 13,
2, 8, 14,
3, 9, 15, 22,
4, 10, 16, 23,
5, 11, 17, 24,
18, 25,
19, 26,
20, 27,
21, 28,
]
def create_skeleton_animation(dof, rot_quat, transl, fps=30,
width=1920, height=1088,
camera_distance=2.5, camera_elevation=-15):
"""渲染 G1 机器人 mesh 动画并输出 MP4 视频。
Args:
dof: (T, 29) 关节角度
rot_quat: (T, 4) wxyz 四元数
transl: (T, 3) 根位置
fps: 输出视频帧率
width, height: 渲染分辨率
camera_distance: 摄像机距离
camera_elevation: 摄像机俯仰角 (度)
Returns:
str: MP4 视频文件路径
"""
model = mj.MjModel.from_xml_path(XML_PATH)
# 确保离屏帧缓冲区足够大
model.vis.global_.offwidth = max(model.vis.global_.offwidth, width)
model.vis.global_.offheight = max(model.vis.global_.offheight, height)
data = mj.MjData(model)
renderer = mj.Renderer(model, height=height, width=width)
# 摄像机设置
camera = mj.MjvCamera()
camera.distance = camera_distance
camera.elevation = camera_elevation
camera.azimuth = 90
pelvis_id = model.body('pelvis').id
T = dof.shape[0]
# 映射 DOF 顺序
mapped_dof = np.zeros((T, model.nq - 7), dtype=np.float32)
for i, jm in enumerate(JOINT_MAPPING):
mapped_dof[:, jm] = dof[:, i]
# 渲染到视频
video_path = os.path.join(tempfile.gettempdir(), 'g1_motion.mp4')
writer = imageio.get_writer(video_path, fps=fps, codec='libx264')
for t in range(T):
data.qpos[:3] = transl[t]
data.qpos[3:7] = rot_quat[t] # wxyz, MuJoCo 原生格式
data.qpos[7:] = mapped_dof[t]
mj.mj_forward(model, data)
# 摄像机跟踪 pelvis
camera.lookat[:] = data.xpos[pelvis_id]
renderer.update_scene(data, camera=camera)
img = renderer.render()
writer.append_data(img)
writer.close()
renderer.close()
return video_path
|