前向运动学 以BVH Mocap动捕数据为例

Xsens动作捕捉 2022-10-30 12763

前向运动学

如下图所示, 虚拟角色通过关节, 连接形成了一个树状结构. 对于人形虚拟角色, 一般把腰看成根关节. 在得到关节旋转后, 我们怎样计算得到每个关节的位置呢? 这就是前向运动学(Forward Kinematics)解决的问题.

前向运动学 以BVH Mocap动捕数据为例  第1张
当人体所有关节的旋转都为0时, 虚拟角色有一个初始的姿态. 举例来说, 下面的虚拟角色呈站立姿态, 两臂伸平, 像是一个T字, 所以称为T-Pose

本文会用到一些旋转相关的知识, 关于旋转的表示, 可以参考我之前写的一个知乎帖子

二维前向运动学

为了表述简单, 我们先从二维前向运动学开始

前向运动学 以BVH Mocap动捕数据为例  第3张

问题的输入是, 根关节P0P_0 的位置和旋转, 关节 P1,P2P_1, P_2 在局部坐标系下的旋转, 以及在局部坐标系下, 子关节相对于父关节的偏移量.

问题的输出是, P1,P2,P3P_1, P_2, P_3 在全局坐标系下的位置.

我们假设, 当所有关节旋转都是0时, 整个链条是一个水平伸直的状态.

前向运动学 以BVH Mocap动捕数据为例  第4张

首先我们先来计算, P1P_1 在全局坐标系下的位置

P1=R0l01+P0P_1 = R_0 l_{0\sim1} + P_0

可以看到, x1P1y1x_1 P_1 y_1 坐标系, 相对于 xP0yxP_0 y 的旋转角度为 θ0\theta_0, 对应的旋转矩阵为

R0=(cos?θ0?sin?θ0sin?θ0cos?θ0)R_{0}=\left(\begin{array}{cc} \cos \theta_{0} & -\sin \theta_{0} \\ \sin \theta_{0} & \cos \theta_{0} \end{array}\right)

接下来, 我们需要计算 P2P_2 的位置

x1P1y1x_1 P_1 y_1 坐标系下, P2P_2 的局部坐标为 R1locall12R_1^{local} l_{1 \sim 2} .

l12l_{1 \sim 2} 旋转到全局坐标系, 也就是 xP0yxP_0y 坐标系下, 然后再加上 P1P_1 的平移量. 这样就可以得到 P2P_2 在全局坐标系下的位置

P2=R0R1local l12+P1P_{2}=R_{0} R_{1}^{\text {local }} l_{1 \sim 2}+P_{1}

接下来, 我们需要计算 P3P_3 的位置

可以先将 P3P_3 变换到 x1P1y1x_1 P_1 y_1 坐标系下, 然后再转化到 xP0yx P_0 y 全局坐标系下.

P3P_3x2P2y2x_2 P_2 y_2 坐标系下的局部位置是 R2locall23R_2^{local} l_{2 \sim 3}

可以看到, x2P2y2x_2 P_2 y_2 坐标系, 相对于 x1P1y1x_1 P_1 y_1 的旋转角度为 θ1\theta_1, 对应的旋转矩阵为

R1local =(cos?θ1?sin?θ1sin?θ1cos?θ1)R_{1}^{\text {local }}=\left(\begin{array}{cc} \cos \theta_{1} & -\sin \theta_{1} \\ \sin \theta_{1} & \cos \theta_{1} \end{array}\right)

P3P_3 变换到 x1P1y1x_1 P_1 y_1 坐标系下 (这里上标代表在x1P1y1x_1 P_1 y_1 坐标系下)

P3(1)=R1local R2local l23+P2(1)P_{3}^{(1)}=R_{1}^{\text {local }} R_{2}^{\text {local }} l_{2 \sim 3}+P_{2}^{(1)}

然后再转化到 xP0yx P_0 y 全局坐标系下

P3=R0P3(1)+P1=R0R1local R2local l23+R0P2(1)+P1=(R0R1local R2local )l23+P2\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}

这里可以观察到, 可以通过旋转变换矩阵的累乘, 得到最终的旋转变换矩阵.


三维前向运动学

有了二维的结论, 前向运动学也可以推广到三维空间上.

前向运动学 以BVH Mocap动捕数据为例  第5张

如果用旋转矩阵表示每个关节的旋转, 将前面二维情况下的旋转矩阵, 换成三维的旋转矩阵就好了.

我们可以用四元数, 来表示每个关节的旋转

对于根关节, 全局旋转与局部旋转是相同的q0=q0local q_{0}=q_{0}^{\text {local }}

那么,就可以通过四元数旋转公式, 对 l01l_{0 \sim 1} 进行旋转, 然后再进行平移操作, 得到 P1P_1 的位置

P1=q0l01q0?+P0P_{1}=q_{0} l_{0 \sim 1} q_{0}^{*}+P_{0}

这里 q0?q_0^* 代表单位四元数 q0q_0 的共轭

同样地, 也可以求出 P2P_2 的位置

q1=q0q1local P2=q1l12q1?+P1\begin{aligned} &q_{1}=q_{0} q_{1}^{\text {local }} \\ &P_{2}=q_{1} l_{1 \sim 2} q_{1}^{*}+P_{1} \end{aligned}

最后求出 P3P_3 的位置

q2=q0q1local q2local P3=q2l23q2?+P2\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表示根关节(对应于我们前面介绍的 P0P_0 ), 包括平移属性和旋转属性, 由CHANNELS描述.

JOINT表示关节点(对应于我们前面介绍的 P1,P2P_1, P_2 ), 包括平移属性, 由CHANNELS描述. 关节的旋转, 是按照欧拉角来描述的. 欧拉角有不同的旋转顺序, 比如先绕 xx 轴旋转, 再绕 yy 轴旋转, 最后绕 zz 轴旋转, 这样的CHANNEL就会被记为Xrotation Yrotation Zrotation, 关节的局部旋转矩阵 RR 可以这样得到

RzRyRxv=RvR_{z} R_{y} R_{x} v=R v

旋转的CHANNEL会有不同的顺序, 比如说如果CHANNEL为Zrotation Xrotation Yrotation, 那就是先绕Z轴旋转, 然后绕x轴旋转, 最后绕y轴旋转. 在读取BVH文件的时候, 需要注意CHANNEL的顺序. 后面的motion部分, 每一帧的数据就是所有的channels, CHANNEL出现的顺序, 和后面Motion数据部分是对应的.

End Site是叶子节点(或者说是链条的末端, 对应于我们前面介绍的 P3P_3 ). End Site只需要包含偏移量就够了, 不需要再包含旋转的信息了, 所以End Site下没有CHANNELS.

OFFSET表示当前的关节, 在父关节局部坐标系下, 相对于父关节的偏移量. 对应于我们前面介绍的 l01,l12l_{0 \sim 1}, l_{1 \sim 2} . 一般来说, ROOT节点的偏移量为 (0,0,0)(0, 0, 0)

在读取Hierarchy时, 可以通过递归的方式实现, 也可以手动设置一个栈, 在读入{的时候入栈, 开始处理新的关节; 在读入}的时候退栈, 代表这个关节处理结束.

Motion部分:

Frames的意思是, 接下来会有多少帧的数据. Frame Time代表了帧率. 之后每一行, 代表一帧的运动数据.

这些数据, 是按照前面channel定义顺序出现的. 按照上面BVH结构的定义, 首先是Root关节的平移量: Xposition Yposition Zposition, 接下来是Root关节的旋转量: Xrotation Yrotation Zrotation, 然后是LeftUpLeg关节的旋转量: Xrotation Yrotation Zrotation. 因此, 这些数字依次代表:

根关节 x,y,zx,y,z 方向上的平移量, 根关节绕 x,y,zx,y,z 轴旋转的欧拉角, LeftUpLeg 关节绕 x,y,zx,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文件

前向运动学 以BVH Mocap动捕数据为例  第6张

The End