├── .gitignore ├── CharacterAnimation.pdf ├── CharacterAnimation.tex ├── LICENSE ├── README.md ├── elegantbook.cls ├── figure ├── cover.jpg └── logo-blue.png ├── image ├── BellmanPrinciple.png ├── CMA-ES.png ├── CharacterAnimationMethods.png ├── CharacterPD.png ├── Contact.png ├── DynamicProgramming.png ├── EulerIntegration.png ├── Feedback-Control.png ├── Feedforward-Control.png ├── FullCharacterPD.png ├── GenerativeControlPolicies.png ├── GenerativeModels.png ├── IPM-Control.png ├── IPM.png ├── JacobianTransposeControl.png ├── JointType.png ├── LearningMotionModel.png ├── MDP.png ├── MomentofInertia.png ├── OptimalControl.png ├── PDController.png ├── Physics-basedCharacterAnimation.png ├── Retargeting.png ├── RigidBodySystem.png ├── RotationRepresentations.png ├── SAMCON.png ├── SamplingbasedPolicyOptimization.png ├── SimulatingCharacter.png ├── SimulatingControllingCharacter.png ├── SkiningDeformation.png ├── TwoLinksJoint.png ├── UsingMotionData.png ├── ZMP-GRF.png ├── ZMP-stance-phase.png ├── actuatedun.png ├── founder.png ├── games105.png ├── scatter.jpg ├── taujoint.png └── trajectoriesPD.png └── reference.bib /.gitignore: -------------------------------------------------------------------------------- 1 | ## Core latex/pdflatex auxiliary files: 2 | *.aux 3 | *.lof 4 | *.log 5 | *.lot 6 | *.fls 7 | *.out 8 | *.toc 9 | *.fmt 10 | *.fot 11 | *.cb 12 | *.cb2 13 | .*.lb 14 | 15 | ## Intermediate documents: 16 | *.dvi 17 | *.xdv 18 | *-converted-to.* 19 | # these rules might exclude image files for figures etc. 20 | # *.ps 21 | # *.eps 22 | # *.pdf 23 | 24 | ## Generated if empty string is given at "Please type another file name for output:" 25 | # .pdf 26 | 27 | ## Bibliography auxiliary files (bibtex/biblatex/biber): 28 | *.bbl 29 | *.bcf 30 | *.blg 31 | *-blx.aux 32 | *-blx.bib 33 | *.run.xml 34 | 35 | ## Build tool auxiliary files: 36 | *.fdb_latexmk 37 | *.synctex 38 | *.synctex(busy) 39 | *.synctex.gz 40 | *.synctex.gz(busy) 41 | *.pdfsync 42 | 43 | ## Build tool directories for auxiliary files 44 | # latexrun 45 | latex.out/ 46 | 47 | ## Auxiliary and intermediate files from other packages: 48 | # algorithms 49 | *.alg 50 | *.loa 51 | 52 | # achemso 53 | acs-*.bib 54 | 55 | # amsthm 56 | *.thm 57 | 58 | # beamer 59 | *.nav 60 | *.pre 61 | *.snm 62 | *.vrb 63 | 64 | # changes 65 | *.soc 66 | 67 | # comment 68 | *.cut 69 | 70 | # cprotect 71 | *.cpt 72 | 73 | # elsarticle (documentclass of Elsevier journals) 74 | *.spl 75 | 76 | # endnotes 77 | *.ent 78 | 79 | # fixme 80 | *.lox 81 | 82 | # feynmf/feynmp 83 | *.mf 84 | *.mp 85 | *.t[1-9] 86 | *.t[1-9][0-9] 87 | *.tfm 88 | 89 | #(r)(e)ledmac/(r)(e)ledpar 90 | *.end 91 | *.?end 92 | *.[1-9] 93 | *.[1-9][0-9] 94 | *.[1-9][0-9][0-9] 95 | *.[1-9]R 96 | *.[1-9][0-9]R 97 | *.[1-9][0-9][0-9]R 98 | *.eledsec[1-9] 99 | *.eledsec[1-9]R 100 | *.eledsec[1-9][0-9] 101 | *.eledsec[1-9][0-9]R 102 | *.eledsec[1-9][0-9][0-9] 103 | *.eledsec[1-9][0-9][0-9]R 104 | 105 | # glossaries 106 | *.acn 107 | *.acr 108 | *.glg 109 | *.glo 110 | *.gls 111 | *.glsdefs 112 | *.lzo 113 | *.lzs 114 | 115 | # uncomment this for glossaries-extra (will ignore makeindex's style files!) 116 | # *.ist 117 | 118 | # gnuplottex 119 | *-gnuplottex-* 120 | 121 | # gregoriotex 122 | *.gaux 123 | *.gtex 124 | 125 | # htlatex 126 | *.4ct 127 | *.4tc 128 | *.idv 129 | *.lg 130 | *.trc 131 | *.xref 132 | 133 | # hyperref 134 | *.brf 135 | 136 | # knitr 137 | *-concordance.tex 138 | # TODO Comment the next line if you want to keep your tikz graphics files 139 | *.tikz 140 | *-tikzDictionary 141 | 142 | # listings 143 | *.lol 144 | 145 | # luatexja-ruby 146 | *.ltjruby 147 | 148 | # makeidx 149 | *.idx 150 | *.ilg 151 | *.ind 152 | 153 | # minitoc 154 | *.maf 155 | *.mlf 156 | *.mlt 157 | *.mtc[0-9]* 158 | *.slf[0-9]* 159 | *.slt[0-9]* 160 | *.stc[0-9]* 161 | 162 | # minted 163 | _minted* 164 | *.pyg 165 | 166 | # morewrites 167 | *.mw 168 | 169 | # nomencl 170 | *.nlg 171 | *.nlo 172 | *.nls 173 | 174 | # pax 175 | *.pax 176 | 177 | # pdfpcnotes 178 | *.pdfpc 179 | 180 | # sagetex 181 | *.sagetex.sage 182 | *.sagetex.py 183 | *.sagetex.scmd 184 | 185 | # scrwfile 186 | *.wrt 187 | 188 | # sympy 189 | *.sout 190 | *.sympy 191 | sympy-plots-for-*.tex/ 192 | 193 | # pdfcomment 194 | *.upa 195 | *.upb 196 | 197 | # pythontex 198 | *.pytxcode 199 | pythontex-files-*/ 200 | 201 | # tcolorbox 202 | *.listing 203 | 204 | # thmtools 205 | *.loe 206 | 207 | # TikZ & PGF 208 | *.dpth 209 | *.md5 210 | *.auxlock 211 | 212 | # todonotes 213 | *.tdo 214 | 215 | # vhistory 216 | *.hst 217 | *.ver 218 | 219 | # easy-todo 220 | *.lod 221 | 222 | # xcolor 223 | *.xcp 224 | 225 | # xmpincl 226 | *.xmpi 227 | 228 | # xindy 229 | *.xdy 230 | 231 | # xypic precompiled matrices and outlines 232 | *.xyc 233 | *.xyd 234 | 235 | # endfloat 236 | *.ttt 237 | *.fff 238 | 239 | # Latexian 240 | TSWLatexianTemp* 241 | 242 | ## Editors: 243 | # WinEdt 244 | *.bak 245 | *.sav 246 | 247 | # Texpad 248 | .texpadtmp 249 | 250 | # LyX 251 | *.lyx~ 252 | 253 | # Kile 254 | *.backup 255 | 256 | # gummi 257 | .*.swp 258 | 259 | # KBibTeX 260 | *~[0-9]* 261 | 262 | # TeXnicCenter 263 | *.tps 264 | 265 | # auto folder when using emacs and auctex 266 | ./auto/* 267 | *.el 268 | 269 | # expex forward references with \gathertags 270 | *-tags.tex 271 | 272 | # standalone packages 273 | *.sta 274 | 275 | # Makeindex log files 276 | *.lpz 277 | -------------------------------------------------------------------------------- /CharacterAnimation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foocker/CharacterAnimation/58aab01adc2e9e08969799832f9d766cf2cd4c5c/CharacterAnimation.pdf -------------------------------------------------------------------------------- /CharacterAnimation.tex: -------------------------------------------------------------------------------- 1 | 2 | \documentclass[lang=cn,newtx,10pt,scheme=chinese]{elegantbook} 3 | \title{角色动画入门} 4 | \subtitle{Games105学习笔记} 5 | 6 | \author{Sisyphes} 7 | \institute{家里蹲} 8 | \date{\today} 9 | \version{0.9} 10 | % \bioinfo{自定义}{信息} 11 | 12 | % \extrainfo{使用的是优美的Elegant\LaTeX{} 模板} 13 | 14 | \setcounter{tocdepth}{3} 15 | 16 | \logo{logo-blue.png} 17 | \cover{cover.jpg} 18 | 19 | % 本文档命令 20 | \usepackage{array} 21 | \newcommand{\ccr}[1]{\makecell{{\color{#1}\rule{1cm}{1cm}}}} 22 | 23 | % 修改标题页的橙色带 24 | \definecolor{customcolor}{RGB}{32,178,170} 25 | \colorlet{coverlinecolor}{customcolor} 26 | \usepackage{cprotect} 27 | 28 | \addbibresource[location=local]{reference.bib} % 参考文献,不要删除 29 | 30 | \begin{document} 31 | 32 | \maketitle 33 | \frontmatter 34 | 35 | \tableofcontents 36 | 37 | \mainmatter 38 | 39 | \chapter{0} 40 | \section{废话} 41 | 写到最后,才是真的开始。目前有很多问题,后续看情况优化更新。 42 | 43 | \chapter{角色动画介绍} 44 | 45 | \begin{itemize} 46 | \item 模板官网:\href{https://elegantlatex.org/}{elegantlatex.org} 47 | \item 模板GitHub 地址:\href{https://github.com/ElegantLaTeX/}{ElegantLaTeX} 48 | \item 课程官网:\href{https://games-105.github.io/}{games-105} 49 | \item 课程Github 地址:\href{https://github.com/GAMES-105/GAMES-105}{GAMES-105-hw} 50 | \item 项目地址:\href{https://github.com/foocker}{Sisyphes} 51 | \item 作图工具:\href{https://www.geogebra.org/?lang=en}{geogebra} 52 | \end{itemize} 53 | 54 | \section{基本介绍} 55 | 这一节老师讲的比较精彩,建议看原视频,这里随便贴几张图。 56 | 57 | \begin{figure}[htbp] 58 | \centering 59 | \includegraphics[totalheight=1.5in]{"./image/Physics-basedCharacterAnimation.png"} 60 | \caption{Physics-based Character Animation} \label{fig:Physics-basedCharacterAnimation} 61 | \end{figure} 62 | 63 | \begin{figure}[htbp] 64 | \centering 65 | \includegraphics[totalheight=1.5in]{"./image/GenerativeControlPolicies.png"} 66 | \caption{Generative Control Policies} \label{fig:GenerativeControlPolicies} 67 | \end{figure} 68 | 69 | \begin{figure}[htbp] 70 | \centering 71 | \includegraphics[totalheight=2in]{"./image/CharacterAnimationMethods.png"} 72 | \caption{Character Animation Methods} \label{fig:CharacterAnimationMethods} 73 | \end{figure} 74 | 75 | 76 | \section{工业应用与实践} 77 | 略。 78 | \subsection{基础代码} 79 | 可参考\href{https://theorangeduck.com/page/all}{theorangeduck}等。 80 | 比如线性蒙皮LBS: 81 | \begin{lstlisting}[language=C++] 82 | // https://github.com/orangeduck/Motion-Matching/blob/main/character.h 83 | void linear_blend_skinning_positions( 84 | slice1d anim_positions, 85 | const slice1d rest_positions, 86 | const slice2d bone_weights, 87 | const slice2d bone_indices, 88 | const slice1d bone_rest_positions, 89 | const slice1d bone_rest_rotations, 90 | const slice1d bone_anim_positions, 91 | const slice1d bone_anim_rotations) 92 | { 93 | anim_positions.zero(); 94 | 95 | for (int i = 0; i < anim_positions.size; i++) 96 | { 97 | for (int j = 0; j < bone_indices.cols; j++) 98 | { 99 | if (bone_weights(i, j) > 0.0f) 100 | { 101 | int b = bone_indices(i, j); 102 | 103 | vec3 position = rest_positions(i); 104 | position = quat_mul_vec3(quat_inv(bone_rest_rotations(b)), position - bone_rest_positions(b)); 105 | position = quat_mul_vec3(bone_anim_rotations(b), position) + bone_anim_positions(b); 106 | 107 | anim_positions(i) = anim_positions(i) + bone_weights(i, j) * position; 108 | } 109 | } 110 | } 111 | } 112 | 113 | void linear_blend_skinning_normals( 114 | slice1d anim_normals, 115 | const slice1d rest_normals, 116 | const slice2d bone_weights, 117 | const slice2d bone_indices, 118 | const slice1d bone_rest_rotations, 119 | const slice1d bone_anim_rotations) 120 | { 121 | anim_normals.zero(); 122 | 123 | for (int i = 0; i < anim_normals.size; i++) 124 | { 125 | for (int j = 0; j < bone_indices.cols; j++) 126 | { 127 | if (bone_weights(i, j) > 0.0f) 128 | { 129 | int b = bone_indices(i, j); 130 | 131 | vec3 normal = rest_normals(i); 132 | normal = quat_mul_vec3(quat_inv(bone_rest_rotations(b)), normal); 133 | normal = quat_mul_vec3(bone_anim_rotations(b), normal); 134 | 135 | anim_normals(i) = anim_normals(i) + bone_weights(i, j) * normal; 136 | } 137 | } 138 | } 139 | 140 | for (int i = 0; i < anim_normals.size; i++) 141 | { 142 | anim_normals(i) = normalize(anim_normals(i)); 143 | } 144 | } 145 | 146 | \end{lstlisting} 147 | 148 | 比如运动学中的前后向驱动: 149 | \begin{lstlisting}[language=C++] % 前向运动FK 150 | // https://github.com/orangeduck/Animation-Looping/blob/main/database.h 151 | // Here I am using a simple recursive version of forward kinematics 152 | void forward_kinematics( 153 | vec3& bone_position, 154 | quat& bone_rotation, 155 | const slice1d bone_positions, 156 | const slice1d bone_rotations, 157 | const slice1d bone_parents, 158 | const int bone) 159 | { 160 | if (bone_parents(bone) != -1) 161 | { 162 | vec3 parent_position; 163 | quat parent_rotation; 164 | 165 | forward_kinematics( 166 | parent_position, 167 | parent_rotation, 168 | bone_positions, 169 | bone_rotations, 170 | bone_parents, 171 | bone_parents(bone)); 172 | 173 | bone_position = quat_mul_vec3(parent_rotation, bone_positions(bone)) + parent_position; 174 | bone_rotation = quat_mul(parent_rotation, bone_rotations(bone)); 175 | } 176 | else 177 | { 178 | bone_position = bone_positions(bone); 179 | bone_rotation = bone_rotations(bone); 180 | } 181 | } 182 | \end{lstlisting} 183 | 184 | \begin{lstlisting}[language=C++] % 反向运动IK 185 | // https://github.com/orangeduck/Animation-Looping/blob/main/database.h 186 | // Compute backward kinematics for all joints 187 | void backward_kinematics_full( 188 | slice1d local_bone_positions, 189 | slice1d local_bone_rotations, 190 | const slice1d global_bone_positions, 191 | const slice1d global_bone_rotations, 192 | const slice1d bone_parents) 193 | { 194 | for (int i = 0; i < bone_parents.size; i++) 195 | { 196 | if (bone_parents(i) == -1) 197 | { 198 | local_bone_positions(i) = global_bone_positions(i); 199 | local_bone_rotations(i) = global_bone_rotations(i); 200 | } 201 | else 202 | { 203 | vec3 parent_position = global_bone_positions(bone_parents(i)); 204 | quat parent_rotation = global_bone_rotations(bone_parents(i)); 205 | 206 | local_bone_positions(i) = quat_inv_mul_vec3(parent_rotation, 207 | global_bone_positions(i) - parent_position); 208 | local_bone_rotations(i) = quat_inv_mul(parent_rotation, global_bone_rotations(i)); 209 | } 210 | } 211 | } 212 | \end{lstlisting} 213 | 214 | \section{学术进展} 215 | \begin{enumerate} 216 | \item \href{https://www.zhihu.com/column/c_1061982920763293696}{角色动画论文选读} 217 | \item \href{https://github.com/rosswhitfield/ase}{ASE} 218 | \item 作者团队新论文\href{https://github.com/heyuanYao-pku/Control-VAE}{Control-VAE}\ 219 | \item DL代码更新汇集\href{https://github.com/sebastianstarke/AI4Animation}{AI4Animation} 220 | \item \href{https://github.com/orangeduck/Motion-Matching}{Motion-Matching} 221 | \item \href{https://theorangeduck.com/page/code-vs-data-driven-displacement}{code-vs-data-driven-displacement} 222 | \end{enumerate} 223 | 224 | \chapter{数学基础} 225 | 226 | 由向量点乘定义:$\mathbf{ab}=\lVert \mathbf{a} \rVert \lVert \mathbf{b} \rVert \cos(\theta)$,得到 227 | 向量$\mathbf{a}$在向量$\mathbf{b}$上的投影:$\mathbf{a}_\mathbf{b} = \lVert \mathbf{a} 228 | \rVert \cos(\theta) = \frac{\mathbf{ab}}{\lVert \mathbf{b} \rVert}$ 229 | 230 | 叉乘 231 | 232 | \begin{equation} 233 | \begin{aligned} 234 | \boldsymbol{c}=\boldsymbol{a} \times \boldsymbol{b} & =\left[\begin{array}{c} 235 | a_y b_z-a_z b_y \\ 236 | a_z b_x-a_x b_z \\ 237 | a_x b_y-a_y b_x 238 | \end{array}\right] \\ 239 | & =\left[\begin{array}{ccc} 240 | 0 & -a_z & a_y \\ 241 | a_z & 0 & -a_x \\ 242 | -a_y & a_x & 0 243 | \end{array}\right]\left[\begin{array}{l} 244 | b_x \\ 245 | b_y \\ 246 | b_z 247 | \end{array}\right]=[\boldsymbol{a}]_{\times} \boldsymbol{b}\\ 248 | &=\|\boldsymbol{a}\|\boldsymbol{b}\| \sin (\theta) \boldsymbol{n}\\ 249 | &=det\left[\begin{array}{ccc} 250 | \boldsymbol{i} & \boldsymbol{j} & \boldsymbol{k} \\ 251 | a_x & a_y & a_z \\ 252 | b_x & b_y & b_z 253 | \end{array}\right] 254 | \end{aligned} 255 | \end{equation} 256 | 257 | 其中\(\boldsymbol{n}\)是由 $\boldsymbol{a}$ 到$\boldsymbol{b}$在右手坐标系 258 | 下的第三个方向的方向向量,$[\boldsymbol{a}]_{\times}$表示$\boldsymbol{a}$的 259 | 叉乘矩阵,注意是一个叉乘对应一个矩阵写法,比如$(\boldsymbol{a} \times \boldsymbol{b}) \times \boldsymbol{c}=[\boldsymbol{a} \times \boldsymbol{b}]_{\times} \boldsymbol{c}$ 260 | 另外,两向量的叉乘其叉乘矩阵是一个反对称阵。 261 | 262 | 点积和叉积的一个典型应用是确定两向量的最小旋转角。将一个向量旋转到另一个向量, 263 | 沿着两向量叉乘后的方向向量旋转点积的运算得到的余弦角度。 264 | 265 | 向量的旋转:向量$\boldsymbol{a}$沿着向量$\boldsymbol{u}$旋转 266 | $\theta$得到$\boldsymbol{b}$,如何计算$\boldsymbol{b}$。 267 | 具体推到见图:$\boldsymbol{b} = \boldsymbol{a} + \boldsymbol{v} + \boldsymbol{t}$, 268 | $\boldsymbol{v} = (\sin(\theta))\boldsymbol{u} \times \boldsymbol{a}$, 269 | $\boldsymbol{t} = (1 - \cos(\theta))\boldsymbol{u} \times (\boldsymbol{u} \times \boldsymbol{a})$ 270 | 271 | % \begin{minipage} 272 | 273 | % \end{minipage} 274 | Rodrigues'旋转公式: 275 | \begin{equation} 276 | \boldsymbol{b}= \boldsymbol{a}+(\sin \theta) \boldsymbol{u} \times \boldsymbol{a}+(1-\cos \theta) \boldsymbol{u} \times(\boldsymbol{u} \times \boldsymbol{a}) 277 | \end{equation} 278 | 279 | 由正交基的写法$\boldsymbol{a} = a_x \boldsymbol{e_x} + 280 | a_y \boldsymbol{e_y} + 281 | a_z \boldsymbol{e_z}$ 282 | 可以得到点乘和叉乘的公式。 283 | 284 | 单位阵,对角阵,对称阵,反(skew)对称阵 $A^T = -A,[\boldsymbol{a}]_{\times}+[\boldsymbol{a}]^{T}_{\times}=\boldsymbol{0} $, 285 | 正交阵,$A^{T}A=I, det(A)=\pm 1$,特征值,特征向量 $A\boldsymbol{x}=\lambda\boldsymbol{x}$。 286 | 287 | 这里默认向量是列向量。因此$\boldsymbol{a}\boldsymbol{b}=\boldsymbol{a^T b}= 288 | \boldsymbol{b^{T}a}$,同时 289 | 290 | 有了叉乘矩阵的写法,可以将Rodrigues'公式用矩阵形式重写。 291 | 292 | 行列式的基本性质: 293 | \begin{itemize} 294 | \setlength{\itemindent}{2em} 295 | \item $det I = 1$ 296 | \item $det AB = det A * det B $ 297 | \item $det A^T = det A$ 298 | \item 可逆:$det A^{-1} = (det A)^{-1}$ 299 | \item 正交:$det U = \pm 1$ 300 | \end{itemize} 301 | 302 | \section{刚体变换} 303 | 平移,旋转,缩放,仿射变换。 304 | 305 | 旋转矩阵行列式为1,所以不会改变坐标轴的顺序,同时$R\boldsymbol{u}=\boldsymbol{u}=R^{T}\boldsymbol{u}$ 306 | 得到$(R-R^T)\boldsymbol{u}=0$,因为$R-R^T$是一个反对称阵,可将其写为 307 | 叉乘形式,$\boldsymbol{u^{'}}\times \boldsymbol{u}=0$,其中$\boldsymbol{u^{'}}$ 308 | 一般非0。当旋转矩阵是对称阵是,比如绕$\boldsymbol{u^{'}}$轴旋转$k\pi$度。 309 | 由Rodrigues'公式 310 | \begin{equation} 311 | R=I+(\sin \theta)[\boldsymbol{u}]_{\times}+(1-\cos \theta)[\boldsymbol{u}]_{\times}^2 312 | \end{equation} 313 | 得到 314 | \begin{equation} 315 | \boldsymbol{u} \leftarrow \boldsymbol{u}^{\prime}=\left[\begin{array}{l} 316 | r_{32}-r_{23} \\ 317 | r_{13}-r_{31} \\ 318 | r_{21}-r_{12} 319 | \end{array}\right] \leftarrow R-R^{\mathrm{T}} 320 | \end{equation} 321 | 进一步得到旋转轴的长度 322 | $\|\boldsymbol{u^{'}}=2\sin(\theta)$。 323 | 324 | \subsection{坐标系变换} 325 | 物体坐标系的坐标不因旋转的改变而改变,而旋转本身会改变其在世界坐标系 326 | 下的坐标表示,对于同一点$\boldsymbol{a}$,其旋转前后有如下关系: 327 | $\left(x^{\prime}, y^{\prime}, z^{\prime}\right)^T: 328 | \boldsymbol{a}$ 在物体坐标系中 $(x, y, z)^T: 329 | \boldsymbol{a}$ 在全局坐标系中 330 | $$ 331 | \begin{aligned} 332 | \boldsymbol{a} & =\left[\begin{array}{ccc} 333 | 1 & \mid & \mid \\ 334 | \boldsymbol{e}_x & \boldsymbol{e}_y & \boldsymbol{e}_z \\ 335 | \mid & \mid & \mid 336 | \end{array}\right]\left[\begin{array}{l} 337 | x \\ 338 | y \\ 339 | z 340 | \end{array}\right] \\ 341 | & =\left[\begin{array}{ccc} 342 | \mid & \mid & \mid \\ 343 | \boldsymbol{e}_x^{\prime} & \boldsymbol{e}_y^{\prime} & \boldsymbol{e}_z^{\prime} \\ 344 | \mid & \mid & \mid 345 | \end{array}\right]\left[\begin{array}{l} 346 | x^{\prime} \\ 347 | y^{\prime} \\ 348 | z^{\prime} 349 | \end{array}\right] 350 | \end{aligned} 351 | $$ 352 | 因此旋转矩阵表示为将局部坐标映射为全局坐标的变换矩阵。 353 | \begin{equation} 354 | \begin{gathered} 355 | R=\left[\begin{array}{ccc} 356 | \mid & \mid & \mid \\ 357 | \boldsymbol{e}_x & \boldsymbol{e}_y & \boldsymbol{e}_z \\ 358 | \mid & \mid & \mid 359 | \end{array}\right]^{-1}\left[\begin{array}{ccc} 360 | \mid & \mid & \mid \\ 361 | \boldsymbol{e}_x^{\prime} & \boldsymbol{e}_y^{\prime} & \boldsymbol{e}_z^{\prime} \\ 362 | \mid & \mid & \mid 363 | \end{array}\right] \\ 364 | {\left[\begin{array}{l} 365 | x \\ 366 | y \\ 367 | z 368 | \end{array}\right]=R\left[\begin{array}{l} 369 | x^{\prime} \\ 370 | y^{\prime} \\ 371 | z^{\prime} 372 | \end{array}\right]} 373 | \end{gathered} 374 | \end{equation} 375 | 376 | 当存在平移时,从物体坐标到全局坐标有: 377 | \begin{equation} 378 | \left[\begin{array}{l} 379 | x \\ 380 | y \\ 381 | z 382 | \end{array}\right] 383 | =R\left[\begin{array}{l} 384 | x^{\prime} \\ 385 | y^{\prime} \\ 386 | z^{\prime} 387 | \end{array}\right] + \boldsymbol{t} 388 | \end{equation} 389 | 从全局坐标到物体坐标: 390 | \begin{equation} 391 | \left[\begin{array}{l} 392 | x^{\prime} \\ 393 | y^{\prime} \\ 394 | z^{\prime} 395 | \end{array}\right] 396 | =R^{T}\left(\left[\begin{array}{l} 397 | x \\ 398 | y \\ 399 | z 400 | \end{array}\right] - \boldsymbol{t} \right) 401 | \end{equation} 402 | 正交阵的自由度是3。 403 | 404 | 绕坐标轴的旋转矩阵: 405 | \begin{equation} 406 | \begin{aligned} 407 | & R_x(\alpha)=\left(\begin{array}{ccc} 408 | 1 & 0 & 0 \\ 409 | 0 & \cos \alpha & -\sin \alpha \\ 410 | 0 & \sin \alpha & \cos \alpha 411 | \end{array}\right) \\ 412 | & R_y(\beta)=\left(\begin{array}{ccc} 413 | \cos \beta & 0 & \sin \beta \\ 414 | 0 & 1 & 0 \\ 415 | -\sin \beta & 0 & \cos \beta 416 | \end{array}\right) \\ 417 | & R_z(\gamma)=\left(\begin{array}{ccc} 418 | \cos \gamma & -\sin \gamma & 0 \\ 419 | \sin \gamma & \cos \gamma & 0 \\ 420 | 0 & 0 & 1 421 | \end{array}\right) 422 | \end{aligned} 423 | \end{equation} 424 | 425 | 任何旋转都可以由沿基本轴的旋转合成。需要注意的是,分别沿$x,y,z$轴 426 | 旋转$\alpha, \beta, \gamma$时,基本轴选择物体坐标系时,其旋转矩阵(Intrinsic)为 427 | $R_x(\alpha)R_y(\beta)R_z(\gamma)$,基本轴旋转世界坐标系时,旋转矩阵(Extrinsic)为 428 | $R_z(\gamma)R_y(\beta)R_x(\alpha)$。常见工具maya是物体坐标系,Unity是可选择, 429 | 但基本顺序是YXZ。 430 | 431 | 平移插值:$x_t = (1-t)x_0 + x_t$。 432 | 433 | 旋转插值:矩阵形式几乎无法操作;欧拉角表示做多次矩阵乘法,同时 434 | 需要处理奇点($\alpha+n\pi$)问题,比如人在原地转圈,万向锁问题等; 435 | 轴角表示($u, \theta$),做旋转组合比较麻烦,常见插值$\theta_t=(1-t)\theta_0 + \theta_1$ 。 436 | 其旋转速度不恒定,恒定方式为:\begin{equation} 437 | \begin{aligned} 438 | R(\delta \boldsymbol{\theta}) & =R^T\left(\boldsymbol{\theta}_0\right) R\left(\boldsymbol{\theta}_1\right) \\ 439 | \delta \boldsymbol{\theta}_t & =(1-t) \mathbf{0}+t \delta \boldsymbol{\theta} \\ 440 | R\left(\boldsymbol{\theta}_t\right) & =R\left(\boldsymbol{\theta}_0\right) R\left(\delta \boldsymbol{\theta}_t\right) 441 | \end{aligned} 442 | \end{equation} 443 | 四元数表示(2D旋转可由复数表示,3D旋转最终由William Rowan Hamilton发明了四元数解决), 444 | \begin{equation} 445 | \label{WilliamRowanHamilton} 446 | \begin{aligned} 447 | \boldsymbol{q}_{\boldsymbol{t}} & =\frac{\sin [(1-t) \theta]}{\sin \theta} \boldsymbol{q}_0+\frac{\sin t \theta}{\sin \theta} \boldsymbol{q}_1 \\ 448 | & cos \theta = \boldsymbol{q}_0 \boldsymbol{q}_1 449 | \end{aligned} 450 | \end{equation} 451 | 452 | \subsection{四元数} 453 | 基本定义,基本性质(加减乘除点积,共轭,范数,矩阵表示等)参考\href{https://en.wikipedia.org/wiki/Quaternion}{Quaternion}。 454 | 455 | 四元数(quaternion)的矩阵记法: 456 | $\boldsymbol{q} = w + x\boldsymbol{i} + y\boldsymbol{j} + z\boldsymbol{k}= 457 | \left[\begin{array}{l} 458 | w \\ 459 | x \\ 460 | y \\ 461 | z 462 | \end{array}\right] 463 | = 464 | \left[\begin{array}{l} 465 | w \\ 466 | \boldsymbol{v} 467 | \end{array}\right] 468 | $ 469 | 470 | 扩展定义: 471 | $q=[w, \boldsymbol{v}]^{\mathrm{T}} \in \mathbb{H}, w \in \mathbb{R}, 472 | \boldsymbol{v} \in \mathbb{R}^3$。 473 | $w=[w, \mathbf{0}]^{\mathrm{T}}$ : 常量四元数。 474 | $\boldsymbol{v}=[0, \boldsymbol{v}]^{\mathrm{T}}:$ 纯四元数。 475 | 记法 476 | $ 477 | \left[\begin{array}{l} 478 | w \\ 479 | \boldsymbol{v} 480 | \end{array}\right] 481 | $ 482 | 容易写出其基本运算规律,比如$\boldsymbol{q}^{*}=[w, -\boldsymbol{v}]^T$, 483 | 乘法\begin{equation} 484 | \boldsymbol{q}_1 \boldsymbol{q}_{\mathbf{2}}=\left[\begin{array}{l} 485 | w_1 \\ 486 | \boldsymbol{v}_{\mathbf{1}} 487 | \end{array}\right]\left[\begin{array}{c} 488 | w_2 \\ 489 | \boldsymbol{v}_{\mathbf{2}} 490 | \end{array}\right]=\left[\begin{array}{c} 491 | w_1 w_2-\boldsymbol{v}_1 \cdot \boldsymbol{v}_2 \\ 492 | w_1 \boldsymbol{v}_2+w_2 \boldsymbol{v}_1+\boldsymbol{v}_1 \times \boldsymbol{v}_2 493 | \end{array}\right] 494 | \end{equation} 495 | 有基本性质: 496 | \begin{itemize} 497 | \item 共轭$(\boldsymbol{q}_1 \boldsymbol{q}_2)^* 498 | = \boldsymbol{q}^{*}_2 \boldsymbol{q}^{*}_1$ 499 | \item 范数 $\|q\|^2 = q^{*}q= qq^{*}$ 500 | \item 逆 $qq^{-1}=1 \rightarrow q^{-1}=\frac{q^*}{\|q\|^2}$ 501 | \item 单位四元数 $\|q\|=1, q^{-1}=q^*= 502 | \left[\begin{array}{l} 503 | w \\ 504 | \boldsymbol{v} 505 | \end{array}\right] \Leftrightarrow R^{-1} = R^T$ 506 | \end{itemize} 507 | 508 | 对比复平面上单位圆的表示,单位四元数可表示为4D空间中的超球面上的 509 | 点,$\left[\begin{array}{l} 510 | \cos\frac{\theta}{2}, \\ 511 | \boldsymbol{u}\sin\frac{\theta}{2} 512 | \end{array}\right], \|\boldsymbol{u}\|=1$。 513 | 表示的含义和轴角表示相同$(\boldsymbol{u}, \theta)$。 514 | 具体而言,任何3D旋转$(\boldsymbol{v}, \theta)$的单位四元数表示为 515 | $ 516 | \left[\begin{array}{l} 517 | w \\ 518 | \boldsymbol{v} 519 | \end{array}\right] = \left[ \cos\frac{\theta}{2}, 520 | \boldsymbol{u}\sin\frac{\theta}{2} \right] 521 | $ 522 | 其轴角表示的 523 | 角度$\theta = 2 arg \cos w$,轴为$\boldsymbol{u}= 524 | \frac{\boldsymbol{v}}{\|\boldsymbol{v}\|}$。 525 | 这里左右表达在行列上有差异,右边应保持列表达。轴角表示和 526 | 单位四元数是一个满射关系。 527 | 528 | 529 | \begin{problemset} 530 | \item 向量$\boldsymbol{p}$绕轴角$(\boldsymbol{u}, \theta)$旋转, 531 | 得到的向量$\boldsymbol{p}^{\prime}$,用四元数如何运算? 532 | \item 旋转的组合,用四元数实现,在运算上有什么特点? 533 | \item 旋转如何插值? 534 | \end{problemset} 535 | 536 | \begin{solution} 537 | \begin{enumerate} 538 | \item[1.] 把$p$点写为一个纯四元数形式, 539 | $ 540 | \left[\begin{array}{l} 541 | 0 \\ 542 | \boldsymbol{p}^{\prime} 543 | \end{array}\right] = 544 | q \left[\begin{array}{l} 545 | 0 \\ 546 | \boldsymbol{p} 547 | \end{array}\right] q^{*} 548 | $ 549 | 其中$\boldsymbol{q}=\left[\begin{array}{l} 550 | \cos\frac{\theta}{2}, \\ 551 | \boldsymbol{u}\sin\frac{\theta}{2} 552 | \end{array}\right]$。注意这里$\boldsymbol{q},\boldsymbol{-q}$ 553 | 表示同样的旋转。 554 | \item[2.] 单位四元数$q_1, q_2$, 组合旋转$q=q_2 q_1$, 3D向量$p$, 555 | 则先后旋转$q_1, q_2$,是保持四元数乘积的,结果形式仍为问题1所示。 556 | \item[3.] 在单位4维球弧上,简单的线性插值$q_t = (1-t)q_0 + tq_1$会使得 557 | $q_t$不是一个单位四元数,不在弧上,需要做单位化处理,但此种处理在 558 | 旋转速度上是不恒定的,虽然实际效果差别不大。满足恒定速度的插值 559 | SLERP:Spherical Linear Interpolation, $q_t = a(t)q_0 + b(t)q_1$, 560 | 其中$a(t)=\frac{\sin[(1-t)\theta]}{\sin(\theta)}, b(t)=\frac{\sin(t\theta)}{\sin\theta}, 561 | \cos\theta=q_0 q_1 $。 562 | \end{enumerate} 563 | 564 | \end{solution} 565 | 566 | \chapter{角色运动学} 567 | 假设角色身体是刚性的,运动是骨骼绕关节的旋转。将角色定义为关节的 568 | 铰链(joint)结构(skeleton)。关节之间的称为骨骼(body、bone、link)。 569 | 570 | 姿势:旋转关节。每个关节定义一个局部坐标系,同时有一个全局朝向。 571 | \section{前向和逆向运动学} 572 | 对于任意单链结构(每个子节点都有且只有一个父节点,各骨骼长度一样),假设初始朝向 573 | 全为单位阵$I$,则叶子节点(关节)旋转$R_n$,其朝向变为$Q_n = R_n$,同时其父节点 574 | 在此基础上旋转$R_{n-1}$,则其朝向为$Q_{n}=R_{n-1}R_{n}$,父节点朝向为$Q_{n-1}=R_{n-1}$。 575 | 以此类推到根节点。容易得到$Q_i = Q_{i-1}R_i$,表示从局部旋转到全局朝向的 576 | 关系。因为朝向是正交矩阵,从而得到从全局朝向到局部旋转的关系为:$R_i = Q_{i-1}^T Q_i$。 577 | 578 | 要计算某关节的全局坐标系,只需要用其父关节的全局坐标系乘以它自身的局部旋转,同样 579 | 父关节的全局坐标系的转置(逆)乘以当前关节的全局坐标系,就能得到其局部旋转。 580 | 581 | 对于一般情况,各骨骼长度不一,朝向不一。设根节点初始位置,朝向,旋转,长度 582 | 分别为$\boldsymbol{p}_0,Q_0, R_0, l_0$,其中$Q_0 = R_0$, 可以得到子节点 583 | 的位置,$\boldsymbol{p}_1 = \boldsymbol{p}_0 + Q_0 \boldsymbol{l}_0$,朝向 584 | $Q_1 = Q_0 R_1$,第三个节点的位置$\boldsymbol{p}_2 = \boldsymbol{p}_1 + Q_1 \boldsymbol{l}_1$。 585 | 以此类推,在叶子节点局部坐标系下的点$x_0$,其在全局坐标系下应如何表示,或者关于 586 | 某一个父节点的朝向下,应该如何表示,或者父节点的局部坐标系下如何表示。 587 | $x = p_n + Q_n x_0$。 588 | 对于五个节点($0~4$)的单链来说有: 589 | \begin{equation} 590 | \begin{aligned} 591 | \boldsymbol{x} & =\boldsymbol{p}_4+Q_4 \boldsymbol{x}_0 \\ 592 | & =\boldsymbol{p}_3+Q_3 \boldsymbol{l}_3+Q_3 R_4 \boldsymbol{x}_0 \\ 593 | & =\boldsymbol{p}_3+Q_3\left(\boldsymbol{l}_3+R_4 \boldsymbol{x}_0\right) 594 | \end{aligned} 595 | \end{equation} 596 | 上式得到$\boldsymbol{x}$在$Q_3$中的局部坐标为$\boldsymbol{x}^{Q_3}= 597 | Q_{3}^{T}(\boldsymbol{x} - \boldsymbol{p}_3)=\boldsymbol{l_3}+R_4 \boldsymbol{x}_0$。 598 | 599 | 第0个关节旋转,朝向变了,但第一个关节关于第0个关节的局部关系是不变的,用第0个关节 600 | 的朝向乘以第一个关节相对于第0个关节的位置,再加上第0个关节的原点,就得到了第一个关节的 601 | 世界坐标(局部坐标系的原点在世界坐标系下的位置)。接着旋转第一个关节,那么第二个关节的坐标原点就变了, 602 | 同理可得到第二个关节的局部坐标系原点在世界坐标系中的坐标值。 603 | 604 | 当从根节点递归旋转到叶子节点,就能得到每个局部坐标系的朝向和坐标原点位置。 605 | 606 | 问题:如果知道某个坐标系下的点$x_0$(局部坐标),如何计算其在全局坐标系下的位置? 607 | 608 | % \begin{algorithm}[h] 609 | % \caption{bool DM improve($S_H$)} 610 | % \label{alg::conjugateGradient} 611 | % \begin{algorithmic}[1] 612 | % \Require 613 | % Real-time task set $S_H$ 614 | % \Ensure 615 | % The schedulability of $S_H$ 616 | 617 | % \State $D=0$; 618 | 619 | % \For {${\tau _i}:{S_H}$} %For循环结构 620 | % \State $D = D+C_i/D_i$; 621 | % \EndFor 622 | % \If {$D > n$} 623 | % \State return false; 624 | % \Else 625 | % \State return true; 626 | % \EndIf 627 | % \end{algorithmic} 628 | % \end{algorithm} 629 | 630 | \begin{algorithm}[h] 631 | \caption{前向运动学根到叶子} 632 | \label{alg::FKrootend} 633 | \begin{algorithmic}[1] 634 | \Require 635 | 所有关节点的旋转矩阵 $R_i$, 坐标$x_0$, 636 | \Ensure 637 | 全局坐标 $x$ 638 | 639 | \For {$i$ in root to end effector } %For循环结构 640 | \State $Q_i = Q_{i-1}R_i$; 641 | \State $p_{i+1} = p_i + Q_{i}l_i$ 642 | \EndFor 643 | \State $x = p_E + Q_E x_0$ 644 | \end{algorithmic} 645 | \end{algorithm} 646 | 647 | 从一个目标点开始,以此将其转化为其父节点的局部坐标系,即可得到当前点在全局坐标系 648 | 下的表示。也可以计算其关于非根节点下的坐标表示。这就是前向运动学的迭代。 649 | \begin{algorithm}[h] 650 | \caption{前向运动学叶子到根} 651 | \label{alg::FKendroot} 652 | \begin{algorithmic}[1] 653 | \Require 654 | 所有关节点的旋转矩阵 $R_i$, 坐标$x_0$, 655 | \Ensure 656 | 全局坐标 $x$ 657 | \State $x=x_0$; 658 | \For {$i$ in end effector to root } %For循环结构 659 | \State $x = l_{i-1} + R_i x$; 660 | \EndFor 661 | \end{algorithmic} 662 | \end{algorithm} 663 | 664 | 将上两算法的全局坐标系$x$改为局部坐标$Q_k$,则分别有: 665 | 666 | \begin{itemize} 667 | \item[1] 前向运动学第$k+1$个节点到叶子 668 | \begin{itemize} 669 | \item[1.1] $Q^{\prime}_i = Q^{\prime}_{i-1}R_i, (Q^{\prime}_{0}=I)$ 670 | \item[1.2] $p^{\prime}_{i+1} = p^{\prime}_i + Q^{\prime}_{i}l_i$ 671 | \end{itemize} 672 | \item[2] 前向运动学叶子到第$k+1$个节点 673 | \begin{itemize} 674 | \item[2.1] $x = l_{i-1} + R_i x$ 675 | \end{itemize} 676 | \end{itemize} 677 | 678 | % \begin{algorithm}[htbp] 679 | % \caption{前向运动学第$k+1$个节点到叶子} 680 | % \label{alg::FKQkend} 681 | % \begin{algorithmic}[1] 682 | % \Require 683 | % 所有关节点的旋转矩阵 $R_i$, 坐标$x_0$, 684 | % \Ensure 685 | % $x_0$相对局部坐标$Q_k$的坐标 686 | 687 | % \For {$i$ in joint $k+1$ to end effector } %For循环结构 688 | % \State $Q^{\prime}_i = Q^{\prime}_{i-1}R_i, //(Q^{\prime}_{0}=I)$; 689 | % \State $p^{\prime}_{i+1} = p^{\prime}_i + Q^{\prime}_{i}l_i$ 690 | % \EndFor 691 | % \State $x = p^{\prime}_E + Q^{\prime}_E x_0$ 692 | % \end{algorithmic} 693 | % \end{algorithm} 694 | % 中间插这么多? 695 | % \begin{algorithm}[htbp] 696 | % \caption{前向运动学叶子到第$k+1$个节点} 697 | % \label{alg::FKendQk} 698 | % \begin{algorithmic}[1] 699 | % \Require 700 | % 所有关节点的旋转矩阵 $R_i$, 坐标$x_0$, 701 | % \Ensure 702 | % 全局坐标 $x$ 703 | % \State $x=x_0$; 704 | % \For {$i$ in end effector to joint $k+1$ } %For循环结构 705 | % \State $x = l_{i-1} + R_i x$; 706 | % \EndFor 707 | % \end{algorithmic} 708 | % \end{algorithm} 709 | 710 | \subsection{角色的前向运动学} 711 | 树结构,根节点一般选择为腰的位置。 712 | \begin{algorithm}[h] 713 | \caption{角色前向运动学} 714 | \label{alg::FKrootendtree} 715 | \begin{algorithmic}[1] 716 | \Require 717 | 所有关节点的旋转矩阵 $R_i$, 坐标$x_0$, 718 | \Ensure 719 | 全局坐标 $x$ 720 | 721 | \For {$i$ in joint\_list } %For循环结构 722 | \State $p_i = i^{\prime}$s 父节点 723 | \State $Q_i = Q_{p_i}R_i$; 724 | \State $x_i = x_{p_i} + Q_{p_i}l_i$ 725 | \EndFor 726 | \end{algorithmic} 727 | \end{algorithm} 728 | 729 | 节点的类型,自由度Degrees of Freedom(DoF),一个正方体,三个轴均可旋转,平移,则自由度 730 | 为6,记为$(\boldsymbol{p}, R)\in \mathbb{R}^3 \times SO(3)$,当其被固定在一平面时,自由度 731 | 只有帖平面旋转和平移(前后,左右),DoF=3,当给串在一根线上,只能绕线(轴)旋转和上下平移, 732 | DoF=2,固定在一个轴上,不能移动,只能旋转的情况,DoF=1。 733 | 734 | 自由度为1的有膝盖(knee),手肘(elbow)称为hinge joint或revolute joint,臀部(hip),肩膀(shoulder)自由度为3 735 | ,称为ball-and-socket joint,自由度为2的称为universal joint。对人体而言, 736 | 自由度1,3的旋转角度,会被控制在一个范围内。 737 | 738 | 姿势参数由根节点位置,朝向和内部关节点的旋转来表示:$\boldsymbol{\theta}=(\boldsymbol{t}_0, R_0, R_1, \cdots)$。 739 | 740 | \subsection{运动数据的文件表示} 741 | 参考\href{https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html}{bvh} 742 | 743 | \subsection{逆向运动学IK} 744 | 通过指定末端点(关节)的位置和朝向,利用逆向运动学算法自动计算出每个关节的位置和旋转,这样会更直观。 745 | 相对前向运动学的应用会更加广泛,比如角色绑定,对于动画师来说,逆向控制操作更友好,效率更高。 746 | 747 | 前向:已知系统的参数$\theta$和属性$\boldsymbol{x}$, 有$\boldsymbol{x}=f(\theta)$,表示从系统的 748 | 参数计算某一个属性的过程。计算上比较容易,但$\theta$的自由度DoF往往比$\boldsymbol{x}$的大得多, 749 | 调试$\theta$达到一个较好的$\boldsymbol{x}$往往并不容易。 750 | 751 | 逆向:给定属性$\boldsymbol{x}$,找到符合$\boldsymbol{x}=f(\theta)$的一组参数$\theta$,有 752 | $\theta=f^{-1}(\boldsymbol{x})$,表示从系统的属性计算参数的过程,这通常需要求解一个非线性问题, 753 | 存在多解的可能性,并且$\boldsymbol{x}$的设定,有一定的直觉性。 754 | 755 | IK:给定末端点位置和朝向$\boldsymbol{x}=f(\theta), 756 | Q=Q(\theta)$,需要让末端点到达指定点$\widetilde{x}$,且朝向到指定朝向$\widetilde{Q}$,来求解中间所有节点的位置和朝向。 757 | 758 | IK的优化建模:$\min_{\boldsymbol{\theta}}F(\boldsymbol{\theta}), 759 | F(\boldsymbol{\theta}) = \frac{1}{2}\|f(\boldsymbol{\theta})-\widetilde{\boldsymbol{x}}\|^2$, 760 | 这里$\theta$一般表示所有关节点的旋转。朝向忽略了,一般较容易实现。 761 | 762 | 763 | \subsubsection{循环坐标下降法} 764 | Cyclic Coordinate Descent循环坐标下降法,依次沿着某一坐标轴去更新参数,每一步更新的距离是使得更新之后 765 | 的点在这个坐标轴的方向上使得目标值最小的那个点。 766 | 767 | 实现简单,只有点积,叉积计算,计算量比较小,不会考虑目标函数的性质, 768 | 完全基于坐标来更新,在特定情况下,收敛速度很慢,且有时候得到的解不稳定, 769 | 存在抖动情况。 770 | 771 | 考虑目标函数的性质,最基本的算法是梯度下降法。 772 | 773 | \subsubsection{雅可比和梯度下降法} 774 | 基本定义和性质参考: 775 | \href{https://en.wikipedia.org/wiki/Gradient}{Gradient}, 776 | \href{https://en.wikipedia.org/wiki/Gradient_descent}{Gradient descent}, 777 | \href{https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant}{Jacobian matrix and determinant}, 778 | \href{https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse}{Moore Penrose inverse}, 779 | \href{https://math.ecnu.edu.cn/~jypan/Teaching/MatrixComp/mc.pdf}{矩阵计算讲义}。 780 | 781 | 沿着使得$F(\boldsymbol{\theta})$增加最快的方向(梯度方向)的负方向更新参数。 782 | \begin{equation} 783 | \begin{aligned} 784 | & \qquad \nabla_\theta F(\boldsymbol{\theta}) 785 | \end{aligned}=\left[\begin{array}{c} 786 | \frac{\partial F}{\partial \theta_0}(\boldsymbol{\theta}) \\ 787 | \frac{\partial F}{\partial \theta_1}(\boldsymbol{\theta}) \\ 788 | \vdots \\ 789 | \frac{\partial F}{\partial \theta_n}(\boldsymbol{\theta}) 790 | \end{array}\right]=\left(\frac{\partial F}{\partial \boldsymbol{\theta}}(\theta)\right)^T 791 | \end{equation} 792 | $$\boldsymbol{\theta}^{i+1} = \boldsymbol{\theta}^{i} - \alpha \nabla_\theta F(\boldsymbol{\theta^{l}})$$ 793 | 794 | \begin{equation} 795 | \begin{aligned} 796 | \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) \\ 797 | & =J^T\color{red}{\Delta} 798 | \end{aligned} 799 | \end{equation} 800 | 其中$f: \mathbb{R}^n \mapsto \mathbb{R}^3$, 801 | $J=\frac{\partial f}{\partial \boldsymbol{\theta}}= 802 | \left(\begin{array}{llll}\frac{\partial f}{\partial \theta_0} 803 | & \frac{\partial f}{\partial \theta_1} & \cdots & 804 | \frac{\partial f}{\partial \theta_n}\end{array}\right)$, 805 | 矩阵的第$i$列为 806 | $\frac{\partial f}{\partial \theta_i} = 807 | \left[\begin{array}{c} 808 | \frac{\partial f_x}{\partial \theta_i} \\ 809 | \frac{\partial f_y}{\partial \theta_i} \\ 810 | \frac{\partial f_z}{\partial \theta_i} 811 | \end{array}\right]$。 812 | 这里的$n$,通常是$\theta$的个数和单个的基础维数组合而成,比如一个节点的旋转是3维,有24个节点,$\theta$的维度即72。 813 | 814 | 雅可比矩阵可采用深度学习框架,比如pytorch计算,利用其自动计算梯度的能力。 815 | 也可以利用Rodrigues'旋转公式(2.2)来计算。 816 | 817 | 假设所有节点均是 hinge joint,对于铰链关节点$\boldsymbol{x}, \text{沿着} \boldsymbol{r_i}$绕轴$\boldsymbol{a_i}$旋转 818 | $\delta\theta_i$度,$\boldsymbol{r_i}$起点为节点$i$,由公式2.2得到 819 | \begin{equation} 820 | \begin{gathered} 821 | \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) \\ 822 | \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 823 | \end{gathered} 824 | \end{equation} 825 | 826 | 对于节点为ball joint的,有三个自由度,将旋转由欧拉角来表示$R_i = R_{ix}R_{iy}R_{iz}$, 827 | 因此每个ball joint可以表示为三个hinge joints的乘积。有 828 | \begin{equation} 829 | \begin{array}{ll} 830 | \boldsymbol{a}_{i x}=Q_{i-1} \boldsymbol{e}_x \\ 831 | \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 \\ 832 | \boldsymbol{a}_{i z}=Q_{i-1} R_{i x} R_{i y} \boldsymbol{e}_z & 833 | \end{array} 834 | \end{equation} 835 | 若欧拉角形式表示为$R_i = R_{ix}R_{iy}R_{ix^{\prime}}$,则需要修改$a_{iz}$为 836 | ${a}_{i x^{\prime}}=Q_{i-1} R_{i x} R_{i y} \boldsymbol{e}_x $ 837 | 838 | 雅可比矩阵的每一列都可以经过前向运动,逐次的由节点到目标节点的叉乘($\boldsymbol{a_{i*}}$世界坐标系)计算而得。 839 | 其和梯度下降,均属于一阶优化,收敛速度较慢,而且每一轮迭代都需重新计算雅可比矩阵。总体上其收敛速度快于CCD,但 840 | 计算量比CCD大一些。 841 | 842 | \subsubsection{雅可比逆法} 843 | Jacobian inverse method 844 | 考虑一个简单的二次规划问题: 845 | \begin{gather*} 846 | \min _{\boldsymbol{\theta}} F(\boldsymbol{\theta})=\frac{1}{2} \boldsymbol{\theta}^T A \boldsymbol{\theta}+\boldsymbol{b}^T \boldsymbol{\theta} \\ 847 | \text {Gradient:} \quad \nabla_\theta F(\boldsymbol{\theta})=A \boldsymbol{\theta}+\boldsymbol{b}\\ 848 | \text {Optimality condition:} \quad \nabla_\theta F\left(\boldsymbol{\theta}^*\right)=0 \tag{3.*}\\ 849 | \Downarrow \\ 850 | \boldsymbol{\theta}^*=-A^{-1} \boldsymbol{b} 851 | \end{gather*} 852 | 其中$A$是对称正定的。 853 | 854 | 高斯-牛顿法:将$f(\theta)$在$\theta^0$做一阶近似,即 855 | $f(\boldsymbol{\theta}) \approx f(\boldsymbol{\theta}^0) + J(\boldsymbol{\theta} - \boldsymbol{\theta}^0)$。 856 | 得到原目标函数的近似表达: 857 | \begin{equation} 858 | \begin{aligned} 859 | 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 \\ 860 | & =\frac{1}{2}\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)^T J^T J\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right) \\ 861 | & +\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)^T J^T\left(f\left(\boldsymbol{\theta}^0\right)-\widetilde{x}\right)+\boldsymbol{c} 862 | \end{aligned} 863 | \end{equation} 864 | 是一个二次型,如上二次规划,可得到其一阶近似后的最优条件(first-order optimality condition), 865 | $$ 866 | (\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} 867 | $$ 868 | 即 869 | \begin{equation} 870 | J^T J\left(\boldsymbol{\theta}-\boldsymbol{\theta}^0\right)=-J^T\Delta 871 | \end{equation} 872 | 如果$J^T J$可逆,得$\boldsymbol{\theta} = \boldsymbol{\theta^0} 873 | - (J^T J)^{-1} J^T \Delta$。因为$J:\mathbb{R^n}\rightarrow 3$,一般$n>3$,两矩阵的乘积 874 | 的秩不大于乘积因子的秩,矩阵的秩不大于行列数的最小值($rank(AB)\leq\min(rank A, rank B), rank(A_{m\times n}) \leq \min(m, n)$), 875 | 得$J^T J$不可逆,但$J J^T$可能可逆。假设其可逆, 876 | 等式两边同时乘以$J$,得到$J(\boldsymbol{\theta} - \boldsymbol{\theta^0}) = 877 | \boldsymbol{\widetilde{x}} - f(\boldsymbol{\theta^0})$。采用广义逆(非方阵情况下) 878 | 定义(Moore-Penrose) Pseudoinverse,可以得到$\boldsymbol{\theta}$的更新方式: 879 | \begin{equation} 880 | \begin{aligned} 881 | \boldsymbol{\theta} &= \boldsymbol{\theta^0} - J^{+}\Delta \\ 882 | &= \boldsymbol{\theta^0} - J^T (J J^T)^{-1}\Delta 883 | \end{aligned} 884 | \end{equation} 885 | 886 | 在实际更新过程中,需要增加一个学习率参数,否则不符合非线性函 887 | 数的线性逼近是局部的这个性质,收敛情况会更差或不好控制。同时上 888 | 面只考虑了单点移动,旋转情况,若考虑多点情况,比如由四个关节点组成的系统,节点 889 | $\boldsymbol{x_2}, \boldsymbol{x_3}, \boldsymbol{x_4}$ 890 | 均需要旋转,则目标函数为 891 | \begin{equation} 892 | \left[\begin{array}{l} 893 | x_2 \\ 894 | x_3 \\ 895 | x_4 896 | \end{array}\right]=f(\boldsymbol{\theta}) \in \mathbb{R}^9 897 | \end{equation} 898 | 此时的雅可比矩阵大小为$9\times 4$,$J^T J$就可能可逆。此时对上面更新式子可做如下$J^+ = (J^T J)^{-1}J^T$。 899 | 也就是说,雅可比的广义逆,根据其形状来定,“高瘦”(多点,约束较多)为后者,“矮胖”(单点,欠约束ik)为前者。 900 | 901 | 雅可比逆方法一般比雅可比转置方法,梯度下降法,效率高,但并不是$J^T J, J J^T$ 902 | 一定可逆,此时可考虑扰动。 903 | 904 | 对于扰动(参考链接文档"矩阵计算讲义")情况,对称阵$+$ 905 | $\lambda I$,选择合适的参数,一定能可逆。有$J^{*}=J^T (J J^T + \lambda I)^{-1}$,或 906 | $J^{*}= (J^T J + \lambda I)^{-1}J^T$。在角色动画中,$\lambda$一般称为阻尼系数。 907 | 908 | 阻尼雅可比逆法: 909 | \begin{equation} 910 | \begin{aligned} 911 | & 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) \\ 912 | & \boldsymbol{\theta}^{i+1}=\boldsymbol{\theta}^i-\alpha\left(J^T J+\lambda W\right)^{-1} J^T \Delta \\ 913 | & \lambda \text { : damping parameter } \\ 914 | & W=\left[\begin{array}{llll} 915 | w_0 & & & \\ 916 | & w_1 & & \\ 917 | & & \ddots & \\ 918 | & & & w_n 919 | \end{array}\right]: \text { weight matrix } \\ 920 | & 921 | \end{aligned} 922 | \end{equation} 923 | 这里$W$是权重矩阵,$\lambda$是damping parameter。 924 | 925 | 将上面对角矩阵$W$"退化"到单位阵,在角色动画中,即每个关节点一视同仁,具有相同的权重,同时 926 | $F(\theta)$正则项变为平方项。 927 | 928 | 带有阻尼项的方法,被称为 Levenberg-Marquardt algorithm,它避免了 929 | 高斯-牛顿法的不稳定情况。从另一个角度看,阻尼项等价于给优化函数加了一个正则项。 930 | 它带来了一些直观意义,比如单位阵$I$表示,每个关节用尽可能少的旋转移动来达到目标位置, 931 | 各个节点一视同仁,对于$W$则限制了不同关节的移动幅度,特征值越大(对角数),对应的关节移动的幅度越小。 932 | 933 | 以上是针对一个机械臂(单链结构)的理论分析,而对于角色来说,链式结构是一个树型结构。 934 | 比如同时将手,脚,胳膊移动到各自的目标位置,此时的优化目标可设定为 935 | \begin{equation} 936 | \label{IK3.11} 937 | \begin{gathered} 938 | 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 \\ 939 | \boldsymbol{\theta}=\left(\boldsymbol{t}_0, R_0, R_1, R_2, \ldots \ldots\right) 940 | \end{gathered} 941 | \end{equation} 942 | 这里$i=3$,也就是把各个单链加起来。 943 | 944 | 实际使用中,通常不会考虑所有约束,比如旋转胳膊,让手到前方靠下的位置, 945 | 若根节点选择髋关节,不加约束,会使得上半身超前倾; 946 | 若根节点选择在脚上,会使得整个身体都会向前倾斜; 947 | 若希望移动手的时候,身体不移动,可将根节点设置到肩关节处。 948 | 949 | 启发式方法:\href{http://andreasaristidou.com/FABRIK.html}{FABRIK}。 950 | 951 | 952 | \begin{problemset} 953 | \item 实现BVH的解析 954 | \item 用pytorch等实现前向运动 955 | \item 实现逆向运动IK 956 | \item 工业实现库的复用 957 | \end{problemset} 958 | 959 | \chapter{角色动画} 960 | T-Pose 每个关节旋转为0,或者单位阵,也称为Bind pose或Reference pose。 961 | A-Pose,动画师比较倾向,肩膀位置比较自然,在绑定任务上,更加自然,artifact 962 | 会比较弱。A-Pose形状不唯一。 963 | 964 | \section{重定向} 965 | 相同的动作作用在不同的参考姿势上,易知只需要找到两姿势的映射关系。 966 | 动画上称为Retargeting。 967 | 968 | 首先考虑最简单的情形,一个骨骼,即骨骼$A$到骨骼$B$的重定向。 969 | 初始条件有$A, R^{0}_A = I, B, R^{0}_B = I$,两者的初始朝向不同,但在各自局部坐标系下 970 | 都是单位阵。初始朝向的转换关系为$R_{A \rightarrow B}$,若$A$做了$R_A$的旋转, 971 | 要使得$B$达到同样的效果,易得将$B$初始朝向转为$A$,然后做$A$的动作即可,固有 972 | $R_B=R_A R_{A\rightarrow B}^T$(旋转矩阵正交)。 973 | 974 | 其次考虑一个关节点组成的链结构,$A:= p_i : i$,骨骼$p_i$是骨骼 975 | $i$的父节点,两骨骼$A,B$。初始条件有节点的全局朝向映射关系:$Q^{A\rightarrow B}_{p_i}, 976 | Q^{A\rightarrow B}_{i}$。若$A$各骨骼分别做了旋转$R_i ^A, R^A_{p_i}$,根据第三章 977 | 可知$Q_{p_i}^A = R_{p_i}^A, Q_i ^A = Q^A _{p_i}R^A _i$。$B$同理, 978 | $Q_{p_i}^B = R_{p_i}^B, Q_i ^B = Q^B _{p_i}R^B _i$。 979 | 根据上面的讨论,对于每一根骨骼(link),有$Q^B = Q^A(Q^{A\rightarrow B})^T$, 980 | 即$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$, 981 | 结合$R_i ^B = (Q_{p_i}^B)^T Q_i ^B$。$\Rightarrow R_i ^B = 982 | Q_{p_i}^{A \rightarrow B}\left(Q_{p_i}^A\right)^T Q_i^A\left(Q_i^{A \rightarrow B}\right)^T= 983 | Q_{p_i}^{A \rightarrow B} R_i^A\left(Q_i^{A \rightarrow B}\right)^T$。 984 | 同样计算$R_{p i}^B=R_{p i}^A\left(Q_{p i}^{A \rightarrow B}\right)^T$。 985 | 总的来说就是根据单链的推导的关系,得到骨骼朝向的映射关系,然后将被定向的链$B$的朝向 986 | 表示为参考链$A$的朝向表达,然后根据IK中父子链的局部于全局坐标关系,计算而得。 987 | 图示\ref{fig:Retargeting}。 988 | \begin{figure}[htbp] 989 | \centering 990 | \includegraphics[totalheight=2in]{"./image/Retargeting.png"} 991 | \caption{Retargeting} \label{fig:Retargeting} 992 | \end{figure} 993 | 994 | 记忆:$B$父旋转=父旋转*父朝向映射的逆, $B$子旋转=父朝向映射*子旋转*子朝向映射的逆。 995 | 996 | 最后是参考姿势到参考姿势的重定向,递归考虑。 997 | 998 | 实际情况,动捕的时候是人,角色却很复杂,比如人体脊柱一般2-3个关节,而角色可能有 999 | 7-8个关节,另外还有衣服,斗篷,道具等。大概列举如下: 1000 | \begin{itemize} 1001 | \setlength{\itemindent}{2em} 1002 | \item 不同数量的骨骼 1003 | \item 骨骼名称不同 1004 | \item 参考姿势不同 1005 | \item 骨骼的大小不同 1006 | \item 骨骼的拓扑结构不同 1007 | \item ...... 1008 | \end{itemize} 1009 | 1010 | 一个可能的重定向pipeline: 1011 | \begin{itemize} 1012 | \setlength{\itemindent}{2em} 1013 | \item[1] 骨骼映射 1014 | \item[2] 调整动捕数据大小,通常将根关节的位移缩放到虚拟角色和目标角色的腿长一致,至少能保证走路时,脚不会飘着 1015 | \item[3] 处理关节T-pose的区别 1016 | \item[4] 穿模修正,浮空,脚打滑等的IK后处理。 1017 | \item[5] \dots 1018 | \end{itemize} 1019 | 1020 | \section{关键帧动画} 1021 | 主要是插值。 1022 | 1023 | \subsection{插值} 1024 | 平滑度:Discontinuity, $C^{0}\text{-continuity}$,$C^{1}\text{-continuity}$,$C^{2}\text{-continuity}$。 1025 | 位置,速度,加速度是否一致。或者连续,导数相等,二阶导数相等。$C^2$一般在物体建模中才会体现出效果来。 1026 | 1027 | 点集$D={(x_i, y_i)|i=0, \cdots, N }$的多项式$f(x)=a_0 + a_1 x + a_2 x^2 + 1028 | \cdots + a_n x^n$拟合,其中$n=N-1$。容易得到 1029 | \begin{equation} 1030 | \left[\begin{array}{c} 1031 | a_0 \\ 1032 | a_1 \\ 1033 | \vdots \\ 1034 | a_n 1035 | \end{array}\right]=\left[\begin{array}{ccccc} 1036 | 1 & x_0 & x_0^2 & \cdots & x_0^n \\ 1037 | 1 & x_1 & x_1^2 & \cdots & x_1^n \\ 1038 | \vdots & \vdots & \vdots & \ddots & \vdots \\ 1039 | 1 & x_N & x_N^2 & \cdots & x_N^n 1040 | \end{array}\right]^{-1}\left[\begin{array}{c} 1041 | y_0 \\ 1042 | y_1 \\ 1043 | \vdots \\ 1044 | y_N 1045 | \end{array}\right] 1046 | \end{equation} 1047 | 但,高阶多项式函数拟合容易\href{https://personal.math.ubc.ca/~peirce/M406_Lecture_3_Runge_Phenomenon_Piecewise_Polynomial_Interpolation.pdf}{龙格库塔现象}(误差受高阶项影响很大,不稳定,可至无穷大)。 1048 | 1049 | 解决办法,分段插值。 若线性,则$f(x)=S_i(x), x \in[x_i, x_{i+1}]$, 1050 | $S_i(x)=y_i + \frac{y_{i+1} - y_i}{x_{i+1}-x_i}(x-x_i)$。 1051 | 若非线性,可选样条插值(可证明,固定两端点,中间人选2点固定,则四点组成的曲线,一定是三次多项式 1052 | $S_i = a_i x^3 + b_i x^2 + c_i x + d_i$)。 1053 | 经典的有:三次样条,三次Hermite样条,Catmull-Rom样条。 1054 | 1055 | 对于三次样条,给定$N+1$个点,可分成$N$个片段,每个片段由一个样条逼近,则总共有$4N$个参数,因此需要至少$4N$个方程。 1056 | \begin{equation} 1057 | \begin{aligned} 1058 | \text { Interpolation condition: } & S_i\left(x_i\right)=y_i, \quad S_i\left(x_{i+1}\right)=y_{i+1} \\ 1059 | C^1 \text { continuity: } & S_{i-1}^{\prime}\left(x_i\right)=S_i^{\prime}\left(x_i\right) \\ 1060 | C^2 \text { continuity: } & S_{i-1}^{\prime \prime}\left(x_i\right)=S_i^{\prime \prime}\left(x_i\right) \\ 1061 | \text { boundary condition: } & S_0^{\prime}\left(x_0\right), S_{n-1}^{\prime}\left(x_n\right), S_0^{\prime \prime}\left(x_0\right), S_{n-1}^{\prime \prime}\left(x_n\right) 1062 | \end{aligned} 1063 | \end{equation} 1064 | 求解$4N \times 4N$可得。但此种方式求逆复杂,并且方程是整体关联的,牵一发而动全身。 1065 | 1066 | 希望插值函数有局部性,计算上没那么昂贵。三次Hermite样条给出了方案,只考虑当前段的首尾信息不考虑 1067 | 前后的1,2此光滑性,这样就不受前后片段的影响,解决条件不够的方法,就是增加额外信息, 1068 | $S_i^{\prime}(x_i)=m_i, S_{i+1}^{\prime}(x_i)=m_{i+1}$,$m_i$表示$x_i$的速度。 1069 | 1070 | 把当前片段映射到$[0,1]$,用$t$表示,有:$S(t)=at^3 + bt^2 + ct + d$。将$m_i$(为了 1071 | 方便,假设映射后的单位片段的左右速度为$m_i$)的值表示出来,有矩阵关系: 1072 | \begin{equation} 1073 | \begin{aligned} 1074 | S(t) & =a t^3+b t^2+c t+d \\ 1075 | & =\left[\begin{array}{llll} 1076 | t^3 & t^2 & t^1 & 1 1077 | \end{array}\right]\left[\begin{array}{cccc} 1078 | 2 & -2 & 1 & 1 \\ 1079 | -3 & 3 & -2 & -1 \\ 1080 | 0 & 0 & 1 & 0 \\ 1081 | 1 & 0 & 0 & 0 1082 | \end{array}\right]\left[\begin{array}{l} 1083 | y_i \\ 1084 | y_{i+1} \\ 1085 | m_i \\ 1086 | m_{i+1} 1087 | \end{array}\right] \\ 1088 | & =\left[\begin{array}{c} 1089 | 2 t^3-3 t^2+1 \\ 1090 | -2 t^3+3 t^2 \\ 1091 | t^3-2 t^2+t \\ 1092 | t^3-t^2 1093 | \end{array}\right]^T\left[\begin{array}{l} 1094 | y_i \\ 1095 | y_{i+1} \\ 1096 | m_i \\ 1097 | m_{i+1} 1098 | \end{array}\right] 1099 | \end{aligned} 1100 | \end{equation} 1101 | 左边每一行称为Hermite基函数,$H_i, i=0, 1, 2, 3$。更高维 1102 | $y, m$,对应了同样维度的$a, b, c, d$向量。 1103 | 1104 | \subsection{插值和样条} 1105 | \begin{equation} 1106 | \begin{aligned} 1107 | S(t) & =a t^3+b t^2+c t+d \\ 1108 | & =\left[\begin{array}{c} 1109 | 2 t^3-3 t^2+1 \\ 1110 | -2 t^3+3 t^2 \\ 1111 | t^3-2 t^2+t \\ 1112 | t^3-t^2 1113 | \end{array}\right]^T\left[\begin{array}{l} 1114 | y_1 \\ 1115 | y_2 \\ 1116 | m_1 \\ 1117 | m_2 1118 | \end{array}\right] 1119 | \end{aligned} 1120 | \end{equation} 1121 | 1122 | Catmull-Rom Spline是说:给定四点$P_0, P_1, P_2, P_3$, 1123 | 有$y_1=p_1, y_2=p_2, m_1 = \frac{p_2 - p_0}{2(x_2 - x_0)}, 1124 | m_2 = \frac{p_3 - p_1}{2(x_3 - x_1)}$。这里$1, 2$表示去上面第一个 1125 | 片段$i=1$。 1126 | 1127 | \subsection{旋转的插值} 1128 | \begin{figure}[htbp] 1129 | \centering 1130 | \includegraphics[totalheight=2in]{"./image/RotationRepresentations.png"} 1131 | \caption{Rotation Representations} \label{fig:RotationRepresentations} 1132 | \end{figure} 1133 | SLERP for Quaternions \ref{WilliamRowanHamilton},本质上是一个线性插值。 1134 | 1135 | 如何实现四元数的非线性插值?\href{https://splines.readthedocs.io/en/latest/rotation/kochanek-bartels.html}{kochanek-bartels}。 1136 | 该文讨论了四元数的贝塞尔曲线插值,效果比线性更平滑,但是在实现上,其加速度有时候抖动会比较大, 1137 | 并且加速度的求解对应到曲面上的最小曲率问题,比较困难。 1138 | 一般正确处理边界条件,效果差别不太大。 1139 | 1140 | \section{数据驱动动画} 1141 | 靠关键帧来生成动作,是一个劳动密集型工作。因为一般关键帧 1142 | 的fps并不高,k帧挺麻烦。 1143 | 1144 | 1145 | 动捕:使用动捕设备采集动作数据,或利用AI从视频中提取,或者生成。 1146 | 使用动捕数据,可实现无人机的跟踪等各自应用。 1147 | 1148 | 惯性传感器,惯性测量单元(Inertial Measurement Unit, IMU)记六轴传感器, 1149 | 包括3个加速度器和三个角速度器,记录关节点的相对运动,利用IK或优化的算法求解出每个 1150 | 关节的旋转,得到运动数据。但是惯性传感器存在飘逸问题,传感器的飘逸,对加速度,速度的积分 1151 | 得到传感器的位移,位移加上当前的位置得到全局的位置,而长时间的积分的误差导致位置存在偏差。 1152 | 可以利用重力传感器,磁场传感器,光流传感器,来增加全局变量,来缓解飘逸问题。 1153 | 1154 | 基于光学的设备,反/发光标记点,若干相机组成的系统,来重建三维环境,从而得到位置信息。 1155 | 比如:多视角几何通过几何相交等形状来确定点的位置。光学设备最大问题是场景问题,一般对场景 1156 | 要求比较高。脸的动捕,会标记更多点,并且会戴额外的相机,对准脸。光学动捕需要处理丢失数据, 1157 | 补点一般比较麻烦(按秒算钱,可以使用ML,AI来简化后处理流程)。 1158 | 1159 | 基于多视角相机来获取动捕数据,相机本身位置是固定的,可以省去标记点,但是准确度大大则扣。 1160 | 1161 | RGBD相机,提供深度信息,从而可以重建三维物体。 1162 | 1163 | 单目相机的动作估计。信息不全,把三维姿态映射为二维,本身存在奇异性问题, 1164 | 利用更多数据来缓解这个问题。 1165 | 1166 | 如何使用动捕数据?Motion Synthesis。 1167 | CMU:数据比较老,比较多,质量比较插。\href{https://paperswithcode.com/dataset/lafan1}{lafan1}:数据较全,质量ok。 1168 | 1169 | \begin{figure}[htbp] 1170 | \centering 1171 | \includegraphics[totalheight=2in]{"./image/UsingMotionData.png"} 1172 | \caption{Using Motion Data} \label{fig:UsingMotionData} 1173 | \end{figure} 1174 | 1175 | 动作连接,转移(Motion Transtion):将一段动作和另外一段动作衔接起来: 1176 | 在第一个动作选择一帧,在第二个动作中找到类似的一帧,然后将两帧在时间上做一个对齐,该帧 1177 | 称为切换帧,在各自切换帧前后分别选更多帧,做一个插值。$\boldsymbol{p}(t)= 1178 | (1-\phi(t))\boldsymbol{p}_0(i) + \phi(t)\boldsymbol{p}_1(i), 1179 | \boldsymbol{p}=(t_0, R_0, R_1, \dots)$,$i$表示帧数。实际情况,不一定两边 1180 | 都取若干帧,两者比例根据情况分析,最终达到足够平滑就行。但是这种方法,是不能处理 1181 | 脚底打滑。比如,一段是向左走,一段是向右走。更好的处理方式,是先做对齐,下一段 1182 | 动作和上一段目标动作做对齐。对齐什么,怎么对齐?对齐主要就是坐标系的选择,坐标系是跟着 1183 | 物体一起移动,定义方式多种,这里介绍"Facing(Hidding, Moving) Frame"坐标系(朝向,原点位置), 1184 | 原点跟随物体根关节一起移动,一般定义为根在地面的投影,局部坐标系的$z$轴一般定义为朝向, 1185 | 实际定义方式多种,比如速度的方向,面向的方向(局部坐标系的$z$在世界坐标系的方向), 1186 | $z$轴,主要是假设角色坐标系是一个$y-up$坐标系,符合Unity,Maya等软件, 1187 | 也符合渲染的设定。一般假设Facing Frame的旋转,是沿着$y$轴旋转,其次坐标原点只有平面$x-z$ 1188 | 的位移。$R=\theta \boldsymbol{e}_y, \boldsymbol{t}=(t_x, 0, t_y)$。 1189 | 1190 | Facing Frame把朝向定义为根关节局部坐标的$z$轴,因此Facing Frame坐标系的朝向(旋转)是把 1191 | 世界坐标的$z$轴转到局部坐标的$z$轴的矩阵。两个肩膀或两个髋关节的连线对应了局部坐标系 1192 | 的$x$轴,因此其平均值可以看作当前坐标系的$x$轴,也可以定义,世界坐标系的$x$轴与两髋关节 1193 | 的平均$x$方向的映射为朝向,另外一种思路,利用旋转的分解:$R=R_y R_{xz}$。 1194 | 知道了根关节的朝向$R$后,希望可以分解出$R_y$。局部坐标轴的$y$轴到世界坐标的$y$轴,利用 1195 | 叉乘,可以计算出两者的旋转矩阵$R^{\prime}$,有$R_y=R^{\prime}R$。因为$R^{\prime}$ 1196 | 使得局部的$y$与世界的$yt$重合,有了$R_y$,容易得到$R_{xz}=R^{T}_y R$。可以证明,$R_{xz}$ 1197 | 对应与在$x-z$平面上一向量的旋转。 1198 | 1199 | 有了如上定义,对于动作的每一帧,都可以计算其朝向坐标系$(R_0, t_0), (R_1, t_1)$。此时,将 1200 | 两个动作连接,先对给定的两切换帧做对齐处理,然后插值。这里注意,某一段动画,确定好 1201 | 切换帧的坐标系,其后帧均需按此坐标系表示。因此有 1202 | \begin{equation} 1203 | \begin{aligned} 1204 | R(i)&=R_0 R^{T}_1 R_1(i)\\ 1205 | \boldsymbol{t}(i)&=R_0 R_1 ^T (\boldsymbol{t}_1(i)-\boldsymbol{t}_1) + \boldsymbol{t}_0 1206 | \end{aligned} 1207 | \end{equation} 1208 | $i$表示后续帧,$R(i),t(i)$分别表示后续帧的朝向和位置。 1209 | 1210 | 有时候根关节的运动是没有的,比如动捕数据去掉了根关节的运动信息,动画 1211 | 师没有做根关节的运动。这种情况就只需要做姿态对齐。 1212 | 1213 | Path Fitting:让角色跟着轨迹走。对于去除了跟关节运动的动画, 1214 | 算出轨迹的方向(每一点的速度和位置),将其乘(?)到姿势上,就得到了沿着 1215 | 轨迹的动画了。 1216 | 1217 | 动作合成:有很多动作,根据这些动作来生成新的动作。Motion Graphs动作图(02年)。 1218 | 动作图的基本单元,动作片段。这就涉及到动作分割问题。主要思路是算动作 1219 | 的距离图(距离的定义多样),用距离图的局部最小值作分割判断。 1220 | 1221 | 1222 | 交互式动画Pipeline: 1223 | \begin{itemize} 1224 | \setlength{\itemindent}{2em} 1225 | \item 检查用户输入 1226 | \item 检查环境 1227 | \item 检查其他角色 1228 | \item 确定是否迁移 1229 | \item 获取下一个姿势 1230 | \item 处理姿势 1231 | \item 更新角色 1232 | \item 更新环境 1233 | \item \dots 1234 | \end{itemize} 1235 | 1236 | Motion Matching每一帧的流程: 1237 | \begin{itemize} 1238 | \setlength{\itemindent}{2em} 1239 | \item 检查用户输入 1240 | \item 根据输入,找到与当前状态最邻近的姿势 1241 | \item 将两姿势做光滑连接 1242 | \item next\_pose $=\min _{i \in \text { Dataset }}\left\|x_{\text {curr }}-x_i\right\|$ 1243 | \item $x:\text{特征向量}$ \begin{itemize} 1244 | \item root linear/angular velocity 1245 | \item position of end effectors w.r.t. root joint 1246 | \item linear/angular velocity of end effectors w.r.t. root joint 1247 | \item future heading position/orientation (e.g. in 0.5s, 1.0s, 1.5s, etc.) 1248 | \item foot contacts 1249 | \item ...... 1250 | \end{itemize} 1251 | \item 光滑混合 \begin{itemize} 1252 | \item \href{https://www.theorangeduck.com/page/spring-roll-call}{将当前姿势平滑地混合到目标姿势} 1253 | \end{itemize} 1254 | \end{itemize} 1255 | 1256 | \section{基于学习的动画} 1257 | 在常见人体的运动表示数据(姿态)组成的空间中,符合人体的运动一般是一个低维流形嵌入,否则 1258 | 随机采样的动作,就是有效的。这也说明,人体运动数据的自由度,没有其表示数据的自由度 1259 | 那么高。比如,一般走路时候,手是反向摆动,膝盖的不能向上旋转,人受物理定律限制, 1260 | 一般是走在路面的。主成分分析是一个比较简单的寻找低维空间算法。 1261 | 1262 | \subsection{主成分分析} 1263 | 找维度之间的相关性,将点$\boldsymbol{x}_i$在$\boldsymbol{u}$作投影,使得 1264 | 投影后的量最大限度的保持原有信息,分布范围比较大。 1265 | 1266 | 可用方差来刻画,求方向$\boldsymbol{u}$,已知点集$\boldsymbol{x}_i$在其上的 1267 | 投影$\boldsymbol{u}:w_i=\boldsymbol{x_i}\boldsymbol{u}$,使得 1268 | $w_i$的方差$\frac{1}{N}\sum_{i}(w_i - \bar{w})^2$最大。 1269 | 可以证明,令$X=\left[\begin{array}{c} 1270 | \left(\boldsymbol{x}_0-\overline{\boldsymbol{x}} 1271 | \right)^T \\ \left(\boldsymbol{x}_1- 1272 | \overline{\boldsymbol{x}}\right)^T 1273 | \\ \cdots \\ \left(\boldsymbol{x}_N-\overline{\boldsymbol{x}} 1274 | \right)^T\end{array}\right]$ 1275 | 则$\boldsymbol{u}$是$X^T X$的最大特征值对应的向量。 1276 | 1277 | 给定数据${\boldsymbol{x_i}}, \boldsymbol{x_i}\in \mathbb{R}^N$,PCA进一步 1278 | 拓展了上面的步骤,给出了最佳的$k$个方向。有: 1279 | 1280 | \begin{equation} 1281 | \boldsymbol{x}_i=\overline{\boldsymbol{x}}+\sum_{k=1}^n w_{i, k} \boldsymbol{u}_{\boldsymbol{k}} 1282 | \end{equation} 1283 | 其中$\boldsymbol{u}_k$称为第$k$个主成分。对应了第$k$大特征值的特征向量, 1284 | $w_{i, k}=(\boldsymbol{x_i} - \overline{x})\boldsymbol{u}_k$表示 1285 | $\boldsymbol{x_i} $在$\boldsymbol{u_i} $上的分数。 1286 | 1287 | 计算上,可以用协方差矩阵的特征分解来做。 1288 | \begin{equation} 1289 | \Sigma=X^T X=U\left[\begin{array}{llll} 1290 | \sigma_1^2 & & & \\ 1291 | & \sigma_2^2 & & \\ 1292 | & & \ddots & \\ 1293 | & & & \sigma_N^2 1294 | \end{array}\right] U^T 1295 | \end{equation} 1296 | 其中$\sigma_i \geq \sigma_j\geq 0\text{当}i0$,第二部分是阻尼项,和速度相关。$f_t$表示 2155 | 动摩擦带来的摩擦力,同时大小和速度相反,等于支持力乘以一个系数(静摩擦略)。 2156 | 为了不让效果下陷很明显,一般$k_p$需要很大,才能保证陷入的深度比较小,但太大会导致 2157 | 仿真的数值不稳定,为了让其稳定,需要设定更小的步长。 2158 | 2159 | 另外一种思路,把接触点建模成一个约束。主要关心这个点与接触面垂直的方向,在这个方向上,接触点的位置应该满足 2160 | 什么条件。 2161 | 在图\ref{fig:Contact}中,可以算出接触点在垂直方向上的速度$v_c$有如下关系: 2162 | \begin{equation} 2163 | \begin{aligned} 2164 | & x_c=x+r_c \\ 2165 | & v_c=v+\omega \times r_c=J_c\left[\begin{array}{l} 2166 | v \\ 2167 | \omega 2168 | \end{array}\right] \\ 2169 | & v_{c, \perp}=v+\omega \times r_c=J_{c, \perp}\left[\begin{array}{l} 2170 | v \\ 2171 | \omega 2172 | \end{array}\right] 2173 | \end{aligned} 2174 | \end{equation} 2175 | 在这里,我们需要要求在运动过程中,在竖直方向上的速度$v_c$不小于0,也就是说接触点可以 2176 | 向上移动,不能向下移动,同时接触力必须大于等于0,否则接触力$\lambda$将物体拉向地下,除此之外, 2177 | 如果接触点已经向外移动,脱离接触,这时候,就不能对其施加额外的力,否则该力将做功,这和 2178 | 约束力不做功矛盾。如果接触点的力$f_c>0$,此时物体不能移动(否则做功0),即$v_c = 0$。 2179 | 将这些所有约束一起考虑,得到如下方程: 2180 | \begin{equation} 2181 | \label{eq::LinearComplementary} 2182 | \begin{gathered} 2183 | {\left[\begin{array}{cc} 2184 | m I_3 & 0 \\ 2185 | 0 & I 2186 | \end{array}\right]\left[\begin{array}{c} 2187 | \dot{v} \\ 2188 | \dot{\omega} 2189 | \end{array}\right]+\left[\begin{array}{c} 2190 | 0 \\ 2191 | \omega \times I \omega 2192 | \end{array}\right]=\left[\begin{array}{l} 2193 | f \\ 2194 | \tau 2195 | \end{array}\right]+J_c^T \lambda} \\ 2196 | v_c=J_c\left[\begin{array}{c} 2197 | v \\ 2198 | \omega 2199 | \end{array}\right] \geq 0 \\ 2200 | \lambda \geq 0 \\ 2201 | v_c>0 \Rightarrow \lambda=0 \\ 2202 | \lambda>0 \Rightarrow v_c=0 2203 | \end{gathered} 2204 | \end{equation} 2205 | 方程\ref{eq::LinearComplementary}被称为线性互补方程(LCP问题),较难解。 2206 | 2207 | 碰撞建模(LCP方式),速度大于0,离开,不能深入,力只能推不能拉,因此大于等于0,力不能做功,因此力和速度不能同时不为0。 2208 | 当前只考虑了支持力,若考虑摩擦力,会更复杂,可参考Fast contact force computation for nonpenetrating 2209 | rigid bodies(David Baraff. SIGGRAPH ’94)。 2210 | 2211 | 总结,多刚体运动系统模拟方程: 2212 | \begin{equation} 2213 | \begin{aligned} 2214 | & I_n=R_n I_0 R_n^T \quad f_c=\text { Penalty } \quad(1.) \\ 2215 | & M_n\left(v_{n+1}-v_n\right) / h+C_n\left(v_n\right)=f_c+J_n^T \lambda \quad(2.) \\ 2216 | & J_n v_{n+1}=c_n \quad(3.) \\ 2217 | & x_{n+1}=x_n+h v_{n+1} \quad(4.) \\ 2218 | & q_{n+1}=q_n+\frac{h}{2} \bar{\omega}_{n+1} q \quad(5.) 2219 | \end{aligned} 2220 | \end{equation} 2221 | 2222 | \begin{figure}[htbp] 2223 | \centering 2224 | \includegraphics[totalheight=2in]{"./image/RigidBodySystem.png"} 2225 | \caption{Rigid Body System} \label{fig:RigidBodySystem} 2226 | \end{figure} 2227 | 首先(1.)根据刚体初始状态做初始化,若碰撞建模以Penalty方式建模,需要计算其接触力$f_c$的大小, 2228 | 然后(2. 3.)构造运动学方程,其中关节的约束(3.)比较关键,对该运动学方程求解,可以得到 2229 | 下一时刻的速度和角速度,最后分别对速度和角度做积分,得到新的位置,和新的旋转(四元数)信息。不断迭代,就完成了对一个 2230 | 刚体系统的仿真。 2231 | 2232 | 需要注意的是,在上面的方程中,关节之间的主动力没有写上,这个力是使得角色运动的力,如何计算这个力, 2233 | 将在下一节中说明。 2234 | 2235 | \begin{problemset} 2236 | \item 隐式积分的收敛性 2237 | \item 什么是刚体动力学方程? 2238 | \end{problemset} 2239 | 2240 | \chapter{角色控制} 2241 | \section{角色仿真和控制} 2242 | 如何在一个物理引擎中定义刚体?通常我们需要定义如下参数: 2243 | \begin{enumerate}[itemindent=2em] 2244 | \item 质量,转动惯量(Masses): $m, I$ 2245 | \item 位置,速度,朝向,角速度(Kinematics): $x, v, R, \omega$ 2246 | \item 几何(Geometry): 2247 | \begin{enumerate}[itemindent=2em] 2248 | \item Box,Sphere, Capsule, Mesh,\dots 2249 | \item Collision detection 2250 | \item Compute $m, I$ 2251 | \end{enumerate} 2252 | \end{enumerate} 2253 | 2254 | \begin{figure}[htbp] 2255 | \centering 2256 | \includegraphics[totalheight=2.5in]{"./image/SimulatingCharacter.png"} 2257 | \caption{Simulating a Character} \label{fig:SimulatingCharacter} 2258 | \end{figure} 2259 | 若直接把物体采用图\ref{fig:SimulatingCharacter}的方式建立在仿真器中,物体将会瘫在地上Rogdoll, 2260 | 虽然角色的动作是物理真实的,但因为缺乏控制,只能在重力的作用下,瘫在一起。 2261 | 2262 | 为了让一个角色(刚体)如我们希望的样子运动,需要给它施加一个外力。这就是物理仿真和关键帧动画的最大区别。 2263 | 在关键帧动画中,我们希望角色处于什么状态,直接将该角色的状态,位置设置成目标姿势就行,但物理仿真不能直接 2264 | 设置,只能通过在每个刚体上加上合适的力和力矩,这些力和力矩体现在运动方程中,会改变每一个刚体的速度和加速度, 2265 | 从而进一步影响角色的动作。通常而言对刚体施加的各种力会综合到施加在质心上的一个力,力矩的效果上。 2266 | 对一个真实的物体,不可能施加一个力矩,可以近似理解为一对大小相等,方向相反的力。 2267 | 2268 | 为了让一对刚体运动,可以在关节上加上一个力矩(joint torque,$\tau$),称为关节力矩。而关于关节力矩(Joint Torques)的含义,需要多说几句。 2269 | 一是什么是关节力矩,二是如何作用关节力矩。 2270 | 首先运动约束方程\ref{constrain::eqjoint}中运动部分没有关节,约束$Jv=0$是关于关节的约束, 2271 | 而关节力矩本质上是在关节上施加的力矩,运动方程中起作用的力$f,\tau$都加在刚体上。所有现在的问题是,在关节 2272 | 上加了力矩,这个力矩是如何加在它所对应的两个刚体上? 2273 | 2274 | 关节力矩是一个近似,一个机械臂的运动可由电机,马达来驱动,驱动带来的旋转效果,就是力矩。这个力矩往往 2275 | 是多个力$\sum_i f_i = 0$的综合效应,多个合力为0的力,在关节上产生一个大小为$\tau$的力矩,该力矩 2276 | 最终效果是在两个连接的刚体上,分别产生$\tau, -\tau$的力矩。 2277 | \begin{figure}[htbp] 2278 | \centering 2279 | \includegraphics[totalheight=1.5in]{"./image/taujoint.png"} 2280 | \caption{关节力矩} \label{fig:tau joint} 2281 | \end{figure} 2282 | 在方程上,可表示为 2283 | \begin{equation} 2284 | \begin{aligned} 2285 | M\left[\begin{array}{c} 2286 | \dot{v}_1 \\ 2287 | \dot{\omega}_1 \\ 2288 | \dot{v}_2 \\ 2289 | \dot{\omega}_2 2290 | \end{array}\right]+\left[\begin{array}{c} 2291 | 0 \\ 2292 | \omega_1 \times I_1 \omega_1 \\ 2293 | 0 \\ 2294 | \omega_2 \times I_2 \omega_2 2295 | \end{array}\right] & =\left[\begin{array}{c} 2296 | 0 \\ 2297 | \tau \\ 2298 | 0 \\ 2299 | -\tau 2300 | \end{array}\right]+J^T \lambda \\ 2301 | J v & =0 2302 | \end{aligned} 2303 | \end{equation} 2304 | 表示对这个关节对应的两个刚体分别加了$\tau, -\tau$的力矩。 2305 | 2306 | 上面只是说明了在一个关节加了一个力矩,问题是怎么加?在实际环境中,由控制器 2307 | Controller来完成。 2308 | \begin{figure}[htbp] 2309 | \centering 2310 | \includegraphics[totalheight=2.5in]{"./image/SimulatingControllingCharacter.png"} 2311 | \caption{Simulating and Controlling} \label{fig:SimulatingControlling} 2312 | \end{figure} 2313 | 控制器是根据角色当前的一些信息,位置,朝向,速度,角速度等,加上一些控制信号,比如走路到某地, 2314 | 做什么动作,根据这些信息,实时计算每个关节的关节力矩,然后将这些关节力矩发送给仿真器, 2315 | 产生最终的动作变化。 2316 | 2317 | 回看运动方程,$M \dot{\textcolor{blue}{\boldsymbol{v}}}+C(\boldsymbol{x}, \boldsymbol{v}) = 2318 | \textcolor{red}{\boldsymbol{f}}+\boldsymbol{J}^T \boldsymbol{\lambda}, J \boldsymbol{v} =0$ 2319 | 它是一个关于加速度和力的方程,这里就衍生出两个动力学概念:前向动力学,逆向动力学。 2320 | 前向动力学是说给定力force/torques,计算加速度,即$[\boldsymbol{x}, \boldsymbol{v}, \textcolor{red}{f}]\mapsto \dot{\boldsymbol{v}}$。 2321 | 逆向动力学是说给定加速度,计算力使得其产生这样的加速度,即 2322 | $[\boldsymbol{x}, \boldsymbol{v}, \textcolor{blue}{\dot{\boldsymbol{v}}}]\mapsto f$。 2323 | 2324 | 控制器根据角色的状态,计算每个关节的力矩,让角色动起来,其计算出来的关节力矩的总自由度(参数量), 2325 | 和定义角色状态所需要的参数量有一些关系。如果驱动力$f,\tau$的总自由度比关节的自由度小,这种被称为 2326 | 一个欠驱动(underactuated),若更大,则称为完全驱动fully-actuated。举例,机械臂是一个完全驱动系统, 2327 | 人体是一个欠驱动系统。对于人体来说,其根关节是不固定的,不能直接更改根关节的性质,另外人体的关节比 2328 | 刚体少一个,因此人体关节能提供的驱动数一定比刚体的状态自由度小。从另一个角度来说,人体在空间站中处于 2329 | 失重状态,若没有外界力作用,人只能处于漂浮状态,驱动力缺少对人体全身的控制,或者从走路的角度来看, 2330 | 人只能根据对地面施加力,根据地面的反作用力来控制人体质心的位置变化,从而移动,而不能直接控制质心的位置,来改变位置。 2331 | 2332 | 对于完全驱动系统,对任意加速度,都可以找到一个力,产生这样的加速度,而对于欠驱动系统,有很多加速度, 2333 | 我们都没办法找到这样的力,达到该效果。这样看来,对于欠驱动系统来说,我们往往是希望其不要落入无法控制的状态 2334 | 之下,而尽量保持在可以控制的状态中,比如怎么保持平衡,怎么走路等。 2335 | \begin{figure}[htbp] 2336 | \centering 2337 | \includegraphics[totalheight=1.5in]{"./image/actuatedun.png"} 2338 | \caption{actuated and unactuated} \label{fig:actuatedun} 2339 | \end{figure} 2340 | 2341 | \section{前向、反馈、比例微分、轨迹追踪、雅可比转置等控制} 2342 | 前向控制:执行预定义好的控制信号,没有考虑到当前角色的真实状态,若角色受到了扰动, 2343 | 偏离了原计划的状态,是没办法修正回来,比如没有考虑到人在床上躺着,但是却一直执行向前 2344 | 迈腿的动作,因此连站起来都不可能。反馈控制: 2345 | 会根据当前角色的状态,去调整,使得其不会偏离正常范围,往往还希望受到扰动。 2346 | 比例微分控制Proportional-Derivative Control(PD):通常是一个反馈控制。 2347 | \begin{figure}[htbp] 2348 | \centering 2349 | \includegraphics[totalheight=1.5in]{"./image/PDController.png"} 2350 | \caption{PD Controller} \label{fig:PDController} 2351 | \end{figure} 2352 | 比例微分在角色动画中,是一个前向控制,主要前向或反馈的看法是和控制目标有关系。 2353 | 2354 | 图\ref{fig:PDController} 中,控制器会根据物体当前的位置和速度,计算一个力,使其到达指定 2355 | 位置(高度)。若重力加速度恒定,力的大小容易计算,否则计算更加麻烦。比例微分不是这样的思路。 2356 | 它考虑当前位置和目标位置的偏差,根据偏差的大小来计算力的大小(线性拟合),也即比例控制$k_p$, 2357 | 仅仅考虑这一个,容易想到,当物体到达指定目标时,物体会继续运动,然后$k_p<0$,使得最终 2358 | 物体的运动轨迹程来回震荡的曲线,进而考虑速度,减去速度带来的继续运动效果,即微分控制$k_d$,可以理解为 2359 | 阻尼,逐渐耗散掉原始能量。这样最终的运动轨迹呈现图右边的情况。公式也可以写为$f=k_p e + k_d \dot{e}$, 2360 | 包含了位置误差控制和位置误差关于时间的微分控制。从图右边看到,比例微分最终效果所在位置和目标位置 2361 | 有一点小的误差(分析略),该误差一般称为稳态误差Steady-state error,可以使用PID(Proportional-Integral-Derivative Control)去掉稳态误差,不过, 2362 | 在动画领域,很少使用。该积分项和历史相关,在实现上存在一些额外的问题。所以实际应用中一般采用 2363 | 将$k_p$设置的比较小的办法来减少稳态误差,但同时会带来系统的数值计算不稳定,以及物体动作很僵硬的效果。 2364 | 不管怎么说,$k_p$一般不会设置得很大。另外$k_d$太小会震荡,太大会比较慢。 2365 | 2366 | 在角色控制中,$k_p$称为stiffness刚性参数,$k_d$称为dampping阻尼参数,图\ref{fig:CharacterPD}展示了对于一个刚体 2367 | 做PD控制的逻辑。 2368 | \begin{figure}[htbp] 2369 | \centering 2370 | \includegraphics[totalheight=1.5in]{"./image/CharacterPD.png"} 2371 | \caption{Character PD} \label{fig:CharacterPD} 2372 | \end{figure} 2373 | 2374 | \begin{equation} 2375 | \label{eq:PDCharacterJoint} 2376 | \tau=k_p(\overline{\bar{q}}-q)+k_d(\dot{\bar{q}}-\dot{q}) 2377 | \end{equation} 2378 | 这里我们需要按照\ref{eq:PDCharacterJoint}计算每个节点的关节力矩,使得子关节到达旋转多少角度,到达 2379 | 指定位置。自然而然,全身控制包含了对所有节点的控制,如图\ref{fig:FullCharacterPD}所示。实际经验告诉我们, 2380 | 比如对于一个角色在任意状态到T-pose的转换,若stiffness太小,则无法达到指定目标T-pose,若太大,会很快到达指定 2381 | 效果,但很僵硬。对于阻尼项dampping,若很小,就会有很明显的震荡,若很大,花费的时间会更长,但始终能达到指定 2382 | 目标。 2383 | \begin{figure}[htbp] 2384 | \centering 2385 | \includegraphics[totalheight=1.5in]{"./image/FullCharacterPD.png"} 2386 | \caption{Full Character PD} \label{fig:FullCharacterPD} 2387 | \end{figure} 2388 | 2389 | 给出了一个PD控制后,控制本身变为了去设计一个轨迹,然后追踪这个轨迹。每一个关节设计一个轨迹, 2390 | 就组成了全身跟踪\ref{fig:FullCharacterPD}。问题,如何去设计合理的轨迹? 2391 | \begin{figure}[htbp] 2392 | \centering 2393 | \includegraphics[totalheight=1.5in]{"./image/trajectoriesPD.png"} 2394 | \caption{trajectories PD} \label{fig:trajectoriesPD} 2395 | \end{figure} 2396 | 不过,实际设计出来的轨迹,用PD控制,往往会出现相位偏差问题,包括利用动捕数据,相位偏差导致 2397 | 动作完成时间滞后,或不能完整完成,或者大体动作像,但不对。更进一步会导致,后续跟踪无法去修复 2398 | 前面的误差。 2399 | 2400 | PD控制是一个反馈控制,而对于走路来说,反而是一个前向控制,因为反馈本身是对关节的力矩的反馈, 2401 | 但走路跟踪的状态实际是从一个目标轨迹上获取的,它和当前角色的状态没有关系。 2402 | 2403 | 以上跟踪会存在摔倒情况,原因在于没有对全局状态做跟踪。具体来说,对于关节$\tau_j$来说, 2404 | 会给子节点施加一个$\tau_j$的力矩,对父节点施加一个$-\tau_j$的力矩,对全身来说,总体 2405 | 力矩为0,因而无法改变全局(质心)朝向和位置。增加根节点力矩root force/torque是一个解决的方法。 2406 | 这种方法,虽然能使物体保持平衡,但会使得角色看起来被某种东西拽着,呈现一种非物理真实的效果, 2407 | 主要原因在于根关节没有父节点,施加在上面的力,实际会作用到全身。 2408 | 2409 | 在没有这些外力的情况下,如何使角色受控制,呈现一个平衡的效果? 2410 | 2411 | \subsection{更稳定的PD控制} 2412 | 一个简易的弹簧系统,就是一个PD控制系统。 2413 | 具体来说,质量,位置,速度分别为$m,x,v$的弹簧,进度系数,阻尼系数$k_p,k_d$,为了方便起见,设目标值为0, 2414 | 则有$f=-k_p x - k_d v$。设步长为$h$,其半隐式欧拉积分(用更新之后的速度去更新位置)为: 2415 | \begin{equation} 2416 | \begin{aligned} 2417 | & v_{n+1}=v_n+h \frac{f}{m} \\ 2418 | & x_{n+1}=x_n+h v_{n+1} 2419 | \end{aligned} 2420 | \end{equation} 2421 | 假设$m=1$,带入$f$,写为矩阵形式: 2422 | \begin{equation} 2423 | \begin{gathered} 2424 | {\left[\begin{array}{l} 2425 | v_{n+1} \\ 2426 | x_{n+1} 2427 | \end{array}\right]=\left[\begin{array}{cc} 2428 | 1-k_d h & -k_p h \\ 2429 | h\left(1-k_d h\right) & 1-k_p h^2 2430 | \end{array}\right]\left[\begin{array}{l} 2431 | v_n \\ 2432 | x_n 2433 | \end{array}\right]} \\ 2434 | \boldsymbol{s}_{n+1}=A \boldsymbol{s}_n \\ 2435 | \boldsymbol{s}_{n+1}=A^n \boldsymbol{s}_1 2436 | \end{gathered} 2437 | \end{equation} 2438 | 这里假设$A$有两个特征值,$\lambda_1, \lambda_2,\in \mathbb{C}$ 2439 | 于是有$A=P diag(\lambda_1, \lambda_2)P^{-1}$。 2440 | 2441 | 对$P$做变换,$z=P_{-1}s$,则$z_{n+1}=diag(\lambda_1, \lambda_2)z_n$,这里$s$为上面的矩阵$s_i$。 2442 | 于是上面矩阵关系可重新写为:$z_{n+1}=diag(\lambda_1, \lambda_2)^n z_1$。 2443 | 2444 | 容易得到$|\lambda_1|>1 \Rightarrow \lim_{x \to \infty}\|z_n\| \rightarrow \infty$,即系统不稳定。 2445 | 也就是为了保证仿真系统稳定,需要$A$的特征值都小于1。具体到$k_p,k_d,h$的关系,需要根据实际的数据,模拟出来, 2446 | 以确定$h$应该选取的范围。一般对于一个50kg的角色,可以设置$k_p = 200, k_d = 20$,在实际中可能存在几百步长以后, 2447 | 系统就不稳定,此时可能$h=0.5\thicksim 1ms, 1000\thicksim 2000Hz$,但$h$太小,意味着每一秒中需要计算更多次 2448 | 的计算,会带来运算量的增加。如何解决系统不稳定的问题? 2449 | 2450 | 将PD控制的某一部分(速度)变为隐式欧拉表达(可大大增加仿真系统的稳定性), 2451 | 此时系统方程变为: 2452 | \begin{equation} 2453 | \label{eq:stablepd} 2454 | \begin{aligned} 2455 | & v_{n+1}=v_n+h (-k_{p}x_n - k_{d}v_{n+1}) \\ 2456 | & x_{n+1}=x_n+h v_{n+1} 2457 | \end{aligned} 2458 | \end{equation} 2459 | 矩阵形式为: 2460 | \begin{equation} 2461 | \left[\begin{array}{l} 2462 | v_{n+1} \\ 2463 | x_{n+1} 2464 | \end{array}\right]=\frac{1}{1+h k_d}\left[\begin{array}{cc} 2465 | 1 & -k_p h \\ 2466 | h & 1+k_d h-k_p h^2 2467 | \end{array}\right]\left[\begin{array}{l} 2468 | v_n \\ 2469 | x_n 2470 | \end{array}\right] 2471 | \end{equation} 2472 | 2473 | 通常用显式的PD,可能需要很小的步长,1/1000秒,如果用stable PD(\ref{eq:stablepd}),则能使用 2474 | 更长的步长。通常来说,一个仿真系统,控制部分能做到120Hz,一般就是stable PD。 2475 | 2476 | \subsection{轨迹优化} 2477 | 使用动捕数据,会存在跟踪不上的问题,对于给定的轨迹,若沿着直接到目标的轨迹,也存在同样的问题。 2478 | 如何找到控制轨迹,使得能跟踪上,不存在偏差?可以在已有的轨迹上,做新的设计,或者轨迹优化 2479 | Trajectory Optimization。 2480 | 2481 | 找到轨迹: 2482 | \begin{enumerate}[itemindent=2em] 2483 | \item 仿真轨迹: $s_0, s_1, \ldots, s_T$ 2484 | \item 控制轨迹:$a_0, a_1, \ldots, a_{T-1}$ 2485 | \item 最小化目标函数:$$\min _{\left\{s_t, a_t\right\}} f\left(s_T\right)+\sum_{t=0}^{T-1} f\left(s_t, a_t\right)$$ 2486 | \item 同时满足限制: 2487 | $$ 2488 | \begin{aligned} 2489 | M \dot{v}+C(\boldsymbol{x}, \boldsymbol{v}) & =\boldsymbol{f}+J^T \lambda & & \text {运动方程} \\ 2490 | g(\boldsymbol{x}, \boldsymbol{v}) & \geq 0 & & \text { 限制条件 } 2491 | \end{aligned} 2492 | $$ 2493 | \end{enumerate} 2494 | 其中目标函数$f(s_T)$表示轨迹结束时的状态,以及求和部分表示每一时刻,加了多少力和当前的状态。 2495 | 而约束是因为角色运动需要对当前的状态施加力,根据仿真,得到下一时刻的状态。状态$s_i$和力$a_i$ 2496 | 之间是存在运动方程限制的,$g$表示其他可能的约束,比如关节约束,角色不能走到悬崖的边上(位置约束)。 2497 | 2498 | 举个例子,对图\ref{fig:CharacterPD}中滑杆运动,希望其运动轨迹为$\sin$函数,此时目标轨迹应该为什么? 2499 | 沿用上面找轨迹的建模思路有: 2500 | \begin{equation} 2501 | \begin{array}{ll} 2502 | \min _{\left\{x_n, v_n, \bar{x}_n\right\}} & \sum_{n=0}^N\left(\sin \left(t_n\right)-x_n\right)^2+w_{\bar{x}} \sum_{n=0}^N \bar{x}_n^2 \\ 2503 | \text { s.t. } & v_{n+1}=v_n+h\left(k_p\left(\bar{x}_n-x_n\right)-k_d v_n\right) \\ 2504 | & x_{n+1}=x_n+h v_{n+1} 2505 | \end{array} 2506 | \end{equation} 2507 | 这里$\sum_{n=0}^N \bar{x}_n^2$为一个位置正则项,限制其值比较小,使得轨迹比较平滑。限制条件s.t中,在优化领域被称为 2508 | 硬约束,需要两边一定相等,软约束即为两边约等就行,可写为如下: 2509 | \begin{equation} 2510 | \begin{aligned} 2511 | \min _{\left.c_n, v_n, \bar{x}_n\right\}} & \sum_{n=0}^N\left(\sin \left(t_n\right)-x_n\right)^2+w_{\bar{x}} \sum_{n=0}^N \bar{x}_n^2 \\ 2512 | & +w_v \sum_{n=0}^N\left(v_{n+1}-v_n-h\left(k_p\left(\bar{x}_n-x_n\right)-k_d v_n\right)\right)^2 \\ 2513 | & +w_x \sum_{n=0}^N\left(x_{n+1}-x_n-h v_{n+1}\right)^2 2514 | \end{aligned} 2515 | \end{equation} 2516 | 式中约束部分的权重参数$w_v,w_x$刻画了软的程度,越大越硬,极限情况极为硬约束。 2517 | 2518 | 在实际工作中,会面临上面优化目标的参数太大的问题,对于这种情况,还有一种被称为Collocation method的方法, 2519 | 它优化的不是实际的参数,对给定目标轨迹,同时优化一系列带参数的曲线,而曲线本身往往是多项式 2520 | 或样条曲线,优化曲线转化为优化样条等曲线的参数,从而达到参数降维的目的。 2521 | 具体优化目标可表达为: 2522 | \begin{equation} 2523 | \begin{aligned} 2524 | & \min _{\left\{x_n, v_n, \bar{x}_n\right\}} \sum_{n=0}^N\left(\sin \left(t_n ; \theta\right)-x\left(t_n ; \theta\right)\right)^2+w_{\bar{x}} \sum_{n=0}^N \bar{x}^2\left(t_n ; \theta\right) \\ 2525 | & \text { s.t. } \quad \begin{aligned} 2526 | v\left(t_{n+1} ; \theta\right) & =v\left(t_n\right) \\ 2527 | & +h k_p\left(\bar{x}\left(t_n ; \theta\right)-x\left(t_n ; \theta\right)\right)-h k_d v\left(t_n ; \theta\right) \\ 2528 | \qquad x\left(t_{n+1} ; \theta\right) & =x\left(t_n ; \theta\right)+h v\left(t_{n+1} ; \theta\right) 2529 | \end{aligned} 2530 | \end{aligned} 2531 | \end{equation} 2532 | 其中优化参数为${x_n, v_n, \bar{x}_n}$。 2533 | 2534 | 常见的优化方法:梯度下降,牛顿法,拟牛顿法。但对于上面这个问题,是一个典型的二次型问题, 2535 | 可以具体解出来。 2536 | 2537 | 而在实际工作中,我们会面临优化问题常常是高度非线性的,比如角色运动,具体计算的时候,系统是一个黑盒子,无法得到具体 2538 | 的梯度值,或者高维曲离散面震荡明显,梯度不可靠等问题。另一方面,很多情况,我们都很难对运动本身做准确的建模,比如人手 2539 | 的软的,拿筷子很容易,但在仿真系统中,用刚体拿筷子,就比较麻烦。这种时候,没具体模型,无法计算梯度。 2540 | 因此,基于梯度的优化算法,也有它的局限性。无法利用梯度优化的问题,可以选择启发式方法。 2541 | 2542 | 非梯度优化的迭代方法: 2543 | \begin{enumerate}[itemindent=2em] 2544 | \item 目标:找一个变量$\boldsymbol{x}$最小化$f(\boldsymbol{x})$ 2545 | \item 首先初始化$\boldsymbol{x}$(角色跟踪动捕数据,动捕数据本身可作为初始估计) 2546 | \item 迭代: 2547 | \begin{itemize}[itemindent=2em] 2548 | \item 根据当前估计$\boldsymbol{x}$,生成一些样本点${\boldsymbol{x_i}}$ 2549 | \item 计算每个样本点的函数值$f_i = f(\boldsymbol{x_i})$ 2550 | \item 更新$\boldsymbol{x}$的估计 2551 | \end{itemize} 2552 | \end{enumerate} 2553 | 有贝叶斯优化,进化算法(CMA-ES),随机优化,蒙特卡洛序列等。 2554 | 2555 | 缺点:随机采样,通过若干样本点,来更新,速度慢,精确性比较比较差。 2556 | 2557 | \subsubsection{CMA-ES} 2558 | [Hansen 2006, The CMA evolution strategy: a comparing review]。 2559 | 对于黑盒子也可以使用,并且有些比较复杂的动作,也能有较好的效果。 2560 | \begin{figure}[htbp] 2561 | \centering 2562 | \includegraphics[totalheight=1.5in]{"./image/CMA-ES.png"} 2563 | \caption{CMA-ES} \label{fig:CMA-ES} 2564 | \end{figure} 2565 | CMA-ES每次对给定参数,都需要做仿真,过程比较漫长,若仿真的轨迹比较长, 2566 | 就更难收敛。 2567 | 2568 | 其他略。 2569 | \subsubsection{SAMCON} 2570 | SAmpling-based Motion CONtrol [Liu et al. 2010, 2015], 2571 | 对动捕数据的轨迹跟踪。每次采样,只考虑下一帧,即优化一步。 2572 | 本质是一个序列蒙特卡洛方法。 2573 | \begin{figure}[htbp] 2574 | \centering 2575 | \includegraphics[totalheight=1.5in]{"./image/SAMCON.png"} 2576 | \caption{SAMCON} \label{fig:SAMCON} 2577 | \end{figure} 2578 | 2579 | 从初始状态开始,采一些样本点做仿真,从仿真的结果中取出 2580 | 一小部分作为新的出发点,再采样,以此类推。 2581 | 2582 | 其他略。 2583 | \subsection{反馈控制} 2584 | 在前向控制中,对给定轨迹,若加一点扰动,角色会快速偏离原始轨迹,为了解决这个 2585 | 问题,需要引入反馈控制,即每一时刻有一个反馈的策略,这个策略会自动根据当前状态 2586 | 进行更新(根据原始状态和扰动状态的偏差),这样保持运动在原始轨迹附近。这样每一步 2587 | 就会有相应的反馈策略。 2588 | \subsubsection{Static Balance} 2589 | 静态平衡,让PD控制的目标是一个T-pose,去跟踪一个单一的姿势,角色虽然能保持住跟踪的姿势, 2590 | 但会逐渐趋于摔倒状态。这就是缺少一个反馈控制,在角色将要摔倒的时候,将其更正回来。 2591 | 比如让角色站立保持不动,可以使用左右摇晃,或者推一下,保持不摔倒,或者原地下蹲等,原点晃肩。 2592 | 2593 | 这里面有个关键概念:平衡。通常而言,静止物体的重心在地面的投影点在支撑面内,就可以保持平衡。 2594 | 支撑面指物体于地面的接触面的凸包。比如人体,就是两脚所围住的面积。 2595 | 2596 | 用PD控制去计算一个反馈力矩: 2597 | \begin{equation} 2598 | \label{eq:feedbackf} 2599 | \tau=k_p(\overline{\boldsymbol{c}}-\boldsymbol{c})-k_d \dot{\boldsymbol{c}} 2600 | \end{equation} 2601 | 2602 | 其中$\dot{\boldsymbol{c}}$表示质心的速度,$\overline{\boldsymbol{c}}$表示支撑平面的中心。 2603 | 对于保持某种姿势的角色来说,其已经有其他的关节力矩来做 2604 | 这件事,要让角色保持平衡,可在其他关节(踝关节,髋关节)上施加上面的力矩,使得质心的投影点在支撑面内。 2605 | 除了PD控制策略,还可以使用雅可比转置控制。 2606 | 2607 | \subsection{Jacobian Transpose Control} 2608 | \begin{figure}[htbp] 2609 | \centering 2610 | \includegraphics[totalheight=1.8in]{"./image/JacobianTransposeControl.png"} 2611 | \caption{Jacobian Transpose Control} \label{fig:JacobianTransposeControl} 2612 | \end{figure} 2613 | Jacobian Transpose Control 雅可比转置控制:用关节力矩来等价的实现 2614 | 一个力的效果。这个力并没有真正加在关节上,因此也被称为虚力。具体效果可以用做功来衡量: 2615 | 功率=力*速度=力矩*角速度。本质来说就是IK问题,通过对前向运动学进行求导来得到。即 2616 | $P=\boldsymbol{f}^{T}\dot{\boldsymbol{x}}=\boldsymbol{\tau}^{T}\dot{\boldsymbol{\theta}},$ 2617 | 前向动力学$\boldsymbol{x}=g(\boldsymbol{\theta})\Rightarrow \dot{\boldsymbol{x}}=J\dot{\boldsymbol{\theta}}, 2618 | J=\frac{\partial g}{\partial \boldsymbol{\theta}}$。 2619 | 两者结合得到$\boldsymbol{f}^T J \dot{\boldsymbol{\theta}}=\boldsymbol{\tau}^T \dot{\boldsymbol{\theta}}$, 2620 | 也即: 2621 | \begin{equation} 2622 | \boldsymbol{\tau} = J^T \boldsymbol{f} 2623 | \end{equation} 2624 | 加力点$\boldsymbol{x}$的前向运动学的雅可比矩阵的转置乘以$\boldsymbol{f}$得到力矩。 2625 | 2626 | 进一步推导可得到如下结论: 2627 | \begin{equation} 2628 | \tau_i=\left(x-p_i\right) \times f 2629 | \end{equation} 2630 | 即每个关节的力矩等于关节点到加力点的向量叉乘力$\boldsymbol{f}$。 2631 | 2632 | 利用这个结论,可以改写上节中保持静态平衡所使用的PD控制方法。通过虚力的方式, 2633 | 采用公式\ref{eq:feedbackf}来计算一个力,而非力矩。具体来说,假设力(非真实力,加不了 2634 | 通过一些关节力矩实现类似的效果)加在质心上,使得质心被推到目标位置$\bar{\boldsymbol{c}}$。 2635 | 类似的,用质心减去对应关节(髋关节,膝关节,踝关节)的位置,然后叉乘$\boldsymbol{f}$。 2636 | 除此之外,目标$\bar{\boldsymbol{c}}$可以有高度,当高度降低,可以实现角色下蹲的效果。 2637 | 2638 | 但上面的方式,实现的平衡比较弱,能实现比较稳的站住,当有外力推角色的时候,力稍微大一点,可能就很难 2639 | 保持平衡。 2640 | 2641 | 进一步工作参考:[Macchietto et al. 2009 - Momentum Control for Balance]。 2642 | 2643 | \section{用简化模型学习走路} 2644 | 前几节中,主要讲了如何仿真一个虚拟角色,如何控制一个角色,让角色保持一个姿势,如何利用反馈控制, 2645 | 来保持原地的静态平衡,也能让角色在平衡中,做一些简单的动作。基本上是动捕数据的一个跟踪效果,动作本身所 2646 | 涉及的很多细节并没有考虑进去,这些细节用纯粹的基于运动学的方式难以实现,比如人在挥手的时候, 2647 | 手上的角动量会变化,身体会反抗,就会有一些微弱的摇晃。 2648 | 2649 | 除了站在原地平衡之外,这一节主要讲怎么走路。移动在运动学上,基于运动合成的方法,给角色设置合 2650 | 适的姿态(位置变化别太远)就可以移动了。但基于物理学(动力学)的方法,我们不能控制一个角色的全局位置, 2651 | 只能通过让角色和地面的交互,让地面的反作用力来驱动角色运动。 2652 | 2653 | 粗略来说,在2000年的前十五年中,基于仿真的角色动画方向研究的主要内容就是走路。走路难度还是挺大的。 2654 | 2655 | Static Balance 只能实现比较慢的走路,单单考虑质心,来保持身体 2656 | 的平衡,更加丰富的走路,需要考虑角动量。 2657 | 2658 | 本节主要涉及下面三篇文章: 2659 | \begin{itemize}[itemindent=2em] 2660 | \item ZMP(Zero-Moment Point) 2661 | \item Inverted Pendulum Model(2010) 2662 | \item SIMBICON(Simple Biped locomotion Control, 2007) 2663 | \end{itemize} 2664 | 2665 | 简化模型:找到最能影响角色平衡的量,比如质心,来构建一个抽象模型。 2666 | 2667 | Simplify humanoid / biped robot into an abstract model 2668 | \begin{itemize}[itemindent=2em] 2669 | \item Often consists of a CoM and a massless mechanism 2670 | \item Need to map the state of the robot to the abstract model 2671 | \end{itemize} 2672 | 2673 | Plan the control and movement of the model 2674 | \begin{itemize}[itemindent=2em] 2675 | \item Optimization 2676 | \item Dynamic programming 2677 | \item Optimal control 2678 | \item MPC 2679 | \end{itemize} 2680 | 2681 | Track the planned motion of the abstract model 2682 | \begin{itemize}[itemindent=2em] 2683 | \item Inverse Kinematics 2684 | \item Inverse Dynamics 2685 | \end{itemize} 2686 | 2687 | \subsection{Zero-Moment Point} 2688 | 当人在走路时,如\ref{fig:ZMP-GRF}所示,可将脚所受的力分解如右边小图。 2689 | \begin{figure}[htbp] 2690 | \centering 2691 | \includegraphics[totalheight=1.6in]{"./image/ZMP-GRF.png"} 2692 | \caption{ZMP-GRF} \label{fig:ZMP-GRF} 2693 | \end{figure} 2694 | 上半身的移动,主要也就是踝关节处所施加的力。对于脚来说,是一个脚平面与大地平面 2695 | 的相互作用。对于脚平面上的任意点$p_i$,其有地面力$f_i$,可分解为$f^y _l + f^{xz} _l$, 2696 | 分别称为支持力,摩檫力。对于脚平面上的所有点的力分析,等价于对某一个点$p$的力分析。 2697 | 按照前面对刚体的处理方式,假设地面是平整的且脚面点到点$p$是在同一水平面上,则 2698 | 有$\tau_{GRF}=\sum_{i}(p_i - p)\times f_i$。由$f_i$的分解, 2699 | 有 2700 | \begin{equation} 2701 | \begin{aligned} 2702 | \boldsymbol{\tau}_{\mathrm{GRF}} & =\sum_i\left(\boldsymbol{p}_i-\boldsymbol{p}\right) \times\left(f_i^y+f_i^{x z}\right) \\ 2703 | & =\sum_i\left(\boldsymbol{p}_i-\boldsymbol{p}\right) \times f_i^y+\sum_i\left(\boldsymbol{p}_i-\boldsymbol{p}\right) \times f_i^{x z} 2704 | \end{aligned} 2705 | \end{equation} 2706 | 得到力矩在水平(平行)和竖直(垂直)两个方向上的分解。这里$P$可任意选择,水平向的力具有旋转效果, 2707 | 我们可以选择一个点,使得$\tau_{GRF}^{xz}=0$。该点被称为Center of Pressure,解算方程,容易得到 2708 | $\mathrm{p}=\frac{\sum_{i}\mathrm{p_i}\mathrm{f_i}^y}{\sum_{i}\mathrm{f_i}^y}$ 2709 | $\Rightarrow \tau_{\mathrm{GRF}}^{xz}=0$, 2710 | 如此选点(Center of Pressure),可只关心竖直方向上的力。 2711 | 2712 | 在水平方向上,所有力,力矩作用在脚上,使得脚在水平上保持不移动,因此有静态等式: 2713 | $f_{ankle} + f_{GRF} + mg = 0$,同时关于参考点$o$的动量也有:$(u -o)\times f_{ankle} + (p-o)\times f_{GRF} 2714 | + (x-o)\times mg + \tau_{GRF}^{y} + \tau_{ankle} = 0 $, 2715 | 参数相关含义参考图\ref{fig:ZMP-stance-phase}。 2716 | \begin{figure}[htbp] 2717 | \centering 2718 | \includegraphics[totalheight=1.6in]{"./image/ZMP-stance-phase.png"} 2719 | \caption{ZMP stance phase} \label{fig:ZMP-stance-phase} 2720 | \end{figure} 2721 | 若只考虑水平方向,则上式去掉$\tau_{GRF}^{y}$,将力$f$映射到水平面$xz$,从而有 2722 | \begin{equation} 2723 | \begin{array}{r} 2724 | \left((\boldsymbol{u}-\boldsymbol{o}) \times \boldsymbol{f}_{\text {ankle }}\right)^{x z}+\left((\boldsymbol{p}-\mathbf{o}) \times \boldsymbol{f}_{\mathrm{GRF}}\right)^{x Z} \\ 2725 | \quad+(\boldsymbol{x}-\boldsymbol{o}) \times m \boldsymbol{g}+\boldsymbol{\tau}_{\text {ankle }}^{x z}=\mathbf{0} 2726 | \end{array} 2727 | \end{equation} 2728 | 上式中,$x$是脚掌的质心,已知$u, o, x,f_{*}$,则$p$点可计算出来$(?,?,0)$。 2729 | 点$p$被称为零力矩点(Zero-Moment Point, ZMP),因为它假设了旋转力矩$\tau_{GRF}^{xz}=0$, 2730 | 同时和力矩的水平分量也等于$0$。 2731 | 2732 | 零力矩点成立的条件是$p$在支撑面(support polygon)内。 2733 | 2734 | 如果人正处于摔倒状态,上面计算得到的$p$点,将在支撑面的外面,此时 2735 | $\tau_{\mathrm{GRF}}^{xz} \neq 0$。此时水平方向的合外力矩不为0, 2736 | 会让脚向外反转,抬起来,使人更容易摔倒。 2737 | 2738 | 总的来说,ZMP是一个指标,只要能保证$p$点在支撑面内,就可以实现让角色处于动态平衡的状态。 2739 | 但是如何控制ZMP就相对比较复杂,需要较大篇幅,比如一门课,才能说明白。 2740 | 2741 | ZMP控制移动得比较慢,它假设脚掌与地面平行,对机器人来说,需要让膝盖弯曲,来控制零力矩点 2742 | 在支撑面内。 2743 | \subsection{Inverted Pendulum Model} 2744 | IPM称为倒立摆模型:轴在地下,物体在顶部,受重力影响,向下旋转移动。从图\ref{fig:IPM}可 2745 | 得到$\ddot{\theta}=\frac{g}{\ell} \sin \theta$。 2746 | \begin{figure}[htbp] 2747 | \centering 2748 | \includegraphics[totalheight=1.6in]{"./image/IPM.png"} 2749 | \caption{IPM} \label{fig:IPM} 2750 | \end{figure} 2751 | 控制过程,由在轴部位移动来控制。比如轴底加一辆小车,杆往左下滑动,车就往左边移动。 2752 | 比如把扫帚放在手指上保持扫帚的平衡就是IPM控制。 2753 | 2754 | 这里简单介绍一篇比较经典的使用IPM思路来控制角色的工作:[Coros et al. 2010 - Generalized Biped Walking Control]。 2755 | 2756 | 结合图\ref{fig:IPM-Control}来说明该文章的主要思路: 2757 | \begin{itemize}[itemindent=2em] 2758 | \item 将角色的质心和站立脚映射为IPM(重心到支撑点的连线构成倒立摆),人在摔倒的时候, 2759 | 速度会越来越大 2760 | \item 通过当前质心的速度,计算下一个脚步的位置,使质点位于摆的顶部,此时刚好稳定,速度为$0$, 2761 | \item 脚当前位置和之前的位置,可以使用样条插值,做一个脚部轨迹 2762 | \item 使用 IK 计算目标姿势(通过质心和脚的位置算出一个目标姿势,通过PD控制,实现对角色的控制) 2763 | \end{itemize} 2764 | 2765 | \begin{figure}[htbp] 2766 | \centering 2767 | \includegraphics[totalheight=1.6in]{"./image/IPM-Control.png"} 2768 | \caption{IPM Control} \label{fig:IPM-Control} 2769 | \end{figure} 2770 | 2771 | 根据能量守恒定律,动能转化为势能,可以得到如下关系: 2772 | \begin{equation} 2773 | \begin{aligned} 2774 | & \frac{1}{2} m v^2+m g h=\frac{1}{2} m v^{\prime 2}+m g h^{\prime} \\ 2775 | & v^{\prime}=0 \text { and } h^{\prime}=L=\sqrt{h^2+d^2} \\ 2776 | & d=v \sqrt{h / g+v^2 /\left(4 g^2\right)} . 2777 | \end{aligned} 2778 | \end{equation} 2779 | 得到脚步长$d$。 2780 | 2781 | 该方法比较通用,鲁棒,在转化为IK去计算姿势这一块,比较复杂。 2782 | 2783 | \subsection{SIMBICON} 2784 | 文章[SIMBICON:SIMple BIped Locomotion CONtrol. Yin et al. 2007] 2785 | 第一个实现了比较鲁棒的步行控制,可以通过非常简单的反馈,实现步行控制。 2786 | 主要思路可以简单理解为跟踪控制+反馈。 2787 | 2788 | 具体的略。 2789 | 2790 | 该方法动作质量上稍微有点不尽人意。 2791 | 2792 | \begin{problemset} 2793 | \item 实现一个站立控制,包含PD Control,Hand of God,Static Balance 2794 | \item 实现走路控制器(Bonus) 2795 | \item 简化模型基本都是在走路的基础上建立起来的,很难迁移到其他动作上,对于更广泛的动作如何实现控制? 2796 | 甚至生成新的动作。(强化学习对控制策略的学习,会带来更好的效果,但可解释性比较差) 2797 | \end{problemset} 2798 | 2799 | \chapter{最优控制和强化学习} 2800 | 本章从与强化学非常相关的理论最优化控制出发,来说明强化学习和传统方法的一些区别。 2801 | \section{最优控制} 2802 | 前面讲到,通过对角色施加力和力矩,来控制角色,实现对角色的驱动。比如需要做一个后空翻, 2803 | 需要找到一些自动控制策略,比如PD target,然后去执行这个PD target让角色的仿真轨迹 2804 | 和目标轨迹相同。为了找到这些轨迹,可以把控制建模成优化问题。在每一帧,都可以计算当前帧 2805 | 和参考数据的偏差,将这些偏差加起来,就构成了一个优化问题。优化的对象是每一帧需要施加的力。 2806 | 在具体的仿真过程中,需要每一步都满足简化的物理规律。 2807 | 2808 | 具体来讲就是找到一组控制序列${a_t}$从初始状态$s_0$开始,生成状态序列${s_t}$, 2809 | 最小化目标函数 2810 | \begin{equation} 2811 | \label{eq:trajectory} 2812 | \min h\left(s_T\right)+\sum_{t=0}^{T-1} h\left(s_t, a_t\right) 2813 | \end{equation} 2814 | 使得对$[0, T]$中所有时间,满足$f(s_t, a_t) - s_{t+1} = 0$。同时角色本身满足运动方程: 2815 | \begin{equation} 2816 | \label{eq:trajectorycontrol} 2817 | \begin{gathered} 2818 | M \dot{\boldsymbol{v}}+C(\boldsymbol{x}, \boldsymbol{v})=\boldsymbol{f}+J^T \lambda \\ 2819 | g(\boldsymbol{x}, \boldsymbol{v}) \geq 0 2820 | \end{gathered} 2821 | \end{equation} 2822 | 2823 | 若只优化轨迹,称为开环控制,或者前馈控制。 2824 | 前馈控制:根据一些预定义的控制策略做修改,不根据状态的改变而改变。因此对于扰动,就比较困难。 2825 | 2826 | \begin{figure}[htbp] 2827 | \centering 2828 | \includegraphics[totalheight=1.6in]{"./image/Feedforward-Control.png"} 2829 | \caption{Feedforward Control} \label{fig:Feedforward-Controll} 2830 | \end{figure} 2831 | 2832 | 反馈控制:根据当前角色受到扰动之后的状态,更新控制策略(信号?)的变化,从而保证角色在控制过程 2833 | 中能经得起干扰。 2834 | \begin{figure}[htbp] 2835 | \centering 2836 | \includegraphics[totalheight=1.6in]{"./image/Feedback-Control.png"} 2837 | \caption{Feedback Control} \label{fig:Feedback-Controll} 2838 | \end{figure} 2839 | 反馈控制也能描述成轨迹优化的形式,区别在于:轨迹优化优化的是每一时刻的控制信号,但反馈控制 2840 | 优化的是控制策略。控制本身是一个函数,可以由该函数,根据角色当前的状态,来计算一个控制信号。 2841 | 2842 | 不管是轨迹优化(开环控制),还是反馈控制,都属于最优控制的几种不同类型。区别可由图\ref{fig:OptimalControl} 2843 | 可见: 2844 | \begin{figure}[htbp] 2845 | \centering 2846 | \includegraphics[totalheight=1.6in]{"./image/OptimalControl.png"} 2847 | \caption{Optimal Control} \label{fig:OptimalControl} 2848 | \end{figure} 2849 | 开环只需要知道起始点和目标点,好处是简单,坏处是禁不起扰动。反馈控制计算了状态空间中的所有状态, 2850 | 更加困难,也更加使用,其理论基础来源于动态规划。 2851 | 2852 | 带约束的优化问题,除了前面讲的思路,还有经典的拉格朗日乘子法:对于目标函数$f(x)$ 2853 | 和限制条件$g(x)=0$其拉格朗日函数为: 2854 | \begin{equation} 2855 | L(x, \lambda)=f(x)+\lambda^T g(x) 2856 | \end{equation} 2857 | 拉格朗日解是原始问题的必要条件,因为解在目标函数和条件函数的梯度是平行关系。 2858 | 2859 | 优化目标\ref{eq:trajectory},\ref{eq:trajectorycontrol}的拉格朗日乘子函数有: 2860 | \begin{equation} 2861 | L(s, a, \lambda)=h\left(s_T\right)+\sum_{t=0}^{T-1} h\left(s_t, a_t\right)+\lambda_{t+1}^T\left(f\left(s_t, a_t\right)-s_{t+1}\right) 2862 | \end{equation} 2863 | 可以推出: 2864 | \begin{equation} 2865 | \begin{aligned} 2866 | \frac{\partial L}{\partial s_T} & =\frac{d h}{d s}\left(s_T\right)-\lambda_T=0 \\ 2867 | \frac{\partial L}{\partial s_t} & =\frac{\partial h}{\partial s}\left(s_t, a_t\right)+\left(\frac{\partial f}{\partial s}\left(s_t, a_t\right)\right)^T \lambda_{t+1}-\lambda_t=0 \\ 2868 | \frac{\partial L}{\partial a_t} & =\frac{\partial h}{\partial a}\left(s_t, a_t\right)+\left(\frac{\partial f}{\partial a}\left(s_t, a_t\right)\right)^T \lambda_{t+1}=0 \\ 2869 | \frac{\partial L}{\partial \lambda_t} & =f\left(s_t, a_t\right)-s_{t+1}=0 2870 | \end{aligned} 2871 | \end{equation} 2872 | 整理一下,可得到: 2873 | \begin{equation} 2874 | \label{eq:PMP} 2875 | \begin{array}{r} 2876 | s_{t+1}=f\left(s_t, a_t\right) \\ 2877 | \text {"costate" dynamics } \quad \lambda_T=h_s^{\prime}\left(s_T\right) \\ 2878 | \lambda_t=h_s^{\prime}\left(s_t, a_t\right)+\left(f_s^{\prime}\left(s_t, a_t\right)\right)^T \lambda_{t+1} \\ 2879 | a_t=\arg \min _a h_a^{\prime}\left(s_t, a_t\right)+\left(f_a^{\prime}\left(s_t, a_t\right)\right)^T \lambda_{t+1} 2880 | \end{array} 2881 | \end{equation} 2882 | 方程\ref{eq:PMP}称为Pontryagin’s Maximum Principle(PMP)。在最优控制理论中, 2883 | 开环控制最优的必要条件即为PMP。 2884 | 2885 | PMP方程,最早是研发控制火箭而提出的方程,对于比较简单的系统分析起来比较方便。 2886 | PMP方程主要由一个前向过程+两个反向过程+一个更新过程。分别对应到方程中 2887 | $\{1\},\{2,3\},\{4\}$式子,最终优化出一个满足目标的轨迹$\{a_t\}$。 2888 | 2889 | 2890 | 对于闭环控制,见\ref{fig:OptimalControl}右侧,可以从一个简单的例子来考虑这个问题: 2891 | 找最短路径。 2892 | \begin{figure}[htbp] 2893 | \centering 2894 | \includegraphics[totalheight=1.6in]{"./image/DynamicProgramming.png"} 2895 | \caption{Dynamic Programming mini path} \label{fig:DynamicProgramming} 2896 | \end{figure} 2897 | 找到一个策略$a_t = \pi(s_t)$,最小化$J(s_0) = \sum_{t=0}h(s_t, a_t)$,并且有 2898 | $s_{t+1}=f(s_t, a_t)$。 2899 | 对于图\ref{fig:DynamicProgramming}中,可以将$a_t$理解为信号,$s_t$理解为状态。 2900 | 2901 | 问题:什么条件下,$\pi$是最优策略?答案是Bellman’s Principle。它告诉我们, 2902 | 最优策略$\pi$,对于任何子问题,所给出的策略,也是最优的(动态规划中的最优子结构)。 2903 | 2904 | 从Bellman’s Principle出发,定义价值函数$V(s_i)$,表示从状态$s_i ^{j}$出发,到 2905 | 目标位置,在最优策略$\pi(s_t)$下,得到的轨迹中所有边的代价和。 2906 | \begin{figure}[htbp] 2907 | \centering 2908 | \includegraphics[totalheight=1.6in]{"./image/BellmanPrinciple.png"} 2909 | \caption{Bellman Principle} \label{fig:BellmanPrinciple} 2910 | \end{figure} 2911 | 假设我们得到了节点的价值函数,对于图\ref{fig:BellmanPrinciple}来说, 2912 | 图中各节点数字均得到,根据最优子结构性质,要找到整体的最优策略, 2913 | 只需要计算$a^{j}_0 + s^{j}_0$,哪一条最小,即可得到答案。也即知道价值函数 2914 | 后,只需要计算上一时刻到当前时刻的代价加上当前时刻的价值函数,选出最小的,即为 2915 | 最佳策略。 2916 | 2917 | 对于\ref{fig:DynamicProgramming}给定的符号,价值函数可以定义为: 2918 | \begin{equation} 2919 | \begin{aligned} 2920 | V\left(s_0\right) & =\min _\pi \sum_{t=0} h\left(s_t, a_t\right) \\ 2921 | \quad= & \min _{a_0}\left(h\left(s_0, a_0\right)+\min _\pi \sum_{t=1} h\left(s_t, a_t\right)\right) 2922 | \end{aligned} 2923 | \end{equation} 2924 | 简写形式被称为Belleman 方程: 2925 | \begin{equation} 2926 | \label{eq:Belleman} 2927 | V(s)=\min _a(h(s, a)+V(f(s, a))) 2928 | \end{equation} 2929 | 它递归得给出了最优策略和价值函数的关系。同时,若得到了价值函数, 2930 | 那么最优策略可以通过$\pi(s)=\arg \min _a(h(s, a)+V(f(s, a)))$ 2931 | 计算出来。也可以将$h(s, a)+V(f(s, a))$记为$Q(s,a)$,称为$Q$function, 2932 | 或状态价值函数。 2933 | 2934 | 对于离散控制,优化$V$或$Q$问题不大,对于连续情况,比如人体运动所施加的力, 2935 | 优化就可能变得很困难。 2936 | 2937 | \subsection{LQR} 2938 | 对于一般问题,Belleman方程可能没有解析解,而对于特定的问题,比如Linear Quadratic Regulator(LQR) 2939 | 就有解析解。LQR要求目标函数$\min h\left(s_T\right)+\sum_{t=0}^{T-1} h\left(s_t, a_t\right)$中的$h$是一个二次函数, 2940 | 对于运动学方程$f(s_t, a_t) - s_{t+1}=0$是一个线性函数。 2941 | 2942 | 在PD控制章节部分,给出的例子\ref{fig:PDController},就是一个典型的LQR问题。假设我们的目标轨迹是一个 2943 | 正弦函数,则优化目标可写为: 2944 | \begin{equation} 2945 | \begin{aligned} 2946 | & \min _{\left\{x_n, v_n, \bar{x}_n\right\}} \sum_{n=0}^N\left(\sin \left(t_n\right)-x_n\right)^2+\sum_{n=0}^N \bar{x}_n^2 \\ 2947 | & \text { s.t. } \quad \begin{aligned} 2948 | v_{n+1} & =v_n+h\left(k_p\left(\bar{x}_n-x_n\right)-k_d v_n\right) \\ 2949 | & x_{n+1}=x_n+h v_{n+1} 2950 | \end{aligned} 2951 | \end{aligned} 2952 | \end{equation} 2953 | 2954 | 对于一般LQR问题,优化目标可写为: 2955 | \begin{equation} 2956 | \begin{aligned} 2957 | & \qquad \min s_T^T Q_T s_T+\sum_{t=0}^T s_t^T Q_t s_t+a_t^T R_t a_t \\ 2958 | & \text {满足} \\ 2959 | & \qquad s_{t+1}=A_t s_t+B_t a_t \quad \text {其中} 0 \leq t