如何基于3D姿态估计数据驱动骨骼绑定3D角色的运动?
问题解答
1. 关节独立XYZ轴的作用
每个关节的独立xyz轴属于关节局部坐标系,作用是定义关节的旋转自由度基准:
- 符合人体/物体的生理运动逻辑,比如肘关节局部y轴对应屈伸方向,肩关节三个轴对应旋内/旋外、展/收、屈/伸三个自由度
- 正向运动学计算的基础:单个关节的旋转是先在自身局部坐标系下计算变换,再逐层乘父级关节的变换矩阵,最终得到该关节在全局空间的位姿
2. 姿态估计工具与3D角色的坐标系统一方法
不需要借助DCC工具,纯代码可完成对齐,步骤如下:
- 首先确定两边一致的基准姿态,优先选T-pose,其次选A-pose,确保两侧同一姿态下所有对应关节的位置/朝向可对齐
- 建立关节映射表,明确姿态估计输出的每个关节ID对应3D角色的哪个骨骼节点
- 采集基准姿态下,姿态估计输出的所有关节全局坐标点集P,以及3D角色所有对应关节的全局坐标点集Q,使用SVD算法求解两个点集之间的刚体变换矩阵
T(包含旋转、平移分量),该矩阵即为坐标系转换矩阵,可以直接解决z轴朝向不一致这类轴向定义差异 - 后续所有姿态估计输出的关节位姿,都先左乘
T转换到角色坐标系下再使用
3. 直接设置关节角度无法得到正确姿态的原因
通常是以下几个问题导致:
- 欧拉角旋转顺序不匹配:不同工具输出的欧拉角xyz旋转顺序不同,比如Azure Kinect默认输出ZYX顺序的欧拉角,若你设置时采用XYZ顺序,最终旋转结果会完全不同,推荐统一转换成四元数处理避免该问题
- 旋转参考空间不匹配:姿态估计输出的关节角度通常是相对于父关节的局部空间旋转,若你直接赋值到全局空间旋转,必然会出现姿态错位
- 未减去绑定姿态偏移:所有骨骼绑定的角色都存在默认的Rest Pose(绑定姿态),你需要将姿态估计输出的绝对角度转换为相对于绑定姿态的相对旋转偏移,再赋值给骨骼
- 关节局部朝向定义差异:即使轴向一致,不同工具的同方向轴可能指向相反,比如Kinect局部x轴朝右,部分角色的局部x轴朝左,需要额外计算每个关节的旋转偏移量提前修正
- 骨骼长度不匹配:若姿态估计源的骨骼长度与目标角色的骨骼长度差异较大,直接复用角度也会出现姿态错位,可通过先对齐两端关节位置反算旋转解决
4. 纯代码实现运动驱动的方案
- 优先加载GLB/GLTF格式的带绑定模型,该格式标准统一,避免自定义格式的解析问题
- 可选用的工具库:Python环境用
trimesh+pygltflib,Web环境用Three.js,客户端环境用Unity/Unreal Engine的原生API - 核心实现逻辑:
- 读取模型的骨骼层级树、每个骨骼在Rest Pose下的局部变换矩阵
- 预计算坐标系转换矩阵、每个关节的旋转偏移矩阵
- 逐帧接收姿态估计的关节数据,转成四元数后乘偏移量,赋值给对应骨骼的局部旋转属性
- 调用工具库自带的蒙皮更新接口,自动完成顶点的位置更新,实现模型姿态驱动
示例代码(Three.js 环境转换欧拉角):
// 将Kinect输出的ZYX顺序欧拉角转成四元数 const kinectEuler = new THREE.Euler(roll, pitch, yaw, 'ZYX'); const jointRotation = new THREE.Quaternion().setFromEuler(kinectEuler); // 乘预计算的关节偏移四元数 const finalRotation = jointRotation.multiply(jointOffsetQuat); // 赋值给对应骨骼 skeleton.bones[jointIndex].quaternion.copy(finalRotation);
内容的提问来源于stack exchange,提问作者Jacob Chang
相关产品推荐
相关产品推荐

