前向运动学 以BVH Mocap动捕数据为例
前向运动学
如下图所示, 虚拟角色通过关节, 连接形成了一个树状结构. 对于人形虚拟角色, 一般把腰看成根关节. 在得到关节旋转后, 我们怎样计算得到每个关节的位置呢? 这就是前向运动学(Forward Kinematics)解决的问题.
本文会用到一些旋转相关的知识, 关于旋转的表示, 可以参考我之前写的一个知乎帖子
二维前向运动学
为了表述简单, 我们先从二维前向运动学开始
问题的输入是, 根关节P_0 的位置和旋转, 关节 P_1, P_2 在局部坐标系下的旋转, 以及在局部坐标系下, 子关节相对于父关节的偏移量.
问题的输出是, P_1, P_2, P_3 在全局坐标系下的位置.
我们假设, 当所有关节旋转都是0时, 整个链条是一个水平伸直的状态.
首先我们先来计算, P_1 在全局坐标系下的位置
P_1 = R_0 l_{0\sim1} + P_0
可以看到, x_1 P_1 y_1 坐标系, 相对于 xP_0 y 的旋转角度为 \theta_0, 对应的旋转矩阵为
R_{0}=\left(\begin{array}{cc} \cos \theta_{0} & -\sin \theta_{0} \\ \sin \theta_{0} & \cos \theta_{0} \end{array}\right)
接下来, 我们需要计算 P_2 的位置
在 x_1 P_1 y_1 坐标系下, P_2 的局部坐标为 R_1^{local} l_{1 \sim 2} .
将 l_{1 \sim 2} 旋转到全局坐标系, 也就是 xP_0y 坐标系下, 然后再加上 P_1 的平移量. 这样就可以得到 P_2 在全局坐标系下的位置
P_{2}=R_{0} R_{1}^{\text {local }} l_{1 \sim 2}+P_{1}
接下来, 我们需要计算 P_3 的位置
可以先将 P_3 变换到 x_1 P_1 y_1 坐标系下, 然后再转化到 x P_0 y 全局坐标系下.
P_3 在 x_2 P_2 y_2 坐标系下的局部位置是 R_2^{local} l_{2 \sim 3}
可以看到, x_2 P_2 y_2 坐标系, 相对于 x_1 P_1 y_1 的旋转角度为 \theta_1, 对应的旋转矩阵为
R_{1}^{\text {local }}=\left(\begin{array}{cc} \cos \theta_{1} & -\sin \theta_{1} \\ \sin \theta_{1} & \cos \theta_{1} \end{array}\right)
将 P_3 变换到 x_1 P_1 y_1 坐标系下 (这里上标代表在x_1 P_1 y_1 坐标系下)
P_{3}^{(1)}=R_{1}^{\text {local }} R_{2}^{\text {local }} l_{2 \sim 3}+P_{2}^{(1)}
然后再转化到 x P_0 y 全局坐标系下
\begin{aligned} &P_{3}=R_{0} P_{3}^{(1)}+P_{1} \\ &=R_{0} R_{1}^{\text {local }} R_{2}^{\text {local }} l_{2 \sim 3}+R_{0} P_{2}^{(1)}+P_{1} \\ &=\left(R_{0} R_{1}^{\text {local }} R_{2}^{\text {local }}\right) l_{2 \sim 3}+P_{2} \end{aligned}
这里可以观察到, 可以通过旋转变换矩阵的累乘, 得到最终的旋转变换矩阵.
三维前向运动学
有了二维的结论, 前向运动学也可以推广到三维空间上.
如果用旋转矩阵表示每个关节的旋转, 将前面二维情况下的旋转矩阵, 换成三维的旋转矩阵就好了.
我们可以用四元数, 来表示每个关节的旋转
对于根关节, 全局旋转与局部旋转是相同的q_{0}=q_{0}^{\text {local }}
那么,就可以通过四元数旋转公式, 对 l_{0 \sim 1} 进行旋转, 然后再进行平移操作, 得到 P_1 的位置
P_{1}=q_{0} l_{0 \sim 1} q_{0}^{*}+P_{0}
这里 q_0^* 代表单位四元数 q_0 的共轭
同样地, 也可以求出 P_2 的位置
\begin{aligned} &q_{1}=q_{0} q_{1}^{\text {local }} \\ &P_{2}=q_{1} l_{1 \sim 2} q_{1}^{*}+P_{1} \end{aligned}
最后求出 P_3 的位置
\begin{aligned} &q_{2}=q_{0} q_{1}^{\text {local }} q_{2}^{\text {local }} \\ &P_{3}=q_{2} l_{2 \sim 3} q_{2}^{*}+P_{2} \end{aligned}
BVH文件格式
BVH文件分为Hierarchy和Motion两部分, Hierarchy部分是描述虚拟角色的树形结构, Motion部分是记录每一帧虚拟角色运动的姿态. 下面是一个BVH文件的例子
Hierarchy
ROOT Hips
{
OFFSET 0.00 0.00 0.00
CHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation
JOINT LeftUpLeg
{
OFFSET 3.64953 0.00000 0.00000
CHANNELS 3 Xrotation Yrotation Zrotation
JOINT LeftLeg
{
OFFSET 0.00000 -15.70580 0.00000
CHANNELS 3 Xrotation Yrotation Zrotation
JOINT LeftFoot
{
OFFSET 0.00000 -15.41867 0.00000
CHANNELS 3 Xrotation Yrotation Zrotation
JOINT LeftToeBase
{
OFFSET 0.00000 -1.53543 5.73033
CHANNELS 3 Xrotation Yrotation Zrotation
End Site
{
OFFSET 0.00000 0.00000 2.95275
}
}
}
}
}
JOINT RightUpLeg
{
OFFSET -3.64953 0.00000 0.00000
CHANNELS 3 Xrotation Yrotation Zrotation
JOINT RightLeg
{
OFFSET 0.00000 -15.70580 0.00000
CHANNELS 3 Xrotation Yrotation Zrotation
JOINT RightFoot
{
OFFSET 0.00000 -15.41867 0.00000
CHANNELS 3 Xrotation Yrotation Zrotation
JOINT RightToeBase
{
OFFSET 0.00000 -1.53543 5.73033
CHANNELS 3 Xrotation Yrotation Zrotation
End Site
{
OFFSET 0.00000 0.00000 2.95275
}
}
}
}
}
}
MOTION
Frames: 2575
Frame Time: 0.008333
1.1473 32.8029 1.7158 1.8673 -8.9395 -2.4851 -1.8048 4.4401 9.1220 10.6891 -0.0000 0.0000 -14.4262 1.6207 -8.1203 -6.6388 0.0000 0.0000 -3.3489 -5.6780 -0.1448 8.2598 -0.0000 0.0000 -19.6835 1.1206 -1.8147 9.2354 0.0000 0.0000 -3.9666 2.1039 -0.9182 11.2827 0.3187 1.3724 3.9228 -10.7439 -1.8312 8.9441 -3.8475 2.9412 18.9681 13.3439 -0.6110 -26.5111 -27.4973 -88.3621 -0.0000 -10.0680 -0.0000 -16.6777 -0.4690 -19.8491 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 14.7299 -19.0543 5.1655 -22.5208 25.1609 83.9932 -0.0000 9.9763 0.0000 -25.4105 -2.5127 14.0891 -0.0000 -0.0000 0.0000 -0.0000 -0.0000 0.0000
Hierarchy部分:
Hierachy通过递归的方式描述了树形结构, 如Hips下面有leftupleg, rightupleg
ROOT表示根关节(对应于我们前面介绍的 P_0 ), 包括平移属性和旋转属性, 由CHANNELS描述.
JOINT表示关节点(对应于我们前面介绍的 P_1, P_2 ), 包括平移属性, 由CHANNELS描述. 关节的旋转, 是按照欧拉角来描述的. 欧拉角有不同的旋转顺序, 比如先绕 x 轴旋转, 再绕 y 轴旋转, 最后绕 z 轴旋转, 这样的CHANNEL就会被记为Xrotation Yrotation Zrotation, 关节的局部旋转矩阵 R 可以这样得到
R_{z} R_{y} R_{x} v=R v
旋转的CHANNEL会有不同的顺序, 比如说如果CHANNEL为Zrotation Xrotation Yrotation, 那就是先绕Z轴旋转, 然后绕x轴旋转, 最后绕y轴旋转. 在读取BVH文件的时候, 需要注意CHANNEL的顺序. 后面的motion部分, 每一帧的数据就是所有的channels, CHANNEL出现的顺序, 和后面Motion数据部分是对应的.
End Site是叶子节点(或者说是链条的末端, 对应于我们前面介绍的 P_3 ). End Site只需要包含偏移量就够了, 不需要再包含旋转的信息了, 所以End Site下没有CHANNELS.
OFFSET表示当前的关节, 在父关节局部坐标系下, 相对于父关节的偏移量. 对应于我们前面介绍的 l_{0 \sim 1}, l_{1 \sim 2} . 一般来说, ROOT节点的偏移量为 (0, 0, 0)
在读取Hierarchy时, 可以通过递归的方式实现, 也可以手动设置一个栈, 在读入{的时候入栈, 开始处理新的关节; 在读入}的时候退栈, 代表这个关节处理结束.
Motion部分:
Frames的意思是, 接下来会有多少帧的数据. Frame Time代表了帧率. 之后每一行, 代表一帧的运动数据.
这些数据, 是按照前面channel定义顺序出现的. 按照上面BVH结构的定义, 首先是Root关节的平移量: Xposition Yposition Zposition, 接下来是Root关节的旋转量: Xrotation Yrotation Zrotation, 然后是LeftUpLeg关节的旋转量: Xrotation Yrotation Zrotation. 因此, 这些数字依次代表:
根关节 x,y,z 方向上的平移量, 根关节绕 x,y,z 轴旋转的欧拉角, LeftUpLeg 关节绕 x,y,z 轴旋转的欧拉角, 等等
常用的人体数据集
SFU Mocap:
CMU Mocap:
Ubisoft La Forge Animation Dataset ("LAFAN1"):
PFNN (Phase-Functioned Neural Networks for Character Control, SIGGRAPH 2017) 用到的数据集:
Mixamo数据集(Mixamo包含了一些fbx格式的文件, 可以通过现有的软件转化成bvh)
常用预览BVH的软件
FBX Review: 这是一款轻量的模型查看软件, 能够方便地读取展示BVH文件.
下载地址
Blender:
可以从这里导入BVH文件