GAMES105-计算机角色动画基础 - P3:Lecture03 Character Kinematics: Forward and Inverse Kinematics - GAMES-Webinar - BV1GG4y1p7fF
ok啊大家晚上好,那我们开始上课啊,非常欢迎大家来参加,我们这个是games 105,也就是计算机角色动画基础的第三节课,其实第一节课我们讲的比较简单啊,主要是计算机角色动画里面的一些主要的一些技术。
然后还有近2年的一些进展啊,然后第二节课呢主要是一些关于线性代数的回顾,那其实数学对于我们觉得动画来说也是非常重要的,我们其实在后面的不断的讲课过程中。
我们会经常的会把这个一些用到的数学知识逐渐的给介绍出来,那另外一部分其实比较重要的呢是我们在三维旋转,其实我们这个三维旋转其实比二维旋转要复杂很多,我们有很多种常用的不同的表示方式。
那就是随着回字有四种写法呃,比较常用的像全程矩阵表示,向资源数表示,这其实是在我们大部分的这个物理引擎啊,大部分的游戏引擎里面会经常用到,然后一些其他的表示方式,比如说轴角表示。
我们其实也会经常用到一些,还有一些其他的这个可能我没有完全介绍的这种表示方式,那我们今天呢是稍微讲一讲,就是我们第三节课主要内容就是关于这个运动学,那相当于前两节课呢,这节课的内容稍微有点难度啊。
所以说我们我还是将能够尽量能够把它讲清楚,那我们这门课我们之前也提到了,从第三节开始,我们有一个a boys,第三节开始我们有这样一个课程作业,那这个现之前有同学说这个这个作业提交网站现在打不开。
那我现在已经把它已经把它打开了,大家可以去注册一下,注册的时候你可能需要这个需要这个注册码啊,那当然这个现在这个cold base就是我们的作业会放在我们的这个githu,现在可能跟大家上去看是空的。
那我们会在本周内啊把这个作业放出来,然后这个整体来说那不会太难的,就主要是关于我们今天讲的部分,当然有一部分可能今天讲不到,我们也会涉及到,我会放在下一节课啊,进行这个进行讲解,那是因为不是特别难吧。
所以说我们也就时间短一点,我们大概给啊2~3周的时间,那我们可以去让大家提交,然后我们当时助教会给大家做一个简单的评分,ok那我们今天的主要内容刚才已经提到了,我们今天主要是讲这个运动学的基本知识。
也就是前线运动学以及逆向运动学学的基本知识,那关于选项运动学啊,我们主要是讲这个所谓的运动学是什么什么意思,其实我之前也是之前查了一下这个这个这个英语词典啊,其实解释还是非常清楚的,运动学就是研究身体。
研究多这个物体运动的这么一个学科,但是呢它有一个前提条件,就是说我不考虑物体的质量和力,这样这样一些这个外来的性质,并且这个跟一些性质,那这个时候这个其实如果说我们考虑这些性质的话。
那其实就不再是运动学了,那个叫动力学,我们后面很大一部部分,这个篇幅其实会涉及到基于动力学或者基于物理仿真的角色动画,我们该怎么实现,我们该怎么去去控制,那我们其实有很长一段时间会涉及到这些内容。
那当然在前面这下班时间,我们其实是不用考虑这些内容的,那其实主要是关于物体的运动这方面的这个这个这个啊这些知识,那当然这个角色因为我讲解说动画嘛,其实这个角色是范围是非常非常大的。
人其实是一个最常用的一个角色,然后另外其实像机器人,这个像这个比如说这个bodynamics,这个这个这个阿拉斯,然后一些机械臂其实他都可以认为是一个角色,那这些角色啊。
这其实我们应该说角色动画跟机器人领域,在这方面其实有非常大的这个共享的一部分的这个技术,所以实际上我们也是经常会关注一下那边的一些进展,那当然记起来,其实这些角色对于我们通常可以找到一些比较共性的部分。
公共性的特点,就是说我们一般来说是认为是研究什么样的一个东西,一个角色呢就是他的身体本身是刚性的,然后他只会这个它只会产生这个这个这个沿着关节进行旋转,那当然说如果当时我也看到很多这个血角色。
他其实是这个是这个这个非常性的,那这种其实我们可以把我们这个抗性这种这种描述进行扩展,其实也是可以实现同样的效果,那具体来说我们其实可以把这个人的建模型给抽象一下,我们知道他这个一个人。
那么其实我们这个在做动作的时候,我们其实身体的啊移动是围绕着我们的身体的一些关节进行的,比如说我转一下胳膊,那其实我这个胳膊的旋转,其实我们的关节保证了我的这个身体。
我的这个大臂和小臂在移动的过程中不会这个分开,那它只会沿着围绕这个关节进行旋转,那它这个模型里边我们的关节之间其实就是它关节是连接的啊,每个关节是连接的两个boarding。
但其实我们在不同的领域和那个不同的名字,比如说也可以叫bb叫骨骼,也可能叫这个link,也可能有其他的名字啊,但总体来说呢,我们其实是一我们在这个建模里边,就是整个身体我们可以抽象成若干个骨骼的组合。
然后每两个骨骼之间会有一个关节进行连接。
这是我们一个常用的抽象,那当然比如说在一个真正的在一个常用的这个这个这个软件里边,比如说这个blender,我看到要一个一个例子,那么其实是用了一个虚拟角色,其实它内部都是有若干个这个关节系统。
那当然其实对于这个动画来说,那其实很多时候可能我们不会被特别关注啊,这个这个骨骼或者是这个bb的它的它的形状或者它的这个什么参数,那我们大部分情况下只会关心一下关节的位置。
以及关节的旋转所带来的这个角色姿态的改变,那先就说到这件事情,如果说我们把这个先把这个角色定义成一个关节和构成的,这样的一个系统,那么我们需要创建一个角色啊,创建一个姿势。
那我们实际上就是去旋转某一个关节,旋转每个关节,把它摆动到某一个姿势,那当然实际上我们一说到关节,它的作用是什么,它的作用是连接两个啊,两个物体,两个两个肢体,两个两个骨骼。
使得它在旋转的过程中不会分开,但实际上如果说这个,但这个事本身是不能直接满足的,就是为了能够让我们整个姿势,这个角色在我国呃产生一个资质的过程中。
它不会发生这种这种这种这种这种分开的立体的这样的一个现象,那我们其实是要非常准确的计算我每一个关节在某一次旋转过程中,它的这个带来的影响,就是它它就每个关节在旋转之后,它的位置和它的朝向。
那这里其实这个整个这个计算过程呢,就是所谓的前项运动的前向运动学的这样一个过程,当然我们可以这个更加简单的举一个简单的例子啊,就是说比如说这是一个机械臂,那当然是一个链条了,它有若干扇没有。
比如说这样有四个关节啊,五个关节,那每个关节其实我们可以认为就是他每个关节会连接一个一个一个,一个物体,一个肢体,我们可以认为这个肢体上,所以这个关节我们可以绑上了一个坐标系。
那这个坐标系在我旋转过程中,它的朝向,或者说我这个这个坐标系的这个这个在于世界全局坐标系里的,这个这个方向,那其实可以定义为是这个关节的朝向,那我们就在前项运动鞋的其中一个很重要的部分。
就是说我们在摆一个角色,摆到某一个姿势之后,我需要知道每一个这个关节或者每一个肢体,它的去这个局部的这个坐标系,在全局坐标系的朝向是多少,那这个其实我们需要做这样一个计算。
那我们可以简单的这个这个这个图简简单的计算一下,比如说我们一开始的时候,这个角色他的这个坐标系表示是跟世界,是跟世界坐标系是从呃是平行的,是重合的。
那么其实我们知道他的这个这个朝向呢应该就是就是就是啊单位旋转,那如果说我们比如说我们把这个最后一个关节把它旋转了一下,我们在旋转最后一个关键的时候,其实前面的关键是不是不会受到影响。
所以它只会影响到最后一个关节,它本身的这个局部坐标系的这个这个朝向,那么这个朝向变成了比如q4 的这个朝向,就变成了这个r4 这样一个旋转,那我们接下来如果说我们往前走一步,我们去旋转r 14的呃。
这个副关节就是r3 啊,在这个选项过程中,这个r3 实际上它同时会旋转,它带来的影响是同时旋转了这个第三个关节以及第四个关节,所以它会把首先把r3 自己的这个朝向啊变成了啊。
就把这个第三个关节的这个朝向变成了r3 ,同时把第四个关节朝向它也同时做了一个旋转,我们其实回想一下我们上节课讲到的啊,这个这个旋转的叠加旋转的组合,那么其实可以得到它最终是一个就是r 14的选项。
现在变成了r 13乘以r4 这样一个这样一个结果,那类似的我们可以继续不断的往依次往前去旋转它前一个关节,那每次旋转其实都会带来后面就是每一个字。
它会会带来比如后面每一个子关节的这样一个啊朝向的一个变化,那最终我们其实如果旋转完整个这个机械臂之后,那我们得到了一个这个姿态,那这个姿态里边每一个关节的朝向实际是他父节点,所有负极点到祖父节点。
整个这一条链条下来的这个朝向的乘积的总和,这是一个前向的那个朝向的一个计算,那么这里我们其实也可以看到,这里有一个规律,就是说实际上每一个关节它的朝向,或者说它的一个局部坐标系的一个方向。
都是在都是等价于它负关节的局部坐标系的方向,乘以这个当前这个关节的旋转,那其实也是反过来,如果说我们知道负极点的朝向呃,副关节的朝向,知道这个当前关节的旋转,那我们可以直接计算出这个当前关节的朝向。
那如果反过来,如果说我们知道当前关节的朝向以及腹关节的朝向,那么也可以计算出啊这个关节的旋转是多少,因为我们只需要这个副关节求一个逆,然后乘以当前关节,就完成了这样的一个这样一个转换。
啊另外呢如果说我们是想要继续,比如说可能想要计算当前关节朝向,相对于他前面某一个关节的这个朝向,这个相对旋转,那我们其实也可以通过这样的计算,可以很容易得到。
他其实就是说从前面那个关节到当前关节这一路上,所有关节的旋转的生机,那就该得到的是我们这两个之间的相对旋转的值,我们可以从另外一个角度来看这个问题啊,就是说我们可以假设前面我们提到。
我们假设每一个关节上都连接了,都绑定了一个坐标系,那我们可以进一步假设说这个坐标系的原点,那就是刚好是落在我们每个关节的这个旋转的这个点上,那在这个过程中呢,我们可以如果说每一个关节,比如说第零个关节。
它跟第一个关节之间的这个或者第一个关节的,第二个关节的这个相对相对距离是l0 ,那其实等价于数是什么呢,它是等价于是说第一个关节的位置在地理跟关节的局部坐标系的表示,这个坐标就是二零那一次。
那一次后面是类推的,那这种情况下,比如说我把第一个关节旋转了一个角度,那时间它会带动整个后面所有的关节发生旋转,而在这个过程中,因为这个第一个关节,第一个关节的位置在这个第零个关节坐标系里的。
相对的这个这个局部表示局部坐标是不会发生变化的,那么如果说我们需要去计算第一个关节所对应的这个坐标系,或者它的朝向这个坐标系的原点的世界坐标系的位置的话,那我们其实是等价于是要做一个变换。
就是把第一个关节的在第零个关节的局部坐标下的那个那个坐标,转化成在世界坐标系的坐标,那这个过程实际上我们就是一个简单的坐标转换的过程,我们需要用第零个关节的这个这个啊朝向啊,乘以这个局部坐标的这个向量。
然后再加上地理关节的位置,当地灵关节曲我们怎么设定了,它可能在原点或者可能在一个p里的这样一个位置,那类似的,如果说接下来我把第一个关节旋转了一下,那么得到了那么同样的对应的第二个关节它的坐标啊。
这个坐标原点的位置其实也会同样发生变化,那这个过程中,实际上我们也可以同样的用这个坐标转换来计算出第二关节,它的坐标原点的位置应该在哪里,那意思我们可以不断的这样的这个以此类推。
那当我们把这个角这个角色姿态,从第零个关节前向传播到最后一个关节之后,那我们就可以计算出每一个关节所它所对应的这个坐标轴啊,这个这个坐标系的朝向以及坐标系原点的位置。
那么接下来一个问题就是如果说我们在这个坐标系里边有一个点x0 ,那我们该怎么计算它的全局坐标系下的位置呢,那其实这个非常简单的,这就是这就是一个坐标转换,我们可以用啊。
q4 也就是它的第四个关节的朝向乘以x0 ,然后加上p4 ,那得到这样一个全球最大的位置,那类似的,如果说我们知道了第三个关节,它的朝向它的这个坐标原点的位置。
然后我们又知道q x0 在q4 在第四个关节里边这个局部坐标,那我们可以怎么算呢,那么同样我们还是也是可以去计算这个x的在全局坐标系里的位置,那这样当然这过程我们需要去做一部的这个这个代入啊。
我们就是把q4 的这个和p4 的计算改代入到这个p3 的呃,这个第三个坐标轴坐标系的这个计算里面去,那在这个过程实际上我们可以同样的可以通过这个通知公式中,我们可以推导出x0 。
那他这原来是在q第四个关节的局部标系,它在第三个关节的局部特系局部坐标系下的这个坐标表示,实际上也是可以啊,通过这样的方式来进行计算的,那类似我们可以继续往前推。
首先可以得到这个x0 这个关这个这个点在前面每一个局部都系下,它的表示应该是多少,所以总结一下,就是我们在计算这个前向坐标前项啊,运动鞋的过程中呢,我们其实是要有前线的。
其实可以做一个从根节点到目标节点,那末端节点这样一个前向的一个迭代,那我们可以在这个过程中可以去逐步的更新每一个坐标的位置,有一个啊每一个坐标系的每一个关节所对应的坐标系的这样一个位置,和它的朝向。
然后当我们整个啊前向的这个这个,当我们完成一遍这样的一个前向的运算的时候,我们其实就更新了每一个关节的朝向和它的坐标位置,那相反的我们其实也可以反向来对它进行计算,比如说我们可以从一个目标点开始。
那这个目标点我依次的把它转换到它的负节点的这个局部,坐标系里面去,那么知道每次转换我需要用这个当前坐标系,比如说刚才是在q4 ,那我先用q4 的呃,在q3 里面的局部坐坐标,那就是比如说它的局部坐标。
它的圆顶的局部坐标是l4 l3 ,那它的这个朝向的局部坐标,应该是刚好是等于我的这个这个关节的旋转也是r3 ,那r4 ,那我们可以把这个每个点,这个转换到它的这个复节点的这个坐标系之内。
然后我们依次做这样的转换,我们也可以同样的计算出啊,每个点在就当前这个点在全球数字游戏的表示方啊,这个这个坐标表示,那其实我们如果说我们不是从根节点开始,我们就是想要计算一个相对的位置。
比如说我想计算l0 在看某前面某一个关节局部坐标系,比如说r一或者q一这样一个局部坐标系下的坐标表示,那我们其实可以同样的做样这样的一个前线运动学计算,就是通过这样一个啊一个便利的一个迭代的方式。
但我们可以不不是当这次我们不是从根节点开始,而是从我们的这个比如说r一这样的一个环节开始,那我这样计算得到的结果就是我们的末端点就是x0 ,这个点在r一下的这个坐标表示,那同样如果说我们不关心朝向的话。
其实我们可以做一个逆向的迭代,然后我们也是一逐次的把r2 s0 逐次地转移到啊它负节点,或者他的在主副节点的每一个关节的局部坐标系统之内,那这样的话我们就完成了一个减项运动学的这样一个计算,ok啊。
那我们前面主要是介绍的是关于这个单链条的非常简单的一个例子,就在这个单链条的一个情况下,我们如何去进行前线运行计算,那对于一个角色来说呢,我们其实可以把它建建模成一个啊。
由多个链条连接到同样一个根节点构成的这么一个树形的一个结构啊,通常来这是一个我们对这个角色的一个抽象进抽象模型,你在这个过程中,我们这个角色有一个也只有一个根节点,然后除此之外呢。
比如说我这个从根节点到手,它会形成一条一条链,一一个一个形象链条,然后从根节点到腿,这是另外一个倾向链条,那当然这个过程中,实际上根节点的选择,我们通常来讲根节点会把它放在这个角色的腰的这个位置。
因为本质上它带来的是我们这个整个角色的一个啊,整个角色的朝向和这个位置的一个信息,那这个不同根据点的选择其实也会带来不同的这个旋转效果,就比如说我在这个在跟你脸是在腰部的时候。
如果说我要求我的腿旋转一个角度,那其实就把腿抬起来了,但如果说其实我对角色来说,我完全可以选择另外一个关节作为我的根节点,比如说我选择这个右脚作为它的根节点,那这个时候他用同样的方式去旋转它的这个腿。
他的这个右腿,你们看这个角色它的旋转方式会发生一定的变化,但本质来说这个他俩的这个姿态是完全是其实是相同的,只不过只不过是这个整个身体的朝向随着使用,因为它优势跟随我的根基点来决定的。
那么它其实会带来不同的朝向化的变化,那其实我们说到这个人嘛,其实我人的从解剖的解剖学,这个生物解剖学来说,实际上我们每个关节会有不同的这样的一个性质,就比如说这个比如说最简单的。
比如说我们的这个胳膊或者我们的腿,其实我们发现它只能先一个方向去旋转,它不能像先另外的方向去旋转,因为就是如果发生旋转的话,多半是这个人可能是是是是是有点问题的,比如他把他骨折了,但其实这个整体来说。
人的旋转,每个关节这个旋转的这个方式,但是由这个关节和这个就是一个骨骼的这个具体的呃结构,和它的这个形状来共同确定,那当然实际上对于人体来说,对一个非常精确的人的模型来说,其实这个关节的种类是非常多的。
就是不同的,甚至可能每一个关节都会有不同的这个旋转方式,那当然在结合动画里面,我们通常来说不会做这么复杂的一个建模,我们可能会把这个模型建的稍微简单一点,我们可能只会考虑几种比较常用的比较。
就是这种这种关节的表示方式,那当然比如说我们查了几只有几几种,比如说这是其中的两种,一种是所谓的hinge drt,我其实也是叫也经常也会叫做云啊,revolua revud。
它是一个单自由度的一个旋转,其它其实它刚好对应的是,比如说胳膊,就是这个手肘,然后还有膝盖,那他只能沿着一个方向,它有一个确定的一个坐标,一个旋转轴,它只能沿着这个轴进行旋转。
然后另外一个也是很常见的一个关节,就是像比如说我们的这个这个这个髋关节,然后还有这个这个肩关节,它的这个从解剖学上来说,它其实就是一个碗的洗个形状,然后另外一个是一个骨头。
然后这个骨头和在这个碗里边可以进行这样一个旋转,那这个学生呢还是没有一个确定的旋转轴的,就是他其实可以在这在整个这个范围内可以任意的进行旋转,也可以这个这个朝向的一种发病啊,这个转换。
那这种关节呢一般我们叫burant或者叫bsocket dt,其实是比较形象的描述的这个关节的这个这个状态,那当然对于不同的关系来说,我们或者说对于整个不同的系统来说。
我们其实是有一个非常重要的一个性质进行描述,它就是所谓的自由度,那从严格定义来讲,自由度表示什么意思呢,自由度是说是一个物理系统,我需要多少参数可以唯一准确地描述它当前的状态。
那是代表这个东西的这个物体的自由度,那么对于我们来说,实际上更加直观的想法,就是说这个动物体它可以在多少方向上进行移动,代表它的自由度,就比如说这样一个立方体放在这个空间里,什么都没有,没有任何约束。
那首先它是可以沿着空间的x y z,因为我是一个三维空间,它可以沿着x y z3 个方向分别移动,除此之外呢,它还可以沿着x和y和z这三个轴发生了旋转,其实回想一下欧拉欧拉角,欧拉欧拉表示还有他。
那有人说诶,我是不是可以沿着另外一个随便的一个轴,不一定非得沿着x y z,这个也是没有问题的,但是回想一下我们坐标,而我们三维旋转的几种表示,其实我们会任何一个旋转。
都可以把它分解成沿着坐标轴的旋转的这样的一个组合,所以总体来说呢,这个物体它的自由度,因为它可以沿着六个六种不同的方式进行进行移动,所以它自由度等于六,那我们其实可以比如说我把这个屋方体。
这个立方体放在一个平面上,我我跟他说,你只能在这个平面上移动,那这个时候其实平面就约束了这个立方体它所能够移动的方式,以及沿着这个xz方向的平移,以及沿着沿着竖直方向,就是y方向这个旋转,那这种情况下。
我们知道它只有三种旋转移动方式,那它的自由度应该就等于三,那就是还有一些更更加在另外一个例子,就是说比如说把这个物体这个立方体穿在一个轴上,那只能沿着轴上下移动,以及沿着轴进行旋转。
那这个时候它自由度就应该就等于二,所以说对于刚才我们这两个关节来说,首先这个hd它的自由度等于一,因为它只能沿着一个确定的轴进行旋转,而一个爆炸它其实是自由度就等于三了。
那么这里其实这个对于我们关节来说,我们知道关节一个最基本的性质,就是说我们关节需要保证两个物体不会分开,所以它其实相当于限制了我这个物体不会发生水平的,这样的一个就是位移方向的移动。
因为如果说我发生了位移平移的话,那么这个这个关节其实就分开了,所以说对于关节来说,我们自由度至少是就通常来讲,比如说就是就是最最最多也就是三,因为我们这只考虑旋转的情况下,他它是有这样的一个约束的。
那当然其实想一想,我们是为了两个关节的,这个这个就是两个自由的关节,其实两个自的关节就好几种不同的形式,其中一种形式是所谓universal j,它是两个轴,在工位,两个轴可以是垂直的两个轴。
然后我可以沿着这个分两个它的两个关节啊,两个肢体可以分别的沿着这两个轴进行旋转,那这个环节其实在我们的这个机器里面用的还是挺多的,因为它可以用来作为一个传动的一个转向,的这样一个功能啊。
另外其实在身体上来说,这你可以想象一下,比如说手腕手腕差不多是一个universal转的这种抽象,就是你可以沿着沿着这个手腕的这个轴一个轴发生转动,同时也可以沿着另外一个轴发生转动。
所以它差不多是一个啊原veral转的,那除此之外呢,其实我们还有另外一个性质,就是所谓的这个关节的一呃极限角度极限就是glimit或者angle limit。
就比如说其实最典型的我们知道我们的膝盖是不能向反方向移啊,旋转的,所以它就决定了我的膝盖的旋转范围应该是比如从零到,比如,说70度80度这样的一个范围,但是我膝盖想反方向旋转,那那其实这个人也不对了啊。
另外就是说像集市,像是爆炸,就是它也是有相应相应的极限的,就是这个可能很多时候是取决于我身体的这个柔韧性,就别说我可能这种韧性比较差,那我可能抬腿只能抬90度,那有些人人气很好,我可以抬个70度啊。
sorry,他抬个150度可能更高,所以这个其实也是我们在做这个在做这个角色,这个运动的这个动作生成的时候,其实也会需要经常,考虑的这样一个问题,ok那我们其实前面也提到了,我们在每一个关节。
就是我们这个角色,我们可以把它表示成一个树形结构,那这个树形结构里边每一个就整体的这个角色的姿态,我们可以利用根节点的位置和朝向,以及在这个树理树形结构里,每一个关节的旋转,把它放在一起。
这样就构成了对这一个某一个姿势的这样的一个一个参数化的表示,你看他这个参数化表示里边我们可以简这里我只是简单写,把它写成了一个旋转矩阵的方式,但实际上因为考虑到我们每个关节可,能它有不同的这个自由度。
比如说一个指关节,对于一个指关节来说,其实我表示它的旋转我只需要一个参数,也就是它沿着那个旋转轴的这个转角,而对一个bl一个球形关节,那么至少需要三个选择,三个参数去把它表示出来。
那整体来说它它因为它自由度是三嘛,所以说整体来说我这个人的自由度是由每一个关节的,就就是表示美表示整个人的他的姿态所需要的参数,其实也是首先是由root关节的这个位置和朝向这六个自由度。
加上每一个关节自由度的总和,能得到我们整个整,个角色的自由度,那这个关节表示实际上就是我刚才说它其实分为两部分,一部分是这个root,也就是说根节点的位置和朝向,他确定了这个角色整体的位置和方向。
然后再加上每一个关节的每个内部关节,它的旋转的表示,那共同构成了对一个姿势的一个参数化,那当然在这个过程中,我们通常还是会做一些啊,就是为了计算的方便,我们通常还是会要求这个顺序是有一点点有一点要求的。
就是说它通常来讲我们会要求一个关节在这个表列表里的顺序,是比它后面所有的子关节都,要靠前的,这又带来一个什么好处呢,就是说实际上回想一下,我们在做前项运动,前项运动学计算的时候,我们是需要从根节点出发。
沿着每一个这个运动学练去依次更新每链上每一个关节的位置和朝向,那如果说我们在假定父节点永远是在子节点的前面,这样的一个顺序的情况下,我们其实只要病历一遍我们这个这个关节的列表,那么就可以完成整个的更新。
那这个其实也是这个编程里边非常也是这个数据结构里边非常有非常,但可能大家都已经了解过了,一个就是树数的这,样一个数据结构,它表示成它的一个实际的实现方式,那这个时候我们其实有两个问题。
第一个问题就是说如果说我们知道了每个关节,或者说每个肢体它的朝向,我们该如何计算每个关节的旋转,因为前面我们前运动学是说我知道了每个关节的旋转,我需要去计算整个关节的长相,那他这个其实我们也非常简单了。
我们其实只要是用这个副关节的朝向,然后去求逆,然后再乘以子关节的朝向,就可以得到这个关节的旋转,那另外一个说实际上我们这里是前面是假设说这个关键是,刚体的刚性的,它不会发生这个伸长,不会发生拉伸。
那如果说我们需要这个结果的发生拉伸怎么办,其实可能其实从其实一般来说生物来说不会有这个问题,但是机械机器人经常是有这样的问题的,就是我可能有一个只有一个线性的一个关节,就它是会连着某一个方向进行移动。
那另外或者说比如我可能有一个所谓的这个平面的一个约束,这个物体可能在平面这个关节可以在一个水平面上进行移动,那这种情况下,我们其实只要把我们的这个关节的参数化的方式稍微改变一。
下比如说我们可以加上一个额外的参数,就是不会美,除了每一个在每个内部关节之外啊,旋转之外,我们可以加上每个内部关节它的平移,那作为一个坐标,其实可以很容易地实现我们前面这个啊。
对这个可变形关节的这样的支持,那具体来当我们这个前面只是说每一个姿态它的参数化的一个表示,那然后通常来讲我们会就比如说我们一个动作,有可能它是有很多姿态进行构成的。
我们可以把这样一个姿态放到一个文件里面去,那这个文件其实有很多种不同常用的格式了,其中,一种比较长了格式,就所谓的b位置文件,那其实还有些其他的格式,比如说a f c啊,a m c。
然后还有比如说像是这个fbx,这都是一些比较更加复杂一点的格式,因为什么文件其实是就是说我们见到了很多这种常见的这种开源的啊,动作动作捕捉的库,他会他会基本来说都会提供一杯瑞士格式。
这是使用非常广泛的一种呃角色动作表示的一个格式,那实际上不管哪一种都能格式,我们都会如果说我们仔细研究它的方式,它的这个表达方式的话,它会有两部分,其中一部分就特别就,是以这个b位置文件为啊作为例子。
他这个比如说你打开一个变为必备的是文件,它的开头的一部分,它会先定义一个角色,就是定义什么,就是这个角色在旋转,在每个关节旋转等于零的时候,其实我们通常也会把这t po这样一个零的时候。
它每一个关节和它的腹关节之间的相对和相对位置关系,那其实我们可以当我们读完前面这个之后,我们首先可以再建立一个建立一个虚拟角色,然后接下来会有一部分会会描述在前面这样定义好的这个角色之上。
我每一个关节的旋转应该,是多少,当然每一个关节的旋转我们定义好一系列旋转呢,其实是定义好的一个姿势,那我们如果说比如说这个动作可能有有有有,比如100个不同的姿势构成。
那么其实可能有100个这样的这个这个旋转的表示,那对于b位置文件来说,其实大家这个这个非常简单的例子,就是上面这一块,它其实定义了我的这个角色的这个形状。
是通过我们每个关节和它父节点这个相对的位置来定义出来的,然后另外的话其实并非成文件,它有另外一部分,就是说他还同时因为并非成文,它是用,这个欧拉角来表示一个关节旋转的。
所以因为欧拉奖我们知道它是有一个旋转顺序的一个问题,所以在编文编位置文件的这个角色骨骼的定义里边,我们其实也会额外的同时也会定义一个奥拉雅的顺序,那当然这里比如说我这里显示z y x。
那其实它对应的欧拉角的顺序是r x,r z乘以r x乘以r y这样的一个顺序,那实际上并为这些文件下面的这个病,它每一行代表了一个姿势,那其实每一行是以刚好是按照我们上面定义好这个顺序的。
这个每一个每一个轴的这个旋转,表示的这样一个啊这个这个这个参数那只是一个b文直接文件,但如果这个关于这个文件的更多的一些信息呢,大家可以去,比如说其实可以看,比如说是一个呃比较比较简单的一个介绍。
那其实我们可以找到很多的,大家可以很容易地找到一些其他的关于b维持文件的这个这个文件类,型,文件格式的一些介绍,介绍的这个文档,ok那我们前面其实简单回这个介绍了一下。
关于这个角色的前项运动学的计算的公式,那总体来说减项运动学是一个非常简单的,其实本质上就是一系列的坐标转换的这个这样一个组合,那其实我们比较重要的或者比较关心的一部分,其实是所谓的逆向运动学。
那当然这里也是这我这里有一篇文章啊,这是我最近也看到的是一个关于逆向运动学的一个方法的一个啊,一个sv啊,但是也是大家有兴趣可以去参考一下。
那什么是逆向运动学,其实逆向运动学相对于前项运动学来说,因为在这个实际过程啊,使用过程中应用的还是更加广泛一点,因为主要是呃就比如说我们做一个角色的这个啊所谓的这个绑定。
那么通常来讲会建立若干个逆向运动学的这样一个控制器,那这样的话我们在这个动画师,他主要还是通过这样逆向运动学的这样这样一个控制器,来去改变这个角色姿势,因为这为什么要为什么做这样的事情呢。
因为主要是说大家如果说自己试过啊,在软件里面通过比如说去旋转每一个关节的角度去调姿势,你会发现这个这个这个过程是非常非常非常麻烦的,因为主要是说比如说我们用奥拉角,虽然说我们知道奥拉角。
我们可以很明确的知道每一个旋转,每个轴的旋转代表了什么样的一个,大概代表什么样的一个一个一个一个旋转,但是我把它组合起来,想把一个角色姿势摆到某一个状态下,你会发现这是非常非常困难的,因为我稍微调一调。
就是主要是说我们很难去有一个非常直观的方式能够告诉他,我就是想要转到某一个角度去,而这个逆向运动学其实是反过来的,就是说我可以通过直接指定我末端点的位置和朝向。
然后让我的这个逆向运动学算法去自动计算出我每一个关节。
它的朝向应该是多少啊,它的旋转应该是多少,那这样的过程时间会比如我直接去调每一个关节的长相,要更加简单一点,而且同时结果也更加直观一些。
其实我们前面讲到这个逆动力学i k问题啊,其实是一个更加通用的,更加广泛的逆向问题的一个一个特例,那我们知道什么是逆向问题或者什么是前向问题,就是说我们当知道一个系统,比如说一个人一个角色。
我们知道这个系统的一些参数,就比如说我知道这个角色的每个关节的旋转,那我们想要去计算它某一个关节啊,某一个这个关节点的位置,那其实代表了这个现象的一个属性,从这个关节的参数,从系统的参数计算某一个属性。
这个过程实际是一个前项计算的过程,那当然这个角色角色这个前运动学是一个例子,另外还有一些其他的,比如说我知道一个软体,一个可以变形的一个物体,它的刚性刚度以及它的这个不同系数。
那么可以通过前向仿真的方式得到这个软体,它在于某些外力条件下,它的形变的这个过程,但总体来说呢,我们这个前项的计算或者前项的仿真是一个比较容易的过程。
因为本质上我们我们在知道这个确定的这个这个这个函数的形式之后,那我们只要计算一下函数的值,就可以得到前前前前下中学的解啊,然后在这个过程中,通常来讲我们的目标的这个属性。
它的这个维度会远远的小于我的这个参数的维度,就比如说我们的关节,它这个整体的旋转的自由度啊,通常是比较大的,然后呢我们如果说我们只用来控制一个末端点的话,那这个末端点通常它只有三维嘛。
所以它的这个自由度会相对相对较低一些,那反过来如果说他的逆向问题说的是什么,逆向问题,就是如果说我知道这个我要求的这个目标参数的一个目标值,那我需要想去计算一些系统参数。
使得这个目标使得这个目标属性它达到我想要的那个值,就比如说在i k里边,我们是想要让一个末端肢体的位置达到我的一个想要到的一个目标点,那这个过程我们如何去计算每一个关节的这个旋转,是让它达到这个位置。
那这样通常来讲这样一个逆向问题是会比前面问题要难很多的,因为主要最主要的原因是什么呢,是这个前项问题所要去,它只要去计算这个函数的这个值就可以了,而这个函数本身呢又是一个高度非线性的函数。
那我们在求解一下有问题的时候,我们其实要求解一个高度的非线性非常复杂的函数,它的根是多少啊,这是一个通常来讲是非常非常困难的问题,而除此之外呢,因为这个函数通常来讲它还有很多通常来讲不是单节的。
他可能有很多,它会有很多这个不同的额外的呃,不同的不同的这个这个这个这个组合可以完成同样的目标,那这个工程我们该如何选择这个组合,使它能够达到我们想要的这个效果,这其实也是另外一个比较难的问题。
ok那具体到我们的这个逆向运动学这个问题呢,我们其实可以知道我们想要去求解的是一个知道末端点的位置去,我需要反推出每一个关节的旋转的这样一个问题,那当然因为我们把每个关节的这个旋转。
我们会有相应的这个旋转表示,那把它参数化成不同的这个比如说可能欧拉甲呀,或者是轴角表示,或者是四元数表示,我们实际上的计算应该是说我们在知道目标端点的目标末,末端点位置的情况下。
我需要去反算出我的每一个旋转坐标,表示的是那个参数的值,但对于i k问题来说,我们其实有很多要注意的,要注意到一点,就是说i k他可能有在一些听力情况下,它可能有解,可能没有解。
然后甚至大部分情况它应该是一个多解的问题,就是在这个例子里边,我这个机械臂有一个长的和两个短的这样一个这个关节构成,那么当这个如果说我们目标点超出了整个这个机械臂的总长之外。
那这个这个问题其实是没有解的,然后如果他小,他在里边这半圈其实也是没有解的,其实可以看到,其实我不管怎么选择我的专辑,我应该是不我没有办法去啊,到达里边这圈的位置啊,而这是中间这部分。
其实我们会发现它其实任何一个点我们都至少有两种,至少两种以上的这个这个方式能够去到达同一个点,随着时间对ik问题来说,我们可能很多情况下是一个多解问题,那一个最简单的1i k问题就是所谓的秃顶。
i k就是两关节或者叫两个物体,两肢体的这样一个ik的一个这个这样一个问题,但这两关节i k问题,其实在我们的这个角色动画里面也非常非常重要了,因为我们很多情况下,对于一个人来说。
比如说我们的胳膊或者腿,它其实就是一个两关节的一个两关,两个关节构成的这样一个一个指令,一个直体,那这种情况下,我们其实比如我们想要一个手去伸到前面去,然后去碰到一个物体。
我们很多时候只需要去解一个两关键i k的问题,就可以就可以完成这件事情,当然两关两关节i k为什么他就是帮他把它单独拿出来说呢,因为主要是说两关加i k,它是一个非常简单的一个i k问题。
我们可以用很容易的方法去算出这个ik问题的解,就比如说我可以简单的说简单的想象一下,比如说我想让这个关节这个关节二移动到我这个目标点的位置,那我其实我可以完全可以沿着关节零做一个半圆。
然后沿着关节二就是用这个关节1~2的这个关节的长度做一个半圆,那这两个圆的交点,那肯定就是这个关节点的这个一的关节点一的位置,然后我们可以根据这个相对关系,计算出我这个每个关节的旋转应该是多少。
或者说我们有另外一种非常更加简单的算法,比如说比如说我是想还是刚才那个问题,我有一个两关节的一个节臂,我想让这个关节点二旋转到啊,移动到我这个目标点x这个位置,那这个过程我们该怎么实现呢。
那我们其实有两个,第一步我们旋转一下关节点,一关节一使得零和二的距离等于零到x的距离,这该怎么算呢,其实这个非常也非常简单哦,大家可以回想一下这个呃所谓的余弦公式,我们知道三角形的两个边的长度啊。
以及第三个边的长度,那我们是可以通过余弦公式计算出这个这个中间的夹角应该是多少,那我们就可以相对来说计算出了这个关节的旋转应该是多少,那在完成这一步之后,我们知道零和二的距离等于零到x的距离。
那我们其实在想要把二移动到x,其实我就只需要再做一次旋转,我们旋转一下啊,关节点零使得二跟x重合,那这就完成了这样一个ik的计算,那这个旋转其实我们前面已经已经已经啊提到了,我们当时怎么算呢。
因为这是两个相,它等价,于是我需要把向量零二移动旋转了,现在0x这个位置,那我需要加一个旋转,这个旋转它的旋转轴是零二和0s做一个插成,然后它的旋转角应该是这两个啊,这两个轴这两个向量做一个点乘。
那其实这个很容易的计算出我这个旋转矩阵是多少,那在这基础在这个基础之上呢,因为我们只考虑的是一个三维的旋转,三分的旋转,我们在中间这个连线的这个平面上,所有的旋转都是满足条件的。
那我们其实可以进一步的再沿着0x再做一次额外的一个旋转,把这个把这个轴啊选择了我们需要的角度,就比如说我们在做一个呃这个比我想象的那个角,把我的手或者我把我的脚伸到某一个位置,那么其实膝盖的朝向。
我们通常来讲还是希望他能够朝向一个比较舒服的一个方式,那么其实在前面完成这个一和二的这样一个操作之后,我们可以再加额外的加一个,沿着沿着这个宽到脚站的连线的这样一个旋转,来把这个膝盖旋转。
我们需要的需要的角度,所以总体来说只是对于一个to link i k就是一个二关节的一个选呃,i k问题我们可以有非常简单的这样一个求解方式,但是更加通用的一些问题,比如说一个机械臂。
那可能有只有四个四个五个关节,我们的iq问题是说,我们是希望这个最终末端点这个关节,它的位置能够能够被移动到我的一个目标位置,同时这个末端点这个朝向也是能够达到,我希望那个朝向。
就是我们一般需要去求解一个ik问题的这样一个基本的定义,本来当这个过程中这个朝向来说,如果说我们考虑要求这个末端点是一个是一个抱枕的,那其实朝向我们这这个修改朝向是一个比较容易的一个问题。
那修改这个位置是相当于比较难的,那这里我们先暂时先忽略到朝向这个这个问题,我们只考虑这个位,置就是像让这个末端点的位置跟我的目标位置及,这个达到我的目标位置啊,去去求解这样一个i k问题。
那这个这个那在这个里边,因为我们对这个每一个关节的旋转,我们把它参数化形成一个,比如说可以用fate来表示这个参数,那这个末端点它的位置整体上我们可以写成是一个函数,这个函数的参数是这个我们每个点啊。
每个关节的旋转,这个参数的这个放在一起,然后它的这个函数的值是这个末端点的位置,就说我们这个iq问题等价,于是说我需要去找到这么一组参数,theta,它,使得我的这个末端点的就目标点的位置减掉fx呃。
它的值等于零,那其实就是这是一个求根问题,当然我们知道这样一个求根问题,我们可以等价的把它转换成一个优化问题,我们知道这个当这是一个二次函数。
我们其实可以很容易很容易很容易的这个知道就是这个优化函数的啊,这个函数的最优最极值是刚好是等于零,也就是说当f x但末端点跟x重合的时候,那这个优化问题取得取得极值,那这个优化问题呢。
其实如果说我们把它写这个形式化的,把它这个画出来,比如画出,函数图像它可以描述成这样一个方式,这样这样一个形式,就说比如说我这里有两个参数,就要连个c才一,然后我们可以把这个y轴关于函数值。
那它大概是这样一个形状,当然实际上这个只是一个解决一个失意时间的形状,可能是一个非常复杂的一个函数,那对于k来说,它本质上它的它这个函数目标函数值,其实是大概可以写成这样的一个形式。
那当这个图像这个三维图像不太好看了,我我我们会我们为了简单说这个描述一些来简单,我们可以把它做一个正向的,这从上到下,来一个投影,投影了一个二维平面,这样的话我们在这个这个描述的时候会更加容易一些。
这里每一个圈其实对应的是这个目标函数的一个等值面,然后越往里这个目标函数值越小,这是我们的这个函数图像的一个假设,那我们为了求解这个优化问题,通常来讲我们是从一个初始状态开始。
就比如说我现在关节这个角色已经有在一个初始姿势了,那从这个初始姿势开始,我们需要去寻找一个目标姿势,就这个优化的中心点,优化的这个最优点,它能够使得我的ik问题有解啊,得到i。
k问题的解也就是我的目标点达到了我的目标位置,那通常来讲这个优化问题对于因为这是这个目标函数,是一个非常是一个高度非线性的问题,所以说我们很难去进行,去找了一个算法,它能直接就得到。
直接就计算出最终那个中间点的位置啊,最优点的位置,所以大部分情况下我们会使用一个迭代求解的一个方法,那迭代求解它其实是一个是一大类的方法,那从简单来说他的他的这个这个这个迭代过程。
这是我从一个初始点位置,从这个c0 开始,我首先去找到一个方向,那个,方向就可能是最有最有希望来提高我的目标啊,目标函数的这个值的这样一个方向,那接下来呢我沿着这个方向移动一个距离。
移动一个合适的距离啊,这不合适的距离是多少,不同的方法有不同的这个不同的计算方式,然后计算完之后呢,我们一定把这个参数更新到一个新的位置,然后我们再继续重复上面的过程,也就是说再去找一个新的方向。
然后再沿着新的方向进行移动,然后来实现我们这个来来来来来,继续来进一步的更新我们这个目标参数的值,然后我们可以不断的重,复这个过程,直到我足够的接近了我这个最优点,那这个足够的接近。
我们当然有也有相应的这个判别方式来决定我是否已经足够接近了,那当然很多情况下,因为这是一个非线性函数,我们其实使用不同的不同的算法的情况下,我们会发现可能它经过很多次迭代之后,他还是没有足够接近。
那我们还是需要在在一定的这个循环之后,一定得迭代次数之后,我们需要停止我们的这个迭代,然后返回当前的值,我们可以认为它是一个坐标值啊,最优值,或者说我们也可以认为可能这个方,程就是没有解。
那其实这个迭代方法有很多种不同的方式啊,就是我们就是关键,就是说我们需要去如何去找到一个比较好的这个方向来更新我的参数,另外一个问题就是我们如何去找一个比较合适的距离去更新的参数。
那这个方向首先有很多种不同的找法,其实最简单的找法就是因为考虑到我们这个实际上每一个,比如说我这个这个目标函数,它是由啊若干个,比如说有两个参数来互相定义的。
那我们其实完全可以就认为这个参数所对应的一个坐标轴的方向,就是我去更新,我的这个旋转啊,更新我这个参数的方向,那这个对应的方法就是所谓的坐标下降法,他过他的做法就是说我每次是沿着这个坐标轴。
沿着某一个坐标轴去更新我的参数,也就是说在这个更新过程中,我是沿着这个坐标轴去做一次查找,我更新的距离,是是是使得我更新之后,这个点在这个坐标轴这个方向上所对应的这个目标值最小的那个点。
那我当时完成这个更新之后呢,我可以换一个坐标轴,沿着另外一个方向做同样的这样一个迭代的下降,那我们可以不断的做这样一个不断,的交换我这个坐标轴,然后最终逐渐的我这个因为我每次都是保证这个是一个新的点。
新的坐标点是比我之前那个坐标点,它所用的这个坐标啊,目标函数的值要小,所以可以保证每一次坐标下降的过程,我的值总是在不增,至少是在不增加的,虽然他可能不一定真的减少,那至少是在不增加的。
所以说和不断的这样的代,我最终会得到一个点,那这个点它的它的这个对应的这个坐标值,对应的这个这个目标函数的值应该是一个最优的,至少是一个局部最优点,那当然我们这个过程中。
我们需要去不断的切换我们的这个呃下降的这个坐标的方向,那如果说我们只有两个坐标值,只有两个参数,那这个其实比较简单,但如果说我们有若干个,比如说大于二个,比如有三个,或者是比如有十个不同的参数。
那我们其实有十个不同的坐标轴需要进行选择,那如何进行选择呢,其实最简单的一种方法就是说我们可以循环的依次的去去,去用每一个轴进行下降,比如说第一次沿着第一个轴下降,第二次沿着第二个轴下降。
和第十次沿着第,十个轴下降,然后第11次我们就循环回来,再次沿着第一轴进行进行这个坐标下降,那这个对应的方法就是所谓的循环坐标下降法,用ccd的方法,那这就是我们cc用cc的方法去计算i k。
去求解这个ik的这个优化问题,那其实就是ccd ccd i k就是我们循环坐标下降的啊,这个还可以方法,那具体来说呢,就是说比如说我想去把这个坐标轴啊。
这个机械臂我想让他这个末端点移动到我的这个目标位置,那我们可以通过什么,通过一系列的这个这个坐标下降来完成这一点,但比如说首先,我为了能够把这个x第四个点啊末端点移动到目标位置。
我们首先旋转一下第三个啊关节,然后选择第三关节,使得我这个末端点就是第三关节和第四个末端点的连线,跟我们第三关节和目标点的连线重合,那样就完成了一次这个这个坐标下降。
其实感觉这好像这这这跟坐标它有什么关系呢,其实可以想象一下,因为第三关节它的这个参数本身它就是带这个,它的旋转本身是一个参数,所以实际上我相当于是沿着这个参数这个所有的取值范围。
就是它这个坐标轴去寻找一个点,这个点使得我的这个目标点的位置啊,末端点的位置跟这个目标点的位置尽可能进,那我们知道这个参数取值,比如把这个在整个取值范围内,每个点都取值之后。
我们可以看到它这个末端点的末端点的位置其实是落在一个圆圆周上,那这个圆周上跟这个目标点最近的那个点的位置,肯定是这个目标点跟圆心的连线和这个圆周的交点,那其实我们前面这个移动的过程。
旋转的过程其实就是刚好是找到了这个点,所以它对应的就是说我沿着这个第三个关节这个旋转参数,这个坐,标轴的一个下降的一个结果,那做完这件事之后呢,那我们可以选择下一个坐标轴,比如b对关节三做完旋转之后。
我们对关键二左右旋转,那同样的我们会把这个坐标轴的啊旋转目标,这个选这个旋转的这个目标,就是把让这个关第二个关节跟末端末端点的连线,让它重合于呃,第二个关节和我这个目标位置的这样一个联系。
那当然这里其实还有另外一个问题,就是这个旋转,比如一开始这个旋转我们该如何进行,其实这就是我们不断提到了好多次的这种如何把一个向量旋转让,它跟另外一个向量重合这样一个问题,我们其实已经出现好多次了。
那我们可以做一个插成,得到它的这个旋转轴,再做一个点乘,得到它这个旋转角度,那我们就完成了这样一个旋转,ok那我们依次对从第三周开始,第三个关节开始旋转,第三个轴啊,关节旋转,第二关节旋转。
第一个关节在旋转啊,不好意思,在旋转第零个关节诶,那我们就完成了一次这个啊坐标下降的这样一个循环,那接下来继续,我们回到了第三个关节,我们的记,然后再进一步的进行后面的旋转,首先来说这个ccd这个。
方法呢就是我们可以描述成他就是说依次从某一个顺序开始,从第三开关节开始,也可以比如说从第零关键开始,按照某一个顺序进行依次去循环去旋转每一个关节,然后每次旋转都使得我这个关末端点的位置。
跟我这个对应关节和那个目标点的这个这个连线是重合的,那就完成了一次下降来完成,这个整体来说每次旋转,因为它就是一个差成和一个点乘来完成,所以它整体来说计算量是非常非常相当于是非常小的。
而且实现起来也非常容易,而且实际上运行起来也是非常快,的,那当然它有一些特点,可以可以说是可以说是you,可以是feature,也可以说是bug,就是说他总是第一次移动。
那个关节相对来说会比其他关节移动的更多一点,然后另外这个坐标下降,循环坐标下降,本质上来说它是不会考虑我这个函数本函数的性质,我们目标函数的性质。
它就完全是基于一个这个啊坐标纸的坐标方向来进来进行这个更新,所以有些时候他可能这个在这些特定的情况下,他是这个他的这个收敛速度会比较慢的,你可能需要很多次礼贷才能得得到解,甚至可能很多次迭,代都没有解。
然后另外就是说你会发现,就是他最终那个输出这个这个因为我每次迭代之后,所最后得到的这个关节这个角色,他的姿势可能并不是一个非常稳定的这个状态,就是什么意思呢,就是说比如说我这个末端点不能点。
我从一个位置逐渐的向另外一个移动,然后我在每一然后在移动过程中,我不断的求解这个c c d,然后可能会发现这个ccd求解,说这个解它可能不是一个非常稳定的一个姿势,它可能会在不断的抖动。
那这个其实也是ccd的一点一点问题,那当然c d我们应该也提到了,就是说它可以从你可以选择任何一个方向,比如说我可以刚才刚才这个过程,实际上我是从3210这样的一个顺序去依次去一旋转每个关节。
那反过来其实我可以连着另外一个方向,0123这样的顺序进行循环,其实它也可以得到另外一个也是都可以得到ik的解,那当然还是我们现在这个就提到了c d的性质,他总是第一个关节相对来说会移动的多一点。
但是其实在我们实际的实现过程中,我们会根据一些我们的这个这个这个关节,就这个这个这个这个机械臂或者这个链,这个链条的细这个性质来选择一下,到底应该到底应该怎么去旋转,就比如说当然对于人来说啊。
比如说我移动,比如我伸手去够一个物体,那我可能我的肩膀的移动的范围,肯定是比我这个这个手的移动范围要小一点的,那我实际上我在做这个ccd的过程中,我其实应该从先先旋转肩膀,然后再旋转手肘。
然后依次这样的一个循环方式,那这是一个啊ccd的一个具体实现的问题,那当然前面提到了,其实ccd做的这个循环坐标下降的方法,它其实是没有考虑到我的目标函数的一个性质。
那其实如果说我们考虑到目标函数的性质,我们其实可以实现更加快一点的,就是更加有效,更加这个效率更加高一点的这种啊优化方法,比如说一个最简单的优化方法就是所谓的梯度下降法啊,这个梯度下降好。
可能大家在其他地方,就比如说我们现在这个目标函数是这样一个形式,那么可以计算它的梯度,那他梯度是什么呢,他梯度就是说这个目标函数它是一个标量,它是一个数,那这个数这个标量函数啊。
对这个参数里边的每一个分量进行求导,然后把这个导数值写成一个列向量的形式,那就对应的那个就是这个目标函数的一个梯度,那当然其实我们可以写成这样,这这个从形式上来说它是一个类型啊。
它其实也可以写成这样一个jcb,我们后面可以会进行问题啊,这个假发比矩阵的转置这样一个形式,那一个函数的梯度代表了什么呢,这个它是代表了这个函数,这个函数增长最快的那个方向,就是在某一个点。
我沿着梯度向方向移动,那我这个,函数只是增长最快,所以说梯度下降其实是反过来的,就是说既然你是增长最快的方向,那我就沿着你的反方向移动一下,那就因为本质上我是想这函数变得更小。
所以我其实沿着的反方向移动一下,那样的话我期待我的函数其实是在变小的,那当然在这个过程中,我们其实这个不长,我们前面这个这个gradient就是这个梯度方向,梯度只是给了一个方向,我们需要确定一个不长。
我们不但要提到,那我可能会越过,会错过我的这个极值点,那甚至说我可以如果是听这个几率很大的话,是可以不断的在这个节点之间之间反复横跳,那最终还是不能找到这个这个不能落在这个节点位置。
甚至原来说如果说这个函数性质比较差的话,那我可能会可能会这个一次走的太远了,跑到一个啊,跑到一个非常不好的,地方去了,那这种情况下可能会导致我整个优化过程会会崩溃。
所以说通常在我会选择一个相对来说小一点的啊,能保证我这个这个算法是收敛的,所以总体来说呢我们这个所谓梯度向量法,就是说我们更新每次它是一个迭代方法吧,我每次迭代都是把我的当前的这个参数值。
沿着我的梯度方向的反方向移动一小段距离,那当然这个梯度对于这样一个函数来说,对我们i k这个函数来说,如果对这个函数计算它的梯度,它刚好是等于里边就是这个我们前向运动学这个函数。
前向运动学函数对于每一个旋转参数的导数,然后乘上当前的这个位置,当前这个位置跟我目标位置的这个距离差,他就定义了好这个方向,那前面这个导数我们通常可以把它写成一个矩阵的形式。
就是就是所谓的夹克雅克比矩阵,所以总体来说呢这种计算方式就这种更新方式,其实我们也通常会把它叫做叫做jacobin transpose method,就是雅克贝学院的这个转制这样的一个方法。
那就是它本质上等价于是我们的这个规定的方法,他俩是一个等价的方法,那当这里有一个问题,就是这个亚克比矩阵啊,就是刚才说的它其实是每一个函数,那这个时候就因为这个f我们知道它是一个前向运动学的函数。
它的输出是一个点,那这个点对我们前面这个定义来说,它其实是一个三维的一个末端点的坐标,那他的雅克比矩阵其实就是真的是一个矩阵,比如说对一个如果他说是一个点的话。
那这个亚克比矩阵应该是一个三乘以n的一个矩阵,这个n代表的是我这个整个这个机械臂上所有参数的这样一个数量,然后这里的每一列每一列,比如说第i列,那它对应的值应该是f f就是这个坐标值的三个参数。
fx fy和f z分别的对这个对应的这个thea进行求导,那其实它它也刚好是就是正好是三个值对应的这一这一列的三个数,就是牙科牙科比矩阵的这样的一个一个一个定义,那这里相当于有一个问题。
就是说在我这样一个定义的基础之上啊,情况下,我该怎么去计算我的雅克比矩阵,或者说我该怎么计算出我的梯度,其实最简单的方法就是说也不是最简单的方法,就是其实因为我们现在有很多比较比较方便的。
其实我们可以比较容易的用这个框架来实现一遍,我的这个前项运动学的计算,那接下来实现完之后呢,我们其实可以利用这个框架,比如ptouch或单词flow,用这些框架本身自带这个自动求v的。
自动求求积分微分的这样的一个方法来计算出我的这个梯度,或者说我的压力比矩阵,那接下来建这个之后呢,我们只要要比人之后,那就只要去做这个啊t t下降了哎,就是完成了我们这i k的计算。
那这是一个非常简单或者非常偷懒的方法,那实际上也是对于更加可能一些很复杂的系统来说,其实这种方法反而是更加更高效率的方法,比如说sim link,它是可以自动的根据比如设计一个c之后。
它可以自动的生成一个比如c的c加加的代码,而这个代码就刚好是可以计算出我的这个亚比矩阵的值,其实这个我们其实一直都是都是用这些这一类的工具来实现的,但如果说我没有这样的工具,在这情况下我们该怎么算呢。
当然另外一种计算方式呢,就是用所谓的有限差分的方法呃,完成这样一个计算,那具体来说是怎么做呢,就是说如果说我要计算这个f跟这个某一个参数,比如非得一这个参数,它的导数我们可以先做一遍前运动学。
用当前的参数值做一遍前线运动学,然后呢我把这个c t一加上一个很小的一个偏移,然后再做另外一篇前言导学,那这个时候我们知道它的导数的值大概是等于,大约是等于二次函数值的差,然后除以我加了这个偏移。
也就是说其实对应的就是两次,我在这次计算模拟脸的位置是x,然后加上点偏移之后,我计算一个新的模拟点,位置是xp 2,那这两个之间的连线啊,这两之间的差,然后除以dbc。
它其实刚好也是对应于这个ab矩阵的这一列,那这个上的所有的值,那当然对于前向前这个呃有限差分来说呢,实际上我们是需要依次对每一个关节,每一个参数去做一遍这样形象的这个做一遍前一同学,然后做一遍差分计算。
然后逐渐的把这个夹克比矩阵每一列给填起来,那当然整个这个计算过程是相对来说还是比较大的,那另外一个就是说实际上对于i k问题来说,我们其实还有另外一种简单的计算。
我们这个甲比真的方法就当这里我们先举个简单例子,就是我们假设这里的所有的关节都是转的,就是一个单字由度的关节,它只能沿着某一个轴旋转,那比如说在这样一个状态之下。
比如说这个关键一它的旋转轴在世界坐标系下的这个随便转轴是a1 ,然后然后我其实我会沿着这个轴进行旋转,那么接下来呢就是说因为这个1k转的嘛,那我对一的这个旋转就是关键一的旋转。
它的参数其实就是沿着这个轴的旋转角度,那在这样的表示之下,这个雅克比对这一列有说末端点的位置,受这个旋转在这个旋转之下产生的这个位移是多少,我们其实可以怎么计算呢,其实回想一下。
比如说我们想要做一个旋转,比如说我在这样当成一个状态下,我把这个a我沿着这个坐标轴,沿着这个旋转轴转了一个非常非常小的角度,一个dfa,那我会把这个末端点其实在这个旋转过程中,它会移动到一个新的位置。
x撇其实回想一下,上节课我们讲这个旋转公式啊,就是这个罗德里格旋转公式,我们可以知道这个x撇的位置差不多,它等于是说差不多是跟着我这个呃ai这个旋转轴x乘x,它给出了一个方向。
差不多是跟那个是跟那个沿着这个方向移动一点点距离,跟着是是是是是是差不多的,所以实际上我们可以直接对这个楼里格斯公式进行这个做一次做一次,这个就把它处就是首先把这个差值除以除以我这个旋转的一个角度。
然后把这个旋转角度让让他这个取对领取极限,那么其实可以很容易得到说是什么,就是说这一点对应的这个偏导数,也就实际上就是说我这个轴啊,整个这个旋转位置对我这个选这连着这个轴的旋转,求这个偏导数。
或者说我们讲牙背矩阵这一列,他刚好就等于这个坐标轴全局的位置啊,全局的这个陈述向量叉乘以我这个末端点到这个关节点的连线,这个向量就这两个向量做一个插成,就刚好是说我这个啊这个牙比矩阵的这一列。
那对应的其实我们这只是计算比例了,对于另外一个关节,比如说对于二这个关节,我们需要同样做这个计算,只不过这个时候我需要用这个关节二,他的这个啊旋转轴在全局坐标系下表示。
然后叉乘上关键二到这个x到我目标目标点啊,这个末端点位置这样的一个这样一个向量做这样一个差长来进行计算,所以总的来说这个对于这个呃ik问题,我们知道就是在这样一个性质状态的表示之下。
我们可以逐次的对每个jt计算这个插成,然后完成对这样一个夹克比矩阵的每一列的这这个这个这个计算,那当然前面那个一只假设说我是每一个关节是一个性质状态,它只有一个旋转轴,但如果说我是一个爆炸的。
比如说它是一个爆炸,它其实它是有三个选择的,它是有一个三个参数的,灵安说我们可以它旋转轴是不固定不确定的,那在这种情况下,我们该如何去计算这雅克比矩阵的每一列,但这个计算其实我们还是有些要求的。
就说我们是只能是如果说我们在假设我这个旋转,那这种情况下,我们其实可以用我们前面的这个关于根据性质照用插值的方式计算,压压压v矩阵的方式来进行计算,就比如具体来说。
比如说我我把这个我我是这个爆炸这个旋转的参数表示是一个欧拉角,它是一个x rx乘以r y乘以r z这样顺序的一个欧拉角来进行表示,那这种表示我们可以近似的,我们可以等价地认为是说什么呢。
是说我有一个三个hd,这三个性质上它的这个重合在同一个重叠,在同一个位置,都是重叠在关节,重叠在关节一的这样一个位置上,然后在它分别旋转带来带来了我整个这个关键一个旋转,那他这样的基础之上呢。
我们知道因为这个欧拉角有三个坐标嘛,所以说我对应的其实是我雅克贝整理的三列,就是分别对f分别对这个cx fy和fate z分别求导,那对应的是这个贾克比真的三这个三列,那每一列其实都是对应的这个坐标啊。
这个这个欧拉角这个旋转的坐标轴,然后叉乘上我的这个目标点位置,减掉当前位置就是r这个距离,然后得到的是我们这个末端点这个这个牙比矩阵这个坐标值,或者我们显示器这个公式的话,其实就是这样子。
就说push f就是我的末端点位置对我这个某一个欧拉角,某一个欧拉角的导数应该等于对应的欧拉角的轴差,乘上我这个末端点到这个关节点这个这个距离这个向量,那得到的是我这个对应两个比矩阵那一列。
那么这里需要注意的是什么呢,就是说我们这里这个插成计算,我们其实是要求这个坐标轴是在世界坐标系下的这个这个向量表示,那对于我们这样一个欧拉角表示来说,我们其实比如说第一个旋转,我们分别是沿着x轴旋转。
再沿着y轴旋转,再沿着z轴旋转,这个旋转过程中,我们知道第1x的旋转轴,第一个坐标轴它在世界坐标系来表示应该是x轴,然后因为s是一个局部的,它其实是一个局部的一个坐标轴,我需要把它转换到世界坐标系。
我应该用它乘以呃,我这个当前关节的腹关节的这个这个旋转,那这个朝向,那这个选这个,那y轴呢,其实y轴我们并不能并不是直接把y轴旋转过去,因为在我们这个欧拉表示里边,y轴实际上是在x轴旋转的内部。
就是局部进行旋转的,所以说是这个y轴旋转时候,它的坐标轴的这个在全局坐标系的这个向量表示,应该是它的副节点负关节的朝向乘以x轴的旋转之后得到的这个方向,那其实是对应的我y轴这个y轴这个旋转方向。
那类似的z轴,因为它是最后一个旋转的轴,它应该是它的最大负关节乘以x的旋转,那乘以y的旋转,然后它的再乘以z轴,那其实这个才是我的最终的z轴的方向,所以实际上有这样一个表示方式,我们其实是要特别注意的。
就是它跟微有点不太一样,我就不能直接的简单用x y和z3 个轴距差成我的这个r的方向,而是我需要分别把x y x z转换到选角坐标系,那这个时候排在后面的这个轴,其实它会受到前面两个轴旋转影响。
那当然这个欧拉角其实有不同的表示方式,如果说欧拉欧拉角的表示方式是这个r x r y,然后又沿着x另外一个i就就进行旋转,它实际上这三个坐标轴计算也是类似的,只不过我们比如第三个是x。
那我们其实这里就是把x做了一个做了一个这个坐标,变换到一个新的坐标轴的位置,ok那我们当我们计算完这个雅克比矩阵之后呢,我们其实前面提到了这个实际上我们的这个计算过程,我们的这个呃作为梯度下降。
那其实就等价等价,于是说我沿着雅克贝矩阵的转置,然后乘以当前这个摸都摸到脸跟摸点和目标点之间的偏差,这样的算出来这个方向进行这个去移动我的参数值,那整体来说呢它是一个一阶的一个优化方法。
首先它肯定它从效率上来说,它还是会比啊,就迭代次数来说,它还是会比那个通常来讲会比ccd的方法要快一点,但是呢缺点是说他毕竟还是还是本质上是一个一阶的方法,就是它的呃收敛性还是相对来说不是非常快的。
另外就是说每次迭代我都需要重新计算一下你这个牙比矩阵的值,所以实际上整体算计算量会比c c c c d的方法要大很多,因为我们知道每次计算雅可比矩阵我至少要算一遍,对每一个轴计算一遍,这个差成整体来说。
这个计算是相相对来说比较大的,但是好处是它收敛会快一点,ok那其实我们前面也提到了,就说我们知道这个对于一个目标函数来说,它的梯度的方向对应的是说它增长最快的方向,或者在再放再透一点。
就是说我我是说但它代表的是一个做这个变化最快的这样一个方向,所以说如果说我在某一个位置,在某一个参数,比如说这个c2 星的一个位置,我们发现它的它的梯度是零,这个代表什么,代表在这个位置上。
我不管怎么移动,我的这个函数值都不会发生,都不会发生变化,那它对应的是一个局部极值点,那当然不一定不一定是最小点,那至少是一个局部极值点,那这个局部极值点这个条件也就是这个green这个t等于零。
其实是对应的是这个极值点的一个一阶的这个这个最优条件,那就是比如说这个一个非常简单的例子,我们的所谓的二次二次规划问题,就是说这create procreatic program没问题。
就比如说我这个目标函数是一个二次函数,就可以表示成一个c和t乘以a乘以fa,然后乘以这样一个形式,那这里我们就对a有一个基本的要求,就是我们要求a是一个正定的,那正定有什么,要什么什么什么意义呢。
回想一下,其实a是正定的表示,首先它是一个对称阵,其次对于任何一个thea,我的这个二次形式是指大于零的啊,应该是大于零,不是大于等于零啊,对大于零,对任一个它应该是大于等于零的重开。
那这样一个这样一个目标函数,实际上我们可以计算一下它的这个啊这个这个梯度啊,其实可以可以算出他应该等于a c a加上b,所以说再叠加上我们的这个最优条件,我们要求这个梯子等零。
那么可以很容易地计算出对于这个目标函数来说,它的极致,它的最优最优的值,所有的参数应该是这个a a a a a加b等于零,求解一下,其实它应该是a的转a的逆乘以b啊。
其实这个这是一个二次函数的极值点问题,那对于我们这样一个i k问题,我们这个这样的一个函数来说,我们其实也可以把它转化成一个二次函数的这个这个急求极值的问题。
那这个求极值的方向其实也就代表了我的一个更新,我这个参数的方向,就比如这在这里边我们其实也用了一点一点点这个呃微积分的知识,就说我们对于一个f这样一个前向运动学,我们知道它是一个非线性的函数。
我们可以把它在当前点就四点附近作为一个一阶的泰勒展开,那大概可以写成这样的一个形式,那对应的是应该是f在c的点,当前点的位置加上亚克比矩阵乘以一个一个偏移的这样一个方式。
然后把它带入到我们的这个目标函数,那其实可以得到我们的目标函数其实是可以写成这样一个形式,它是一个近似,然后呢在如果说我们要求这个根据我们的第一集的这个最优条件,我们要求这个这个梯度应该等于零。
那在这种情况下,如果说啊我们也他t6 点,我们可以把它简写一下,简写一下,因为这个文明这个delta我们一直用它来代表当前这个目标点的位置,跟这个这跟这个当前末端点的位置跟目标位置的差。
然后在这样一个条件下,如果说这个gt乘以j它是一个可逆的矩阵,那我们其实很容易可以很容易得到我们c的更新方式,应该是沿着这样的一个矩阵,这样的一个计算所得的方向进行更新。
你就可以可以可以找到一个很快的找到一个更最新最优解,那这个问题就是说这个这地上你这是不是可逆的,实际上对于我们这个一个点一个末端点的这样一个问题来说,我们会发现它的牙科比矩阵是一个是一个变胖的一个矩阵。
那这个变胖的矩阵你会发现j t乘以j,它是一个更加一个大的一个矩阵,然后他本身肯定是肯定是不可逆的,因为这个我们知道一个矩阵是可逆,你知道他的这个他是满制的。
然后这个矩阵本身它的它是它是它是只有航班成没有列满值,他乘起来之后,我们会发现一个变成更大的矩阵之后,这个矩整个矩阵的秩应该是不会比这个这个贬矩阵的值要大的,所以他肯定是以不满值的矩阵。
所以说整体来说是不可逆的,但是怎么办呢,但是其实我们想想,如果反过来啊,其实虽然他是个变胖的矩阵,所以g1 t乘以g也是一个更大的矩阵,这个矩阵肯定是不可逆的,但是如果说把如果。
但是如果计算g乘以g t,我们会发现他其实乘车结果是一个3x3的矩阵,这个矩阵是有可能是可逆的,所以如果说我们在假设这样一个矩阵是可逆的情况下,那我们可以把这个上面的东西稍微做一个变换。
我们两边都乘以j,然后对应的左边j乘以g t和右边的j乘j t,我们就可以把它约掉了,那其实他最终得到一个结论,就是说这等价于在这种情况之下,他这个前面这个东西等价,于是说j乘以c的减c的零。
然后等于我的负的dota这样的一个方式,还记得这个c减c0 是我的更新方向,然后delta是我当前的目瞪眼的位置和目标位置,这个差,或者说我们再具体点,其实对于对于比如说回到我们前面那个问题的定义。
其实刚刚我刚才说的,实际上c的减c的零代表了我的这个参数应该向什么方向去更新,然后右右侧时间是我当前就这个机械臂末端点的位置,跟我目标点位置,这个差是我的,这个是我右边的这个右边的值。
那么这个其实我们我们其实需要去做一个计算了,我们就需要去找这样一个方向,怎么找这个方向呢,实际上我们可以使用这个伪逆的方法,因为j因为这个旋转矩阵啊,这个夹克比矩阵是一个不是一个方阵。
它是一个长方形的正,那我其实对方面来说,我可以把它求逆,然后算出这个方向来,对于不是一个方阵的情况,我们可以用尾翼违逆它的定义,其实是可以写成g t,然后里边是j j t的逆,然后形成这样的一个形式。
当然这是对于对于一个扁胖的一个举人来说,他的伪逆是可以这样实现的,那算出这样违逆之后,实际上我们就可以得到一个啊更新的方向,那得到一个更新的距离啊,这个这个过程因为我们使用了这个雅克比的女呃伪逆。
所以实际上我们通常会把这个方法叫做这个雅克比啊,甲亢病verse的方法,那回到我们前面这个问题啊,就是啊这是我们的更新方式啊,我们刚好会把它交到这个亚克比扎克病verse的方法。
那回到我们前面这个这个这个这个这个方法,这个就是这个前面这个问题,就是我们前面这个推推高斯牛顿法这个地方,我们是要求他这个gt乘以j是可逆的情况下。
我们可以用这样的一个方式来计算出我们的这个这个更新的方向,刚才我们提到,对于我们单单点的这i k问题来说,它这个东西肯定是不可逆的,但是他在什么情况是可逆的,就是我们对单比来说,我们知道这个是这个矩阵。
是一个比矮胖的矩阵,所以他肯定j t乘以j是一个不可逆的矩阵,但如果说我这个i k是又是一个多点的i k问题,比如说还是个机械臂,我想要末端点到一个位置,我想中间第三个关节到另外一个位置。
然后同行第二个关节到再到到到到x2 这个位置,然后在这个基础,在这个同时呢,我们用这个每个机械臂,每一个关节又是一个hinge转,它只能沿着一个字为轴旋转,那在这个情况下。
我们可以定义一个f就是前向运动学的函数f的,它计算出三个末端点的位置,那它是一个久违的一个向量,但是我的这个整个的参数范围,参数空间只有四,因为只有四个关节,只有四个关节,然后每个关节的这个作为停止转。
它只有一个旋转自由度,所以说他最终的这个雅克比矩阵其实是一个9x4的,一个一个高寿的一个矩阵,那这种情况下gt乘以j大概是一个可逆的状态,所以总结一下呢,这那这就是在这种肯定状态下。
其实我们可以用这样的方式来更新它的这个这个位置,但实际上这种方式但对于一个高胖的高射的一个举证来说,j t j t j乘以-1啊,对这逆,然后乘以这个g的转制。
这样的一个这个计算其实也是这个两个被矩阵的一个尾逆,所以其实lv矩阵伪逆其实对于有这个亚比矩阵的大小的形状,它会有不同的形式,也就是说对于一个矮胖的矩阵,那其实是前面这种形式,对于高数的矩阵。
它其实是后面这种矩阵啊,这种形式,而且ipad矩阵其实对应于是说我一个啊就是一个单点的一个,一个ik问题,或者说一个这个这个欠约束的一个i k问题,然后对这个高效的一个矩阵。
其实对应的是我们可能有很多点,但是我这个约束过多,以至有可能没办法达到每一个点,我需要去找一个最接近某一个点的这个这样一个ak,就这样一个姿态,他对于这个高寿的问题,然后但实际上通常来讲。
因为这个通常一个伪逆计算,我们得到了一个更新的方向,然后我们可以更新我的这个参数的值,使它达到了我们这个最优点最优最优的方最后的位置,但实际上因为我们前面这个算法过。
程中我们其实是本质上是对于一个高度非线性的函数,把它线性化了,线性化通常来讲其实只是在这个函数附近才是有效的,然后如果离得很远的话,那个线性化误差会非常非常大的。
所以从此所以实际上我们其实可以在这个基础上再加一个learning rate,来避免我一下走的太远,然后导致我这个这个解围崩溃这样的问题,那总体来说呢,其实这种方法就是就是就是从从优化的角度讲。
他其实对应的是所谓的高斯牛顿法,那通常来讲,因为我们考虑,的这个一接近四,它会比我简单的green descent,就是梯度下降法或者甲和平transpose方法要快一点。
当然有一个问题就说我其实是要计算一个矩阵的逆的,不管是j乘以g t的逆,或者是gt乘以j的逆,它总是要计算一个逆,那这里就是一个问题,就是说这个矩阵我要求逆的这个矩阵。
其实我们还是无法保证他就一定就是可逆的,甚至他有可能是可逆的,但是他是接近于不可逆的,在这种情况下,我对他求逆或者是这个球逆本身是没有意义的,或者是说这个球你会给我一个非常非常巨大的一个一。
个矩阵就是每个数都会有很大,那它会对应的什么呢,它就对应一个非常大的一个移动,那他会带给我整个优化带来一个很不稳定的效果,所以说呢为了解决这个问题,我们其实可以在这个球逆之前。
在里边再加上一个所谓的阻尼项,那构成的这个方式,就所谓就是就是就是叫dt这个java verse方法,那这个阻击下的存在其实保证了我这整个这个内部这个矩阵是可逆的,而实际上我们其实可以简单的推到。
我们其实很容易的证明,其实在有阻尼向存在的情况下,这两种这两种尾音的方呃,情境的结果应该是相同的,然后这种方法加上阻尼,像这种方法其实也有另外一个名字叫叫lemon book macqua的方法。
但是这个number看还有另外一种形式,就是这个这个总结下来后面的成绩不是一个单位者,而是j乘j t这样的方这个这样一个形式,但总体来说我们知道其实这是一个它也是一个常用的一种优化方法。
它会首先它避免了这个啊高斯牛顿法,在这个中间这个矩阵不可逆的情况下,它会带来不稳定的问题,另外呢其实相当于在狼在这个单品相非常非常大的时候,实际上这个方法。
等价于是一个qq下降法的只是一个也是我们的一个改进方式,从另外一个角度讲,我们加上这个阻尼线等价于什么呢,等价于说我这个函数,我的目标函数后面我加了一个约束项,加了一个正则项,这个正则项的效果是什么呢。
就是我知道我想把这个角色这个x这个目标点位置移动到一个目标啊,末末端点的位置移动到一个目标点,在这个过程实际上我每个关节都会发生旋转,但是这个正则项目要求是什么呢。
是要求我这个每个关节它用最可能少的这样一个旋转来实现这个移,动他实验室时间体体现了这样一个效果,但这个政策下我们其实可以做进一步的这个可以用它做很多的事情,比如说这就是在前面这种方式里边。
其实我这个每一个关节它的这个权重是相同的,意思是什么呢,就是我每个关节可以用相同的这样移动,就或者说我尽可能让每个关节使用相同的移动去,就去实现这样的一个目标点,呃,这个ik问题就解决解这个iq问题。
而实际上我们可以给每个关节以不同的选中,比如说我就想让他那个我不想让他这个髋关节移动很多,就是root关节,移动很多啊,但是我想让他的手部或者胳膊移动多一点,那么其实相当于我们其实可以给这个末端点啊。
root关节给一个很大的一个权重,可能一个很大的w0 ,然后反而是给这个中间,比如第二个关节一个很小的ww 2,那它其实最终的结构效果就是说我的这个拉布林,就是第零个关节会尽可能的少一动。
而尽可能多的用这个w2 ,这个就用这个第二个关节来实现来进行旋转,来完成我的ak问题,实际上其实可以某种程度上控制我的这个旋转过程中的这个实际效果,那前面我们其实还是简单,就是以一个链条。
一个机械臂为例,我们讲了几简单简介绍了几种这个ik的算法,那实际上对于一个全身这样一个来说,我们通常来讲对一个整体的一个角色,因为我们知道它是一个树形结构,多个链条构成放在一起,组成了这么一个角色。
那这个艾克在这个上面,其实我们的iq问题通常来说可能经常会比较复杂,就是我们可能同时需要这个手移动到一个位置,然后另外一个胳膊移动一个位置,然后另外一个角移动到某一个位置。
那共同构成的这么一个act的问题,那实际上这个问题来说,我们同样是可以写出它的,这个把它写成一个一个一个一个优化函数的角色,一个优化问题,它就是那每一个优化问题,它是由若干子问题构成。
每个子问题都是一个啊前向运动学函数,减掉我的目标目标点的位置,那这个叉我作为我的优化目标,比如说我这有三个目标,那么其实这个三个这个啊优化问题,这个这个形式。
那其实就是前面有三个这个x的平方模的平均长度,平方距离平方,然后最后加一个正则项,类似于这样的一个形式,那当我有时有了这样一个用函数之后,其实我们可以加一些其他的约束。
比如说呃可能我想让某个关节用的用的更多一点,或者说想让某个关节这个就是基本不动,那么其实可以解除同类似的这样一个优化函数,那在有这个优化函数之后呢,那我们其实可以计算它的牙科比镇,我们可以使用雅克比啊。
in贾克比,inverse that jacobin,这个呃just pose这样的方式来我们计算出整个角色的这个姿态的变化,那当然这里面参数对于整个其实这对于整个这个角色来说。
我们就让它其实参数这里fa其实代表了整个这个这个啊姿态的一个,参数化的一个方式,以及每个关节的旋转,那当然其实如果说其实我其实我们在实际的这个一使用过程中,通常来讲。
我们可以可以一次可以不用一次考虑所有的这个这个这个约束,我们很可能只有一次只考了一个约束,就比如说我可以实现这样一个i k,我可以旋转我的胳膊,是让我的胳膊去移动到这个点,那在这个过程我们通常来讲。
我们会选择一个根节点和和一个末末端节点,就比如我把根节点选在这个位置的话,那么实际上这个这个ik的过程会会会带来什么效果呢,就是说我会不好意思,就是说我的这个胳膊它的移动手的过程中。
它同时会移动整个上半身,就形成一种向前伸的这样一种效果,这样完成,因为如果说我们不加热其他约束的话,实际上他是想要用尽可能用每个关节用差不多相同的旋转啊,这个这个旋转实现这个i k。
那其实就相当于我这个整个身体,其实可能会向这个ik的方向去进行清洗,来完成这个这个这个目标的操作,那另外呢如果说比如说选择这个根节点,我不是选择这个这样腰的位置,而且选的比我选择一个角的位置。
它会实现一个节目效果了,就说我其实还是用同解去求解同样的一个i k目标,但这次的话因为我会用到整个这条链上所有的关节,那其实它就相当于是说前面这个问题是我只有上半身向前倾斜。
然后后面这种问题就是说我其实和整个身体都会向前倾斜,去实现这个目标的位置,那反过来如果说我只想让这个胳膊会发生旋转,我不想让其他身体发生旋转,那我可以把我的根管根节点选在比如说这个肩膀那个位置上。
那它其实就只有前面这个发生移动,然后实现我的这个呃ik的这个目标,ok好,那我们其实到现在就主要是简单回顾一下,我们其实这节课主要是讲了,我们第一部分是关于前线运动学的这个计算。
我们主要是一系列的做标转换,通过一个迭代的一个更新,可以是可以从一从当我给出每个关键的学生之后,我可以计算出相应的每一个关节的朝向,以及每个关节的位置,就是我倾向运动学计算的过程。
然后同样我们做更加常用的一类方法,或者更加有用的一些方法,是所谓的逆向运动学计算,就是我们给出目标点末端点的位置和朝向,我们希望通过一个i k来去反求解每一个关节的旋转。
来使得我这个末端点能够达到那个目标点的位置,然后这里这里我们其实说i k问题,我们是可以把它写成一个优化问题的,那优化问题的不同解,不同方法其实也对应于i k问题的我们常见的几种啊。
这种算法比如说有祈求解优化问题的这个循环,循环坐标下降法,其实就对于ccd的i ik的c c d方法,那求解优化问题的这个梯度下降法,其实就对于求解i k问题的啊,甲亢病transpose方法。
然后比如说对于这个求解优化问题的这个高速运算法,或者说leon leon bug mrt的方法,其实对应于我的这个求解i k问题的啊,贾克病inverse方法。
这其实也是我们一些很常用的一些常见的一些算法,但实际上我们还可以对另外一种分类,就是说这是前面这几种方法都是基于优化问题的这样一些i k方法,他们还有一些其他的方法,就是所谓启发式的方法。
就是说我们先不去考虑这个具体优化函数的性质,我们可以通过设想,就是拍脑袋拍出一些想法来,然后按照这个方法可以实现对x问题的求解,比如说c c d,如果我们不考虑他跟优化问题联系的话。
它其实那个每一步优化的过程其实是一个启发式的一个过程,那其实还有现在还有还有另外一种非常常用的启发式启发式的方法,就所谓这个这个february的方法,f a b r a k,那这是111年的时候。
一个一篇一篇论文提出的方法,其实现在很多我们常见的这个物理引擎和游戏引擎,其实也都支持这样的方法,那这个方法我们在这里就不多介绍了,大家有兴趣的话可以去看这个相关的论文啊,当然其实也可以。
但很容易在很多地方找到这个中文的一些啊。
对这个方法的简介好,那我们今天的这个内容就到这里,然后我们这个对跟现在不太说了,其实我们这个第一次的这个作业,我们很快就会放出,放在我们的这个啊,我们的这个网上,我们这个这个github上。
然后然后大家欢迎大家进行参考,然后来来这个来做这个作业,然后提交好的,那我们今天的课程呢就到这里啊。
GAMES105-计算机角色动画基础 - P4:Lecture04 Character Kinematics (cont.) & Keyframe Animation - GAMES-Webinar - BV1GG4y1p7fF
我们就开始上课。
非常高兴啊,大家第四次,今天是我们Game305计算机角色动画基础的第四节课,当然前面几节课可能上一课内容稍微有点难度,可能有些同学听起来稍微有点麻有点废,就是遇到一些困难。
当然我们今天可能会稍微再把上节课的内容再重复一下,当然我们额外的加了一些新东西,然后后面再讲一讲有关Keyframe Animation,就是关键帧角色动画里边一个关键技术,就是关键帧差值的技术。
我觉得整体来说今天的内容应该比上节课稍微简单一点,当然有什么问题的话我们课后还可以继续的交流,OK,上周我们一直也在说,就是我们的Game305,sorry,我们的第一个Lab。
就是关于潜向运动学和逆向运动学的这样一个Lab,我们总算是发布在了我们的GitHub上,当然大家现在可以去看一下,其实现在已经在上面了,如果大家有时候可以看一下,就是在我们的网站GitHub。
然后Game305,然后Game305这样一个Repo里边,我们现在有一个Lab,Lab1,如果大家打开Lab1的话,你可以看到这是Lab1里边的一些文件,然后还有一些基本的介绍。
整体来说这个Lab我们需要用Python来写,当然时间也是为了助教评分方便,我们对大家可以用的Python的库,这个模块我们还稍微做了一点限制,我们尽量大家只用Numpy和Scipy。
然后还有我们显示的Panda3D,这样的一个框架来写,如果大家有用的其他的比较少见的依赖库的话,可能我们在这个评分的环境可能没有装,可能大家的交上来的作业就没法运行,这可能就不通过了,所以大家还是尽量。
当然也是最后练习,因为我们毕竟还是上这门课,就还是尽量的用一些我们指定的这些Python的库,来完成这个作业,当然作业的最后的提交,我们其实是我们之前已经发在网上了,我们是用的Games。
以前一直用的这样一个作业提交系统,大家只要按照要求提交一个Python文件,提交OI的一些文件,如果你做了我们的选作的那部分内容的话,大家可以这样提交,然后总的时间我们是三周,从今天开始三周截止。
然后截止之后我们的助教会进行评分,当然实际上这个作业相对来说不是那么难,如果说你是做基本内容的话,所以说我估计可能两周也就差不多了,所以我们两周之后其实会有第二个作业,就是Lab2的那种。
然后我们可能中间会有一些重叠,但是这个也是希望大家能够多多参与,这个是一个挺不错的一个,但是助教也是花了不少功夫,在这个Lab环境的搭建上,OK那就刚才我们说的,其实我们这节课主要两个部分。
一个部分是简要的,回顾我们去年,上节课感觉好像是过了很久一样,上节课我们讲的,关于角色运动学的两个主要部分,一个是前线运动学,一个是逆向运动学,当然我们会额外的讲一点,我们其实一直就提到了。
这个motion returning的一些内容,另外第二部分主要是讲一讲,一些差值的常见的一些差值的算法,那当然首先就是回顾一下,我们上节课讲的内容,一个角色运动学,主要是前线运动学,是从知道pose。
知道每个关节旋转,去计算每个关节的位置,还有逆向运动学,我们知道末端节点,比如手的位置,我们想去反算出每个关节的位置,这是我们上节课主要讲的内容,那当然我们简先简要回顾一下,我们的前线运动学的部分。
我们知道我们对于任何一个角色,比如说这样一个人形的角色,我们通常来讲会把它抽象化成,有若干个关节和若干个骨骼,构成的这样的一个模型,当然骨骼总是围绕着关节旋转,并且在我们常见的角色里边。
一般来说我们关节是不会分开的,分开了有些特殊的角色,它是会分开的,但大部分情况下,骨骼只会围绕着关节旋转,而不会让它分开,当然这样一个结构的话,我们之前也说过,我们可以把它给表示成,一个竖形的结构。
从根基点开始,根基点通常来讲,我们会把它放在腰部宽部,然后从根基点开始出发,我们有若干条这样的一个运动学链,从根基点到脚,从根基点到手,从根基点到脑袋,这样子每一条链构成,就是这些链合在一起。
构成一个竖形的结构,在定义的时候,我们其实这个角色,它有一个初始化的一个姿势,然后在这个姿势里边,每两个关节之间,就是每个关节到它的副关节,它们之间的一个距离,其实是一个,相对来说是一个。
在这个副关节局部,要写的这样一个坐标表示,在这样的定义之下,如果说我们每个关节,加上了一个旋转,其实这个角色,就会被旋转到一个新的姿势,这个姿势下,我们的前项运动学FK,做的事情就是说。
当我给出了这样一个姿势之后,我们需要去计算,每个关节的朝向,每个关节的位置,当我们说朝向的时候,实际上是说,假如说我们有一个坐标系,它粘在对应的关节上,当我旋转完之后,这个局部的坐标系。
在世界坐标系它的方向,它的旋转,我们通常会把它叫做朝向,当然这个过程中,我们前项运动学计算,基本来说是一个迭代的过程,我们从根据点出发,沿着每一条运动学链,去依次积累,我们每一个关节上的旋转。
然后同样的,其实我们如果说,计算出了它的负节点的旋转,负节点朝向,我们把负节点的朝向,乘以当前关节的旋转,其实也就得到当前关节的朝向,类似的关节的位置,我们其实也可以,同样的通过这种迭代式的。
计算来进行完成,当然这里需要注意的是说,如果说我们知道了,每一个关节的朝向,我们知道朝向是什么,朝向是全局的一个旋转,代表了每个关节,它自己内部坐标系,在世界坐标系的位置,如果说我们知道。
每一个关节的旋转之后,每一个关节的朝向之后,如果说我们想计算,每个关节的旋转,这个其实相对来说,是比较容易的,我们只需要用负节点的旋转,sorry 负节点的朝向的逆,比旋转矩阵来说,它的逆就是它的转制。
用负节点旋转的转制,乘以当前节点的旋转,得到的就是关节的旋转,这是一个非常简单的这样一个关系,当然我们上面也提到了,其实我们的,包括我作业里面也用到了,就是我们一般来说很多的动作。
我们可以把它存在文件里,你们来说一个动作,它是有多个姿态,多个姿势来构成的,每一个姿势可能就是代表了,若干个参数化的参数,BWH文件,其实我们作业里会用到,它是一个非常常用的。
这样一个动作捕捉的文件类型,BWH文件具体的文件格式,大家可以去看一下,我们的就是这里的链接,其实我们作业里面,也提供了一个中文的链接,也是我们一个助教写的,一个简单的文档,大家可以去作为参考。
大概来说,其实对BWH文件来说,或者是对于任何一个动作捕捉,或者动作的一个文件类型来说,它可能不是BWH,哪怕是FBX,哪怕是比如说AMC,AMS,AMF,对这样的文件来说,它大概来说都会分成两部分。
其中一个部分,就比如BWH文件来说,它的这个文件的一开头,它会定义一个角色的模型,其实就是这个模型,它怎么定义这个模型,它是通过每个关节,相对于它附节点的偏移,它是一个三维的向量,通过这个偏移来告诉你。
这个模型长什么样子,就比如它会告诉你,这个肩膀的这个关节,到这个中间这个脊柱的这个关节,它的偏移多少,一般来说可能是一个X,方向的一个向量,然后类似比如腿膝盖的关节,在这个髋关节的下方多远。
它会给出你这样的一个偏移,来定义这样一个角色,那接下来一般来说第二部分,对这个文件来说第二部分,它一般来说会有若干行,或者若干个区块,那每一个区块,它会定义一个姿势,那怎么定义呢。
因为上面这个角色的这个关节,其实已经在上边,文件的前方已经定义好了,那我们接下来其实是后面,只需要定义每个关节,它的旋转,以及根节点的位置和朝向,那其实我们就已经定义好,这个角色的姿势了。
那我们把一系列的角色姿势,放在同一个文件里面,它就是定义为一个动作,那这是一个常用的,一个这个文件的一个表示,那当然这里就是一个问题,就是说对于我们一个角色来说,通常来讲我们有这样一个。
初始的这样一个姿势,然后把每个关节加上旋转之后,然后它会变到一个新的姿势,那么在这个初始的姿势,其实我们认为,它的旋转应该是等于0的,或者说它的这个,每一个关节的旋转,或者每一个关节的朝向,都等于这个。
如果用矩阵表示的话,都等于单位阵,那这样的一个pose,通常来讲,我们会有一个专用的名字,我们叫它T pose,那有些时候可能也叫它,Bend pose或者叫Reference pose,总而言之。
不管它这个pose是什么形状的,它总于就是我们讲这个,Reference pose或者讲T pose的时候,我们总是认为,我们总是指的是,就是说这样一个,所有旋转都等于0的,这样的一个姿势,OK。
那其实比如说这样一个,具体的一个角色,比如把它绑到一个,这个三维的角色模型之上,其实这个T pose,大概是这个样子,那当然实际上我们常见的,除了T pose之外,还有另外一类,叫做A pose。
其实A pose有很多种,非T pose就是A pose,就A pose一般来说,常见的这种常见,就是说这个手,不是水平举起来的,因为水平举起来,就是一个T,像一个T,对一个字母T,那这个它一般来说。
我们这个手可能是,一点向下半举,这样一个状态,然后这个看起来,好像是一个A字,所以它一般来说,本来叫做A pose,那实际上比如说,在我们的很多,见角色模型的时候,我们有时候会见到一个。
见到T pose,有些时候会见到A pose,那这个其实用哪一种pose,对于我们做动画来说,其实关系,如果说我们只关心,骨骼的移动来说,这个其实关系并不是很大,但是实际上呢,就是说可能。
如果我们有听众里边,或者我们的学生,大家有做过这个,有做这个动画方向的,比如说做过建模的,可能你可能有些经验,至少我的了解,就是说这个动画师,他在做绑钉的时候,他会比较喜欢A pose。
这个原因其实有两个吧,一方面是说,比如说我从一个正常的姿态,因为我正常人站起来,站着的时候,我的手是下垂的,这是一个比较,比较自然的一个状态,那如果说我要把手举平,那这个时候我的肩膀。
肩膀附近这边的肌肉,和这个皮肤,它会有一个比较大的形变,那这个时候,我如果把它绑在这个,一个3D模型上,那我做其他动作的时候,这个形变有些时候,有些时候会带来一些artifact,会带来一些这个。
一些走样的问题,那另外一方面,其实也是,就是不知道,有没有注意过,就当你把这个手抬平,做出这样一个T pose的姿势的时候,实际上不止你的肩膀,这个关节会旋转,其实你的这个,这个叫clavicle。
应该叫锁骨,就这一带,其实也会往上移动,就是整体来说,这个其实也是,给我的绑定带来一些难度,就当我做其他姿势的时候,你会感觉这个,这个角色看起来就不自然,所以很多时候,大家做动画时绑定的时候。
他会喜欢用A pose做绑定,那这样就,形成带来一个什么问题呢,就是说我们动捕这边,就是大家做动作捕捉的时候,大家都是一般来说,标准都是做T pose,然后你的角色,其实反而是A pose。
所以这经常有时候,会带来一些问题,那当然实际上A pose,T pose大家都可能比较标准了,我们说T pose,基本上就是这种,两臂平伸,两脚竖直站立,这是一个T pose,那A pose呢。
就这个种数就很多了,比如说这是一种A pose,这样也是一种A pose,你看这个脚会分得开一点,那当然这个也是一个A pose,你这个手可能在,分的角度不一样一点,那当然这个也是一个。
当然不是A pose,反正是某一个pose,那这个pose在什么时候会出现呢,其实有时候也经常会看到,特别是有时候,比如说我以前经常,比如从动画师那边,拿了一个绑定的模型,然后我把所有的关节。
设成0之后,哎呀那个角色就变成,这么一个奇怪的姿势了,那为什么是这样一个姿势呢,其实这个可能,我觉得某种程度上,也是可能跟动画师的经验有关,就是说通常来讲,比如说你在Maya里边,比如说这个软件。
我是怎么建角色的呢,我通常来说是,先建好一个胳膊,然后把这个胳膊复制一份,做一个镜像,然后再跟另外一个胳膊,拼在一起,然后腿一颗,甚至可能都跟胳膊,是同一个地方来的,然后最后建成这么一个pose。
那它这个过程中,其实Maya,比如像这种软件,它做镜像的时候,它会把那个镜像,对应的那个transformation,应该叫什么,叫那个变换,其实是会记录下来的,那这个transformation。
其实会带来的,什么问题呢,就是它会,它是一个非0的旋转,把它变成0之后,它就从transformation,就会旋转到原来,这个没有变的那个状态下,那就可能会出现,这样一个姿势,那当然可能比如说。
有些这个建模师,他有经验的话,他可能会做,做完这个建模之后,他会先做一步,这种清零的操作,比如让软件自动的,这个重置一下,每个关节的相对位置,这样的话可以保证,我这个姿势是比较正确的,那否则你会出现。
这种问题,那当然A pose和T pose,当然这个只是一般来说,只是这个两个reference pose的问题了,当然这里有一个,非常重要的问题,就是说比如说,我这两个角色,一个是T pose。
一个是A pose,对吧,那如果说我让这两个角色,同时把两个胳膊,向上旋转90度,那会发生什么事情呢,我们一看到,就是同样旋转了90度,这对T pose来说,它其实是做出了这样一个,类似于欢呼的姿势。
那对A pose来说,因为它开始的时候,手臂垂得更低一点,所以说向上旋转90度之后,它会变成这样一个姿势,就是没有前面那个T pose举得高,或者是反过来,比如说我让它们同时向下旋转90度。
那对于T pose来说,是一个比较正常的姿势,而对A pose来说,它其实旋转的感觉,明显过头了,变成另外一个姿势了,这样就是说,对于这个参考姿势,其实对于我们,就用相同的这样的一个姿态参数。
比如说每个关节旋转多少度,用相同的这一套姿态参数,放在不同的reference pose下,就是放在T pose下,或者放在A pose下,它所能呈现出来的。
这样的一个姿态,其实是不同的,比如说这是一个,我就是找了一个非常简单的例子,其实左边这是一个T pose,右边是一个A pose,但是我把T pose的每个关节旋转,让它去放在右边。
那个A pose上去进行播放,你可以看到对于同一个动作,对于这个T pose看起来,是一个正常的人在走路,那A pose这个人感觉是,稍微有点不太正常,就是这个手指总是会放开,OK。
所以这是一个非常典型的,这个A pose和T pose的一个问题,所以说这里我们需要解决什么问题,如果说我们想要用一些,比如说我们的角色就是A pose,我们接好的,它就是按照A pose绑定的。
但是我们的动作,比如说我们的动物数据,它是一个T pose采的数据,那我们该怎么把一个T pose的数据,映射到A pose,让A pose的这个角色,能够使用T pose的数据呢。
那当然这就是一个,所谓的retargeting问题,再讲具体一点,比如说我有一个骨骼,一个Skeleton,它的pose,它是用A pose来定义的,也就是它旋转,所有的旋转都等于0的时候。
它是这样一个姿态,然后在这个骨骼上,我摆了一个姿势,是这样一个姿势,然后我假设这里边,每个关节的旋转,分别旋转了一个角度,那个角度我整体来说,计为一个RA,它是一致的,那这个动作。
可能是来自于我们动物数据,那另外一个,比如说我有一个A pose,这是我绑定好的角色,绑定好的角色,那我也想站上这个角色,它也做出同样的姿势,那我这个旋转对应的是RB,那对于这个情况来说。
RB我是不知道的,我是想要计算的,那接下来的问题就是说,我能不能从RA算出RB等于多少,那这个就是Motion Retargeting问题,就是我们的动作重定向的,一个非常基本的问题。
那当然这个前提条件是说什么呢,就是我们只考虑,关节旋转带来的问题,但动作重定向,还有很多其他的内容,比如说关节的长度变化,那可能会带来一些,比如说穿模呀,比如说动作奇怪的这些问题。
那个其实是不是很好解决的,那个不是我们今天会解决的问题,OK,OK那回到这个问题,这个事情该怎么做,那我们可以从一个,非常简单的任务开始来看,这个比如说我现在有两个物体,就两个钢体,两个物体。
那一开始的时候,这两个物体其实开始的,处理状态是不一样的,就是说我们认为这个,比如这个物体,它是在旋转等于0的时候,它是表现出一个,横放的一个姿态,然后这个物体,它一开始的时候是斜着放的。
然后它也是这个旋转等于0的状态,就这两个物体我在创建的时候,就创建成这两个,这样一个姿势,然后一个,它们俩都是在这个旋转等于0的时候,是表现出这样的姿势,那如果这个A物体,比如说A物体。
因为这两个物体是,如果它们大家相同的话,那实际上我们总是可以把A物体,按照某一个方式旋转,让它跟B物体是方向相同的,就是能够重合的,那假如说这个旋转是RA到B,就用这样一个旋转矩阵来表示。
也就是说这个RA这样一个物体,当它的旋转是RA到B的时候,它所呈现的这个形状,是跟B完全重合的,就朝向和位置,其实是完全一样的,OK,那我们可以剪,就是我们就是稍微剪辑一下。
就是RA到B是把这个物体旋转到B,这样一个位置的时候,它所对应的朝向,那特别注意到这个B本身,它的旋转是0,所以说这是两个不同的物体,那接下来,比如说我有一个目标的一个姿势,目标的一个朝向。
我把这个A物体,经过某一个旋转,已知的旋转RA,旋转到了这样一个姿势,那接下来我想问的是说,我该如何旋转B物体,使得B物体能够到达这个姿势,其实这就是一个retarget的问题。
但是我们的retargeting是,针对一个物体来做的,那这个过程怎么做呢,其实我们知道从A物体到B物体,从A物体让它旋转到跟B物体重合,是需要做一个RA-B的这样一个旋转的。
那反过来如果我想把B旋转到跟A物体重合,那我需要做什么旋转,其实非常简单,我们就只要把RA-A到B的旋转,把它做一次逆变换,就可以做了,那当然这个逆变换,就RB到A,那应该就是RA到B的转制。
因为旋转矩阵嘛,逆就是它的转制,那我做完这件事之后呢,那这时候B物体的旋转,就跟A物体的重合了,那接下来因为我们目标的姿态,是A物体这样一个姿态,就通过RA旋转得到了,所以说为了把B物体旋转成这个姿势。
我们首先把B物体旋转的跟A重合,然后接下来再用A物体的旋转,去旋转这个旋转这个时候的B物体,那总的旋转应该是多少,那其实它是做了两次旋转,B物体首先做了一次B到A的旋转,然后再做了一次RA的旋转。
那随着我回想一下,第二节课讲的这个关于旋转的combination,旋转的组合的这样一个过程,我们可以知道为了把B物体旋转到这样一个姿势,那它需要加的旋转是什么呢,它旋转是RA。
这是后旋转它应该放在前面,然后乘上RA到B的转制,RA到B的转制其实就是往回走的这样旋转,那这个其实就是我们想要的关系,就是说当A物体和B物体只有单个物体的时候,我们知道A物体到某一个姿态的旋转之后。
那B物体到这个姿态的旋转,是可以通过这样一个公式直接得到了,OK,OK那前面我们这其实是提到是一个单个物体,那我如果说我们这不是一个单个物体,而是一个两个关节的这样一个链条,或者一个角色。
那当然我们为了标记简单,我们假设其中一个前面这个是一个副关节,那前面肯定固定不动的,也许是动的,这个我关节要了,就是它是一个副关节,然后这个右边这个关节是一个子关节,那同样的B它也是一个两个关节的。
一个跟A是完全相同的一个角色,这么一个角色,这么一个链条,那当然它在初始化这个旋转等于0,所有的旋转等于0的时候,它跟B的位置是不一样的,它是斜着的,然后A是直着的,OK,那同样的它是一个是负节点。
用PI表示,一个是子节点,用I表示,那我们进一步的假设,假设我们是知道的,我们是知道,从就是从A物体的这个子关节,它的全局的朝向,跟B物体的子,这个作为子关节的这个关节,它的全局朝向之间的差。
是用QI A到B这样的一个矩阵表示,那副关节,我们在全局的这个表示之下,它也是QI的PI A到B,这是我们的这个假设,这是我们知道的部分,然后另外呢,比如说我有一个这样一个姿态,目标的一个姿态。
从这个A物体旋转到目标的姿态,我分别需要把P的旋转,或者P的朝向变成RPA,然后它PI和P之间的这个关节,它的旋转应该是RI*A,这也是我们知道的,那接下来时间我们可以很容易计算出。
因为我们知道这两个是关节的旋转,所以我们可以很容易计算出,这两个关节在这个姿态下的朝向应该是多少,分别是QPA,是这样算出来的,然后QIA,这是子关节,它应该是副关节的朝向乘以关节的旋转。
得到了这样一个关系,OK那接下来我们对另外一个方面,比如说从B,如何能够得到这个关节的旋转,那首先副关节,因为这是我们要求的,假如说我们知道了,就是那副关节,它的旋转应该是RBPI,那子关节的这个旋转。
就是中间这个关节的旋转应该是RBI,就是这两个量是我们需要求的量,那假如说我们如果说这两个东西是知道的情况下,那我们其实可以算出来,在这个从B看来,这样一个姿势里边,P就是它的副关节的朝向。
应该是这么应该是RPI,然后子关节相对于这个,这个B链条的I,它的朝向应该是QBI,它其实应该是副关节的这个朝向乘以,这个后面这个关节的旋转,OK所以整个这里边,这部分是知道的,这个也是知道的。
然后两个R是知道的,RA是知道的,然后这个所以说整个RA的这个朝向我知道,然后RB这边朝向和旋转,我们都是不知道的,是我们需要求的,那我们应该怎么求呢,其实回想一下,我们当刚才讲。
当个钢体的这个旋转的时候,我们知道B的旋转,B的朝向,因为单个钢体来说,我们没有旋转,我们每个钢体的旋转就是它的朝向,它的朝向等于A的朝向,乘以A和B的朝向的差的这个逆,这是我们对于单个钢体这个。
单个物体,单个链条的时候,它的这个这个计算方式,那同样的其实前面我们把它所有的这个,关节的旋转,转成关节的朝向之后,那我们其实可以把每个关节的,副关节和子关节分别单独去处理,对于副关节来说,我这个变换。
其实是可以写成这个,副关节在A下面的这个朝向,就A的副关节的朝向,乘以两个关节,A和B两个两个Skeleton,两个链条,它们之间副关节的这个朝向的差,那子关节也类似的,我们可以做出这样的计算。
那接下来实际上我们对,我们其实这个时候已经知道了,在这个B这个Skeleton B,在五个B这样的一个条件下,它的这个旋转,就旋转到同样的这样一个姿势的情况下,的这个副关节的朝向和子关节的朝向。
我们都知道了,那这个时候我们想要去求这个B的中间,这个关节子关节的旋转,那么它应该是副关节的朝向的逆,乘以子关节的,乘以子关节的朝向,那得到应该是子关节的旋转,那我们其实可以把它展开,因为这里我得到了。
这他们两个用A和A到B表示的一个形式,那么可以把它分别展开,注意到中间这一块,中间这里中间这里其实是A关节,A骨骼的副关节朝向和子关节朝向,所以说这两个东西其实可以算出来,它刚好是等于A里边。
就是骨骼A里边,它所对应的这个关节的旋转,其实就回到我们刚才这里边,就是说实际上对于这个这两个,A关节A骨骼和B骨骼,就是A链条和B链条,如果说我们知道他们每一个链条的这个节点,副节点和子节点。
分别在全局下的旋转的这个位移旋转的偏移,以及A旋转到某一个姿势下需要做的这个旋转,那么B旋转到同样的姿势,那这个关节所需要做的旋转,其实是可以通过这样的一个表达能够算出来,就是副节点的全局偏移。
乘以这个子节点的在A下面的这个旋转,然后再乘以子节点的全局偏移,然后再转至这样一个形式,那当然这里对于副节点来说,因为副节点它没有副节点了,所以说前面这个是没有的,所以说对于副节点的这个计算。
我们只需要考虑后面这一半就可以了,OK 所以这其实就是两个链条,两个关节的链条,我们其实可以通过这样的方式,实现两个这个骨骼旋转T pose和A pose。
如果是A pose和B pose之间的这个动作的迁移,那类似的如果说我们把它再复杂一点,其实两个链条再泛化,其实就是我们正常的一个角色的这个迁移了,就比如说还是回到我们刚才那个问题。
我们有一个A角色它是T pose,有一个B角色它是一个A pose,然后A角色做出这样一个姿势的时候,它需要做RA的旋转,然后B角色做出同样的姿势,它需要做RB的旋转,所以这两个旋转一般是不同的。
那RB跟RA是什么关系,其实根据刚才我们的这个两个链条的旋转关系,其实我们可以很容易就得到,对于任何一个关节就内部的关节,我们可以知道它的对B关节,摆到同一个pose的情况下。
B的角色它每一个关节所需要的旋转,和A的角色每一个关节所需要的旋转,有这样的一个关系,那这个就是我们的motion targeting,一个最基本的一个这样的一个旋转关系,所以说就是其实你不需要用IK。
不需要用什么特别奇怪的一些,不知道什么的方法,其实如果说只是关节T pose和A pose之间的转换的话,我们可以很容易地通过这种方式来进行迁移,OK 当然还有一些其他的问题。
就是说比如说这两个pose,比如说A的角色和B角色,如果它的骨骼数量不一样该怎么办,那这个其实是稍微复杂一点,主要是说骨骼数量不一样的情况下,我们需要决定我们把哪一个关节的旋转。
映射到另外一个角色的哪一个关节上,这个其实是经常出现的问题,而且有时候不一定是骨骼数量不一样,可能骨骼名字都不一样,所以你可能需要手动做一些映射,当然好处是什么呢,如果说我们这两个角色,为什么黑掉了。
大家能看到吗,OK 好的 不好意思,我的B站的播放好像刚才出了点问题,OK 好的 如果大家没有断就没有问题,好的 那就刚才我们就说到,其实我们要做的这个retargeting。
就是说一方面是说我们每一个关节,我们需要把名字,比如一个角色的关节多,另外一个关节少,或者反过来,或者是名字不一样,我们通常来说需要做一些手动的映射,那当然来说对于人来说,比如说都是人形角色。
那我们可以期待,比如说他们俩肯定都会有相同数量的腿,腿上有相同数量的关节,然后身上胳膊上有相同数量的关节,所以说我们总是可以,一定程度上建立好这个映射,那建立好这个映射之后。
我们进一步去处理A pose和T pose之间的关系,或者说T pose和一个奇形怪状的一个pose之间的关系,那我们就可以比较不错的,让一个动作能够在一个角色身上能够展示出来,OK。
那前面主要是我们对一个前线运动学的一些回顾,但是我们稍微补充了一点内容,就是retargeting,那我们接下来讲一讲,还是回顾一下我们逆向运动学的部分,但是逆向运动学是稍微比较难一点。
因为主要是说通用来说,一般来说这个逆向问题总是会比前线问题难一些,当然我们前面也提到了,对于逆向问题,什么是逆向问题,或者什么是逆向运动学的问题,它其实是可以表示什么呢,OK 不好意思。
就是说我们通过前线运动学,我们可以计算出每一个关节的位置,特别是我们可以计算出末端节点的位置,但这个末端节点不一定是最后一个关节,那可能是中间的某一个关节,只要是这个链条上的最后一个点。
我就认为它是末端节点,OK 那这个节点,这个整个计算过程,我们总是可以写成表达成一个函数,那具体形式我们就不管了,我们可以符号上把它表示成一个函数,那x=fθ,fθ其实就是每一个关节的旋转表示。
那可能是欧拉角,可能是肘角,可能是Coternin,这个其实无所谓的,然后对于这个末端关节来说,我有一个目标位置,x波浪线,xτ,然后这个我是想要把这个点,它的末端点挪到这个位置的时候,我需要计算。
对应的θ应该是多少,实际上我这个其实我是求解一个方程,因为方程最终我的目标是想要,fθ的位置跟x的波浪线相同,那其实就是相当于,我需要去求解一个方程,这个方程是xτ,x波浪线减去fθ,那等于0。
那我们上节课也提到了,其实这个方程,我们可以等价地把它写成一个优化问题,那就是大概形式上写成这样一个形式,那当然有同学也问了,我刚才群里也问,说为什么这里是1/2,那为什么这里是平方。
这个其实完全就是为了我们后面求导方便,我们只是把它写成这样一个形式,因为我们求导之后,这个1/2就可以直接被消掉了,要不然这个总是带着2就很麻烦,那当然这个优化问题,有好多种不同的解法,其中一种解法。
就是所谓的循环坐标下降方法,因为我们这个坐标,这个θ它是一个向量,比如说对于这个观念来说,它至少有四个,有四个参数是我需要优化的,所以说对于每一个参数来说,循环坐标下降的做法。
就是说我分别沿着每一个参数,所对应的那个坐标去做搜索,然后搜索到某一个值时候,这个值对应的能够把我,沿着这个坐标上的所有的,这个目标函数的值变得最小,那我就认为我下一次deadeye,就从这个点开始。
所以说每一步我都在解一个优化问题,只不过这个优化问题是一个,是一个我只去优化这个函数的,一个变量的这样一个优化问题,所以它是一个循环坐标下降的方法,那当然表现在IK这个问题里边。
其实我们上一课也给大家提过,它其实是一个非常简单的,一个有点像启发式算法的这样一个过程,就是我每次旋转一个,依次旋转我的这样一个关节,按照某一个确定的顺序,然后每次比如旋转关节3的时候。
我会把关节3旋转,使得这个末端节点和这个目标点的连线方向,跟关节3跟这个目标点连线的方向是相同的,那依次的比如说关节2,我们也同样的做这样的旋转,然后关节1做这样的旋转,关节0做旋转,然后依次略退。
直到我们旋转的足够近,那当然我们上期可以提到,就这个为什么我们要做这样的旋转呢,或者我们为什么要把它旋转到跟这个,跟这个3到X之间的这个距离,这个向量平行呢,或者重合呢,那它主要是因为。
本质上我们是要在求解这样一个优化问题,而这个为了让这个,它本质上优化问题上,它的优化问题本质上是想让这个4的这个节点,跟X基本能近,然后在我旋转3的时候,如果说3旋转一圈。
那它所对应的这个4所划过的这个空间,就是一个圆球,一个圆周,或者一个圆球,那这个圆周上呢,离这个X最近的那个点,肯定是3跟X连线,跟这个圆周的交点,那这个具体证明,大家可以自己去证了,这个很容易证明。
所以说,这也就是说,为什么我们要把4旋转到这个点,就是因为这个点,其实是这个函数,在指优化3的时候,它的最小点,那如果说我们有另外一个问题,另外一个问题,比如说还是这样一个。
类似于跟刚才的连线很像的一个连条,但是唯一的区别呢,这里关节2不再是一个旋转的关节,它不能转,但是呢它是可以伸长的,那对于这样一个关节来说,我们该怎么做这个CCD呢。
其实做第一次这个阻压下降是没有问题的,因为3是一个可旋转的关节,所以我们其实还是一样,把3旋转到这个重合于这个连线的方向上,那接下来2该怎么办,那2它现在我说它是一个可以伸长的关节。
这个伸长关节可以表示,类似于什么,类似于一个,比如想想打气筒,打气筒就是一个可以伸长的关节,它不能旋转,它不能沿着那个打气筒这个位置旋转,假设这个2,3是一个打气筒,那我们在这个打气的过程中。
这个我们去把它伸长缩短,那这个过程中你会发现,这个随着我3,4这个关节,沿着2这个方向去移动,这个4所走过的这条线,其实应该是沿着跟2,3这个关节方向,所平行的一条直线,OK。
那接下来按照CCD的这个基本算法,我们其实目标是想要去找到一个长度,对于2来说它的参数就是这个伸长的量,想找到这么一个长度,使得我这个4的位置跟x尽可能近,那问题就来了,这个最近的这个点在哪里。
当然我们以前前面也说了,就是如果说3,4沿着2的这个方向随便移动的话,它是会形成一条直线的,所以说这个最近点,应该是外面这个点到这条直线的距离,这个具体是什么,是这个点到这个直线的垂线的距离。
所以实际上就是说,我其实可以沿着x向这个2,3这条D方向做一条垂线,那这条垂线上这个点跟这个4能够达到这个点,就应该是我对应的那个点,也就是说我其实会把3,4,就把2,3伸长。
然后直到伸到4跟这个垂线重合的时候,那它应该就对应到这个极致点,所以这其实也是最高限,就是实际上如果说你知道了坐标情况下,它本质上就是一个优化问题,那其实对于一个更加奇形怪状的一些关节。
我们其实可以有同样的思路去分析它,然后可以去找到一个合适的解决,OK那类似的我们可以不断地做,剩下的关节就是旋转关节了,我们可以继续这样的循环,那其实可以这样继续拉长,然后这样做,OK行。
这就是循环坐标下降,那当然我们前面也提到了,循环坐标下降不是一个非常快的方法了,想要再快一点的,那我们需要用一些雅克比矩阵的方法,那雅克比矩阵有两种,我们需要用的是什么呢,就是说这个前项运动学。
它把它写成一个函数之后,它应该是它的对这个,它每一个参数的导数,对每一个,因为F本身是一个三维向量,是一个三维向量,是一个高维的函数,这个函数对它每一维的这个参数做偏导,然后把这个偏导。
每一个偏导写成一列,写成一这个矩阵之后,那这个矩阵叫做雅克比矩阵,但如果大家对这件事情搞不清楚的话呢,我建议可以去去看一下大一下半年,可能是大一下半年,这个新生态数有关多元微积分的,多元微分的部分。
那里会比较详细的会讲这个,这个导数到底是什么,在我们知道雅克比矩阵之后呢,那我们其实可以做两种不同的方式去进行迭代,一种方式是这个雅克比转制的方式,它其实是实际上是说。
我们知道当前这个莫德纳位置和目标点位置的偏差,比如我们叫做delta,那这个雅克比转制的方法,实际上我会用这个delta乘以,雅克比矩阵的转制,它给出了一个方向,那这个方向是我更新这个参数θ的方向。
那另外一种方式呢就是雅克比,雅克比inverse就逆雅克比矩阵这样一个方法,其实跟前面这个形式是非常相近的,唯一的区别就是这里不再是转制了,而是一个雅克比矩阵的尾逆,那当然我们上一节课也提到了尾逆。
为什么要做尾逆呢,因为雅克比矩阵不是方阵,然后对于不是方阵的情况下,我们只能去做它的尾逆,那这是我们上节课讲的内容,当然这里非常重要的就是说这个雅克比矩阵怎么算,那我们也提到了,很简单的方法。
你用这个πtouch实现一遍forward kinematics,实现一遍fk,那对那个方程进行求导,那你就得到了一个雅克比矩阵,那如果说你没有这种方式的话呢,其实只是对一些特定的问题。
比如说我们如果这个所有关节都是hinged joint,那我们可以通过差乘的方式来计算雅克比矩阵里的每一列,那当然我们也提到了,雅克比矩阵的每一列,对应的是其中某一个关节的旋转参数。
那如果是所有都是hinged joint的话,那其实就是一个hinged joint的旋转角度,那这个时候呢,它这一列的竖直应该是这个hinged joint,这个旋转关节的旋转轴。
差乘这个关节点到末端点的位移的这个向量,那这是每一列,我们需要对每一列分别去做这样一个计算,那当然上节课有一个一流问题,就是说如果说我们不是一个hinged joint,而是一个ball joint。
那该怎么办,当然我们是当时直接给出了结论了,就首先ball joint来说,一个ball,它其实可以有三个自由度的旋转,那对应的话我们需要用三个参数来表示它,其实本质上就是一个正常的旋转表示,然后对于。
如果说我们把这个旋转表示成一个欧拉角,那欧拉角是三个旋转的叠加嘛,那么它其实这个欧拉角你可以等价的认为,它是三个hinged joint,集联,就是一次连接,但是它们连接之间的关节是长度是零。
所以它们三个点在同一个位置,然后我们这个对应的雅赫比矩阵应该是对应的三列,然后每一列分别对应于欧拉角的三个角的这个偏导数,那当然我们也直接给出了结论,就是说OK这个每一列它应该是这个轴乘以我的这个r。
然后这个轴是这样的一个形式,看到有朋友也在问,为什么这三个轴要这么算,当然上节课我们其实这个地方讲的不是非常的仔细啊,我们再稍微都补充两句,就是说实际上对于这样一个欧拉角,假如说我们现在就是先不管z吧。
我只管rx*r,ry它对应的是什么旋转呢,它是说我先把这个坐标轴这个这个实心的这个坐标轴,沿着x做了一个旋转,沿着x做旋转,x轴是不会变的,那y和z轴会稍微旋转一点,转到这样一个角度上。
那然后再做r的旋转,它是沿着这个旋转之后的这个坐标轴,它去旋转的,其实你是这样的表示,这是intrinsic的表示,你总是可以,总是可以理解成这样的旋转,就是rx*ry。
我说先沿着x轴没有变过的x轴旋转,然后再沿着变过的y轴旋转,那我做了两次旋转,所以说对于第一次旋转来说,对于x的参数来说,我们如果把它假设成一个hinger joint。
它的旋转轴应该就是世界坐标系的x轴,那在这个后面做y旋转,它的旋转轴是哪一个,它旋转是局部坐标系的y轴,那局部坐标系的y轴,实际上它是通过rx轴旋转之后得到的。
所以说它对应的是四次世界坐标系的rx*y这样一个轴,OK,那当然在此之外呢,其实因为这不是第一个关节,不是根关节,所以它其实它的副关节也带有,本身也带有一些旋转,所以说这个一开始rx的旋转。
它其实是在副关节旋转之后得到了,所以说它在世界坐标系的表示,应该是副关节的旋转,副关节的朝向乘以这个轴,得到的是这个x轴,那对应的y,其实最前面还要再乘以一个朝向,所以说就回到了我们上次的这个扇子。
得到的这块,为什么三个轴是这样的方式,实际上是欧拉角得到的这样的结果,OK,那这还有一个非常自然的问题,我们是否,因为我们知道前面提过的,我们的这个参数,旋转的参数表示有好几种,其中一种是轴交表示。
轴交表示好像非常自然,因为我们前面一直说都是轴,轴的方向差成这个距离,那我们如果说我们把它表成轴交表示,我们这个甲和壁矩,是不是也可以这么算呢,那当然结论是不可以,因为主要是说,对于轴交表示来说。
你的参数是每个这个轴交表示的三个变量,然后这个三个变量,它在改变的时候,它是同时改变了旋转角度,并且改变了旋转轴的方向,大家要理解这件事情,所以说如果说我们用轴交表是一个旋转的话。
如果用轴交表示来参数化一个暴撞的话,那它的甲和壁矩这个计算是非常,相对来说会复杂很多,这个公式我就不写了,但大家只要知道这个东西就复杂很多了,如果真的说你需要计算的话。
那我其实很建议你用拍套子自己写一遍,然后用拍套子来算,这可能还更准确一点,更不容易出错,OK。
那回到上一站我们讲的这个,其实我们上一节放了一个video,就是这个video,看起来非常fancy的一个Character Rig,就是这个我们可以随便去调这个Character。
然后这个它里面有很多很多不同的关节,我们可以去改这些关节。
然后去调一个关节的姿势,那当然这里就是说我们前面提到,这个所谓的Character IK,就一个链条来说我们可能可以,就一个末端点就能解决问题了,但对于一个这个角色来说,那它的关节点关节很多。
而且实际上同时我可能会要求,比如说我讲你这两个脚不要动,然后你身体动,然后你的手可以动也可以不动,然后类似于这样子,然后每一条这个要求,其实都会对应于一个优化问题,那我总的这个姿势。
其实是所有这些优化问题的这个,优化问题的总和,那最后我们可以加一个,正则项来避免我旋转过多,那当然这个其实你,如果你建立好这个优化问题之后呢,那你自然可以算雅克比矩阵,然后用雅克比矩阵去做这个。
Jackie Bean Transpose也好呀,去做Jackie Bean Reverse也好呀,那都是可以做的,那实际上还有一些其他的方式啊,就是比如说更加heuristic,更加这个启发制的方法。
就是通常来讲,比如说我们知道这个角色,它总是会可以抽象来说是,由若干个这个链条来构成的,每次都是从根基点开始,比如说我要移动这个手的位置,那我要是根基点不要发生变化,那我其实可以只需要把这个。
这是一根链条了,我们前面讲IK的时候,我们一直都是用一根链条来做,做展示的嘛,所以从这个根基点开始,做这一根链条,那可以非常简单,我们前面的IK的算法,都可以直接拿来用,包括你怎么算,你雅克比矩阵。
你也可以用的,那当然另外一个问题,稍微复杂一点,就是说如果说我做这个链条,移动的时候,做这个IK的时候,我想让手移动到这里来,但是我要求你的脚不要动,那这个时候其实从这个脚开始,从脚到腿。
小腿大腿到根关节点,然后到我的这个躯干,然后就到手腕,这一整条是一个链条,那这个链条本身,我们其实也是可以去,使用我们这个IK的,那当然这里需要注意一个什么问题呢,就是说这个链条,跟刚才这个问题。
跟前面这个问题,不同点在哪,在于它是穿过了根结点,而我们一般的角色的姿态,的这个表达来说,它是根结点,给出根结点的位置和朝向,加上每一个关节的旋转,那这里根结点是比较特殊的,所以说为了做IK。
我们大概可以做什么呢,大概是两步走三步走,首先我们先不管根结点,我就是用这个链条,上的每一个关节,我先去算一遍IK,那这个IK会给我什么呢,其实会给我每一个关节的旋转,那给出每一个关节的旋转之后呢。
我再做一遍FK,但实际上你做IK的时候,你其实已经在不断做FK了,所以说实际上你就用,最后一次FK的结果,那这个FK结果里面,其他关节其实不关紧要,但是根关节的位置是比较重要的,从这个点假设点不动。
你从这个点,沿着这个链条做一次FK,你会算出一个新的根结点的位置,然后你就把根结点位置设置成,你算IK得到的这个位置,那朝向也设置成,这次IK得到的朝向,那接下来还需要做一件事情,就是从这个关节出发。
如果从这个关节出发向上走,你比如说这个膝盖这个关节,你算出来这个旋转,其实是把这个,从子关节看来去旋转,副关节的一个旋转,但实际上我们参数化的时候,是从这个根结点开始,从宽关节开始,它实际上是要求的是。
从副结点看来去旋转,子结点的旋转,那其实这两个是刚好逆过来的,所以说实际上,从这个结点出发到上面去,我算出这两个关节,这三个关节的旋转,那我当我把它转化成,从root出发的这个,正常的姿态表示的时候。
这三个关节的旋转我需要求反,因为它是对于这个,对于条来说,我其实是旋转了副结点,而对于这个根结点来说,我其实是旋转的是子结点,所以它俩是相反,这个其实也是需要注意的地方。
那类似的如果说有两个constraint,那比如说我要求,sorry,好对这是另外一个,如果说我还是刚才那个问题,前面是我是移动手,但是呢其实另外一个问题,就是我移动这个root。
其实想想前面那个video里边,其实有很多这样的例子,就是两个脚不动,我一下挪动一下躯干,那其实就是腿会在,其他地方发生变化,然后这个躯干位置会发生移动,而这个脚位置是不变的,实际上这个问题。
跟前面问题是一样的,其实比刚才前面问题,还再简单一点,因为前面这个问题,我其实是要算手的,这个问题我只要算到root就可以了,所以说本质上它是,常常的方式可以解决掉,但另外一个稍微复杂一点的问题。
就是说我同时要求两个脚都不动,然后去移动这个根结点,或者移动上面的,比如头的这个关节,那这个时候该怎么办呢,这个就稍微复杂一点了,就是说两种方式,一种方式说我还是,去构造一个更加复杂的。
就更加通用的这样一个优化问题,那我其实可以继续算甲隔壁枕,算这个甲隔壁枕的转质之类的,去算这个优化,另外一种方式也是启发式的,就是说我们其实可以,比如说先从左脚开始,先算这个链条,把右脚先忽略掉。
它怎么动我们不管,这个电条移动完之后,那这个时候我发现,这个左脚已经偏移我原来的位置了,然后再把它拽回来,所以大概是这么一个,Horizontal的方法,其实也可以实现类似的效果,那当然实际上。
包括我们前面那个video里边,我们这个视频里边,那个角色的这个模型,它的这个IK,实际上是多个IK的一个总和,就通常来讲,比如说我这个角色,可能会见很多个IK,比如说从根结点出发,到两个脚。
分别是两个IK,两个IK的链条,然后到两个手,是两个IK的链条,到头是一个IK的链条,可能还有很多,其他的复杂的更复杂的链条,这个系统,然后每次我用户,比如说我需要挪动,这个根结点的时候。
那我其实可能是同时去,让这个,所有的这些链条都启用,然后来实现,它不要出现这个移动的问题,不要让这个,就是我在这个计算过程中,可以同时可以保证,每个手的位置和脚的位置不变,那如果说。
比如说我只移动脚的时候,那可能其他的链条是不变,是不会被启动的,那我就是这样的话,我只会影响这个,只会通过去求解,这一个IK链条,来实现对脚的移动,然后在这个过程中,比如说有些链条,可能它是不起用的。
那比如说我可能自由的,我可能用IK来移动手,那可能它可能会带到,我上半身发生一些移动,那这个过程中,实际上其他链条是说,我在去求解,一个下一个问题的时候,我会用这个,上一个问题的那个。
已经解出来的那个位置,作为一个初始解。
来进行求解,那回到刚才这个video,其实这个,其实中间有好几个地方,是展示了这种效果,就实际上这个video,其实它就是好多个,这个IK的这样一个设定,就比如说我在移动手的时候,实际上我可能会把。
整个胳膊会发生移动,但是我在移动另外一个,比如在移动胳膊的时候,这个手的位置,会自动固定到,这个刚才移动过的位置上,那这样的话,它会这个让我的这个,整个这个条的这个过程,是比较符合直观的。
比较符合自然的,行OK,那我们其实就是相当于很快的。
也不算很快,大概已经差不多一个小时了,我们就回顾了一下,前面IK的,FK和IK的主要内容,然后这个,然后我们有稍微多讲了一点,关于这个retargeting,关于这个动作的重新线,然后还有IK。
这个Full body IK的,一些额外的一些内容,当然时间关系,我们这个question先不讲了,我们到最后再一起来问问题,好的,那我们接下来是我们这节课的,第二个部分,就是关于关键帧动画。
以及这种关键帧插值,那实际这个地方,最主要的是关键帧插值,因为关键帧动画,我觉得这个非常非常简单。
非常简显易懂,当我们知道这个,一般我们说动画嘛,其实它的起源,或者它很早期的动画,就很多种不同的形式,比如说这种,这种就是在叫一种,其实它也不是走马灯。
因为走马灯一般说的是那种,在灯外面画一圈,然后这个灯会不断转,那个东西叫走马灯,这个东西,其实好像没有中文的名字,我不知道它应该叫什么,但大概就是说,你其实这个,它是一个你可以从这个上方的。
这个孔洞去看里边这个画。
那这个孔洞,它不断会挡住这个光线,其实可以给你造成一种,这个画面,就让你看不到画面切换的过程,那因为视觉暂留,这个效果嘛,所以你就看起来,这个动画,这个画是会动起来的,其实我们知道现在电视。
我们一直常说的,这个电视它基本原理就是这样子,你在两帧之间插一帧黑帧,然后你看到的动画,就是动作是连续的,那如果你插的足够多的话呢,整体来说就是连续的,OK,那这样的话,我们看到的这些帧。
其实就是所谓的关键帧了,就是我们看到哪一帧,然后这个它们会动起来,它们连接起来,会形成一个动画,那当然现在我们一般,做一个动画的时候,实际上我们会做的更加这个,就是比如说,作为一个手工设计。
手工绘画画这个动画,那我们通常来讲,动画师他会先把一些,一些这个动作的,一些重要的一点,比如说这个走路,然后捡东西,然后再站起来,放在那儿,然后这个过程中,其实比如走路,开始这个姿态是比较关键的。
然后蹲下来,这个姿态是比较关键的,然后这另外一个姿态,是比较关键的,然后这些其实是,所谓的关键帧,然后从两个关键帧之间的过渡,那所谓叫这个过渡帧,这个叫in between,所以说这是关键帧。
当我们知道关键帧之后,我们如何求过渡帧,这样的一个过程,实际上就是我们这个绝对动画里边,做差值的一个,非常重要的一个问题,那当然我们大部分情况下,我们过渡帧不会有这么长,因为当然,因为现在有一些技术吧。
比如说一些新的,绝对动画方面领域的这个研究,它也会做一些,一些探索,就是说如果说,这个关键帧之间离得非常远,比如说这个人在门口,另外那个人已经坐在椅子上了,那中间我这个人需要,我怎么去生成。
从门口到坐到椅子上,整个这段动作的这个流程,那其实现在一些新的研究,是在做这些事情,那当然对于我们,这一般3D动画来说呢,我们通常来讲,不会有这么远的过渡帧,就是通常来讲,这两个过渡帧之间。
还是相对来说比较近,那在这种情况下,实际上我们就是说,做了一些过渡帧之后,我们是希望把这个,把这个关节,我们是希望把这些过渡帧,我们把它平滑地连接起来,比如过渡帧,不好意思,或者关键帧。
比如说我可能每隔一秒,做一次关键帧,或每隔半秒做一次关键帧,然后但是我整个视频播放的频率,可能是60fps,或者是比如说30fps,那从这个,从两个关键帧之间,可能就有若干帧,是我没有给出的。
那我们这个关键帧的插曲技术,就是需要从我给出那几帧里面,去把我没有记录的那个关键帧,把它给算出来。
是这样一个过程,sorry,那当然来说,我们这个关键帧插指的技术,我们可以更加抽象起来,它其实就是一个,一个就是数据点插指的一个问题,就比如说我给出了若干个点,这个data points。
比如说这里x是一个横坐标,当然我们可以认为它就是时间了,然后y是某一个参数的值,那可能比如说是,比如说一个,比如说我的一个肘关节的旋转,那可能对应的是y,那我给出了若干个关键帧上的,对应这个坐标的旋转。
它的值,那接下来的问题就是说,当我给出这些离散值之后,这个插指问题是说,我想去找到一个函数,这个函数是我的横坐标,这个时间或者是x的函数,它首先满足在所有的这些点上,在所有的给出的这些点上。
这个函数的值跟y是相等的,然后除此之外呢,我还需要去计算,这个一个新的点x,这个点可能是我们从来没有见过一个点,在这个点上,我需要去计算一个合理的,对应的这个参数的值,y的值或者fx的值。
就是一个插指问题,那当然我们插指啊,就我们一般说插指,其实有两种不同的插指方式,一种是所谓的内插,一种所谓的外插,一般来说我们做动画领域,我们常用的都是内插,就是说我要查询这个x的位置。
是在我整个给出的数据点的x的范围内,那如果这个x落在范围外呢,那这就是外插了,通常来讲外插会经常会有些问题,一方面它不准,另一方面它有可能会出来,比如说出来一些非常奇怪的一些值。
但是主要原因也是因为外插,我们缺少了一边的约束,就是我们不知道外插,比如说x在这里的时候,x在左边我们是没有任何信息的,那这种情况它应该取多少,我们是不知道的,其实就好像就是我们经常以前做的一些。
所谓的质类题,比如说给你四个数,第一个数是1,第二个数是2,第三个数是3,第四个数是4,然后你第五个数是多少,那这里就没有任何保证,你也不知道第五个数是多少,按照规律可能是5。
但其实我说100也没有问题,反正我只要找到一个函数,把它给拟合上就没有问题了,所以说实际上这个对于内插来说,其实就是我们说的函数拟合问题,或者函数叉值问题,就是我们下去算一个函数。
它能够去首先在每个点上,达到每个点的这个函数值,同时在一个新的函数点上,我们能够计算这个新的函数,新的这个值,那这个所谓拟合函数,所谓叉值函数,当然有很多种不同的方法了,比如最简单的。
这种所谓的这个t度函数,就是如果只要我没有遇到下一个x,那我就保持当前x对应的y的这个值,这是一个非常简单的t度函数,那对这个t度函数来说,实际上这个fy就在这个点的值,它应该就是它前面这个y的值。
当然t度函数有很多种不同的定义方式了,这是其中一种定义方式,还有些其他定义方式,比如说我可以定义成,当前x的值是多少呢,它是离这个x最近的给出的那个x,所对应的y的值,其实是类似于这样的一个曲线。
那当然集体函数也非常不好,因为它不连续,看起来就是总是蹦蹦跳跳的,所以说我们跟查查用的,也是一种常用的方式,就是所谓的这个,就是所谓的这个线性查证函数,线性查证函数其实非常简单。
就是我把这些查证点画出来,然后那两个点之间做条直线,当我要查询一个新的点的时候,我就把它落到这条直线上,然后查一下这个直线上,当前这个值是多少,所以说这个值,因为我们知道直线的方程,我们可以很容易。
首先我们可以算出斜率,然后可以很容易写出直线的方程,那最近的这个x的位置,它所对应的值,应该是大概是这样一个形式,那当然我们可以稍微把它变一变形了,其实我们可以把x写在一块,然后y写到另外一边。
那我可以把前面这个x,可以用另外一个符号来代替,比如我改成t,用t来代替,那么其实我们通常讲,t取值范围是从0到1,那0的时候是代表前面这个值,1的时候代表后面这个值。
然后f2是中间t等于某一个数的时候,这样一个值,这是一个线性差值的一个表示,那当然线性差值,我们也可以把它再变形一下,形成这样的形式,它类似于是说,我用t是一个混合的一个参数,我用t这个参数。
去把两端y1和y2,就是x1和x2之间的这个两端的值,做一个混合,得到当前的这个值,那这也是线性差值的另外一种形式,当然这个时候我们其实有一个非常重要的性质,就是我们需要注意,想要我们差值方式能够提供。
就是所谓的平滑性,或者说它的连续性,就比如说一开始我们的阶梯差值,阶梯函数,它有一个非常重要的问题,就是说它是不连续的,就是它在某一个点上,它是间断的,就是这两个点属于同一个x,但是它的值是不同的。
然后类似的比它平滑一点的,就是我们所谓的这个线性差值函数,它实际上整个这个函数值,是不会发生间断的,但是你会发现它是一个间间的形状,它的这个速度是不连续的,那类似的我们可以进一步的要求它,再平滑一点。
再光滑一点,我们可以进一步要求它在中间这个连接点上,它的速度也是保持一致的,那看起来再平滑一些,那可以再进一步的,我们甚至可以要求在这个点上,不只是速度一致,我们要求它的加速也是一致的。
那就是适应的这个所谓的C2平滑,但实际使用过程中,其实我们肉眼能够看出来的范围里边,其实C1平滑和C2平滑的效果其实差不多,就是你经常还是看不出来,它们到底有多大的区别,当然这可能是在那个建模领域。
比如说这个几何建模和制造领域,可能它有比较大的区别,OK,所以我刚才提到了,比如说这个线性函数,虽然说它是连续的,但是它不光滑,它不光滑还体会表现什么效果呢,就比如说我看到这个角色,会有一个很奇怪的。
就是先往一个方向动,然后突然就开始转向,下一个方向动,这样的话就有一个非常明显的,就是这个顿挫感,所以实际上很多时候,我们还是希望缓震的技能光滑一些,那这个时候就需要一个。
非线性的产生函数来帮我们做这一点,OK,那实际上我们最常用的非线性函数,其实就是所谓的多项式,OK,不好意思,我们多项式,其实可以写成x的若干次方的,这么一个集数和的这样一个形式,那如果说我们假设。
我们是要用一个多项式去做差值函数,那么该怎么去,因为这里多项式来说,我们定一个多项式,需要知道每一个项的系数,那么该怎么求这些项的系数,那首先我们其实已经给了,有这样一个数据集了,里面有比如说n个点。
n+1个点,从0表号0到n,那这n+1个点,那对第一个点来说,对x0来说,我们可以写出f(x0),在这个多项式表达之下,它的值应该是多少,它应该是这个,我就直接把这个s代替成x0就好了。
然后根据我们差值的要求,那肯定f(x0)它应该等于y0,这是我们的差值的要求,那接下来对于我们这个多项式,这个数据集里面的每一个点,我们都可以,我们都可以列出这样一个方程,x0是一个方程,x1是方程。
一直到xn也是一个方程,那这些方程,那每一个的都是,就这里边位置量,其实是所有的a,这里x其实都是已知量,那么可以把这个位置量,写成一个列,然后已知量写成一个矩阵,那其实构成了类似于这样一个矩阵的形式。
那我知道得了一个矩阵之后呢,那我如果想要求这个方程的系数,那其实只要把右边求个逆乘到左边就可以了,其实可以得到这样的形式,那当然这里有一个前提条件,就是说这个方程必须是可逆的,那什么时候可逆呢。
首先它必须是一个方阵,那什么时候是一个方阵呢,因为我们这里有,因为整体来说这里是n个方程,我们有n个点嘛,n个点对应n个方程,那有多少个未知数呢,这是一个n阶的一个,小n阶的这样的一个多项式。
那它的未知数是n加1个,所以说我们至少需要一个,大n-1阶的一个多项式,才能保证它是方阵,才能保证我这个差值这个函数这个方程是有解的,OK,那当然这就是说,比如说我有10个点。
比如说这个差值函数有10个点,那我就需要一个9阶的多项式,那比如说我差值函数有100个点,那我就需要99阶的一个多项式,才能把这个差值函数给算出来,那当然这里有一个非常重要的问题,就是所谓的笼格现象。
笼格现象说的是什么呢,就是说一个高维的高阶的多项式,比如说一个10阶15阶甚至20阶的多项式,比如这是一个15阶多项式,如果说这个15阶多项式去,去拟合一系列的点,它可以拟合的非常精确。
但是呢在这个点靠近边缘的地方,在这个范围靠近边缘的地方,它会出现一个非常巨大的震荡,因为这个其实也很容易理解,因为这个Rx的15次方,当Rx大于1的时候,这个15次方会把Rx变得非常大,比如说2。
2的15次方是一个非常巨大的数字,所以说不管前面这个a15是多少,它总是会把这个,就是边缘的地方会变得越来越大,越来越震荡,变成这样一个形式,所以实际在差值里边,我们总是不喜欢这种震荡。
这种震荡呢我们是不可控的,所以我们是总是希望,我们能够尽可能用一些低的多项式,来去做这个拟合,来去做这个差值,但是我们前面也提到了,因为如果我们用多项式的话,那不管用多少个,就是如果说我们有N个点。
大N个点,那我们一定这个接触是不能小于大N减1的,不然的话我是不可能满足,我的差值的这个点上的要求的,这时候该怎么办呢,其实一个想法就是说,我一次去拟合所有的点,是不好的,那我们可以一段一段的拟合。
每一段我只拟合几个点,比如说我每次只拟合三个点,只拟合四个点,那我可以只用一个三次多项式,就可以完成了,如果每次去拟合两个点,那就是一个一次多项式,那这就所谓的这个样条差值,或者叫分段差值。
那基本思想就是说,我用一系列的低阶的,这个分段的多项式,去拟合我给出的一大堆参数,去做这个在对大参数之间做差值,那刚才我们说的,其实一开始我们给出的这个线性差值函数,其实就是一个分段线性的一个样条函数。
它做的事情就是说,我每次去在两个点之间做一个差值,做一个拟合,然后它只需要一次多项式,那一次多项式就是一个线性函数,其实可以这样的形式,那实际上用了更多的,用了很多的一种方式,就是所谓的三次样条函数。
三次样条函数是什么呢,就是说我是把每一个,它是一个分段的一个函数,然后每一段是一个三次多项式,就S立方加上S平方,加上X加上长数项,构成这样一个函数,然后我每一次其实是,就这里每一段。
都是一个小的三次函数,然后它们是首先是保证连续的,其次还是可以保证,一定程度上可以保证平滑性,当然样条嘛,其实一般来说我们讲。
一般来说我们讲一样条,大部分来说都是说这个,所谓的三次样条,就是这个这个,因为为什么呢,就是说样条是什么东西,如果你去查一查Spline这个词,它其实是代表是这么一个东西,就是说我们在做图的时候。
以前大家没有电脑的时候,我们其实工程师要画图的嘛,那画图的时候直线比较简单,都是直尺了,那我如果想画曲线怎么办,那曲线其中一种方式,就是用这种的曲尺,它本身不是曲,它本身是直的,但是我可以。
因为它是一个有弹性的钢尺,或者塑料尺,我们可以把这个加成一些固定,比如把两端固定住,然后把中间拉起来,它就会变弯,甚至有可能这个两,可以中间加两个点,然后把它变成一个S型,那在这个过程中。
这个东西就会呈现出,一个曲线的一个形状,但实际上我们其实可以,可以可以做一些这个证明,就是这些是有一个结论,就是说对于这样一个薄型的,这样一个薄啊,如果说我在两边加一个点,两边固定住。
然后中间从两个点去固定,那我们其实可以得到,它这个曲线,其实刚好是一个三次多项式,所构成的曲线,所以这个其实Spline,为什么大家三次,其实这也是跟三次样条,是直接相关的,OK,但怎么证我就不知道了。
我就只是看到了一个结论。
所以说呢,就是我们回到现在的问题,就是我们其实,我们的这个所谓的三次样条,所谓这个Cubic Spline,就是我们目标是说,给出了这样一个数据集,它里面有大N个点,然后我们想要用若干个。
三次样条去分别连接,任意两个点,然后构成一个完整的曲线,然后每一小段都是一个,三次多项式,那在这个过程中呢,首先我们可以看到,这里有N个点,从X0一直到X5,或者到XN,那我需要多少段,样条函数呢。
可以查一查,1,2,3,4,5,也就是说,N加一个点,我们需要N个样条函数,所以说就是刚才这个问题,对于N个数据,N加一个数据构成的,这样一个数据集,我们有N个段,N段函数,就是N个这个三次样条函数。
然后每个三次样条函数,有四个参数,分别是X1,X2,X立方,X平方,X,还有常数项它的系数,有A,B,C这四个系数,所以说我们总共有N段,这样的一个样条函数,我们就需要4N个,未知的参数。
需要从所有的数据里面,去估计出来,OK,回想一下,其实我们前面讲,多项式差异的时候,我们当时就是已经用了,这个方法,这个方法叫什么,代定系数法,就是说我们这里有四个代定系数,我们可以分别把一些方程。
根据这四个代定系数,列一些方程,然后从这个方程里去解除,这些代定系数,对于三次样条函数来说,我这个方程要怎么列呢,这个方程我就具体就不列了,但是我可以写一些基本的关系,就是说首先差值,因为首先我知道。
虽然我用了三次样条函数,但是我知道它本质上,还是一个差值,差值有一个基本要求,就是说它在,这个样条函数在每个点上,在给出的每个数据点上,应该跟这个数据点的值是相同的,你要说比如说,对于这个I段。
对于这个X1,X2之间,这一段来说,首先它在起点这个位置的,因为这是一小段样条,这一小段样条函数,它在起点的位置的值,应该跟起点的这个数值是相同的,而在终点的值,应该跟这个给出数据集里边。
那个终点值是相同的,这是一个差值的一个条件,其次呢,因为这个实际上还有很多参数,我还不知道,我们可以额外地加一些,更多的约数,这个约数来让我的平滑性,会变得好一些,比如说我要求它是C1连续的。
就是一阶平滑的,一阶平滑是速度是相同的,也就是一阶导数是相同的,那这个我要求是,从这一段样条,在起点,也就是X1这个点的导数,应该跟它前面这一段样条,在X点这个导数是相同的,那其实就是这样一个方程。
类似的我对每一个点,每一段我都有这样的关系,那类似的,同样的我们还需要一个C2连续的,我们可以进一步地要求,它是C2连续的,那其实就是每一个端点,它的二阶导数也是相同的,那当然算一算这个方程还是不够。
那我们还需要一些额外的参数,这个参数就是什么,我在最端最外面这个点,最外面这个点,这是端点,这个边缘点,我们会额外地加一些条件,比如说这个边缘点等于0,这后面这两个导数等于0,那这个时候它会也是。
给我一个额外的条件,所有的这些方程放在一起,我们就有4n个方程了,那这4n个方程,因为它是,它本质上来说是,所有的差值函数,4n个未知数的一个方程,所以你是可以把这个4n个方程,写成一个什么呢。
写成一个4n乘4n的,一个大的矩阵,乘以4n个未知数的,一个列项量,等于右边的一个常数,这样一个形式,所以为了解除,所有4n个系数,你需要把那个4n乘以4n的,这么大一个矩阵求个逆,然后再去做这个计算。
所以这个会很大一个问题,就是带来一些什么问题呢,就是首先这个矩阵,这个矩阵本身是不分块的,不分块的意思就是说,我每一个点,每一个数据点,它的移动都会带来,整个曲线形状的变化,当然这个形状变化。
可能有大有小,但是整体来说,它是会发生变化的,所以它没有一个,很难去实现一个局部的控制,另外一方面呢,就是它整体来说,这个求解还是相当来说,比较昂贵的,就是说我们,因为我们要,基本来说要求解。
一个4n乘4n的,这么一个大叙数,大矩阵的一个,一个界限方程的求解,虽然说这个矩阵,本身是一个西数矩阵,所以说这个矩阵的求逆,并不是那么的,并不是这个立方的,这样一个数量级,这样一个复杂度。
但实际上在总体来说,它也是非常非常昂贵的,所以实际上我们,后面我们就是,至少来说,我们常用一些性质,就是首先我们先解决,这两个问题,就是首先我们是希望,这个我们的差值函数,是有局部性的。
就比如说我在Maya里边,去调一个动画,我就调一个动作,我在调后面的,某一个点的时候,我不希望前面的,这个人的姿势发生变化,因为这个东西,我前面已经调好了,我不想再动了,所以我要求至少,它有局部性。
我调一个点的时候,它只会在局部改变,我这个曲面的形状,其次我还是希望,我们这个差值方式,差值计算是比较,怎么说呢,计算量不要那么大,就是我在做,矩阵求逆的时候,不要做那么大的矩阵求逆。
所以这个我们更加常用的,一类差值方式,就不是Qubic差值,不是这个三次样条差值,而是要三次厄米特差值,Qubic Hermite Spline,当然我第一次,看到这个词的时候。
我说为什么叫Hermite,不叫Hermite呢,因为这是个法语词,法语词的时候,这个H是不发音的,所以这是三次厄米特,这个样条,它其实实际上是说,在三次样条的情况之下,我希望我前面,每一小段。
这一小段,它只根据两个端点的,信息就可以求出来,那当然前面这个,现在这个下面写的,其实还是我们三次,样条差值的条件,对于这个厄米特差值来说,厄米特这个样条来说,因为我们要求,它只根据前后,这个每一段。
起始和结束两个点的信息,来进行计算,所以说就它不会涉及到,前面那一段这个样条的形状,所以说后面这三个,这个条件,我们是不能用的,那这时候我们会发现,这个我们条件不够啊,我们有4n个位置数。
我们只有2n个,只有2n个这个方程,2n个这个这个约数,那我这个方程是不够的,所以说厄米特这个,就是厄米特的样条,其实进一步的假设,我们提供了更多的信息,就原来的差值,我们只要求你提供,每个点的值。
x在每一点的值y,对于如果说我们想要用,这个三次厄米特差值,我们需要同时知道,每个点在这个,就这个样条,就是我们的每个,每个数据在每个点x上,它的这个导数是多少,就一阶导数m值,或者说我进一步的。
或再具体的说,比如说这个,当我给出数据的时候,我除了给出这个数据的点的值之外,我还需要告诉你在这个点,它的速度是多少,这才能实现我们的,这个厄米特差值的,这样一个要求,当我给出这样信息之后。
厄米特差值实际上,它是要求我每一小段,我只需要考虑这一小段就可以了,其他点跟它是无关的,都是独立计算的,那么就这一小段,它在这个前后两端,在这一端的值,跟这个y1相同,这一端的值跟y2相同,那对应的。
这是因为它是一个三次函数,我要这个三次函数的导数,在x1点的时候,跟这个值,跟这个给出的速度相同,m1的速度相同,然后另外一端跟m2的速度相同,那就是厄米特差值,或者说我们再具体一点。
再简化再稍微解释清楚一点,就是说当我们给出,就其他点都是无所谓的了,我们只给出,比如说x1,对应的y1和m1的值,m1是速度,然后以前x2,对应的y2和m2的值,我就要去计算一个三次曲线,三次函数。
这个三次函数,满足我们的差值条件,就是说这个三次函数,在x1的点值是y1,在x1点的导数的值是m1,类似的在x2点的值是y2,在x2点的导数是m2,当然如果说,既然我们只考虑这一个片段。
我们可以再把它稍微简化一点,比如说我可以把x,我就用t来表示,从t去0的时候是起x1,t等于1的时候对应x2,那我其实可以,类似的就是刚才,比如刚才是sx1,那我现在就是s0,然后sx2就是s1。
t等于1,但严格来说这里m1和m2,跟前面这个m1和m2是不相同的,因为我这做这个代换的时候,它是这个t,其实是有一个尺度,有一个这个长度的变化的,所以严格来说,其实m1应该。
这里的m1应该是前面m1的这个,应该是x2-x1的这个差的倍数,但我们就不严格了,这我们就只是为了简化起见,我们还是用m1和m2,来代替这样的一个,这样这样一个速度值,OK,那接下来呢。
我们就拿回到我们前面的,这个代定系数法,那怎么做呢,我们知道把0代进去,对应的应该是y1,那0代进去之后,你可以得到它前面,含t项都是0了,所以只有d,那类似的1代进去,然后就可以做这些计算,然后导数。
其实因为这是一个,三次多项式嘛,我们三次多项式,多项式的导数,这个非常容易计算了,这个大家这个大一的时候,应该是练过无数次了,那这个时候,其实我们可以,很容易地写出来,不好意思,我们可以很容易地写出来。
刚才这个前面,这个约束条件,它所定的方程,应该写成这样的形式,s0没有没有这个t项,只有d,那s1就是,每一项的系数,应该都是1嘛,a+b+c+d,然后类似的导数,只有c,然后1的话。
其实是这样一个方程,我们把这个方程,可以还是类似的,我们可以把它写成,一个矩阵的形式,这样看得比较清楚,那其实我们的未知数,是a b c d这四个数,那前面的系数,是一个4*4的矩阵。
那这个4*4的矩阵,我们可以非常容易验证,首先它是可逆的,其次它的逆是非常好求的,如果是,我觉得相信大家这个,以前在新生代数考试的时候,应该都算过类似的题,这个还是,应该算是还比较痛苦的。
但这个矩阵相对来说比较容易,就是我们可以解出来,它的值,就是这时候,我们可以解出来,a b c d,就是这个三次多样式,它的系数,可以写成这样一个矩阵,这个矩阵其实就是,前面这个矩阵逆,这个矩阵乘以。
y1 y2 m1 m2,就是这个值和速度,的这样一个方程,OK,所以说这时候,我们其实就已经构建好了,这样一个,三次Omega叉值的,这样一个方程,就是当我们给出,这个一个小片段里边,两个端点的值。
和两个端点的速度,y和m来表示,那这个函数,我们可以构造一个三次函数,这个三次函数,它每一个,每一个项的系数,是可以通过这样的方式,通过这样的矩阵,乘以我这个点的这些,这些信息得到这个,这个系数关系。
就是Omega值,那当然我们可以进一步来看,这件事情,就说我们是一个三次函数,对吧,三次函数,它本身是at+bt^2+c+d,那其实看起来,这好像是一个,跟点乘的形式很像,所以实际上我们可以把它。
写成一个点乘的形式,那点乘里的前面一部分,是t的每个幂次,包括乘数项,那右边这个部分是abcd,就是我们四个系数,然后因为我们前面,因为我们前面已经得到了,abcd跟y1,y和m的关系。
可以是这样一个关系,所以把它代入到后面,我们可以得到st,就是我们这个三次函数,它其实是t1t2t3t4,乘以我中间这个系数矩阵,再乘以y和m,这个信息的形式,那进一步的,其实我们可以。
因为这是一个三个矩阵的乘法,因为矩阵是满足,是可以做结合的,所以说我可以先,本来我原来是abcd的时候,我先算到后面这一半,但我们可以反过来,我先算前面这一半,我们先把t乘到这里的每一列上。
那我可以得到这样一个形式,就是第一,首先这个t的这个函项链,乘上第一列,其实得到一个多项式,一个三次多项式,乘到第二列,得到另外一个多项式,然后一字累堆,随着最后的一档形式,随着最后这个形式。
其实我会进一步的,写成这样一个形式,就跟前面这个,其实是等价的,就是说我其实是四个多项式,分别把它写成,多项式的值写成一个函项量,然后它跟一个类项量做点乘,然后得到的那个值,就是我的这个亚当函数的值。
那这个时候,这个四个多项式,形式是非常规律的,看起来好像不规律,但实际上我画出来,你会发现它是非常规律的,是一个非常对称的,这么一个图像,那这四个多项式,其实就是三次亚当函数的,这个厄米特基函数。
就本质上来说,当我给出两个端点的这个值,y和它的速度m之后,我所需要的求得的,这个三次亚当函数,它其实就是四个,厄米特多项式,厄米特基函数,在这个我给出的这些值上,做的一个,做的一个加群平均,加群求和。
这就是这个三次亚当,三次厄米特差值的,这样一个基本方式,那类似的,实际上我们前面这东西是可以,前面我们都说的是一维的,就是只对一个参数,y是一个一维的量,那实际上,我们可以很容易的把它扩展到。
一个高维的一个函数,就高维限于空间,或者高维的,比如三维空间,那其实这里原来是,ABCD是三个,三个,原来三个数,那现在变成,四个数,现在变成了四个向量,那对应的我们前面是,写成这样的形式。
我们可以把它稍微改改形式,只是对于一个一维的,一个问题,一个信号,一个量,我们写成这样的形式,对高维来说,比如说y和m,这时候给出的,就不再是一个数了,而是一个向量,首先y代表一个点的位置。
m代表了这个点的速度的方向,这样的一个式,那这个时候我们三次样条查试,其实同样的,少一个图,其实同样的是,四个样条查试函数,四个Omega的基函数,然后用这里的,y和m的每一个分量去乘它。
然后得到对应分量上的,这个查试函数,对,当然这里就是有一个,非常有意思的一个,其实我们知道,三次样条,其实在很多地方都有用得上了,比如说一个非常简单的例子,就是我们大家平时用这个,PowerPoint。
用PPT,其实非常常用的,这样一个曲线的,这样一个函数,曲线的这样一个工具,比如说我们可以知道,这个对于曲线来说,我们可以去更改,比如我去改一下,我去编辑它的顶点,你会看到实际上,这是这个曲线。
比如我随便画了条曲线,它有四个点来构成,然后这里每一段,其实是一个,当然严格来说,我们其实不知道,它是用什么函数了,因为这属于这个PowerPoint的,内部的一些信息,但是从它行为来看。
它其实跟三个函数比较像的,比如说这个点和这个点,我们知道这两个点的位置,然后这个曲线本身,又穿过这两个点,它其实就是一个,本质上就是一个差值函数,然后同时这两个点,我同时还给出了这两个点,这个导数。
这个导数是用这样一个,这个方向和这个长度,来代表它的方向和大小,我可以看到,其实我给出,我如果调整这个导数的位置,其实它所对应的中间,这一段的函数的形状,也会发生变化,然后类似如果说,改变这个点的位置。
其实也会带来,这个曲线的形状的变化,所以实际上就是说,为什么我们前面要,论辨的函数,它需要给出,给出两个点的位置,两个差值点的位置,然后以及两个,另外这个两个差值点的速度,那我们如果给出,这些信息之后。
那我们其实就可以,直接算出这个曲线上,每个点的位置,OK 不好意思,当然这个三次,厄米特差值,其实有一个,有一些个特例,就是说也是一个,很常用的一种差值方式,就是所谓这个。
这个Kalman Ruhm Spline,就这个差值,它其实是这个,为什么说是特例呢,因为说我们一般,给出这个三次厄米特差值,我们需要给出两个点的值,以及两个点的导数值,那这两个点的值,是比较好给的。
但是导数值不太好给,比如说你像刚才那个,PowerPoint的曲线,那个例子,你是可以通过一些,其他的方式给画出来,当然还有另外一种方式,就是说如果说我们认为,比如说P1点,它的导数值。
是P0和P2点的这个差,这个方向,是它的导数值,然后类似的,P2这个点的导数值,是P1和P3的,这两个点的这个方向,它们的差,那我们其实就得出了一个,一个新的这种差值方式,就这个差值。
其实这个厄米特差值,一个特例,就它叫Kalman Ruhm差值,就这样的话,时间我还是有四个控制点,那我其实是,不是直接去控制,这个点的这个导数了,它的这个方向,而是通过额外的一个控制点。
通过这个控制点的方向,来控制这个点的方向,那这个其实也是,非常常用的一个,一个差值方法,那为什么要提这个差值呢,首先它常用了,这个大家也是,非常需要了解的,另外一个就是说,实际上我们要说。
首先这两个作者,就是它是Kalman Ruhm,Kalman和Ruhm是两个人,其中第一个人,是这个Idovan Kalman,第二个人那个Rafael Ruhm,这个就是我们就不放图片了,这个因为他。
这个首先这个Idovan,其实是非常有名的,就是这个大家可能,比如说我们以前,就是大家可能知道Pixar,这个Idovan就是Kalman,他是Pixar的创始人之一,然后其实还有另外一个。
也是非常著名的,就是说2019年的,ACM的图灵奖,是发给了两个,做图形学的这个,开山的人物,就是其中一个是Idovan,一个是这个Pan Hanrahan。
那当然我们是,直接摘了一段,这是ACM的,关于这个他们俩,这个评选评奖时候,这个介绍,其实大家可以在网站上,直接看到,这其实就是第一段,那当然实际上就是,主要是表彰他们,在这个三维计算机图形学。
然后还有计算机生成影像,这个领域的,一个非常重大的贡献,OK那我们前面其实讲了,都是非常简单的,一些这个点的差值,就是一些平移,一些位置,一些线性的量的差值,那我们其实前面也提到了,就是说在我们讲。
旋转表述的时候,其实我们已经稍微提到过了,就是说我们做,很多时候我们需要,做旋转的差值,比如说我知道两个物体,一个物体在零时刻的旋转,是这样子,在一时刻的旋转是那样子,我现在计算零到一中间,每一时刻。
它的旋转应该是多少,那当然这里我其实,前面讲完了,我们这些差值方式之后,这里其实就是需要讲的内容,就不是很多了,那我们就回顾一下,我们的这种,我们的旋转表示有哪几种,比如说首先,基本的旋转矩阵表示。
然后比如说这种,欧拉角表示,欧拉角表示是三个参数,分别是代表连续旋转了,三次坐标的坐标,分别连着,连着三个不同的坐标轴,它的旋转的这个角度,然后还有轴角表示,它代表了一个旋转轴的方向,和一个旋轴的。
沿着这个轴旋转的大小,这个角度,然后最后也是非常常用的,所谓的这个,四元数表示,就是我们最常用的,这种四元数表示,其中对于这个,欧拉角表示和轴角表示,它们有一些,它们有一些,有一个好处,好处是什么呢。
就是说我其实,不管这个参数怎么设,它总是会得到一个,合法的旋转,所以实际上就是说,我们可以直接的,把我们前面,对于一个平面点,或者对于一个立体点,或者一个三维点,对一些高维点的,这样一个差值。
我们可以很容易的,把它扩展到,对应的旋转参数上,或者说比如说,我们的线差值,我们的三次两条差值,我们的三次恶名特差值,我们可以把,用这些差值方式去差值,就是我们把每一个旋转的,关键帧把它表示成。
对应这个,比如说用表成欧拉角的一个点,或者表示成一个,这个修标表示的一些点,然后把这些点,用这个把它看作,一个三维向量进行差值,它总是得到一个,合法的一个旋转,但实际上这两种方式,它是不一样的。
但实际上世界效果来说,可能差的不是那么多,你可以看到这两个,其实我是从,做了差值相同的点,你会发现它其实转的过程中,有一点点偏差,但是整体来说,可能差偏差没有那么很大,当然这里前提条件是什么呢。
就是说你在做差值的时候,你要正确的处理了,这个起点问题,起点问题是什么呢,就是说比如说我在,一个人原地转圈,那它可能你会发现,它的轴角表示,不管是轴角表示也好,还是欧拉角表示也好,它的旋转的角度。
都是先从零开始,比如说涨涨涨涨涨到π,涨到180度,或者涨到360度,这时候它会突然变到零,或者变到负π,这样一个形式,就是它会有突然一个突变,这种突变实际上,在我差值的时候,它会带来什么问题。
就是这个东西,它会先往一个方向转,转着转突然它会,有非速的反向转,然后再去往正确方向转,就是说你没有处理好,极致点的话,你会带来那个问题,但如果说你正确处理,极致点的时候,实际上效果上来说。
都是看起来差不多的,就是基本上是跟我们,想象的类似的,当然一个问题就是说,它其实这两种差值,都是不能保证,我在两个点之间的速度,是连续的,是一致的,是相同的,就是说它可能这个旋转,是忽快忽慢的这种情况。
那如果说我想让,两个点之间,它在旋转的过程中,这个角速相同,角速不变,那我们其实可以用什么,我们可以用SLURP,用这个,对于这个四元数的,这样一个SLURP运算,那这个上节课,其实我们也讲过了。
不是上节课,第二节课我们其实,也讲过了它的公式,大概是这样一个形式,那SLURP本质上来说,它只是一个线性差值,那线性差值,其实带来一个问什么问题呢,就是说,比如像这个视频里边,左边这一部分。
你可以看到它其实是,比较死板的,先往一个方向转,然后倒头,然后再往另外一边转,它会有很明显的,一个线性的,一个效果,但如果说非线性差值,比如说我们把它变成,一个三次Omega差值,你会感觉它是一个动物。
虽然做同样的动作,但整起来会平滑很多,那咱如何对Coterni,去做一个非线性差值,比如三次样条,那这个其实就不是,不在我们这节课,我们这门课会需要讲的内容,大家如果感兴趣的话,但这有篇论文。
这比较老的一篇论文,它其实是当时讨论的,如何对这个,这个四元数去做这个差值,那基本思路来说,我可以稍微讲两句,因为我们其实今天,没有讲的一个东西是什么呢,是Bezar曲线,大家知道,如果大家有兴趣的话。
可以去看一下Games102,Games102其实,就是讲几何建模的时候,它有专门一部分,去讲这个Bezar曲线,那这个Bezar曲线,其实有一种构造方式,就是我可以通过多次迭代的。
进行线性差值来构造Bezar曲线,那同样的,我们可以把这个线性差值的,这样一个过程,去把它放到,用这个Slurp来代替,我们的线性差值,我们就可以实现一个,四元数的这样一个Bezar曲线。
那当然这个条件是,那这个问题是什么呢,就是说这个,有点卡壳了,对 当然这里有个问题就是说,实际上我们前面讲到了,比如说三次元量差值,或者说比如说这种线性差值,所有这些差值方式,其实我们都要求。
我们要过这个固定点,那Bezar曲线,其实我们知道它一个性质,就是说它只有端点的时候,是通过的,然后不是端点的时候,是不会通过的,那这个时候实际上,我们知道对于三次Bezar曲线,和三次Emmy的差值。
其实我们是可以建立一个等价关系的,就是我们可以找到一组Bezar的控制点,让它实现的曲线,跟我三次的Emmy的差值形状是一样的,那我们其实就是说,比如我们构造一个Spline的一个。
用刚才我说的Bezar曲线的这种方式,去做Spline的这个非线性差值,我们可以说,比如说我可以做一个转换,把那个按照我们,比如说按照这个,三次Emmy的差值,和Bezar曲线之间的转换。
关系做一个转换之后,再用这种Bezar曲线的,线性迭代线性构造,来实现来用Slope来构造这样一个曲线,当然不管怎么样,就是不管用哪一种方式,实际上,虽然即使我们用使用了Bezar曲线,其实你会发现。
如果是你真的是去,实际去计算一下,这个你的速度和你的加速度,你会发现它也不是一个常数,它有可能就,比如说加速度,你会发现它这个加速度,有时候会抖动会非常大的,其实这个加速度,其实你可以认为它对应于什么。
对应的是你的这个在球面上的曲率,球面上的curvature,所以实际上这个如何去让我的曲率最小,其实不是一个简单的问题,其实这个优化是很难解的,所以说这个总而言之,就是我们大部分情况下。
实际上就是我们试一下,比如说我们这个这些样弹函数,不是样弹函数,我们不同的这种差值方式,比如说是用Ology来表示,用Ology来表示,用Ascend Angle来表示。
用这个Quaternion来表示,后面两个是Quaternion,然后不管是用线性还是用非线性,其实你可以看到,如果说我们在正确处理边界条件的情况下,实际上效果不会特别的,差的不会特别大。
除非说我这两个差值点之间隔得非常远,那这种情况下,比如说我们为了,我们其实可以用,加一些更密的这种关键帧,来实现更好的效果,而不用特别去关心,我到底用什么方式去把它差值的。
所以这也是一些实现上的这个方式,OK,那我们最后再回顾一下,其实我们讲了非常非常多,我本来还想可能一个多小时能讲完,发现时间还是花了讲了一个半小时,总体来说,我们今天还是两部分。
前面一部分花了比较长的时间,就是还是在重新回复了,回顾了一下,我们前上节课讲到的角色动画的,这个角色运动学的部分,然后特别是我们又额外的增加了一些,关于运动重新项和全身IK的,这样一些内容,另外一部分。
就是说这个关键帧动画,但我们最主要的是说,我们讲了如何去进行关键帧之间的差值,那差值在什么地方有用呢,比如说在Maya,或者其他各种软件里面,我们做动画的时候,我们会需要用到差值。
因为我们一般来说只会给关键帧,另外还有什么地方管用,其其作用呢,比如说我生成动作,比如我动捕数据,比如说我动捕数据,也许是比较低帧的帧数,比如说是或者是比如说50帧,比如有些动有些这个并非这文件。
它是50fps的,就50帧每秒,但是我实际的播放的时候,我需要60帧每秒,那我该怎么把一个50帧每秒的动作,变成60帧每秒呢,那我其实需要做一个差值,然后再重新采样来得到这样一个一个映射。
OK好那这个就是我们今天的主要内容,看看大家有什么问题,那可以在这个现在可以这个在我们的这个弹幕里,可以可以发这个问题,我们大概可以有几分钟的时间来回答来回答一下,惯性化差惯性差值。
这个我还真是第一次听到这个词啊,你说的是基于物理的差值吗,这个基于物理差值,其实我们就没讲啊,这个看了一下,我还其实之前我还稍微看了一眼,好像是Unreal他们最近的一些新的方式啊。
我就是具体公式我就没仔细看了,但是大概思路是通过那个翅膀的XPPT,XPPT就是这个基于位置的这个物理仿真里边的这个去求解,去求解这个这个约束的方法去代入了进来,用那个东西来去做做这个IK啊。
他这个我就我们我们这门课就不讲了,就是那我觉得你如果真有兴趣的话,我觉得你可以去很容易的,你可以在网上找到一些这种这种啊,比如说博客呀,或者是一些一些这个论文,各位就会关于这个方向。
对这个Daniel Holden他应该他有一个博客是讲这个的,有兴趣可以去看一下,啊,四元数线差值这个我们就刚才提到了,其实可以证明的就是在我们用slurp,首先用slurp的时候。
我们可以证明他的在这一小段,做线差值的时候,他的速度是恒定的,但是如果说我们是线性差值,然后再把它因为线差值,他是不再是一个因为slurp是单位阵嘛,单位四元数嘛,我们线差值,如果只是线性差值的话。
他他会变成一个非的非单位四元数,我们需要再做一个projection,做一个normalization,把它变成一个单位阵,那这种差值方式,他是不连续的,速度是不恒定的,所以稍微会有一点会有点落差。
那slurp是不是保证他一个这个过程中,他的速度是恒定的,这个关键这个关键是相差较远,这个过渡针啊,这个我建议你可以去看一下,see graph去年和今年的一些工作,啊包括前年吧。
就这两这几年出了两三篇工作了,see graph就是最早有一篇叫robust motion in between,啊,好像是于毕的两个人做的,然后今年see graph有另外一篇文章。
是那个浙大的金小刚老师,还有反正浙大的几个学生,就一分一分,我记得是浙大的,但我当我不是很确定了,然后这个他们他们做的一个工作,就是效果也是挺不错的,就主要的方法,基本来说还是需要数据驱动的方法。
我们后面再下两节课,下一课我们还讲不到,在下一节课的时候,我们会讲到一些关于学习的方法,就基本来说思路,还是说我去学一个啊统计模型,那这个在这个模型之下,我们告诉你从这里到那里。
他做什么动作可能是最大概率的,那我们其实可以通过这种方式去,不好意思去寻找一样一个动作,就生成一个动作,他有最大的概率,在我们数据集里面有最大的概率,能满足开始和结束这样的姿势。
那这就是我们生成这个啊in between,就是补肩超远距离补肩的一个基本思路,啊,a pose和t pose,就是刚才我们其实也提到了,就是说实际上他是都是初始姿势,只不过是两个不同角色的初始姿势。
我看看能不能找得到,对实际上我们是他是两个不同的角色,他不是同一个角色,他是两个不同的角色,只不过其中一个角色,我在刚创建好之后,他没有旋转的时候,他的他的字看起来,若眼看上去他是一个t pose。
然后另外一个角色,他的刚建出来之后,他若眼看上去是一个a pose,他就是两个不同的角色,所以实际上我们前面说的这个动作重新下,Motion Targeting,本质上我们是在从一个角色的动作。
我怎么让另外一个角色能够重复出来,那我们需要做一些数据运算,其实是干这个事情的,当然还有另外一个问题,就是说这个啊,实际上我们前面说的这个绑定啊,就是这个绑定,就很多时候时间里,比如说你在Maya。
或是很多软件里面,但我对Maya比较熟悉啊,所以我举的都是Maya的例子,比如你在Maya里边绑定,通常来讲我可能,也许我建立时候这个角色是一个t pose,但我绑定的时候。
我把它转到一个a pose的形状,然后再绑了这个角色的这个外,这个这个蒙皮,蒙皮的模型之上,那这个时候这个角色本身,他的初始的姿态其实还是a pose,sorry还是t pose,所以实际上我。
比如我给一个旋转的话,其实对应的是这个pose的旋转,所以这个时间是啥,也是一个需要处理的一个,所谓的这个重新下问题,如果说有一个另外一个姿势,可能是a pose,那我过来就重新下,哎呀ok好吧。
我这个一方面时间的关系,另一方面我的这个,放在旁边这个iPad没电了,我看不了弹幕了,所以说大家有人有进一步的问题呢,那欢迎大家去我们的这个,我们的这个这个这个,QQ群和我们的这个论坛。
我们论坛其实之前很抱很抱歉,我之前一直没有注意论坛,因为好像一直没有人,很长一段时间好像都比,没有收到通知,我不知道有人在上面发帖了,那我们现在最近也看到,有几个人在论坛里,这个有些问题。
我们也会尽快地把它回答掉,那如果大家在这个听课上,还有什么其他的问题呢,那也欢迎在我们的这个,QQ群和论坛上,做进一步的交流,我们有助教会在里边,会跟大家做交流,好的那我们今天的课程,就上到这里。
那非常感谢大家,今天来跟我们一起来学习。
我们下周再见。
GAMES105-计算机角色动画基础 - P5:Lecture05 Data-driven Character Animation - GAMES-Webinar - BV1GG4y1p7fF
ok好那个很抱歉啊,稍微晚了一分钟啊,这个直播间开的有点晚呃,那不管怎么样,还是非常欢迎大家来参加我们这个game,4105,也就是计算机角色动画基础的第五节课啊,当然前面几节课,特别前面两节课吧。
这个数学讲的有点多啊,还是主要是简单回顾一下,我们或者说在这个基于呃,数据的角色动画的这样的一个呃方向上,我们的一些非常基本的一些技术,那当然今天主要讲两个部分了,就是可能。
首先我们说是基于数据的角色动画,那么主要是两个部分,第一个部分是数据从哪里来啊,那主要是讲讲motion capture,但是motion capture,我们也只能说是大概两说一下。
介绍一下motion capture,就是动作捕捉里边,我们可能有几种不同的捕捉方法,但实际上这个动作捕捉,里边有很多工程问题,这个很难去一一的讲清楚,那另外一部分呢,就是说我们得到数据之后怎么用。
那我们会稍微讲一讲,从这个嗯大方向上,就这个这个idea上,从这个这个基本的思想上来讲一讲,关于这个动作重叠项,关于动作这个转移和这个呃混合,然后关于这个用动作图,要motion graph来实现啊。
可交互的这个角色,动画的一些基本基本概念,基本方法,那主要是今天我们这样的一个内容,那第一部分呢就主要是motion capture,或者说我们如何来获取总啊,获取一个运动数据。
当然基本来说我们在做动画的时候,大部分比较呃就是最直接的一种方法,那肯定就是关键帧技术了,我们上节课也稍微提到过呃,我们总是会有动画师,他去一帧一帧的,或者说一个关键帧,一个关键帧的。
去把我们的整个的动作给建立出来,那当然在这个过程中呢,我们通常来讲还会需要用到一些差值技术,因为关键帧我们不可能,比如说这个这个动作可能是呃30帧,32vs 30帧每秒,那我那我去画30帧的话。
其实还是比较困难的,我最好还是说能够去少画一些,然后靠差值来产生这些这些动作,实际上30帧应该是说我们做动画的,是比较应该是最低限度了,但是很多时候我们需要的这个动作会更高,需要帧数会更高一些。
比如说这个像是一般游戏,我们最后都说最好是60帧,60f ps才能比较啊比较流畅,那其实还有像比如说vr啊,或者一些其他的这个特定的环境里边,我们可能还需要120fps,那这种情况下。
我们只靠关键帧的话也是比较难去实现,这个就整体来说这个代价会比较高嘛,但是不管怎么样,靠关键帧来形成的动作,总而言,这是一个非常非常劳动密集型的,一个一个工作,就是我们需要很有经验的一个动画师。
他可能他才能做的比较好,所以说我们大部分情况下,比如说在电影里边,而且游戏里边我们需要用大量的,比较自然的各种丰富,非常复杂的动作的时候,就是现在其实动作捕捉技术是一个,非常重要的一类这类的方法。
动作捕捉,其实除了我们一般来说的这种电影,游戏里边,我们就用它来产生来收集大量的动作,另外的话其实现在实时的动作捕捉,在很多方面也也有一些新的应用啊,比如说这种虚拟偶像。
那么像之前我们也看过一些这个视频,一个比如说一个一个一个,一个人穿着一个啊,这东西叫中之人,对他们叫中之人,就是说我可以穿着一个实时动捕设备,然后去驱动一个虚拟偶像,去跳舞或者去唱歌。
那当然还有这种元宇宙,其实现在ma他们做的事情,其实本质上是想要把我的动作,能够把它采集到这个虚拟世界里边去,那我们在社交场合,可以让另外的另外的人可以看到我的动作。
那另外还有像比如说专业体育体育训练啊,其实他们可以通过采集来收集他们的,这个呃动作,然后来分析动作的这个这个这个这个程度,你其实也可以帮助他们提高自己的成绩,我就想今年奥运的时候。
还当时他们还有一个有一个例,有一个这个新闻呢,就说其实今年奥运军团也是用了很多,类似的设备啊,类似的这个技术,来帮助体运动员来提高成绩,这样的技术,那另外其实也像比如像这个医疗领领域,那其实可以用动捕。
一方面我来帮助医医生去诊断,像是这种啊,比如说跟运动相关的一些疾病,还有像是比如说这种呃,附件就是受伤复健时候,可以帮助医医生来做一些诊断,做一些治疗的工作,还有比如像机器人领域,其实我用的还是蛮多的。
哎呦不好意思,像比如说我们可以用动捕来去实现,比如说无人机的这个跟踪,然后比如说机械臂的跟踪,然后定位这样的一些各种信息,各种方式就各种应用嗯,那当然动捕其实也是一个怎么说呢。
其实虽然说动捕本身的这个呃,历史不是非常多啊,但是实际上我们对动作的分析,其实可以可以追溯到比较老。
比较早期的一个时间,一个时间,就比如说早期,当这个摄影术刚刚兴起的时候,其实就有一些人尝试用摄影术来对动作,进行采集,进行分析,比如这个和这个非常经典的,这个非常有名的这个人。
这个edward my bridge,他们以前当当时就就是做出一些方法,可以用连拍的方式来记录这个动物,或者人的各种动作,那当然这里有一个非常有经典的一,个结论啊,就是说他们通过这个方式发现。
其实马在奔跑的时候,他确实是有一个瞬间是四个角,都是离地的,那这些现在也是涉及到我们经常来说,比如说我们什么样叫走,什么样是跑,我们知道竞走,大家知道是不允许两个脚同时离地的,不然就违规。
因为这两个脚同时离地就变成跑了,所以这个其实是指这个动作分析上的一些。
一些很有意思的结论,那当然其实这更加接近于动物的,其实应该是叫这种叫转描技术,就是以前大家在做这个,特别是在做动画的时候啊,很多时候我们直接靠动画师去画,它,有时候需要一个动画师有非常好的技术。
才能保持我这个这个不会出现这种呃,就比如说人体走形啊这种这种这种问题,那其实这种动画师一般是很难去这个训练,是比较困难的,所以说实际上,最早我们其实可以用另外一种方式。
其实可以通过把一个人真的人动作拍下来,然后拍下来之后呢,我们可以通过一种把它算,把它这个转描的方式,然后把它转化成一个卡通角色,或者转化成一个数字角色,首先这个技术很早就是在很多电影里。
就得到了很多使用,比如特别有名的,像是这个迪士尼的早期的一些电影,比如说像是白雪公主啊,像是这个爱丽丝梦梦游仙仙境,其实当时这也是一个说法,就是说实际的这个,当时迪士尼也是刚刚创建嘛。
所以说实际上确实他也缺一些,非常有经验的这种动画师,然后他其实用这种方法,可以很有效地,降低它的这个动作的制作成本。
那其实也可以保证我的这个作画的质量,但实际上就是你会发现,其实提醒你的有些动作片段,它会不停的转描到不同的这个电影里面去。
然后另外其实也是我们在备课的时候,也是突然也发现一件事情,就是说我们在那个其实我们国内,我国的最早的一部动画电影,其实也是整个亚洲最早的一部动画电影,这个是1941年的这个铁扇公主。
然后在这个b电影拍摄的时候,其实大家也知道,是在在这个这个这个战争年代啊,所以说其实拍的也是非常不容易,然后在这个电影里面呢,其实他们也是大量的使用了转描的技术,来降低我们这个设置的成本。
那当铁人公主呢,他的一个制作商是这个万氏兄弟,那其实万事群里必是这个,其实几个姓万的这个人,然后他们组成的这样一个动画公司,然后其中有一个非其中之一啊,这个叫做万籁鸣先生。
他其实后面拍了一个更加有名的电影,叫做大闹天宫,我想大家应该都看过,所以说实际上这个也是我们这个国内的,这个动画人的一个呃,非常有非常非常有,非常有这个热情的这样的一个故事。
那当然是在这个基础之上呢,比如前面都是二维的所谓的转变,我们其实之前也放过这个这个视频,就是实际上我们在做三维的动画的时候。
我们很多时候也是可以,利用转描技术来实现,就比如说我,我其实不是一个非常差的动画师。
我其实也说我可能用玛雅来说,我就只能简单的用一下ik啊,然后随便弄几个动作,但如果说想要我要去做一个非常复杂的,一个人的这个这个动画的话,那我其实也可以使用类似转描的技术。
就比如说我可以从我从网上找一段,比如说武打的视频,那我其实可以照着这个视频,然后把这个角色的姿势,那这个实现其实际上本质上也是一个转变,要的技术,只不过我们是在三维场景里把它实现的。
那当然这个过程时间本质上还是手动的。
那如果说我们把它自动化,我们就靠这个计算机的技术,靠这个这个图形学的技术。
我们把这个动作,能够直接从我们的输入的图像,或者是相关的一些呃信息里面,把它重建出来,那其实就是我们现代的这个动捕设备。
就是我们现在这个动捕系统,当然我们在动作系统其实有很多不同的,这种类型的,主要目的,就是说可以通过各种各样的传感器,然后把我们的动作,把我们的动作里的信息,然后把它重建出来,然后并且把它转化成我们数字。
能够存储的一些格式,那当然不同的这个动捕设备,当然有不同的优缺点,它也使用在不同的这种这种场合之下,那我们这里可以介绍几种比较简单的呃。
比较常见的这个动捕设备,但其实比较早期的,就是所谓的这种基于外骨骼的动捕设备,对像基于外骨外骨骼的设备,其实是这个是这个原理上是非常简单的,因为我们可以穿上一个外骨骼,然后这个我们在身体移动的时候。
我们自然会带动这个外骨骼来移动,那么我们可以测量外骨骼的,它的比如说每个关节之间角度啊,其实就刚好对于人的关节上的角度,那当然外部基于外国国的这类增补技术呢,其实最主要的原因,最大的一个问题就是。
所以实际上,比如说我们做一些比较难的动作的时候,或者比较危险的,比如躺在地上,那我其实穿着外骨骼基本是很难去实现的,但实际上我们但是另外一方,比如说我们做手部的采集的时候。
其实我们现在看到手的采集的时候,很多时候还是会需要一些这种手套啊,这种基于外五格的这种方式,因为相对来说,首先手的动作,可能对身体的影响没有那么大,其次我们对于手的采集,因为手枪来身体来说其实比较小的。
采用这样一个设备的话,其实可以针对性的把手动作,踩的更准确一点,比如说但是现在来说,我们其实基于外国的这个这个解决方案,相对来说不是那么多,不是那么不是那么多箭啊,那当然对于手来说。
还是比较常见的一类方法,那么另外一方法就是所谓的,基于这种惯性传感器的这个动作捕捉技术,那就是观察mu嘛,就是这个惯性测量单元,一般来说是所谓的六周六周六轴传感器,然后通过这三个传感器的信息的话。
我们可以得到,比如说我把这个一些传感器,绑在我的胳膊上,绑在我的腿上,那我可以通过这些传感器来计算出,我的胳膊和我的腿的相对运动,那既然这些这些运动之后呢,我们可以用i k。
然后或或或者优化的方法来去求解,每个关节的旋转,那当然这里其实有很大的问题,就是说所有基于惯性传感器的方法,总是会不可避免的遇到一个问题,就是漂移啊,就是这个传感器的漂移。
因为我们知道所有的测量都有误差的,而我们惯性传感器,或者是一个速度,那为了能够得到我们的位置的话,速度再积分是位移,然后位移加上我们当前的位置,那才能得到我们这个新的一个位置,所以总体来说。
我们其实是在用惯性传感器来说,我们是没有很难去得到一个全局的,呃位置的,那在这过程中我们会出现什么现象呢,就是说其实你会发现,可能每次我加了一个数,这个数大概是差不多,没有差很多的。
但是如果说我连着加加了很久,这个误差就会带来就会积累的越来越多,可能这个人在做着做着动作,它就会飘起来,或者现在地面下去,所以实际上这类方法的话,大家总是用一些其他的方式来帮助我们。
去减少这个漂移带来的影响,就比如说我们可以加c这种,加一些这种这种启发式的,这种这种啊辅助措施,就比如说我们知道人做动作的时候,我们其实脚应该是踩在地面上的,那如果说这个人飘起来的话。
那我知道他肯定是有什么漂移,有这个漂移了,那我可以根据比如说这种脚在地上,这样一个鲜艳,那另外一方面的话,我们可以用更加准确的传感器,那可以允许我们去做更长时间的,这样的积分。
然后也不会带来非常大的问题,那另外一方面,我们可以可能也许会加一些更多的传感器,就比如说可能有些比如说这种重力传感器,因为重力因为对我们来说,重力的方向总是固定的,还是一个向下的方向。
那这样的话其实这是一个全局的信息,可以帮助我们在这样的一个动捕过程中,来避免在这个方向产生过大的漂移,那类似的还有一些其他的,比如说这种磁场传感器,那我们其实知道我们的这个磁场的方向。
地磁场的方向在某一个小局部里,它是不会变的,那这个时候的话,其实你可以通过磁场的方向来帮助你,去,确定一些这个这个全局上的这种方向信息,然后lisa还有其他的,比如说光流传感器。
比如说我们可以判断一下这个我的视觉,我的这个拍摄的视频,他的这个动啊移动的方向来判断,我这个啊传感器带来这个偏差,但总的来说,这个基于对这个光线传感器的方法就是好,处是说什么呢,它是不需要。
一般来说它对场地的要求会比较低,就是说我其实可以穿着它跑步,可以跑的,但只要是在我信号能够收到范围内,我其实可以跑的比较远,另外就说我这个范围可比较大,可以跑,可以爬高,可以去做各种动作。
那缺点就是这个漂移问题,你总是需要想办法去解决啊,不然的话这个动作,其实漂移带来的这个这个误差,因为主要是说他飘的方向是不确定的,甚至你如果你修的话,你甚至有可能需要去每帧去去单独处理。
才能把它修到修的准确,所以实际上现在应该说事实上的标准啊,应该还是这个基于光学的这种杜补设备,那基围观的动物是个基本的配置,就是我们就是他会在这个人身上,然后去穿戴一些反光的这种这种标记点。
那这个不一定是反光,也许它自己会发光,然后在这接下来呢,我们会有一些这种,在一般来说可能有若干个镜头,若干个相机,可能6~8个或者更多的一个相机,然后分布在整个这样一个空间范围内,那这样每一个相机。
比如说对于反光的这种动物解决方案来说,这个相机一般来说它是一个呃,是一个这种红外线相机,就是它可以发出红外线,其实我们这个肉眼是看不见的,然后这个红外线打到我们这个标记点上,它会反光。
一般来说该点它是一个非常反光的,是一个漫反射的一个一个一个一个标记,一个球体啊,或者是一个一个一个点,然后他会把这个关系部分,光线反射回我的这个相机,那这个相机就会看到,在这个在这个二维平面上。
二维图像上会有个点,那接下来我可以通过若干个相机,来重建一个三维坐标的位置,那接下来我如果说我们,比如说我们在全身上,每个班每个关节,就是每个身体上我都放了若干个点,那么可以通过这些点的三维位置。
来反推出每一个关节的位置,每个关节的这个位置和朝向,那这既是我们的这个关键,传感器的一个基本原理,那咱这里用了一个最基本的技术,就是这个所谓这个多视角几何的一些技术,就是如果说我们有相有热巴的相机。
然后这些相机我们都是已经标定好的标的,好意思说什么呢,就是我们知道每个相机的三维位置,以及每个相机的这个内参和外参,就是它的朝向,那我们实际上就知道,如果说我们当知道所有这些信息之后。
如果我们说我们看到这个屏幕上有个点,那我们是可以知道这个点,比如从相机的这个传感器处罚它,像这个点可以做一条这个向量,可以做一条直线,那这条直线对方程我们是完全可以知道,那另外一方面,我对另外一个相机。
我们其实可以同样的做这条直线,那这样两条直线的交点,其实对应的就是我们这个发光的这个点,它的三维坐标,当然这是一个最基本的,最简单的一个一个一个模型,但实际上我们通常来讲,两个相机是很难去准确定位的。
因为主要原因是说它会,它还是会有测量误差,一般来说我们至少会需要三个相机,看到一个点,对于大部分动物兽来说,两项大部分论说,我还是需要三个相机能看到它啊,当然也是越多的话。
我们可以把这个误差降到更低一点,所以总体来说是,那比如说这一般大一点的都不是为,或者说比方说我们做一些呃比较,比如说多人的动作,那么可能需要更多的相机,因为它总是会有谁知道。
这样的话我一方面一个方向角度未知道了,我可以从另一方面看到这个同样一个点,那可以避免我们的这个啊测量的误差,但实际上这个光光学动捕,其实很大的一个问题在于这个场地啊,就是说因为我每一个相。
因为我前面提到每一个相机,就是每一个点为了定位它,我至少要保证有若干个相机,能够同时看到这样一个点,然后每个相机实际上因为它有,它会有一个朝向,会有一个视角的一个方向,那这样的话。
我们需要在若干个相机的这个,所有的这个视角方向的,这个他能看见这个范围的并集,在这个范围区采集,才能保证我这个相机是,他能保证我这个这个这个重建是准确的,所以实际上对于一般来说。
比如说我们想做一个8x8,8米乘7米乘7米这样一个空间,我们可能就需要12个相机,才能保证这个效果比较好,而且更大的一些场景,比如说像是这种我之前也见过,也去参观过一些大型的这个动捕。
这个场地他可能放了100 100多个相机,然而场地非常巨大,可以有几十米乘以几十米,然后可能这个高度可能有有有有,有好几层logo啊,这种这种场地的话,就说他可以做很多事情,比如说一般我们做这种。
比如说武术啊,这种就比较容易做的,他甚至可以比如说我可以采集一些动物,比如说马匹在里面跑,我可以把整个动作给踩下来,那当然缺点就是说这种场地,那当然非常贵的了,因为这也是这个这个。
因为毕竟一方面相机很贵,因为后面这个场地很贵啊,所以说实际上这个一般一般人,我们要想用这个技术,用这种设备的话,还是需要,还是需要点这个这个这个这个经费知识的,那当然还有另外的其他问题啊。
就是说我们一般来说做这个动捕设备啊,光学动捕,那其实我们需要这个非常重要的部分,就是这个标记点,那我们其实比如身体上,我们一般来说会放上,比如说40多个或或者50多个标记点。
然后通过这些标记点来重建这个人的位置,来重建人的这个整体的这个这个姿态,那当然对于不同的部位来说,我们可能有不同的标记点,这个这个呃,不同大小的标点和不同表演点配置,就比如说做手做手的这个采集。
那你可能需要稍微小一点点标记点,或者对于脸来说,其实标记点会更小一点,就可以看到,这里其实是非常典型的,这个踩这个脸的表情的时候,我的表弟点的数量,而这个世界还有另外一个问题,就是说就是动补。
就是本身来说,我每次测量我总是会误差了嘛,而且随着我的距离增大,这个误差其实会变得更大一些,所以说你会发现,比如他如果说我需要踩脸的这个表情,这样动作的话,通常来讲它会再额外去带一个摄像头。
这摄像头是直接对着脸的,不然的话我只是靠外面,就是刚才像这种场景,外面这一圈摄像头它就拍脸的话,基本是拍不到的,就算能拍到,但是会非常非常不准确,当然另外还有一些问题,就是说我们在做这个动捕的时候。
其实特别是做一些比较复杂的动作的时候,你会发现这个标题点经常会丢失,这丢失是什么意思呢,就是说比如说我可能在做动作的时候呃,会遮挡住我一些表格点,比如我趴在地上,那我胸前的标志点全都看不见了。
那或者说比如说我做一些这种这种这个啊。
比较激烈的动作,比如说在这爬,那这种动作很有可能会把我的标记点,就是给蹭掉了或者蹭歪了,然后这些其实都会对我们这个光纤动物,带来很不利的影响,就主要说我们因为本质上,因为我们需要做的事情。
是为了能够求解关节的位置,我们需要知道哪一个标记点,就我看到的是完全是分不开的,就是一堆白点,我需要知道哪一个白点,对应的是哪个标记点,然后这些标记点如果它丢了之后呢。
我们需要怎么去把这个丢失的这个决定,把它给补回来,所以实际上动捕里边非常长的,一个非常麻烦的一个一个过程,就是说我需要去补点补点意思就是什么呢,我采集的时候丢掉的那些标记点,就比如说被遮挡了。
或者是掉了,或者说比如说他有些标记的时候,他去这个认认错了。
比如说应该是腿上的标记点,我认成手上了,这个其实也经常发生,为什么呢,比如说我可能这个人他会做一个做一个。
比如说手摸膝盖的这样一个动作,那这个时候比如说手上的标记点,你会发现它会膝盖的标记点,会在某一时可能会重合。
那重合之后又离开了之后呢,那其实有些时候他会用这个系统做不出来,这个到底这个标题原来是在手上,还是在膝盖上,所以这时候他也会会出错,所以总的来说,你会需要做很多很多这样的后处理来完成。
这个比较好的这样的一个一个啊技术,这样一个一个动物动物的过程。
其实我们之前也跟那个,东北的一些公司聊过,就是他们对这个后处理,基本来说是按秒算钱的。
是按照这个我采的数据的,这个这个这个这个时长来来进行,就是处理的这个动作的时长来进行,算起来,这还是很高的,可能大概有个几秒钟,你就就就就就要几百上千的这样的废物,当然这个one。
其实我们这前2年也是有些工作的,就不就是在这个同行学领域啊,就是说比如像丹尼holon,他们在e b做过一些这种。
来简化这样的一个标记点标定的过程,那这个其实国内我们这也看到一些工作。
好像是清华的这个团队,也做过一个相关的工作。
但这是动补的一些这个光学,动捕的一些是一些一些一些问题,那除此之外呢,就是除了这个标记点这个位置问题啊,其实还有很大的一个问题,在于标记点本身它是有体积的,这个体质会带来一些什么问题呢。
就是说比如说我想我们之前也踩过一些,我们当时想上一个动捕去动捕的演员,我说我们想采一段这个钱果番哎呀,那这个动物园就非常非常辛苦,因为比如他的身后是有很多这种标记点,然后他做一个前滚翻。
这些表演都很个人,所以你会发现其实就穿这种衣服的话,你的这个首先这个标题点本身,会影响这个你的动作,它会带来一些,可能会带来不必要的这个这个受伤,另外一方面呢,比如说你要有要去采动物,就采动物。
其实我们之前看过一些视频,就是说采这个猫啊,才不狗啊,这些动物其实还好一点,就是你可以它如果训练的比较,比较比较那个呃比较好的话,那他其实可能还是会比较配合,但总感觉他也是不太舒服的。
因为本质上它身上有东西,你会发现这猫和狗它会不停的抖,要是更其他的动物的话,你就是靠标记点来标来踩,还是更加困难一点,所以时间还有很多,其实也是一个非常热点的研究问题啊,就是说我们如何能通过。
无标记点的动作采集来实现一个动作捕捉,那其实最最常见的,或者说现在也是比较常用的一种方法,就是这个基于多视角相机的,这种无标节点的动捕,其实本质上来说,跟我们有标记点的这个关节动物,其实是差不多的。
就是说我其实也是通过若干个,已经标定好位置的相机,而这些相机都是同时拍同一个场景,那这样的话在这个过程中,其实还是通过识别这个图像里边的,人的这个关节啊,人的这个位置,肢体的位置。
然后再利用我们这个都视角几何,来重建出每个关节,每个点的位置,那这个其实这类方法相对来说还是,怎么说呢,就是其实嗯但是我不是做广告了,我就是随便网上找了一个,找了一个视频。
但是这个从当这个作为他们宣传视频来说,应该还是还是会弄得不错的,但实际上因为这个光学,都用这个标记点呢,然后为什么大家都用这个呃,就是这种红外线,这个这个这个作为作为输入呢。
就是主要是说我这个标记点它是非常亮的,反光嘛,我可以非常准确地定位它,在图像里定位它,但是如果说我们没有这个标的点,我们只是纯粹靠视觉的输入的话,那你很多时候你是很难有些时候很不准的。
就他会受到光线的影响非常大,另外说比如说这人穿的衣服啊,穿的衣服的颜色啊,其实有时候也会带来一些影响,然后还有就是可能有些关节,比如说我们我们其实之前也试过一些,这种啊,基于光学动捕的。
就是基于这种多多多试讲动捕的这些设备,就是就是有几个关节,特别是比如说踝关节和脚趾,还有手的位置,其实很难踩到整,就是因为像大关节,比如说膝盖和脚脚踝的位置,我们相对来说比较容易确定的,但是再细一点。
比如说我想需要去确定角面的方向,但是脚面的方向我需要知道脚趾的位置,这个时候其实很多说,它是很不太容易踩得准的,当然这个技术还在不断的发展,不断的迭代了,我们当时也是可以逐渐的看到,一些新的技术。
能够把这个问题逐渐解决掉,那他还有些其他的方法,比如说这种以前大家可能有些同学知道,像这个xbox当年出过一个呃,非常便宜的深度深度摄像头,就是叫connect,但是现在connect已经已经停产了。
如果没记错的话,那其实深度摄像的时候,其实给我们一些信息,因为我们只是靠这个视频的话,我没有深度嘛,我们只能知道他的这个二维的位置,那深度其实给我们一个三维的一个,一个位置。
那其实从这个通过这个信息的话,一方面我们其实可以重建出这个人的这个,表面的这个mesh表面的这个这个这个形状,另外一方面,我们其实也是可以去重建这样的,一个人的姿态,所以其实knight当时出来的时候。
其实他的打的卖点就是说,他其实可以实现这个实时的人,姿态的一个动捕,然后其实允许你去跟用一个你的虚拟形象,在这个这个这个游戏环境里去去互动,那其实猫现在大家玩switch。
switch有一些这种像是健身类的,像健身环啊,还有一些这种,比如说有啊一个拳击的游戏吧,就是它其实本质上还是通过两个传感器,就是你放在手柄的传感器去踩动作,其实knap其实非常超前。
当年就是说我们可以直接踩这个动作,但是但是技术上来说,其实还有相当大相当大的难度的,因为主要是说我们这个深度相机,其实它提供的信息相对来说还是偏少的,就是说比如说首先遮挡的时候。
我看到深度就是混在一起了,那这个时候你怎么分辨哪一部分人,前面哪哪一部分人使用哪一部分是腿,或者说比如说我身转个转个转个身,那我怎么分辨哪个是哪边是左边,哪边是右边,那这里其实还有很多技术上的问题。
那这个其实也是一个,就是现在当然大家不用connect,但是也有一些新的这种基于这种rgb d相机,一般都是就是这个,它会提供一个非常比较准确的,然后这个深度信息来帮助我们去做这种,三维三维动捕设备。
但相对来说这种这几种方法的,所以market list的方法就是从精度上来说,还是比起现在这个,就是这种基于光学动捕的这种方法,就是从精确度上和这个鲁棒性上,还是稍微差一点,这个确实也是必须要承认的。
这样的一个差距,那除此之外还有一些其他的方法对,就是这种方法我们应该是从我的角度讲,我觉得应该不能叫做动捕设备,动捕风,不好意思,应该不能叫做动物方法,这种方法,一般我们应该是叫做动作估计方法。
或者运动估计方法,就比如说最经典的最近这个问题,就是说这种单镜头相机的这个动作,估计为什么这是一个叫动作,姑且不叫动作捕捉呢,呃动作捕捉呢,因为我们知道这个单进单视角啊。
其实它是一个啊信息不全的一个问题,就是我们其实把一个三维的姿态,把它映射为二维,其实我们会遇到会碰到一个歧异性问题,就比如说我同样的一个因为有视差嘛,所以说我可能会有很多姿势。
在这个投影之后看起来是同一个姿势,那这种情况下我怎么重建呢,我其实无法判断哪个是对的,那通常来讲,我们还是需要一些额外的一些信息,比如说我看过很多很多动作之后,然后用这些动物做的这个相似性。
来帮助我们确定应该是哪一个姿势,那这种情况下,他并不是真的就是就是准确的估计,他只能说是呃,并不是真的一个准确的动作,而只是说我在一些最最大的可能。
基层基础之上,我做了一个估计,那与此相关的还有一些其他的,就比如说用一些比较啊,sparts,就是说比较少少量的传感器来进行,动作捕捉,就比如说我们前面讲这个惯性动捕,或者说我们前面讲的这种光学的。
这种基于标记点动部,我们通常来讲,需要全身每个关节都绑上,至少一个这样的标记点,那我们可以确定这个点的位置,那其实如果说我们可以用更少一点标记点,来实现这件事情,就比如说比如说我们只有五个标记点。
或者六个标点,我们只在手和脚上榜样这些动啊,这些这些标点的位置,那我们其实以某种程度,也可以估计出我们全身的动作,比如说最简单我可以做i k嘛,当然i k其实你需要解决,解决一些像是这种这种姿态。
方向不合理的问题,你还是需要解决这个这个飘面的问题,所以这个其实虽然说这个,这个其实本质上也是一个需要解决的问题,那咱可以再进一步,比如说我们前面都是六个,至少六个标记点或者五个标点。
我们至少知道手和脚的位置都是知道的,那其实还有更更更进一步的,就是说我们只有三个标记点,或者只有一个标记点,这是什么情况呢,就是vr的场景里边,比如说我只有vr头盔,就vr头盔。
那这个时候我只有一个标记点的位置,那通过这个标记点的位置,我们怎么去重建全身的动作,那这是本身也是一个非常非常去这个,去这个信息不足的一个问题,另外比如说像三个标点,就是我头有头盔和两个手柄。
像比如说我带一个这个这个htc wife呀,或者是那个这个oculus,那其实他可以给一个比较精确的,这两个头盔和手的位置,那这个时候我上半身动作相对是比较简单,我可用i k,那这个时候腿怎么办。
所以其实你看到最早的这个meta就是facebook,他们以前这个demo是没有腿的,那没有腿,这个其实糖尿特别诡异啊,感觉好像一堆人在那飘着,好像鬼魂一样,但是后来他们今年啊就是长期。
上个月放了个demo啊,上上个月哎呀说终于有腿了,然后这个感觉好像这个进这个,进步了很大一截啊,当然这个后来要也是有人这个问了一下啊,还问了一下他们这个,这个他们公司的这个啊公关人员。
就是这个公关系的这些人,好像说这个至少这个弹幕本身并不是,并不是真的重建出来了,这个demo还是动捕出来的这个结果,但他们只是说,ok,我们我们现在展示的是,将来我们会实现的效果,那这个能不能实现。
我们也不知道,但实际这个技术实际上当年也是,其实以前有一个公司叫做那个kia,我不知道,可能有些这个这个业内的这个人,大概听过这个公司,就他们当时主打的就是以其中一个产品,就是说我可以通过三点来。
去重建全身的姿态,当然这个公司现在那个网站已经没有了,那公司这个公司被苹果买了,所以说可能这个也许苹果的vr,可能会用上这类似的一个技术,所以说到底跟my time跟facebook哪个会先出来。
我也不知道,当然这个问题本身确实还是一个iphone的问题,我们在这个第一节这个introduction,这个就是概论那一节课上,其实我们也提到嘛,就是说你你总也是就是靠纯靠给matic,方法的话。
你还是很难去完整的重建,我的这个这个比较准确的,比较物理准确的这样的动作,那时间如果想要做的更好的话,那我们其实还是需要一点无理仿真的方法,那这个其实我们之前做过一些工作,但是总体来说。
现在还是离这个真正的使用还是有点距离,虽然说还是有点效果,但是从稳定性上还是有需要进一步的提高,ok那我们其实前面主要是简单回顾了一下,我们这个动捕这样一个,其实动捕是一个非常大的topic。
就是除了简单的这个,像比我之前前面提到的这些问题之外,还有像比如说怎么做retargeting啊,怎么去,就是就是比如说像这种这个设备,本身可能是这个比较廉价的,那这种情况下。
我们怎么去同样实现高质量的这个东部,所以这其实还是有很多问题需要解决的,那我们接下来再做下一个问题,比如说当我们假如说我们已经得到了,非常好的动捕设备和动捕的数据,那当然这个动捕数据实际上就是说。
比如说我们如果想平时常用的话,就是我们只是想用一下,想想想想玩一玩,那实际上现在有很多开源的动捕的数据库,我记得好像之前忘了哪一节课,我好像已经提过几个,就比如说非常经典的c mu。
就是凯南基贝隆大学的这个动物数据库,一个非常老了啊,也对,但是非常非常的大,就非常非常的全嗯,当然缺点就是这个动捕的这个质量,稍微差了一点,因为这个实际上其实我觉得可能有一部分,可能是。
但当时他们的学生,所以说有些质量不是特别好,然后还有一些新的,比如说像是you be s ub,他们当时也供公开了一个数据库,叫了fn,然后这个数据库其实比较全,然后动作这个内容也比较多。
所以其实这现在公开的一些数据集,也是有的,其实我们也逐渐看到一些新的数据,再在公开出来,然后这些时间,如果说我们平时想要去尝试一下的话,其实可以拿来用嗯,但不管怎么样,假如说我们有这个动作数据之后。
我们该怎么使用呢,那当然我们其实我们上节课也是提到了,像这个b位置在这个动捕文件啊,就是一般来说当我们动捕之后,一部分是说我这个角色动捕的时候,使用的这个角色,它是什么样的一个大小,什么样一个姿态。
那就是它的teapot是什么样子,其次呢那我们接下来会有一些数据,那一般来说我们会表示成,比如说关节旋转或者是关节旋转,加上这个关节的位移,或者是类似的这样的一个输入,总而言这是一大批一堆这样的动作。
那为了能够把这些数据能够用起来呢,通常来讲我们还是需要若干个环节,那首先来说我们需要做这个动作重叠项,因为我们动捕的人,因为我总是从人身上踩的嘛,我们想要把它变成一个虚拟角色身上,那我们需要一些重定向。
重定向基础之上呢,我们可能需要对动作进行一些编辑,然后呢我们需要对工作进行连接啊,或者混合,然后最终就是就比如说放在一个动,动作图里边,让我们能够实现这种可交互的,这个动作的这个组合和生成。
那当然第一步呢其实就是动作重定向了,那我们其实前面上节课稍微讲了,讲这个非常非常简单的动作重叠项,但动作重叠项本身是一个非常大的问题,就是说因为我们面临着什么问题呢,是我们痛苦的时候是一个人。
那这个人他的骨骼上,可能就是可能是十几个关节,或者22 20 20多关节,但我们的角色可能是非常复杂的,就比如说我们很多很多角色,比如说我们的这个比如动捕的时候,我们这个脊脊柱上。
一般来说可能就2~2个或者三个关节,就是最多了,但实际上我们在真正的角色里边,可能脊柱场景会有就七八个关节,那类似的,还有一些,比如说我们在真正角色上,我们可能会有这个比如说有他有衣服,有这个袍子。
有斗篷,那这个斗篷,所以我运动它会它会,我希望它会产生一些这种形变,产生一些移动,那这些很多时候我们可以通过一种额外的,这种啊随动的这种骨骼来实现,所以谁就算真的逊角色身上的话,你会发现这个骨骼的数量。
骨骼的名字,骨骼的这个长短,然后甚至骨骼的这种这种这种突破结构,那都可能跟人不一样,就比如说有些时候,我们可能需要用一个人的动作,去驱动一个人马啊,这人马那其实是六条腿的,哈哈那这种事该怎么办。
所以说实际上这是一个很复杂的一个问题,然后我们通常来讲,比如说如果retirement做的不好的话,那基本来说常见的几个缺陷啊,就比如说这种浮空,比如说脚,那就这个重庆向之后,这个这个人就飘在天上。
或者说脚在地上打滑,或者是比如说这种穿模的问题,就比如说这个一般来说会出现在,比如说这个一个瘦人嘛,然后放在一个非常卡通的一个角色身上,比如说这个角色可能有个大脑袋,那你可以很容易的想到我做周末的时候。
我一伸手,那肯定我就穿到脑袋脑袋里面去了,但这个整体来说,这种方这种这些问题其实都不好解决啊,就是现在大部分能够比较好的实现的,方法呢,应该主要还是靠一些手工的方法,去去单独去处理。
那当然这个整个动捕的这个重叠,像这样一个流程啊,大概就只是写着写啊,就是拍脑袋拍了一些,就比如说我们首先因为这个名字不一样嘛,我们首先就建立一下,这个从动捕的数据到虚拟角色的这个关节,它需要做一个映射。
然后需要去调整一下我们这个动捕的大小,因为是很多时候,比如我们动武他的这个单位可能是米,或者是有些时候英寸嗯,但是我们这个虚拟角色,他单位市场我不知道了,有时候你可能导进去之后。
你会发现这个角色跟我们东部的骨骼,差的这个体积差了很多,但通常来讲的话,我们会需要,比如说我们知道,这是一个走路比较多的动动作,那么通常来讲我们会把这个根关节,因为主要是根关节这边的这个位置。
它的位移比较重要,我们可以把根关节的位移,我们整体上加一个加一个这个缩放,比如说把它至少缩到,比如说腿的长度,跟我们的虚拟角色长度差不多,那胳膊什么的可以不用先不用管,那这样的话。
至少可以保证我们在走路的时候,他脚不会飘在天上,那除此在此,在此之外呢,我们还需要去处理一下,比如说这种关节t pos的这个区别,那我们之前上节课其实讲了一下t pose,但是大家很多同学在在做。
我们的这个lab一啊,就是实验一的这个这个,第三个就是第三个部分啊,就是关于这个t pose和apples之间转换,那你需要做一些计算,其实那个是非常简单的,就是所以说我如果只有t pos和a的区。
转这个区别的话,我觉得这个其实是非常简单的问题,但是一旦说我这个身体长度不一样,或者说是骨骼数量不一样,那这个就就变得复杂一点,那么这里其实有些有些非常简单的这个嗯,启发性的一个启发式的一些规则啊。
就比如说我可能有些关节在,在我的仿真角色啊,在我的新角色里有,但是在我的那个动物数据里没有,那我们其实可能有些处理,比如说是不是把这个旋转设成零啊,或者是不是这个旋转,应该是前后两个关节的平均值啊。
那这个其实有很多你需要去调整的部分,那除此之外呢,其实最终我们还是需要去解决,这种穿模的问题,那这种时候我们一般还是需要用i k,来做一下这种,比如说foot skating或者floating。
这是其实i k总是不可避免的,所以说我们上节课讲i k,我是上上节课讲ak,实际是就是ak对于整个动画,就是基于这种运动学的动画来说,是非常非常重要的一类方法,对,然后那个其实还有另外一种方法。
就是说我们下面这种动作捕捉啊,就是其实我们本质最终还是记录的,是关节的旋转,那我们发现在我们动作重温下这个过程中,时间很多时候,我们是因为我们直接copy了关节的旋转,所以说可能会带来一些穿模的。
因为穿模问题本质上它是位置带来的问题,而不是这个就是只靠旋转去直接解决,它还是相对比较困难的。
所以时间还有一些新的一些提法,也不是新的了,就说有些这个业界的人才,能够提出一些新的这种说我们表示方式,如果我们可以不用记录关节的旋转,那我们可以反相反的,我们还是有点像我们前面提到的。
像这种六个六点追踪这种,我们只需要去记录像,比如说手的位置,脚的位置,还有比如说关键关节的位置,比如说根关节的位置,还有比如说这种啊髋关节的位置,然后我在做重定向的时候。
我只需要重定向这个相应的这个位置,然后之后呢再在新的角色之上做i k,其实也就是从从实践上来说,其实可以实现比较好的效果,但这个其实但总的来说,如果说你要达到这样的效果呢。
你还是需要相当多的这种工程上的这种,这种这种处理的嗯,但这是育碧的,育碧,当时那个放了一个非常非常专业的一个,一个一个demo。
那其实也是现在一些新的软件,一些软件,比如说unreal啊,real其实是支持这种i k rig,就是当你需要手工去创建所谓的i k,i k的这个retark的链,然后我再去retara的时候。
相当于把retara练这个这个这个关键位置,这个冲电线过去,然后再去这个求解i k来还原这个动作,ok然后对这只是关键重定向的问题。
那如果说我们已经重叠很好了,我已经有一些动作,这些动作已经能够,在我的虚拟主的身上去播放了,那接下来呢就是说我们其实很多时候,我们踩动作嘛,我们也只能是一段一段踩,我们很难去呃。
就是一方面是我们只能只能一段一段踩,另外一方面我们做动作的时候,基本来说,可能我想要的几段动作,是分散在不同的动捕数据里面,那么很多时候,我们需要把这些动作组合起来,连接起来,就完成一个更完整的动作。
就比如说我们这里我们有一个跑步,有一个走路,那我想实现这个人从走着走着,突然就跑起来,那我该怎么办呢,那就是这里其实就涉及到,我们需要做一个动作的啊,连接动作的动作转移,那这个方法其实比较简单了。
就说非常直观了,就是说比如说我需要做多的转移,那我需要从第一个,从第一个动作里边找出一帧,这针一般来说可能是脚落地,那一帧会比较好一点,然后在另外一个动作,比如跑步之间找出类似的一针。
这两针其实看起来是差不多的,然后找到这两针之后呢,我在我把这两针在时间上做一个对齐,就比如说我在,就是我认为这两针在播放的这一帧的时候,我下一帧就应该是另外动作的另外一针,然后呢。
其实我最直接最直接的方法就是,我直接做一个切换就可以了,把前一个动作放完,放到那一针的时候,我直接切到另外一个动作再去放下一针,那当然这个问题这个其实确实可以做了,但是这个其实大家可以想一想。
这个效果也不会太好了,因为主要是他会有非常明显的间断,那这个阶段会给我们带来一种这个,这个动作,首先就是动作不连续嘛,它效果就不是特别好,所以实际上我们通常为了能够做这个,平滑的一个转移。
我们不大会说会在这一帧上直接做切换,除非说这两帧的动作是完全相同的,但是很多什么时候完全相同,其实确实有些时候完全相同的,为什么呢,就是说我们一般作为比如说游戏,为了游戏啊,我们专门去动补一个动作。
一个循环动作,比如走路,他们一般会有一些要求的,比如说我所有的动作都是,比如说从一个呃自然姿态开始,然后比如说一个端枪姿态,然后端枪自带我踩一个动作,就是比如说我做一个挥拳挥拳的动作。
那我就可能挥完拳之后再回到这个姿态,然后或者比如说这个跑步或者走路,其实它都会保持一个差不多的一个姿势,那这种情况下就可以保,基本来说在我切换的时候,可以差不多保证我是同样一个姿势,那这种话。
我们就可以不需要做后面这些处理,但是很多时候,我们在这两个地方不太一样的时候,我们还是需要去做一些所谓的平滑操作,就简单来说,就是说我们实际上我们在这个切换的时候,我们不能只考虑一整。
我们通常来讲会把这个输入前面那个动作,比如走路,把他这个后面那一段,就是从这个这个切换点切换这一帧的,后面这一段,还有切这个切换后手动的前面这一段,我们把这部分都同时考虑进来,那接下来呢。
我们其实可以在这两个动作之间去做一个,逐渐的一个差值,就说这个怎么用一个参数,这个参数从零变到t啊,从零变到一,就是说从一开始这个地方,就是在这个切换前若干针开始,然后到切换后若干针做过一个平滑的渐变。
那在这个过程中呢,我们其实可以很简单地,采用我们这个线性差值,然后让我们最终的就在这个平过渡过程中,的每一帧是对,是等于它对应的这个对应的这个混合参数,然后用它去作为这个差值参数来进来。
对两段动作的对应阵进行差值,那咱这ile其实代表了一个真的,一个帧数的这个指标,就真正经打一针,ok那当然这个时候其实线性差值也不是,就是就是很多时候我们可能也不一定,非用到现非非得要使用线性。
它只有我们其实有很多其他的一些函数,比如说这个这个就是从更加通用来说,其实这个t本身可以是一个一个,比较更加平滑一点的函数啊,比如说可能用二次函数啊,或者说一些比如说exponential啊。
就这种这种指数函数之类的,来去实现一些特定的更加平滑的一些效果,那当然实际上就是这个这个杀人的过程,我们其实也不一定说是,一定是要两边都取若干针,其实可能比如我只是在目标的那个动作上,多取一点帧数。
然后做这个平滑,或者我们也可以在这个起始前面那个动作,我们在结束之前多取一些针,然后做这个平滑,其实都可以实现比较平滑的一个效果,那当然其实如果说我们确实这两个动作,都是到这就为止了。
我只是到这一帧就没有其他动作了,那我们也可以做其他的一种方式,就比如说我可以计算一下,这个中间这个偏移大概有多少,然后可以把这个偏移,逐渐的把它去混,合到我们这个新的动作里边呃,下一个动作里边去。
那当然方法上还是说我在下一个动作,还是提取前面若干针,然后用一个参数,逐渐的把这个偏移给混合到我们的,这个混合到我们的动作里面去,那当然类似的,我们也可以去混合前面的动作。
那这个在我们使用了这个差值之后呢,那我们其实可以看到,像刚才我们这个问题,这个动作现在这个差值看起来很平滑了,虽然说这个虽然说平滑,但是这个人看起来不太对啊,因为他首先他突然转了个向。
其次这个感觉明显脚在地上打滑嘛,所以说其实我们这只是一个动作,连接的更加平滑一些,但是其实本质它并不能完全解决,我们这个动作,这个比如说打滑这种问题,当然就这个就就这个例子而言,其实他对他打滑。
他就是他这个动作打滑,其实最主要的一个原因是什么呢,是说我们这两个动作,其实本来是一个是向左走的,然后那个跑是向右走,是向右走,但是这个跑是向左跑,那这个时候下我们没有做任何的这种对齐。
那我就直接强制的让这个人去转个身,过去了,那这个其实是结果肯定是不是很好的,所以实际上为了能够实现比较好的效果呢,我们其实首先还是应该先把我们的目标,下一段动作跟上一段动作做一个对齐。
那对齐之后我们再去做这个差值,那就是这个动作看起来更加平滑一些,那当然这个对齐其实是涉及到一个问题,就是说我们该如何去对齐,或者说我们该对齐到哪里,但这里其实有很多不同的这种,实际的这种方式。
但我们这只介绍其中一种,就是说所谓的这个一个非常重要的一个,参考系啊,叫叫facing frame,有谁叫hiding frame,或者有谁叫什么叫什么。
这个moving frame反正是各种各样的名字,但大部分说的是同一个东西,就就是说我们就假设这个人,他在走路的时候,我们有一个参考系,有一个坐标系,这个坐标系是跟着这个人一块走的,它的原点。
坐标系的原点是是,比如说就绑在这个人的这个根关节上,但是呢我们一般会要求说是什么呢,就是这个原点是啊,不是他不是绑的更快,二是它随它跟跟关节一样移动,但是差不多是跟关节的位置,在地面上的一个投影。
那这样的话其实可以,只要可以避免我这根关节,因为我走路的时候,其实我的跟关节,我的腰是不断伤害移动的嘛,这会带来我这个呃这个这个坐标系的,不是特别稳定的这样一个问题,另外呢。
我们其实也是要求这个坐标系的某一个轴,一般来说可能是z轴,然后它会指向我的角色的朝向,那当这个朝向其实有很多种不同的定义了,就是说也许是我的速度的方向,或者也许是我面向的方向,面向的方向是对应什么呢。
比如说我们可以认定义为是z轴的局部坐,标系的z轴在世界坐标系的方向,但是为什么是这种呢,其实这是说明,其实我们是基于这样一个假设,就是我们现在假设,我们现在的动画系统是在y up这样的一个。
这个坐标系里面,其实one up是这个动画和图形学,图形学领域的一个非常常用的,一个一个坐标的一个设置啊,但是也不是玩不是绝对的,因为可能像unity是wup的,maya是wup的。
但是呢那个unreal好像是z up,如果没记错的话,但这个why,我觉得wy up,为什么是觉得是比较自然的呢,因为主要是我们做渲染的时候,因为我们知道我们渲染,然后我们要渲染成图像。
那图像上我们一般定义两个坐标轴,横着是x轴,竖直是y轴,然后同时在这个渲染过程中,我们其实有另外一个深度方向是z轴,一般来说我们是这样定义的,所以说这样自然而言,自然而然这个向上的那个轴变成y轴。
当然机器人啊,或者一些很大的其他相关领域,他们会愿意用zapp的,但我们这里假设啊,假设我们这个后面就是大后面几页p p t,我们都是假设,我们是在一个y up的一个坐标系啊。
同时这个角色的teapos,我们知道我们建立一个teapot,它会有一个正方向,那么一般来说我建议假设这个正方向,他面朝的方向是z正z的方向,那在这种设定之下,那个x方向一个是哪一个方向的。
x y应该是右右手的方向是吧,xy哎呀不对,我发现我这就去画反了,哈哈哈,其实x应该往这边往往往这边去才对啊,这样这样才是对的,其实应该向左边是x,但这个这个不是很重要的,就是我们假设有这么一个坐标系。
那总体来说这个所谓的facing frame,我们知道一个坐标系,它有两个关重要的这个定义啊,就是两个重要的量来定义吧,一个是它的方向,就是它的这个朝向,或者说这个坐标系相对于玄机坐标系,它的朝向。
然后另外一个就是它这个坐标原点的位置,那来这个facebook frame啊,这个facing frame就是我们朝向坐标系来说,我们一般来说会假定它的这个坐标旋转,朝向是只有只有沿着y轴的旋转。
有时它只会在立,这个只会有平这个p,这个这个竖直方向的那个旋转,然后其次呢它这个坐标原点是只有x和z,就只有平面的位移,那这是我们的一个假设,那当然在这种情况下,其实这个旋转啊,这个位移是比较好定义的。
我们可以就定义成,他就是过根关节在地面的投影,那当这个理论说,我也可以用其他的方其他方式的定义,只要我这个定义本身是一个比较啊确定的,就对所有动作相同定义就可以了,然后这个旋转其实有很多种,不同的人。
用不同的这个常用的这种啊定义啊,就比如说我刚才说到了,一般来说我们可以把朝向定义成,我跟关节局部坐标的z轴,它的方向和定位是朝向,那我将根据这个定义的话。
那我们可以认为这个facing frame的朝向,那其实就是这个旋转嘛,他就是说把这个世界坐标系的z,旋转到我局部坐标系的z轴,它所需要的这个旋转,那对应的就是我这个坐标坐标系的朝向。
那其实还有一些其他的定义,比如说我知道柱根管子的朝向,有些时候不是那么精确,因为做一些动作的时候,可以玩上半身往左转,下半身往右转啊,比如我现在这个这个这个这个坐在椅子上,坐在摇椅上,就经常会出去。
会会做这种动作嗯,那有些还有一些其他的定义啊,就是说比如说我们可以为它更加稳固,为了更加体现出我上半身的动作啊,那我们其实可以定义说,比如说我两个肩膀,两个肩膀的连线要按照我们这个角色定义。
它应该指向的是我角色的局部,坐标系的x轴,那类似有两个这个髋关节,就大腿根的两个连线啊,这根连线其实也对应的是x轴,那么它的平均方向的话,其实我认为就是就是我当前角色的x轴,那我可以同样的定义说。
我这个facing frame的旋转,应该是这个x轴跟世界坐标系,x轴的这个这个这个对应的这个旋转,那当然还有一些其他的方式,也有一种比较常用的是说,我可以把一个旋转给做一下分解。
把它分解成两个子旋转的乘积,那这两个子旋转,第一个旋转是只有y轴方向的旋转,然后第二个旋转是沿着xz平面上的,某一个轴的旋转,或者说在这个再请下,再具体一点,就是说我可以把总是可以把一个旋转矩阵。
把它分解成两个,然后这两个第一部分是沿着y轴的一个旋,转或者任意一个轴的旋转,总而这是凡是某一个轴,另外第二个旋转是对于y轴来说,它是x z的旋转,或者在通用在这个广义点来说,就是说我第一个是一个。
沿着某一个轴的旋转,第二个是沿着跟这个轴平行的垂直的,那个面上的某一个向量的旋转,也就是说比如说这个大家说这个,比如说这样一个旋转,这是一个总的一个r的旋转,我可以说先是沿着x z平面上的一个轴。
ux z,你看这个轴转了一下,转完了之后呢,我再沿着世界坐标系的y轴又转了一下,那这两个旋转的乘积啊,组合呢其实就是我的总旋转,我总是可以做这件事情的,那接下来呢就说按照我们前这个,我们前面这种分。
就是这个faframe的定义来说,实际上我们是说,我们知道了根关节的选朝向之后,我希望能做这个分解,从根关节的朝向里边,算出这个r y等于多少,那这个其实有一个比较简单的方法,就是说比如说根关节。
我们知道它朝向这个矩阵是r2 ,那这个朝向其实对应的这三个局部坐标轴,那他肯定是跟原来世界坐标系的坐标轴,是不重合的,那这里边我只关心这个局部坐标轴的,这个y轴,然后它和世界坐标轴的y轴的关系。
我们知道这是两个向量吗,那这两个向量,我们总是可以去计算一个旋转,用叉乘的方式去计算一个旋转,然后这个旋转能够,把我这个局部坐标系的y轴,把它旋转到全局坐标系的方向上,那我记这个方式,这个旋转为阿撇。
那接下来呢我其实可以把阿撇乘上r,得到了一个旋转,这个旋转其实刚好就是r y,其实可以想一想,因为我加了这个旋转之后呢,这个y轴的方向,其实就跟原来这个坐标系的y轴,是重合的了,那在这种情况下呢。
也就是说在这个rp乘以r,整体的这个旋转之下,我这个坐标系本身的y轴的方向是不变的,那另外一方面它也代表了什么呢,代表我们这个整个这这个组合的这个乘,积的旋转,它的旋转轴应该是y轴。
那其实刚好就是我们的要求,那算出r y之后呢,那其实其实其实我们很多时候不需要算,这个算这个分子的另外一部分的这个xz,我们其实很多时候不需要的,但如果说还是真的需要的话。
那我们其实可以用r y的转置乘以r,因为你我们要求是r y乘以r,x z等于r嘛,那所以rx z就是r y的逆乘以r,那这个其实是得得到得到这样的指,那当然这个通过这样的计算的话。
基本实际上我们是可以证明的,可以证明这个r x z这是一个旋转嘛,它对应的旋转轴是xz平面上的一个向量,那具体怎么正,我就我们就不讲了,因为这个正起来稍微有点有点麻烦,那基本上你是可以想象一下的。
就是说就是思路上了,你可以想象一下,就是因为我们刚才定义这个r p r,我们在定义这个rp的时候,我们是说是要把这个旋转之后的y轴,局部的y轴旋转到世界的y轴,那这个旋转如果用叉乘方式。
算出那个旋转轴的话,你会发现这个这个旋转轴,肯定是在xz平面上的,然后其实你可以进一步的发现,其实这个x轴的这个s平面的那个轴,刚好是说跟我原来那个旋转,它里边分解之后,那x和z轴其实是同一个轴。
那这样的话可以保证我这个分解之后,他其实是就是这个这个这个这个对应的,这个选手肯定还是在x平面上的,但这里证明我们这里就不再不再拓展了,就说这个只有这样一个结论,那当然就在这样的一个定义之下。
不管我们是使用哪一种啊,r的定义,我们总是可以对于这个,比如这个走路的动作,那在动作里的每一帧,我们都其实都可以计算它对应的这个呃,facing frame,就是这个朝向坐标系,那比如说我在这一时刻。
我需要从这个动作,这个动作,这个姿态转换到另外一个跑步的这个姿态,我要做这个转换了,那我们就需要做一个什么事情呢,我们需要去计算,该把整个这个跑步的这样一个轨迹,怎么把它旋转过来。
跟我这个走路的这个轨迹对齐,在这一帧对齐,那对齐之后呢,我们就可以去做这个做这个混合,做这个转移,做这个差值了,那问题就是说这个对齐,我们肯定是要做一个坐标坐标变换的,那个坐标变换应该怎么做呢。
这个就比较简单了,因为我们是知道我们是假设从这个帧开始,后面所有的针都是在这个坐标系下表示的,那我们其实需要去计算,就是说这个坐标系相对于这个坐标系,它都是两个都是两个局部坐标系。
这两个这两个坐标系之间的这个相对关系,那实际上我们可以得到这样一个这样一组,这组关系吧,就是说对于从这一帧开始,跑步的后面的所有针,然后当它我播放了这一帧的时候,我需要把计算这个更关节的朝向。
那这个跟关节的朝向,因为我已经把它对齐到我们原来走路的,这走路的后半段了,那这个跟关节的朝向应该是啊,第一针就是对齐的那一帧的朝向,有这样的一个相对关系,然后类似的根关节的位置。
我们也可以计算出这样一个相对位,因为它本质上就是一个坐标转移,那就有一个坐标转换,所以说实际上刚才我们那个节目,那个那个那段动画,现在其实可以比较顺滑的,给给插值在一起了,给连接在一起了。
那么其实可以看到这个走路走着走着,突然就变成跑步了,因为我们中间在中间这一段中间,这一时刻,他两边附近我们做了这样一个对齐,做了这样一个差值,那就可以实现这样一个完整的,这样一个连接效果。
那当这个连接有些时候,我们也不是非得要把这个不好意思,我们有时候也不是非得要把它对齐的,这个取决于我们想实现什么样的效果,就比如说我们可以实现了呀,我就不要对齐,但我实现一个飞踢,那这也是没有问题的。
就是你这个只要你把这个坐标的呃,混合和坐标的这个方向的计算能够搞对了,那其实这个这个效果其实是,这就取决于我们想要实现什么样的,这个动作,当然还有另外一个就是说,实际上我们很多时候。
我们的动捕或者我们动作数据,运动数据它是没有全局的位置,就比如我们前面这个动作,前面这个动作,实际上我们都是动捕的设备嘛,所以动捕出来的数据,你是可以看到这个它走路的时候,其实住的跟关节的位置。
是在不断往前移动的,但是很多时候有些时候不一定动补,有些时候可能是,比如说动画师做出来的动作,他可能会不是,他可能会不会做这个root根关节的动作,或者有些时候我动不出来动作之后。
我可能会手动的把这个根关节的位置,和关节朝线给去掉,那这时候在这个基础之上呢,我这样的话,我可能我就避免了这个对齐的问题,我可以指我就不用去关心两个关节,两个两两个动物数据之间怎么对齐。
我只要关心当前这个姿态,跟另外一个姿态该怎么对齐就可以了,那在这个是就在游戏里还是很常见的,就是你可以看到很多游戏都是说,这个人他就是在悬空的做一些动作,然后呢,它的位移。
其实是随着我的游戏的这个逻辑啊,或者是游戏的这种啊编程,它去实时的算出来,那当然会带来一些什么问题,就说你是这个人,他其实你会感觉,这个人其实在游戏里会飘啊,他会他会他会他会。
就是看起来不是那么不是那么准确,但对游戏来说,其实这个有些时候也不是最关键的部分,因为我们关心的可能主要还是它可玩性,所以有些时候比如说相信,哪怕这个渲染效果比较差呀,或者说动态效果稍微没有那么真实啊。
但游戏本身也是比较好玩的,那当然这个是我在网上找了一个视频了,因为这个就是说我们该如何在真实世界里,做出一个对啊,像游戏跑步一样的姿势,大家没事时可以试试,看起来挺好玩的,那当然在这样一个这个设定之下。
就是说比如说我们这个动捕数据里边,我们只有就是没有跟关节的这个全局位置,我们只有局部每个关节的这个全局旋转,那实际上这个方案这个这种动作,其实也是相对来说,用起来也不是特别也是表达sorry。
用起来也是比较简单的,就是比如说,我想让这个角色去跟着一个轨迹走,那我们其实其实可以非常简单的对,根据这个轨迹,因为轨迹比如2v一个一个轨迹,一个曲线,我们总是可以算出它每一点的这个方向。
我们算一下导数就可以了,没点的位置,那这个速度和位置其实就定义好了,就对应刚好是sorry,这个朝向和位置,就刚好对应我们前面的这个facing frame,这个朝向坐标系的这个位置。
那么其实可以把这个位置乘上,我这个每个每个点的姿势,那么其实这个就相当于是,把每一个动画放在这条轨迹上播放,它就形成了一个走路或者跑步啊,就是去跟随的这样一个动作,那当然前面我只是讲了。
是说的是两个动作之间的一个如何去连接,我去把这个这个连接做的做得比较,比较平滑,比较自然,那当然实际上我们通常来讲,还是想要做更加复杂的,比如说我有很多很多不同的动作,我们希望这些动作之间能够。
我们希望能够根据这些动作去实时的生成,新的动作,那在这个过程中,我们希望这个生成动作,能够去满足一些用户的交互的指令,不好意思,用户的交互指令,或者说比如说b站,或者比如说这个跟其他的角色有些互动。
那这个时候我们该怎么办呢,那我们其实就不断地提到了,我们这个所谓的这个动作图,陌生graphs,然后其实动作和我们之前提到,它本质上是一个状态机啊,但是嗯这类方法,其实最早还是就是来源于02年。
但是这个术语哈哈哈,这个词是来源于这个02年的这篇paper,那我们上第一节课也提到了,其实那1年有差不多三篇工作,做了差不多相同的一个想法,但是呢这个为什么动作图图这件,这个大家都叫他动作图呢。
因为主要是这个片配这篇论文的这个名字,起的比较简短哈哈,然后这样的话大家也比较容易记啊,所以说这个论文起名字还是非常重要的,当然起的简短有另外一个坏处,就是你在网上。
比如说我想搜motion graphs,我发现很难去搜索这篇论文,论文,就是因为motion graph本身运动图是另外一个,有另外一个概念,它它就是有点混淆,所以说这也是一个也是另外一个问题了。
当然前面提到了,其实动作图本身上来说,它就是一个状态机,就是我们每一个节点记录了,我当年是什么动作,然后我该,然后这个动作的下一个动作,可以连接到另外的哪些动作之上,那接下来我们需要做的事情。
就是说我需要在这些动作之间做一个迁移,做一个选择,他就是使用这个动作图来实现,那当然这里最基本的,就是我们动作图的一个基本单元,那么其实就是动作片段,按动作片段来说。
我们之前也提到了,就是像比如说动捕,比如说专门对游戏,我们就要动补一段设备啊,动不动作,那通常来讲这个动作是比较短的,我就是做一小段动作,然后那这个动作起始和结束的,这个真都是比较对齐的,我比较容易用。
那比如说我们,但是很多时候我们是在动捕的时候,我们会不是做这样的动作,而是说我们是希望更加可能这个动物,这个人他在在在厂子里做了就随便走,大概走了很长一段时间,那这个时候我们该怎么去建立一个动物,动物。
其实motion graph这篇paper,当时它主要解决的是这样一个问题,就是我们给了一段很长的动作之后,我们该我没有说怎么办,怎么去自动的创建一个这个动作图,那这里其实最最最简最最基本的这个操作。
最主要的操作就是说,我们该如何对我们很长的一段运动数据,当我输入来进行输入进来之后,我们该怎么对它分割,然后怎么确定,哪些点和哪些点是可以连接在一起的,那么这里,咱们这就是一个非常简单的一个思路啊。
就是说我们要做分割嘛,那么肯定是最近的那个点,就两个姿态相连,最近的那个点,其实是应该是我比较有可能会做这个迁移,做这个动作连接的这个点,所以说呢我其实可以算一个什么呢,可以算一个距离的一个一个图。
那这个图里边每个像素,比如说我这个动作可能有1000帧,那这个图上就是1000x1000,这么一个一个图,然后每一个像素对应的是说第i帧和dj针,他们俩之间动作的距离啊,姿态的距离。
那当然这个姿态的距离,我们有很多种不同的方式了,比如说我可以去就计算一下每个姿态,它的对应关节的旋转的距离,旋转的差别,那这是担任一种距离方式,那还有其他的方式,比如说可以计算算一下那个前线运动学。
然后实际算的是每一个每一帧,它的每个关节的位置的区别,那这也是一种距离方式,当然还有一些其他的距离方式,这个区域我怎么定义呢,比如说我也可以定义一下,比如说关节的速度,或者整体整体这个人的速度。
那可能这个速度本身,我也认为它也是距离的一部分,还有一些其他的,比如说可能关注一下手的位置更重要一些,我身体的其他的位置可能没那么重要,那我可以把根据手的位置来定义这个距离。
那这其实也是另外一种具体方式,但总体来说我们定义了某种距离,定距离的计算方法之后,那我们总是可以做出这样一个距离图来,那这个在这个距离图里边,它的局部极小值,就说这个在这个点上,在这个点上。
他不管往哪个方向移动,那个对应像素的那个那个距离值都比较大,那这个局部最小值,那这些局部最小值,其实比如这个点是局部最小值,那它对应的只是第横的d多少d x像素,纵向的第y个像素。
那就是它是对应的是第一个动作的,x轴和这个division,那这两针有可能是连在一起的,但实际上它会有很多误报了,就说你肯定还是需要很多时候,我们还是需要一些手工的去去处理。
才能把这个做得更加更加好一点,那当然有这个,就实际上这个一方面,我给出了这个对这个动作的分割,比如这一段是一个动作,另外两个任意两个点点之间,是可以确定一个动作,另外以后我也确定了说。
我从这个点应该会联系到哪一个点,就是比如说从这个动作,我可能会连接到哪几个动作,比如说在这这条这条直线上,我们看到的是对应横轴的这一帧,比如x d i j它可以传转移到,比如这个从这个姿态。
它有可能会转移到这个姿态,到这个姿态,到这个姿态,因为他们的距离都是比较小的嘛,所以说这个这这姿态范围内都是可以对应,这几个片段都是可以有可能转移的,那他确定了这些之后呢,其实我们就可以把这个动作图。
给构造出来了,因为它最关键两个部分我们已经有了,第一个部分是说,每一个节点代表了一个动作,然后另外一个就是说两个节点之间,我们有边来有边来连接那个边,其实从我们比如我们会从前面,比如像刚才提到的。
他们只要距离可能小于某个范围,那么其实就是认为这两个边是有呃,这两个点之间有边连接的,当然实际上我们有些时候,可能一个节点不一定代表一个动作,可能代表若干个动作,那这些动作之间可以在可以做一些差值。
怎么差值呢,我们其实就提取出对应的帧,然后这两针之间做一个线性差值,其实就可以做到差值,或者说比如说若干针,那我可能会做一个双线性差值或者类似的,所以每一个节点代表一个动作片段,然后节点之间有。
如果说这个动作和另外一个动作,能够做几个迁移,那我们其实可以可以连一条边,那我们实际上迁移是什么,我们什么时候做迁移呢,一种一种可能是说比如说我们用户控制,比如说这人他正常往前走的时候,我需要去转向。
那我可能走的走完了,这个走完这漫完这半步之后呢,那我马上就开始选,我下一个动作应该转向了,我去看一下,我就是当前走路能够连接那些动作里边,哪一个动作对应的是转向,那这个转向怎么判断呢。
因为我们知道每一个动,每一个节点它的动作是什么,那这个节点它对应的这个动作,从第一帧到最后一帧,我的这个全局位置的偏移,我是可以,我是可以很容易计算的,那这个拼音其实就告诉我。
我接下来这个动作它是做了转向,还是在5g网直着走,就就会这样判断,那根据这些信息呢,那我们就可以去选择一个合适的动作,来去完成我用过的指令,那当然有些时候我们可能会做一些,更深的这样搜索的搜索了。
我们有时可能会做一个,比如说稍微做一些深度优先搜索,然后比如说我知道我未来若干针,我需要完成一个什么动作啊,未来比如说若干秒,比如前面有个台阶,那我需要提前很长一段时间去规划一下,我的。
比如说这个走路的这个落点,来保证我在台阶前面合适的位置,能够启动上台阶的这样一个动作,所以有些时候你可能会需要,稍微做的复杂一点,但总体来说呢,就是我们在动作图的这个基础之上呢。
我们想要实现一个可交互的角色呃,的这样一个动画,那么其实最关键的就是说,我们其实最近可交互嘛,我们需要去检查一下,有实时每隔一帧,比如说每每一帧,我们可能需要去检查一下,用户的输入是什么。
然后检查一下当前环境的一些信息,检查一下,比如场景的其他的角色,我们是不是要跟他互动,然后根据所有这些信息去决定,我们接下来是否需要做这个迁移,是否需要迁移到一个新的动作上。
我们就要去得到下一帧的这个姿态,然后得到下一站姿态之后呢,我们其实可能会做一些后处理,我们上一次的那个实验,lab一其实有一个选做的一个课题,就说这个人在走路的时候,比如说我这个人怎么就他端了一杯水。
那这端着一杯水的这个动作,在我们的动捕数据里是没有的,那我们其实有些时候是需要一些,比如i k来去把握一个动物的,得到一个姿态,来把它变成,比如说端着一杯水的这样一个姿态,然后算完之后呢。
那我们就可以更新角色姿态,然后更新环境的信息,然后更新各种各样的其他的相关信息。
所以整体来说是这么一个这么一个,不断循环的一个过程。
那当然这里其实是一些例子啊,就是一个非常简单的例子,其实还是我们前面的那个这个,跟跟踪这个动作的这样一个例子,就说我们在跟踪这个这个曲线的曲线,跟踪的这样一个例子,就是我们之前那个简其实比较简单。
就是我直接把这个角色的位置,放在这个曲线合适的位置上,但那个缺点就是说,你可以明显的看到脚会打滑,因为本质上来说,我我动作本身是不会严格,按照这个曲线走的,而这个局比如说魔神graph的话。
我们其实可以判断一下,比如说这个这一步走完了之后,那我们就可以判断一下,一个下一步就是这里,其实这个图里面,这个视频里面,每一个这个点,其实就是我们切换的那个位置,那我们其实可以根据我接下来曲线的朝向。
然后在我接下来所有的可连接的,这些动作里面去找哪一个,它的动作的结束的位置。
跟跟我的曲线更加符合,那得到之后呢,我们就可以去做。
去让这个这个角色去跟着这个轨迹做的,比较比较呃,比较完好的词,比较准确的跟踪这个轨迹,那另外呢比如说我们动作有很多,也可能不一定是走路,我们肯定跑步啊,或者说其他那些动作,那其实也可以加一些其他的命令。
去让这个角色去完成,这也是比较,就是在动这个基于动作图的基础之上,我们可以比较容易实现一些效果,那当动图有一些基本的一些问题啊,就是说我们之前也提到了,首先如果说这个动作很多的话。
那么这个动作图图会变得非常非常的复杂,另外一方面的话,其实动作图本身,它的响应的速度是怎么说呢,因为我们每次都是在一个动作,一个片段播放完之后,我们才会去切换到另外一个片段。
不然的话你会看到明显的差值的误差,所以说它的这个响应速度,有些时候是不够快的,就特别是如果说我的这个呃,动作之间的这个这个这个这个不好意思,有些时候它响应速度可能是不够快的。
就如果说我这动作有一个动作比较长,那可能它需要播放,整个播放完我才能去去做响应,所以这个是比较比较比较比较比,较难做的一个问题,另外就是说为了能够创建这个动作图,我们前面也说到了。
虽然数像motion graph这种,我们可以基于一个距离函数来去判断,哪一个地方是可见一点,但这种判断通常来讲是不是那么准确的,就实际上为了实现很好的效果的话,我们通常还是需要在动捕的阶段。
就把这东西做好,所以这个其实对我们动捕的成本,也是就是也是增加了一些,我们这个动捕方面的成本,所以说总体来说,这个动作图虽然用的很多吧,但是实际上为了把它做好的话,还是有些各种各样的这种这种方法啊。
还有各学校各种各样的这种这种技巧,和各种各样的工程上的实现,才能把它完成完成的比较好。
那所以说实际上现在有新的一些方法,比如说motion match,当然motion making,其实就是说它主要实现的是一个,非常重要的,跟motion graph一个最大的不同点。
就是说它的切换是更加细腻的,就有点相当于什么呢,相当于我每一帧都会去判断,我接下来应该切换到哪一帧,但这个不可能不一定是每一帧了,这个游戏是为了实现效果来说,你可能会隔若干针,比如0。1秒或者零点几秒。
我就再去判断下一帧,但总体来说它的这个切换的频率,切换的力度就不再是一个片段了,而是一个姿态,那这样的话,其实会给我们带来非常好的效果,就是说它对用户控制的响应,会飞更加快速一点。
那另外就是说嗯就它本身从这个算法来说,我们知道这个动motion matin,它基本思路就是说,我每次每一帧结束的时候,我去在我的动态库里边去找找一针,找到哪一针的那一帧跟我当前帧离得最近。
同时又最能满足我,比如说用户控制这个运动运动的方向,我其实还是定义了一个距离函数,这个定义这个距离函数,我用这个距离函数,去在我的整个数据库里边去找一个最近邻,那这个最近邻,就是我下一个需要播放的姿势。
然后这两个姿势之间,因为这个姿势肯定是跟我当天姿势,不完全一致嘛,那么其实这两个之间还是去做一个差值,做一个这个这个平滑混合了,然后来保证我这个姿势连这个比较平滑,比较连续。
那这样的话其实就是刚才我说的,它就是它因为每帧都会做切换的话,其实可以保证我跟你一个更细粒度的,这样的一个这样一个呃生成这样一个切换,另外一方面的话,它其实也是支持说我这个数据本身。
我可以是就是去洞补一下,比如这个人在动捕这个工作室里面,我就做了15分钟左右的动作,他可能做了各种各样的跑步,各种各样的转啊转圈,然后急转弯等等动作,然后我其实可以都不需要,像我们前面那样的。
需要手工去做这样的切分去做对齐,他其实就直接用这样的数据,因为本质上他就在数据库里去找最近零了,那其实对我们这个运动本身的这个呃,这个这个这个成本,或者这个难度也其实也建立了很多。
所以总体来说motion main是一个新的方法,当然缺点就是说嗯它不像motion,motion graph这种动作,有些时候你可能想要的动作,他可能不一定会找到,不一定是最近的。
就是你就是把一个把一个,就是就是为了能够得到这个动作,你可能需要去更加好一点的设计,你的这个距离函数,所以这其实是另外一个方面,就是说一方面他有灵活的一方面,另一方面就可控性上也会有一点。
相对来说有一点欠缺,ok那我们其实再回顾一下,我们今天主要还是讲了一下关于角色,基于数据的角色动画的两个重要的部分,一个是如何获取的数据,我们主要讲运动捕捉的一些基本概念,另外是如何使用数据。
主要是说动作之间如何做连接,如何做对齐,如何做这个转移,然后再继续在这个基础之上呢,我们其实可以实现一个动作图,也就是这个呃状态机,来实现我们可交互的角色动画。
那当然这个还有一些motion matching,这个部分,我们就只是简单的介绍了一下,这个基本概念。
那就是这个就是,以上就是我们今天的主要内容啊,这个很抱歉,我们可能前面这个有点卡呀,因为我们这个同步也有,也有录录录的这个视频啊,所以说大家可能如果实在是卡的不行的话,我觉得也可以回头再去看一下回放。
希望还是能够啊,应该回放,希望是没有问题的,我等会儿等一下我检查一下,ok好,那我觉得可能也没有什么更多的问题了,就是说这个因为今天的课程的内容,相对来说比较容易啊,我相信大家应该听下来。
应该是可以理解的比较好的,然后那个我们下节课会讲一讲,关于这个learning based,然后嗯来总结一下,我们这个就是前面这一半的,关于运动学的这个动作,生成的这些计算机动画的这些技术,好的。
那我们今天的课程就到这里啊,然后那个大家有什么问题的话,我们可以继续在我们的微信群里边,做进一步的交流,我们其实也看到了很多很多同学在里边问,关于上面这个作业的问题啊,这个我们的助教还是很很很很很很。
很认真的,还是会尽量给大家回答好的,那我们今天的课就到这里啊。
GAMES105-计算机角色动画基础 - P6:Lecture06 Learning-based Character Animation - GAMES-Webinar - BV1GG4y1p7fF
好的那我们就开始上今天的课啊,稍微缓一下,ok ok好的,那这个是我们今天是games 4105,也就是计算机角色动画基础的第六节课,因为本来应该是上周是第六节课,但是不好意思啊,因为有些事情。
所以说耽搁了一下,我们这个啊,我们后面还是会有多出一周来的,应该还是有问了一下那个games的啊,这个负责人他其实说后面还是有充足的时间,在过年之前我们可以慢慢学哈哈,所以说总体来说我们差一周。
我们后面会补起来的,这个大家不用担心,然后我们的那个作业啊,应该是今天截止啊,其实也是给我们这个助教一个一个一个一个时间吧,就是说我们截止之后呢,那主教可能会他会给会会的,然后呢也会给大家一些反馈。
嗯当然记者之后呢,我们的那个作业本身还是一直开放的啊,就是说那个在github上我们还是有作业的这个本体啊,当然如果有兴趣继续做呢,还是可以继续做的,只不过可能今年的这个评分。
这个数学可能就不会再去评分了,因为这个也是也是也是考虑到一些工作量的这个这个这个问题,然后我们的第二次的啊作业我们这周应该能放出来,这个逐渐还是稍微花了点时间去去实现那个那个框架啊。
所以说我们还是稍微缓一缓,但我们这周会放出来,然后跟上一次上上一个这个love一样,我们这个时间大概是三周啊,然后我们形式也是差不多的,那我们今天的主要内容呢,其实按照我们的这个大纲啊。
我们其实主要是讲一讲learning based condemation,这是动画,我们前面上节课讲的是什么,我们上节课讲的是基于数据的角色画啊,数据基于数据的话。
我们主要是说我们怎么去把现有的数据进行连接,进行重组,然后怎么去响应用户的这个交互,在于它并不是直接去啊连接这些数据,而是对这个数据进行一些处理,把它放在一个模型里边。
然后使用这个模型来帮助我们去生成动作,那当然今天我们这个内容来说,实际上还是会稍微回顾一下,当然一方面是因为我们上节课有些东西没有完全讲完,另一方面也是中间隔了一周之后啊,我们也是稍微再回顾一下。
因为这个跟作业,我们那个实验啊,实验二是相关的啊,这个当然也确实是隔了两周,我觉得时间过了也是非常久了,感觉也是希望能够再稍微回顾一下,然后呢第二部分我们主要是讲一讲比较传统的,教传统应该是什么呢。
就是说实际上我们在但角色动画这个领域吧,就是特别是基于运动型的角色动画,在这个领域实际上在怎么说呢,在2016年之前有很长一段时间,其实大家都是在追求一种,就是就是当然是因为这个问题。
本质上来说是我们怎么去对角色这个动作呃,人物的动作进行建模,我们用什么样的模型能够表达他的一个统计上的一个这么一个规律,还是用一些飘,现在看来相对传统一的方法主要是基于这个高斯模型的各种方法。
那当然我们今天呢也只是稍微讲两句,就是提两个跟这个高斯模型相关的两个我觉得比较有意思的工作,但是这个具体的细节上我们可能是没法讲的,因为作为一个比较初级的这个入门课。
我觉得讲的那个地方可能会太过于深入了,而且可能光就是比如说我们讲一讲高斯模型,高斯混合模型或者高斯过程,这个可能就得要一节课了,所以说我觉得这个这可能是我们不是我们最关心的问题,那我们其实稍微提一下。
那最后还有一点learning based model,一个能够生成角色动画的这么一个模型,那这部分内容呢我们今天暂时还讲不到,我们下节课还有时间关系啊,我们下节课会把这个这部分再把它讲完。
首先learning based的模型这边其实呃内容也不是很多的,我们当时也是只是给大家简单回顾一下这2年,因为主要是近5年吧,我这个方面有很大的很大的发展。
那主要是这脸这最近的一些就是里程碑的一些工作,那主要是c graf的一些工作,ok那我们今天主要的内容啊,我其实刚才也说了,就是三部分,第一部分我们还是回顾一下,或者说回顾之外,我们再去额外的再去啊。
讲一讲这个更仔细的讲一讲这个有关这个可交互动画的,可交互角色动画的这样的一个一个我们怎么该实现这样一个问题,时间上节课我们其实已经提到了,就说我们如果说想要去实现一个可交互的角色。
比如说我们加上这个角色,它能够根据用户的一些控制,比如说用户有遥感手柄啊,去控制这个角色的这个动作方向和动作类型,那我们是希望我们的这个角色能够根据这些控制信号,它自动的生成合理的动作。
那这个过流程实际上在大部分物理引擎里其实都是可以,那我们可以抽象化的把它写成一,第一步检查一下,用户的输入,第二步检查一下啊,在我的数据集里边找到一个符合用户输入的这样的一个动呃动作片段。
然后接下来就播放这个动作片段,那这就是一个基本的一个这样一个流程,当然为了支持这样的一个流程,我们实际上也有一种一些非常常用的一些数据结构,那其实这个motion graphs或者动作图或者叫状态机。
其实就是一个非常基本的实现这样的一个功能的这样的一个数据结构,当然这个实现方式上可能有很多种类型的,但大概都是说我们这个节点,还有或者ue哈,它有些插件可以支持类似的功能。
那基本来说你我们都知道就是一个节点或者一个函数,它代表了一个动作,那我们其实就是说在不同的动作之间,我们有若干个可以连接的地方,那我们其实上节课也稍微回顾了一下。
最早在motion graph这个论文当时出来的时候,其实也是说我们是否可以找到一些自动的方法,从比如说给一段数据,这个数据可能是没有做任何预处理的,我们希望能够从里边自动的把这些啊。
就这些连接的位置能够自动的把它提取出来,然后能够实现这样连接,这其实当时我们他们想就是这个工作,其实他本来是想做的事情,那当然实际上我们我我我感觉,其实其实从这个长期以来的这个这个工程实践上来看啊。
其实自动的方法还是相对来说少用一些,就是大部分来说就是还是需要动画师或者技术美工,他们去手工去挑这样的动作,然后其实在动作捕捉的时候也需要专门的去处理,这样才能保证质量,就是靠东靠东那个自动的方法了哈。
其实在很长一段时间的这个这个在质量上还有很大的问题,当然motion graph我们前面也提到了,其实它每一个节点它其实是一个动作片段,所以实际上motion graph相对来说是一个呃。
就是在它其实就相当于在这个动作片段这么一个时间的颗粒度下去,进行这个可交互的动画生成的这样的一个过程,或者再具体来讲,就是说我们每当一个动作,它就它是一个动作片段呢,它其实是一小段这个移动轨迹。
或者是一小段这个呃因姿态的集合,那我们继续在播放这一系列的姿态,当我播放完之后,那这个这个当前这个动作片段结束之后,我再去检查一下我当前是不是有用户的输入,然后接下来根据用户输入。
就选择下一个可以转换过去的这样的一个motion clip,然后接下来就去播放它,那当然这里有些有些有些这个就是说有些问题,就是说我们是不是其实我们是不是可以在这个动作的中间去做切换呢。
因为就是一个基于运动学的控制,比如说基运动鞋的控制,意味着你可以随时改变角色的姿态,而且这个这个这个改变是可以任意的,那这种情况下,实际上就是说你是敢敢是没有问题的,当然带来了一些缺点。
就是说如果说我在这个播放的中途去迁到另外一个动作的话,是有可能带来一个比如说图翻这个动作突变,那可能会带来我这个动作这个不是很流畅,或者是这个这个卡顿啊,或者说突然有些这个奇怪的这个姿势就冒出来了。
这其实是会有这样的问题,所以总的来说为了一个其实也是一个平衡,一个这个啊动作质量和响应的速度啊,就是很多时候我们的这个动作图,它里边的每个动作片段其实相对来说比较短的,比如说走路可能他只走一步。
然后可能比如走路到跑步中间的过渡,可能会有专门的一段动作,可能连接到不同的跑步之上,这样的话其实也是能够上我的这个响应能够更加的更加的及时一些,但是一个具体的例子啊。
就是说比如说我们这里是一个非常简单动作图,那动作是什么,我们就不管了,咱就是走路吧,然后其实有一个直向前走直线的一个一个走路,然后他在这个节时间结束了之后呢,或者说向直走。
那其实可以有很多种不同的这个动作图啊,动作片段,那比如说在当前时刻,我这个角色啊,其实我们上节课其实也回顾了,就是我们其实在对于角色来说,我们通常来讲会有一个参考啊,局部的参考坐标系。
那这个参考坐标系可以有很多种不同的定义方式啊,一般来说他会说他给出了我的当前的角色的一个朝向的方向,那当然这个术语可能在不同的软件里啊,可能不同的场景有不同的叫法。
比如说有些地方好像是叫叫叫root root transtranslation啊,或者root transformation,但其实大概说的是同样一个东西,但总体来说就这个角色他现在正在向前走。
所以他其实这个root或者说这个重心的轨迹大概是这个样子,那么接下来呢比如说我现在想用户是用遥感,我输入了一个转弯的信号,那其实我期望的这个角色,他后面的运动轨迹大概是沿着这样一个转弯的这个方向去移动。
那当然因为用这个用这个角色,现在当前正在走向前走了,所以他其实还是要走完这一个啊,走完这一步之后才去真的去响应这个用户的交互,那在这个过程中,实际上因为每一个转移过去的这样的一个动作。
我们其实都是预先知道的,所以说我们可以把每一个转移过去的这样的动作,接在我们当前这个运动轨迹之后,那其实这里其实需要回想一下,我们上节课提到的,我们该怎么去把一段动作去跟当前的角色的位置。
或者当前角色这个姿态的这个这个轨迹的墨迹,这个末端能够把它连接起来,但其实你需要做一个hiding frame,就是这个朝向坐标系的对齐,然后去做相应的这个坐标转换啊,其实做完之后做完这样坐标转换之后。
我们其实是可以直接得到,就是非常容易计算啊,你其实可以直接计算出来,在每一个呃可能的这样的下一个动作片段结束的时候,这个角色应该在什么位置,那其实从这个角度,从这个因为我们现在只有三个非常简单的动作。
那其实你可以知道,那其实是这样一个,就是这个动作它所对应的或者是这个动作它所对应的这个结束的时候,这个角色的位置跟我的控制器的输入是比较近的,那其实就可以直接选择这样一个动作作为下一个动作进行进行连接。
嗯,但实际上这个东西可以实现就是很自动的,就是我们可以叠加上一些其他的一些这种优化算法,或者说这个啊规矩个叫什么来着,动作估啊,这个叫planning,那叫什么规划算法,对对对。
一些这些规划算法可以实现一些看起来很智能的一些角色,就是说它实际上是说在呃motion graph,就是在动作图这样的一个状态机的基础之上,我们可以叠加上一些这个动态算法,比如说s2 。
它可以实现在场景里边自动寻路或者是自动避障,就等等,类似于这样的这样的一些一些技巧,那当然实际上这个在真正实践上,其实你可以跟其他很多的算这个技术呃相结合的,比如说像一些这种这种叫什么。
那那math这应该叫叫叫导航图还是叫什么,反正就是可以跟这些东西结合起来,可以共同完成一个这样的功能,所以不管怎么样,就说魔神gram它是一个底层的一个一个数据结构。
那我们上面其实可以用各种不同的这种规划算法,来完成更加智能的角色,那当然motion graph刚我们不断地提到了,它其实有一个非常大的问题,就是说我们其实为了实现比较好的效果。
我们一般来说是会在每一个动作的这个动作片段结束的时候,我们才会去做下一帧,下一次的这样一个搜索呀,然后去判断啊,然后去连接啊这样的这样的一个操作,当然这个优点是说我们其实这样的话。
我们可以保证我们动作总体总体来说是这样比较自然的,因为基本来说我们都只要动捕,或者说我们动画师做出来的动画是很很好的,那我们其实这样播放出来的动画应该也是也不会很差的。
但是缺点就是说相对来说它的响应速度会稍微慢一点,就是说我可能用户推了一个摇杆,推了一下摇杆,那可能这个角色其实还是会做完,当前比如他正在向前走,那还是需要走完当前这一步,他才能去找转身。
那这个时候其实是有一点有点响应不及的,那为了能够让用他这个角色能够很快的响应,那也许说我其实还是需要专门去采一个数据,那这个数据可以在中间进行切换来实现一个比较快的响应。
但实际上这个这种方法还是比较麻烦的,主要是因为我如果说随着我这个期待的用户的这个输入很多的情况下,那我们其实需要准备的这样的这个动作片段其实也会非常的多,那动作片段多了之后呢,那如果大家还有印象的话。
我们第一节课,第二节课讲的那个非常大的这个动动作图的这个状态机,它会变得非常非常复杂,也会带来非常非常复,这个容易出的出了bug的这种各种情况,所以实际上我们为了能够想要实现一个更快的这种响应啊。
就是说我们其实还是希望能够把动作图的这个在每一个动作片段结束,就是这样一个级别的这样一个规划,把它进一步的细化到,我们是不是可以在每一帧的结束,你每一帧结束其实就是就是当前帧了。
我们是不是可以把它改成在每一帧的结束里边去,你就直接检查一下用户的输入,然后去选择去寻找下一个这个合适的一个姿态,然后根据这些姿态去更新我的角色,那我其实可能因为整个队真的是我们的。
我们的响应从原来的一个动作,可能是零点几秒,现在变成了每一帧,那就是可能是每秒钟30啊,或者每周60p s,这样其实你的想速更快一点,那当然这个过程其实主要需要解决的问题。
就是我们该如何能够保证我们生成的动作是一个比较好的动作,因为如果说我随便切换的话,那可能这个动作是完全就可以有可能是跑飞的一个状态,当然这种其实就是有一些方法其实就非常有名的。
也是这2年就是大家都是认为可能是一个新的革新式的方法,就是motion matching,应该说是可能没有受到那么多人关注的一个方法,叫motion fails。
这个motion fields是2010年的工作,然后motion martin大概是16年左右的工作,那我当时也是跟这个啊motion matching,他们这个发明人啊,就是其实a b的两个人。
其实当时我们也跟他们这个有过交流啊,基本上是他们其实也是说,其实他们这个方法也是从这个motion files这样一个工作启发得来的,然后加了一些很多很好的工程实现,然后产生很好的效果。
那当然我们今天稍微先讲一讲这个motion fields,因为争这个是一个算是一个比较比较早一点的idea,我们其实前面提到了,我们没有不好意思,我们先其实前面提到了,我们其实每一个动作。
比如说我采了一段动的数据之后,我们其实每一帧我们可以把它看成是一个动作,它本身是一个序列,这个序列里面每一个帧都是一个姿态,运动的速度,然后每个关节的这个旋转等等等等,所以说比如说这是一段向前走的动作。
一段向前走路的动作,那其实我们可以把它拆成,比如说这样有四针,然后每帧有但有这个一个朝向,一个速度这样的一个关系,那如果说我们还有另外一段动作,那我们其实可以同样把它拆成若干针。
那然然后这些针其实也会带有跟之前不太一样的,就是每帧的前进的方向啊,速度啊这些信息,那如果说我们有很多很多动作,这些动作可能比如说我让一个人在一个这个场地里边。
我去走一个大概啊啊比如说20分钟你就随便走,那最终你会得到一大堆的这样一个这样一个针,这样一个不姿态,就是其实整体来说这些姿态每一个姿态都是处于不同的状态,不同的位置,不同的速度不同朝向。
那整体来说它们构成一个非常大的一个field field,意思就是什么呢,是说我对于我这个角色的某一个状态,就是在在这个范围内的这样一个状态,我总是可以在他的这个附近找到一些最近林。
然后这些最近连有跟他相似的这样一个姿态,当然但是呢因为这些最近人来自于不同的动作,来自不同作者,意味着它可能会有不同的前进方向,不同的速度不同的其他的一些,比如说手呀,手要做什么事情。
只要做什么事情这些这些各种信息,那在这种情况下,比如说我需要用户给了一个输入,比如我需要你向左向右走,哎呀不好意思,像你向右走,那这个时候呢我其实需要做的事情呢,就是说因为我最近林里边也有很多动作。
已经有很多这个相当姿态了,那这些相关姿态里边可能有一些,它是对应向右走的这样一个这样一个这样一个动作的,那我们其实我需要做的事情就是把这些向右走,对应向右走的这些姿态给挑出来,然后把它们做一下混合。
做一下bend,那得到的这个姿态,那应该就是对应于我这个向右走这样一个姿态,那我其实就把接下来就是把我的角色更新了,当时那个姿态就可以,那这其实工作这篇工作就是说这个motion field。
它基本来说就是就是这样一个思想,它是那个2010年的sg h的啊,论文啊,motion feels for interactive,carina of emotion,当然这个是比较老的了。
但是这个效果其实还是蛮好的,那基本思想就是说我们不再是根据一个模式clip,就是不再是一个呃一个片段,一个app用的片段去进行处理,而是我们其实而且我们去做这个每一帧的这个这个这个速度啊。
和这个朝向等等这样的这样的参数的这样的一个计算,当然就像我们刚才所说的,实际上对于任何一个点,让我们构造好这样一个motion fields这样一个运动,这个长这样,不好意思,这样运动场之后呢。
实际上对于当前这个状态,比如中间那个圈代表当前这个状态,它在某一个他在一个近邻的范围内,我们是可以找到若干个,那么其实或者说我们就是我们就只要a那个k精灵,我们这里只要前面最离他最近的cake呃。
这个状态叫姿态,那么其实每一个姿态我们刚才也提到了,它是来自于不同的motion clip,来自不同的动作,那其实会朝向不同的这个有不同的速度方向,会有不同的运动这样的一个目标。
那接下来呢motion field这个工作他就收,ok我其实也不用特别关心到底哪一个是往哪个翻走的,我可以根据某种方法去给每一个这样的一个呃,它周围的这个近邻的姿态附上一个权重。
比如说这个有一部分是向上走的,一部分是向右走的,那我给它分别附上一个权重之后,那可能最终它的平均的这个运动方向就是向右上走的这样一个状态,那类似的其实如果说我们给它附上一个不同的权重。
比如说这里边我可以向右走的这些这个姿态,权重大一点,然后向上走的这个姿态,这个权重小一点,那整体的一个平均姿态就平均的速度其实就会变成向右走,ok所以实际上对于这个motion fields来说。
它其实的它整个这个这个这个这个idea,就是说还是我们前面那个框架,我在关键帧,我们在这个动作里面的每一帧,我们在生成动画的每一帧的时候,我们首先检查一下用户的输入。
然后根据当前角色的这个状态去找n个最近的这样的一个这个姿态,但这个姿态是我们的数据集里提供的,然后接下来呢我们根据用户的输入去计算合适的这样的一个混合权重。
然后再用这个混合权重去混合我们这些最近连姿态里边的各种参数,然后接下来呢我们用混合之后的这样一个速度,去更新我角色的这样一个状态,那这就完成了一个这样的一个计算。
听起来还是比较简单的,但实际效果上也是比较比较不错的,这个其实你可以看到,其实motion graph嘛,就是sorry,motion fields,其实大概也是类似的一个效果。
就是我们其实可以让这个角色非常自由的是,就是跟跟随着用户的控制进行进行这个操作,那当然实现你的控制方式可以有不同的类型,比如你控制可以有定点移动或者是一个定向移动等等等。
那另外其实motion field其实也支持一些外力,就是因为本质上来说,他总是根据当前角色的姿态去寻找一个离他最近的这个姿态,就比如说如果说我需要对推一下这个角色,那我其实可以稍微加一点点物理仿真。
让这个角色的这个被推出了一点点失去平衡的这个状态,那这个状态呢我们其实希望他还是在我们这个motion fields,就是说在我们这个动作的这个范围之内的,那这个时候我还是可以找他跟他最近的一个最近的。
然后从这个最近连来帮助我继续往前走,那其实就是实现了一个怎么说呢。
就是有一点点这种根据物理的这个反馈的同时,还能够继续沿着我们原来的目标移动的这样的一个,这样一个这样一个效果,那当然他们之前也做了一些对比啊,就比如说其实确实有些模式graph就是最基本的陌生国up。
因为他总是来说我们总是在一个动作结束的时候,才会去进行下一次动作的这个判断,所以说当我比如说用户不断的去改我们的输入方向的时候。
那实际上我们的人motion graph相对来说它的这个想要速度会稍微慢一点,但这个其实本质上来说也是因为魔神graph它里边的这个transition,就是他这个动作之间的迁移是定死的。
就是我们在设计的时候就已经定好了,而这个motion field这一类的方法,实际上它是一个实时在计算的这样一个过程,也就是相对来说它会它提供更大的自由度。
那同时其实motion field还会些其他好处,就是说在motion graph我其实是要求我的这个起始状态和more结束了,它就每一个动作片段的时候,起始和结束应该是相同的,类似的。
反正是有一个预预定好的一个姿态,这样才能保证我的迁移是平滑的,但是motion fields来说,我其实不需要做这样的预处理,我可以直接拿一段很长的动作进来,它就可以实现这样的控制。
那当然魔术shield看起来这个效果很好,但为什么这么多年没人用呢。
我觉得其实我觉得在这个特别是在角色动画里面的,很多做这个研究的领这个结果都有这样的问题,就是你发现其实效果很好,看起来,但是实际上很难有人用,我其实个人觉得啊,这个只是我自己拍的拍出来的。
但我不知道这个实际上工业界对这些事情是怎么看法,有可能主要是还是什么呢,就是说因为motion fields它里边有一个像就是说我们找到最近林之后呢,我们需要去根据当前的用户输入去计算。
对这个n个近邻的这个呃差值的这样的一个参数啊,这样一个权重,那这里其实很大的一个问题,就这个差值权重该怎么算呢,其实想想这个主要是说。
其实很多时候也许你需要自己design去设计一些规则来计算这个插值函数,但是可能你直接设计它效果可能不一定很好,那这个控制策略可以根据我当前输入的这个姿态啊,用户的输入去去去输出一个对这个n个这个角色。
n个姿态进行混合的这个权重,就是你这东西你能让他弄work,它就work,你能work,那就是很很难work,另外一个问题就是说这个强化信息这个训练很多时候是一个比较啊,跟tx相关的。
就跟我们的这个控制目标相关的,也就是说很难去我换一个目标的话,所以相对来说总体这个方法用起来的话是有一点点难度的,但是motion matching呢,这个前面也提到了。
它其实是从motion fails出发,他就做了一些简化,那这些简化其实还是比较重要的,但第一个简化就是说我们在motion field里边,我们其实是需要去找n格最近点的。
然后motion motion时间是发现我其实不用找了,我找一个就够了,所以它实际上我就只需要找一个最近的,找到一个最近的之后,那我其实就用一个科技零的情况下,我就压根就没有这个不烂的问题了。
就没有这个混合的问题了,我可以直接把这个最近你的这个姿态作为我角色的下一个姿态,那当然这个过程中我直接作为一个角色下一字塔也会带来问题,主要是说我找到这个最近林,他大概率跟我就是很大的概率。
但不是非常大的一个,这个这个是跟根据实践相关吧,就是他有很大的概率是说他的姿态跟我当前的姿态不是同一个动作,上的相邻姿态,这意味着什么呢,意味着如果说我直接把当前的角色换成下一个这个最美的姿态的话。
那我这个角色他的动作会出现一个跳变,那这个其实上面的这个效果其实我们是非常不想要了,所以时间为了为了减少这个问题的话,实际上我们还是在实现上,通常来讲是需要把当前角色的这个姿态这个比较平滑的。
把它给混合到我的目标的姿态里面去,那边平滑怎么混怎么混合,我们其实可以用差值,就比如说用那种线性插值,或者说用一个一个一个移动平均数,没用average这样类似的方法来实现这样的一个差值策略。
当然还有其他的方式,就是总体来说,我们的目标是说能够平滑地从当前这个姿态变到下一个姿态,当然在这里边有两个问题是需要解决的,第一个问题就是说我们因为我们需要去找最近零。
那我们需要有一个距离函数来告诉我们什么是近,就什么什么样的这个哪两个姿态是近的,那我们需要一个函数来衡量任任何两个姿态之间的距离。
那这个距离函数实际上是我们motion match里边非常重要的一个部分,就是这个距离函数如果没有设计好的话,那个效果其实也是比较差的,当然魔术发展其实它最终就是但是这个形式上来说。
这个距离函数还是比较简单的,因为本质上呃,比如最简单的距离函数,我们就直接比较两个姿态的每个关节角的距离,那这其实给我们定义了一个一个距离函数,那当然实际上这个效果其实不是很好,大家可以自己试一下。
这就是我们比如lab那个第二个实验,那个lab two的时候,哪有兴趣可以自己自己实现一下看看,但是呢但是在这个基础上呢,我们其实还可以稍微提取一些特征,就是我们不是直接比较角色关节的旋转。
而是一些在这个角色的关节旋转之上,我们额外记录计算的一些这种特征向量,那算出这个字特征向量之后呢,那我们其实在特征向量之间可以直接做一个,比如说做算一下欧式距离。
就算一下两个特征向量的差的二分数或者一范数,或者什么样的方式都写作,而且这是某一个范数,你总是可以比较这样一个距离,那当然对于这个特征向量啊,其实也许也有很多种不同的定义方式,比如说一种可能的组合。
就比如说我们可以在这个特征线里边,相当于跟关节的位置,就是非常重要的一点,一些比如说这个我未来的的用户的阿sir,未来的角色的位置和常用的信息,这是什么意思呢,就是说比如说我前面有我踩了一段动作。
这个人在这个场地里面绕了可能十分钟,那它可能前后左右移动,然后向向左下转,那么其实我可以大概知道,因为从动作数据里面,我们是可以知道,如果他继续沿着当前这个动作去去去走,从当前某一个位置出发。
然后继续演去研究当前的这个角色,动啊,动捕出去移动,那么它在0。5秒啊,一秒啊,或者1。5秒之后,它会在哪里,那接下来实际上我们可以把用户的输入,把它给这个计算成一个未来的位置和朝向的这样的一个信号。
就比如说我用户输入右摇杆,向右推摇杆,那我其实是希望比如说我可以可以定义成,他是其实希望这个角色在未来,比如0。5秒之后,这个角色是向右朝向的。
那这样呢这样的话其实我们可以这个距离函数其实定义就是什么呢,就是说我在数据里边得到的这个角色,0。5秒之后的这个朝向,应该跟我推摇杆得到的这个输入的这个朝向是一致的,那所以这个其实也是一个定义的。
也是非常重要的一个距离函数,一部分,你说的还有些其他的,比如说我们也许会考虑一下这个啊脚部跟地面接触的这个关系,比如说左脚接触和右脚接触,那肯定这个是应该是就是迈左脚的时候。
迈右脚的时候其实应该是两cha应该距离比较远的,这个其实可能有些额外的信息需要我们加入,但是还有其他的,比如说我们想到的也许是,比如说想让这个角色他走的时候去伸手去抓一个东西。
那这个时候其实也许需要把这个抓这个东西,这件事情也作为你这个具体函数的一部分,所以实际上就相当于我们前面对这个啊这个motion graph。
就这个transition就是运动之间的这个切换关系的这个定义啊,这是这是motion graph那边需要做的事情,但对于motion match来说。
实际上你需要做的就是说我们可以我们需要定义一些这样的,具体的跟我们任务相关的一些特征,然后用这些特征来去帮助我们去寻找一个最近的,那当然找到最近您之后呢。
那我们其实接下来做的事情就是需要把当前的这个姿态,把它平滑的给混合到我们的这个最近的姿态之上,那这个其实有几种方式嘛,就说这种一方面,首先我们知道只要我切换,那肯定有一定的概率就会出现一个一个跳变。
跳变是不好的,所以实际上从从工程实践上角度讲,为了提高我们运动的平滑性,我们其实也不需要真的每一帧去做这个动作,因为用户可能输入并不会一直变化的嘛,所以说我们其实也在这个情。
如果在用户没有输入没有变化的情况下,我们其实可以隔一段时间去重新判断一下,这样减少跳变,另外的话就是说我们其实前面也说了,我们需要这个平滑的把当前的姿态可以让他过渡到我的下一帧的。
找到了这个最近离的这个姿态之上,那这个里边其实有很多可能的技巧,我们前面说的你做一个moving average,这是一种可行可行的方案,还有些其他的技巧,就比如说这个叫nalized。
这叫惯性或者叫带阻尼的这个混合,那这个我们这个这里就不讲细节了,但是如果有兴趣的话,可以参考一下这个du holden他的这个博客啊,但是这个其实好像有些我看到其实也看到一些中文的这个翻译。
整点这个也是一个方法,你的基本思想就是其实是一个平滑的一个混合的这样一个过程,那当然除此之外呢,我们还有一些其他的问题啊,比如说我们想要实现一个很好的一个嗯这个这个这个这个性能,但实际上最基本来说。
因为我们是要做最近的搜索嘛,这如果说我们数据集很大的情况下,那个最近的搜索,我们做一个线性搜索其实是非常慢的,所以实际上通常来讲,我们还是需要一些这种更加有效的这种呃动数据结构。
比如说像ky train,这是一类比较这种比较好,比较有效的数据结构来实现,但是如果说这个实在不行的话,如果数据量不是很大的话,那我做一个线性思维所其实也不是也不是太大的问题。
当然可能限速的太太太那个待太高的情况,它会比较慢,另外就是说其实这个也是玉玉璧呀,他们当时在那个gdc做报告的时候,他们其实也是提出了说他们当时用了一些技巧,比如说叫这个dance card。
其实dance card主要是用来去设计,我在这个motion match里面,因为motion matching我需要需要一堆数据作为我这个搜索的目标嘛,那这一堆数据它其实决定了我这动作输出的质量。
以什么样的动作去响应用户的交互,所以首先我们可以预先做一下计划,比如说保证我这个采集的这些动作能够覆盖到我想要的目标,其实他们当时用的所谓的dance card。
这么一个这么一个东西来指导他们的动作捕捉。
然后这个效果上来说,实际上我们当时已经看过了,这只是其实我觉得这个v6 经翻了好多遍了,但总体来说就是一个非常非常非常这个灵活的一个角色,然后其实它跟前面的motion fields是一样的。
就是其实他也是可以用来去响应一些外界的这种干扰了,比如说我可以去推一下这个角色。
我甚至可以比如说这个场景里边有些啊对像这种这种环境,那其实可以自动的根据啊合适的这种场景的这种变化,来选择合适的动作,那另外一方面,其实如果说我们做的再细一点的话,我们也可以加一点点物理仿真进去。
就是他推的那一瞬间,可能你不是说我跳的这个动图里面数据,而是说我其实这里仿真了一下。
稍微加了一点这个这个rag doll simulation,那是我rag dosimon,我们可以讲就是时间可以让那个face效果更加好一点,但是实际上这个实现起来也不是很复杂啊。
这个我们助教前两天自己自己写了一下,其实也就是也就是花一点时间的这个这个这个功夫,那其实用一个比较相对来说不是很大的数据集,也可以实现比较灵活的走路,当然有一个问题啊。
就是说motion magic本身就是也不能说虽然说他是一个很好的方法,很灵活的方法,但它本身也也是有一些呃基本的缺点,就是它作为一个对它本身并不能自动的去解决像这种滑步这样的问题。
就是说就是我就不能说不能希望这这种一个方法能够能够,非常完美的解决所有的问题啊,所以这实际上为了能够实现更好的效果的话,其实像这种滑步的话,还是需要额外的,比如说用i k有一些ik来帮助我们。
比如说把这个角在需要的时候固定在地面上,就是你可能需要去改变这个腿的位置,这个旋转甚至有可能需要去改变整个角色这个动作的这个幅度,或者速度等等,来来保证这个脚步打滑。
那这个其实本身也是一个比较复杂的问题,ok行,那我们前面其实还是稍微回顾了一下,就是我们在这个角色动画里边用motion graf和motion matching,我们可以可以可以比较是比较好的。
实现一个可交互的这样一个虚拟角色,那就用户只要给一些输入,那我们就可以输出相应的这个,我就可以在我们的数据集里边去重放一些相应的动作,来产生合适的合适的动画,当然这个本质上来说是什么呢。
就是说我们其实还是在重放一些动作,就是说这个这个数字,它的这个质量其实完全是来自于我的输入的这个动作捕捉数据,那当然这里其实长期来一直有一个问题,就是我们的输入数据总是有限的。
我们总是希望能够产生一些新的数据,这些数据可能不在我们动作捕捉里边,但是我希望这个生成的结果能够是一些比较自然的动作,那这里面就是实际上我们该怎么去对,我们根据我们已有的运动数据来对自然这件事情。
自动作自然这件事去把它建模,但实际上这个事情其实是比较比较比较难的,其实对于人来说,我觉得我们判断一下是非常容易的,比如说一看这个这两个动物,这两个动画,这左边这一看就是一个比较正常的人走路。
右边这一看就不是一个正常的人走路,或者说这不是一个人在走路,就甚至右,比如右边,其实右边本质上来说,我只是把它做了一个随机的姿态嘛,就是前面对左边是在播放一个动捕数据,那我们其实可以看到随机姿态。
如果说我们只是随机的产生一些姿态的话,那这些姿态绝大多数都是不真实的,当然偶尔我会产生一两个姿态,看起来好像也是能做出来的,但是其实绝大部分都是不自然的,那其实我们是第一个实验里边,就是我们留作业里边。
其实第一个实验里有一个是关于什么的,关于这个ik其实有些同学也做了,也发现这个i k在做i k的过程中,这个姿态其实也是经常会变得一些很奇怪,完全不像人的一个状态。
所以首先这也就是说明实际上这个这个能够找到这样一个模型,它能够告诉我们什么是好的姿态,什么是自然的姿态,什么是不自然的姿态,这个其实是非常重要的一个问题。
当然实际上我如果说我们再仔细回头看一下我们当时这个动作,比如说走路,其实人的动作或者是一般这个我们常见的动作动物啊,或者很多角色的动物啊,动作它其实是一个有一种所谓的低维结构的这样的一个一个数据。
或者再去点一点什么呢,就是说比如说我们这个一个动作啊,不好意思啊,比如说我们这个角色他可能有比如说20个关节,然后20个关节呢,那可能比如说我们知道每个关节的旋转,我们表示成一个啊奥拉角。
那可能或者欧拉角或者是早认证,我们每个旋转至少需要三个自由三个参数来进行计算,那在这个过程中,我们知道20个参数,20个关节呢其实大概就是60个啊,所谓的这个这个60个自由度。
那当然这个时候作为绝大部分的自由度,其实都是冗余的,因为我刚才也看到了,如果他不是冗余的话,那我们应该随机采一个样,踩一个随机产生一个姿态,它应该就是很好的姿态,但实际上考虑到我们的角色。
比如说人在走路的时候,我们的手和脚,它其实是处于一个协调的状态的,我们正常一般来说如果不加以不特别注意的话啊,其实肯定来说你的手和脚是反向摆动的,为什么要反向摆动呢,因为他们带来手和脚的反向摆动。
可以带来互相抵消的这个角速度来帮助你平衡,要不然的话,其实这个你要需要额外的做一些额外的废掉一些力去保持平衡,这个实际上是人总是要偷懒嘛,所以总是想要找一个这样的姿势去去走,那另外还有一些其他的。
比如说我们的关节骨骼结构也会带来一些问题,也会带来一些这种啊自由度的减少,就比如说一些例子啊,比如说像大家知道我们抬胳膊,比如说抬胳膊,把胳膊抬抬到上面去,首先在这个过程中。
你的身体至少你的肩关节会发生旋转,除此之外呢,其实你的锁骨就是中间那个关节其实也会有一个旋转,这样这两个旋转其实比例关系是比较固定的,还有一些其他例子,比如说手指,我弯手指。
如果说我们这个没有拿东西的话,就是你可以发现我弯手指的时候,就是比如食指和中指啊,这样的你会发现你的这个手指,它两个关节他俩的旋转的角度也是有一个相对比较固定的关系,因为这都是我们的身体结构,来来来算。
不好意思,都是由我们的身体结啊结构来确定出来的,那其实还有一些其他的,比如说这个我们总是在地面上移动嘛,因为这是这是我们的这个这个物理定律来解决来来决定的。
所以说我们其实也是不可能任意的变到一个其他奇怪的姿态姿态上,所以总的来说我们也这就是说我们的这个人的动作,其实是相对来说一个比较低维的结构,或者再具体一点啊,或者再抽象一点的话,其实相当于是什么呢。
或者叫低维的流行,或者叫类似的一个一些一些说法,但总体来说,但这个只是一个非常简单的实例,其实不会是这么简单的一个这个卷饼一样的姿态,其实更加复杂一点的姿态,那当然这里边其实有一个问题。
就是说呃这在这在这里边,sorry,不好意思,那在这里边其实我们可以说比如整个这个空间里边,就是我们整体的这样一个可行的,比如说60维的这么一个姿态空间,那实际上我们可自然的看起来比较真实的动作。
那其实就是在中间里边的这样一个曲面啊,这个曲面可能多少倍我们是不知道的,但总而言之,它是一个它是一个相对的低位的曲面,如果说当我们人在里面走的时候,他可能在这个曲面上就是一条在曲面上面的一个一个曲线。
那当然如何如何去找这样的方式,主要如何去找这样的一个低v局面,那么其实有很多种方法,我们这里这里只是简单介绍其中一种方法就是pc叫叫这个主成分分析,对主成分分析。
但主流分析它主要是一个非常常用的一个技术,就是在数据处理里边,它主要是用来去寻找我们高维数据里边不同维度之间的相相对关系,然后另外一个功能就是说在这在这个基础之上呢,我们可以去做这种呃降维的操作。
这降维可以干什么事情,我刚才提到提到了,为什么要降维,主要是因为我们知道我们的动作应该是在一个低维度的这样一个,空间里面去,那我们其实降维就是在给出一些数据之后。
我们就从这些数据里去学这样的一个地位空间,那比如说这里面这个就是一个简单的例子,假如说我们这里的点,我们这些点我们可以认为每一个点,比如说都是这个走路这个状态里边的一个姿势,我们知道一个姿势是什么。
它代表了每个关节的旋转和root和这个根关节的这个这个位置等等等等,答案这里只是一个示意图了,我就随便点了一些点,但是大体来说你可以想象应该是一个类似的这样的一个东西,当然我们肉眼一看,我们打眼一看。
ok我们可以知道,其实这个这个动作其实是这些数据其实有非常非常有规律的,什么规律呢,就是说实际上比如说我可以找到这么一个轴,我可以找到这么轴,可以在这个轴上发现这个数据是以一个比较比较宽的呃。
就是比较大范围的分布的这样的一个这样一个轴,那通常来讲我们认为这个轴其实它可以提供了我们很多有用的信息,那么除此之外的另外一个轴,相对来说这个数据的分布就比较小,然后比较小。
其实通常来讲我们认为他会说这个这部分信息可能是一方面,可能是一些噪音,就比如说因为动捕嘛,我们其实我有点有,有时候会采集一些噪音,但是有一些其他的,比如说我这人我我闲着没事,我这腿在那乱晃。
那这乱晃这个动作其实对我这个整体动作来说可能没有那么大的影响,那这些这些动作其实可能也会体现在另外一个另外一个轴这个方向上,总的来说这个轴相对来说一些不是特别有,相对来说不是特别有用的信息。
那pc的方法就是说我们是需要去找到一些轴在这个轴上,数据在这些轴上的投影,它能够提供给我们尽可能多的信息,那这其实是pc的这样的一个目的,那当然这种这个东西怎么做呢。
我们其实可以需要给它一个更加这个数据更加准确的一个定义,就比如说我现在有一堆数据了,有一堆数据点,那可能是高维的,我们当然这里只画成二维的,然后我假设有一个空间里边的一条设一条方向。
一个方向它是一个是一个一个向量,当然是一个单位向量,那当然这些所有这些数据点在这个单位向量上的投影,它当然它是一个像一个一个标量,是一个数,这个数其实也是某种程度上。
也是代表了这个这个这些数据点的一个一个一个数值表示,那当然如果说我们把这些知识点做完这个映射之后,我们其实可以看到它在这个这个方向上,它大概所代表的这个分布式是大概是这个样子,当然相对。
如果说我画了另外一条轴,另外一个方向,那我们做同样的操作,我们其实可以发现,其实这个数据点也会也会在这个另外一条轴上有类似的分布,当然你可以比较可以发现,其实在第一个轴这里的分布它散得更开一点。
或者说它的方差更大一点,而在第二个轴,就这条轴,如果我如果每个都做投影的话,你会发现它整体来说更加集中一些,或者它的方差更加小一点,而实际上如果说我们让这个轴在这个空间里绕着某一个方向。
比如说随机转任何一个角度,那其实我们对应的可以算出很多很多不同的轴,都会有对应的不同的方差,那这里边其实我们定义什么,这个因为我们目标是找到它能够在这个轴上的信息,最大的这样的一个一个方向。
那所以说实际上我们是认为这个方差,就代表了它能够多大程度上描述这个数据里面的信息,或者说实际上我们目标pc的目标就是首先我找到一个轴,找到一个轴,这个当这个轴本身是一个单位向量。
我希望我的每一个数据点在这个单位向量上的映射还应该是一个标量,这个标量本身我可以计算,就比如说每比如说有有100个点,那我就算出100个标量,那对这100个标量,我希望能够去算一个方差。
那我希望我找到了这个eu的这个这个方向向量,它能够让这个方差最大化,这是我pc的一个基本目标,但这里我们只找一个整,那这个我们可以稍微再把它稍微这个在写的这个更加紧凑一点。
比如说我可以通常来讲一个啊矩阵x来代表所有的数据,在这里是数据,每个数据点它是一个向量,那因为我们在我们的这个这门课里边,它都是一列向量,所以我把它转置一下,这是行向量,要把所有的好像叠在一起。
构成了一个稍微一个一个扁长的,一个是一个长条的一个矩阵,那横的一般来说,因为主要来说我们通常来讲数据的数据是更多一点的,当然理论,但是也其实也是相对少一点也可以,但是有不同的有类似的方法。
但我们假设如果数据更多一点,数据比为数多,那么其实它是一个瘦长的一个矩阵,那么其实可以证明怎么证,我就不讲了,但是这个我们是可以证明的,这个最好的这样的一个u或者不是最好的u。
就是就是这样的一个u这样的一个方向向量,它是它总是x t乘以x这个矩阵的一个特征值,所以这个u应该是对应于x1 x这个这个矩阵,它是一个方阵,最就是数值最大的一个特征值所对应的这个特征向量。
这是我们就是这种pc的一个一个就就对应的这样的一个层层限量,其实就是我们的这个这个最最大的这个这个叫叫逐个主成分,那当然我们得到u之后呢,我们其实可以知道我们这x我们其实可以反过来写成。
因为我们根据我们这x定义嘛,我们就可以把它反过来写成是x平均值,加上一个呃w一个这个这个数值,然后乘以这个ud这个形式,这是来自于我们的定义,那pc a如果说我们再继续把它扩展一下。
因为我们前面只是讲了,我找到一个轴,让这个轴方向上的这个这个方差最大,那首先pc是可以进一步的定义的,比如说我找到一个轴之后,我把这个轴里边的所有的信息都可以从这个原来的数据集里剪掉。
然后在剩下的一些信息里边,我再找一个新的轴,然后这个新的周易它的方方差能够达到最大,然后依次类推,所以总体来说呢c c就说我们给了数据数据集之后,我们是可以找若干个轴,若干个这个方向。
然后这个原来这个点在每一个方向上都有投影,然后对应的每一个方向的投影的方向呃,都有一个投影的这个这个典藏的这个值,然后我们在这个在pc时说,我找到了一组这样的u k,就这样的这个方向之后呢。
我可以保证对于每一个u k它的这个对应的这个点乘,就是这个硬这个projection,就是这个这个投影的这个方差都能达到当时的最大值,也就是说第一个主元素组成分它是肯定是整体的最大值。
然后第二个主成分它其实是映射之后呢,得到的是去掉了第一个主成分之后的那些信息再去映射,然后他的他的这个方差的最大值,然后依次做类推,总的来说,其实我们可以从这里边是有一些性质的。
就是首先这个u k它的主成分我们其实其实可以类似的,可以推导,我们就可以得到一些什么,就是所有的uk应该都是我们的这个啊这个方差矩阵,协方差矩阵就是x t乘以x,它本身就是对一个斜方差矩阵。
它都是这个斜方差矩阵的啊,特征特征向量,然后这个斜方肌斜方差矩阵,因为本身它是一个对称的,或者至少是这个半正定的,所以它其实是可以保证它是有这个一定有n个特征值的。
那我们其实可以把n个特征值按照从大到小排序排序之后呢,那其实对应的第一个特征值,对应的其实就是我的主成分的第一个主成分,第二个特征值就是对应的第二个主成分,而实际上按照我们前面的这种定义。
第一个主成分上的这个方差,投影的方差肯定是最大的,第二个副主成份投影的方差是次大的,然后依次类推,同时还有一个另外一个性质,就是说所有的主成分,所有的这个主主成分,这个轴这些u应该都是互相垂直的。
就是互相就是所以整体来说,这个如果说把所有的eu写成一个矩阵的话,它应该是一个对一个选一个一个不是旋转角,一个正交矩阵,这是我们这个pc的一个结论。
当然实际上我们可以把比如说我们可以直接对这个我们的这个数据,比如对一段走路这个动作做一下pc,那我们怎么做呢,我们知道这个走路动作它是一系列的这个姿态构成吧,然后每个姿态都是一些关节的旋转来构成。
比如说我们可以把关节旋转转化成一个,比如说这个旋转向量,旋转向量什么,旋转向量是轴乘以角,就是轴角表示,然后这样的话,比如说20 20个关节或者25个关节,那它就对应了一个20 20x20。
sorry 20乘以三的这么一个这么一个向量,然后把这个向量定义成一个数据点啊,比如说这个动作本身,比如说300帧,那它就有300个数据点,那么对这300数据点可以做一下pc。
我们可以看到一些什么结果呢,比如我们可以到第一个主成分,大概长长的是这个样子,看起来其实已经是一个在走路,但是有点有点稍微有点奇怪,第二个主成分其实还是有点类似于走路的一个一个状态。
然后第三个主持人看起来就稍微有点怪了,但他其实还是是就是可以可以看到大家说前两个主成分不能弯,腿,弯的不够,然后第三个主成分主要是处理弯腿上一个姿势,然后第四个主流分其实是除了弯嘴之外。
还可能要处理一下这个这个这个呃宽的这个旋转这样的一些信息,当然回去回到我们前面这个这个这个pc的这个定义啊,就是pc实际上是找到了一系列的啊主成分的方向。
然后实际上我相当于是某种程度上我做了一个这个坐标系机的转换,就说我们其实把原来的一个数据点,它是一个x向量,我们把它转化成,比如你首先这个所有数据点的一个平均值。
加上一个新的在一个新的基底的这样一个表示,就这w其实对应的在新的基底下这样一个坐标坐标的参数,但是如果说我们选择了若干基地,比如说对于这样的一个是刚才我们提到的,比如说这是我们走路,我们选择一个方向。
然后把他的这个这个w算进来,然后再选第二个方向,把对应的这个w和这个矩阵算下来,那我们其实可以发现还有一个什么事情呢,比如说比如说我们目标我们输入的这个动作是一个正常走路。
然后如果说我们只是把平均值加上第一个主成分,那他出来动作大概是这个样子,然后在这个基础之上呢,我再把第二个主成分和香的坐标值加起来,你会发现它会稍微变得更加自然一点,然后类似的。
如果说把第三个主成分再加上去的话,它就变得更加自然一点,我是不是差差错,好ok sorry,我好像这个动画稍微有点问题啊,但是确实总之结论是没有问题,也就是说我们其实加了若干主人文之后。
这个动作就会越来越接近于我们原始的那个动作,从这个其实从这个pc来讲,这个嗯这个就是这个方法本身来说,它其实也是有这样的一个结论的,就是说因为我们前面提到了每一个主成分的方向。
它代表了我这个我在这个方向上的这个投影的方差的,这个总是让这个投影的方差变得最大,那其实另外一个角度讲,它其实也代表了在这个投影方向上,我能够表达我原有数据的多少信息。
那这个信息量我其实是可以用方差来进行一个定义的,所以说一个一个典型的这样的一个pc分析,我们可以看到,比如说这是一个若干涸的pc,我们可以看到它对应每一个主成分方向,或者说对于每一个啊特征值方向。
它的方差是从大到小逐渐减小,而实际上在大部分情况下,因为我们知道我们数据本身它是有一个相对来说是一个dv结构的,所以说通常来讲,你会发现在前面几个方差的这个值是相对来说非常非常大的。
就相对后面来说是相对来说比较大的,然后后面逐渐的逐渐的变得比较小,如果说我们把它累加起来,如果说我们把它方差,把每一个这个特征值或者是每一个主人分对的方差累加起来。
你会发现它可能从一个相对比较小的一个比较大的一个值,一个中等大小的值,然后然后很快的就变成一个非常接近于一的这个范围,所以实际上这也说明什么呢,就是说为了能够或者说就是另外一个角度讲述。
就是实际上这个也代表了我能够以多大的这个呃百分比,能够去恢复我原有的数据,就比如说这个比如说在这个情况下,比如像这样一个图,我们可以看到,比如说在大概在六或者八左右吧,比如在第八。
如果说我用前八个主成分的这个数值加在一起,它其实大概就能恢复90%多的这样的一个原始的数据信息,那剩下还有10% 10%左右那个局我怎么看了,如果说我们也许认为剩下的10%或者5%,可能就是来自于噪音。
我们其实可以忽略掉这些噪音的干扰,我们就只考虑前面这些组成分的部分,所以总体来说我们通常来讲会选择一个一个范围,比如说比如95%,那通常来讲是一个比较好的一个范围的这个95%,它所对应的是多少个主成分。
能够达到达到95%的这个这个方差的这个总和,那其实就代表了我我能能够多大程度上恢复原样的数据,这是一个通常来讲,对于主成分析来说,我们都会选择这样一个阈值,然后只会使用前面的啊若干个主成分。
来进行后面的这个这个后面的这个运算的操作,另外一个问题就是说实际上因为每一个主成分,我们知道它是代表了数据在这个数据点在这个上面的映射的这个方差,实际上在这个对于这样一个分布来说,实际上我们可以看到。
对于一个稍微相对来说更加接近于中心的这样一个点,它所对应的一个姿态,通常来讲它会比一个比较远的点的对应那个姿态,更有可能是一个比较好的姿态,这是一个这个基本的规律,所以实际上另外一个角度讲。
我们其实可以知道是说这个对于一个角色对一个姿态,比如说给出一个这个数据点,我们可以算出它的每一个主成分方向上的投影的这个值,然后我们知道这然后这个值跟方差的比值。
因为我们知道这个呃不同不同主成分让他们的这个方差代是不一样的,而且时间都意味着什么呢,意味着我们在不同组成部分轴上,离原点的距离的这个对应的这个好坏程度的变化速度也是不一样的。
所以说为了这个我们可以把它这个把它做一下这个啊正则化,我们其实可以算一下,当前这个投影值除以我们当前的这个这个对应这个轴上的这个方差值,其实就是做了一个做了一个正规化的操作。
那我们其实可以认为这个值其实它大概是告诉了我们,这个姿态本身是不是一个很好的姿态的这样的一个一个度量,或者说我们当我们给了一个新的姿态,比如它它是一个我们数据集上完全没有的一个姿态。
一个x我们同样的可以先去做一下这个投影,然后投影到每一个朝向上,然后接下来我们其实可以用同样的方式,比如除以它的这个方差,然后可以看到通过这个可以判断一下它有多大多大可能性,是是一个很好的一个姿态。
那这个东西其实可以用到什么地方呢,就比如说我们可以把它作为一种这种先验一个动作的鲜艳,然后跟其他的一些任务来做结合,比如说我们想要做i k,我们前面做i k的时候,其实我们一直提到了。
我们i k其实本质上是一个优化问题,优化问题里边的第一项,那我其实是想要让我的每个末端节点尽可能接近我的目标位置,那其实还有第二项,我们其实刚才那节课里也提过了,就我们其实有第二项。
我们是希望在满足第一项即可满足第一项条件之下呢,我希望我整体的旋转即可能小,其实我给大家也看了一下,我们发在这个我们在群里交流的时候,有些同学发的这个ik的结果,看那个角色的这个动作在不停的转啊。
比如说你用c4 d啊,其实你不太容易去考虑这样的第二项,这个就让让这个整体动作最小这样一个姿势,因为只靠边的话,就是相对来说更更更更加容易一些,这个项其实存在就是说为了能够其实本质来说。
这个我们是基于什么假设呢,基于说我的姿势,每个关节旋转尽可能小的时候,我可能这个姿态是更加容易是真实的一个姿势,就是基于这样一个假设,那当时这个还有更可以稍微改造一下。
因为前面这个假设是要求我这个姿势的每个关节旋转机,能想到其实本质上最小的就是t pose嘛,那t bot这个其实也有时候也是从来讲也不是我们最想要的姿势,所以实际上我们可以再稍微改一改。
我们可以让这个角色的姿态非常跟某一个,比如说可以跟当前的一个状态尽可能的进,那这样的话其实也是给我的这个角色做一下这个呃,做一个正子化,做一个这个啊限制说你在做i k的时候,你不要跑得太远了。
因为跑的太远,你可能就完全不对的一个一个一个状态了,当然这里其实还是说我们只是给了一个啊pose一个姿势作为我们的目标,那如果说我们其实可以进去把它把它替换掉,因为我们前面前面一直用的。
我们其实前面一直提到是说我们其实是可以利用这样的一个距离,就是我在美国某个主成分上的一个一个投影,然后处理对应主成分上的这个方差,这个距离它大概代表了这个角色的姿态,有多大可能性是一个好的姿态。
那我其实可以把这个东西作为我的第三个正则项,那其实也会给多少人能够给我们一些作用,就是说比如说我一直走路,它会出现一个什么效果呢,就比如我们走路就是两个手手和腿,手握脚是这个共用的。
就是一个对侧移动的这样的一个状态,也就是说如果说i k里边把一个手抬起来了,那其实很有可能这个出来结束,他会他会把另外一个手向后摆,就比如说ik是把一个手向前摆。
那其实就这样的一个呃正则向其实会让我另外一只向后摆,同时我的我的两个腿其实也会相应的进行摆动起来,它其实是带来一个全身的一个姿态的一个控制,那如果说我们不是走路,而是另外的一些动作的话呢。
其实它会带来的是类对于那种动作的这种啊,这种这种这种这种动作的效果,单从另外一个角度讲,就是我们前面提到的这些动作啊,就是我们前面比如走路跑步什么各种各样的动作之后。
我们都是我们为什么说我们能够判断这些角色是啊,sorry,这些动作这些姿态应该是自然的,那我们其实是是有一个假设,这个假设是什么呢,就是说我们这些动作总是从一个分布里面。
就是一个概率分布里边去得到了一些样本,我再具体一点,或者另外一句话,换一种方法,就是说我们其实比如说给了一个姿态之后,我们其实可以假设总是有这样一个函数,这样一个概率的一个一个一个函数。
它可以计算出我这个姿态是一个自然的姿态的这样的一个概率,然后那个函数可以非常复杂了,比如说这个非常简单的这种可能是一个高斯分布的这样一个状态,或者可能会更加复杂的,比如说这是一个不是一个单风。
而是一个多风的这样的一个这样一个这样一个非线性的一个函数,可以比较复杂,但是如果说我们知道有这样的一个参数,也有这么一个分布之后呢,我们其实可以在这个分布上进行采样,那我们其实期望的说。
因为它是一个很好的一个一个分布,那我就理论来说,我上面采样这些样本点应该都是对应的是比较好的,这样的一个一个动作啊,比较正气净正气的这样一个角色的姿态,到另外一个角度讲。
就是如果说等创作通常来讲这样一个分布我们通常是不知道的,所以实际上我们能做的事情是什么呢,就是说我们得到了一些数据,我们动不动捕了两个小时,那我就得到大量的这个动作和大量的姿态,那这个姿态。
我们其实如果说我们假设他们都是来自于同样的一个概率分布的话,那我们该如何去计算这样的概率分布,如这其实就是我们的这个所谓这个啊概率模型的,就说我们知道我们有若干个数据之后,我们知道这些数据是我们猜测。
我们假设这些数据是来自同一个分布的,那我们希望能够找到一个方法,能把这个分布给估计出来,那估计出来这个分布之后呢,那我们其实可以用它来去做下一步的,比如说我们做i k呀,或者做各种动作生成这样的工作。
当然怎么估计,那这个其实有很多经典的方法,就比如说最经典的一种方法就是所谓的概率啊,sorry高斯分布,我们其实就假设就没有任何没有任何根据的,我们就假设说假设这个分布就是一个高斯分布。
那高斯分布其实非常简单,它就是一圈一圈一个椭椭圆的这样一个一个形状,但如果说在我们给出这样的假设之后,比如说我们就是假设是靠村部,那高速布里边,我们知道它有几个参数呢。
它的这个平均值和方差其实是两个参数,是我们需要从数据里边去得到的,那这个得到其实我们会有一些方法,比如说用这个基本的方法,就是这个这个最大收益最大最大似然估计,对最大最大私人估计这样的方法。
那我们其实可以得到什么呢,就是我们的平均值应该是刚好是等于我所有数据点的平均,然后我的这个方差应该是这个方差矩阵,就是协方差矩阵,就是x当我们把它写成还是我们那个s定义,对应的这个行向量。
所以说x一个数据的这样一个矩阵,那它这个方差其实应该是这个数据矩阵的转置乘以,它本身它就是一个小一点的方阵这样的一个形式,那p c a实际上我们其实我们知道pc我们的计算时。
我们我们是对刚才这个方差矩阵,对这个矩阵其实对他做了一个特征值分解,那分值分解其实对应的是什么呢,其实分支分解某种程度上也是可以对应于一个坐标转换,或者一个变量代换,在这里就是原来那个变量是x。
我们其实通过变量代换之后,把它变成一个w的这样一个一个函数,而对于w来说,因为这个方差的矩阵它是一个对角阵,对角就是在这个高斯分布式这个方差是对角阵的时候,它其实代表了说什么,代表是说每一个呃。
所以说也就意味着我们前面这个方差这样一个矩阵,这个它的这个概率密度函数,我们是可以写成一个乘积的形式的,然后每一个子乘积都是一个对应的这个一个小的一个一个正态分布,这样的一个形式。
那就在这样的一个定义之下呢,实际上我们可以回头看一下,我们前面就看那个i k里面那个公式啊,我们这个优化公式是说我们前面一项是ik本身的这样一个目标,然后后面一项我们写成了这样一个方式。
实际上我们可以把它稍微变一下形,原来是一个求和的一个形式,我们把它变成一个倒呃对数,然后乘积的一个形式,因为这两个其实是相等的,然后接下来呢其实回顾一下前面关于这个pc的降维之后。
就是做做这个坐标变换之后的这个这个形式,我们其实可以大概可以直接把它可以判断出他其实是前面这个形式,其实刚好是说我们这个呃该为密度函数的这样一个负的,log的这样的一个形式。
所以本质上来说我们前面的i k就是当我们有一个motion prior的时候,我们做i k实际上就是两部分,实际就是两部分,是让我的末端点去移动到某一个位置,然后第二部分是我们的正则项。
他去让我他去来保证我们输出这个动作是比较自然的,那这个过程实际上是说我们是在去最小化food log负的,这个就是这个这个概率密度函数的负对数,那另外一个分法就其实等价于是说我在最大化我这个姿态。
在这个概率密度函数的下载值,或者换句话说是因为我在最大化这个姿态,这个姿态的这个是一个正常的姿势的概率,其实它代表这样的一个作用,所以总体来说实际上在我们如果说我们有一个模型prior。
那可以是一个比如说前面用的pc或者高斯分布,得到这么一个prior之后,不管怎么样,我们其实有一个普尔可能什么形式,然后我们从一些初级点里把它学出来了。
那接下来这个问题我们其实是这个motion和呃运动合成的一个问题,就是说是需要去就等价于说我们去做了这么一个,去优化了这么一个函数,这个函数的第一部分是我的跟动作相关的一些一些信息。
第二部分是一个运动啊,motion prior,就是我们的动作先验的这样一个信息,它来保证我这个角色的姿态是一个比较正常的姿态,总的来说这个样子,当然这是一个非常通用的一个写法了。
当然这里面ex我们前面一直说它x可能是只是代表了一个pose,但实际上我们可以进一步的扩展一下,它其实可能不一定只代表一个pose,它能代表若干个pose,这些pose整体构成一个动作。
一个动作片段或者时间,它也不一定是pose,他可能是任何一个动作相关的一个特征,就比如说pc的这个权重等等等等等等,这些都是一些我们我们会关心的一些一些特征。
然后前面这个跟动作运动相关的这样的一个嗯函数,那其实其实我们要生成什么样的动作,比如说对于i k我们是去实际到末端点的这个位置,然后对于一些比如说我们可以给出一些少量的关键帧。
我需要让这个动作去补全这个关键帧之间的动作,那其实我对应的其实就只是关键帧上算相当算一个loss,而且比如说用户用户控制,我希望我生存的动作能够按照用户的这个手柄控制,向向向左右向向右自由的旋转。
自由的移动,那就是对应的相对来说也是不同形式的,这样的这个这个前面这个这个函数,还有一些其他的,比如说环境有些约束,比如有一个物体或者有个桥,有个悬崖,我希望主角做运动的时候不要跳进去。
那其实这个也可以转化成对应的这样一个优化函数,这个这个这个项目总体来说,但这个形式上本体来说就是就是这么一个常见的一个形式,前面加一个跟任务相关的一个项。
然后加上一个对呃运动先验的这样的一个这样的一个一个一个正则项。
是我们常用的一个形式,然后这个形式其实也是用了很多。
也是在很久很很也是在很长一段时间都是被被使用过的。
不好意思对,就是像比如这是一个嗯04年的一个工作了,就是他其实是就是刚才我们说他其实是基于pc的,比如说我在一个比如少量几个动作开始。
比如可能有三四个动作,三段动作之上,我算了一个pc,那这个pc其实就给了我一个这个动作的这个比如说我在pc做完pc之后。
找前面几个主要组成分啊,这些组成分离,这其实就告诉我在什么样一个范围,这个动作是看起来可能是自然的,然后接下来我们就可以给出一些其他的一些约束,比如说这个角我在移动的时候,我希望这个脚抬的更高一点。
那这个时候其实它会转换到一个对于相对于高度的一个优化变量,然后再加上我pc给出的这个动作经验,那我其实可以保证我在运动过程中生成一个看起来比较自然的动作,同时能够满足我前面这个运动心眼。
其实对应的类似于其实差不多也是ik的这样一个功能。
还有其他的,比如说像这种在一个后宫番里边,我们可以调整中间某一个状态时候角色的姿势,那其实我们也可以加上前面的优化的过程之后来实现这样的过程,同时整体来说动作也是相对来说是是是比较自然的。
ok当然前面实际上对于pc或者基于高斯分布的这样的一个先验。
相对来说还是非常非常有局限性的,就是说其实我们看了一些动作,比如走路比较简单,但是稍微复杂一点的,比如说做一个后空翻啊,做一个比如说做一段跳舞,那其实这个靠一个高斯步是很难实现的,因为主要是说我们动作。
其实我们把它假设高斯是一个非常非常大的一个简化,而且实际上我们这个动作他的这个概率,概率密度函数可能是一个非常复杂的概率密度函数,只靠高斯的话可能很难去,比如他可能还有多多个这个多个峰值。
一个高斯只是单风了嘛,他可能很难去踩到其他峰值的这个效果,所以说为了解决这些问题嘛,其实后来也是有很多一些相关的这个研究去想要去把这个高斯的模型,把它用更加复杂一点的高斯模型来来去实现。
就比如说09年的时候,这个我这工作很多了,我只是随便挑了两个,也不是随便就是挑两个比较比较经典的,就比如说这个就是这个以前这个陈建翔老师他们组以前做的一些工作,他们是可以用了一个高斯混合模型。
高斯混合模型它不是一个一个高斯,是一个每一个n代表一个正态分布,它对应的是一个高斯模型,然后高斯混合模型其实是把若干个这样的混合模型,用一个参数给混合起来,实现了一个一个更加复杂的这样一个函数。
然后他们其实把这个模型应用在比如说这个动作编辑上面。
那其实可以得到一些很有意思的效果。
这个模型里边比如说我可以对我这里跟安安,按照我刚才提到了,就是其实motion prior就是我们这个动作先验里边不一定只有一个动作。
它可能代表了若干个动作。
那这些若干动作合在一起,其实构成了一个完整的。
就是比如说我判断相当于我是我我我这个输出这个概率函数,输出其实是整个这一小段动作,它是不是真的动作,那这个其实跟我有什么效果,就比如说我在改这样一个角色,整个这段动作里面某一个某一帧的这个状态的时候。
实际上他是可以因为本它这一帧对应的,其实是说为了让这一帧在整个洞里看起来真实,其他对应的可能整个动作都是需要相当发生一些变化之后,才能让这一帧看起来真实的。
所以产生这样的一个效果,其实还有些其他的,主要是说可以在这方面可以做一些这种动作编辑的操作。
还是非常有意思的。
然后其实都已经,而且实际上比如说你会做做做sorry,你会做动作编辑之后呢,其实自然的一些其他的操作,比如说我们前面提到了基于视频的单视角,视频的动作捕捉,它本身是一个签约签约束的问题。
就是说实际上我们不同的姿势,很多种不同的姿势可能都会意识到同样一个二维的平面的效果。
但是如果说我们有这样的动作经验的话,其实我们可以过滤掉很多不正确的姿势,那其实剩下的部分应该就是比较正确的,就是可能是我们想要的这个角色真正的姿势。
然后以此类推,当然还有其他的,比如说更加复杂一些方法,比如说前面的这个高斯混合模型,其实它也是有一些基本的缺陷呢,就说我我我混合的这个数量是需要预先指定的,而且是一个比较比较敏感的一个参数。
还有些其他的方法,比如说12年的时候就是那个circle啊,其实他现在后来不做,他们以前也是做animation的,他们之前也也做了一些尝试,就说把这个高斯不是用高斯混合模型了,而是用高斯过程。
就这个一个这个g p r v m来去实现,我们对我们这个动作的这个鲜艳的这样一个估计,那这个具体的这个这个内容我就不讲了。
但是这个这也是一个很有意思的工作,那实际上也是可以实现非常符自由的。
这样一个受控的这样的一个角色啊,这样一个受控的角色同时也可以生成非常自然的动作。
当然是前面这两种方法,就是不管哪一种方法,其实都是基于一个啊高斯分布,就是高斯过程呀,或者高斯就当是这一类的这个比较传统的这种这种这种概率模型,当然就是虽然效果还是不错的。
但实际上你可以如果你读了他paper论文,或者说你尝试自己实现的话,你会发现其实实现起来非常的麻烦,因为好多参数特别是高斯模型里边,比如说高斯混合模型里边,这个我的这个混合多少个高斯分布,这是一个参数。
或者比如说gpl v m里边,比如说我的这个核空间,我的科目函数该怎么定义,这也是一些非常难得,所以说总体来说,如果说你想实现这个效果是非常麻烦的一个过程,而另外一方面,其实从表现力来讲。
我们其实前面这个函数。
我们整体这个函数实际上是非常非常复杂的,就是他我们前面只是假设它是高斯的,但这件假设其实也不完全是准确的,所以实际上随着后来其实也是近2年近5年的发展,其实大家也发现,特别是生成模型。
其实大家看到生成模型在很多领域,特别是在图像生成方面,图像语音生成方面,我们其实看到非常非常好的效果,特别是这2年看到的,比如说这种stability fusion。
就是可以给一段文字可以生成非常好的这个图像,其实它本质上也是一样的问题,就是我给大量模型训练之后,我学到了一个非常复杂的函数,这个复杂的函数可以估计我这个动作到底这个图像到底是不是真的。
图像对动物来说也是一样的,就是我们其实可以用神经网络,加上这个这个神经网络的这样的方法来代替,我们前面传统这些方法里面用的这种各种各种复杂的高斯混合模型,来实现一个对我们这个运动先验这样的一个估计。
然后在这个估计之上,那我们其实可以生成可以很容易地生成更加复杂的这个动作,但这个我们下节课会会做进一步的介绍,那我们今天只是稍微提一提,我们主要做什么事情,然后反正我们前面也是稍微回顾了一下。
我们关于动作图和motion matching的一些。
稍微稍微稍微多讲一点时间细节,ok那我们今天的这就是我们今天主要内容了,那我们今天的内容就到这里啊,非常感谢大家。