HY-Motion动作导出规范:SMPLH骨骼映射实操指南
1. 为什么需要这份指南?
你刚用HY-Motion生成了一段惊艳的3D动作,点击“导出”按钮后却卡在了FBX或BVH文件里——角色扭曲、关节错位、动画一播放就崩?别急,这不是模型的问题,而是动作数据和你的3D软件之间缺了一张准确的“翻译表”。
HY-Motion输出的是标准SMPLH参数空间下的骨骼运动序列,它不直接对应Maya的Joint、Blender的Armature,也不等于Unity的Humanoid Avatar。它像一份用德语写就的精密乐谱,而你的动画管线是中文指挥家——中间必须有本可靠的《德-中动作术语对照手册》。
这份指南不讲原理推导,不堆数学公式,只聚焦一件事:让你今天下午就能把HY-Motion生成的动作,干净、稳定、无变形地导入到主流3D工具中,并驱动真实角色。我们会手把手带你完成SMPLH骨骼到通用动画骨架的映射、坐标系对齐、旋转归一化、关键帧重采样等所有实操环节,每一步都附可验证的代码片段和避坑提示。
2. 理解HY-Motion输出的本质
2.1 它不是“动画文件”,而是一组骨骼参数快照
HY-Motion生成的不是传统意义上的FBX或BVH,而是一个.npz文件(或内存中的NumPy数组),里面包含三类核心数据:
poses: 形状为(T, 156)的数组 —— 每帧T对应24个SMPLH关节的全局旋转矩阵(展平为156维向量)trans: 形状为(T, 3)的数组 —— 每帧的角色根节点平移向量(世界坐标系下)betas: 形状为(16,)的数组 —— 体型参数(本指南聚焦动作导出,体型参数通常固定为0)
关键认知:
poses里的每个关节旋转,是相对于其父关节的局部旋转,且采用轴角(axis-angle)表示法,不是欧拉角,也不是四元数。直接塞进Blender的Rotation属性框会彻底错乱。
2.2 SMPLH骨架长什么样?和你的软件差在哪?
SMPLH是SMPL的增强版,增加了手部关节(共52个关节点),但HY-Motion仅使用其中24个主干关节(从root到head,不含手指与脚趾)。它的标准拓扑结构如下:
root (0) → pelvis (1) → spine1 (2) → spine2 (3) → spine3 (4) → neck (5) → head (6) ↘ left_hip (7) → left_knee (8) → left_ankle (9) → left_foot (10) ↘ right_hip (11) → right_knee (12) → right_ankle (13) → right_foot (14) ↘ left_collar (15) → left_shoulder (16) → left_elbow (17) → left_wrist (18) ↘ right_collar (19) → right_shoulder (20) → right_elbow (21) → right_wrist (22)而你的Maya/Blender/Unity角色骨架,大概率是这样命名的:
| 软件 | 常见关节名 | 对应SMPLH索引 |
|---|---|---|
| Maya | Hips,Spine | 0,1,2... |
| Blender | hips,spine | 0,1,2... |
| Unity | Hips,Spine | 0,1,2... |
问题来了:名字相似 ≠ 坐标系一致 ≠ 旋转方向相同。SMPLH使用Y-up右手坐标系,而Unity默认Y-up但部分插件用Z-up;Blender默认Z-up;Maya默认Y-up但旋转顺序是XYZ而非SMPLH的ZYX。这些细微差异,就是动作导入后“拧巴”的根源。
3. 实操:从.npz到可用FBX的四步流程
我们以Blender 4.2 + Python API为例(Maya/Unity逻辑完全一致,仅API调用不同),演示完整导出链路。所有代码均可直接运行。
3.1 步骤一:加载并解析HY-Motion输出
import numpy as np import bpy from mathutils import Matrix, Vector, Quaternion # 加载HY-Motion生成的.npz文件 data = np.load("/path/to/output.npz") poses = data["poses"] # (T, 156) trans = data["trans"] # (T, 3) T = poses.shape[0] # 将156维轴角向量转为24个3x3旋转矩阵 def axis_angle_to_matrix(axis_angle): """将轴角向量(3,)转为3x3旋转矩阵""" theta = np.linalg.norm(axis_angle) if theta < 1e-8: return np.eye(3) axis = axis_angle / theta # 罗德里格斯公式 K = np.array([[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]], [-axis[1], axis[0], 0]]) return np.eye(3) + np.sin(theta)*K + (1-np.cos(theta))*(K @ K) # 解析每帧的24个关节旋转矩阵 rot_matrices = [] for t in range(T): frame_rots = [] for j in range(24): # 仅取前24个关节(SMPLH主干) start_idx = j * 3 aa_vec = poses[t, start_idx:start_idx+3] R = axis_angle_to_matrix(aa_vec) frame_rots.append(R) rot_matrices.append(frame_rots)3.2 步骤二:构建SMPLH到Blender骨架的映射字典
# SMPLH关节索引 → Blender Armature中Bone名称的映射 smplh_to_blender = { 0: "hips", # root → hips 1: "spine", # pelvis → spine (注意:SMPLH的pelvis是root子节点,Blender常将spine设为pelvis子节点) 2: "spine.001", 3: "spine.002", 4: "spine.003", 5: "neck", 6: "head", 7: "thigh.L", 8: "shin.L", 9: "foot.L", 10: "toe.L", 11: "thigh.R", 12: "shin.R", 13: "foot.R", 14: "toe.R", 15: "shoulder.L", 16: "upper_arm.L", 17: "forearm.L", 18: "hand.L", 19: "shoulder.R", 20: "upper_arm.R", 21: "forearm.R", 22: "hand.R", } # 关键:坐标系转换矩阵(SMPLH Y-up → Blender Z-up) # 绕X轴旋转-90度:[1,0,0; 0,0,1; 0,-1,0] yup_to_zup = np.array([ [1, 0, 0], [0, 0, 1], [0, -1, 0] ]) # 应用坐标系转换到所有旋转矩阵 for t in range(T): for j in range(24): R_smplh = rot_matrices[t][j] R_zup = yup_to_zup @ R_smplh @ yup_to_zup.T # 双边变换 rot_matrices[t][j] = R_zup3.3 步骤三:在Blender中驱动骨架(核心!)
# 获取当前场景中的Armature对象 armature_obj = bpy.data.objects["Armature"] # 替换为你的骨架名 bpy.context.view_layer.objects.active = armature_obj bpy.ops.object.mode_set(mode='POSE') # 遍历每一帧 for t in range(T): bpy.context.scene.frame_set(t + 1) # Blender帧从1开始 # 设置根节点位置(应用坐标系转换) pos_world = trans[t] # SMPLH的trans是Y-up pos_zup = np.array([pos_world[0], pos_world[2], -pos_world[1]]) # Y-up → Z-up armature_obj.location = Vector(pos_zup) armature_obj.keyframe_insert(data_path="location", frame=t+1) # 驱动每个关节 for smplh_idx, bone_name in smplh_to_blender.items(): if bone_name not in armature_obj.pose.bones: continue bone = armature_obj.pose.bones[bone_name] R_local = rot_matrices[t][smplh_idx] # 将3x3矩阵转为Blender可用的Quaternion(自动处理万向节锁) quat = Quaternion() quat[:] = R_local.flatten() # Blender接受展平的9元素数组 # 关键:设置rotation_quaternion(不是rotation_euler!) bone.rotation_mode = 'QUATERNION' bone.rotation_quaternion = quat bone.keyframe_insert(data_path="rotation_quaternion", frame=t+1) # 切回物体模式 bpy.ops.object.mode_set(mode='OBJECT') print(f" 已成功将{ T }帧动作导入Blender骨架!")3.4 步骤四:导出为FBX并验证
在Blender中:
- 选中骨架 →
File > Export > FBX (.fbx) - 关键设置:
Primary Bone Axis:YSecondary Bone Axis:XAdd Leaf Bones:False(避免多余末端骨)Bake Animation:TrueNLA Strips:False
- 导出后,用FBX Review打开,检查动作是否自然流畅。
验证技巧:导入FBX到Unity时,若角色“跪着”或“倒立”,说明坐标系转换漏了根节点平移;若手臂“反向旋转”,说明局部旋转矩阵未正确应用坐标系变换。
4. 常见问题与硬核解决方案
4.1 问题:动作看起来“僵硬”或“抽搐”
原因:HY-Motion输出帧率通常为20fps,而你的目标引擎要求30fps或60fps,直接线性插值会破坏动作动力学。
方案:使用贝塞尔曲线重采样(非线性)
from scipy.interpolate import CubicSpline # 假设原帧数T=100,目标帧数T_new=300 t_old = np.linspace(0, 1, T) t_new = np.linspace(0, 1, 300) # 对每个关节的每个旋转分量(如quat.x, quat.y...)单独插值 for j in range(24): for comp in range(4): # 四元数4个分量 comp_values = np.array([rot_quats[t][j][comp] for t in range(T)]) spline = CubicSpline(t_old, comp_values, bc_type='periodic') new_values = spline(t_new) # 存入新数组...4.2 问题:手指/面部动作缺失
原因:HY-Motion-1.0当前不生成手部精细动作(SMPLH的24关节不含手指),poses中手指关节(索引23+)恒为零。
方案:用IK/FK混合驱动
- 导出主体动作后,在Blender中为手部添加IK约束;
- 将
right_wrist(22)作为IK目标,手动调整手指姿态; - 或接入OpenPose实时手部关键点,驱动手指骨骼。
4.3 问题:多角色动作无法同步
原因:HY-Motion每次只生成单人动作,trans是相对世界原点的位移,多角色需手动分配起始位置。
方案:预设角色偏移矩阵
# 为角色A、B、C定义初始偏移 offsets = { "A": np.array([0, 0, 0]), "B": np.array([2, 0, 0]), # B在A右侧2米 "C": np.array([0, 0, 2]) # C在A前方2米 } # 导出时,对每个角色的trans加上其offset for char_id, offset in offsets.items(): trans_char = trans + offset # 广播加法 # 后续同上...5. 总结:让动作真正“活”起来的三个关键
1. 映射要准,不是“差不多”
SMPLH的spine1绝不能草率映射到Blender的spine——必须确认该Bone在层级树中的父节点是hips,且其本地坐标系与SMPLH的pelvis→spine1向量方向一致。一个关节映射错误,会导致整条肢体运动失真。
2. 坐标系转换是“全链路”工程
它不只是旋转矩阵的变换,还包括:
- 根节点平移向量的坐标系转换(
trans) - 所有关节旋转矩阵的坐标系适配(
poses) - 导出FBX时的轴向设置(
Primary/Secondary Bone Axis)
三者缺一不可,漏掉任一环,动作就会“漂移”或“翻转”。
3. 导出不是终点,而是动画管线的起点
HY-Motion给你的是高保真动作“毛坯”。真正让它服务于生产,还需:
- 在Blender/Maya中做FK/IK修正(尤其手部、足部接地)
- 用Motion Matching技术做动作过渡衔接
- 结合物理模拟(如NVIDIA PhysX)增加布料、头发动态
这并非HY-Motion的缺陷,而是专业动画制作的必然流程。它解放了你最耗时的“动作设计”环节,把创造力还给了动画师。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。