-
Notifications
You must be signed in to change notification settings - Fork 2
/
CharacterAnimation.tex
3307 lines (2903 loc) · 157 KB
/
CharacterAnimation.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\documentclass[lang=cn,newtx,10pt,scheme=chinese]{elegantbook}
\title{角色动画入门}
\subtitle{Games105学习笔记}
\author{Sisyphes}
\institute{家里蹲}
\date{\today}
\version{0.9}
% \bioinfo{自定义}{信息}
% \extrainfo{使用的是优美的Elegant\LaTeX{} 模板}
\setcounter{tocdepth}{3}
\logo{logo-blue.png}
\cover{cover.jpg}
% 本文档命令
\usepackage{array}
\newcommand{\ccr}[1]{\makecell{{\color{#1}\rule{1cm}{1cm}}}}
% 修改标题页的橙色带
\definecolor{customcolor}{RGB}{32,178,170}
\colorlet{coverlinecolor}{customcolor}
\usepackage{cprotect}
\addbibresource[location=local]{reference.bib} % 参考文献,不要删除
\begin{document}
\maketitle
\frontmatter
\tableofcontents
\mainmatter
\chapter{0}
\section{废话}
写到最后,才是真的开始。目前有很多问题,后续看情况优化更新。
\chapter{角色动画介绍}
\begin{itemize}
\item 模板官网:\href{https://elegantlatex.org/}{elegantlatex.org}
\item 模板GitHub 地址:\href{https://github.com/ElegantLaTeX/}{ElegantLaTeX}
\item 课程官网:\href{https://games-105.github.io/}{games-105}
\item 课程Github 地址:\href{https://github.com/GAMES-105/GAMES-105}{GAMES-105-hw}
\item 项目地址:\href{https://github.com/foocker}{Sisyphes}
\item 作图工具:\href{https://www.geogebra.org/?lang=en}{geogebra}
\end{itemize}
\section{基本介绍}
这一节老师讲的比较精彩,建议看原视频,这里随便贴几张图。
\begin{figure}[htbp]
\centering
\includegraphics[totalheight=1.5in]{"./image/Physics-basedCharacterAnimation.png"}
\caption{Physics-based Character Animation} \label{fig:Physics-basedCharacterAnimation}
\end{figure}
\begin{figure}[htbp]
\centering
\includegraphics[totalheight=1.5in]{"./image/GenerativeControlPolicies.png"}
\caption{Generative Control Policies} \label{fig:GenerativeControlPolicies}
\end{figure}
\begin{figure}[htbp]
\centering
\includegraphics[totalheight=2in]{"./image/CharacterAnimationMethods.png"}
\caption{Character Animation Methods} \label{fig:CharacterAnimationMethods}
\end{figure}
\section{工业应用与实践}
略。
\subsection{基础代码}
可参考\href{https://theorangeduck.com/page/all}{theorangeduck}等。
比如线性蒙皮LBS:
\begin{lstlisting}[language=C++]
// https://github.com/orangeduck/Motion-Matching/blob/main/character.h
void linear_blend_skinning_positions(
slice1d<vec3> anim_positions,
const slice1d<vec3> rest_positions,
const slice2d<float> bone_weights,
const slice2d<unsigned short> bone_indices,
const slice1d<vec3> bone_rest_positions,
const slice1d<quat> bone_rest_rotations,
const slice1d<vec3> bone_anim_positions,
const slice1d<quat> bone_anim_rotations)
{
anim_positions.zero();
for (int i = 0; i < anim_positions.size; i++)
{
for (int j = 0; j < bone_indices.cols; j++)
{
if (bone_weights(i, j) > 0.0f)
{
int b = bone_indices(i, j);
vec3 position = rest_positions(i);
position = quat_mul_vec3(quat_inv(bone_rest_rotations(b)), position - bone_rest_positions(b));
position = quat_mul_vec3(bone_anim_rotations(b), position) + bone_anim_positions(b);
anim_positions(i) = anim_positions(i) + bone_weights(i, j) * position;
}
}
}
}
void linear_blend_skinning_normals(
slice1d<vec3> anim_normals,
const slice1d<vec3> rest_normals,
const slice2d<float> bone_weights,
const slice2d<unsigned short> bone_indices,
const slice1d<quat> bone_rest_rotations,
const slice1d<quat> bone_anim_rotations)
{
anim_normals.zero();
for (int i = 0; i < anim_normals.size; i++)
{
for (int j = 0; j < bone_indices.cols; j++)
{
if (bone_weights(i, j) > 0.0f)
{
int b = bone_indices(i, j);
vec3 normal = rest_normals(i);
normal = quat_mul_vec3(quat_inv(bone_rest_rotations(b)), normal);
normal = quat_mul_vec3(bone_anim_rotations(b), normal);
anim_normals(i) = anim_normals(i) + bone_weights(i, j) * normal;
}
}
}
for (int i = 0; i < anim_normals.size; i++)
{
anim_normals(i) = normalize(anim_normals(i));
}
}
\end{lstlisting}
比如运动学中的前后向驱动:
\begin{lstlisting}[language=C++] % 前向运动FK
// https://github.com/orangeduck/Animation-Looping/blob/main/database.h
// Here I am using a simple recursive version of forward kinematics
void forward_kinematics(
vec3& bone_position,
quat& bone_rotation,
const slice1d<vec3> bone_positions,
const slice1d<quat> bone_rotations,
const slice1d<int> bone_parents,
const int bone)
{
if (bone_parents(bone) != -1)
{
vec3 parent_position;
quat parent_rotation;
forward_kinematics(
parent_position,
parent_rotation,
bone_positions,
bone_rotations,
bone_parents,
bone_parents(bone));
bone_position = quat_mul_vec3(parent_rotation, bone_positions(bone)) + parent_position;
bone_rotation = quat_mul(parent_rotation, bone_rotations(bone));
}
else
{
bone_position = bone_positions(bone);
bone_rotation = bone_rotations(bone);
}
}
\end{lstlisting}
\begin{lstlisting}[language=C++] % 反向运动IK
// https://github.com/orangeduck/Animation-Looping/blob/main/database.h
// Compute backward kinematics for all joints
void backward_kinematics_full(
slice1d<vec3> local_bone_positions,
slice1d<quat> local_bone_rotations,
const slice1d<vec3> global_bone_positions,
const slice1d<quat> global_bone_rotations,
const slice1d<int> bone_parents)
{
for (int i = 0; i < bone_parents.size; i++)
{
if (bone_parents(i) == -1)
{
local_bone_positions(i) = global_bone_positions(i);
local_bone_rotations(i) = global_bone_rotations(i);
}
else
{
vec3 parent_position = global_bone_positions(bone_parents(i));
quat parent_rotation = global_bone_rotations(bone_parents(i));
local_bone_positions(i) = quat_inv_mul_vec3(parent_rotation,
global_bone_positions(i) - parent_position);
local_bone_rotations(i) = quat_inv_mul(parent_rotation, global_bone_rotations(i));
}
}
}
\end{lstlisting}
\section{学术进展}
\begin{enumerate}
\item \href{https://www.zhihu.com/column/c_1061982920763293696}{角色动画论文选读}
\item \href{https://github.com/rosswhitfield/ase}{ASE}
\item 作者团队新论文\href{https://github.com/heyuanYao-pku/Control-VAE}{Control-VAE}\
\item DL代码更新汇集\href{https://github.com/sebastianstarke/AI4Animation}{AI4Animation}
\item \href{https://github.com/orangeduck/Motion-Matching}{Motion-Matching}
\item \href{https://theorangeduck.com/page/code-vs-data-driven-displacement}{code-vs-data-driven-displacement}
\end{enumerate}
\chapter{数学基础}
由向量点乘定义:$\mathbf{ab}=\lVert \mathbf{a} \rVert \lVert \mathbf{b} \rVert \cos(\theta)$,得到
向量$\mathbf{a}$在向量$\mathbf{b}$上的投影:$\mathbf{a}_\mathbf{b} = \lVert \mathbf{a}
\rVert \cos(\theta) = \frac{\mathbf{ab}}{\lVert \mathbf{b} \rVert}$
叉乘
\begin{equation}
\begin{aligned}
\boldsymbol{c}=\boldsymbol{a} \times \boldsymbol{b} & =\left[\begin{array}{c}
a_y b_z-a_z b_y \\
a_z b_x-a_x b_z \\
a_x b_y-a_y b_x
\end{array}\right] \\
& =\left[\begin{array}{ccc}
0 & -a_z & a_y \\
a_z & 0 & -a_x \\
-a_y & a_x & 0
\end{array}\right]\left[\begin{array}{l}
b_x \\
b_y \\
b_z
\end{array}\right]=[\boldsymbol{a}]_{\times} \boldsymbol{b}\\
&=\|\boldsymbol{a}\|\boldsymbol{b}\| \sin (\theta) \boldsymbol{n}\\
&=det\left[\begin{array}{ccc}
\boldsymbol{i} & \boldsymbol{j} & \boldsymbol{k} \\
a_x & a_y & a_z \\
b_x & b_y & b_z
\end{array}\right]
\end{aligned}
\end{equation}
其中\(\boldsymbol{n}\)是由 $\boldsymbol{a}$ 到$\boldsymbol{b}$在右手坐标系
下的第三个方向的方向向量,$[\boldsymbol{a}]_{\times}$表示$\boldsymbol{a}$的
叉乘矩阵,注意是一个叉乘对应一个矩阵写法,比如$(\boldsymbol{a} \times \boldsymbol{b}) \times \boldsymbol{c}=[\boldsymbol{a} \times \boldsymbol{b}]_{\times} \boldsymbol{c}$
另外,两向量的叉乘其叉乘矩阵是一个反对称阵。
点积和叉积的一个典型应用是确定两向量的最小旋转角。将一个向量旋转到另一个向量,
沿着两向量叉乘后的方向向量旋转点积的运算得到的余弦角度。
向量的旋转:向量$\boldsymbol{a}$沿着向量$\boldsymbol{u}$旋转
$\theta$得到$\boldsymbol{b}$,如何计算$\boldsymbol{b}$。
具体推到见图:$\boldsymbol{b} = \boldsymbol{a} + \boldsymbol{v} + \boldsymbol{t}$,
$\boldsymbol{v} = (\sin(\theta))\boldsymbol{u} \times \boldsymbol{a}$,
$\boldsymbol{t} = (1 - \cos(\theta))\boldsymbol{u} \times (\boldsymbol{u} \times \boldsymbol{a})$
% \begin{minipage}
% \end{minipage}
Rodrigues'旋转公式:
\begin{equation}
\boldsymbol{b}= \boldsymbol{a}+(\sin \theta) \boldsymbol{u} \times \boldsymbol{a}+(1-\cos \theta) \boldsymbol{u} \times(\boldsymbol{u} \times \boldsymbol{a})
\end{equation}
由正交基的写法$\boldsymbol{a} = a_x \boldsymbol{e_x} +
a_y \boldsymbol{e_y} +
a_z \boldsymbol{e_z}$
可以得到点乘和叉乘的公式。
单位阵,对角阵,对称阵,反(skew)对称阵 $A^T = -A,[\boldsymbol{a}]_{\times}+[\boldsymbol{a}]^{T}_{\times}=\boldsymbol{0} $,
正交阵,$A^{T}A=I, det(A)=\pm 1$,特征值,特征向量 $A\boldsymbol{x}=\lambda\boldsymbol{x}$。
这里默认向量是列向量。因此$\boldsymbol{a}\boldsymbol{b}=\boldsymbol{a^T b}=
\boldsymbol{b^{T}a}$,同时
有了叉乘矩阵的写法,可以将Rodrigues'公式用矩阵形式重写。
行列式的基本性质:
\begin{itemize}
\setlength{\itemindent}{2em}
\item $det I = 1$
\item $det AB = det A * det B $
\item $det A^T = det A$
\item 可逆:$det A^{-1} = (det A)^{-1}$
\item 正交:$det U = \pm 1$
\end{itemize}
\section{刚体变换}
平移,旋转,缩放,仿射变换。
旋转矩阵行列式为1,所以不会改变坐标轴的顺序,同时$R\boldsymbol{u}=\boldsymbol{u}=R^{T}\boldsymbol{u}$
得到$(R-R^T)\boldsymbol{u}=0$,因为$R-R^T$是一个反对称阵,可将其写为
叉乘形式,$\boldsymbol{u^{'}}\times \boldsymbol{u}=0$,其中$\boldsymbol{u^{'}}$
一般非0。当旋转矩阵是对称阵是,比如绕$\boldsymbol{u^{'}}$轴旋转$k\pi$度。
由Rodrigues'公式
\begin{equation}
R=I+(\sin \theta)[\boldsymbol{u}]_{\times}+(1-\cos \theta)[\boldsymbol{u}]_{\times}^2
\end{equation}
得到
\begin{equation}
\boldsymbol{u} \leftarrow \boldsymbol{u}^{\prime}=\left[\begin{array}{l}
r_{32}-r_{23} \\
r_{13}-r_{31} \\
r_{21}-r_{12}
\end{array}\right] \leftarrow R-R^{\mathrm{T}}
\end{equation}
进一步得到旋转轴的长度
$\|\boldsymbol{u^{'}}=2\sin(\theta)$。
\subsection{坐标系变换}
物体坐标系的坐标不因旋转的改变而改变,而旋转本身会改变其在世界坐标系
下的坐标表示,对于同一点$\boldsymbol{a}$,其旋转前后有如下关系:
$\left(x^{\prime}, y^{\prime}, z^{\prime}\right)^T:
\boldsymbol{a}$ 在物体坐标系中 $(x, y, z)^T:
\boldsymbol{a}$ 在全局坐标系中
$$
\begin{aligned}
\boldsymbol{a} & =\left[\begin{array}{ccc}
1 & \mid & \mid \\
\boldsymbol{e}_x & \boldsymbol{e}_y & \boldsymbol{e}_z \\
\mid & \mid & \mid
\end{array}\right]\left[\begin{array}{l}
x \\
y \\
z
\end{array}\right] \\
& =\left[\begin{array}{ccc}
\mid & \mid & \mid \\
\boldsymbol{e}_x^{\prime} & \boldsymbol{e}_y^{\prime} & \boldsymbol{e}_z^{\prime} \\
\mid & \mid & \mid
\end{array}\right]\left[\begin{array}{l}
x^{\prime} \\
y^{\prime} \\
z^{\prime}
\end{array}\right]
\end{aligned}
$$
因此旋转矩阵表示为将局部坐标映射为全局坐标的变换矩阵。
\begin{equation}
\begin{gathered}
R=\left[\begin{array}{ccc}
\mid & \mid & \mid \\
\boldsymbol{e}_x & \boldsymbol{e}_y & \boldsymbol{e}_z \\
\mid & \mid & \mid
\end{array}\right]^{-1}\left[\begin{array}{ccc}
\mid & \mid & \mid \\
\boldsymbol{e}_x^{\prime} & \boldsymbol{e}_y^{\prime} & \boldsymbol{e}_z^{\prime} \\
\mid & \mid & \mid
\end{array}\right] \\
{\left[\begin{array}{l}
x \\
y \\
z
\end{array}\right]=R\left[\begin{array}{l}
x^{\prime} \\
y^{\prime} \\
z^{\prime}
\end{array}\right]}
\end{gathered}
\end{equation}
当存在平移时,从物体坐标到全局坐标有:
\begin{equation}
\left[\begin{array}{l}
x \\
y \\
z
\end{array}\right]
=R\left[\begin{array}{l}
x^{\prime} \\
y^{\prime} \\
z^{\prime}
\end{array}\right] + \boldsymbol{t}
\end{equation}
从全局坐标到物体坐标:
\begin{equation}
\left[\begin{array}{l}
x^{\prime} \\
y^{\prime} \\
z^{\prime}
\end{array}\right]
=R^{T}\left(\left[\begin{array}{l}
x \\
y \\
z
\end{array}\right] - \boldsymbol{t} \right)
\end{equation}
正交阵的自由度是3。
绕坐标轴的旋转矩阵:
\begin{equation}
\begin{aligned}
& R_x(\alpha)=\left(\begin{array}{ccc}
1 & 0 & 0 \\
0 & \cos \alpha & -\sin \alpha \\
0 & \sin \alpha & \cos \alpha
\end{array}\right) \\
& R_y(\beta)=\left(\begin{array}{ccc}
\cos \beta & 0 & \sin \beta \\
0 & 1 & 0 \\
-\sin \beta & 0 & \cos \beta
\end{array}\right) \\
& R_z(\gamma)=\left(\begin{array}{ccc}
\cos \gamma & -\sin \gamma & 0 \\
\sin \gamma & \cos \gamma & 0 \\
0 & 0 & 1
\end{array}\right)
\end{aligned}
\end{equation}
任何旋转都可以由沿基本轴的旋转合成。需要注意的是,分别沿$x,y,z$轴
旋转$\alpha, \beta, \gamma$时,基本轴选择物体坐标系时,其旋转矩阵(Intrinsic)为
$R_x(\alpha)R_y(\beta)R_z(\gamma)$,基本轴旋转世界坐标系时,旋转矩阵(Extrinsic)为
$R_z(\gamma)R_y(\beta)R_x(\alpha)$。常见工具maya是物体坐标系,Unity是可选择,
但基本顺序是YXZ。
平移插值:$x_t = (1-t)x_0 + x_t$。
旋转插值:矩阵形式几乎无法操作;欧拉角表示做多次矩阵乘法,同时
需要处理奇点($\alpha+n\pi$)问题,比如人在原地转圈,万向锁问题等;
轴角表示($u, \theta$),做旋转组合比较麻烦,常见插值$\theta_t=(1-t)\theta_0 + \theta_1$ 。
其旋转速度不恒定,恒定方式为:\begin{equation}
\begin{aligned}
R(\delta \boldsymbol{\theta}) & =R^T\left(\boldsymbol{\theta}_0\right) R\left(\boldsymbol{\theta}_1\right) \\
\delta \boldsymbol{\theta}_t & =(1-t) \mathbf{0}+t \delta \boldsymbol{\theta} \\
R\left(\boldsymbol{\theta}_t\right) & =R\left(\boldsymbol{\theta}_0\right) R\left(\delta \boldsymbol{\theta}_t\right)
\end{aligned}
\end{equation}
四元数表示(2D旋转可由复数表示,3D旋转最终由William Rowan Hamilton发明了四元数解决),
\begin{equation}
\label{WilliamRowanHamilton}
\begin{aligned}
\boldsymbol{q}_{\boldsymbol{t}} & =\frac{\sin [(1-t) \theta]}{\sin \theta} \boldsymbol{q}_0+\frac{\sin t \theta}{\sin \theta} \boldsymbol{q}_1 \\
& cos \theta = \boldsymbol{q}_0 \boldsymbol{q}_1
\end{aligned}
\end{equation}
\subsection{四元数}
基本定义,基本性质(加减乘除点积,共轭,范数,矩阵表示等)参考\href{https://en.wikipedia.org/wiki/Quaternion}{Quaternion}。
四元数(quaternion)的矩阵记法:
$\boldsymbol{q} = w + x\boldsymbol{i} + y\boldsymbol{j} + z\boldsymbol{k}=
\left[\begin{array}{l}
w \\
x \\
y \\
z
\end{array}\right]
=
\left[\begin{array}{l}
w \\
\boldsymbol{v}
\end{array}\right]
$
扩展定义:
$q=[w, \boldsymbol{v}]^{\mathrm{T}} \in \mathbb{H}, w \in \mathbb{R},
\boldsymbol{v} \in \mathbb{R}^3$。
$w=[w, \mathbf{0}]^{\mathrm{T}}$ : 常量四元数。
$\boldsymbol{v}=[0, \boldsymbol{v}]^{\mathrm{T}}:$ 纯四元数。
记法
$
\left[\begin{array}{l}
w \\
\boldsymbol{v}
\end{array}\right]
$
容易写出其基本运算规律,比如$\boldsymbol{q}^{*}=[w, -\boldsymbol{v}]^T$,
乘法\begin{equation}
\boldsymbol{q}_1 \boldsymbol{q}_{\mathbf{2}}=\left[\begin{array}{l}
w_1 \\
\boldsymbol{v}_{\mathbf{1}}
\end{array}\right]\left[\begin{array}{c}
w_2 \\
\boldsymbol{v}_{\mathbf{2}}
\end{array}\right]=\left[\begin{array}{c}
w_1 w_2-\boldsymbol{v}_1 \cdot \boldsymbol{v}_2 \\
w_1 \boldsymbol{v}_2+w_2 \boldsymbol{v}_1+\boldsymbol{v}_1 \times \boldsymbol{v}_2
\end{array}\right]
\end{equation}
有基本性质:
\begin{itemize}
\item 共轭$(\boldsymbol{q}_1 \boldsymbol{q}_2)^*
= \boldsymbol{q}^{*}_2 \boldsymbol{q}^{*}_1$
\item 范数 $\|q\|^2 = q^{*}q= qq^{*}$
\item 逆 $qq^{-1}=1 \rightarrow q^{-1}=\frac{q^*}{\|q\|^2}$
\item 单位四元数 $\|q\|=1, q^{-1}=q^*=
\left[\begin{array}{l}
w \\
\boldsymbol{v}
\end{array}\right] \Leftrightarrow R^{-1} = R^T$
\end{itemize}
对比复平面上单位圆的表示,单位四元数可表示为4D空间中的超球面上的
点,$\left[\begin{array}{l}
\cos\frac{\theta}{2}, \\
\boldsymbol{u}\sin\frac{\theta}{2}
\end{array}\right], \|\boldsymbol{u}\|=1$。
表示的含义和轴角表示相同$(\boldsymbol{u}, \theta)$。
具体而言,任何3D旋转$(\boldsymbol{v}, \theta)$的单位四元数表示为
$
\left[\begin{array}{l}
w \\
\boldsymbol{v}
\end{array}\right] = \left[ \cos\frac{\theta}{2},
\boldsymbol{u}\sin\frac{\theta}{2} \right]
$
其轴角表示的
角度$\theta = 2 arg \cos w$,轴为$\boldsymbol{u}=
\frac{\boldsymbol{v}}{\|\boldsymbol{v}\|}$。
这里左右表达在行列上有差异,右边应保持列表达。轴角表示和
单位四元数是一个满射关系。
\begin{problemset}
\item 向量$\boldsymbol{p}$绕轴角$(\boldsymbol{u}, \theta)$旋转,
得到的向量$\boldsymbol{p}^{\prime}$,用四元数如何运算?
\item 旋转的组合,用四元数实现,在运算上有什么特点?
\item 旋转如何插值?
\end{problemset}
\begin{solution}
\begin{enumerate}
\item[1.] 把$p$点写为一个纯四元数形式,
$
\left[\begin{array}{l}
0 \\
\boldsymbol{p}^{\prime}
\end{array}\right] =
q \left[\begin{array}{l}
0 \\
\boldsymbol{p}
\end{array}\right] q^{*}
$
其中$\boldsymbol{q}=\left[\begin{array}{l}
\cos\frac{\theta}{2}, \\
\boldsymbol{u}\sin\frac{\theta}{2}
\end{array}\right]$。注意这里$\boldsymbol{q},\boldsymbol{-q}$
表示同样的旋转。
\item[2.] 单位四元数$q_1, q_2$, 组合旋转$q=q_2 q_1$, 3D向量$p$,
则先后旋转$q_1, q_2$,是保持四元数乘积的,结果形式仍为问题1所示。
\item[3.] 在单位4维球弧上,简单的线性插值$q_t = (1-t)q_0 + tq_1$会使得
$q_t$不是一个单位四元数,不在弧上,需要做单位化处理,但此种处理在
旋转速度上是不恒定的,虽然实际效果差别不大。满足恒定速度的插值
SLERP:Spherical Linear Interpolation, $q_t = a(t)q_0 + b(t)q_1$,
其中$a(t)=\frac{\sin[(1-t)\theta]}{\sin(\theta)}, b(t)=\frac{\sin(t\theta)}{\sin\theta},
\cos\theta=q_0 q_1 $。
\end{enumerate}
\end{solution}
\chapter{角色运动学}
假设角色身体是刚性的,运动是骨骼绕关节的旋转。将角色定义为关节的
铰链(joint)结构(skeleton)。关节之间的称为骨骼(body、bone、link)。
姿势:旋转关节。每个关节定义一个局部坐标系,同时有一个全局朝向。
\section{前向和逆向运动学}
对于任意单链结构(每个子节点都有且只有一个父节点,各骨骼长度一样),假设初始朝向
全为单位阵$I$,则叶子节点(关节)旋转$R_n$,其朝向变为$Q_n = R_n$,同时其父节点
在此基础上旋转$R_{n-1}$,则其朝向为$Q_{n}=R_{n-1}R_{n}$,父节点朝向为$Q_{n-1}=R_{n-1}$。
以此类推到根节点。容易得到$Q_i = Q_{i-1}R_i$,表示从局部旋转到全局朝向的
关系。因为朝向是正交矩阵,从而得到从全局朝向到局部旋转的关系为:$R_i = Q_{i-1}^T Q_i$。
要计算某关节的全局坐标系,只需要用其父关节的全局坐标系乘以它自身的局部旋转,同样
父关节的全局坐标系的转置(逆)乘以当前关节的全局坐标系,就能得到其局部旋转。
对于一般情况,各骨骼长度不一,朝向不一。设根节点初始位置,朝向,旋转,长度
分别为$\boldsymbol{p}_0,Q_0, R_0, l_0$,其中$Q_0 = R_0$, 可以得到子节点
的位置,$\boldsymbol{p}_1 = \boldsymbol{p}_0 + Q_0 \boldsymbol{l}_0$,朝向
$Q_1 = Q_0 R_1$,第三个节点的位置$\boldsymbol{p}_2 = \boldsymbol{p}_1 + Q_1 \boldsymbol{l}_1$。
以此类推,在叶子节点局部坐标系下的点$x_0$,其在全局坐标系下应如何表示,或者关于
某一个父节点的朝向下,应该如何表示,或者父节点的局部坐标系下如何表示。
$x = p_n + Q_n x_0$。
对于五个节点($0~4$)的单链来说有:
\begin{equation}
\begin{aligned}
\boldsymbol{x} & =\boldsymbol{p}_4+Q_4 \boldsymbol{x}_0 \\
& =\boldsymbol{p}_3+Q_3 \boldsymbol{l}_3+Q_3 R_4 \boldsymbol{x}_0 \\
& =\boldsymbol{p}_3+Q_3\left(\boldsymbol{l}_3+R_4 \boldsymbol{x}_0\right)
\end{aligned}
\end{equation}
上式得到$\boldsymbol{x}$在$Q_3$中的局部坐标为$\boldsymbol{x}^{Q_3}=
Q_{3}^{T}(\boldsymbol{x} - \boldsymbol{p}_3)=\boldsymbol{l_3}+R_4 \boldsymbol{x}_0$。
第0个关节旋转,朝向变了,但第一个关节关于第0个关节的局部关系是不变的,用第0个关节
的朝向乘以第一个关节相对于第0个关节的位置,再加上第0个关节的原点,就得到了第一个关节的
世界坐标(局部坐标系的原点在世界坐标系下的位置)。接着旋转第一个关节,那么第二个关节的坐标原点就变了,
同理可得到第二个关节的局部坐标系原点在世界坐标系中的坐标值。
当从根节点递归旋转到叶子节点,就能得到每个局部坐标系的朝向和坐标原点位置。
问题:如果知道某个坐标系下的点$x_0$(局部坐标),如何计算其在全局坐标系下的位置?
% \begin{algorithm}[h]
% \caption{bool DM improve($S_H$)}
% \label{alg::conjugateGradient}
% \begin{algorithmic}[1]
% \Require
% Real-time task set $S_H$
% \Ensure
% The schedulability of $S_H$
% \State $D=0$;
% \For {${\tau _i}:{S_H}$} %For循环结构
% \State $D = D+C_i/D_i$;
% \EndFor
% \If {$D > n$}
% \State return false;
% \Else
% \State return true;
% \EndIf
% \end{algorithmic}
% \end{algorithm}
\begin{algorithm}[h]
\caption{前向运动学根到叶子}
\label{alg::FKrootend}
\begin{algorithmic}[1]
\Require
所有关节点的旋转矩阵 $R_i$, 坐标$x_0$,
\Ensure
全局坐标 $x$
\For {$i$ in root to end effector } %For循环结构
\State $Q_i = Q_{i-1}R_i$;
\State $p_{i+1} = p_i + Q_{i}l_i$
\EndFor
\State $x = p_E + Q_E x_0$
\end{algorithmic}
\end{algorithm}
从一个目标点开始,以此将其转化为其父节点的局部坐标系,即可得到当前点在全局坐标系
下的表示。也可以计算其关于非根节点下的坐标表示。这就是前向运动学的迭代。
\begin{algorithm}[h]
\caption{前向运动学叶子到根}
\label{alg::FKendroot}
\begin{algorithmic}[1]
\Require
所有关节点的旋转矩阵 $R_i$, 坐标$x_0$,
\Ensure
全局坐标 $x$
\State $x=x_0$;
\For {$i$ in end effector to root } %For循环结构
\State $x = l_{i-1} + R_i x$;
\EndFor
\end{algorithmic}
\end{algorithm}
将上两算法的全局坐标系$x$改为局部坐标$Q_k$,则分别有:
\begin{itemize}
\item[1] 前向运动学第$k+1$个节点到叶子
\begin{itemize}
\item[1.1] $Q^{\prime}_i = Q^{\prime}_{i-1}R_i, (Q^{\prime}_{0}=I)$
\item[1.2] $p^{\prime}_{i+1} = p^{\prime}_i + Q^{\prime}_{i}l_i$
\end{itemize}
\item[2] 前向运动学叶子到第$k+1$个节点
\begin{itemize}
\item[2.1] $x = l_{i-1} + R_i x$
\end{itemize}
\end{itemize}
% \begin{algorithm}[htbp]
% \caption{前向运动学第$k+1$个节点到叶子}
% \label{alg::FKQkend}
% \begin{algorithmic}[1]
% \Require
% 所有关节点的旋转矩阵 $R_i$, 坐标$x_0$,
% \Ensure
% $x_0$相对局部坐标$Q_k$的坐标
% \For {$i$ in joint $k+1$ to end effector } %For循环结构
% \State $Q^{\prime}_i = Q^{\prime}_{i-1}R_i, //(Q^{\prime}_{0}=I)$;
% \State $p^{\prime}_{i+1} = p^{\prime}_i + Q^{\prime}_{i}l_i$
% \EndFor
% \State $x = p^{\prime}_E + Q^{\prime}_E x_0$
% \end{algorithmic}
% \end{algorithm}
% 中间插这么多?
% \begin{algorithm}[htbp]
% \caption{前向运动学叶子到第$k+1$个节点}
% \label{alg::FKendQk}
% \begin{algorithmic}[1]
% \Require
% 所有关节点的旋转矩阵 $R_i$, 坐标$x_0$,
% \Ensure
% 全局坐标 $x$
% \State $x=x_0$;
% \For {$i$ in end effector to joint $k+1$ } %For循环结构
% \State $x = l_{i-1} + R_i x$;
% \EndFor
% \end{algorithmic}
% \end{algorithm}
\subsection{角色的前向运动学}
树结构,根节点一般选择为腰的位置。
\begin{algorithm}[h]
\caption{角色前向运动学}
\label{alg::FKrootendtree}
\begin{algorithmic}[1]
\Require
所有关节点的旋转矩阵 $R_i$, 坐标$x_0$,
\Ensure
全局坐标 $x$
\For {$i$ in joint\_list } %For循环结构
\State $p_i = i^{\prime}$s 父节点
\State $Q_i = Q_{p_i}R_i$;
\State $x_i = x_{p_i} + Q_{p_i}l_i$
\EndFor
\end{algorithmic}
\end{algorithm}
节点的类型,自由度Degrees of Freedom(DoF),一个正方体,三个轴均可旋转,平移,则自由度
为6,记为$(\boldsymbol{p}, R)\in \mathbb{R}^3 \times SO(3)$,当其被固定在一平面时,自由度
只有帖平面旋转和平移(前后,左右),DoF=3,当给串在一根线上,只能绕线(轴)旋转和上下平移,
DoF=2,固定在一个轴上,不能移动,只能旋转的情况,DoF=1。
自由度为1的有膝盖(knee),手肘(elbow)称为hinge joint或revolute joint,臀部(hip),肩膀(shoulder)自由度为3
,称为ball-and-socket joint,自由度为2的称为universal joint。对人体而言,
自由度1,3的旋转角度,会被控制在一个范围内。
姿势参数由根节点位置,朝向和内部关节点的旋转来表示:$\boldsymbol{\theta}=(\boldsymbol{t}_0, R_0, R_1, \cdots)$。
\subsection{运动数据的文件表示}
参考\href{https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html}{bvh}
\subsection{逆向运动学IK}
通过指定末端点(关节)的位置和朝向,利用逆向运动学算法自动计算出每个关节的位置和旋转,这样会更直观。
相对前向运动学的应用会更加广泛,比如角色绑定,对于动画师来说,逆向控制操作更友好,效率更高。
前向:已知系统的参数$\theta$和属性$\boldsymbol{x}$, 有$\boldsymbol{x}=f(\theta)$,表示从系统的
参数计算某一个属性的过程。计算上比较容易,但$\theta$的自由度DoF往往比$\boldsymbol{x}$的大得多,
调试$\theta$达到一个较好的$\boldsymbol{x}$往往并不容易。
逆向:给定属性$\boldsymbol{x}$,找到符合$\boldsymbol{x}=f(\theta)$的一组参数$\theta$,有
$\theta=f^{-1}(\boldsymbol{x})$,表示从系统的属性计算参数的过程,这通常需要求解一个非线性问题,
存在多解的可能性,并且$\boldsymbol{x}$的设定,有一定的直觉性。
IK:给定末端点位置和朝向$\boldsymbol{x}=f(\theta),
Q=Q(\theta)$,需要让末端点到达指定点$\widetilde{x}$,且朝向到指定朝向$\widetilde{Q}$,来求解中间所有节点的位置和朝向。
IK的优化建模:$\min_{\boldsymbol{\theta}}F(\boldsymbol{\theta}),
F(\boldsymbol{\theta}) = \frac{1}{2}\|f(\boldsymbol{\theta})-\widetilde{\boldsymbol{x}}\|^2$,
这里$\theta$一般表示所有关节点的旋转。朝向忽略了,一般较容易实现。
\subsubsection{循环坐标下降法}
Cyclic Coordinate Descent循环坐标下降法,依次沿着某一坐标轴去更新参数,每一步更新的距离是使得更新之后
的点在这个坐标轴的方向上使得目标值最小的那个点。
实现简单,只有点积,叉积计算,计算量比较小,不会考虑目标函数的性质,
完全基于坐标来更新,在特定情况下,收敛速度很慢,且有时候得到的解不稳定,
存在抖动情况。
考虑目标函数的性质,最基本的算法是梯度下降法。
\subsubsection{雅可比和梯度下降法}
基本定义和性质参考:
\href{https://en.wikipedia.org/wiki/Gradient}{Gradient},
\href{https://en.wikipedia.org/wiki/Gradient_descent}{Gradient descent},
\href{https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant}{Jacobian matrix and determinant},
\href{https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse}{Moore Penrose inverse},
\href{https://math.ecnu.edu.cn/~jypan/Teaching/MatrixComp/mc.pdf}{矩阵计算讲义}。
沿着使得$F(\boldsymbol{\theta})$增加最快的方向(梯度方向)的负方向更新参数。
\begin{equation}
\begin{aligned}
& \qquad \nabla_\theta F(\boldsymbol{\theta})
\end{aligned}=\left[\begin{array}{c}
\frac{\partial F}{\partial \theta_0}(\boldsymbol{\theta}) \\
\frac{\partial F}{\partial \theta_1}(\boldsymbol{\theta}) \\
\vdots \\
\frac{\partial F}{\partial \theta_n}(\boldsymbol{\theta})
\end{array}\right]=\left(\frac{\partial F}{\partial \boldsymbol{\theta}}(\theta)\right)^T
\end{equation}
$$\boldsymbol{\theta}^{i+1} = \boldsymbol{\theta}^{i} - \alpha \nabla_\theta F(\boldsymbol{\theta^{l}})$$
\begin{equation}
\begin{aligned}
\nabla_\theta F\left(\boldsymbol{\theta}^i\right) & =\left(\frac{\partial f}{\partial \boldsymbol{\theta}}\left(\boldsymbol{\theta}^i\right)\right)^T\left(f\left(\boldsymbol{\theta}^i\right)-\widetilde{\boldsymbol{x}}\right) \\
& =J^T\color{red}{\Delta}
\end{aligned}
\end{equation}
其中$f: \mathbb{R}^n \mapsto \mathbb{R}^3$,
$J=\frac{\partial f}{\partial \boldsymbol{\theta}}=
\left(\begin{array}{llll}\frac{\partial f}{\partial \theta_0}
& \frac{\partial f}{\partial \theta_1} & \cdots &
\frac{\partial f}{\partial \theta_n}\end{array}\right)$,
矩阵的第$i$列为
$\frac{\partial f}{\partial \theta_i} =
\left[\begin{array}{c}
\frac{\partial f_x}{\partial \theta_i} \\
\frac{\partial f_y}{\partial \theta_i} \\
\frac{\partial f_z}{\partial \theta_i}
\end{array}\right]$。
这里的$n$,通常是$\theta$的个数和单个的基础维数组合而成,比如一个节点的旋转是3维,有24个节点,$\theta$的维度即72。
雅可比矩阵可采用深度学习框架,比如pytorch计算,利用其自动计算梯度的能力。
也可以利用Rodrigues'旋转公式(2.2)来计算。
假设所有节点均是 hinge joint,对于铰链关节点$\boldsymbol{x}, \text{沿着} \boldsymbol{r_i}$绕轴$\boldsymbol{a_i}$旋转
$\delta\theta_i$度,$\boldsymbol{r_i}$起点为节点$i$,由公式2.2得到
\begin{equation}
\begin{gathered}
\boldsymbol{x}^{\prime}-\boldsymbol{x}=\left(\sin \delta \theta_i\right) \boldsymbol{a}_i \times \boldsymbol{r}_i+\left(1-\cos \delta \theta_i\right) \boldsymbol{a}_i \times\left(\boldsymbol{a}_i \times \boldsymbol{r}_i\right) \\
\frac{\partial f}{\partial \theta_i}=\lim _{\delta \theta_i \rightarrow 0} \frac{\boldsymbol{x}^{\prime}-\boldsymbol{x}}{\delta \theta_i}=\boldsymbol{a}_i \times \boldsymbol{r}_i
\end{gathered}
\end{equation}
对于节点为ball joint的,有三个自由度,将旋转由欧拉角来表示$R_i = R_{ix}R_{iy}R_{iz}$,
因此每个ball joint可以表示为三个hinge joints的乘积。有
\begin{equation}
\begin{array}{ll}
\boldsymbol{a}_{i x}=Q_{i-1} \boldsymbol{e}_x \\
\boldsymbol{a}_{i y}=Q_{i-1} R_{i x} \boldsymbol{e}_y & \frac{\partial f}{\partial \theta_{i *}}=\boldsymbol{a}_{i *} \times \boldsymbol{r}_i \\
\boldsymbol{a}_{i z}=Q_{i-1} R_{i x} R_{i y} \boldsymbol{e}_z &
\end{array}
\end{equation}
若欧拉角形式表示为$R_i = R_{ix}R_{iy}R_{ix^{\prime}}$,则需要修改$a_{iz}$为
${a}_{i x^{\prime}}=Q_{i-1} R_{i x} R_{i y} \boldsymbol{e}_x $
雅可比矩阵的每一列都可以经过前向运动,逐次的由节点到目标节点的叉乘($\boldsymbol{a_{i*}}$世界坐标系)计算而得。
其和梯度下降,均属于一阶优化,收敛速度较慢,而且每一轮迭代都需重新计算雅可比矩阵。总体上其收敛速度快于CCD,但
计算量比CCD大一些。
\subsubsection{雅可比逆法}
Jacobian inverse method
考虑一个简单的二次规划问题:
\begin{gather*}
\min _{\boldsymbol{\theta}} F(\boldsymbol{\theta})=\frac{1}{2} \boldsymbol{\theta}^T A \boldsymbol{\theta}+\boldsymbol{b}^T \boldsymbol{\theta} \\
\text {Gradient:} \quad \nabla_\theta F(\boldsymbol{\theta})=A \boldsymbol{\theta}+\boldsymbol{b}\\
\text {Optimality condition:} \quad \nabla_\theta F\left(\boldsymbol{\theta}^*\right)=0 \tag{3.*}\\
\Downarrow \\
\boldsymbol{\theta}^*=-A^{-1} \boldsymbol{b}
\end{gather*}
其中$A$是对称正定的。
高斯-牛顿法:将$f(\theta)$在$\theta^0$做一阶近似,即
$f(\boldsymbol{\theta}) \approx f(\boldsymbol{\theta}^0) + J(\boldsymbol{\theta} - \boldsymbol{\theta}^0)$。
得到原目标函数的近似表达:
\begin{equation}
\begin{aligned}
F(\theta) & \approx \frac{1}{2}\left\|f\left(\boldsymbol{\theta}^0\right)+J\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)-\widetilde{\boldsymbol{x}}\right\|_2^2 \\
& =\frac{1}{2}\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)^T J^T J\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right) \\
& +\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)^T J^T\left(f\left(\boldsymbol{\theta}^0\right)-\widetilde{x}\right)+\boldsymbol{c}
\end{aligned}
\end{equation}
是一个二次型,如上二次规划,可得到其一阶近似后的最优条件(first-order optimality condition),
$$
(\nabla F(\theta))^T=J^T J\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)+J^T\left(f\left(\boldsymbol{\theta}^0\right)-\widetilde{\boldsymbol{x}}\right)=\mathbf{0}
$$
即
\begin{equation}
J^T J\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)=-J^T\Delta
\end{equation}
如果$J^T J$可逆,得$\boldsymbol{\theta} = \boldsymbol{\theta^0}
- (J^T J)^{-1} J^T \Delta$。因为$J:\mathbb{R^n}\rightarrow 3$,一般$n>3$,两矩阵的乘积
的秩不大于乘积因子的秩,矩阵的秩不大于行列数的最小值($rank(AB)\leq\min(rank A, rank B), rank(A_{m\times n}) \leq \min(m, n)$),
得$J^T J$不可逆,但$J J^T$可能可逆。假设其可逆,
等式两边同时乘以$J$,得到$J(\boldsymbol{\theta} - \boldsymbol{\theta^0}) =
\boldsymbol{\widetilde{x}} - f(\boldsymbol{\theta^0})$。采用广义逆(非方阵情况下)
定义(Moore-Penrose) Pseudoinverse,可以得到$\boldsymbol{\theta}$的更新方式:
\begin{equation}
\begin{aligned}
\boldsymbol{\theta} &= \boldsymbol{\theta^0} - J^{+}\Delta \\
&= \boldsymbol{\theta^0} - J^T (J J^T)^{-1}\Delta
\end{aligned}
\end{equation}
在实际更新过程中,需要增加一个学习率参数,否则不符合非线性函
数的线性逼近是局部的这个性质,收敛情况会更差或不好控制。同时上
面只考虑了单点移动,旋转情况,若考虑多点情况,比如由四个关节点组成的系统,节点
$\boldsymbol{x_2}, \boldsymbol{x_3}, \boldsymbol{x_4}$
均需要旋转,则目标函数为
\begin{equation}
\left[\begin{array}{l}
x_2 \\
x_3 \\
x_4
\end{array}\right]=f(\boldsymbol{\theta}) \in \mathbb{R}^9
\end{equation}
此时的雅可比矩阵大小为$9\times 4$,$J^T J$就可能可逆。此时对上面更新式子可做如下$J^+ = (J^T J)^{-1}J^T$。
也就是说,雅可比的广义逆,根据其形状来定,“高瘦”(多点,约束较多)为后者,“矮胖”(单点,欠约束ik)为前者。
雅可比逆方法一般比雅可比转置方法,梯度下降法,效率高,但并不是$J^T J, J J^T$
一定可逆,此时可考虑扰动。
对于扰动(参考链接文档"矩阵计算讲义")情况,对称阵$+$
$\lambda I$,选择合适的参数,一定能可逆。有$J^{*}=J^T (J J^T + \lambda I)^{-1}$,或
$J^{*}= (J^T J + \lambda I)^{-1}J^T$。在角色动画中,$\lambda$一般称为阻尼系数。
阻尼雅可比逆法:
\begin{equation}
\begin{aligned}
& F(\theta)=\frac{1}{2}\|f(\boldsymbol{\theta})-\widetilde{\boldsymbol{x}}\|_2^2+\frac{\lambda}{2}\left(\boldsymbol{\theta}-\boldsymbol{\theta}^{\boldsymbol{i}}\right)^{\boldsymbol{T}} W\left(\boldsymbol{\theta}-\boldsymbol{\theta}^{\boldsymbol{i}}\right) \\
& \boldsymbol{\theta}^{i+1}=\boldsymbol{\theta}^i-\alpha\left(J^T J+\lambda W\right)^{-1} J^T \Delta \\
& \lambda \text { : damping parameter } \\
& W=\left[\begin{array}{llll}
w_0 & & & \\
& w_1 & & \\
& & \ddots & \\
& & & w_n
\end{array}\right]: \text { weight matrix } \\
&
\end{aligned}
\end{equation}
这里$W$是权重矩阵,$\lambda$是damping parameter。
将上面对角矩阵$W$"退化"到单位阵,在角色动画中,即每个关节点一视同仁,具有相同的权重,同时
$F(\theta)$正则项变为平方项。
带有阻尼项的方法,被称为 Levenberg-Marquardt algorithm,它避免了
高斯-牛顿法的不稳定情况。从另一个角度看,阻尼项等价于给优化函数加了一个正则项。
它带来了一些直观意义,比如单位阵$I$表示,每个关节用尽可能少的旋转移动来达到目标位置,
各个节点一视同仁,对于$W$则限制了不同关节的移动幅度,特征值越大(对角数),对应的关节移动的幅度越小。
以上是针对一个机械臂(单链结构)的理论分析,而对于角色来说,链式结构是一个树型结构。
比如同时将手,脚,胳膊移动到各自的目标位置,此时的优化目标可设定为
\begin{equation}
\label{IK3.11}
\begin{gathered}
F(\theta)=\frac{1}{2} \sum_i\left\|f_i(\boldsymbol{\theta})-\tilde{x}_i\right\|_2^2+\frac{\lambda}{2}\|\boldsymbol{\theta}\|_2^2 \\
\boldsymbol{\theta}=\left(\boldsymbol{t}_0, R_0, R_1, R_2, \ldots \ldots\right)
\end{gathered}
\end{equation}
这里$i=3$,也就是把各个单链加起来。
实际使用中,通常不会考虑所有约束,比如旋转胳膊,让手到前方靠下的位置,
若根节点选择髋关节,不加约束,会使得上半身超前倾;
若根节点选择在脚上,会使得整个身体都会向前倾斜;
若希望移动手的时候,身体不移动,可将根节点设置到肩关节处。
启发式方法:\href{http://andreasaristidou.com/FABRIK.html}{FABRIK}。
\begin{problemset}
\item 实现BVH的解析
\item 用pytorch等实现前向运动
\item 实现逆向运动IK
\item 工业实现库的复用
\end{problemset}
\chapter{角色动画}
T-Pose 每个关节旋转为0,或者单位阵,也称为Bind pose或Reference pose。
A-Pose,动画师比较倾向,肩膀位置比较自然,在绑定任务上,更加自然,artifact
会比较弱。A-Pose形状不唯一。
\section{重定向}
相同的动作作用在不同的参考姿势上,易知只需要找到两姿势的映射关系。
动画上称为Retargeting。
首先考虑最简单的情形,一个骨骼,即骨骼$A$到骨骼$B$的重定向。
初始条件有$A, R^{0}_A = I, B, R^{0}_B = I$,两者的初始朝向不同,但在各自局部坐标系下
都是单位阵。初始朝向的转换关系为$R_{A \rightarrow B}$,若$A$做了$R_A$的旋转,
要使得$B$达到同样的效果,易得将$B$初始朝向转为$A$,然后做$A$的动作即可,固有
$R_B=R_A R_{A\rightarrow B}^T$(旋转矩阵正交)。
其次考虑一个关节点组成的链结构,$A:= p_i : i$,骨骼$p_i$是骨骼
$i$的父节点,两骨骼$A,B$。初始条件有节点的全局朝向映射关系:$Q^{A\rightarrow B}_{p_i},
Q^{A\rightarrow B}_{i}$。若$A$各骨骼分别做了旋转$R_i ^A, R^A_{p_i}$,根据第三章
可知$Q_{p_i}^A = R_{p_i}^A, Q_i ^A = Q^A _{p_i}R^A _i$。$B$同理,
$Q_{p_i}^B = R_{p_i}^B, Q_i ^B = Q^B _{p_i}R^B _i$。
根据上面的讨论,对于每一根骨骼(link),有$Q^B = Q^A(Q^{A\rightarrow B})^T$,
即$Q^B _{p_i} = Q^A _{p_i}(Q^{A_{p_i}\rightarrow B})^T, Q^B _i= Q^A _i(Q^{A\rightarrow B} _i)^T$,
结合$R_i ^B = (Q_{p_i}^B)^T Q_i ^B$。$\Rightarrow R_i ^B =
Q_{p_i}^{A \rightarrow B}\left(Q_{p_i}^A\right)^T Q_i^A\left(Q_i^{A \rightarrow B}\right)^T=
Q_{p_i}^{A \rightarrow B} R_i^A\left(Q_i^{A \rightarrow B}\right)^T$。
同样计算$R_{p i}^B=R_{p i}^A\left(Q_{p i}^{A \rightarrow B}\right)^T$。
总的来说就是根据单链的推导的关系,得到骨骼朝向的映射关系,然后将被定向的链$B$的朝向
表示为参考链$A$的朝向表达,然后根据IK中父子链的局部于全局坐标关系,计算而得。
图示\ref{fig:Retargeting}。
\begin{figure}[htbp]
\centering
\includegraphics[totalheight=2in]{"./image/Retargeting.png"}
\caption{Retargeting} \label{fig:Retargeting}
\end{figure}
记忆:$B$父旋转=父旋转*父朝向映射的逆, $B$子旋转=父朝向映射*子旋转*子朝向映射的逆。
最后是参考姿势到参考姿势的重定向,递归考虑。
实际情况,动捕的时候是人,角色却很复杂,比如人体脊柱一般2-3个关节,而角色可能有
7-8个关节,另外还有衣服,斗篷,道具等。大概列举如下:
\begin{itemize}