-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
303 lines (303 loc) · 374 KB
/
search.xml
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[从代数的角度分析线性系统的解]]></title>
<url>%2Farchives%2F49358.html</url>
<content type="text"><![CDATA[由于行列式和特征值在几何篇中已经讲过了,这部分也就只有线性相关和线性空间的内容介绍]]></content>
<tags>
<tag>linear algorithm</tag>
<tag>math</tag>
</tags>
</entry>
<entry>
<title><![CDATA[从几何的角度理解线性系统的解]]></title>
<url>%2Farchives%2F55560.html</url>
<content type="text"><![CDATA[继续填之前线性代数的坑,这一讲开始有点意思了,希望讲清楚了]]></content>
<tags>
<tag>linear algorithm</tag>
<tag>math</tag>
</tags>
</entry>
<entry>
<title><![CDATA[为什么学习线性代数]]></title>
<url>%2Farchives%2F61424.html</url>
<content type="text"><![CDATA[懒得打字了,干脆直接讲吧 为之前线性代数填坑系列]]></content>
<tags>
<tag>linear algorithm</tag>
<tag>math</tag>
</tags>
</entry>
<entry>
<title><![CDATA[OpenCV中的相机模型]]></title>
<url>%2Farchives%2F57350.html</url>
<content type="text"><![CDATA[简要整理了一下OpenCV中的相机模型 最基础的就是小孔成像模型,简要描述一下。对于一个在相机坐标系下的点(X,Y,Z)(X,Y,Z)(X,Y,Z),其通过小孔投影到归一化平面上的坐标为 x=X/Zy=Y/Z\begin{aligned} x &= X / Z \\ y &= Y / Z \end{aligned} xy=X/Z=Y/Z 但是实际中由于镜头曲面和装配的原因会导致图像产生畸变。OpenCV中考虑了径向畸变和切向畸变。 径向畸变是指成像装置边缘的畸变,远离透镜中心的区域比透镜中心光线更弯曲,是由透镜曲面造成。对于径向畸变OpenCV中使用这样的公式: xdistorted=x(1+k1r2+k2r4+k3r6)ydistorted=y(1+k1r2+k2r4+k3r6)\begin{aligned} x_{distorted} &= x(1 + k_1r^2 + k_2r^4 + k_3r^6) \\ y_{distorted} &= y(1 + k_1r^2 + k_2r^4 + k_3r^6) \end{aligned} xdistortedydistorted=x(1+k1r2+k2r4+k3r6)=y(1+k1r2+k2r4+k3r6) 如果考虑得再多一点,k4−k6k4-k6k4−k6有值的话,公式如下: xdistorted=x1+k1r2+k2r4+k3r61+k4r2+k5r4+k6r6ydistorted=y1+k1r2+k2r4+k3r61+k4r2+k5r4+k6r6\begin{aligned} x_{distorted} &= x\frac{1 + k_1r^2 + k_2r^4 + k_3r^6}{1 + k_4r^2 + k_5r^4 + k_6r^6} \\ y_{distorted} &= y\frac{1 + k_1r^2 + k_2r^4 + k_3r^6}{1 + k_4r^2 + k_5r^4 + k_6r^6} \end{aligned} xdistortedydistorted=x1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6=y1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6 可见径向畸变是对称的。 切向畸变是指装配中透镜和成像平面不平行造成的畸变。切向畸变的表达式为: xdistorted=x+[2p1xy+p2(r2+2x2)]ydistorted=y+[p1(r2+2y2)+2p2xy]\begin{aligned} x_{distorted} &= x + [2 p_1 xy + p_2 (r^2 + 2 x^2)] \\ y_{distorted} &= y + [p_1 (r^2 + 2y^2) + 2 p_2 xy] \end{aligned} xdistortedydistorted=x+[2p1xy+p2(r2+2x2)]=y+[p1(r2+2y2)+2p2xy] 以上公式中, [k1,k2,k3,k4,k5,k6,p1,p2][k_1, k_2, k_3, k_4, k_5, k_6, p_1, p_2][k1,k2,k3,k4,k5,k6,p1,p2]为畸变参数,r2=x2+y2r^2 = x^2 + y^2r2=x2+y2。 将上面的内容合并起来,径向畸变和切向畸变一起考虑,可以得到 xdistorted=x1+k1r2+k2r4+k3r61+k4r2+k5r4+k6r6+[2p1xy+p2(r2+2x2)]ydistorted=y1+k1r2+k2r4+k3r61+k4r2+k5r4+k6r6+[p1(r2+2y2)+2p2xy]\begin{aligned} x_{distorted} &=x\frac{1 + k_1r^2 + k_2r^4 + k_3r^6}{1 + k_4r^2 + k_5r^4 + k_6r^6} + [2 p_1 xy + p_2 (r^2 + 2 x^2)] \\ y_{distorted} &= y\frac{1 + k_1r^2 + k_2r^4 + k_3r^6}{1 + k_4r^2 + k_5r^4 + k_6r^6} + [p_1 (r^2 + 2y^2) + 2 p_2 xy] \end{aligned} xdistortedydistorted=x1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6+[2p1xy+p2(r2+2x2)]=y1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6+[p1(r2+2y2)+2p2xy] 这是最常用到的畸变模型了,一般来说用到的畸变模型也就到此为止。 在OpenCV中畸变参数的排列为(k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]])(k_1, k_2, p_1, p_2 [,k_3[,k_4, k_5, k_6[,s_1,s_2,s_3,s_4[,\tau_x, \tau_y]]]])(k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]]),包含的元素个数必须为4、5、8、12或者14。 剩下的6个参数使用相对比较少一点,这里不具体描述。详细内容可以参考OpenCV官方文档。]]></content>
<tags>
<tag>camera</tag>
<tag>calibration</tag>
<tag>distortion</tag>
</tags>
</entry>
<entry>
<title><![CDATA[虚数与旋转]]></title>
<url>%2Farchives%2F23535.html</url>
<content type="text"><![CDATA[简单解释一下虚数和旋转的关系 平面旋转 将平面上的点(x,y)(x,y)(x,y)用复数x+yix + yix+yi表示,其中i2=−1i^2 = -1i2=−1。在这个坐标系中,基为{1,i}\{1, i\}{1,i}。如果将复平面旋转角度θ\thetaθ,我们可以将111和iii映射到 1→cosθ+isinθi→−sinθ+icosθ\begin{aligned} 1 \rightarrow \cos \theta + i \sin \theta \\ i \rightarrow - \sin \theta + i \cos \theta \end{aligned} 1→cosθ+isinθi→−sinθ+icosθ 这个应该比较容易验证,在坐标系上画一下就行了。 那么对于一般的坐标来讲呢?我们可以得到 x(cosθ+isinθ)+y(−sinθ+icosθ)=x(cosθ+isinθ)+iy(isinθ+cosθ)=(x+iy)(cosθ+isinθ)=(x+iy)eiθ\begin{aligned} &x(\cos \theta + i \sin \theta) + y (-\sin \theta + i \cos \theta)\\ =& x(\cos \theta + i \sin \theta) + i y ( i\sin \theta + \cos \theta) \\ =& (x + iy) (\cos \theta + i \sin \theta) \\ =& (x + iy) e^{i\theta} \end{aligned} ===x(cosθ+isinθ)+y(−sinθ+icosθ)x(cosθ+isinθ)+iy(isinθ+cosθ)(x+iy)(cosθ+isinθ)(x+iy)eiθ 这里的eiθe^{i\theta}eiθ就可以表示旋转,而且左乘右乘也没有什么区别,因为复数乘法有交换律。eiθe^{i\theta}eiθ应该也属于李群,有兴趣的朋友可以自己证明一下,这个应该比较简单。同样的θ\thetaθ也就是对应的李代数啦。个人认为使用复数形式来表示旋转比矩阵形式表示旋转看起来直观方便的多。 3D旋转 四元数是拥有3个虚部的复数,单位四元数通常用来表示3D空间中的旋转,和平面旋转大致可以类比一下。iii是复数的虚部,令u\mathbf{u}u为纯单位四元数,那么u\mathbf{u}u可以表示为某一个通过原点的轴。那么我们有 euθ=cosθ+usinθe^{\mathbf{u}\theta} = \cos \theta + \mathbf{u} \sin \theta euθ=cosθ+usinθ 同样的,这样的单位四元数用来表示3D旋转。 但是熟悉单位四元数表示旋转的同学应该都知道使用四元数进行旋转远远不是这么简单,所以我们进行接下来都一些推导。 先让我们脱离四元数,从三维坐标来考虑这个问题吧。元素x\mathbf{x}x绕通过原点的轴u\mathbf{u}u旋转了角度ϕ\phiϕ。如图 其中x∥\mathbf{x}_{\parallel}x∥为与轴u\mathbf{u}u平行的分量,x⊥\mathbf{x}_{\perp}x⊥为与旋转轴垂直的分量,具体的表示图里有里我就不写了。 接下来要说明的是与轴平行的分量在旋转中是不变的,变化的只有与旋转轴垂直的分量。令x′\mathbf{x}'x′为x\mathbf{x}x旋转后的量,x⊥′\mathbf{x}'_{\perp}x⊥′为旋转后的垂直分量。在旋转轴u\mathbf{u}u的作用下,垂直分量x⊥\mathbf{x}_{\perp}x⊥的旋转过程发生在基{x⊥,u×x⊥}\{ \mathbf{x}_{\perp}, \mathbf{u} \times \mathbf{x}_{\perp} \}{x⊥,u×x⊥}张成的平面上。回忆一下平面旋转,基为{1,i}\{1,i\}{1,i}时,旋转角度θ\thetaθ,有 1→cosθ+isinθ1 \rightarrow \cos \theta + i \sin \theta 1→cosθ+isinθ 同样的,我们可以得到 x⊥→x⊥cosϕ+(u×x⊥)sinϕ\mathbf{x}_{\perp} \rightarrow \mathbf{x}_{\perp} \cos \phi + (\mathbf{u} \times \mathbf{x}_{\perp}) \sin \phi x⊥→x⊥cosϕ+(u×x⊥)sinϕ 最后我们得到旋转后的结果 x′=x∥+x⊥cosϕ+(u×x⊥)sinϕ\mathbf{x}' = \mathbf{x}_{\parallel} + \mathbf{x}_{\perp} \cos \phi + (\mathbf{u} \times \mathbf{x}_{\perp}) \sin \phi x′=x∥+x⊥cosϕ+(u×x⊥)sinϕ 根据四元数乘法 我们有 [euθ]L[e−uθ]R=[100cos2θ+sin2θ[u]×+(uuT+[u]×[u]×)sin2θ][e^{\mathbf{u}\theta}]_L [e^{-\mathbf{u}\theta}]_R = \begin{bmatrix} 1 & 0 \\ 0 & \cos^2 \theta + \sin2\theta [\mathbf{u}]_{\times} + (\mathbf{uu}^T + [\mathbf{u}]_{\times}[\mathbf{u}]_{\times})\sin^2\theta \end{bmatrix} [euθ]L[e−uθ]R=[100cos2θ+sin2θ[u]×+(uuT+[u]×[u]×)sin2θ] 用这个矩阵左乘(0,x)(0, \mathbf{x})(0,x)可以得到 xcos2θ+sin2θ[u]×x+(uuTx+[u]×[u]×x)sin2θ=xcos2θ+(u×x⊥)sin2θ+(uuTx∥+[u]×[u]×x⊥)sin2θ=xcos2θ+(u×x⊥)sin2θ+x∥sin2θ−x⊥sin2θ=x∥+x⊥cos2θ+(u×x⊥)sin2θ\begin{aligned} &\mathbf{x} \cos^2 \theta + \sin2\theta [\mathbf{u}]_{\times} \mathbf{x} + (\mathbf{uu}^T \mathbf{x} + [\mathbf{u}]_{\times}[\mathbf{u}]_{\times}\mathbf{x})\sin^2\theta \\ = &\mathbf{x} \cos^2 \theta + (\mathbf{u} \times \mathbf{x}_{\perp}) \sin2\theta + (\mathbf{uu}^T \mathbf{x}_{\parallel} + [\mathbf{u}]_{\times}[\mathbf{u}]_{\times}\mathbf{x}_{\perp})\sin^2\theta \\ = & \mathbf{x} \cos^2 \theta + (\mathbf{u} \times \mathbf{x}_{\perp}) \sin2\theta + \mathbf{x}_{\parallel} \sin^2\theta - \mathbf{x}_{\perp} \sin^2\theta \\ = & \mathbf{x}_{\parallel} + \mathbf{x}_{\perp}\cos2\theta + (\mathbf{u} \times \mathbf{x}_{\perp}) \sin2\theta \end{aligned} ===xcos2θ+sin2θ[u]×x+(uuTx+[u]×[u]×x)sin2θxcos2θ+(u×x⊥)sin2θ+(uuTx∥+[u]×[u]×x⊥)sin2θxcos2θ+(u×x⊥)sin2θ+x∥sin2θ−x⊥sin2θx∥+x⊥cos2θ+(u×x⊥)sin2θ 所以使用对偶四元数旋转的时候是 euθ/2⊗x⊗e−uθ/2e^{\mathbf{u}\theta/2} \otimes \mathbf{x} \otimes e^{-\mathbf{u}\theta/2} euθ/2⊗x⊗e−uθ/2 其中⊗\otimes⊗为四元数乘法。 NOTE:很遗憾,目前为止也只是抽象推导了四元数旋转,还是没有对四元数旋转得到更加直观的理解。3D旋转与虚数的关系并没有2D来的那么直观。相关理解可能后续有时间再继续进行吧。 参考文献 [1]B. V. Adorno, “Robot Kinematic Modeling and Control Based on Dual Quaternion Algebra—Part I: Fundamentals.,” 2017. [2]J. Sola, “Quaternion kinematics for the error-state KF,” p. 73.]]></content>
<tags>
<tag>math</tag>
<tag>rotation</tag>
</tags>
</entry>
<entry>
<title><![CDATA[对偶四元数基础]]></title>
<url>%2Farchives%2F30055.html</url>
<content type="text"><![CDATA[对偶四元数将平移和旋转放在同一个状态量中,而不是将它们分开。对偶四元数形式上由对偶数和四元数的概念组合而成。 对偶数 对偶数于复数类似由两部分组成,但是并不是同一个东西,具体来看。对偶数可以写成 z=r+dεz = r + d \varepsilon z=r+dε 其中rrr为实部,ddd为对偶部;ε\varepsilonε为对偶算子,表示ε2=0\varepsilon^2 = 0ε2=0但是ε≠0\varepsilon \neq 0ε≠0。和复数类似,iii是用来区分实部和虚部的,但是ε\varepsilonε和iii的实际意义不一样。 基本运算 对偶数加法 (rA+dAε)+(rB+dBε)=(rA+rB)+(dA+dB)ε(r_A + d_A \varepsilon) + (r_B + d_B \varepsilon) = (r_A + r_B) + (d_A + d_B)\varepsilon (rA+dAε)+(rB+dBε)=(rA+rB)+(dA+dB)ε 对偶数乘法 (rA+dAε)(rB+dBε)=rArB+rAdBε+rBdAε+dAdBε2=rArB+(rAdB+rBdA)ε\begin{aligned} (r_A + d_A \varepsilon)(r_B + d_B \varepsilon) &= r_Ar_B + r_Ad_B\varepsilon + r_Bd_A\varepsilon + d_Ad_B\varepsilon^2 \\ &= r_Ar_B + (r_Ad_B + r_Bd_A)\varepsilon \end{aligned} (rA+dAε)(rB+dBε)=rArB+rAdBε+rBdAε+dAdBε2=rArB+(rAdB+rBdA)ε 对偶数除法 (rA+dAε)(rB+dBε)=(rA+dAε)(rB+dBε)(rA−dAε)(rB−dBε)=rArB+(rBdA−rAdB)ε(rB)2=rArBrB2+rBdA−rAdBrB2ε\begin{aligned} \frac{(r_A + d_A \varepsilon)}{(r_B + d_B \varepsilon)} &= \frac{(r_A + d_A \varepsilon)}{(r_B + d_B \varepsilon)} \frac{(r_A - d_A \varepsilon)}{(r_B - d_B \varepsilon)} \\ &= \frac{r_Ar_B + (r_Bd_A - r_Ad_B)\varepsilon}{(r_B)^2} \\ &= \frac{r_Ar_B}{r_B^2} + \frac{r_Bd_A - r_Ad_B}{r_B^2}\varepsilon \end{aligned} (rB+dBε)(rA+dAε)=(rB+dBε)(rA+dAε)(rB−dBε)(rA−dAε)=(rB)2rArB+(rBdA−rAdB)ε=rB2rArB+rB2rBdA−rAdBε 加法和乘法都都很普通没什么意思,这个除法可以注意一下,分母是被除数实部的平方。 对偶数微分 基本形式上没有什么特别的 ddxs(x)=limδx→0s(x+δx)−s(x)δx\frac{\mathrm{d}}{\mathrm{dx}} \mathbf{s}(x) = \lim_{\delta x \rightarrow 0}\frac{\mathbf{s}(x + \delta x) - \mathbf{s}(x)}{\delta x} dxds(x)=δx→0limδxs(x+δx)−s(x) 对偶数的导数是另一个对偶数,参考对偶数除法。 值得注意的是,对偶数的对偶算子条件是ε2=0\varepsilon^2 = 0ε2=0,这给我们使用泰勒展开带来了相当多的便利。 f(rA+dAε)=f(rA)+f′(rA)1!dAε+f′′(rA)2!(dAε)2+f′′′(rA)3!(dAε)3+…=f(rA)+f′(rA)dAε\begin{aligned} f(r_A + d_A\varepsilon) &= f(r_A) + \frac{f'(r_A)}{1!}d_A\varepsilon + \frac{f''(r_A)}{2!}(d_A\varepsilon)^2 + \frac{f'''(r_A)}{3!}(d_A\varepsilon)^3 + \dots \\ &= f(r_A) + f'(r_A)d_A\varepsilon \end{aligned} f(rA+dAε)=f(rA)+1!f′(rA)dAε+2!f′′(rA)(dAε)2+3!f′′′(rA)(dAε)3+…=f(rA)+f′(rA)dAε 注意,这里是等于哦,不是约等于哦。不需要什么去掉多阶项线性化,展开就只有一阶项。 四元数 (都是Hamilton) 四元数是复数的一个扩展,基本形式为: q=w+(xi+yj+zk)\mathbf{q} = w + (x \mathbf{i} + y \mathbf{j} + z \mathbf{k}) q=w+(xi+yj+zk) q=(w,v)\mathbf{q} = (w, \mathbf{v}) q=(w,v) 每个元素含义的细节不说了,没啥好写的很多地方都有。 四元数计算 数乘,sss是数值 sq=(sw,sv)s \mathbf{q} = (sw, s\mathbf{v}) sq=(sw,sv) 加法 q1+q2=(w1+w2,v1+v2)\mathbf{q}_1 + \mathbf{q}_2 = (w_1 + w_2, \mathbf{v}_1 + \mathbf{v}_2) q1+q2=(w1+w2,v1+v2) 乘法 q1q2=(w1w2−v1v2,w1v2+w2v1+(v1×v2))\mathbf{q}_1 \mathbf{q}_2 = (w_1w_2 - v_1v_2, w_1\mathbf{v}_2 + w_2\mathbf{v}_1 + (\mathbf{v}_1 \times \mathbf{v}_2)) q1q2=(w1w2−v1v2,w1v2+w2v1+(v1×v2)) 共轭 q∗=(w,−v)\mathbf{q}^* = (w, -\mathbf{v}) q∗=(w,−v) 模长 ∥q∥=qq∗\| \mathbf{q} \| = \mathbf{q} \mathbf{q}^* ∥q∥=qq∗ 对于单位四元数而言,有∥q∥=1\|\mathbf{q}\| = 1∥q∥=1。单位四元数可以表示三维空间中绕轴n\mathbf{n}n旋转角度θ\thetaθ的旋转: q=(cos(θ2),nsin(θ2))\mathbf{q} = (\cos(\frac{\theta}{2}), \mathbf{n} \sin (\frac{\theta}{2})) q=(cos(2θ),nsin(2θ)) 对偶四元数最大的优势就在于它插值特别顺滑。 对偶四元数 把对偶数和四元数结合起来,我们得到了对偶四元数 q=qr+qdε\mathbf{q} = \mathbf{q}_r + \mathbf{q}_d \varepsilon q=qr+qdε 其中qr\mathbf{q}_rqr和qd\mathbf{q}_dqd都是四元数。 对偶四元数计算 数乘 sq=sqr+sqdεs \mathbf{q} = s \mathbf{q}_r + s \mathbf{q}_d \varepsilon sq=sqr+sqdε 加法 q1+q2=qr1+qr2+(qd1+qd2)ε\mathbf{q}_1 + \mathbf{q}_2 = \mathbf{q}_{r1} + \mathbf{q}_{r2} + (\mathbf{q}_{d1} + \mathbf{q}_{d2})\varepsilon q1+q2=qr1+qr2+(qd1+qd2)ε 乘法 q1q2=qr1qr2+(qr1qd2+qd1qr2)ε\mathbf{q}_1\mathbf{q}_2 = \mathbf{q}_{r1} \mathbf{q}_{r2} + (\mathbf{q}_{r1}\mathbf{q}_{d2} + \mathbf{q}_{d1} \mathbf{q}_{r2})\varepsilon q1q2=qr1qr2+(qr1qd2+qd1qr2)ε 共轭 q∗=qr∗+qd∗ε\mathbf{q}^* = \mathbf{q}_r^* + \mathbf{q}_d^* \varepsilon q∗=qr∗+qd∗ε 模长 ∥q∥=qq∗\| \mathbf{q} \| = \mathbf{q} \mathbf{q}^* ∥q∥=qq∗ 单位条件 ∥q∥=1qr∗qd+qd∗qr=0\begin{aligned} \|\mathbf{q}\| &= 1 \\ \mathbf{q}_r^* \mathbf{q}_d + \mathbf{q}_d^* \mathbf{q}_r &= 0 \end{aligned} ∥q∥qr∗qd+qd∗qr=1=0 单位对偶四元数是我们的关注重点,因为我们使用它来表示刚性变换。 与刚性变换关系 单位对偶四元数里面包含的刚性变换的信息为: qr=rqd=12tr\begin{aligned} \mathbf{q}_r &= \mathbf{r} \\ \mathbf{q}_d &= \frac{1}{2} \mathbf{tr} \end{aligned} qrqd=r=21tr 其中r\mathbf{r}r是由单位四元数表示的旋转;t\mathbf{t}t是四元数表示的平移,t=(0,v→)\mathbf{t} = (0,\overrightarrow{\mathbf{v}})t=(0,v)。 纯平移和纯旋转就不写了,简单推一推就有了。 和矩阵形式一样,对偶四元数表示多个刚性变换的结合也是连乘。 存在一个点p\mathbf{p}p,使用对偶四元数对它进行刚性变换的时候,计算公式和四元数类似,为 p′=qpq∗\mathbf{p}' = \mathbf{qpq}^* p′=qpq∗ 其中q\mathbf{q}q和q∗\mathbf{q}^*q∗分别为对偶四元数表示的变换和它的共轭;p\mathbf{p}p和p′\mathbf{p}'p′分别为变换前和变换后的点。 参考文献 [1]B. Kenwright, “A Beginners Guide to Dual-Quaternions”]]></content>
<tags>
<tag>math</tag>
<tag>rigid transform</tag>
<tag>quaternion</tag>
<tag>dual-quaternion</tag>
</tags>
</entry>
<entry>
<title><![CDATA[聊聊函数空间]]></title>
<url>%2Farchives%2F56844.html</url>
<content type="text"><![CDATA[在向量空间中,每个向量用它们在向量空间中的坐标表示;同样的,在函数空间中,每个函数也可以用它们在函数空间中的坐标表示。 向量空间 由于类比的方式比较容易让人接受,所以在聊函数空间之前我们先回顾一下向量空间。 首先考虑这样一个问题,如何描述一个向量空间?答案是使用一组线性无关的向量基(e1,e2,…,en)(\mathbf{e}_1, \mathbf{e}_2, \dots, \mathbf{e}_n)(e1,e2,…,en)。在这组向量基张成的空间S\mathcal{S}S中任意一个元素都可以通过这组基线性表示,即 x=x1e1+x2e2+⋯+xnen\mathbf{x} = x_1 \mathbf{e}_1 + x_2 \mathbf{e}_2 + \cdots + x_n \mathbf{e}_n x=x1e1+x2e2+⋯+xnen 其中(x1,x2,…,xn)(x_1, x_2, \dots, x_n)(x1,x2,…,xn)就是向量x\mathbf{x}x在这个向量空间中的坐标。如果这组基是还是正交的,那么我们可以得到 x=⟨x,e1⟩e1+⟨x,e2⟩e2+⋯+⟨x,en⟩en\mathbf{x} = \langle \mathbf{x}, \mathbf{e}_1 \rangle \mathbf{e}_1 + \langle \mathbf{x}, \mathbf{e}_2 \rangle \mathbf{e}_2 + \cdots + \langle \mathbf{x}, \mathbf{e}_n \rangle \mathbf{e}_n x=⟨x,e1⟩e1+⟨x,e2⟩e2+⋯+⟨x,en⟩en 注意到这里我们已经开始引入内积了。实际中我们最常见的向量表示也是写成向量在某个向量空间中的坐标,只不过是一个特定的向量空间,它的基为 e1=(1,0,…,0)e2=(0,1,…,0)⋯en=(0,0,…,1)\begin{aligned} \mathbf{e}_1 &= (1, 0, \dots, 0) \\ \mathbf{e}_2 &= (0, 1, \dots, 0) \\ &\cdots \\ \mathbf{e}_n &= (0, 0, \dots, 1) \end{aligned} e1e2en=(1,0,…,0)=(0,1,…,0)⋯=(0,0,…,1) 这里再提一下特征向量,对于非零向量x\mathbf{x}x,如果有 Ax=λxA\mathbf{x} = \lambda \mathbf{x} Ax=λx 那么x\mathbf{x}x为矩阵AAA的特征向量。 总结一下刚刚说的,空间由基张成,空间中的元素由基的加权和表示。 傅立叶变换 大家都比较清楚的概念,傅立叶变换是将函数从空间/时域变到频域(虽然有些人会怀疑为什么就会变到频域,后面会简单说明一下)。先给出傅立叶变换和逆变换的公式 F(ω)=∫−∞∞f(x)e−2πiωxdxf(x)=∫−∞∞F(ω)e2πiωxdω\begin{aligned} F(\omega) &= \intop_{-\infty}^{\infty} f(x) e^{-2 \pi i \omega x} \text{d} x \\ f(x) &= \intop_{-\infty}^{\infty} F(\omega) e^{2 \pi i \omega x} \text{d} \omega \end{aligned} F(ω)f(x)=−∞∫∞f(x)e−2πiωxdx=−∞∫∞F(ω)e2πiωxdω 这玩意相信大家都很熟悉,甚至倒背如流。不过现在的关注点不是它怎么计算,我们要换个形式来描述它。 函数内积 这个时候就应该引入函数的内积了,函数fff和ggg的内积为 ⟨f,g⟩=∫−∞∞f(x)g(x)‾dx\langle f, g \rangle = \intop_{-\infty}^{\infty} f(x) \overline{g(x)} \text{d} x ⟨f,g⟩=−∞∫∞f(x)g(x)dx 其中a+ib‾=a−ib\overline{a + ib} = a - iba+ib=a−ib,表示复共轭。为什么是这么积分请大家联系向量内积理解。 本来是不想说共轭的,因为还要解释挺麻烦的。但是涉及到复数域就不得不提这个东西了,举个简单的例子解释一下吧 (a+ib)(a−ib)=a2+b2(a+ib)(a+ib)=a2−b2+2iab\begin{aligned} (a + ib)(a - ib) &= a^2 + b^2 \\ (a + ib)(a + ib) &= a^2 - b^2 + 2iab \end{aligned} (a+ib)(a−ib)(a+ib)(a+ib)=a2+b2=a2−b2+2iab 我想你应该从来没有听说过某个元素的模是个复数的。 函数空间正交基 那么现在回到傅立叶变换,根据欧拉公式 eω=e2πiωx=cos(2πωx)+isin(2πωx)=e−2πiωx‾e_{\omega} = e^{2 \pi i \omega x} = \cos(2 \pi \omega x) + i \sin(2 \pi \omega x) = \overline{e^{-2 \pi i \omega x}} eω=e2πiωx=cos(2πωx)+isin(2πωx)=e−2πiωx 连同函数内积带入傅立叶变换中,我们可以得到 F(ω)=⟨f,eω⟩F(\omega) = \langle f, e_{\omega} \rangle F(ω)=⟨f,eω⟩ 继续带入傅立叶逆变换中 f(x)=∫−∞∞⟨f,eω⟩eωdωf(x) = \intop_{-\infty}^{\infty} \langle f, e_{\omega} \rangle e_{\omega} \text{d} \omega f(x)=−∞∫∞⟨f,eω⟩eωdω 这里我们停顿一下,翻回去看看向量在正交基描述的向量空间中是如何表示的。 没错,我想说的就是eωe_\omegaeω构成了函数空间的一组正交向量基。也就是说⟨f,eω⟩\langle f, e_\omega \rangle⟨f,eω⟩是函数fff在这个函数空间中的坐标。所以此时提问,为什么傅立叶变换是将函数从空间/时域变换到频域呢? 同时,很多情况下我们希望经过某些处理后的函数变得平滑。平滑的意思就是去除高频部分留下低频部分,也就是我们说的低通滤波 f^(x)=∫−ωmaxωmax⟨f,eω⟩eωdω\hat{f}(x) = \intop_{-\omega_{\max}}^{\omega_{\max}} \langle f, e_{\omega} \rangle e_{\omega} \text{d} \omega f^(x)=−ωmax∫ωmax⟨f,eω⟩eωdω 同时这里有一个很奇妙的东西,将拉普拉斯算子作用在eωe_\omegaeω上之后,可以得到 Δeω=−(2πω)2eω\Delta e_\omega = - (2 \pi \omega)^2 e_\omega Δeω=−(2πω)2eω 所以eωe_\omegaeω为拉普拉斯算子的特征函数。 微积分 相信大家从《高等数学》/《微积分》里面都学了不少微积分计算技巧,但是这不是这篇内容的重点。同样的,相信大家对各种常微分方程的解算方法也已烂熟于心,比如什么分离变量等等。这里我问一个问题,不知道大家有没有想过微分方程解出来之后是个什么东西?一般来讲,我们是从初中开始学习方程的。我们解过各种方程,一元的二元的一次的二次的,甚至未知量太多了被我们排列成向量去求解。那么问题来了,微分方程解出来是什么东西?相信优秀的同学们应该都知道,微分方程的解是个函数。结合这个结论与我们刚刚推导的傅立叶变换,这里再问一个问题,为什么在常微分方程中使用拉普拉斯变换进行求解那么万能? 偏微分方程 自然界是连续的,并且很多时候是平滑的,所以偏微分方程是目前人类描述自然界最可靠的方法。我们知道偏微分方程多数时候是没有解析方法求解的。但是大家不要小看了人类的智慧,此时就该函数空间上场了。给定描述函数空间的一组基函数(f1,f2,⋯,fn)(f_1, f_2, \cdots, f_n)(f1,f2,⋯,fn),对于任意函数有 f≈x1f1+x2f2+⋯+xnfnf \approx x_1 f_1 + x_2 f_2 + \cdots + x_n f_n f≈x1f1+x2f2+⋯+xnfn 为什么这里不是等于,因为一般的函数通常不能由有限个基函数完全描述。我们也无法通过有限的输入条件求得连续空间的所有值。但是我们有一个假设,那就是平滑,所以可以通过有限个基函数去近似。 所以这玩意怎么用呢?举个例子,对于一般的泊松方程而言 Δf=b\Delta f = b Δf=b 将fff由基函数替换得到 Δf^=(Δf1Δf2⋯Δfn)(x1x2⋮xn)≈b\Delta \hat{f} = \begin{pmatrix} \Delta f_1 & \Delta f_2 & \cdots & \Delta f_n \end{pmatrix}\begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix} \approx b Δf^=(Δf1Δf2⋯Δfn)⎝⎜⎜⎛x1x2⋮xn⎠⎟⎟⎞≈b 它居然就变成了一个线性方程了!不过前面我们说到,由于一般的函数不能由有限个基函数完全描述,所以fff和bbb这两个函数还存在不能由(f1,f2,…,fn)(f_1, f_2, \dots, f_n)(f1,f2,…,fn)描述的分量。因此通常考虑Δf\Delta fΔf和bbb到该函数空间的投影相等,即 ⟨Δf,fi⟩=⟨b,fi⟩\langle \Delta f, f_i \rangle = \langle b, f_i \rangle ⟨Δf,fi⟩=⟨b,fi⟩ 最后我们求解的问题就变成了 (⟨Δf1,f1⟩⟨Δf2,f1⟩⋯⟨Δfn,f1⟩⟨Δf1,f2⟩⟨Δf2,f2⟩⋯⟨Δfn,f2⟩⋮⋮⋱⋮⟨Δf1,fn⟩⟨Δf2,fn⟩⋯⟨Δfn,fn⟩)(x1x2⋮xn)=(⟨b,f1⟩⟨b,f2⟩⋮⟨b,fn⟩)\begin{pmatrix} \langle \Delta f_1, f_1 \rangle & \langle \Delta f_2, f_1 \rangle & \cdots & \langle \Delta f_n, f_1 \rangle \\ \langle \Delta f_1, f_2 \rangle & \langle \Delta f_2, f_2 \rangle & \cdots & \langle \Delta f_n, f_2 \rangle \\ \vdots & \vdots & \ddots & \vdots \\ \langle \Delta f_1, f_n \rangle & \langle \Delta f_2, f_n \rangle & \cdots & \langle \Delta f_n, f_n \rangle \end{pmatrix}\begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix} = \begin{pmatrix} \langle b, f_1 \rangle \\ \langle b, f_2 \rangle \\ \vdots \\ \langle b, f_n \rangle \end{pmatrix} ⎝⎜⎜⎛⟨Δf1,f1⟩⟨Δf1,f2⟩⋮⟨Δf1,fn⟩⟨Δf2,f1⟩⟨Δf2,f2⟩⋮⟨Δf2,fn⟩⋯⋯⋱⋯⟨Δfn,f1⟩⟨Δfn,f2⟩⋮⟨Δfn,fn⟩⎠⎟⎟⎞⎝⎜⎜⎛x1x2⋮xn⎠⎟⎟⎞=⎝⎜⎜⎛⟨b,f1⟩⟨b,f2⟩⋮⟨b,fn⟩⎠⎟⎟⎞ 基函数举例 什么B样条,RBF。算了我不想写了,总而言之很多看起来是用来内插的东西,实际上都可以换个角度想想。 参考文献 M. Botsch, Ed., Polygon mesh processing. Natick, Mass: A K Peters, 2010.]]></content>
<tags>
<tag>math</tag>
<tag>numeric</tag>
</tags>
</entry>
<entry>
<title><![CDATA[换个角度理解无约束数值优化]]></title>
<url>%2Farchives%2F30873.html</url>
<content type="text"><![CDATA[先剧透,我认为常见的无约束的数值优化方法不是在解算二次型就是在转换为二次型再进行解算的路上。至于有约束的方法,也通常利用某些手段变为无约束方法。本文就不讨论KKT那些东西了。 引言 由于计算机硬件的限制,虽然我们用各种漂亮的数学模型去解释我们的工作,但是最后都不得不使用数值优化这个工具。所以我们来讨论一下数值优化到底是怎么做的,至于它是用来做什么的不是这篇文章要讲的东西。 问题定义 在一个最优化问题中,我们通常都是要求解使得某个函数最小的变量,即 x∗=argminxf(x)x^* = \text{arg}\min_x f(x) x∗=argxminf(x) 你要问为什么不是最大,因为我们通常都是希望误差最小。 一次的情况 我们首先来分析下f(x)f(x)f(x)的情况。从我们最熟悉的线性方程开始吧,如果f(x)f(x)f(x)是一个一次的函数,即 f(x)=ax+bf(x) = ax + b f(x)=ax+b 这个时候你问f(x)f(x)f(x)的最小值是什么其实是没有意义的对吧。应该说,对于所有奇次的函数f(x)f(x)f(x)而言,这个问题都是没有意义的。 二次的情况 看完了最熟悉的线性我们来看看二次的吧,即 f(x)=12ax2−bx+cf(x) = \frac{1}{2} ax^2 - bx +c f(x)=21ax2−bx+c 众所周知,当a>0a > 0a>0的时候,满足ax=bax = bax=b的xxx使得f(x)f(x)f(x)最小。如果我们写成矩阵形式的话,则有 f(x)=12xTAx−bTx+cf(\mathbf{x}) = \frac{1}{2} \mathbf{x}^T A \mathbf{x} - \mathbf{b}^T \mathbf{x} + c f(x)=21xTAx−bTx+c 在这种情况下我们就有,当AAA正定时,满足Ax=bA \mathbf{x} = \mathbf{b}Ax=b的x\mathbf{x}x使得函数f(x)f(\mathbf{x})f(x)最小。此时内容已经初显端倪了。 将高次函数转为二次函数 上面我们讨论了一次和二次的情况,那么对于更高次的情况呢?高次函数的最小值目前还没有通解形式,所以就必须从初始值一点点迭代了。在这种方法中,每次迭代直接求得的结果并不是自变量xxx本身,而是要叠加在xxx上的一个变化Δx\Delta xΔx。 梯度下降法 梯度是函数在局部上升最快的方法,也就是说梯度的反方向是函数下降最快的方向。因此我们每次都往函数的负梯度方向迭代,即 xk+1=xk−αJk\mathbf{x}_{k+1} = \mathbf{x}_k - \alpha \mathbf{J}_{k} xk+1=xk−αJk 其中Jk\mathbf{J}_kJk是函数f(x)f(x)f(x)在xk\mathbf{x}_kxk处的导数,α\alphaα是步长。现在问题来了,步长α\alphaα多大的时候这次迭代是最优的?答案是和这次迭代的终点xk+1\mathbf{x}_{k+1}xk+1处的导数Jk+1\mathbf{J}_{k+1}Jk+1垂直的时候。如果这两个方向不是垂直的,那么表示这次迭代还没有到达最优的结果或者说已经超过了最优的结果。想求证的同学可以自己尝试画图。接下来这个问题就变成了 ⟨Jk,Jk+1⟩=0\langle \mathbf{J}_k, \mathbf{J}_{k+1} \rangle = 0 ⟨Jk,Jk+1⟩=0 其中 Jk+1=∇f(xk−αJk)≈Jk−Hk⋅αJk\mathbf{J}_{k+1} = \nabla f(\mathbf{x}_k - \alpha \mathbf{J}_{k}) \approx \mathbf{J}_k - H_k \cdot \alpha \mathbf{J}_{k} Jk+1=∇f(xk−αJk)≈Jk−Hk⋅αJk 这其中HkH_kHk为xk\mathbf{x}_kxk处的Hessian矩阵,那么我们求解的问题变成了 JkTJk−JkTHkJkα=0\mathbf{J}_k^T \mathbf{J}_k - \mathbf{J}_k^T H_k \mathbf{J}_k \alpha = 0 JkTJk−JkTHkJkα=0 也就是说这个问题变成了 α∗=argminα12JkTHkJkα2−JkTJkα+c\alpha^* = \text{arg}\min_\alpha \frac{1}{2} \mathbf{J}_k^T H_k \mathbf{J}_k \alpha^2 - \mathbf{J}_k^T \mathbf{J}_k \alpha + c α∗=argαmin21JkTHkJkα2−JkTJkα+c 这也太刺激了吧!(虽然一般在实际操作的时候大家都不会用这种方法计算步长) 牛顿型方法 牛顿型跟二次函数的关系就更明确,因为牛顿型方法就是将函数近似为二次函数再求解的。为了证实这个说法我们还是简单推导一下。 原始的牛顿法就是直接将高次函数泰勒展开到二次,即 f(x+Δx)≈f(x)+∇f(x)Δx+12ΔxTHΔxf(\mathbf{x} + \Delta \mathbf{x}) \approx f(\mathbf{x}) + \nabla f(\mathbf{x}) \Delta \mathbf{x} + \frac{1}{2} \Delta\mathbf{x}^T H \Delta\mathbf{x} f(x+Δx)≈f(x)+∇f(x)Δx+21ΔxTHΔx 高斯牛顿是解决Frobenius范数下的最优化问题,它直接在范数中泰勒展开到一阶,即 f(x+Δx)≈∥f(x)+∇f(x)Δx∥F2f(\mathbf{x} + \Delta\mathbf{x}) \approx \| f(\mathbf{x}) + \nabla f(\mathbf{x}) \Delta \mathbf{x} \|_F^2 f(x+Δx)≈∥f(x)+∇f(x)Δx∥F2 以上内容,请大家自己展开,我懒得写了。 其实很明显,牛顿法的核心就是将问题局部变为一个二次型的问题进行迭代。 求解二次型问题 对于常见的二次型的问题,形式前面已经给出了,最终就是求解一个形如 Ax=bA \mathbf{x} = \mathbf{b} Ax=b 的线性系统。 在这个线性系统中,通常我们都要求矩阵AAA是对称正定的(当然能稀疏是最好)。对称性通常都很容易满足,一旦AAA不对称的话ATAA^TAATA总该对称了吧。关于正定的问题应该是有很多讨论的,这里不多写。不过关于正定的问题可以提供一次思考方向,对于对称矩阵AAA而言,如果主对角轴上的值aiia_{ii}aii为正数且aii>∑j≠i∥aij∥a_{ii} > \sum_{j \neq i} \| a_{ij} \|aii>∑j≠i∥aij∥,那么矩阵AAA是正定的。以下内容我们都默认AAA是对称正定的,不再赘述。 直接法 这东西大家应该都很熟悉了。各种矩阵分解方法,什么QR、LU、SVD、Cholesky等等,都是在考虑如何计算矩阵AAA的逆A−1A^{-1}A−1。反正都是变着花样进行高斯消元,我甚至想不出有什么好写的。 共轭梯度 由于众所周知的原因,也就是直接矩阵分解求逆的计算量太大了,所以大家都在想办法找一些计算量更小的方法。 前面说到梯度下降法每次迭代都是往梯度的附方向迭代,并且相邻两次的迭代方向在最优的情况下是互相垂直的。也就是会造成大家熟知的“之”字形迭代路径。为了避免这种情况,共轭梯度法考虑每次迭代方向都是共轭的,这样就可以在有限次内得到最优解。 共轭梯度法是用来求解对称正定二次型的方法。应该还是要解释下什么叫共轭,非零向量x\mathbf{x}x和y\mathbf{y}y关于对称正定矩阵AAA共轭,也就是说 xTAy=0\mathbf{x}^T A \mathbf{y} = 0 xTAy=0 于是共轭梯度法选择nnn个共轭且线性独立的方向作为迭代方向,这样最多只需要迭代nnn次。具体的算法细节很多资料都有写,我这里就不多赘述了。 虽然共轭梯度比梯度下降快很多了,但是有时还不是那么快。共轭梯度和梯度下降一样,收敛速度受到矩阵AAA最大特征值和最小特征值之比λmax/λmin\lambda_{\max} / \lambda_{\min}λmax/λmin(条件数)的影响。因此又出现了预处理共轭梯度法,主要思想是通过某些预处理子(也就是某个矩阵乘上这个系统)使得新系统中矩阵A^\hat{A}A^的条件数比矩阵AAA的小。 总结 一般情况下,一个有约束问题通常会想办法先变成无约束问题,一个无约束问题通常会转换为某个二次型计算,最终都要回到一个线性方程求解。 本文是希望从一个不一样的角度来看看数值优化,希望能给大家的工作带来一些不一样的思路。]]></content>
<tags>
<tag>math</tag>
<tag>numeric</tag>
<tag>optsolution</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Laplacian on Triangle Mesh]]></title>
<url>%2Farchives%2F19907.html</url>
<content type="text"><![CDATA[图形学中经常会涉及到拉普拉斯算子,在这里介绍一下拉普拉斯算子在三角mesh上的具体实现。 预备内容 在真正开始讨论拉普拉斯算子之前,我们先说明一些预备的知识。 局部平均区域 基本思想是将计算微分性质作为mesh上点x\mathbf{x}x局部邻域N(x)\mathcal{N}(\mathbf{x})N(x)上的空间平均。下图是几种具体情况: 图中的蓝色区域为局部平均区域。其中barycentric cell是将三角形的重心和三角形边的中点相连,Voronoi cell是将重心换成了外心。使用外心有一个问题,对于钝角三角形来说外心在三角形外部,因此mixed Voronoi cell将钝角三角形的外心替换为钝角对边的中点。 梯度 由于拉普拉斯算子是梯度的散度,所以梯度也是很重要的。假设一个由每个mesh顶点定义的逐块线性函数fff,f(vi)=f(xi)=f(ui)=fif(v_i) = f(\mathbf{x}_i) = f(\mathbf{u}_i) = f_if(vi)=f(xi)=f(ui)=fi,内插得到每一个三角形(xi,xj,xk)(\mathbf{x}_i, \mathbf{x}_j, \mathbf{x}_k)(xi,xj,xk): f(u)=fiBi(u)+fjBj(u)+fkBk(u)f(\mathbf{u}) = f_iB_i(\mathbf{u}) + f_jB_j(\mathbf{u}) + f_kB_k(\mathbf{u}) f(u)=fiBi(u)+fjBj(u)+fkBk(u) 其中u\mathbf{u}u是x\mathbf{x}x在2D共形参数化上的对应参数。内插效果为: 那么fff的梯度为 ∇f(u)=fi∇Bi(u)+fj∇Bj(u)+fk∇Bk(u)\nabla f(\mathbf{u}) = f_i \nabla B_i(\mathbf{u}) + f_j \nabla B_j(\mathbf{u}) + f_k \nabla B_k(\mathbf{u}) ∇f(u)=fi∇Bi(u)+fj∇Bj(u)+fk∇Bk(u) 由于是线性插值,我们有条件对于所有的u\mathbf{u}u我们有Bi(u)+Bj(u)+Bk(u)=1B_i(\mathbf{u}) + B_j(\mathbf{u}) + B_k(\mathbf{u}) = 1Bi(u)+Bj(u)+Bk(u)=1,因此∇Bi(u)+∇Bj(u)+∇Bk(u)=0\nabla B_i(\mathbf{u}) + \nabla B_j(\mathbf{u}) + \nabla B_k(\mathbf{u}) = 0∇Bi(u)+∇Bj(u)+∇Bk(u)=0。联合上面的式子我们可以得到: ∇f(u)=(fj−fi)∇Bj(u)+(fk−fi)∇Bk(u)\nabla f(\mathbf{u}) = (f_j - f_i) \nabla B_j(\mathbf{u}) + (f_k - f_i) \nabla B_k(\mathbf{u}) ∇f(u)=(fj−fi)∇Bj(u)+(fk−fi)∇Bk(u) 这个函数的最速上升方向是垂直于顶点对边的方向,再加上合适的归一化,有 ∇Bi(u)=(xk−xj)⊥2AT\nabla B_i (\mathbf{u}) = \frac{(\mathbf{x}_k - \mathbf{x}_j)^{\perp}}{2A_T} ∇Bi(u)=2AT(xk−xj)⊥ 其中⊥\perp⊥表示逆时针旋转90度,ATA_TAT表示三角形TTT的面积。那么在三角形中每一点的梯度为常数: ∇f(u)=(fj−fi)(xi−xk)⊥2AT+(fk−fi)(xj−xi)⊥2AT\nabla f(\mathbf{u}) = (f_j - f_i) \frac{(\mathbf{x}_i - \mathbf{x}_k)^{\perp}}{2A_T} + (f_k - f_i) \frac{(\mathbf{x}_j - \mathbf{x}_i)^{\perp}}{2A_T} ∇f(u)=(fj−fi)2AT(xi−xk)⊥+(fk−fi)2AT(xj−xi)⊥ 离散拉普拉斯算子 预备内容说完了开始讲讲主题了 均匀拉普拉斯 拉普拉斯算子的均匀离散版本 Δf(vi)=1∣N1(vi)∣∑vj∈N1(vi)(fj−fi)\Delta f(v_i) = \frac{1}{|\mathcal{N}_1(v_i)|} \sum_{v_j \in \mathcal{N}_1(v_i)} (f_j - f_i) Δf(vi)=∣N1(vi)∣1vj∈N1(vi)∑(fj−fi) 总的来说,这个形式用处不大。但是也可以用在各向同性的remesh上。 余切公式 使用混合的有限元/有限体方法(mixed finite element/finite volume method),拉普拉斯算子可以有更精确的离散推导。目标是在局部平均域Ai=A(vi)A_i = A(v_i)Ai=A(vi)上,对逐片线性函数梯度的散度进行积分。为了简化这个积分,对向量值函数F\mathbf{F}F的积分应用散度定理: ∫AidivF(u)dA=∫∂AiF(u)⋅n(u)ds\intop_{A_i} \text{div} \mathbf{F}(\mathbf{u}) \text{d}A = \intop_{\partial A_i} \mathbf{F}(\mathbf{u}) \cdot \mathbf{n}(\mathbf{u}) \text{d}s Ai∫divF(u)dA=∂Ai∫F(u)⋅n(u)ds 这个等式将面积AiA_iAi上的积分转换为了在AiA_iAi边界∂Ai\partial A_i∂Ai上的积分,其中n\mathbf{n}n是边界向外的单位法线。 将拉普拉斯应用到散度定理中 ∫AiΔf(u)dA=∫Aidiv∇f(u)dA=∫∂Ai∇f(u)⋅n(u)ds\intop_{A_i} \Delta f(\mathbf{u}) \text{d}A = \intop_{A_i} \text{div} \nabla f(\mathbf{u}) \text{d}A = \intop_{\partial A_i} \nabla f(\mathbf{u})\cdot \mathbf{n}(\mathbf{u})\text{d}s Ai∫Δf(u)dA=Ai∫div∇f(u)dA=∂Ai∫∇f(u)⋅n(u)ds 将积分分解到每个三角形上。由于局部的Voronoi区域通过三角形两条边的中点a\mathbf{a}a和b\mathbf{b}b,并且每个三角形中∇f(x)\nabla f(\mathbf{x})∇f(x)是常数,那么在该三角形TTT上的积分为 ∫∂Ai∩T∇f(u)⋅n(u)ds=∇f(u)⋅(a−b)⊥=12∇f(u)⋅(xj−xk)⊥\begin{aligned} \intop_{\partial A_i \cap T} \nabla f(\mathbf{u}) \cdot \mathbf{n}(\mathbf{u}) \text{d}s &= \nabla f(\mathbf{u}) \cdot (\mathbf{a} - \mathbf{b})^{\perp} \\ &= \frac{1}{2} \nabla f(\mathbf{u}) \cdot (\mathbf{x}_j - \mathbf{x}_k)^{\perp} \end{aligned} ∂Ai∩T∫∇f(u)⋅n(u)ds=∇f(u)⋅(a−b)⊥=21∇f(u)⋅(xj−xk)⊥ 然后我们将上面得到的梯度离散化公式带进来,得到 ∫∂Ai∩T∇f(u)⋅n(u)ds=(fj−fi)(xi−xk)⊥⋅xj−xk)⊥4AT+(fk−fi)(xj−xi)⊥⋅xj−xk)⊥4AT\begin{aligned} \intop_{\partial A_i \cap T} \nabla f(\mathbf{u}) \cdot \mathbf{n}(\mathbf{u}) \text{d}s &= (f_j - f_i)\frac{(\mathbf{x}_i - \mathbf{x}_k)^{\perp} \cdot \mathbf{x}_j - \mathbf{x}_k)^{\perp}}{4A_T} \\ &+ (f_k - f_i)\frac{(\mathbf{x}_j - \mathbf{x}_i)^{\perp} \cdot \mathbf{x}_j - \mathbf{x}_k)^{\perp}}{4A_T} \end{aligned} ∂Ai∩T∫∇f(u)⋅n(u)ds=(fj−fi)4AT(xi−xk)⊥⋅xj−xk)⊥+(fk−fi)4AT(xj−xi)⊥⋅xj−xk)⊥ 激动人心的部分要来了!赶快会议一下各种三角公式! 让γj\gamma_jγj和γk\gamma_kγk分别表示顶点vjv_jvj和vkv_kvk的三角形内角。由于有 AT=12sinγj∥xj−xi∥∥xj−xk∥=12sinγk∥xi−xk∥∥xj−xk∥A_T = \frac{1}{2} \sin \gamma_j \| \mathbf{x}_j - \mathbf{x}_i \| \| \mathbf{x}_j - \mathbf{x}_k \| = \frac{1}{2} \sin \gamma_k \| \mathbf{x}_i - \mathbf{x}_k \| \| \mathbf{x}_j - \mathbf{x}_k \| AT=21sinγj∥xj−xi∥∥xj−xk∥=21sinγk∥xi−xk∥∥xj−xk∥ 并且有 cosγj=(xj−xi)⋅(xj−xk)∥xj−xi∥∥xj−xk∥cosγk=(xi−xk)⋅(xj−xk)∥xi−xk∥∥xj−xk∥\begin{aligned} \cos \gamma_j &= \frac{( \mathbf{x}_j - \mathbf{x}_i ) \cdot ( \mathbf{x}_j - \mathbf{x}_k )}{\| \mathbf{x}_j - \mathbf{x}_i \|\| \mathbf{x}_j - \mathbf{x}_k \|} \\ \cos \gamma_k &= \frac{( \mathbf{x}_i - \mathbf{x}_k ) \cdot ( \mathbf{x}_j - \mathbf{x}_k )}{\| \mathbf{x}_i - \mathbf{x}_k \|\| \mathbf{x}_j - \mathbf{x}_k \|} \end{aligned} cosγjcosγk=∥xj−xi∥∥xj−xk∥(xj−xi)⋅(xj−xk)=∥xi−xk∥∥xj−xk∥(xi−xk)⋅(xj−xk) 于是就可以得到 ∫∂Ai∩T∇f(u)⋅n(u)ds=12(cotγk(fj−fi)+cotγj(fk−fi))\intop_{\partial A_i \cap T} \nabla f(\mathbf{u}) \cdot \mathbf{n}(\mathbf{u}) \text{d}s = \frac{1}{2} (\cot \gamma_k (f_j - f_i) + \cot \gamma_j (f_k - f_i)) ∂Ai∩T∫∇f(u)⋅n(u)ds=21(cotγk(fj−fi)+cotγj(fk−fi)) 因此在整个局部平均区域AiA_iAi上积分的时候会得到 ∫AiΔf(u)dA=12∑vj∈Ni(vi)(cotαi,j+cotβi,j)(fi−fj)\intop_{A_i} \Delta f(\mathbf{u}) \text{d}A = \frac{1}{2} \sum_{v_j \in \mathcal{N}_i(v_i)} (\cot \alpha_{i,j} + \cot \beta_{i,j})(f_i - f_j) Ai∫Δf(u)dA=21vj∈Ni(vi)∑(cotαi,j+cotβi,j)(fi−fj) αi,j\alpha_{i,j}αi,j,βi,j\beta_{i,j}βi,j与xi\mathbf{x}_ixi,xj\mathbf{x}_jxj的位置关系在上图有给出。 于是,在顶点viv_ivi处,函数fff的拉普拉斯算子的离散平均为 Δf(u):=12Ai∑vj∈Ni(vi)(cotαi,j+cotβi,j)(fi−fj)\Delta f(\mathbf{u}) \coloneqq \frac{1}{2 A_i} \sum_{v_j \in \mathcal{N}_i(v_i)} (\cot \alpha_{i,j} + \cot \beta_{i,j})(f_i - f_j) Δf(u):=2Ai1vj∈Ni(vi)∑(cotαi,j+cotβi,j)(fi−fj) 这就是在计算机图形学中大名鼎鼎的余切公式了。 矩阵化求解 如果要求解肯定还是要具体写成矩阵形式的线性方程,接下来就介绍如何矩阵化一个拉普拉斯或者泊松方程。 矩阵化 考虑在三角mesh上的泊松方程Δf=b\Delta f = bΔf=b或者更高阶偏微分方程Δkf=b\Delta^k f = bΔkf=b的离散化及求解。标量值函数f:S→Rf:\mathcal{S} \rightarrow \mathbb{R}f:S→R是通过在mesh顶点viv_ivi处的函数值fi=f(vi)f_i = f(v_i)fi=f(vi)的逐片线性内插定义的。从上面的推导我们知道了拉普拉斯Δf\Delta fΔf可以写成线性求和的形式 Δf(vi)=wi∑vj∈N1(vi)wij(f(vj)−f(vi))\Delta f(v_i) = w_i \sum_{v_j \in \mathcal{N}_1(v_i)} w_{ij} (f(v_j) - f(v_i)) Δf(vi)=wivj∈N1(vi)∑wij(f(vj)−f(vi)) 如果使用余切离散化那么就是wi=12Aiw_i = \frac{1}{2 A_i}wi=2Ai1,wij=(cotαi,j+cotβi,j)w_{ij} = (\cot \alpha_{i,j} + \cot \beta_{i,j})wij=(cotαi,j+cotβi,j)。将线性和写成矩阵形式之后,我们有 (Δf(v1)⋮Δf(vn))=DM⎵L(f(v1)⋮f(vn))\begin{pmatrix} \Delta f(v_1) \\ \vdots \\ \Delta f(v_n) \end{pmatrix}=\underbrace{DM}_L \begin{pmatrix} f(v_1) \\ \vdots \\ f(v_n) \end{pmatrix} ⎝⎛Δf(v1)⋮Δf(vn)⎠⎞=LDM⎝⎛f(v1)⋮f(vn)⎠⎞ 其中D=diag(w1,…,wn)\mathbf{D} = \text{diag} (w_1, \dots, w_n)D=diag(w1,…,wn),M\mathbf{M}M是对称矩阵 mi,j={−∑vk∈N1(vi)wik,i=jwij,vj∈Ni(vi)0,otherwisem_{i,j} = \begin{cases} -\sum_{v_k \in \mathcal{N}_1(v_i)} w_{ik}, &i = j \\ w_{ij}, &v_j \in \mathcal{N}_i(v_i) \\ 0, & \text{otherwise} \end{cases} mi,j=⎩⎪⎨⎪⎧−∑vk∈N1(vi)wik,wij,0,i=jvj∈Ni(vi)otherwise 更高阶的拉普拉斯可以通过递归的方法得到 Δkf(vi)=wi∑vj∈N1(vi)wij(Δk−1f(vj)−Δk−1f(vi))\Delta^k f(v_i) = w_i \sum_{v_j \in \mathcal{N}_1(v_i)} w_{ij} ( \Delta^{k-1} f(v_j) - \Delta^{k-1} f(v_i)) Δkf(vi)=wivj∈N1(vi)∑wij(Δk−1f(vj)−Δk−1f(vi)) 那么这个矩阵表示就很简单的对应拉普拉斯矩阵L\mathbf{L}L的k次幂Lk=(DM)k\mathbf{L}^k = (\mathbf{DM})^kLk=(DM)k。 因此在nnn个顶点的mesh上更高阶的偏微分方程Δkf=b\Delta^k f = bΔkf=b离散化之后会得到一个(n×n)(n \times n)(n×n)的线性系统 Lkx=b\mathbf{L}^k \mathbf{x} = \mathbf{b} Lkx=b 其中x=(f(v1),…,f(vn))T\mathbf{x} = (f(v_1), \dots, f(v_n))^Tx=(f(v1),…,f(vn))T,且b=(b(v1),…,b(vn))T\mathbf{b} = (b(v_1), \dots, b(v_n))^Tb=(b(v1),…,b(vn))T 矩阵性质 你以为上面就结束了吗?还没有呢!为了保证二次型的对称正定,还需要对上面推导出来的内容进一步调整。 稀疏性 简单说一下吧。拉普拉斯矩阵中只有对角元和边对应的元素是非000的,其余全是0。根据欧拉公式,平均每一行大约7个非000元素。 对称性 由于对角矩阵D\mathbf{D}D捣乱,所以矩阵L=DM\mathbf{L} = \mathbf{DM}L=DM不是对称的。不过对于任意kkk阶的拉普拉斯系统很容易转换为对称系统,即 M(DM)k−1x=D−1b\mathbf{M} (\mathbf{DM})^{k-1} \mathbf{x} = \mathbf{D}^{-1} \mathbf{b} M(DM)k−1x=D−1b 这个对称性应该很容易验证了。 正定性 一般来讲,解偏微分方程都需要边界条件。即对于一个线性系统Ax=b\mathbf{Ax = b}Ax=b,有部分变量xix_ixi保持不变,此时它们不再是未知量了。此时将对应的列ai\mathbf{a}_iai移到右手边,即b←b−xiai\mathbf{b} \leftarrow \mathbf{b} - x_i\mathbf{a}_ib←b−xiai,同时对应的行从整个系统中移除。注意到此时整个系统还是保持对称的!!!在移除边界条件之后,可以证明矩阵L\mathbf{L}L是负定的!(简单点理解,有些行中对角元的绝对值大于该行其它列的元素绝对值的和。请注意,这只是直观理解,绝不是严格证明!!!)负定很简单,直接在每个L\mathbf{L}L上乘一个−1-1−1就行,因此我们最后得到 (−1)kM(DM)k−1x=(−1)kD−1b(-1)^k \mathbf{M} (\mathbf{DM})^{k-1} \mathbf{x} = (-1)^k \mathbf{D}^{-1} \mathbf{b} (−1)kM(DM)k−1x=(−1)kD−1b 由此我们就得到了一个稀疏、对称且正定的线性系统。 参考文献 M. Botsch, Ed., Polygon mesh processing. Natick, Mass: A K Peters, 2010.]]></content>
<tags>
<tag>mesh</tag>
<tag>PDE</tag>
<tag>laplace</tag>
</tags>
</entry>
<entry>
<title><![CDATA[泊松重建]]></title>
<url>%2Farchives%2F7659.html</url>
<content type="text"><![CDATA[开源项目地址 基本思想 目的是计算3D指示函数(indicator function)X\mathcal{X}X,X\mathcal{X}X是一个泛函,将曲面外的点映射为000,将曲面内的点映射为111。 解决问题的关键依据是:模型曲面的有向采样点与模型指示函数之间存在积分的关系。 使用minX∥∇X−V⃗∥\min_\mathcal{X} \| \nabla \mathcal{X} - \vec{V} \|minX∥∇X−V∥,找到合适的函数X\mathcal{X}X使得其梯度最贴近由采样点定义的向量场V⃗\vec{V}V。在此基础上应用散度,将这个变分问题转换为一个标准泊松问题: ΔX=∇⋅∇X=∇⋅V⃗\Delta \mathcal{X} = \nabla \cdot \nabla \mathcal{X} = \nabla \cdot \vec{V} ΔX=∇⋅∇X=∇⋅V 泊松重建方法是一个全局求解的方法,一次性考虑所有的点。 系统概览 输入: 曲面采样集合s∈Ss \in Ss∈S,其中每一个元素都包含坐标s.ps.ps.p和向内法线s.N⃗s.\vec{N}s.N;并假设这些点都在模型MMM的曲面∂M\partial M∂M上,或者离曲面很近。 输出:模型MMM对应的指示函数X\mathcal{X}X,并提取等值面(isosurface)作为曲面。 挑战:从采样点精确计算X\mathcal{X}X 具体思路 由于指示函数成片的都是常数(在模型外全是0,在模型内全是1),梯度场的计算可能导致在曲面边界处存在没有边界的值(unbounded value)。为了拒绝这种情况发生,将指示函数与平滑滤波器卷积并考虑卷积后函数的梯度场。 引理:给定带边界的立体模型MMM,令XM\mathcal{X}_MXM表示MMM的指示函数,N⃗∂M(p)\vec{N}_{\partial M}(p)N∂M(p)为p∈∂Mp \in \partial Mp∈∂M处的向内曲面法线,F~(q)\tilde{F}(q)F~(q)为平滑滤波器,并且F~p(q)=F~(q−p)\tilde{F}_p(q) = \tilde{F}(q-p)F~p(q)=F~(q−p)将它转移到点ppp处。平滑后指示函数的梯度等于由平滑曲面法线场获得的向量: ∇(XM∗F~)(q0)=∫∂MF~p(q0)N⃗∂M(p)dp\nabla (\mathcal{X}_M * \tilde{F})(q_0) = \intop_{\partial M} \tilde{F}_p(q_0) \vec{N}_{\partial M}(p) dp ∇(XM∗F~)(q0)=∂M∫F~p(q0)N∂M(p)dp (原文中有证明) 由于不知道曲面几何,因此不能计算曲面积分;但是输出的有向点集提供了足够的信息去使用离散求和估计积分。 使用点集SSS将∂M\partial M∂M划分为独立的块(patch)Ps⊂∂M\mathscr{P}_s \subset \partial MPs⊂∂M,在独立的块上使用采样点坐标s.ps.ps.p估计在块Ps\mathscr{P}_sPs上的积分,通过块的面积尺度化(每个块的求和权重): ∇(Xm∗F~)(q)=∑s∈S∫PsF~p(q)N⃗∂M(p)dp≈∑s∈S∣Ps∣F~s.p(q)s.N⃗≡V⃗(q)\begin{aligned} \nabla (\mathcal{X}_m * \tilde{F})(q) &= \sum_{s \in S} \intop_{\mathscr{P}_s} \tilde{F}_p(q) \vec{N}_{\partial M}(p)dp \\ &\approx \sum_{s \in S} | \mathscr{P}_s | \tilde{F}_{s.p}(q) s.\vec{N} \equiv \vec{V}(q) \end{aligned} ∇(Xm∗F~)(q)=s∈S∑Ps∫F~p(q)N∂M(p)dp≈s∈S∑∣Ps∣F~s.p(q)s.N≡V(q) 虽然任意的平滑滤波器F~\tilde{F}F~理论上都可行,但是实际上需要慎重选择,最好能满足以下两个条件: 足够窄,不至于过平滑(over-smooth) 足够宽,可以很好的通过由块面积尺度化的在s.ps.ps.p处的值估计Ps\mathscr{P}_sPs上的积分 因此选择高斯滤波器,其方差与分辨率相关。 求解 ∇X~=V⃗\nabla \tilde{\mathcal{X}} = \vec{V}∇X~=V,然而V⃗\vec{V}V一般是不可积的,所以解析解一般不存在。为了更好的估计最小二乘解,在这个等式上应用散度算子得到一个泊松方程 ΔX~=∇⋅V⃗\Delta \tilde{\mathcal{X}} = \nabla \cdot \vec{V} ΔX~=∇⋅V 实现 定义了空间函数在曲面附近有高分辨率,远离曲面有低分辨率(八叉树);向量场V⃗\vec{V}V由空间中一些函数的线性和表示;设置并求解泊松方程;提取指数函数等值面。 离散化 必须选择函数空间去离散化这个问题。使用均匀分辨率的结果开销太大,因此使用八叉树(octree)。 使用采样点的位置定义八叉树O\mathscr{O}O并且将函数FoF_oFo与树的每个节点o∈Oo \in \mathscr{O}o∈O关联,选择的树和函数需要满足以下条件: 向量场V⃗\vec{V}V可以用FoF_oFo的线性和精准有效地表示 由FoF_oFo项表示的泊松方程矩阵形式可以被有效求解 指示函数作为FoF_oFo和的形式可以在模型曲面附近被精准有效地估计 定义函数空间 给定采样点集SSS和最大树深度DDD,定义八叉树O\mathscr{O}O为满足每个采样点都落到深度DDD的叶节点上的最小八叉树。 定义一个函数空间,由一个固定的、单位积分的(unit-intergral)基函数F:R3→RF: \mathbb{R}^3 \rightarrow \mathbb{R}F:R3→R的平移和缩放张成。对于每个节点o∈Oo \in \mathscr{O}o∈O,设FoF_oFo为以ooo为中心且由ooo的尺寸拉伸的单位积分“节点函数”: Fo(q)≡F(q−o.co.w)1o.w3F_o(q) \equiv F \left( \frac{q - o.c}{o.w} \right) \frac{1}{o.w^3} Fo(q)≡F(o.wq−o.c)o.w31 其中o.co.co.c和o.wo.wo.w分别是节点ooo的中心和宽。 函数空间FO,F≡Span{Fo}\mathscr{F}_{\mathscr{O}, F} \equiv \text{Span}\{ F_o \}FO,F≡Span{Fo}有多分辨率结构,类似于传统的小波表示。越好的节点对应越高频的函数,离曲面越近函数表示越精确。 选择基函数 在选择基函数FFF中,目的是选择一个函数,使得向量场V⃗\vec{V}V可以由节点函数{Fo}\{ F_o \}{Fo}的线性和精确有效地表示。 如果将每个采样点的坐标替换为包含它的叶节点中心,向量场V⃗\vec{V}V可以由{Fo}\{ F_o \}{Fo}的线性和有效地表示: F(q)=F~(q2D)F(q) = \tilde{F} \left( \frac{q}{2^D} \right) F(q)=F~(2Dq) 这样每一个采样点都贡献了一项(法向量)到对应它的叶子的节点函数的系数。 由于最大树深度DDD对应2−D2^{-D}2−D的采样宽度,平滑滤波器应该近似为方差在2−D2^{-D}2−D阶上的高斯。因此FFF一个近似为单位方差的高斯。 为了效率我们通过一个简洁的支持函数近似一个单位方差的高斯,因此有: 得到的散度和拉普拉斯算子是稀疏的 估计在点qqq处表示为FoF_oFo线性和的函数,仅仅需要将在qqq比较近的节点o∈Oo \in \mathscr{O}o∈O上求和。 设置FFF为盒滤波器(box filter)与自己的第nnn次卷积,得到的基函数FFF: F(x,y,z)≡(B(x)B(y)B(z))∗n with B(t)={1∣t∣]]></content>
<tags>
<tag>Poisson</tag>
<tag>Poisson Reconstruction</tag>
<tag>3D Reconstruction</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DDG(离散微分几何):曲面参数化]]></title>
<url>%2Farchives%2F8347.html</url>
<content type="text"><![CDATA[翻译自 DISCRETE DIFFERENTIAL GEOMETRY: AN APPLIED INTRODUCTION 第七章 Surface Parameterization。如果有翻译错误或者不当的地方希望能指出,谢谢~ 在这一章中我们将看看曲面参数化的问题。基本想法是我们在空间中有一个曲面,我们想要将它“平铺”在平面上。最古老的例子可能是制作一个地图: 关于地图你会注意到一个事情是它们看起来都存在某种形式的形变:格陵兰岛看起来特别大,或者“北”相对于“东”的角度看起来并不正确。这些现象反应了关于曲面参数化的一般现实:在完美地同时保证长度和角度不变去平铺曲面是不可能的——换句话说,并不是每一个曲面都存在等距参数化。然而,总是可以找到一个保角的或者共形参数化方法,这也是我们将在这个部分进行的内容。 0.1 两个四分之一的旋转得到翻转:复数的一个简要介绍 如果你看到过复数,你可能遇到过像-1的“平方根”这样对于虚数单位iii完全糟糕的描述: i=−1i = \sqrt{-1} i=−1 有一个人提议由于任何一个实数的平方根都是非负的,那么数iii肯定是“虚拟的”。讲得通,对吗?这个说法的问题是它忽视了iii的简单几何意义,它是十分真实的!!!!!因此,让我们开始:符号iii表示在逆时针方向上的一个四分之一的旋转。比如,如果zzz是一个指向东的向量,那么iziziz是一个指向北的向量: 当我们再应用一个四分之一的旋转会发生什么呢?我们当然会得到一个二分之一的旋转! 换句话说,我们有i(iz)=−zi(iz) = -zi(iz)=−z。我们可以将这个声明缩写为i2z=−zi^2z = -zi2z=−z,它意味着我们必须有 i2=−1i^2 = -1 i2=−1 即,两个四分之一的旋转得到一个翻转。这就是全部。没有平方根,也不需要任何创造力。将虚数单位iii考虑为一个90度的旋转将是我们在共形映射中讨论的基本内容。 共形结构 对于空间中的一个曲面f:M→R3f:M \rightarrow \mathbb{R}^3f:M→R3,我们也有一个简单的方法去表达90度的旋转。特别地,如果df(X)df(X)df(X)是R3\mathbb{R}^3R3中的一个正切向量,我们可以通过与一个单位法线NNN叉乘表示一个逆时针方向的四分之一旋转: N×df(X)N \times df(X) N×df(X) 由于向量N×df(X)N \times df(X)N×df(X)也是和曲面f(M)f(M)f(M)相切的,那么在域MMM上肯定有对应的正切向量——让我们将这个向量称为JX\mathcal{J}XJX。换句话说, df(JX)=N×df(X)df(\mathcal{J}X) = N \times df(X) df(JX)=N×df(X) 映射J\mathcal{J}J被称为由fff引导的共形结构。(一些人可能更喜欢称J\mathcal{J}J为几乎复结构或者线性复结构,但是对于曲面所有的这些思想基本是一致的。)一个黎曼曲面是带有一个复结构的曲面,即,它是一个我们知道如何去测量正切向量角度(但是可能不包括它们的长度)的曲面。 关于共形结构J\mathcal{J}J需要被记得的最重要的事情,像虚数单位iii一样,没有什么奇怪的或者神秘的东西:它表示在逆时针方向的一个四分之一的旋转。并且,像前面提到的,两个四分之一的旋转得到一个翻转: J2=−id\mathcal{J}^2 = -id J2=−id 其中ididid表示单位元。 此时你可能会惊讶,“为什么我们要使用两个不同的符号,iii和J\mathcal{J}J,但是它们恰好表示同一个东西呢?”这个说法让人特别迷惑,上图的域MMM看起来十分像(复)平面的一片。但是一般的,MMM并不必须是平面的一片——它可能是任意一个拓扑曲面(一个球面,一个甜甜圈,等等)。并且一般的,正切向量不是复数!因此,写成iXiXiX不是那么能够说得通的,写成Jz\mathcal{J}zJz也是。但是明确的是有一个我们这里想要刻画的关系,并且这个关系由我们的好朋友Augustin Cauchy和Bernhard Riemann描述。 柯西-黎曼方程 想很久以前的地图绘制员,我们的目标是参数化平面上的一个给定曲面。特别的,我们想找到一个保角的映射。我们如何能将这个条件表示得更明确呢?当然,我们知道如何去表达在曲面上90度的旋转,使用复结构J\mathcal{J}J。并且我们知道如何去表达在平面上90度的旋转,使用虚数单位iii。因此,一个保角或者共形映射z:M→Cz:M \rightarrow \mathbb{C}z:M→C对于MMM上的所有正切向量XXX必须满足柯西-黎曼方程 dz(JX)=idz(X)dz(\mathcal{J}X) = idz(X) dz(JX)=idz(X) 换句话说,讲一个向量旋转90度然后再将它映射到平面上,等价于将它映射到平面上然后旋转90度。为了更精准一点,zzz是一个全纯(holomorphic)函数,意味着它同时保持角度和朝向不变(dz(X)×dz(JX)dz(X) \times dz(\mathcal{J}X)dz(X)×dz(JX)向平面外“刺出”)。保角但是逆转朝向的映射被称为反全纯的(antiholomorphic)。 注意到在柯西-黎曼方程中dzdzdz的意义和我们讨论一个浸入fff时dfdfdf的意义没有什么不同:它告诉我们当我们从一个空间到另一个空间的时候正切向量是如何“伸长”的。实际上,和fff一样,映射zzz仅仅是MMM的另一个浸入——此时是到C\mathbb{C}C中去而不是R3\mathbb{R}^3R3。柯西-黎曼的基本思想是这两个浸入应该共享相同的角度符号,就像上面的插图强调的一样。一个看这个图的方法,是去想象我们开始于一个抽象域MMM,它“不知道”如何测量两个向量之间的角度。通过在三维空间中浸入MMM(通过映射fff),我们继承了一般的欧式角度符号。然后我们寻找一个映射zzz到复平面,它共享同样的角度符号(但是可能有不一样的长度符号!)。 黎曼曲面上的微分形式 用一半的生命知道你想要什么。然后用另一半知道怎么获得它。在这个情况下,我们知道我们想要什么:一个满足柯西-黎曼方程的映射zzz。但是我们如何计算它呢?为了将这个问题和已有的计算工具连接起来,让我们用外微积分的形式重写柯西-黎曼。实际上,让我们回顾一下在曲面和它们的共形结构的上下文下微分形式的所有思想。像我们将要看到的一样,这种思考方式可以得到一些漂亮的简单几何表示。 重新想想我们对实值微分形式的几何解释:一个k-型测量一些k维的体积(长度、面积等等)。一个需要注意的事情是在n流形上没有n+1维体积可以测量。比如,我们不能在一个曲线上测量2维面积——仅仅只能是1维长度。同样地,我们不能在曲面上测量3维体积——仅仅只能是1维长度和2维面积。由于这些原因,曲面上的微分形式变得非常容易理解: 0-型看起来像标量函数 1-型看起来像向量场 2-型看起来像标量乘面积 然后这个列表就结束了!没有3-型(或者4-型、5-型)需要去考虑的。(一个更代数的方法去说服你自己面对这个现实是去考虑楔形积的反对称性:α∧β=−β∧α\alpha \wedge \beta = - \beta \wedge \alphaα∧β=−β∧α。当你使用超过两个基1-型进行楔形积的时候会发生什么?) 在曲面上Hodge星也非常容易解释。回想Hodge星背后的基本思想:在n维中,我们可以通过一个完全的(n-k)维子空间确定任意一个k维线性子空间。比如,我们可以通过两个基向量描述R3\mathbb{R}^3R3中的一个平面,或者使用一个单独的法向量。在曲面上,最有意思的情况可能是1-型的Hodge星。粗略地说,任意一个1-型α\alphaα都可以由一个等长的正交1-型⋆α\star \alpha⋆α确定: 看起来很熟悉?至少在曲面上,此时可以清晰的是1-型上的Hodge星和共形结构J\mathcal{J}J关系比较紧密。更准确一点,如果α\alphaα是一个曲面上的1-型,那么对于一个正切向量场XXX我们可以定义一个1-型Hodge星: ⋆α(X):=α(JX)\star \alpha (X) \coloneqq \alpha (\mathcal{J}X) ⋆α(X):=α(JX) 换句话说,将⋆α\star\alpha⋆α应用到向量XXX上等价于将α\alphaα应用到旋转后的矩阵JX\mathcal{J}XJX上。2-型上的Hodge星也可以使用共形结构解释。特别的,让ω\omegaω是曲面MMM上任意一个2-型,让XXX是任意一个单位向量场。那么我们有: ⋆ω:=ω(X,JX)\star \omega \coloneqq \omega(X, \mathcal{J}X) ⋆ω:=ω(X,JX) 换句话说,通过使用我们的2-型去测量面积,我们可以确定一个与之关联的“尺度因子”,它就是曲面上的一个标量函数(即,0-型)。 注意到这里我们已经使用了特别的约定:有两个相等的且相对的方向垂直于α\alphaα,并且我们也可以简单的使用约定⋆α(X)=−α(JX)\star \alpha(X) = -\alpha(\mathcal{J}X)⋆α(X)=−α(JX)(很多作者都是这样做的!)一个需要提及的重要的事情是这个选择会如何影响我们对内积的表示。(下面有个练习说这个,没有翻译) 贯穿上下文我们都将使用之前的约定(⋆α(X):=α(JX)\star \alpha(X) \coloneqq \alpha(\mathcal{J}X)⋆α(X):=α(JX)),并且将使用两条竖线∥⋅∥\| \cdot \|∥⋅∥表示对应的范数,即 ∥α∥=:=⟨⟨α,α⟩⟩\| \alpha \| =\coloneqq \sqrt{\langle\langle \alpha, \alpha \rangle\rangle } ∥α∥=:=⟨⟨α,α⟩⟩ 复微分形式 一般地,一个k-型是一个从k个向量到一个标量的多线性映射。然而,一个“标量”不一定是一个实数。比如,当我们看着面积向量,我们将映射f:M→R3f:M \rightarrow \mathbb{R}^3f:M→R3看作一个R3\mathbb{R}^3R3值的0-型,并且它的微分df:TM→R3df:TM \rightarrow \mathbb{R}^3df:TM→R3是一个R3\mathbb{R}^3R3值的1-型。同样的,我们也可以讨论复值的k-型,即函数将k个向量映射得到一个复数。在复数的设定中,很难将k-型解释为测量k维体积的对象(什么是“复”体积?),但是我们仍然保留同样的代数性质(多线性、反对称等等)。我们也有一些新的工具。 就像我们可以共轭复数,我们也可以共轭复值1-型。就像我们所期望的,这个操作简单地翻转了虚部: (αˉ)(X):=(α(X))‾(\bar{\alpha})(X) \coloneqq \overline{(\alpha(X))} (αˉ)(X):=(α(X)) 类似地,我们可以定义(α∧β‾)(X,Υ):=(α∧β(X,Υ))‾(\overline{\alpha \wedge \beta})(X, \Upsilon) \coloneqq \overline{(\alpha \wedge \beta(X, \Upsilon))}(α∧β)(X,Υ):=(α∧β(X,Υ)),其中α∧β‾=αˉ∧βˉ\overline{\alpha \wedge \beta} = \bar{\alpha} \wedge \bar{\beta}α∧β=αˉ∧βˉ(为什么?) 共形参数化 到目前为止我们有了所有我们需要的工具去描述我们共形参数化的算法了。记得我们想找到一个映射z:M→Cz:M \rightarrow \mathbb{C}z:M→C对于所有的正切向量XXX满足柯西-黎曼方程 dz(JX)=idz(X)dz(\mathcal{J}X) = idz(X) dz(JX)=idz(X) 如果我们将dzdzdz解释为复值1-型,我们可以将这个关系重写为 ⋆dz=idz\star dz = idz ⋆dz=idz 注意到这个声明的几何意义不会改变:映射⋆dz\star dz⋆dz在将它映射到平面上之前将它的参数旋转90度;idzidzidz在将它映射到平面之后再旋转向量90度。最终,角度保持不变。我们可以测量映射的衰退变成共形的,通过测量等式坐标和等式右边整个的区别: EC(z):=14∥⋆dz−idz∥2E_C(z) \coloneqq \frac{1}{4} \| \star dz - idz \|^2 EC(z):=41∥⋆dz−idz∥2 等式E(z)E(z)E(z)被称为共形能量。为了计算一个共形映射,我们仅仅需要在合适的约束下求解一个简单的凸二次最优化问题 minzEC(z)\min_z E_C(z) zminEC(z) 然而,首先我们将使用熟悉的对象(比如拉普拉斯)重写能量——这个方案将使它在离散设定中尤其容易设置和求解我们的最优化问题。 很好。所以计算一个共形映射我们只需要知道如何离散化拉普拉斯Δ\DeltaΔ(我们早就在泊松方程中学过了)和带符号的面积A\mathcal{A}A。然而,让我们换个角度看看我们的最优化问题——原来我们说我们想求解 minz∥⋆dz−idz∥2\min_z \| \star dz - idz \|^2 zmin∥⋆dz−idz∥2 这个公式有一个明显的问题,任意一个常量映射z(p)≡z0∈Cz(p) \equiv z_0 \in \mathbb{C}z(p)≡z0∈C都是一个全局的最优解。换句话说,如果我们将整个曲面MMM映射到一个复平面上的点z0z_0z0那么共形能力就是0。(为什么?因为导数dzdzdz在所有位置都为0!)凭直觉我们可以想象我们正试图拉伸一个小的弹性材料片(像一小片橡胶球)覆盖平面的一个大区域。如果我们不固定这个小片在足够的位置,它将会很简单的坍塌为一个点: 我们因此需要增加一些其它的约束去让解更“有趣”。但是我们应该使用哪个约束呢?如果我们使用太少的约束,解可能仍然会看起来很无趣——比如,如果我们仅仅固定我们的弹性片到一个点上,它可能仍然会坍塌到这个点周围。如果我们使用特别多的约束,可能会导致完全误解——换句话说,可能没有完美的共形映射(⋆dz=idz\star dz = idz⋆dz=idz)同时满足所有我们提出的约束。为了更好的理解这个情况,让我们换个角度看看调和函数并且它们如何与全纯映射相关的。 另一种调研调和函数和全纯函数之间关系的方法是考虑我们的最优化问题 minzEC(z)=12⟨⟨Δz,z⟩⟩−A(z)\min_z E_C(z) = \frac{1}{2} \langle\langle \Delta z, z\rangle\rangle - \mathcal{A}(z) zminEC(z)=21⟨⟨Δz,z⟩⟩−A(z) 它的最优解看起来是什么样?为了让问题更容易分析一点,让我们想象映射zzz被规定沿着∂M\partial M∂M,即我们“固定”所有沿着边界的点。从我们在顶点法线的讨论中,你可以会回想起此时符号面积也固定了,由于它可以被解释为一个边界积分。换句话说,如果我们固定了边界那么A(z)\mathcal{A}(z)A(z)对于所有的映射zzz给出了同样的常量,并且我们只需要考虑最小化Dirichlet能量ED(z)=12⟨⟨Δz,z⟩⟩E_D(z) = \frac{1}{2}\langle\langle \Delta z, z \rangle\rangleED(z)=21⟨⟨Δz,z⟩⟩的映射。特别的,由于EDE_DED是正的且是二次的,它在他的梯度消失的时候取得最小,即无论哪里 Δz=0\Delta z = 0 Δz=0 结论:在固定边界条件下共形能量的最优解是调和的。它同时是纯的吗?换句话说,它保角吗?很难过的说,不:尽管每一个共形映射都是调和的,但不是每一个调和映射都是共形的。 从一个实际的透视,这个观测意味着我们不能只随意地固定我们的橡胶片并且希望得到最好的结果——一般的,一个调和映射将不会保持角度不变(就像上面描绘的,我们固定边界到一个给定的矩形)。转变一下,让我们考虑如下的最优化问题: minzEC(z)s.t.∥z∥=1,⟨⟨z,1⟩⟩=0\begin{aligned} \min_z & E_C(z) \\ s.t. & \| z \| = 1, \\ & \langle\langle z, \mathbb{1} \rangle\rangle = 0 \end{aligned} zmins.t.EC(z)∥z∥=1,⟨⟨z,1⟩⟩=0 其中1\mathbb{1}1表示常函数z(p)≡1z(p) \equiv 1z(p)≡1,即函数在每一个点都等于“1”。这些约束在几何上意味着什么呢?假设AAA是曲面f(M)f(M)f(M)的整个面积。那么第二个约束等价为 1A∫MzdA=0\frac{1}{A} \intop_M zdA = 0 A1M∫zdA=0 即解的平均值为0。等价于:解必须以原点为中心。第一个约束保证了解不会坍塌到原点附近,即为了让范数非0,必须至少有一个非零点z(p)≠0z(p) \neq 0z(p)≠0。同时,这些条件是我们能要求的最“薄弱”的事情:我们不知道我们希望我们的映射去哪里,但是我们确定不希望它坍塌! 最后一节是关于最优化求解的,省略。]]></content>
<tags>
<tag>math</tag>
<tag>DDG</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DDG(离散微分几何):拉普拉斯]]></title>
<url>%2Farchives%2F24479.html</url>
<content type="text"><![CDATA[翻译自 DISCRETE DIFFERENTIAL GEOMETRY: AN APPLIED INTRODUCTION 第六章 The Laplacian。如果有翻译错误或者不当的地方希望能指出,谢谢~ 早先我们提到Laplace-Beltrami算子(通常就简写为拉普拉斯)在很多几何和物理等式中都扮演了基本角色。在这一章中我们将拉普拉斯用在针对三角曲面泊松方程的离散版本中。当这章讨论顶点法线时,我们将看到从两个看待问题的不同方式得到的对于拉普拉斯同样的离散表达式(通过余切函数):使用测试函数(test functions)(或者通常被称为Galerkin projection),或者通过对微分形式进行积分(通常被成为离散外微积分)。 基本性质 在我们开始讨论离散化之前,让我们先建立一些拉普拉斯算子Δ\DeltaΔ和标准泊松问题 Δϕ=ρ\Delta \phi = \rho Δϕ=ρ 的事实。泊松方程展现了这个内容上所有的东西——比如,在物理中ρ\rhoρ可能表示质量密度,这种情况下得到的解ϕ\phiϕ(在合适的常数条件下)给出了对应的重力位势。类似的,如果ρ\rhoρ描述了电荷密度那么ϕ\phiϕ则给出对应的电势。在几何处理中非常多的事情可以通过求解一个泊松方程解决(比如,平滑一个曲面、计算带有指定奇点的向量场、甚至计算曲面上的测地距离)。 通常我们的兴趣在于求解没有边界的密实曲面MMM上的泊松方程。 练习 一个二次可微函数ϕ:M→R\phi: M \rightarrow \mathbb{R}ϕ:M→R被称为是调和的(harmonic),当它满足拉普拉斯的核,即Δϕ=0\Delta \phi = 0Δϕ=0。讨论只有在没有边界的密实连通域上的调和函数是常数函数。 你的讨论不必特别正式——有一对简单的刻画了这个主要思想的观测。这个事实是十分重要的因为它暗示了我们可以将一个常数加到泊松方程的任意解上。换句话说,如果ϕ\phiϕ满足Δϕ=ρ\Delta \phi = \rhoΔϕ=ρ,那么ϕ+c\phi + cϕ+c也一样因为Δ(ϕ+c)=Δϕ+Δc=Δϕ+0=ρ\Delta(\phi+c) = \Delta \phi + \Delta c = \Delta \phi + 0 = \rhoΔ(ϕ+c)=Δϕ+Δc=Δϕ+0=ρ。 练习 一个独立的事实是在没有边界的密实连通域上,常数函数不是Δ\DeltaΔ的像。换句话说,不存在函数ϕ\phiϕ使得Δϕ=0\Delta \phi = 0Δϕ=0。为什么? 这个事实也很重要,因为它告诉我们何时给定的泊松方程可以求解。特别的,当ρ\rhoρ有一个常数部分时那么该问题不是适定的(well-posed)。然而,在一些情况下,简单地移除常数部分可能就能说得通了。即,不需要尝试去求解Δϕ=ρ\Delta \phi = \rhoΔϕ=ρ,而是去求解Δϕ=ρ−ρˉ\Delta \phi = \rho - \bar{\rho}Δϕ=ρ−ρˉ,其中ρˉ:=∫MρdV/∣M∣\bar{\rho} \coloneqq \intop_M \rho dV / |M|ρˉ:=∫MρdV/∣M∣并且∣M∣|M|∣M∣是MMM的全部体积。然而,你必须保证这个技巧在你问题的上下文中是说得通的! 当使用像泊松方程这样的偏微分方程时,在函数之间进行内积通常是有用的。一个尤其通用的内积是L2L^2L2内积⟨⋅,⋅⟩\langle\cdot,\cdot\rangle⟨⋅,⋅⟩,它是在全部的域Ω\OmegaΩ上两个函数逐点乘积的积分: ⟨f,g⟩:=∫Ωf(x)g(x)dx\langle f, g \rangle \coloneqq \intop_\Omega f(x)g(x) dx ⟨f,g⟩:=Ω∫f(x)g(x)dx 直观上,这个操作类似于Rn\mathbb{R}^nRn上一般的点乘:它测量对于两个函数“一致”的度。比如,上面两个函数有一个大的内积;下面两个有一个更小的内积(由深蓝色区域指示): 类似的,对于两个向量场XXX和Υ\UpsilonΥ我们可以定义一个L2L^2L2内积: ⟨X,Υ⟩:=∫ΩX(x)⋅Υ(x)dx\langle X, \Upsilon \rangle \coloneqq \intop_\Omega X(x) \cdot \Upsilon(x)dx ⟨X,Υ⟩:=Ω∫X(x)⋅Υ(x)dx 它测量了两个场在每个点处有多“一致”。 使用L2L^2L2内积我们可以解释一个被称为格林第一等式(Green’s first identity)的重要关系。格林等式说的是对于任意满足要求的可微函数fff和ggg: ⟨Δf,g⟩=−⟨∇f,∇g⟩+⟨N⋅∇f,g⟩∂\langle \Delta f, g\rangle = -\langle \nabla f, \nabla g \rangle + \langle N \cdot \nabla f, g \rangle_\partial ⟨Δf,g⟩=−⟨∇f,∇g⟩+⟨N⋅∇f,g⟩∂ 其中⟨⋅,⋅⟩∂\langle \cdot, \cdot \rangle_\partial⟨⋅,⋅⟩∂表示在边界上的内积并且NNN是向外的法线。 关于拉普拉斯的最后一个关键事实是它是半正定的,即Δ\DeltaΔ满足对于所有的函数ϕ\phiϕ都有 ⟨Δϕ,ϕ⟩≥0\langle \Delta \phi, \phi \rangle \geq 0 ⟨Δϕ,ϕ⟩≥0 (顺便,为什么这个值不是严格大于0的呢?)语言无法解释半正定的重要性。让我们考虑一个非常简单的例子:在平面中形式为ϕ(x,y)=ax2+bxy+cy2\phi(x,y) = ax^2 + bxy + cy^2ϕ(x,y)=ax2+bxy+cy2的函数。任意一个这样的函数可以写为矩阵形式: ϕ(x,y)=[xy]⎵xT[ab/2b/2c]⎵A[xy]⎵x=ax2+bxy+cy2\phi(x,y) = \underbrace{\begin{bmatrix} x & y \end{bmatrix}}_{\mathbf{x}^T} \underbrace{\begin{bmatrix} a & b/2 \\ b/2 & c \end{bmatrix}}_A \underbrace{\begin{bmatrix} x \\ y \end{bmatrix}}_{\mathbf{x}} = ax^2 + bxy + cy^2 ϕ(x,y)=xT[xy]A[ab/2b/2c]x[xy]=ax2+bxy+cy2 并且我们可以同样的定义AAA是半正定的。但是它看起来是什么样子?就像下面描绘的,正定矩阵(xTAx>0\mathbf{x}^TA\mathbf{x}>0xTAx>0)看起来像一个碗,半正定矩阵(mathbfxTAx≥0mathbf{x}^TA\mathbf{x}\geq0mathbfxTAx≥0)看起来像半圆柱,不定矩阵(取决于x\mathbf{x}x的值xTAx\mathbf{x}^TA\mathbf{x}xTAx可正可负)看起来像马鞍: 现在假设你是一个滑雪者,正在令人哀嚎的暴风雪中心地带。你非常冷而且精疲力尽,并且你知道你将你的卡车停在了一个平坦的区域,但是它到底在哪里呢?暴风雪刮的特别猛烈并且可见度很低——所以你能做的就是保持你的手指交叉并且沿着山的坡度下降。(相信我:当一个人在进行数值优化的时候他就是这么觉得的!)如果你是聪明的并且在Pos Def Valley滑雪那么你可以就保持向下并且很快就会安全回到卡车。但是可能你会在那天觉得更有一点冒险精神并且去了一趟Semi Def Valley。在这个情况下你仍然将达到底部,但是在找到你的车前可能必须返回高处并且沿着山谷的长度前进。最后,如果你的座右铭是“安全第二”那么你丢下了对暴风雪的小心并且在Indef Valley中驰骋。这种情况下你可能永远也回不去了! 简单点说:半正定矩阵是很好的因为找到它们描述的二次函数的最小值是很容易的——在数值线性代数中有非常多的工具是基于这个思想。对于像拉普拉斯Δ\DeltaΔ,通常可以被看作无穷维矩阵的一类(如果你花一点时间去读下谱定理相关的内容,你会发现这个类比更深的内容),这样的半正定线性算子来讲也是同样。对于几何和物理中通常给定的泊松方程,Δ\DeltaΔ是半正定的可真**是个好东西! 通过有限元方法离散化 一个几何或者物理问题的解通常通过一个函数来描述:地球上每一点的温度,曲面上每一点的曲率,击中在你视网膜上每一点的光亮,等等。所以可能函数组成的空间十分惊人的大——过于大以至于不能在计算机上表示。有限元方法(finite element method (FEM))背后的思想是选取一个更小的函数空间并且并且尝试从这个空间中找到最有可能的解。更特别地,如果uuu是一个问题真正的解并且{ϕi}\{\phi_i\}{ϕi}是基函数(basis functions)的集合,那么我们可以寻找这些方程的线性组合 u~=∑ixiϕi,xi∈R\tilde{u} = \sum_i x_i \phi_i, x_i \in \mathbb{R} u~=i∑xiϕi,xi∈R 使得差值∥u~−u∥\| \tilde{u} - u \|∥u~−u∥对于一些范数来讲足够小。(在上面我们看到一个具体的曲线uuu和通过隆起状的基函数ϕi\phi_iϕi得到的最好的近似u~\tilde{u}u~。) 让我们从一个非常简单的问题开始:假设我们有一个向量v∈R3v \in \mathbb{R}^3v∈R3,并且想在两个基向量e1,e2∈R3e_1,e_2 \in \mathbb{R}^3e1,e2∈R3张成的平面上找到最好的近似v~\tilde{v}v~: 由于v~\tilde{v}v~肯定在平面上,我们能做好的最好的情况是保证误差仅仅在法线方向上。换句话说,我们希望误差v~−v\tilde{v} - vv~−v和两个基向量e1e_1e1和e2e_2e2正交: (v~−v)⋅e1=0(v~−v)⋅e2=0\begin{aligned} (\tilde{v} - v) \cdot e_1 &= 0 \\ (\tilde{v} - v) \cdot e_2 &= 0 \end{aligned} (v~−v)⋅e1(v~−v)⋅e2=0=0 在这个情况下我们得到了对于两个未知数的由两个线性方程组成的系统,并且可以很容易地计算最优向量v~\tilde{v}v~。 现在有了一个更难的问题:假设我们想要解一个标准泊松问题 Δu=f\Delta u = f Δu=f 我们如何验证给定的函数u~\tilde{u}u~是否是最可能的解?基本的思路仍然适用,只不过我们现在的基是函数ϕ\phiϕ而不是有限向量eie_iei,并且简单的向量点乘⋅\cdot⋅由L2L^2L2内积替换。不幸的是,当尝试求解泊松方程的时候我们不知道正确的解uuu长什么样(在其它情况下我们早就知道!)所以不是误差u~−u\tilde{u} - uu~−u,我们将必须看着残差Δu~−f\Delta \tilde{u} - fΔu~−f,它测量了u~\tilde{u}u~有多满足原始的方程。特别的,我们想“测试”残差沿着每个基方向ϕj\phi_jϕj消失: ⟨Δu~−f,ϕj⟩=0\langle \Delta \tilde{u} - f, \phi_j \rangle = 0 ⟨Δu~−f,ϕj⟩=0 再一次得到了一个线性方程系统。这个条件保证了解在大量可能的“测量”集合上表现得像真正的解。 接下来,让我们得到对于一个三角曲面的系统细节。基函数最自然的选择是逐片线性帽函数(hat functions)ϕi\phi_iϕi,它在和它相关的顶点处为1并且在其它所有顶点处为0: 此时你可能会反对:如果所有的函数都是线性的,并且Δ\DeltaΔ是一个二阶导数,每一次我们估计Δu\Delta uΔu的时候不都会得到0吗?幸运的是我们被格林公式拯救了——让我们看看当我们将等式应用到我们的三角网格上的时候会发生什么,通过将积分分解为独立三角形σ\sigmaσ上的求和: ⟨Δu,ϕj⟩=∑k⟨Δu,ϕj⟩σk=∑k⟨∇,∇ϕj⟩σk+∑k⟨N⋅∇u,ϕj⟩∂σk\begin{aligned} \langle \Delta u, \phi_j \rangle &= \sum_k \langle \Delta u, \phi_j \rangle_{\sigma_k} \\ &= \sum_k \langle \nabla , \nabla \phi_j \rangle_{\sigma_k} + \sum_k \langle N \cdot \nabla u, \phi_j \rangle_{\partial \sigma_k} \end{aligned} ⟨Δu,ϕj⟩=k∑⟨Δu,ϕj⟩σk=k∑⟨∇,∇ϕj⟩σk+k∑⟨N⋅∇u,ϕj⟩∂σk 如果这个网格没有边界那么最后的和会消失,因为相邻三角形的法线是相对的方向。因此沿着共享边的边界积分互相抵消了: 这种情况下,在每一个三角形σk\sigma_kσk中我们会剩下简单的 ⟨∇u,∇ϕj⟩\langle \nabla u, \nabla \phi_j \rangle ⟨∇u,∇ϕj⟩ 换句话说,我们可以“测试”Δu\Delta uΔu只要我们可以计算待定解uuu和每一个基函数ϕj\phi_jϕj的梯度。但是记住uuu本身是这些基ϕi\phi_iϕi的线性结合,所以我们有 ⟨∇u,∇ϕj⟩=⟨∇∑ixiϕi,∇ϕj⟩=∑ixi⟨∇ϕi,∇ϕj⟩\langle \nabla u, \nabla \phi_j \rangle = \langle \nabla \sum_i x_i \phi_i, \nabla \phi_j \rangle = \sum_i x_i \langle \nabla \phi_i, \nabla \phi_j \rangle ⟨∇u,∇ϕj⟩=⟨∇i∑xiϕi,∇ϕj⟩=i∑xi⟨∇ϕi,∇ϕj⟩ 现在关键的事情变成了在每个三角形中两个基函数梯度之间的内积。如果我们可以计算这些,那么我们可以简单地构造矩阵 Aij:=⟨∇ϕi,∇ϕj⟩A_{ij} \coloneqq \langle \nabla \phi_i, \nabla \phi_j \rangle Aij:=⟨∇ϕi,∇ϕj⟩ 并且求解对于系数xxx的问题 Ax=bAx = b Ax=b 其中右手边的元素被给定为bi=⟨f,ϕi⟩b_i = \langle f, \phi_i \ranglebi=⟨f,ϕi⟩(即我们仅仅在右手边使用同样的“测量”)。 将所以的事实放在一起(有几个练习没有翻译),我们发现我们可以通过著名的余切公式表示顶点iii处uuu的拉普拉斯 (Δu)i=12∑j(cotαj+cotβj)(uj−ui)(\Delta u)_i = \frac{1}{2} \sum_j (\cot \alpha_j + \cot \beta_j)(u_j - u_i) (Δu)i=21j∑(cotαj+cotβj)(uj−ui) 其中我们在顶点iii的邻域求和。 通过离散外微积分方法离散化 FEM的方式显示了一种相当标准的做法去离散化偏微分方程。但是让我们尝试一个不同的方法,基于离散外微积分(DEC)。相当有趣的是,虽然这两个方式十分不同,但是我们最后会得到一样的结果! 再一次我们想要求解泊松方程Δu=f\Delta u = fΔu=f,(如果你记得我们对于微分算子的讨论)它也可以被表示为 ⋆d⋆du=f\star d \star d u = f ⋆d⋆du=f 我们早就在笔记中概括了如何使用离散外微积分离散化这类表达式,但是让我们一步步来。我们开始于一个0-型uuu,它表示每个顶点处的一个数uiu_iui: 接着,我们计算离散外导数dududu,它仅仅表示我们想要沿着每一条边积分这个导数: (du)ij=∫eijdu=∫∂eiju=uj−ui(du)_{ij} = \intop_{e_{ij}} du = \intop_{\partial e_{ij}} u = u_j - u_i (du)ij=eij∫du=∂eij∫u=uj−ui (注意到边的边界∂eij\partial e_{ij}∂eij就是两个端点viv_ivi和vjv_jvj。)Hodge星将沿着边eije_{ij}eij的环流转变为穿过对应对偶边eij⋆e^{\star}_{ij}eij⋆的通量。特别的,我们拿着沿着原始边的全部环流,将它除以边的长度得到平均逐点环流(average pointwise circulation),然后乘上对偶边的长度得到通过对偶边的全部通量: (⋆du)ij=∣eij⋆∣∣eij∣(uj−ui)(\star du)_{ij} = \frac{|e^{\star}_{ij}|}{|e_{ij}|}(u_j - u_i) (⋆du)ij=∣eij∣∣eij⋆∣(uj−ui) 其中∣eij∣|e_{ij}|∣eij∣和∣eij⋆∣|e^{\star}_{ij}|∣eij⋆∣分别为原始的和对偶边的长度。接下来,我们取⋆du\star du⋆du的导数和在整个对偶胞腔上的积分: (d⋆du)i=∫Cid⋆du=∫∂Ci⋆du=∑j∣eij⋆∣∣eij∣(uj−ui)(d \star du)_i = \intop_{C_i}d \star du = \intop_{\partial C_i} \star du = \sum_j \frac{|e^{\star}_{ij}|}{|e_{ij}|}(u_j - u_i) (d⋆du)i=Ci∫d⋆du=∂Ci∫⋆du=j∑∣eij∣∣eij⋆∣(uj−ui) 最后的Hodge星简单地将这个量除以CiC_iCi的面积得到这个胞腔上的平均值,并且我们最后得到一个线性方程系统 (⋆d⋆du)i=1Ci∑j∣eij⋆∣∣eij∣(uj−ui)=fi(\star d \star du)_i = \frac{1}{C_i}\sum_j \frac{|e^{\star}_{ij}|}{|e_{ij}|}(u_j - u_i) = f_i (⋆d⋆du)i=Ci1j∑∣eij∣∣eij⋆∣(uj−ui)=fi 其中fif_ifi是在顶点iii处右边的值。(对称矩阵通常更容易进行数值处理并且可以推导出更有效的算法。)另外一个看待这个变换的方式是想象我们离散化这个系统 d⋆du=⋆fd \star du = \star f d⋆du=⋆f 换句话说,我们将0-型项的等式转变为n-型项的等式。当在曲面上操作的时候,算子d⋆dd\star dd⋆d有时被作为共形拉普拉斯(conformal Laplacian),因为当我们对曲面进行共形变换的时候它不会改变。或者说,我们可以将d⋆dd\star dd⋆d就看作是一个给我们在网格上每一个对偶胞腔上积分的拉普拉斯值(而不是逐点的值)的算子。 网格和矩阵 目前我们已经给出了一类像拉普拉斯算子的“算法的”描述。比如,我们确定在顶点iii处的标量函数uuu的拉普拉斯可以约等于 (Δu)i=12∑j(cotαj+cotβj)(uj−ui)(\Delta u)_i = \frac{1}{2}\sum_j (\cot \alpha_j + \cot \beta_j) (u_j - u_i) (Δu)i=21j∑(cotαj+cotβj)(uj−ui) 其中和是在iii的邻域jjj上获得的。在代码中,求和可以简单的写为循环并且在任意一个顶点处估计。然而,使用离散微分算子的一个关键部分是建立它们的矩阵表示。将算子编码成矩阵的动机是我们可以使用标准的数值线性代数包求解像 Δu=f\Delta u = f Δu=f 的系统。(为了让事情更麻烦,一些线性求解器非常喜欢使用被成为回调函数的算子的算法表示——然而,实际上我们就是需要矩阵。) 在泊松方程的情况中,我们想要对于任意一个顶点处值的向量u∈R∣V∣u \in \mathbb{R}^{|V|}u∈R∣V∣构造矩阵L∈R∣V∣×∣V∣L \in \mathbb{R}^{|V|\times|V|}L∈R∣V∣×∣V∣(其中∣V∣|V|∣V∣是网格上顶点的数量),表达式LuLuLu有效的估算上面的公式。但是让我们从一些更简单的事情开始——考虑一个算子BBB,它计算所有邻域值的和: (Bu)i=∑juj(Bu)_i = \sum_ju_j (Bu)i=j∑uj 我们如何构造一个矩阵表示这个算子呢?将BBB考虑为一个机器,它在每一个顶点处取输入向量uuu并且将另一个向量BuBuBu作为输出。为了让这个道路说得通,我们需要知道哪个值对应哪个顶点。换句话说,我们需要给我们网格的每个顶点分配一个独立的索引,在范围1,…,∣V∣1,\dots,|V|1,…,∣V∣中: 我们给哪个顶点具体分配哪个数字并不是很重要,只要没个顶点都有一个数字并且每个数字都有一个顶点就可以了。这个网格有12个顶点并且顶点1和顶点2,3,4,5,9相邻。因此我们可以计算邻域值的和为 这里无论何时顶点jjj是顶点1邻域的时候我们都将“1”放在第jjj个位置上,其它的位置放0。因此这行给出了第一个顶点处的“输出”值,我们将它作为矩阵BBB的第一行。这个矩阵的元素看起来像这样: (你可以验证这个矩阵是对的,或者你可以出去晒晒太阳,这是你的选择。)实际上,幸运的是,我们不必“手动“构建矩阵——我们可以简单的开始于0矩阵然后通过循环我们网格的局部邻域去使用非0元素填充它。 最后,一个重要的需要注意的事情是BBB中很多元素都是0。实际上,对于大多数离散算子0元素的数量远远超过非0元素的数量。因为这个原因,使用稀疏矩阵通常是个好主意,即仅仅储存非0元素位置和值的数据结构(而不是明确的储存矩阵中每一个独立元素)。稀疏矩阵数据结构的设计是一个非常有趣的问题,但是概念上你可以想象稀疏矩阵就是一个元组(i,j,x)(i,j,x)(i,j,x)的列表,其中i,j∈Ni,j\in \mathbb{N}i,j∈N表示非0元素行和列的索引并且x∈Rx\in\mathbb{R}x∈R给出了它的值。]]></content>
<tags>
<tag>math</tag>
<tag>DDG</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DDG(离散微分几何):离散曲面的曲率]]></title>
<url>%2Farchives%2F679.html</url>
<content type="text"><![CDATA[翻译自 DISCRETE DIFFERENTIAL GEOMETRY: AN APPLIED INTRODUCTION 第五章 Curvature of Discrete Surfaces。如果有翻译错误或者不当的地方希望能指出,谢谢~ 对于一个R3\mathbb{R}^3R3中的平滑曲面,法线方向很容易定义:它是正交于所有切向量的唯一方向——换句话说,它是”笔直插入“曲面的方向。对于离散曲面这个问题就不是这么简单了。如果一个网格有共面的面(所有顶点在一个公共平面上)那么这个法线当然上很好定义的:它就是这个平面的法线。但是如果这个多边形不是共面的,或者当我们要算一个顶点处的法线,那么这个法线应该如何定义就不是很清晰了。 实际上有很多种不同的可能,它们都分别来自看待平滑几何的不同方式。但是在深入之前,让我们先建立几个基本的几何事实。 向量面积 这里有一个简单的问题:你如何计算平面中一个多边形的面积?假设我们的多边形有顶点p1,p2,⋅,pnp_1,p_2,\cdot, p_np1,p2,⋅,pn。一个计算面积的方法是像下图左边一样将另外一个点qqq放到中间,并且将所有三角形1,pi,pi+11,p_i,p_{i+1}1,pi,pi+1的面积求和: 一个有趣的事实是如果我们将点qqq放在任意位置,然后将带符号的三角形面积求和,我们仍然可以得到多边形的面积!(带符号的面积意思是当我们的顶点是顺时针方向的时候是负的;逆时针方向时是正的。)你可以仅仅通过看着这幅图来想象为什么会发生这种事情:正的三角形覆盖了“非常多”为负的三角形计算的面积。 这个证明是Stokes‘定理的一个应用——考虑对于一个平面多边形PPP面积AAA的不同的表达式: A=∫Pdx∧dyA = \intop_P dx \wedge dy A=P∫dx∧dy 注意到dx∧dy=d(x∧dy)=−d(y∧dx)dx \wedge dy = d(x \wedge dy) = -d(y \wedge dx)dx∧dy=d(x∧dy)=−d(y∧dx),我们可以将这个面积的表达式写为 A=12∫Pd(x∧dy)−d(y∧dx)=12∫∂Px∧dy−y∧dxA = \frac{1}{2}\intop_P d(x \wedge dy) - d(y \wedge dx) = \frac{1}{2}\intop_{\partial P} x \wedge dy - y \wedge dx A=21P∫d(x∧dy)−d(y∧dx)=21∂P∫x∧dy−y∧dx 其中我们在最后一步中应用Stokes‘定理将我们在整个曲面上的积分转换到仅仅在边界上。现在假设我们多边形的顶点有坐标pi=(xi,yi)p_i = (x_i, y_i)pi=(xi,yi)。从这里我们可以明确得到边界积分,通过将每一条边eije_{ij}eij上的积分求和: ∫∂Px∧dy−y∧dx=∑∫eijx∧dy−y∧dx\intop_{\partial P}x \wedge dy - y \wedge dx = \sum \intop_{e_{ij}} x\wedge dy - y \wedge dx ∂P∫x∧dy−y∧dx=∑eij∫x∧dy−y∧dx 由于坐标函数xxx和yyy沿着每条边是线性的(它们的微分dxdxdx和dydydy因此是常数),我们可以将这些积分写为 ∑∫eijx∧dy−y∧dx=∑xi+xj2(yj−yi)−yi+yj2(xj−xi)=12∑(pi+pj)×(pj−pi)=12∑pi×pj−pi×pi−pj×pj−pj×pi=∑pi×pj\begin{aligned} \sum \intop_{e_{ij}} x \wedge dy - y \wedge dx &= \sum \frac{x_i + x_j}{2}(y_j - y_i) - \frac{y_i + y_j}{2}(x_j - x_i) \\ &= \frac{1}{2} \sum (p_i + p_j) \times (p_j - p_i) \\ &= \frac{1}{2} \sum p_i \times p_j - p_i \times p_i - p_j \times p_j - p_j \times p_i \\ &= \sum p_i \times p_j \end{aligned} ∑eij∫x∧dy−y∧dx=∑2xi+xj(yj−yi)−2yi+yj(xj−xi)=21∑(pi+pj)×(pj−pi)=21∑pi×pj−pi×pi−pj×pj−pj×pi=∑pi×pj 简而言之,我们证明了多边形的面积可以被简单写为 A=12∑ipi×pjA = \frac{1}{2} \sum_i p_i \times p_j A=21i∑pi×pj 我们只看多边形面积这种情况的更一般的一个版本是曲面块f:M→R3f:M \rightarrow \mathbb{R}^3f:M→R3的向量面积(vector area),它由整个域上的曲面法线的积分定义: NV=∫MNdAN_\mathcal{V} = \intop_M NdA NV=M∫NdA 向量面积一个非常好的性质是它仅仅依赖边界∂M\partial M∂M的形状。作为结论,两个看起来十分不同的曲面(比如上面两个)仍然可以有相同的向量面积————这里的物理直观意义是向量面积测量了穿过边界曲面的全部通量。 对于一个平坦的区域曲面上的法线是常量,因此我们通过通常意义上的面积乘上单位法线就可以得到向量面积。当曲面不是平坦的时候事情变得更有趣了——比如,一个环带的向量面积是0因为相对的法线互相抵消了: 这里有另一个相当基础的问题:考虑一个R3\mathbb{R}^3R3中的三角形,并且想象我们被允许去拉动它的一个顶点ppp。使它的面积AAA增长最快的方法是什么?换句话说,AAA相对于ppp的梯度是什么? 你应该仅仅需要一些非常简单的几何参数——没有将内容写到坐标系中的需要等等。 面积梯度 将这些事实都先抛开,让我们看看定义顶点法线的几种不同的方法。大体上有两种从平滑图像上自然引申的定义:面积梯度(area gradient)和体积梯度(volume gradient);我们从前面那个开始。 面积梯度是问,“为了尽可能快的增加全部面积AAA,我们应该往哪个方向“推”曲面?让所有的点沿着切向滑行显然什么也无法改变:我们仅仅会得到同样的曲面。实际上,为了增加曲面面积我们唯一能做的事情就是让曲面沿着法向运动。被用来定义顶点法线的想法是面积相对于给定顶点的梯度。 由于我们已经知道了图和表示单个三角形σ\sigmaσ的面积梯度(上一节有一个练习没有翻译),我们可以简单地表示整个曲面的面积梯度: ∇pA=∑σ∇pAσ\nabla_p A = \sum_{\sigma} \nabla_p A_{\sigma} ∇pA=σ∑∇pAσ 当然,一个给定的顶点ppp只会影响与ppp相邻的三角形的面积。所以我们只需要将这一个小部分三角形的面积梯度求和。 平均曲率向量 上一个练习(没有翻译)中推导的面积梯度的表达式展现了离散微分几何上的所有东西,通常被成为余切公式(cotan formula)。足够有趣的是当使用一个完全不同的方法,被成为平均曲率向量(mean curvature vector)HNHNHN,去定义顶点法线时同样的表达式一样会出现。特别的,对于一个平滑曲面f:M→R3f:M \rightarrow \mathbb{R}^3f:M→R3我们有 Δf=2HN\Delta f = 2 HN Δf=2HN 其中HHH是平均曲率,NNN是单位曲面法线(我们想要去计算的东西),Δ\DeltaΔ是Laplace-Beltramis算子(继续看下面)。因此,另一个定义离散曲面上顶点法线的方法简单地使用了离散拉普拉斯算子到顶点的坐标上并将得到的向量归一化。 这个问题现在变成,“你如何离散化拉普拉斯?“我们将在未来仔细看看这个问题,但是值得记录的事实是Δ\DeltaΔ最直接的离散化将恰好带领我们回到余切公式!换句话说,我们从平均曲率向量得到的顶点法线和我们从面积梯度得到的是完全相同的。 这全部的理论帮助我们对于Laplace-Beltrami算子Δ\DeltaΔ本身获得更好的直观感觉。不幸地,没有一个真正很好的方法去写下Δ\DeltaΔ——你将在一些微分几何课本中找到的标准的坐标公式是Δϕ=1∣g∣∂∂xi(ggij∂∂xjϕ)\Delta\phi = \frac{1}{\sqrt{|\mathsf{g}|}} \frac{\partial}{\partial x^i}(\sqrt{\mathsf{g}} \mathsf{g}^{ij} \frac{\partial}{\partial x^j} \phi)Δϕ=∣g∣1∂xi∂(ggij∂xj∂ϕ),其中g\mathsf{g}g是度量。然而,这个混乱的表达式对于Δ\DeltaΔ到底做了什么事几乎没有提供一点直观感受,并且当它被用来离散化时,由于对于一个三角网格我们从来都没有一个g\mathsf{g}g的坐标表示,因此几乎是没有用的!更早些时候,我们看到(0-型)拉普拉斯可以被表示为Δ=⋆d⋆d\Delta = \star d \star dΔ=⋆d⋆d,它引导了一个相当直接的离散化。但是现在,我们将使用另一个我们更早学到的工具:共形参数化(conformal parameterization)。记得当fff是一个共形映射,那么MMM上的长度和f(M)f(M)f(M)上的长度通过一个正尺度eue^ueu相关。换句话说,对于一些MMM上的实值函数uuu有∣df(X)∣=eu∣X∣|df(X)| = e^u|X|∣df(X)∣=eu∣X∣。此外,共形参数化总是存在的——换句话说,为了使用共形坐标进行证明或者其它的计算我们不必对我们的几何设置一些特殊的假设。因此当讨论Laplace-Beltrami时共形坐标是有用的,我们可以将Δ\DeltaΔ就写为平面拉普拉斯的重新尺度化,即由二阶偏导的和除以测量尺度因子e2ue^{2u}e2u: Δϕ=d(dϕ(X))(X)+d(dϕ(Υ))(Υ)e2u\Delta \phi = \frac{d(d\phi(X))(X) + d(d\phi(\Upsilon))(\Upsilon)}{e^{2u}} Δϕ=e2ud(dϕ(X))(X)+d(dϕ(Υ))(Υ) 其中XXX和Υ\UpsilonΥ是任意一对单位正交的方向。 这里的几何意义是什么呢?记得对于1D中一个普通的传统函数ϕ:R→R\phi:\mathbb{R} \rightarrow \mathbb{R}ϕ:R→R,二阶导数本质上告诉我们的是函数的曲率,即它是凹的还是凸的? 当然,由于Δ\DeltaΔ是二阶导数的和,它会告诉我们一些关于平均曲率的事情也就不是很让人惊讶了! 体积梯度 另一个构造法线的方法是使用体积梯度(volume gradient)。假设我们的曲面包住了体积为V\mathcal{V}V的一些空间区域。和之前一样,我们知道沿着曲面的切向滑动它自己不能真正改变什么东西:我们会得到相同的曲面,它包住了同样的空间区域。因此,最快增加V\mathcal{V}V的方法还是在法线方向上移动曲面。一个多少有点令人惊讶的事实是,在离散情况下,体积梯度相对于面积梯度的确对于顶点法线得到了不同的定义。为了明确这个梯度,我们将使用从上面得到的“基本事实”的3维版本。 首先,很像我们将多边形面积分成三角形,我们将被我们的曲面包住的体积分解为一系列四面体。通过引入一个新点qqq,每一个四面体恰好包含我们离散的一个面。比如,这是顶点ppp附近的体积看起来可能的样子: 就像在多边形的情况中qqq的位置不会造成什么影响一样,我们也使用带符号的四面体体积。(你可以证明它吗?) 接下来,对于单个的四面体而言体积梯度表示什么?一个四面体体积的写法是 V=13Ah\mathcal{V} = \frac{1}{3}Ah V=31Ah 其中AAA是底面三角形的面积并且hhh是高。然后使用和三角形情况中同样类型的几何推理,我们知道 ∇pV=13AN\nabla_p \mathcal{V} = \frac{1}{3}AN ∇pV=31AN 其中NNN是底面的单位法线。 为了写下曲面内部体积相对于给定顶点ppp的梯度,我们简单地对包含ppp的四面体的梯度求和: ∇pV=∑iVi=13∑iAiNi\nabla_p \mathcal{V} = \sum_i \mathcal{V}_i = \frac{1}{3} \sum_i A_i N_i ∇pV=i∑Vi=31i∑AiNi 乍一看这个求和不能引导为一个对于ΔpV\Delta_p \mathcal{V}ΔpV的很好的表达式——比如,它使用了底面的法线NiN_iNi,它和我们当前曲面的几何几乎没什么关系。然而,记住我们可以将qqq放在任意一个我们想放的位置并且对于仍然可以得到同样的表达式。特别的,当我们直接将qqq放到ppp上时,那么NiN_iNi和AiA_iAi(分别)表示我们原始曲面中包含点ppp面的法线和面积: 其它的定义 目前为止我们仅仅看到了从一些平滑定义中推导出来的顶点法线定义。这种思考方式刻画了离散微分几何的本质内涵:平滑设定中的关系应该在离散设定中保证不收到干扰(即Δf=2HN\Delta f = 2HNΔf=2HN应该不管在Δ,H,N\Delta,H,NΔ,H,N是离散对象还是平滑对象时都是对的。)不过,仍然有大量对于顶点法线公共的定义在平滑世界中没有已知的原始对象。(也许你可以找到一个呢?) 统一加权 可能得到顶点法线最简单的方法是对相邻面的法线求和: Nu:=∑iNiN_u \coloneqq \sum_i N_i Nu:=i∑Ni 这个方法的主要缺点是同一个几何的两个不同的细分曲面可能得到非常不同的顶点法线,如同上图描绘的一样。 顶角权重 一个减少对于细分曲面依赖的简单方法是对每一个面的法线使用它们对饮的顶角(tip angles)θ\thetaθ进行加权,即感兴趣顶点处的内角: Nθ:=∑iθiNiN_{\theta} \coloneqq \sum_i \theta_i N_i Nθ:=i∑θiNi 球雕刻的多面体 这里有另一个有趣的方式得到顶点法线:考虑球面S2\mathcal{S}^2S2包含所有R3\mathbb{R}^3R3中到原点单位距离的点。关于球面一个有意思的事实是在点x∈S2x \in \mathcal{S}^2x∈S2处的单位法线NNN就是这个点自己!即N(x)=xN(x) = xN(x)=x。所以如果我们开始于一个多面体,它的所有顶点都在球面上,一个定义顶点法线的可解释的方法就是简单的使用这个顶点的坐标。 实际上,这并不是太难证明在顶点pip_ipi处法线的方向可以只使用边向量eij=pj−pie_{ij} = p_j - p_ieij=pj−pi项表示,其中pjp_jpj是pip_ipi紧邻的邻域。特别的,我们有 NS=1c∑j=0n−1ej×ej+1∣ej∣2∣ej+1∣2N_S = \frac{1}{c} \sum_{j=0}^{n-1} \frac{e_j \times e_{j+1}}{|e_j|^2 |e_{j+1}|^2} NS=c1j=0∑n−1∣ej∣2∣ej+1∣2ej×ej+1 其中常数c∈Rc \in \mathbb{R}c∈R可以被忽略因为我们仅仅对法线的方向感兴趣。(对于这个表达式的推导细节,请看Max的“Weights for Computing Vertex Normals from Facet Normals”)当然,由于这个表达式仅仅依赖于边向量它可以被使用砸任何一个网格上(不仅仅是在球中被雕刻出来的)。 这章后面都是练习,内容不翻译了。但是这些练习都很有价值,值得思考。]]></content>
<tags>
<tag>math</tag>
<tag>DDG</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DDG(离散微分几何):对外微积分快速粗暴的一个介绍]]></title>
<url>%2Farchives%2F40828.html</url>
<content type="text"><![CDATA[翻译自 DISCRETE DIFFERENTIAL GEOMETRY: AN APPLIED INTRODUCTION 第四章 A Quick and Dirty Introduction to Exterior Calculus。如果有翻译错误或者不当的地方希望能指出,谢谢~ 许多微分几何中重要的概念都可以使用外微积分(exterior calculus)很好地解释。起初这些概念看起来真的很像你从向量微积分(vector calculus)里面知道的对象,并且你可能会怀疑给它们起一个这么时髦的新名字的意义。比如,标量场不再被称为标量场,现在被称为0型(0-forms)!在长时间的学习中我们将会看到这个新语言使得从向量微积分中概括确定的想法变得容易——一个重要的例子是Stokes定理,它和离散化紧密相关,并且是根本地从计算上。 外微积分的基本内容可以被分为几个部分: 线性代数:小箭头 如果你曾经学过线性代数,你可能记得有些东西必须使用“小箭头”——也被称为向量。实际上,如果这是你能记起来关于线性代数的所有内容,那么现在是一个绝佳的好时机回去重新学一遍!我们这里将不会复习。 向量微积分:小箭头是怎么变化的? 同样的,如果你学过向量微积分,那么你记得向量微积分必须随着“小箭头”如何在空间和时间上变化操作(例如,风向变化有多快)。换句话说,向量微积分告诉我们如何微分向量。我们也同样不会复习这个话题! 外代数:小体积 线性代数探索了很多你可以用向量操作的事情:你可以加它们,你可以数乘它们,你可以进行内积,可以外积,还有其它的操作。外代数仅仅增加了几个操作到这个列表里面,这让讨论像面积和体积这样的东西更简单。特别的,这些操作让我们构造出被称为k-向量(k-vectors)的东西,它可以被当作“小k-维体积(little k-dimensional volumes)”。 外微积分:小体积怎么变化? 最后,如果向量微积分是研究“小箭头”如何在空间和时间上变化的课题,那么外微积分是研究小体积如何在空间和时间上变化的课题。换句话说,外积分告诉我们如何微分k-向量。 这是一个很大的画面:外微积分对于外代数就像向量积分对于线性代数一样。小体积是有用的,因为它使用非常通用的上下文帮助我们讨论积分。如果这个内容听起来仍然让你感到比较茫然,那么继续读下去吧! 外代数 (注意:k-向量和k维向量在这里面表示的意义是不一样的) 像上面提到的一样,就像线性代数是“小箭头”合适的专用语,外代数(exterior algebra)也是“小体积”,我们称为k-向量,的专用语。字母“k”表示维度,比如,一个1-向量表示一个“小长度”,一个2-向量表示一个“小面积”等等。关于普通向量需要记住的基本内容是它们编码了两个基本信息:方向(direction)和大小(magnitude)。同样地,k-向量也有方向和大小,虽然对于k-维的体积来讲“方向”的概念要比一维向量更复杂一点。它拥有完全的一般性,外代数在任意向量空间VVV中都很合理,但是为了让内容保持简单我们现在仅仅将它使用于我们熟悉的例子,比如平面R2\mathbb{R}^2R2、三维空间R3\mathbb{R}^3R3,或者更一般的,n维空间Rn\mathbb{R}^nRn。 热身:1-向量和2-向量 你如何描述Rn\mathbb{R}^nRn中的体积?粗略地讲,外代数的基本思想是k维的体积可以使用k个向量描述。在线性代数中我们有类似的思想:通过张成,k个向量可以被用来描述k维线性空间(一个向量张成一条线;两个向量张成一个平面;等等)。这两个情况中对于特定的向量选择都不是重要的内容:比如,就像有很多不同的向量对可以张成一样的平面,很多不同的向量对都可以被用来描述同样的2-向量。总体上,出现在外代数中的k-向量和线性空间也没有那么不一样,除了: 它们有“有限的大小”,即它们有一个大小 它们有朝向(orientation) 我们想通过“朝向”表示什么?一个很好的类比是考虑一条线lll和一个向量vvv之间的区别: 一条线带有方向但是并没有朝向的含义,即没有任何标记表示沿着这条线的某条路是“向前”或者“向后”。相对的,一个向量编码了方向和明确的朝向(即,+v+v+v和−v-v−v是相对的方向);除此之外,一个向量有明确的大小,通过它的长度给出。线和向量的类比刻画了k-向量背后的基本思想:一个k-向量相对于k维线性空间就像向量相对于线。实际上,普通向量给我们提供了第一个外代数中对象的例子:一个1-向量就是一个普通向量。 那么2-向量呢?一个对2-向量相当好的可视化就是把任意两个三维空间R3\mathbb{R}^3R3中的向量u,vu,vu,v与一个小平行四边形张成的体积联系起来: 作为简略表达的方式,我们将记这个小平行四边形或者2-向量为u∧vu \wedge vu∧v(这里∧\wedge∧符号发“wedge”的音)。和普通向量一样,两个2-向量被认为是“一样的”,如果它们有同样的大小和方向。比如,所以下图中的平行四边形都被小心构造成有同样的面积。所以这三个都描述了同样的2-向量,即使它们被不同的量歪斜和拉伸: 在这个意义上,我们的平行四边形图画仅仅是2-向量的“漫画”,因为它们每个都只描绘了很多种可能中的一种。然而,由于平行四边形忠实地表示了许多2-型的特征,我们可以使用它们去调研一般的2-型运转的方式。 首先并且是最重要的,我们如何定义一个2-向量的朝向?对于1-向量,想法很简单:这两个(1-)向量+u+u+u和−u-u−u有着相对的朝向,因为它们沿着同一条直线指向相反的方向。同样的,我们可以认为R3\mathbb{R}^3R3中的2-向量也有两个相对的朝向:“上”或“下”,分别对应平面上的两个可能的单位法线:+N+N+N或−N-N−N。因此我们将区别两个表达式u∧vu \wedge vu∧v或v∧uv \wedge uv∧u,记u∧v=−v∧uu \wedge v = -v \wedge uu∧v=−v∧u以表示它们有相反的朝向: 我们可以通过小平行四边形获取哪些性质呢?对于一个大小,它看起来必须是成立的,那就是 u∧u=0u \wedge u = 0 u∧u=0 由于由两个相同的向量描述的“平行四边形”完全没有任何面积!这个思想很好地对应了思想u∧v=−v∧uu \wedge v = -v \wedge uu∧v=−v∧u,因为当u=vu = vu=v时我们就有u∧u=−u∧uu \wedge u = -u \wedge uu∧u=−u∧u。 另一个我们可以注意到的事情是仅仅使用因子a∈Ra \in \mathbb{R}a∈R给一个其中向量缩放尺度,将会以同样的大小缩放平行四边形的整个面积: 我们因此可以通过规则来编码这个性质: (au)∧v=a(u∧v)(au) \wedge v = a(u \wedge v) (au)∧v=a(u∧v) 因此,当我们缩放第二个向量而不是第一个的时候同样的事情将会发生,即u∧(av)=a(u∧v)u \wedge (av) = a(u \wedge v)u∧(av)=a(u∧v)。 当我们加向量的时候平行四边形的性质应该如何解释?下面的图帮助回答了这个问题: 坐标两个面积的和可以被解释为u∧v1+u∧v2u \wedge v_1 + u \wedge v_2u∧v1+u∧v2;右边的面积是u∧(v1+v2)u \wedge (v_1 + v_2)u∧(v1+v2)。中间这个图解释了这两个量是相等的,因为我们丢失的面积等价于我们获得的面积。换句话说,它看起来就是 u∧v1+u∧v2=u∧(v1+v2)u \wedge v_1 + u \wedge v_2 = u \wedge (v_1 + v_2) u∧v1+u∧v2=u∧(v1+v2) (你能在3D中构造出类似的图吗?) 为了获得最后一个性质,我们必须考虑体积而不是面积了,我们将考虑描绘一些平行六面体: 就像2-向量一样,我们记这个小体积或者说3-向量为u∧v∧wu \wedge v \wedge wu∧v∧w。除此之外,我们可以认为这个3-向量是首先由两个向量构造一个小平行四边形,然后沿着第三个向量挤压这个平行四边形得到的: 注意到顺序在这里似乎真的没有什么影响:我们可以先构造2-向量u∧vu \wedge vu∧v然后沿着www扩展它,或者我们可以先构建v∧wv \wedge wv∧w然后沿着uuu去扩展它。我们可以总结这个观察到的内容通过 (u∧v)∧w=u∧(v∧w)(u \wedge v) \wedge w = u \wedge (v \wedge w) (u∧v)∧w=u∧(v∧w) 这意味着我们可以简单地写成u∧v∧wu \wedge v \wedge wu∧v∧w并且对于我们想表达的体积不带有任何歧义。然而,当我们调换被用来构造初始平行四边形的的两个向量顺序时将会发生什么?早先我们说过u∧v=−v∧uu \wedge v = -v \wedge uu∧v=−v∧u,即交换向量的顺序会交换2-向量的朝向。因此,我们有(u∧v)∧w=−(v∧u)∧w(u \wedge v) \wedge w = - (v \wedge u) \wedge w(u∧v)∧w=−(v∧u)∧w,或者就写为 u∧v∧w=−v∧u∧wu \wedge v \wedge w = - v \wedge u \wedge w u∧v∧w=−v∧u∧w 好的,但是这个声明在几何上意味着什么呢?这个负号似乎指示这两个小体积除了朝向以外是相同的。但是朝向对于体积来讲意味着什么?对于向量我们有两个朝向(+u+u+u和−u-u−u)对应“向前”和“向后”;对于2-向量我们有两个朝向(u∧vu \wedge vu∧v和v∧uv \wedge uv∧u)对应平面“上”和“下”的朝向。同样的,我们可以想象一个小体积也有“向内”或“向外”的朝向——比如,你可以想象边界上的法线指向里面或者指向外面,或者边界的一边被刷成红色并且另一边被刷成蓝色。这两个情况中任何一种都只有两个朝向。稍微更深入一点我们可能注意到,每次我们交换3-向量中一对连续的向量都会切换朝向;如果我们切换另一对那么朝向会切换回来。因此,任意的向量偶置换都保证朝向不变,任意奇置换都调换朝向。换句话说,这些3-向量 u∧v∧w=u∧w∧v=w∧u∧vu \wedge v \wedge w = u \wedge w \wedge v = w \wedge u \wedge v u∧v∧w=u∧w∧v=w∧u∧v 都有相同的朝向,并且如下3-向量 w∧v∧u=v∧u∧w=u∧w∧vw \wedge v \wedge u = v \wedge u \wedge w = u \wedge w \wedge v w∧v∧u=v∧u∧w=u∧w∧v 都有相同的朝向,但是这两组的朝向是相反的。 楔形积 我们已经建立了一系列关于小体积使用必须遵守的规则,这提供了楔形积(wedge product)∧\wedge∧的定义。特别的,对于任意一组向量u,v,w∈Rnu,v,w \in \mathbb{R}^nu,v,w∈Rn和标量a,b∈Ra,b \in \mathbb{R}a,b∈R的我们有 反对称性(Antisymmetry) u∧v=−v∧uu \wedge v = -v \wedge uu∧v=−v∧u 结合律(Associativity) (u∧v)∧w=u∧(v∧w)(u \wedge v) \wedge w = u \wedge (v \wedge w)(u∧v)∧w=u∧(v∧w) 加法分配律(Distributivity over addition) u∧(v+w)=u∧v+u∧wu \wedge (v + w) = u \wedge v + u \wedge wu∧(v+w)=u∧v+u∧w 标量乘法分配律(Distributivity of scalar multiplication) (au)∧(bv)=ab(u∧v)(au) \wedge (bv) = ab(u \wedge v)(au)∧(bv)=ab(u∧v) 实际上,这些规则提供了楔形积在任意向量空间中对于任意数量的向量如何表现的正确印象。现在我们放下所有完全给出的正式定义——需要记住的更重要的事情是这些规则是怎么来的。换句话说,“小体积”的行为是如何激励我们在最开始的地方写下这个列表的?如果你可以在你的大脑中获取这些几何图片,就会很自然地得到这些规则。(相反的,如果你不花时间思考楔形积背后的几何,你可能一直都很困惑!) 操作一些具体的例子(即你的作业)应该会帮助对k-向量和楔形积构建一些直觉。稍后我们将在谈到与线性空间的不同中重新回顾楔形积:并不是Rn\mathbb{R}^nRn中的独立向量,我们将会考虑整个向量场,带到微分形式的思想中。 Hodge星 通常,通过指定补来指定这个集合是容易的。比如,如果你问我,“你喜欢什么食物?”更容易去说“我喜欢除了Natto和Doogh之外所有的食物”,而不是说”我喜欢披萨、苹果、汉堡、寿司……“。在线性代数中,这个思想一个很好例子是正交补(orthogonal complement):如果我想确定一个n维线性空间VVV中的k维线性空间W⊂VW \subset VW⊂V,我们可以提供张成WWW的一系列向量w1,…,wkw_1, \dots, w_kw1,…,wk,或者张成不在WWW所有向量的一系列向量w~1,…,w~n−k\tilde{w}_1, \dots, \tilde{w}_{n-k}w~1,…,w~n−k,即它的正交补。比如,R3\mathbb{R}^3R3中的一个平面可以被张成它的两个向量指定,或者也可以被一个作为它法线的单个向量指定: 在外积分中,Hodge星⋆\star⋆(念“star”)提供了一种k-向量的正交补。特别的,如果我们有一个Rn\mathbb{R}^nRn中的k-向量,那么⋆v\star v⋆v将是(n-k)-向量,带有某种“互补的”意思。我们想通过互补表达什么?一个很好的初始例子是R3\mathbb{R}^3R3中的2-向量: 就像R3\mathbb{R}^3R3中的平面可以等价于它的单位法线(它张成了平面的正交补),一个2-向量也可以等价于法线方向的一些向量。但是是哪一个向量呢?不像线性空间,我们需要去对于这个1-向量⋆(u∧v)\star(u \wedge v)⋆(u∧v)选取一个明确的量和方向。这里没有“最好的”选择;我们需要简单的选择一个惯例并确定它——一个好的类比是右手法则被用来决定叉乘u×vu \times vu×v的方向。对于一个2-向量u∧vu \wedge vu∧v,我们将要求 det(u,v,⋆(u∧v))>0\det (u, v, \star (u \wedge v)) > 0 det(u,v,⋆(u∧v))>0 即构成u∧vu \wedge vu∧v的两个向量和由它的Hodge星给的第三个向量组成矩阵的行列式应该是正的。实际上,这个规则在⋆(u∧v)\star(u \wedge v)⋆(u∧v)与u×vu \times vu×v指向同个方向这层意义上和一般的右手法则对应。那么大小又是怎么样呢?再一次我们基于行列式给出一个规则——特别的,在两个单位正交向量u1,u2u_1,u_2u1,u2特殊情况下,我们要求 det(u1,u2,⋆(u1∧u2))=1\det (u_1, u_2, \star(u_1 \wedge u_2)) = 1 det(u1,u2,⋆(u1∧u2))=1 由于Rn\mathbb{R}^nRn中的向量总是可以被一组正交基解释,这个规则独一无二的确定了任意2-向量的Hodge星。实际上,我们现在真的有⋆(u∧v)=u×v\star(u \wedge v) = u \times v⋆(u∧v)=u×v,即对于两个R3\mathbb{R}^3R3中应用楔形积的向量,那么那么星等价于取它们的叉乘。(但是实际上它并没有这么简单!) 更一般地,假设e1,…,ene_1, \dots, e_ne1,…,en是一组Rn\mathbb{R}^nRn中的单位正交基。如果我们开始于k个单位正交向量v1,…,vkv_1,\dots,v_kv1,…,vk,那么Hodge星由以下关系被唯一确定 (u1∧⋯∧uk)∧⋆(u1∧…uk)=e1∧⋯∧en(u_1 \wedge \dots \wedge u_k) \wedge \star (u_1 \wedge \dots u_k) = e_1 \wedge \dots \wedge e_n (u1∧⋯∧uk)∧⋆(u1∧…uk)=e1∧⋯∧en 简短点来说:如果我们对k维“单位体积”和它的补(n-k)维“单位体积”之间进行楔形积,我们将获得一个且只有一个Rn\mathbb{R}^nRn上的n维单位体积。 一个重要的特殊情况(尤其对于考虑曲面的情况)是R2\mathbb{R}^2R2中1-向量的Hodge星,即平面上普通向量的Hodge星。这里有一个简单地可视化一下:如果我们有一个1-向量uuu,那么它们的Hodge星⋆u\star u⋆u将是一个(n-k)-向量。但是由于n−k=2−1=1n-k=2-1=1n−k=2−1=1,我们就会得到另一个和uuu正交的1-向量。比如,如果uuu指向地图上的“东”,那么⋆u\star u⋆u将指向“北”: 当我们继续使用Hodge星,我们得到一个指向西的向量,然后是南,然后又一次回到东。换句话说,2D中的Hodge星就是逆时针方向上四分之一的旋转。 最后,我们可以考虑Hodge星和楔形积之间的交互。比如,对于两个R3\mathbb{R}^3R3中的1-向量u,vu,vu,v,由于先将两个向量加起来再转90度与先分别转90度再起来没有什么区别,我们有 ⋆(u+v)=⋆u+⋆v\star (u + v) = \star u + \star v ⋆(u+v)=⋆u+⋆v 更一般地,同样的等式对于任意维度中的任意两个k-向量都成立,即Hodge星的加法分配律(你可以画其它的图让这个想法更清晰吗?) Rn\mathbb{R}^nRn中楔形积和星的例子 为了让这些想法更直观一点,让我们考虑一些直观的例子。这些例子不意味要特别“深入”,而是仅仅示范一下使用k-向量时候的基本操作。(你将在作业中看到一些更有意思的例子!)这里我们将(1-)向量vvv使用一组正交基e1,…,ene_1, \dots, e_ne1,…,en表示。比如,在2D中v:=e1+e2v \coloneqq e_1 + e_2v:=e1+e2是一个与水平线有45度夹角的长度为2\sqrt{2}2的向量。 例1 令u:=e1+2e2u \coloneqq e_1 + 2e_2u:=e1+2e2和v:=e1+e2−e3v \coloneqq e_1 + e_2 - e_3v:=e1+e2−e3都是R3\mathbb{R}^3R3中的1-向量。然后它们之间的楔形积被给定为 u∧v=(e1+2e2)∧(e1+e2−e3)=e1∧(e1+e2−e3)+2e2(e1+e2−e3)=e1∧e10+e1∧e2−e1∧e3+2e2∧e1+2e2∧e20−2e2∧e3=e1∧e2−2e1∧e2−e1∧e3−2e2∧e3=−e1∧e2−e1∧e3−2e2∧e3\begin{aligned} u \wedge v &= (e_1 + 2e_2) \wedge (e_1 + e_2 - e_3) \\ &= e_1 \wedge(e_1 + e_2 - e_3) + 2e_2(e_1 + e_2 - e_3) \\ &= \cancel{e_1 \wedge e_1}^0 + e_1 \wedge e_2 - e_1 \wedge e_3 + 2e_2 \wedge e_1 + \cancel{2e_2 \wedge e_2}^0 - 2e_2 \wedge e_3 \\ &= e_1 \wedge e_2 - 2e_1 \wedge e_2 - e_1 \wedge e_3 - 2e_2 \wedge e_3 \\ &= -e_1 \wedge e_2 - e_1 \wedge e_3 - 2e_2 \wedge e_3 \end{aligned} u∧v=(e1+2e2)∧(e1+e2−e3)=e1∧(e1+e2−e3)+2e2(e1+e2−e3)=e1∧e10+e1∧e2−e1∧e3+2e2∧e1+2e2∧e20−2e2∧e3=e1∧e2−2e1∧e2−e1∧e3−2e2∧e3=−e1∧e2−e1∧e3−2e2∧e3 这有两个东西需要在计算中被注意到。首先,任意ei∧eie_i \wedge e_iei∧ei都为0。你还记得为什么吗?本质上是因为两个同样的向量张成的平行四边形面积为0。同样需要注意到的一点是我们将2e2∧e12e_2 \wedge e_12e2∧e1替换为了−2e1∧e2- 2e_1 \wedge e_2−2e1∧e2。为什么我们这么做呢?因为e1∧e2e_1 \wedge e_2e1∧e2和e2∧e1e_2 \wedge e_1e2∧e1描述了同样一个2-向量但是方向相对。 例2 令w:=−e1∧e2−e1∧e3−2e2∧e3w \coloneqq -e_1 \wedge e_2 - e_1 \wedge e_3 - 2e_2 \wedge e_3w:=−e1∧e2−e1∧e3−2e2∧e3是从上一个例子得来的2-向量。它的Hodge星为 ⋆w=⋆(−e1∧e2−e1∧e3−2e2∧e3)=−⋆(e1∧e2)−⋆(e1∧e3)−2⋆(e2∧e3)=−e3−(−e2)−2(e1)=−2e1+e2−e3\begin{aligned} \star w &= \star(-e_1 \wedge e_2 - e_1 \wedge e_3 - 2e_2 \wedge e_3) \\ &= -\star(e_1 \wedge e_2) - \star(e_1 \wedge e_3) - 2\star(e_2 \wedge e_3) \\ &= - e_3 -(-e_2) - 2(e_1) \\ &= -2 e_1 + e_2 - e_3 \end{aligned} ⋆w=⋆(−e1∧e2−e1∧e3−2e2∧e3)=−⋆(e1∧e2)−⋆(e1∧e3)−2⋆(e2∧e3)=−e3−(−e2)−2(e1)=−2e1+e2−e3 我们这里做的主要的事情是使用右手法则去决定两个基向量的楔形乘结果指向哪个方向。比如,就像在使用叉乘的时候有e1×e2=e3e_1 \times e_2 = e_3e1×e2=e3,当使用楔形积和Hodge星的时候我们也有⋆(e1∧e2)=e3\star(e_1 \wedge e_2) = e_3⋆(e1∧e2)=e3。关于这些关系更相机的讨论,和关于外代数的基础,将在后文中继续说明。 例3 令u:=e1+e2e3u \coloneqq e_1 + e_2 e_3u:=e1+e2e3、v:=e1+2e2+3e3v \coloneqq e_1 + 2e_2 + 3e_3v:=e1+2e2+3e3和w:=e1−e3w \coloneqq e_1 - e_3w:=e1−e3都是R3\mathbb{R}^3R3中的1-向量,并且假设我们想要计算u∧v∧wu \wedge v \wedge wu∧v∧w。由于楔形积有结合律,我们可以随意通过计算u∧vu \wedge vu∧v或者v∧wv \wedge wv∧w开始,然后将这个结果与最后剩下的一个1-向量进行楔形积计算。比如,我们有 v∧w=(e1+2e2+3e3)∧(e1−e3)=e1∧e10−e1∧e3+2e2∧e1−2e2∧e3+3e3∧e1−3e3∧e30=−2e1∧e2−4e1∧e3−2e2∧e3\begin{aligned} v \wedge w &= (e_1 + 2e_2 + 3e_3) \wedge (e_1 - e_3) \\ &= \cancel{e_1 \wedge e_1}^0 - e_1 \wedge e_3 + 2e_2 \wedge e_1 - 2e_2 \wedge e_3 + 3e_3 \wedge e_1 - 3\cancel{e_3 \wedge e_3}^0 \\ &= -2 e_1 \wedge e_2 - 4e_1 \wedge e_3 - 2e_2 \wedge e_3 \end{aligned} v∧w=(e1+2e2+3e3)∧(e1−e3)=e1∧e10−e1∧e3+2e2∧e1−2e2∧e3+3e3∧e1−3e3∧e30=−2e1∧e2−4e1∧e3−2e2∧e3 与uuu进行楔形积然后得到 u∧(v∧w)=(e1+e2+e3)∧(−2e1∧e2−4e1∧e3−2e2∧e3)=−2e1∧e2∧e3−4e2∧e1∧e3−2e3∧e1∧e2=−2e1∧e2∧e3+4e1∧e2∧e3−2e1∧e2∧e3=0\begin{aligned} u \wedge (v \wedge w) &= (e_1 + e_2 + e_3) \wedge (-2 e_1 \wedge e_2 - 4 e_1 \wedge e_3 - 2 e_2 \wedge e_3) \\ &= -2 e_1 \wedge e_2 \wedge e_3 - 4 e_2 \wedge e_1 \wedge e_3 - 2 e_3 \wedge e_1 \wedge e_2 \\ &= -2 e_1 \wedge e_2 \wedge e_3 + 4 e_1 \wedge e_2 \wedge e_3 - 2 e_1 \wedge e_2 \wedge e_3 \\ &= 0 \end{aligned} u∧(v∧w)=(e1+e2+e3)∧(−2e1∧e2−4e1∧e3−2e2∧e3)=−2e1∧e2∧e3−4e2∧e1∧e3−2e3∧e1∧e2=−2e1∧e2∧e3+4e1∧e2∧e3−2e1∧e2∧e3=0 在第二个等号的时候,我们省略了很多工作,因为注意到任意一个包含多个同样基1-向量的项(例如,e1∧e1∧e2e_1 \wedge e_1 \wedge e_2e1∧e1∧e2)体积都为0,因为对应的小平行四边形的两条边是平行的。因此,我们仅仅写下了剩下的三个包含所有三个基的项(比如,e2∧e3∧e1e_2 \wedge e_3 \wedge e_1e2∧e3∧e1)。通过反复交换基对的顺序,我们可以将这个3-向量转为约定的形式(比如,e2∧e3∧e1=−e2∧e1∧e3=e1∧e2∧e3e_2 \wedge e_3 \wedge e_1 = -e_2 \wedge e_1 \wedge e_3 = e_1 \wedge e_2 \wedge e_3e2∧e3∧e1=−e2∧e1∧e3=e1∧e2∧e3),同时我们将有几个单位3-向量e1∧e2∧e3e_1 \wedge e_2 \wedge e_3e1∧e2∧e3使用一些标量放缩后的副本。这个情况中,所有项的大小加起来为0。这在几何上表示什么呢?它意味着我们原始的1-向量uuu、vvv和www并不是线性独立的,即它们描述了一个体积为0的“平坦的”3-向量。 向量和1-型 现在我们对于“小体积”的操作来说有了一个基本语言,我们继续我们关于外微积分讲述的第二部分。 此时我们给出一个叫做vvv的向量: 这个vvv编码了什么信息?一个检查向量的方法是确定它沿着给定方向的大小或者长度。比如,我们可以随意选择一些方向α\alphaα,并记录vvv投影到α\alphaα方向上的阴影长度: 这个结果是一个简单的数,我们可以记为α(v)\alpha(v)α(v)。这个符号是为了强调α\alphaα是一个函数的思想:特别的,它是一个将一个向量变成标量的线性函数。任何一个这样的函数都被称为一个1-型(1-form)(同时也被称为协向量)。 当然,从这个图中可以清晰地看到所有1-型构成的空间看起来非常像所有向量构成的空间:我们必须选择一些沿其测量的方向。但是通常有一个好理由去区别向量和1-型——这个区别和线性代数中列向量和行向量的区别并没有什么不同。比如,虽然列向量和行向量都表示“向量”,我们仅仅允许我们使用行向量去乘列向量: [α1⋯αn][v1⋮vn]\begin{bmatrix} \alpha_1 & \cdots & \alpha_n \end{bmatrix} \begin{bmatrix} v^1 \\ \vdots \\ v^n \end{bmatrix} [α1⋯αn]⎣⎡v1⋮vn⎦⎤ 如果我们想要去乘两个列向量,我们首先需要取其中一个的转置将其变为行向量: vTv=[v1⋯vn][v1⋮vn]v^Tv = \begin{bmatrix} v^1 & \cdots & v^n \end{bmatrix} \begin{bmatrix} v^1 \\ \vdots \\ v^n \end{bmatrix} vTv=[v1⋯vn]⎣⎡v1⋮vn⎦⎤ 使用同样的方法处理向量和1-型,除此之外我们有两个不同的操作:sharp(♯\sharp♯),将1-型转为向量;flat(♭\flat♭),将向量转为1-型。比如,写成v♭(v)v^{\flat}(v)v♭(v)和α(α♯)\alpha(\alpha^{\sharp})α(α♯)是完全有效的,因为不管那种情况都是将一个向量喂给一个1-型。操作♯\sharp♯和♭\flat♭被称为音符同构(musical isomorphisms)。 所有的这些1-型向量对比的操作(或者甚至行向量和列向量)看起来都像做了很多无关紧要的事情。确实,在像平面的平坦空间中,这两者的区别看起来都是表面上的。然而,在弯曲的空间中,向量和1-型有一个重要的区别——特别的,我们想要去确认我们正在正确的空间中进行测量。比如,假设我们想要测量向量vvv沿着另一个向量uuu方向的长度。记住正切向量被从平面到R3\mathbb{R}^3R3中某个曲面的映射f:R2⊃M→R3f : \mathbb{R}^2 \supset M \rightarrow \mathbb{R}^3f:R2⊃M→R3放缩是重要的。因此,操作♯\sharp♯和♭\flat♭应该满足如下关系 u♭(v)=g(u,v)u^{\flat}(v) = \mathsf{g}(u,v) u♭(v)=g(u,v) 其中g\mathsf{g}g是由fff引导的度量。我们真正测量的方式是这些东西在“伸长”空间中的表现而不是在初始域MMM中的。 坐标系 到目前为止我们有意地避免使用坐标系——换句话说,我们努力在没有任何特定坐标系统x1,…,xnx_1, \dots, x_nx1,…,xn依赖去解释几何关系。为什么拒绝坐标系?有几个原因通常被引用(人们可能会咕哝一些关于“不变性”的东西),但是真正的原因十分简单,没有坐标系的表达式更短、更美妙的并且更容易从中提取真实的含义。这种方法在几何处理中也特别有价值,因为很多没有不依赖坐标系的表达式都很自然地转变为了网格上的基本操作。 但是坐标系在很多情况中仍然十分有价值。有时有些特殊的坐标基可以相当程度地简化分析——比如,回想我们关于主曲率方向的讨论。其它不使用坐标系的时候有一些的确不容易理解的方法去证明一些东西。那么现在我们将打磨一些在坐标系中关于外微积分的基本事实;今天的最后我们将保存我们发现的所有不依赖坐标系的表达式并且礼貌地忘记坐标系曾经存在过。 让我们设置我们的坐标系统。为了一些后面将会逐渐清晰的原因,我们将使用符号∂∂x1,…,∂∂xn\frac{\partial}{\partial x^1}, \dots, \frac{\partial}{\partial x^n}∂x1∂,…,∂xn∂表示Rn\mathbb{R}^nRn中向量的正交基,并且使用dx1,…,dxndx^1, \dots, dx^ndx1,…,dxn表示对应的1-型基。换句话说,任何向量都可以写成线性形式 v=v1∂∂x1+⋯+vn∂∂xnv = v^1 \frac{\partial}{\partial x^1} + \cdots + v^n \frac{\partial}{\partial x^n} v=v1∂x1∂+⋯+vn∂xn∂ 并且任意1-型也可以写成线性形式 α=α1dx1+⋯+αndxn\alpha = \alpha_1 dx^1 + \dots + \alpha_n dx^n α=α1dx1+⋯+αndxn 为了保证你在这个时候神智还是清醒的,你应该完全忽略符号∂∂xi\frac{\partial}{\partial x^i}∂xi∂和dxidx^idxi看起来想导数的事实——就像上面描述的一样,它们仅仅是单位长度正交基的集合。这两个基dxidx^idxi和∂∂xi\frac{\partial}{\partial x^i}∂xi∂通常被称为对偶基,这意味着它们满足关系 dxi(∂∂xi)=δji={1,i=j0,otherwisedx^i \left( \frac{\partial}{\partial x^i} \right) = \delta^i_j = \begin{cases} 1,& i=j \\ 0,& \text{otherwise} \end{cases} dxi(∂xi∂)=δji={1,0,i=jotherwise 这个关系精确的获取了我们在寻找的特性:向量∂∂xi\frac{\partial}{\partial x^i}∂xi∂在1-型dxjdx^jdxj上“投影阴影”,当且仅当这两个基指向同一个方向。使用这个关系,我们可以得到 α(v)=∑iαidxi(∑jvj∂∂xj)=∑iαivi\alpha(v) = \sum_i \alpha_i dx^i \left(\sum_j v^j \frac{\partial}{\partial x^j}\right) = \sum_i \alpha_i v^i α(v)=i∑αidxi(j∑vj∂xj∂)=i∑αivi 即这对向量和1-型看起来就像标准欧式内积。 符号 值得花一点时间去说明一下我们的符号。首先,向量和向量场转变为使用罗马字母表结尾的几个字母表示(分别是uuu、vvv、www或者XXX、YYY、ZZZ),1-型使用希腊字母表中开头的几个小写字母表示(α\alphaα、β\betaβ、γ\gammaγ等等)。虽然一个人通常会从语言上区分“向量”(意味这一个单独的箭头)和“向量场”(意味着一个和空间内所有点粘合在一起的箭头),有一个不幸的使用使用“1-型”项去表示两个内容的先例——很遗憾,从来没有人说过“1-型场”!标量场或者0-型通常使用罗马字母表中间的字母(f,g,hf,g,hf,g,h)或者接近结尾的希腊小写字母(ϕ,ψ\phi,\psiϕ,ψ等)。 你可能注意到了我们特别指定了索引的位置:向量系数viv^ivi的索引在上面,1-型系数αi\alpha_iαi的索引在下面。类似地,向量基∂∂xi\frac{\partial}{\partial x^i}∂xi∂在下面有索引(它们在分母中),1-型基dxidx^idxi在上面有索引。看起来这么神经质的理由是使用爱因斯坦累加符号(Einstein summation notation)的优点:任何时间一对变量都被同样的字母iii同时在“上”和“下”索引,我们将它解释为在所有可能的iii值上的和: αivi=∑iαivi\alpha_i v^i = \sum_i \alpha_i v^i αivi=i∑αivi 索引的位置对音符同构♯\sharp♯和♭\flat♭提供了有趣的记忆帮助。在音符中♯\sharp♯表示音高提高半阶,对应于目标的向上运动。比如,下面的两个音符都对于同样的音高“C”: 因此,为了从1-型到向量我们提高了索引。比如,在平坦(flat)空间中我们不必担心度量并且1-型 α=α1dx1+⋯+αndxn\alpha = \alpha_1 dx^1 + \cdots + \alpha_n dx^n α=α1dx1+⋯+αndxn 变成向量 α♯=α1∂∂x1+⋯+αn∂∂xn\alpha ^{\sharp} = \alpha^1 \frac{\partial}{\partial x^1} + \cdots + \alpha^n \frac{\partial}{\partial x^n} α♯=α1∂x1∂+⋯+αn∂xn∂ 类似地,♭\flat♭表示了音高的下降和在目标上向下的运动: 那么所以♭\flat♭下降了向量的索引给我们一个1-型——即 v=v1∂∂x1+⋯+vn∂∂xnv = v^1 \frac{\partial}{\partial x^1} + \cdots + v^n \frac{\partial}{\partial x^n} v=v1∂x1∂+⋯+vn∂xn∂ 变成 v♭=v1dx1+⋯+vndxnv^{\flat} = v_1dx^1 + \cdots + v_ndx^n v♭=v1dx1+⋯+vndxn 微分形式和楔形积 在上一个部分我们通过投影到不同到坐标轴上测量了向量长度;测量过程有效地定义了我们称为1-型的东西。但是如果我们有一个向量的集合会发生什么呢?比如,考虑R3\mathbb{R}^3R3中一对向量u,vu,vu,v: 我们可以把这些向量看作定义一个平行四边形,像我们对单个的向量所做的那样,我们可以测量这个平行四边形通过测量他投影到某个平面上的“阴影”大小: 比如,假设我们表示这个平面通过一对单位正交1-型α\alphaα和β\betaβ。然后这些投影向量有组分 u′=(α(u),β(u))Tv′=(α(v),β(v))T\begin{aligned} u' &= (\alpha(u), \beta(u))^T \\ v' &= (\alpha(v), \beta(v))^T \end{aligned} u′v′=(α(u),β(u))T=(α(v),β(v))T 因此这个(带符号的)投影面积由叉乘给出 u′×v′=α(u)β(v)−α(v)β(u)u' \times v' = \alpha(u)\beta(v) - \alpha(v)\beta(u) u′×v′=α(u)β(v)−α(v)β(u) 由于我们在未来想要测量很多投影的体积,我们给这个操作一个特别的名字“α∧β\alpha \wedge \betaα∧β”: α∧β(u,v)=α(u)β(v)−α(v)β(u)\alpha \wedge \beta (u,v) = \alpha(u)\beta(v) - \alpha(v)\beta(u) α∧β(u,v)=α(u)β(v)−α(v)β(u) 可能你已经猜到了,α∧β\alpha \wedge \betaα∧β被我们称为2-型。根本上我们将符号∧\wedge∧解释为一个被称为楔形积(wedge product)的在微分形式上的二元操作符。楔形积的代数性质直接继承带符号体积遵循的方式。比如,注意到如果我们逆转了轴α\alphaα和β\betaβ的顺序那么面积的符号也会改变。换句话说,楔形积是反对称的(antisymmetric): α∧β=−β∧α\alpha \wedge \beta = - \beta \wedge \alpha α∧β=−β∧α 反对称的一个重要结果是任何一个1-型和它自己的楔形积为0: α∧α=−α∧α⇒α∧α=0\begin{aligned} \alpha \wedge \alpha &= - \alpha \wedge \alpha \\ \Rightarrow \alpha \wedge \alpha &= 0 \end{aligned} α∧α⇒α∧α=−α∧α=0 但是不要让这个声明变成纯粹的代数事实!从几何上来看,为什么这这个1-型的楔形积为0?十分简单的因为投影到了一个面积为0的平面上!(即,这个面由α\alphaα和α\alphaα张成。) 接下来,考虑投影到分别由α,β\alpha, \betaα,β和α,γ\alpha, \gammaα,γ张成的不同平面上。投影面积的和可以被写为 α∧β(u,v)+α∧γ(u,v)=α(u)β(v)−α(v)β(u)+α(u)γ(v)−α(v)β(u)=α(u)(β(v)+γ(v))−α(v)(β(u)+γ(u))=(α∧(β+γ))(u,v)\begin{aligned} \alpha \wedge \beta (u,v) + \alpha \wedge \gamma (u,v) &= \alpha(u)\beta(v) - \alpha(v)\beta(u) + \alpha(u)\gamma(v) - \alpha(v)\beta(u) \\ &= \alpha(u)(\beta(v) + \gamma(v)) - \alpha(v)(\beta(u) + \gamma(u)) \\ &= (\alpha \wedge (\beta + \gamma))(u,v) \end{aligned} α∧β(u,v)+α∧γ(u,v)=α(u)β(v)−α(v)β(u)+α(u)γ(v)−α(v)β(u)=α(u)(β(v)+γ(v))−α(v)(β(u)+γ(u))=(α∧(β+γ))(u,v) 或者换句话说,在+++之上∧\wedge∧有分配律: α∧(β+γ)=α∧β+α∧γ\alpha \wedge (\beta + \gamma) = \alpha \wedge \beta + \alpha \wedge \gamma α∧(β+γ)=α∧β+α∧γ 最后,考虑三个向量u,v,wu,v,wu,v,w张成在R3\mathbb{R}^3R3中的体积: 我们将考虑这个体积在三个1-型α,β,γ\alpha, \beta, \gammaα,β,γ张成的体积上的投影,但是从一个体积到另一个体积的投影很难可视化!现在我们可以仅仅欺骗自己并想象α=dx1\alpha = dx^1α=dx1、β=dx2\beta = dx^2β=dx2和γ=dx3\gamma = dx^3γ=dx3,因此内心对于这个投影体积的图像看起来就像上面描绘的一样。一种写下投影体积的方法是投影后向量u′,v′,w′u',v',w'u′,v′,w′的行列式: α∧β∧γ(u,v,w):=det([u′v′w′])=det([α(u)α(v)α(w)β(u)β(v)β(w)γ(u)γ(v)γ(w)])\alpha \wedge \beta \wedge \gamma (u,v,w) \coloneqq \det(\begin{bmatrix} u' & v' & w' \end{bmatrix}) = \det \left( \begin{bmatrix} \alpha(u) & \alpha(v) & \alpha(w) \\ \beta(u) & \beta(v) & \beta(w) \\ \gamma(u) & \gamma(v) & \gamma(w) \end{bmatrix} \right) α∧β∧γ(u,v,w):=det([u′v′w′])=det⎝⎛⎣⎡α(u)β(u)γ(u)α(v)β(v)γ(v)α(w)β(w)γ(w)⎦⎤⎠⎞ (你注意到了左上角2×22 \times 22×2子矩阵的行列式也给了我们两个1-型的楔形积吗?)或者说,我们可以将体积解释成一个面的面积乘上剩下边的长度: 用这个方法考虑问题,我们可能构造楔形积的使用三重积(triple product)的另一个定义: α∧β∧γ=(u′×v′)⋅w′=(v′×w′)⋅u′=(w′×u′)⋅v′\begin{aligned} \alpha \wedge \beta \wedge \gamma &= (u' \times v') \cdot w' \\ &= (v' \times w') \cdot u' \\ &= (w' \times u') \cdot v' \end{aligned} α∧β∧γ=(u′×v′)⋅w′=(v′×w′)⋅u′=(w′×u′)⋅v′ 这里有个重要的事情需要注意的是顺序是不重要的——我们总是会得到同样的体积,无论我们选择的是哪一个面(虽然我们仍然必须注意一下符号)。一个更代数的表示方式是楔形积具有结合律: (α∧β)∧γ=α∧(β∧γ)(\alpha \wedge \beta) \wedge \gamma = \alpha \wedge (\beta \wedge \gamma) (α∧β)∧γ=α∧(β∧γ) 总而言之,k个1-型的楔形积给我们一个k-型,它测量了k个向量集合的投影体积。作为结论,楔形积对于任意k-型α\alphaα、l-型β\betaβ和m-型γ\gammaγ有如下性质: 反对称(Antisymmetry): α∧β=(−1)klβ∧α\alpha \wedge \beta = (-1)^{kl}\beta \wedge \alphaα∧β=(−1)klβ∧α 结合律(Associativity): α∧(β∧γ)=(α∧β)∧γ\alpha \wedge (\beta \wedge \gamma) = (\alpha \wedge \beta) \wedge \gammaα∧(β∧γ)=(α∧β)∧γ 并且在β\betaβ和γ\gammaγ有相同度的情况下(即 m=lm = lm=l)我们有 分配律(Distributivity): α∧(β+γ)=α∧β+α∧γ\alpha \wedge (\beta + \gamma) = \alpha \wedge \beta + \alpha \wedge \gammaα∧(β+γ)=α∧β+α∧γ 一个特殊的现实是k-型在它的参数中是反对称的——换句话说,交换两个“输入”向量的相对顺序仅仅会改变这个体积的符号。比如,如果α\alphaα是一个2-型那么有α(u,v)=−α(v,u)\alpha(u,v) = -\alpha(v,u)α(u,v)=−α(v,u)。一般的,一个偶数的交换会保证符号不变;奇数的交换会变号。(一个方便你自己的方法是考虑当你改变矩阵的两列时发生在它行列式上的事情。)最后,你将常常听到人们说k-型是“多线性的”——它意味着当你保证除了一个向量以外的其它向量都固定不变时,那么k-型的表现就像线性映射。几何上它带来这样的感觉:k-型是从k个长度的线性测量构建的(本质上就是说k个不同的点积)。 向量值形式 到这个时候我们仅仅考虑了实值的k-型——比如,α(u)\alpha(u)α(u)表示向量uuu沿着方向α\alphaα的长度,可以被表示为一个实数。然而,一般的,一个k-型可以“吐出”所有的种类的不同值。比如,我们可能想要去处理由复数(C\mathbb{C}C)或者一些更大向量空间(即Rn\mathbb{R}^nRn)中向量表示的量。 一个向量值k-型很好的例子是我们的映射f:M→R3f: M \rightarrow \mathbb{R}^3f:M→R3,它表示一个曲面的几何。在外微积分的语言中,fff是一个R3\mathbb{R}^3R3值的0-型:在MMM的每一个点ppp处,它取0个向量作为输入然后得到一个R3\mathbb{R}^3R3中的点f(p)f(p)f(p)作为输出。类似地,微分dfdfdf是一个R3\mathbb{R}^3R3值的1-型:它取1个向量(平面上的某个方向uuu)并且将它映射到R3\mathbb{R}^3R3中的一个值df(u)df(u)df(u)(表示uuu的“伸长”版本)。 更一般地,如果EEE是一个向量空间那么一个EEE值的k-型(E-valued k-form)取k个向量得到EEE中的一个值。然而,我们在这里必须小心一点。比如,考虑我们对于2-型的定义: α∧β(u,v):=α(u)β(v)−α(v)β(u)\alpha \wedge \beta(u,v) \coloneqq \alpha(u)\beta(v) - \alpha(v)\beta(u) α∧β(u,v):=α(u)β(v)−α(v)β(u) 如果α\alphaα和β\betaβ都是EEE值1-型,那么α(u)\alpha(u)α(u)和β(v)\beta(v)β(v)都是EEE中的向量。但是如果你将两个向量相乘呢?一般会有一个不太好的答案:并不是每一个向量空间都带有一个自然的乘法符号。 然而,有很多空间都带有已经定义好的乘法——比如,两个复数a+bia+bia+bi和c+dic+dic+di的积为(ac−bd)+(ad+bc)i(ac - bd)+(ad+bc)i(ac−bd)+(ad+bc)i,所以我们没有任何问题的明确计算了上面的表达式。在其它的情况下我们必须明确说明我们想要使用哪个乘积——比如在R3\mathbb{R}^3R3中我们可以使用叉乘×\times×,在这个情况下一个R3\mathbb{R}^3R3值2-型看起来像: α∧β(u,v)=α(u)β(v)−α(v)β(u)\alpha \wedge \beta(u,v) = \alpha(u) \beta(v) - \alpha(v)\beta(u) α∧β(u,v)=α(u)β(v)−α(v)β(u) Hodge对偶性 在前面我们看到了k-型测量(带符号的)k维平行六面体的投影体积。比如,就像上面描绘的一样,一个2-型测量投影到一个平面上的平行四边形的面积。但是这里观察到了一些好东西:R3\mathbb{R}^3R3中的一个平面可以被一对基方向(α,β)(\alpha, \beta)(α,β)或者被一个法线方向γ\gammaγ表示。因此除了测量投影面积,我们还可以测量平行四边形(u,v)(u,v)(u,v)的法线和平面法线的一致程度。换句话说,我们可以寻找这样的一个1-型γ\gammaγ γ(u×v)=α∧β(u,v)\gamma(u \times v) = \alpha \wedge \beta (u,v) γ(u×v)=α∧β(u,v) 这个观察刻画了Hodge对偶(Hodge duality)背后的想法:一个n维空间中的k维体积可以被k个方向或者(n-k)个方向的补集确定。那么就应该在k-型和(n-k)-型之间存在某种自然的对应。 微分形式和Hodge星 让我们更深入的考察一下这个想法,通过对0-型、1-型、2-型等等的空间构造一个明确的基——为了保证一切可控我们将在R3\mathbb{R}^3R3中进行操作并且有标准坐标系统(x1,x2,x3)(x^1, x^2, x^3)(x1,x2,x3)。0-型是简单的:任意一个0-型都可以被当作一些函数乘上常数0-型,我们将它记为“1”。我们早就看到过了1-型基dx1,dx2,dx3dx^1, dx^2, dx^3dx1,dx2,dx3,它看起来就像向量空间中的标准正交基: 那么对于2-型呢?考虑任意一个2-型都可以被解释成两个1-型的楔形积: α∧β=(αidxi)∧(βjdxj)=αiβjdxi∧dxj\alpha \wedge \beta = (\alpha_i dx^i) \wedge (\beta_j dx^j) = \alpha_i \beta_j dx^i \wedge dx^j α∧β=(αidxi)∧(βjdxj)=αiβjdxi∧dxj 换句话说,任意一个2-型看起来都像一个基2-型(dxi∧dxj)(dx^i \wedge dx^j)(dxi∧dxj)的某种线性结合。那么这里有多少个基呢?乍一看好像有很多可能: dx1∧dx1dx1∧dx2dx1∧dx3dx2∧dx1dx2∧dx2dx2∧dx3dx3∧dx1dx3∧dx2dx3∧dx3\begin{matrix} dx^1 \wedge dx^1 & dx^1 \wedge dx^2 & dx^1 \wedge dx^3 \\ dx^2 \wedge dx^1 & dx^2 \wedge dx^2 & dx^2 \wedge dx^3 \\ dx^3 \wedge dx^1 & dx^3 \wedge dx^2 & dx^3 \wedge dx^3 \end{matrix} dx1∧dx1dx2∧dx1dx3∧dx1dx1∧dx2dx2∧dx2dx3∧dx2dx1∧dx3dx2∧dx3dx3∧dx3 但是当然并不是这所有的情况都不同:记得楔形积是反对称的(α∧β=−β∧α\alpha \wedge \beta = -\beta \wedge \alphaα∧β=−β∧α),并且它有一个很重要的推导α∧α=0\alpha \wedge \alpha = 0α∧α=0。所以我们的表格看起来更像这样: 0dx1∧dx2−dx3∧dx1−dx1∧dx20dx2∧dx3dx3∧dx1−dx2∧dx30\begin{matrix} 0 & dx^1 \wedge dx^2 & -dx^3 \wedge dx^1 \\ - dx^1 \wedge dx^2 & 0 & dx^2 \wedge dx^3 \\ dx^3 \wedge dx^1 & - dx^2 \wedge dx^3 & 0 \end{matrix} 0−dx1∧dx2dx3∧dx1dx1∧dx20−dx2∧dx3−dx3∧dx1dx2∧dx30 然后我们就只剩下三个不同的基:dx2∧dx3dx^2 \wedge dx^3dx2∧dx3、dx3∧dx1dx^3 \wedge dx^1dx3∧dx1和dx1∧dx2dx^1 \wedge dx^2dx1∧dx2。从几何上来说,所有我们所说的这些都是在说R3\mathbb{R}^3R3中有三个线性独立的“平面”: 那么对于3-型基呢?我们确定至少有一个: dx1∧dx2∧dx3dx^1 \wedge dx^2 \wedge dx^3 dx1∧dx2∧dx3 还有其它的吗?∧\wedge∧的反对称性质再一次发挥作用:很多隐藏的基仅仅是第一个的某种排列: dx2∧dx3∧dx1=−dx2∧dx1∧dx3=dx1∧dx2∧dx3dx^2 \wedge dx^3 \wedge dx^1 = - dx^2 \wedge dx^1 \wedge dx^3 = dx^1 \wedge dx^2 \wedge dx^3 dx2∧dx3∧dx1=−dx2∧dx1∧dx3=dx1∧dx2∧dx3 剩下的消失了是因为出现了重复的1-型: dx2∧dx1∧dx2=−dx2∧dx2∧dx1=0∧dx1=0dx^2 \wedge dx^1 \wedge dx^2 = -dx^2 \wedge dx^2 \wedge dx^1 = 0 \wedge dx^1 = 0 dx2∧dx1∧dx2=−dx2∧dx2∧dx1=0∧dx1=0 一般情况下只有一个基n-型dx1∧⋯∧dxndx^1 \wedge \cdots \wedge dx^ndx1∧⋯∧dxn,它测量一个平行六面体的一般欧式体积: 最后,R3\mathbb{R}^3R3中的4-型又会怎么样呢?此时可能非常容易就能看出一个都没有,因为我们需要从总共只有3个的集合里面选择4个不同的1-型基。从几何上来说:R3\mathbb{R}^3R3中不包含任何一个4维的体积!(或者任何更高维度的体积,都是这样)R3\mathbb{R}^3R3中k-型基完整的清单为 0-型基: 1 1-型基: dx1,dx2,dx3dx^1, dx^2, dx^3dx1,dx2,dx3 2-型基: dx2∧dx3,dx3∧dx1,dx1∧dx2dx^2 \wedge dx^3, dx^3 \wedge dx^1, dx^1 \wedge dx^2dx2∧dx3,dx3∧dx1,dx1∧dx2 3-型基: dx1∧dx2∧dx3dx^1 \wedge dx^2 \wedge dx^3dx1∧dx2∧dx3 它意味着基的个数是1,3,3,11,3,3,11,3,3,1。实际上你可以从这里看到更一般的模式:在n维空间中k-型基的数量由二项式系数给出 (nk)=n!k!(n−k)!\begin{pmatrix} n \\ k \end{pmatrix} = \frac{n!}{k!(n-k)!} (nk)=k!(n−k)!n! (即“n中选k个”),由于我们想要获得k个不同的1-型基并且不关心顺序。这里有一个重要的恒等式就是 (nk)=(nn−k)\begin{pmatrix} n \\ k \end{pmatrix} = \begin{pmatrix} n \\ n-k \end{pmatrix} (nk)=(nn−k) 和之前预料的一样,我们在n-型和(n-k)-型之间有一个一对一的关系。特别的,我们可以确定任何一个k-型和它的补。比如在R3\mathbb{R}^3R3上我们有 ⋆1=dx1∧dx2∧dx3⋆dx1=dx2∧dx3⋆dx2=dx3∧dx1⋆dx3=dx1∧dx2⋆(dx1∧dx2)=dx3⋆dx2∧dx3=dx1⋆(dx3∧dx1)=dx2⋆(dx1∧dx2∧dx3)=1\begin{aligned} \star 1 &= dx^1 \wedge dx^2 \wedge dx^3 \\ \star dx^1 &= dx^2 \wedge dx^3 \\ \star dx^2 &= dx^3 \wedge dx^1 \\ \star dx^3 &= dx^1 \wedge dx^2 \\ \star(dx^1 \wedge dx^2) &= dx^3 \\ \star{dx^2 \wedge dx^3} &= dx^1 \\ \star(dx^3 \wedge dx^1) &= dx^2 \\ \star(dx^1 \wedge dx^2 \wedge dx^3) &= 1 \end{aligned} ⋆1⋆dx1⋆dx2⋆dx3⋆(dx1∧dx2)⋆dx2∧dx3⋆(dx3∧dx1)⋆(dx1∧dx2∧dx3)=dx1∧dx2∧dx3=dx2∧dx3=dx3∧dx1=dx1∧dx2=dx3=dx1=dx2=1 这个映射⋆\star⋆被称为Hodge星,并且刻画了平面可以被它的法线确定想法。更一般地,任何一个平坦的(flat)空间我们都有 ⋆(dxi1∧dxi2∧⋯∧dxik)=dxik+1∧dxii+2∧⋯∧dxin\star(dx^{i_1} \wedge dx^{i_2} \wedge \cdots \wedge dx^{i_k}) = dx^{i_{k+1}} \wedge dx^{i_{i+2}} \wedge \cdots \wedge dx^{i_n} ⋆(dxi1∧dxi2∧⋯∧dxik)=dxik+1∧dxii+2∧⋯∧dxin 其中(i1,i2,…,in)(i_1, i_2, \dots, i_n)(i1,i2,…,in)是(1,2,…,n)(1,2, \dots, n)(1,2,…,n)的任意一个偶置换。 体积形式 目前我们讨论了测量像Rn\mathbb{R}^nRn的平坦空间中的体积。但是现在我们如何测量弯曲空间中的呢?让我们考虑一下我们经常使用的曲面的例子f:R2⊃M→R3f:\mathbb{R}^2 \supset M \rightarrow \mathbb{R}^3f:R2⊃M→R3。如果我们考虑一个通过一对正交单位向量u,v∈R2u,v \in \mathbb{R}^2u,v∈R2张成曲面的区域,很明确的是我们不想要面积dx1∧dx2(u,v)=1dx^1 \wedge dx^2(u,v) = 1dx1∧dx2(u,v)=1,由于它仅仅给我们一个平面上的面积。我们真正想要的是在它被映射fff“伸长”之后的区域面积。换句话说,我们想要R3\mathbb{R}^3R3中对应的平行四边形的大小,由向量df(u)df(u)df(u)和df(v)df(v)df(v)张成。 因此我们可以测量在我们的曲面上任意一个小区域的面积,通过度量的行列式简单的缩放平面中的体积,即通过将2-型det(g)dx1∧dx2\sqrt{\det(\mathsf{g})}dx^1 \wedge dx^2det(g)dx1∧dx2应用到张成感兴趣区域的两个向量u,vu,vu,v上。更一般地,n-型 ω:=det(g)dx1∧⋯∧dxn\omega \coloneqq \sqrt{\det(\mathsf{g})} dx^1 \wedge \cdots \wedge dx^n ω:=det(g)dx1∧⋯∧dxn 被称为体积形式(volume form),在我们讨论积分的时候将扮演一个关键角色。 在弯曲的空间上,我们也使用Hodg星去刻画体积被拉伸的现实。比如,它非常合理地使用一个体积形式ω\omegaω去确定一个函数111,由于ω\omegaω真正表示弯曲空间中的单位体积: ⋆1=ω\star 1 = \omega ⋆1=ω 在k-型上的内积 更一般地,我们会要求任意一个由一对k-型α\alphaα和β\betaβ构造的n-型满足 α∧⋆β=⟨⟨α,β⟩⟩ω\alpha \wedge \star \beta = \langle \langle \alpha, \beta \rangle \rangle \omega α∧⋆β=⟨⟨α,β⟩⟩ω 其中⟨⟨α,β⟩⟩=∑iαiβi\langle \langle \alpha, \beta \rangle \rangle = \sum_i \alpha_i \beta_i⟨⟨α,β⟩⟩=∑iαiβi是k-型上的内积。实际上,一些作者将这个关系作为楔形积的定义——换句话说,他们开始于一些事情类似于“楔形积是一个在k-型上特定的二元操作为α∧⋆β=⟨⟨α,β⟩⟩ω\alpha \wedge \star \beta = \langle \langle \alpha, \beta \rangle \rangle \omegaα∧⋆β=⟨⟨α,β⟩⟩ω”,并且从这里推导我们前面建立的所有性质。这样的操作有一些抽象,它容易让人忘掉楔形积有异常明确的几何意义。(这肯定不是当Hermann Grassmann发明外代数时思考它的方式!)然而,实际上,这个恒等式是非常有用的。比如,如果uuu和vvv是R3\mathbb{R}^3R3中的向量,那么我们可以写出 u⋅v=⋆(u♭∧⋆v♭)u \cdot v = \star (u^{\flat} \wedge \star v^{\flat}) u⋅v=⋆(u♭∧⋆v♭) 即在一个平坦的平面上我们可以通过楔形积表示通常的欧式内积。这个恒等式是正确的有明确的几何意义吗?考虑这个恒等式说的内容:Hodge星将vvv转变为一个vvv是法线的平面。我们通过沿着方向uuu去挤压这个平面构造了一个体积。如果uuu和vvv几乎是平行的那么这个体积将会相当大;如果它们是几乎正交的那么体积会十分小。(但是确定我们真正正确的获取到了它的信息,你应该在坐标系中尝试验证这个恒等式!)类似的,我们可以解释欧式叉乘就像 u×v=(⋆(u♭∧v♭))♯u \times v = (\star(u^{\flat} \wedge v^{\flat}))^{\sharp} u×v=(⋆(u♭∧v♭))♯ 即我们可以创造一个有法线u×vu \times vu×v的平面,通过将两个基向量u∧vu \wedge vu∧v进行楔形积。(在次说明,在坐标系中证实这个东西可以帮助消除你的疑虑。) 微分算子 现在开始我们要进入外微积分(exterior calculus)阶段了。我们到目前为止一直盯着的对象——k-型、楔形积∧\wedge∧和Hodge星⋆\star⋆——确切地描述了一个更一般的被称为外代数(exterior algebra)的结构。为了从代数转入微积分,我们同样需要知道量是如何变化的,正如如何测量大小一样。换句话说,我们需要一些针对微分(differentiation)和积分(integration)的工具。让我们从微分开始。 在我们对于曲面的讨论里面我们简单看了一下曲面f:M→R3f:M \rightarrow \mathbb{R}^3f:M→R3的微分dfdfdf,这告诉我们一些正切向量从域MMM到R3\mathbb{R}^3R3中弯曲的曲面被“拉伸”的方式。更一般地,ddd被称为外导数(exterior derivative)并且对于构建外微积分中的很多微分算子都有重要地位。基本思想是ddd告诉我们k-型沿着所有可能的方向变化有多快。但是现在它具体是如何定义的呢?到目前我们只看到了高级的几何描述。 散度(Div)、梯度(Grad)和旋度(Curl) 在进入外导数部分之前,值得回顾一下基本的向量导数散度、梯度和旋度在做什么,并且更重要的是它们长什么样。这里的关键角色是算子∇\nabla∇(发音为“nabla”),它可以被写为坐标系中所有偏导组成的向量: ∇:=(∂∂x1,⋯,∂∂xn)T\nabla \coloneqq \left( \frac{\partial}{\partial x^1}, \cdots, \frac{\partial}{\partial x^n} \right)^T ∇:=(∂x1∂,⋯,∂xn∂)T 比如,将∇\nabla∇应用到一个标量函数ϕ:Rn→R\phi: \mathbb{R}^n \rightarrow \mathbb{R}ϕ:Rn→R上得到梯度(gradient) ∇ϕ=(∂f∂x1,⋯,∂f∂xn)T\nabla \phi = \left( \frac{\partial f}{\partial x^1}, \cdots, \frac{\partial f}{\partial x^n} \right)^T ∇ϕ=(∂x1∂f,⋯,∂xn∂f)T 它可以被可视化为在一些地方上升速度最快的方向: 我们可以将∇\nabla∇使用两种不同的方法应用到向量场XXX上。点乘给我们带来了散度(divergence) ∇⋅X=∂X1∂x1+⋯+∂Xn∂xn\nabla \cdot X = \frac{\partial X^1}{\partial x^1} + \cdots + \frac{\partial X^n}{\partial x^n} ∇⋅X=∂x1∂X1+⋯+∂xn∂Xn 它在测量向量场“伸展”得有多快,并且在R3\mathbb{R}^3R3上的叉乘给我们带来了旋度(curl) ∇×X=(∂X3∂x2−∂X2∂x3,∂X1∂x3−∂X3∂x1,∂X2∂x1−∂X1∂x2)\nabla \times X = \left( \frac{\partial X^3}{\partial x^2} - \frac{\partial X^2}{\partial x^3}, \frac{\partial X^1}{\partial x^3} - \frac{\partial X^3}{\partial x^1}, \frac{\partial X^2}{\partial x^1} - \frac{\partial X^1}{\partial x^2} \right) ∇×X=(∂x2∂X3−∂x3∂X2,∂x3∂X1−∂x1∂X3,∂x1∂X2−∂x2∂X1) 它表示一个向量场有多少在“围着转”。比如,这里有一对分别有大量散度和大量旋度的向量场: (注意到在这个情况中一个场仅仅是另一个场90度的旋转!)使用这些类型的图像而不是使用上面那些丑陋的表达式对于理解散度、梯度和旋度是更加有用的。 考虑微分 毫不令人惊讶地,我们可以使用外微积分写出类似的符号。然而,这些符号将更容易被推广一点(比如,没有叉乘定义的R4\mathbb{R}^4R4中向量场的“旋度”表示什么意思?)让我们首先看一下0-型(即函数)的外导数,它通常被就称作微分(differential)。为了让事情保持简单,我们开始于实值函数ϕ:Rn→R\phi: \mathbb{R}^n \rightarrow \mathbb{R}ϕ:Rn→R。在坐标系中,微分被定义为 dϕ:=∂ϕ∂x1dx1+⋯+∂ϕ∂xndxnd \phi \coloneqq \frac{\partial \phi}{\partial x^1} dx^1 + \cdots + \frac{\partial \phi}{\partial x^n} dx^n dϕ:=∂x1∂ϕdx1+⋯+∂xn∂ϕdxn 重要的是注意到项∂ϕ∂xi\frac{\partial \phi}{\partial x^i}∂xi∂ϕ恰好对应我们函数ϕ\phiϕ的偏导,其中项dxidx^idxi表示Rn\mathbb{R}^nRn中的一组正交基。换句话说,你可以将dϕd \phidϕ看作是ϕ\phiϕ所有偏导的清单。当然,这个对象看起来非常像我们刚刚看到的梯度∇ϕ\nabla \phi∇ϕ。并且这两个确实紧密相关,除了∇ϕ\nabla \phi∇ϕ是向量场而dϕd \phidϕ是1-型的现实。更精确的有 ∇ϕ=(dϕ)♯\nabla \phi = (d \phi)^{\sharp} ∇ϕ=(dϕ)♯ 方向导数 另一个去调查外导数行为的方法是看我们将一个向量vvv放到1-型dfdfdf中会发生什么。在坐标系中我们可以得到看起来像fff的梯度和和向量uuu之间点积的东西: df(u)=∂f∂x1u1+⋯+∂f∂xnundf(u) = \frac{\partial f}{\partial x^1}u^1 + \cdots + \frac{\partial f}{\partial x^n}u^n df(u)=∂x1∂fu1+⋯+∂xn∂fun 比如,在R2\mathbb{R}^2R2中我们可以放入一个单位向量u=(1,0)u = (1,0)u=(1,0)去得到沿着第一个坐标轴的偏导∂f∂x1\frac{\partial f}{\partial x^1}∂x1∂f: (将这个图和我们上面看到梯度的图比较一下。)一般的,df(u)df(u)df(u)表示fff沿着方向uuu的方向导数(directional derivative)。换句话说,它告诉我们当我们在uuu方向上移动一小段距离时,fff变化的有多快。再一次回到向量计算符号,我们有 df(u)=u⋅∇fdf(u) = u \cdot \nabla f df(u)=u⋅∇f 外导数的性质 任意一个k-型的导数是什么样呢?首先一点,我们希望ddd是线性的——然后,导数就是差(difference)的极限,并且差就是线性的!两个型的导数是什么样呢?回想一下传统的微积分,这个图解释了典型的乘积规则∂∂x(f(x)g(x))=f′(x)g(x)+f(x)g′(x)\frac{\partial}{\partial x}(f(x)g(x)) = f'(x)g(x) + f(x)g'(x)∂x∂(f(x)g(x))=f′(x)g(x)+f(x)g′(x): 暗色的部分表示fgfgfg在xxx处的值;亮色部分表示当我们将xxx移动一个小距离hhh的时候这个值的变化。当hhh越来越小的时候,右上角部分的贡献是可以忽略的并且我们可以将导数写为fff变化乘上ggg与ggg的变化乘上fff的和。(你可以让这个参数更严格一点吗?)由于一个k-型也测量一个(带符号的)体积,这个直觉也带来了楔形积的外导数。特别的,如果α\alphaα是一个k-型那么ddd遵守规则(这个公式需要使用下一节的内容才能推导) d(α∧β)=dα∧β+(−1)kα∧d(β)d(\alpha \wedge \beta) = d\alpha \wedge \beta + (-1)^k \alpha \wedge d(\beta) d(α∧β)=dα∧β+(−1)kα∧d(β) 它说明了整个体积的变化率可以被写为组成体积的变化项,刚好就是上面的图表示的。 1-型的外导数 为了更加具体一点,让我们看看在R3\mathbb{R}^3R3上求一个1-型的微分时会发生什么。在坐标系中解决问题时会让思路完全混乱,但是最后你可能会为非常惊讶于结果的简洁!(稍后,我们将会看到这些思想也可以不用坐标系很好的表示,通过使用Stokes‘定理,它将方法变成离散中的微分。)应用ddd的线性,我们有 dα=d(α1dx1+α2dx2+α3dx3)=d(α1dx1)+d(α2dx2)+d(α3dx3)\begin{aligned} d \alpha &= d(\alpha_1 dx^1 + \alpha_2 dx^2 + \alpha_3 dx^3) \\ &= d(\alpha_1 dx^1) + d(\alpha_2 dx^2) + d(\alpha_3 dx^3) \end{aligned} dα=d(α1dx1+α2dx2+α3dx3)=d(α1dx1)+d(α2dx2)+d(α3dx3) 每一项αjdxj\alpha_j dx^jαjdxj都可以考虑为0-型αj\alpha_jαj和对应的基1-型dxjdx^jdxj楔形积αj∧dxj\alpha_j \wedge dx^jαj∧dxj。将外导数应用到这些项中的一个我们将会得到 d(αj∧dxj)=(dαj)∧dxj+αj∧(ddxj)⎵=0=∂αj∂xidxi∧dxjd(\alpha_j \wedge dx^j) = (d \alpha_j) \wedge dx^j + \alpha_j \wedge \underbrace{(ddx^j)}_{=0} = \frac{\partial \alpha_j}{\partial x^i} dx^i \wedge dx^j d(αj∧dxj)=(dαj)∧dxj+αj∧=0(ddxj)=∂xi∂αjdxi∧dxj 为了让式子更短一点我们在这里使用了爱因斯坦累加符号,但是让我们真的写下所有的项看看 dx=α1∂x1dx1∧dx1+α1∂x2dx2∧dx1+α1∂x3dx3∧dx1=α2∂x1dx1∧dx2+α2∂x2dx2∧dx2+α2∂x3dx3∧dx2=α3∂x1dx1∧dx3+α3∂x2dx2∧dx3+α3∂x3dx3∧dx3\begin{aligned} dx &= \frac{\alpha_1}{\partial x^1} dx^1 \wedge dx^1 + \frac{\alpha_1}{\partial x^2} dx^2 \wedge dx^1 + \frac{\alpha_1}{\partial x^3} dx^3 \wedge dx^1 \\ &= \frac{\alpha_2}{\partial x^1} dx^1 \wedge dx^2 + \frac{\alpha_2}{\partial x^2} dx^2 \wedge dx^2 + \frac{\alpha_2}{\partial x^3} dx^3 \wedge dx^2 \\ &= \frac{\alpha_3}{\partial x^1} dx^1 \wedge dx^3 + \frac{\alpha_3}{\partial x^2} dx^2 \wedge dx^3 + \frac{\alpha_3}{\partial x^3} dx^3 \wedge dx^3 \end{aligned} dx=∂x1α1dx1∧dx1+∂x2α1dx2∧dx1+∂x3α1dx3∧dx1=∂x1α2dx1∧dx2+∂x2α2dx2∧dx2+∂x3α2dx3∧dx2=∂x1α3dx1∧dx3+∂x2α3dx2∧dx3+∂x3α3dx3∧dx3 使用等式α∧β=−β∧α\alpha \wedge \beta = - \beta \wedge \alphaα∧β=−β∧α,我们将得到更加简化的表达式 dα=(∂α3∂x2−∂α2∂x3)dx2∧dx3+=(∂α1∂x3−∂α3∂x1)dx3∧dx1+=(∂α2∂x1−∂α1∂x2)dx1∧dx2\begin{aligned} d \alpha &= \left( \frac{\partial \alpha_3}{\partial x^2} - \frac{\partial \alpha_2}{\partial x^3} \right) dx^2 \wedge dx^3 + \\ &= \left( \frac{\partial \alpha_1}{\partial x^3} - \frac{\partial \alpha_3}{\partial x^1} \right) dx^3 \wedge dx^1 + \\ &= \left( \frac{\partial \alpha_2}{\partial x^1} - \frac{\partial \alpha_1}{\partial x^2} \right) dx^1 \wedge dx^2 \end{aligned} dα=(∂x2∂α3−∂x3∂α2)dx2∧dx3+=(∂x3∂α1−∂x1∂α3)dx3∧dx1+=(∂x1∂α2−∂x2∂α1)dx1∧dx2 这个表达式是不是看起来很熟悉?如果你再看一眼我们对向量导数的回顾,你将意识到dαd \alphadα看起来根本就像α♯\alpha^{\sharp}α♯的旋度,除了它表示一个2-型而不是一个向量场。同时记住(从我们对Hodge星的讨论中)一个2-型和一个1-型在这里并没有什么不同——几何上来说它们都是指定了R3\mathbb{R}^3R3中的一些方向。因此,我们可以将任何一个向量场XXX的旋度写作 ∇×X=(⋆dX♭)♯\nabla \times X = \left( \star d X^{\flat} \right) ^{\sharp} ∇×X=(⋆dX♭)♯ 这里值得使用一系列操作去验证是否每一个东西都讲得通:♭\flat♭将一个向量场XXX转为一个1-型X♭X^{\flat}X♭;ddd计算了一些看起来像旋度都东西,但是解释为一个2-型dX♭dX^{\flat}dX♭;⋆\star⋆将一个2-型变为一个1-型⋆dX♭\star dX^{\flat}⋆dX♭;最后♯\sharp♯将这个1-型变回向量场(⋆dX♭)♯(\star dX^{\flat})^{\sharp}(⋆dX♭)♯。然而,这里得到了之前的信息,一个1-型的外导数看起来像一个向量场的旋度。 目前我们知道了如何用ddd表示梯度和旋度。那么关于剩下的那个向量导数,散度呢?完全不想其它无聊的导数推导,让我们做一个简单的几何观察:至少在R2\mathbb{R}^2R2中,我们可以通过旋转90度和计算它的旋度(考虑我们前面看到的例子)来决定一个向量场的散度。此外,在R2\mathbb{R}^2R2中在1-型上的Hodge星表示旋转90度,由于它定义了一条和原直线方向垂直方向的直线: 因此,我们可以假设散度可以通过首先应用Hodge星然后应用外导数来计算: ∇⋅X=⋆d⋆X♭\nabla \cdot X = \star d \star X^{\flat} ∇⋅X=⋆d⋆X♭ 最左边的Hodge星描述了d⋆X♭d \star X^{\flat}d⋆X♭是一个n-型而不是0-型的现实——在向量微积分中散度被看作是一个标量。这个定义是否真的有用呢?让我们在R3\mathbb{R}^3R3坐标系中尝试一下。首先,我们有 ⋆X♭=⋆(X1dx1+X2dx2+X3dx3)=X1dx2∧dx3+X2dx3∧dx1+X3dx1∧dx2\begin{aligned} \star X^{\flat} &= \star (X_1 dx^1 + X_2 dx^2 + X_3 dx^3) \\ &= X_1 dx^2 \wedge dx^3 + X_2 dx^3 \wedge dx^1 + X_3 dx^1 \wedge dx^2 \end{aligned} ⋆X♭=⋆(X1dx1+X2dx2+X3dx3)=X1dx2∧dx3+X2dx3∧dx1+X3dx1∧dx2 微分后我们得到 d⋆X♭=∂X1∂x1dx1∧dx2∧dx3+∂X2∂x2dx2∧dx3∧dx1+∂X3∂x3dx3∧dx1∧dx2\begin{aligned} d \star X^{\flat} = &\frac{\partial X_1}{\partial x^1} dx^1 \wedge dx^2 \wedge dx^3 + \\ &\frac{\partial X_2}{\partial x^2}dx^2 \wedge dx^3 \wedge dx^1 + \\ &\frac{\partial X_3}{\partial x^3}dx^3 \wedge dx^1 \wedge dx^2 \end{aligned} d⋆X♭=∂x1∂X1dx1∧dx2∧dx3+∂x2∂X2dx2∧dx3∧dx1+∂x3∂X3dx3∧dx1∧dx2 但是当然我们可以重排这些楔形积让式子更简单 d⋆X♭=(∂X1∂x1+∂X2∂x2+∂X3∂x3)dx1∧dx2∧dx3d \star X^{\flat} = (\frac{\partial X_1}{\partial x^1} + \frac{\partial X_2}{\partial x^2} + \frac{\partial X_3}{\partial x^3})dx^1 \wedge dx^2 \wedge dx^3 d⋆X♭=(∂x1∂X1+∂x2∂X2+∂x3∂X3)dx1∧dx2∧dx3 Hodge星最后的应用给我们带来了我们想要的散度 ⋆d⋆X♭=∂X1∂x1+∂X2∂x2+∂X3∂x3\star d \star X^{\flat} = \frac{\partial X_1}{\partial x^1} + \frac{\partial X_2}{\partial x^2} + \frac{\partial X_3}{\partial x^3} ⋆d⋆X♭=∂x1∂X1+∂x2∂X2+∂x3∂X3 总而言之,对于任意一个标量场ϕ\phiϕ和一个向量场XXX我们有 ∇ϕ=(dϕ)♯∇×X=(⋆dX♭)♯∇⋅X=⋆d⋆X♭\begin{aligned} \nabla \phi &= (d \phi)^{\sharp} \\ \nabla \times X &= \left( \star d X^{\flat} \right) ^{\sharp} \\ \nabla \cdot X &= \star d \star X^{\flat} \end{aligned} ∇ϕ∇×X∇⋅X=(dϕ)♯=(⋆dX♭)♯=⋆d⋆X♭ 这里可以注意到一个有意思的事情,(在R3\mathbb{R}^3R3中)梯度、旋度和散度分别是将ddd应用到0-型、1-型和2-型上。 拉普拉斯 另外一个从向量微积分来的微分算子是标量拉普拉斯(Laplacian),它(这里很容易混淆!)通常使用Δ\DeltaΔ或者∇2\nabla ^2∇2表示,并且定义为 Δ:=∇⋅∇\Delta \coloneqq \nabla \cdot \nabla Δ:=∇⋅∇ 即梯度的散度。虽然这个拉普拉斯看起来可能像一长串导数,但是它应该得到你最高的尊敬:拉普拉斯是基本物理法则(任何一个扩散过程和所有波传播的的形式,包括Schr¨odinger等式)中的核心;它的特征值刻画了被用来知道一个给定几何块几乎所有东西(你能听到鼓的形状吗?)。沉重的巨著和全部的东西都在忠实的遵守拉普拉斯,并且在离散情况中我们将看到这一个简单的操作符可以被用于多种多样的任务(曲面参数化、曲面平滑、向量场的设计和分解、距离计算、流体模拟…… 你说的出来的它就能做到!) 幸运的是,我们现在知道如何使用外微积分写出散度、梯度和旋度,尺度拉普拉斯的表达式是很直接的:Δ=⋆d⋆d\Delta = \star d \star dΔ=⋆d⋆d。更一般地,k-型拉普拉斯为 Δ:=⋆d⋆d+d⋆d⋆\Delta \coloneqq \star d \star d + d \star d \star Δ:=⋆d⋆d+d⋆d⋆ 名字“Laplace-Beltrami”只是被用来指定可能有一些数量曲率(由Hodge星概括的)的域。一些人喜欢定义算子δ:=⋆d⋆\delta \coloneqq \star d \starδ:=⋆d⋆,称为协微分(codifferential),并将拉普拉斯写为Δ=δd+dδ\Delta = \delta d + d \deltaΔ=δd+dδ。 你可能会问一个问题:为什么0-型的拉普拉斯不同于一般k-型的拉普拉斯?的确,它是不一样的——考虑到我们将项d⋆d⋆d \star d \stard⋆d⋆应用到一个0-型ϕ\phiϕ的时候:⋆ϕ\star \phi⋆ϕ是n-型,因此d⋆ϕd \star \phid⋆ϕ必须是(n+1)-型。但是在n维空间中没有(n+1)-型!因此当我们写标量拉普拉斯的时候这一项通常被忽略掉。 积分和Stokes‘定理 在前面的部分我们讨论了如何使用外微分ddd去微分k-型。我们也想要一些方法去进行积分。的确,在我们早就有的设定中关于积分没有什么可说的。假设我们想计算平面上区域Ω\OmegaΩ全部的面积AΩA_{\Omega}AΩ: 如果你回想微积分的内容,基本的思想是将整个域变成容易测量的小片(比如正方形)并且将它们的面积求和: AΩ≈∑iAiA_{\Omega} \approx \sum_i A_i AΩ≈i∑Ai 当这些正方形越来越小的时候我们会得到越来越好的近似,最终达到真正的面积 AΩ=∫ΩdAA_{\Omega} = \intop_{\Omega} dA AΩ=Ω∫dA 或者,我们可以使用微分形式写下独立的面积——特别的,Ai=dx1∧dx2(u,v)A_i = dx^1 \wedge dx^2(u,v)Ai=dx1∧dx2(u,v)。因此,这个面积元素dAdAdA除了是R2\mathbb{R}^2R2上标准的体积形式dx1∧dx2dx^1 \wedge dx^2dx1∧dx2之外什么也不是。(不用过于惊讶,因为k-型的所有点都是被用来测量体积的!) 为了让事情更有趣一点,让我们通过一些标量函数ϕ\phiϕ对每一个小正方形的贡献进行加权。在这种情况下我们得到一个量 ∫ΩϕdA=∫Ωϕdx1∧dx2\intop _{\Omega} \phi dA = \intop _{\Omega} \phi dx^1 \wedge dx^2 Ω∫ϕdA=Ω∫ϕdx1∧dx2 再一次这个被积函数ϕdx1∧dx2\phi dx^1 \wedge dx^2ϕdx1∧dx2可以被看作一个2-型。换句话说,你会在你整个生命中使用微分形式,甚至你都没有意识到它!更一般地,n维空间上的被积函数永远是n-型,因为我们需要将n个正交向量“放在一起”来表示局部体积。然而,现在看看曲面(即2-流形)将给我们带来所有我们直觉上需要的东西。 曲面上的积分 如果你想到了我们之前关于Hodge星的讨论,你会记得体积形式 ω=det(g)dx1∧dx2\omega = \sqrt{\det(\mathsf{g})} dx^1 \wedge dx^2 ω=det(g)dx1∧dx2 它测量了我们曲面上一个小平行四边形的面积。因子det(g)\sqrt{\det(\mathsf{g})}det(g)提醒我们不能简单地在域MMM上测量体积——我们也必须考虑任何由映射f:M→R2f: M \rightarrow \mathbb{R}^2f:M→R2带来的“拉伸”。当然,当我们在曲面上积分一个函数的时候,我们也应该考虑这种拉伸。比如,为了积分函数ϕ:M→R2\phi: M \rightarrow \mathbb{R}^2ϕ:M→R2,我们能写下 ∫Ωϕω=∫Ωϕdet(g)dx1∧dx2\intop_{\Omega} \phi \omega = \intop_{\Omega} \phi \sqrt{\det(\mathsf{g})} dx^1 \wedge dx^2 Ω∫ϕω=Ω∫ϕdet(g)dx1∧dx2 在共形参数的情况下事情会变得更简单——由于det(g)=a\sqrt{\det(\mathsf{g})} = adet(g)=a我们就有 ∫Ωϕadx1∧dx2\intop_{\Omega} \phi a dx^1 \wedge dx^2 Ω∫ϕadx1∧dx2 其中a:M→Ra : M \rightarrow \mathbb{R}a:M→R是一个标量因子。换句话说,我们放缩ϕ\phiϕ的值取决于曲面局部的“放大”或者“缩小”。实际上,这全部的内容对于老式的积分给了我们一个非常好的几何解释:你可以想象∫ΩϕdA\intop_{\Omega}\phi dA∫ΩϕdA表示初始平面区域Ω\OmegaΩ一些合适的形变版本的面积。 Stokes‘定理 学习流形上积分的主要原因是使用世界上最强有力工具的优点:Stokes’定理。Stokes’定理是说 ∫Ωdα=∫∂Ωα\intop_{\Omega} d\alpha = \intop_{\partial \Omega} \alpha Ω∫dα=∂Ω∫α 其中α\alphaα是n维域Ω\OmegaΩ上的任意(n-1)-型。换句话说,在流形的边界上积分一个微分形式等同于在全部的域上对导数积分。 如果这个技巧对你而言很熟悉,可能是因为你在不同的内容中反复看到它并且它有不同的名字:散度定理、Green’s定理、微积分基本定理、柯西积分公式等等。将这些特殊情况放在一边将帮助我们理解Stokes‘定理更一般的含义。 散度定理 让我们从向量微积分开始散度定理,它是在说 ∫Ω∇⋅XdA=∫∂Ωn⋅Xdl\intop _{\Omega} \nabla \cdot X dA = \intop_{\partial \Omega} n \cdot X dl Ω∫∇⋅XdA=∂Ω∫n⋅Xdl 其中XXX是Ω\OmegaΩ上的向量场并且nnn表示沿着Ω\OmegaΩ边界的单位法向量场。对于这个定理来讲一个更好的名字可能是“什么东西进去了它就必须出来定理”,因为如果你将XXX考虑为穿过域Ω\OmegaΩ的水流,那么很显然被灌入Ω\OmegaΩ中水的量(通过地下的管道)在任意时刻必须和它的边界流出的量相等: 让我们使用外微积分写下这个定理。首先,记住我们可以将XXX的散度写为∇⋅X=⋆d⋆X♭\nabla \cdot X = \star d \star X^{\flat}∇⋅X=⋆d⋆X♭。如何写出散度定理等式右边的内容看起来更难一点,但是考虑积分在这里做了什么:它取了边界的切线并且将它们变成1-型。比如,∫∂ΩX♭\intop_{\partial \Omega}X^{\flat}∫∂ΩX♭将XXX的正切部分“加起来”。为了获得法线部分我们可以将X♭X^{\flat}X♭旋转四分之一圈,这样可以非常方便的通过给它加一个Hodge星来实现。综上我们得到了 ∫Ωd⋆X♭=∫∂Ω⋆X♭\intop_{\Omega} d \star X^{\flat} = \intop_{\partial \Omega} \star X^{\flat} Ω∫d⋆X♭=∂Ω∫⋆X♭ 如同上面说到的,这是Stokes‘定理的一个特殊形式。我们可以使用Stokes‘定理对散度算子自己提供更几何的解释:当在任何一个区域Ω\OmegaΩ上积分时——无论多小——散度算子通过区域边界给出了全部的通量。在离散情况中我们将会看到边界通量解释就是散度符号——换句话说,在一个单独的点处是没有散度概念的。 顺便一提,为什么d⋆X♭d \star X^{\flat}d⋆X♭出现在左边而不是⋆d⋆X♭\star d \star X^{\flat}⋆d⋆X♭呢?原因是⋆d⋆X♭\star d \star X^{\flat}⋆d⋆X♭是0-型,因此我们必须对它使用另一个Hodge星将它变为测量面积的对象(即一个2-型)。应用这个变换和将dAdAdA加到∇⋅X\nabla \cdot X∇⋅X后没有什么不同——我们正在具体说明在我们的域上体积应该如何被测量。 微积分基本定理 微积分基本定理实际上可能基本到你甚至可能都不会想起来它到底是什么。从根本上它是在说在实线上的一个实值函数ϕ:R→R\phi: \mathbb{R} \rightarrow \mathbb{R}ϕ:R→R ∫ab∂ϕ∂xdx=ϕ(b)−ϕ(a)\intop ^b_a \frac{\partial \phi}{\partial x} dx = \phi(b) - \phi(a) a∫b∂x∂ϕdx=ϕ(b)−ϕ(a) 换句话说,在区间[a,b][a,b][a,b]上全部的变化是(你可能预计到了)你结束时的大小减去你开始时的大小。但是很容易的,一眼就能看出的!所以我们做过的是将它再一次写成Stokes‘定理的形式: ∫[a,b]dϕ=∫∂[a,b]ϕ\intop_{[a,b]} d\phi = \intop_{\partial [a,b]}\phi [a,b]∫dϕ=∂[a,b]∫ϕ 由于区间[a,b][a,b][a,b]的边界仅仅包含两个端点aaa和bbb。 希望这两个例子能让你很好的感受到Stokes‘定理到底在说什么。最后,它读起来很像Zen k¯oan:在外面发生的事情恰好就是在里面变化的函数。(可能这个名字是Stokes‘定理应得的,“微积分基本定理!”) 离散外微积分 目前我们仅仅在平滑的设定中探索了外微积分。不幸的是这个理论是由一些对计算机一无所知的老先生们建立的,因此它不能直接用于只能存储有限信息量的机器。比如,如果我们有一个平滑的向量场或者一个平滑的1-型我们不能储存每一个点处每个小“箭头“的方向——它们实在是太多了!相对的,我们需要保持跟踪一些离散数量(实际上,有限的)的信息块,它们刻画了我们正在操作的目标的基本行为;我们将这个方案称为离散外微积分(discrete exterior calculus)(或者简称为DEC)。关于DEC的大秘密是它确确实实就只是我们前面学过的传统的(连续的)外微积分,除了我们在我们网格的元素上积分微分形式。 离散微分形式 编码一个1-型的方式可能是储存一个与点的一些子集有关的有限“箭头”集合。作为替代,我们将做一些不一样的事情:我们将在网格的每一条边上积分我们的1-型,并且在对应的边上保存结果的数(记住一个n-型的积总是得到一个数)。换句话说,如果α\alphaα是一个1-型并且eee是一条边,那么我们将数 α^e:=∫eα\hat{\alpha}_e \coloneqq \intop_e \alpha α^e:=e∫α 与eee关联起来,其中(α^\hat{\alpha}α^)的意思是表示一个离散的量(不要和单位长度向量混淆)。 这个过程对你来说看起来是不是有点抽象?它不应该这样!考虑下积分表示什么:它告诉我们1-型α\alphaα沿着边eee的平均”流动“有多强。更具体地,记住1-型的积分是如何工作的:在沿着边的每个点我们选择与边相切的向量,将它们放入1-型α\alphaα中,然后将结果的值加起来——每一个值告诉我们一些关于α\alphaα有多贴合这条边方向的事情。比如,我们可以通过求和估算积分 ∫eα≈∣e∣(1N∑i=1Nαpi(u))\intop_e \alpha \approx |e| \left( \frac{1}{N} \sum^{N}_{i=1}\alpha_{p_i}(u) \right) e∫α≈∣e∣(N1i=1∑Nαpi(u)) 其中∣e∣|e|∣e∣表示边的长度,{pi}\{ p_i \}{pi}是沿着边的一个点序,并且u:=e/∣e∣u \coloneqq e/|e|u:=e/∣e∣是对于边的单位向量切线: 当然,这个值完全没有告诉我们一点信息有关于垂直于这条边的“流”的强度:它可能是0,也可能是特别大。我们真的不知道,因为我们沿着垂直方向做任何测量。然而,希望是一些信息仍然会被附近的边(那些不和eee平行的边)采集。 更一般地,一个k-型在每一个k维胞腔(1D中是边,2D中是面等等)的积分被称为离散微分k-型(discrete differential k-form)。(如果你发现了令人困惑的区别,你可能发现使用“积分”这个词替代“离散”这个词是很有帮助的。)然而,实际上,不是每一个离散微分形式必须从连续的形式而来——比如,一束和网格每一条边相关的任意的值是一个完美的离散1-型。 朝向 在我们目前所有的插图中你可能注意到的一个事情是每一条边都用一个小箭头标记。为什么?需要记得的一个事情是当你积分的时候需要考虑方向。比如,积分基本理论(和常识)告诉我们你从aaa到bbb的所有变化与你从bbb到aaa的所有变换相反: ∫ab∂ϕ∂xdx=ϕ(b)−ϕ(a)=−(ϕ(a)−ϕ(b))=−∫ba∂ϕ∂xdx\intop^b_a\frac{\partial \phi}{\partial x} dx = \phi(b) - \phi(a) = -(\phi(a) - \phi(b)) = -\intop^a_b \frac{\partial \phi}{\partial x}dx a∫b∂x∂ϕdx=ϕ(b)−ϕ(a)=−(ϕ(a)−ϕ(b))=−b∫a∂x∂ϕdx 用一个无趣的方法说明:当你从Pasadena到Altadena时海拔到增加是151米,在另一个方向上海拔的“增加”肯定是-151米!仅仅跟踪数字151不会有什么用——你必须说这个量表示什么。 因此,当我们储存一个离散微分形式的时候仅仅存一个数是不够的:我们也必须对网格上对每一个元素确定一个标准朝向,对应我们在积分中用到的朝向。对于一条边,我们早就看到了我们可以将朝向考虑为从一个顶点指向另一个顶点的小箭头——我们可以就将一条边看作一个有序对(i,j)(i,j)(i,j),意味着我们总是从iii到jjj积分。 更一般地,假设我们网格的每个元素是一个有向k-单纯形(oriented k-simplex)σ\sigmaσ,即,给定一些固定顺序(p1,…,pk+1)(p_1, \dots, p_{k+1})(p1,…,pk+1)的k+1个顶点pi∈Rnp_i \in \mathbb{R}^npi∈Rn的集合。和σ\sigmaσ相关的几何是这些点的凸组合(convex combination): {∑i=1k+1λipi∣∑i=1k+1λi=1,λi≥0}⊂Rn\{ \sum^{k+1}_{i=1} \lambda_i p_i | \sum^{k+1}_{i=1} \lambda_i = 1, \lambda_i \geq 0 \} \subset \mathbb{R}^n {i=1∑k+1λipi∣i=1∑k+1λi=1,λi≥0}⊂Rn (说服你自己0-单纯形是一个顶点、1-单纯形是一条边、2-单纯形是一个三角形、3-单纯形是一个四面体。) 两个有向k-单纯形有同样的朝向,当且仅当一个的顶点是另一个顶点的偶置换。比如,三角形(p1,p2,p3)(p_1,p_2,p_3)(p1,p2,p3)和(p2,p3,p1)(p_2,p_3,p_1)(p2,p3,p1)有相同的朝向;(p1,p2,p3)(p_1,p_2,p_3)(p1,p2,p3)和(p2,p1,p3)(p_2,p_1,p_3)(p2,p1,p3)有相对的朝向。 如果一个单纯形σ1\sigma_1σ1是令一个单纯形σ2\sigma_2σ2的一个(不必合适的)子集,那么我们说σ1\sigma_1σ1是σ2\sigma_2σ2的一个面。比如,一个四面体σ\sigmaσ的每一个顶点、边和三角形是σ\sigmaσ的一个面;σ\sigmaσ它自己也是!此外,一个单纯形的朝向和它一个面的朝向一致,只要我们看到在公用顶点上的偶置换。比如,边(p2,p1)(p_2,p_1)(p2,p1)的朝向和三角形(p1,p3,p2)(p_1,p_3,p_2)(p1,p3,p2)的一致。从几何上来说我们所说的所有内容都是有相同方向(上面右图描绘的)的两个“点”。为了让你处理这些网格的时候保持神志清醒,最重要的事情是选择一个朝向并且保持它不变! 所以一般情况下,我们如何在有向k-单纯形上积分k-型呢?记住一个k-型将在每个点“吃掉”k个向量并且吐出一个数——一个不错的标准选择是选取有序边向量(p2−p1,⋯,pk−1−p1)(p_2-p_1, \cdots, p_{k-1}-p_1)(p2−p1,⋯,pk−1−p1)的集合并且他们单位正交化(使用被称为Gram-Schmidt的算法)得到向量组(u1,⋯,uk)(u_1,\cdots,u_k)(u1,⋯,uk)。这个方法中无论何时朝向变化了被积函数的符号也会变化。从数字上说,我们可以通过求和估算这个积分 ∫σα≈∣σ∣N∑i=1Nαpi(u1,⋯,uk)\intop_\sigma \alpha \approx \frac{|\sigma|}{N} \sum^N_{i=1}\alpha_{p_i}(u_1, \cdots, u_k) σ∫α≈N∣σ∣i=1∑Nαpi(u1,⋯,uk) 其中{pi}\{p_i\}{pi}是一个(通常很仔细地选择出来)简单点的集合。(你能看出为什么σ\sigmaσ的朝向会影响被积函数的符号吗?)看起来想很大量的工作,但是实际上很少通过积分来构造离散微分形式:更常见的是,离散形式通过早已离散化的输入点(比如三角网格上的顶点坐标)构造。 顺带一提,离散的0-型是什么?放弃了?好吧,它必须是一个在网格的每一个0-单纯形viv_ivi(即顶点)上被积分的0-型(即一个函数): ϕ^i=∫viϕ\hat{\phi}_i = \intop_{v_i} \phi ϕ^i=vi∫ϕ 按照惯例,函数在0维集上的积分就是函数在那个点的值:ϕ^i=ϕ(vi)\hat{\phi}_i = \phi(v_i)ϕ^i=ϕ(vi)。换句话说,0-型的情况下储存点的采样和储存积分值没有什么区别:这两者等价。注意到0-型的朝向永远是正的,因为在一个顶点上的恒等映射是一个偶置换。 一个也很重要的事情是记住微分形式不必是实值的。比如,我们可以考虑一个映射f:M→R3f:M\rightarrow\mathbb{R}^3f:M→R3,它使用一个R3\mathbb{R}^3R3值的0-型编码了一个曲面的几何;那么它的微分dfdfdf是一个R3\mathbb{R}^3R3值的1-型,等等。同样地,当我们说一个离散微分形式是被存在每个网格元素中的一个数,这个词“数”被用在非常宽松的含义:一个数可以是一个实值、一个向量、一个复数、一个四元数等等。比如,网格上(x,y,z)(x,y,z)(x,y,z)顶点坐标系的集合可以被看作是一个R3\mathbb{R}^3R3值的离散0-型(即它将映射fff离散化了)。当然,唯一需要的是在每一个网格元素储存相同类型的数。 离散外导数 使用积分(即,“离散的”)微分形式而不是点采样的主要优势是我们可以简单的使用Stokes‘定理的优势。回想一下Stokes‘定理说的是对于任何一个k-型α\alphaα和一个(k+1)维域Ω\OmegaΩ都有 ∫Ωdα=∫∂Ωα\intop_{\Omega} d\alpha = \intop_{\partial \Omega} \alpha Ω∫dα=∂Ω∫α 换句话说,我们可以积分微分形式的导数然后我们就知道了它沿着边界的积分。但是这是确切的由一个离散微分形式编码的信息类型!比如,如果α^\hat{\alpha}α^是存在三角形σ\sigmaσ的三条边上的一个离散的1-型,那么我们有 ∫σdα=∫∂σα=∑i=13∫eiα=∑i=13α^i\intop_{\sigma}d \alpha = \intop_{\partial \sigma} \alpha = \sum^3_{i=1} \intop_{e_i}\alpha = \sum^3_{i=1} \hat{\alpha}_i σ∫dα=∂σ∫α=i=1∑3ei∫α=i=1∑3α^i 换句话说,我们可以确切地通过将三个数值加起来估算左边的积分。相当酷!实际上,左边这个东西也是离散微分形式:它是2-型α\alphaα在我们网格上的这一个三角形上的积分。所以为了方便一点,我们将这个家伙称为“d^α^\hat{d}\hat{\alpha}d^α^”,并且我们将运算d^\hat{d}d^称为离散外导数(discrete exterior derivative)。(在未来当上下文含义清晰的时候我们将去掉这个帽子。)换句话说,离散外导数使用在每一个k-单纯形已经被积分的k-型并且应用Stokes‘定理去获得每一个(k+1)-单纯形上的导数的积分。 实际中(即在代码中)你可以看到通过在合适的网格元素上简单的使用局部求和来实现这个运算。然而,在上面的例子中,我们通过给每一条边与三角形的朝向一致的朝向让这个东西特别容易。不幸的是给每一个单纯形分配一致的朝向并不总是可能的,并且一般情况下当将我们逐片积分相加的时候我们需要对符号更小心一点。比如,在下面这个例子中我们有 (d^α^)1=α^1+α^2+α^3(\hat{d} \hat{\alpha})_1 = \hat{\alpha}_1 + \hat{\alpha}_2 + \hat{\alpha}_3 (d^α^)1=α^1+α^2+α^3 和 (d^α^)2=α^4+α^5−α^2(\hat{d} \hat{\alpha})_2 = \hat{\alpha}_4 + \hat{\alpha}_5 - \hat{\alpha}_2 (d^α^)2=α^4+α^5−α^2 离散Hodge星 如同上面提到的,一个离散k-形刻画了沿着k个方向的一个连续k-型的行为,但是没有剩下的沿着(n-k)个方向——比如,2D中的一个离散1-型刻画了沿着边的流但是没有垂直方向的。如果你特别关注了Hodge对偶的讨论,那么这个内容听起来就非常相似了!为了刻画离散情况下的Hodge对偶,我们将需要定义一个对偶网格。一般的,一个n维单纯网格的对偶使用独立的(n-k)-胞腔确定了主要的(即原始的)网格中每一个k-单纯形。比如,在一个2维单纯网格中,主要的顶点都用对偶面定义,主要的边都用对偶边定义,主要的面都用对偶顶点定义。然而,注意到对偶胞腔并不总是单纯形!(看上图。)当原始元素和对偶元素在正交的线性子空间中时,一个对偶的网格是正交对偶。比如,在一个平面三角网格上一条对偶边可能和对应的原始边有一个合适的角度。对于弯曲的域,我们仅仅要求原始元素和对偶元素内在正交,即如果刚性地将一对相邻的三角形展开到平面,原始的和对偶的边应该再一次正交。 对偶网格元素被包含在正交线性子空间的事实很自然地引出了Hodge对偶在离散情况中的符号。特别的,在原始网格上的一个(离散)k-型的离散Hodge对偶是对偶网格上的(n-k)-型。类似地,对偶网格上的一个k-型的Hodge对偶是原始网格上的一个(n-k)-型。原始网格上的离散形式被称为主要形式并且对偶网格上的离散形式被称为对偶形式。给定一个离散形式α^\hat{\alpha}α^(不管是原始的还是对偶的),我们将它的Hodge对偶写为⋆^α^\hat{\star}\hat{\alpha}⋆^α^。 不像连续形式,离散的主要和对偶形式不在一个地方(比如,离散的主要k-型和对偶k-型不能相加)。实际上,主要和对偶形式通常有不同的物理解释。比如,一个主要的1-型可能表示沿着这个主要网格边的全部环流量,然而在同样的上下文中一个对偶1-型可能表示穿过这个对应对偶边的全部通量(看上面的图示)。 当然,这两个量(环流量和通量)是紧密相关的,并且自然地引出了一个被称为对角Hodge星(diagonal Hodge star)的离散Hodge星的定义。考虑一个原始的k-型α\alphaα。如果α^i\hat{\alpha}_iα^i是α^\hat{\alpha}α^在k-单纯形σi\sigma_iσi上的值,那么对于所有的iii对角Hodge星被定义为 ⋆^α^i=∣σistar∣σiα^i\hat{\star} \hat{\alpha}_i = \frac{|\sigma ^{star}_{i}|}{\sigma_i} \hat{\alpha}_i ⋆^α^i=σi∣σistar∣α^i 其中∣σ∣|\sigma|∣σ∣表示σ\sigmaσ的(无符号)体积(根据惯例它对于每一个顶点是等于1的!)并且∣σ⋆∣|\sigma^{\star}|∣σ⋆∣是对应对偶胞腔的体积。换句话说,为了计算对偶型我们简单地乘上标量值,它是保存在每个胞腔中的对应对偶和主要体积的比。 如果我们记得一个离散形式可以被考虑为一个连续形式在每一个胞腔上的积分,对于Hodge星的定义可能就会相当完美:主要的和对偶的量应该有相同的密度,但是我们应该考虑它们是在不同体积的胞腔上积分的现实。我们因此通过体积的比例归一化,当我们主要的和对偶的之间映射的时候。这个特别的Hodge星被称为对角的,因为对偶微分形式的第iii个元素仅仅依赖于原始微分形式的第iii个元素。不难看出,将对偶形式带到主要形式的Hodge星(对偶Hodge星)是将主要形式带到对偶形式Hodge星(主要Hodge星)的逆。 大家,这就是全部了! 嘿,等一会,关于我们其它的运算,像楔形积(∧)(\wedge)(∧)呢?这些运算无疑能在离散设定中被定义,但是这里我们将不会追究细节——基本的诀窍是积分、积分、积分。的确,甚至在连续外微积分中我们都忽略了一对运算像李导数(Lie derivative)(LX)(\mathcal{L}_X)(LX)和内积(iα)(i_{\alpha})(iα)。想一想完整的离散微积分,其中全部的符号表示有d,∧,⋆,LX,iαd,\wedge,\star,\mathcal{L}_X,i_{\alpha}d,∧,⋆,LX,iα等等,在一个活跃且持续的研究领域中相互配合得很好。]]></content>
<tags>
<tag>math</tag>
<tag>DDG</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DDG(离散微分几何):对微分几何快速粗暴的一个介绍]]></title>
<url>%2Farchives%2F39753.html</url>
<content type="text"><![CDATA[翻译自 DISCRETE DIFFERENTIAL GEOMETRY: AN APPLIED INTRODUCTION 第三章 A Quick and Dirty Introduction to Differential Geometry。如果有翻译错误或者不当的地方希望能指出,谢谢~ 曲面的几何 有许多方法考虑平滑曲面的几何(比如,地图集),但是这里有一张图很贴合我们在离散设定下操作曲面的方式。考虑一个小布块悬浮在空中,像下面描绘的一样。它的几何可以通过从欧式平面R2\mathbb{R}^2R2中的一个区域MMM到R3\mathbb{R}^3R3子集f(M)f(M)f(M)的映射f:M→R3f:M \rightarrow \mathbb{R}^3f:M→R3表示: 这样一个映射的微分(differential),记为dfdfdf,告诉我们如何将平面上的一个向量XXX映射到曲面上对应的向量df(X)df(X)df(X)。粗略来讲,想象MMM是一个橡胶片,并且XXX是画在MMM上的一条小黑色线段。我们将MMM拉伸和形变为f(M)f(M)f(M),这个线段XXX同样会被拉伸和形变为另一个线段,我们称之为df(X)df(X)df(X)。稍后我们讨论如何在坐标系中明确表示df(X)df(X)df(X)和其它的一些内容,但是重要的是意识到关于微分除了你刚刚看到的这张图基本上没有更深层次的东西需要知道——微分简单地告诉你当你从一个空间到另一个空间的时候是如何拉长或者“向前推”向量的。比如,被fff向前推后的正切向量XXX的长度可以表示为 df(X)⋅df(X)\sqrt{df(X) \cdot df(X)} df(X)⋅df(X) 其中 ⋅\cdot⋅ 是R3\mathbb{R}^3R3上的标准内积(点乘或者数乘)。注意到这个长度和我们刚刚开始的向量长度一般来讲是不同的。为了行文清晰,我们使用角括号表示平面中的内积,即原始向量的长度应该是⟨X,X⟩\sqrt{\langle X, X \rangle}⟨X,X⟩。更一般的,我们可以测量两个正切向量df(X)df(X)df(X)和df(Υ)df(\Upsilon)df(Υ)之间的内积: g(X,Υ)=df(X)⋅df(Υ)\mathsf{g}(X, \Upsilon) = df(X) \cdot df(\Upsilon) g(X,Υ)=df(X)⋅df(Υ) 映射g\mathsf{g}g被称为曲面的度量(metric),或者更专业一点,由fff引导的度量。注意到全文中,我们将使用df(X)df(X)df(X)交换地表示单个向量的变动(pushforward,应该可以理解成拉伸和形变)与整个向量场,即MMM中每一个点的向量。在大多数表示中我们将考虑这种区别不产生大的歧义,但是值得我们注意一下。全文中我们将使用TMTMTM表示MMM的正切束(tangent bundle),即所有正切向量的集合。 现在我们谈到了正切向量,即沿着曲面平躺的向量。我们同样对与曲面正交的向量感兴趣。尤其是,我们说向量u∈R3u \in \mathbb{R}^3u∈R3是曲面在点ppp处点法线,当 df(X)⋅u=0df(X) \cdot u = 0 df(X)⋅u=0 对所有ppp处的正切向量XXX。为了方便,我们通常给出唯一一个特殊的法向量NNN,被称为单位法线,它的长度为1。当然,在任何一个给定点处都有两个不同的单位法向量:−N-N−N和+N+N+N。我们应该用哪一个呢?如果我们可以为NNN选取一个一致的方向,那么我们称MMM是可定向的。比如,左边的环带是可定向的,但是右边的莫比乌斯带就不是: 对于可定向曲面,我们可以把NNN看作连续映射N:M→S2N:M \rightarrow \mathcal{S}^2N:M→S2(被称为高斯映射(Gauss map)),它将每个点和它对应的单位法线联系起来,将每个点视为单位球S2\mathcal{S}^2S2上的点。实际上,如果我们将S2\mathcal{S}^2S2看作是R3\mathbb{R}^3R3的子集(所有到原点单位距离点的集合),那么我们可以对NNN做所有我们在映射fff上做的事情。实际上,微分dNdNdN(被称为 Weingarten 映射)告诉我们当我们从一个点到另一个点的时候法线方向的变化。比如,我们可以看着沿着由dN(X)dN(X)dN(X)给出的特定正切方向XXX的法线变化——当我们谈论曲面曲率的时候这个理解会变得非常有用。综上我们用这个图来结尾,它表达了关于曲面集合最基础的想法: 共形坐标系 当在曲线上操作时,大家经常被介绍使用等距参数(比如,弧长或单位速度)的想法。这个想法通过假设当我们从当前域到R3\mathbb{R}^3R3的时候没有“伸长”发生使得明确的表达式更简单。一种表示这个规定的方法是 ∣df(X)∣=∣X∣|df(X)| = |X| ∣df(X)∣=∣X∣ 即我们要求保证任何一个向量XXX的范数不变。 对于曲面,一个等距参数并不总是存在(甚至对于局部也是这样!)。绝大多数时候你简单的必须撑大一个东西。比如,你可能知道没有形变地将地球的曲面平铺在平面上是不可能的——这也就是为什么我们得到球形不同的古怪映射种类的原因。 然而,有一个配置(就像对于曲线来说的弧长)当处理明确公式时能让生活更简单,它叫做共形坐标系(conformal coordinates)。十分简单的来说,一个映射fff是共形的,当它保持两个向量之间的夹角不变时。更具体的来说,一个共形映射f:R2⊃M→R3f:\mathbb{R}^2 \supset M \rightarrow\mathbb{R}^3f:R2⊃M→R3对于所有的正切向量X,ΥX, \UpsilonX,Υ满足 df(X)⋅df(Υ)=a⟨X,Υ⟩df(X) \cdot df(\Upsilon) = a\langle X, \Upsilon \rangle df(X)⋅df(Υ)=a⟨X,Υ⟩ 其中aaa是一个正定函数,⟨⋅,⋅⟩\langle \cdot , \cdot \rangle⟨⋅,⋅⟩是R2\mathbb{R}^2R2上的一般内积。实际上,对于一些实值函数uuu,函数aaa通常被eue^ueu代替——这样的话就完全无需担心尺度是否为正了。注意向量仍然可以被拉长,但是曲面不会被剪断——比如,正交的向量永远保持正交: 关于共形映射的一个关键事实是它们总是存在,由单值化定理担保。简而言之,单值化定理说的是任意一个盘都可以被共形映射到平面上。使用如果我们考虑我们曲面f(M)f(M)f(M)上点任一一点ppp,我们知道我们总是能在ppp周围一些小的、盘状的领域找到一个共形参数化。正如单位速度的曲线一样,它通常足够去简单地知道一个共形参数化存在——我们不必明确去构造这个映射。并且,正如弧长参数化一样,我们必须保持关于这个域如何伸展的最少可能信息量的跟踪:仅仅是每个点处的一个数(对立的是,一个完整的雅可比矩阵)。 导数和正切向量 在实线上的导数 目前我们用了非常几何的方法来考虑微分:它告诉我们如何像从一个地方到另一个地方一样去伸长或者变动(push forward)正切向量。实际上,我们可以将几何观点应用到包括导数在内的非常多的情况中。比如,考虑一个很好的不是很古怪的在实线上的实值函数ϕ(x)\phi(x)ϕ(x)。我们使用一贯的方法,通过将ϕ\phiϕ的值作为xxx轴上的高度绘制出来进行可视化: 在这种情况下,导数ϕ′\phi'ϕ′可以解释为高度函数的斜率,就像上图中虚线表示的一样。或者说,我们可以想象ϕ\phiϕ拉长了这个实线自身,由节点在这个图中分布的变化表示: 当导数大的时候,节点分布距离比较远;当导数小的时候,节点分布距离比较近。这个图告诉我们将ϕ\phiϕ的导数写成指向正xxx轴方向的单位切向量XXX的变动项dϕ(X)d\phi(X)dϕ(X): ϕ′=dϕ(X)\phi' = d \phi (X) ϕ′=dϕ(X) 换句话说,ϕ\phiϕ的导数就是当我们从一个R\mathbb{R}R的副本到另一个时的“伸长因子”。但是等一下——这个等式是否讲得通?这个等式的左边是一个标量,但是右边却是一个向量!当然,任何实线上的切向量都可以被表示成一个单独的值,量化它在正或负方向的大小。所以这个等式是讲得通的——只要我们理解我们是使用实数定义R\mathbb{R}R上的正切向量的。通常这种“类型校验”将帮助验证公式和表达是正确的,类似于你可能在物理等式中校验单位的方法。 这里又出现了一个问题:这个导数的解释和我们通常对高度函数项的解释有什么不一样吗?难道我们不也是在那种情况下拉长实线吗?是也不是——当然这个实线仍然拉长到另一个曲线。但是这个曲线现在是平面R2\mathbb{R}^2R2的子集——具体来说,它是曲线γ=(x,ϕ(x))\gamma = (x, \phi(x))γ=(x,ϕ(x))。所以其中一个原因是,这种情况下“类型校验”失败:ϕ′\phi'ϕ′是一个标量,但是dγ(X)d\gamma(X)dγ(X)是一个2维向量。但是最重要的是,由曲线表示的伸长量并不和我们通常的ϕ\phiϕ导数符号对应——比如,当我们寻找∣dγ(X)∣|d\gamma(X)|∣dγ(X)∣的量时我们得到1+(ϕ′)2\sqrt{1+(\phi')^2}1+(ϕ′)2。(为什么这个声明是符号几何原理的?你会如何将ϕ′\phi'ϕ′写入dγ(X)d\gamma(X)dγ(X)项中?你能构造一个等式修复这个合适的符号吗?) 方向导数 目前为止的状态都很好:我们可以将R\mathbb{R}R上实值函数的导数考虑为(正向)单位切向量XXX的变动。但是当ϕ\phiϕ定义在一些其它的域上时,比如平面R2\mathbb{R}^2R2,dϕ(X)d\phi(X)dϕ(X)表示什么呢?这个问题可能将你的思想“伸展”一点,但是如果你理解了这个例子你将很好地用你自己的方式理解正切向量项中的导数。让我们看看这个问题的几何——再一次,有两种我们描绘ϕ\phiϕ的方式。通常的方式是在平面上画高度函数: 导数和坡的斜率相关,但是哪个方向呢?为了回答这个问题,我们将介绍方向导数(directional derivative)的想法——即我们选择一个向量XXX然后看我们在那个方向上山(或下山)有多快。然后再一次我们考虑一个转换后的图像: 由于ϕ\phiϕ是一个从R2\mathbb{R}^2R2到R\mathbb{R}R的映射,我们可以想象它拿起了一个橡胶片然后将它拉成一个长的、细的、沿着实线的一维对象。因此当我们在原来的片上画上一个箭头XXX,然后这个“拉长后”的箭头dϕ(X)d\phi(X)dϕ(X)给我们ϕ\phiϕ在沿着方向XXX上变化的比例,即方向导数。关于类型校验的情况呢?在此之前,将前面的东西回顾一下:dϕ(X)d\phi(X)dϕ(X)是R\mathbb{R}R上的正切向量,所以它可以通过单个实数表示。(在上面我们是怎么继续在高度函数上操作的?我们如何在这个情况下修复方向导数?) 顺便提一下,不要担心这个讨论看起来非常的不正式!当我们开始讨论外积分时,我们将看到一个对于这些想法更明确的、代数的探讨。现在重要的事情是对导数建立一些几何直觉。具体来说:从一个空间映射到另一个空间可以被看作几种弯曲、扭转和伸展(或者可能的撕裂!);导数可以被理解为一个项,它按照这个方法发生在小箭头上。 曲线的几何 我们看到的曲面的图是一个关于理解任意维度形状的好方法。比如,我们可以将一个一维的曲线考虑为从实线区间I=[0,T]⊂RI = [0, T] \subset \mathbb{R}I=[0,T]⊂R到R3\mathbb{R}^3R3的映射γ:I→R3\gamma : I \rightarrow \mathbb{R}^3γ:I→R3,这个微分dγd\gammadγ再一次告诉我们正切向量如何通过γ\gammaγ伸展,并且再一次引入了正切向量XXX的长度为 ∣dγ(X)∣=dγ(X)⋅dγ(X)|d\gamma(X)| = \sqrt{d\gamma(X) \cdot d\gamma(X)} ∣dγ(X)∣=dγ(X)⋅dγ(X) 如果γ\gammaγ保长的话在曲线上操作将更容易,即如果对任意正切向量XXX有 ∣dγ(X)∣=∣X∣|d\gamma(X)| = |X| ∣dγ(X)∣=∣X∣ 这种参数化有许多不同的名字(“单位速度”,“弧长”,“等距”),但是这个想法简单到我们从R\mathbb{R}R到R3\mathbb{R}^3R3时曲线没有伸展——将γ\gammaγ考虑为一个完全松弛的橡胶带。单位速度的观点对于我们没有基本域III的符号时的离散设置通常是合适的一个——从最开始,这个曲线就作为R3\mathbb{R}^3R3的子集给到我们并且我们能做的所有事情就是假设它在那里保持一个松弛的状态。 曲线的曲率 假设我们有一个单位速度的曲线γ\gammaγ和一个在区间III上的正向单位向量XXX。那么 T=dγ(X)T = d\gamma(X) T=dγ(X) 是R3\mathbb{R}^3R3中与曲线相切的单位向量。将这个想法向前推进一步,我们可以看到当我们沿着γ\gammaγ移动时切向的变化。由于TTT可能按照任何比例变化(也可能完全不变!),我们将这个变化分离为两个部分:一个被成为主法线(principal normal)的单位向量NNN表示变化的方向,和一个被成为曲率(curvature)的标量κ∈R\kappa \in \mathbb{R}κ∈R表示变化的程度: dT(X)=−κNdT(X) = - \kappa N dT(X)=−κN 一个事情需要意识到,那就是TTT和NNN总是正交的。为什么?因为当TTT中的变化平行于TTT时,那么它将不再有单位长度!(这个观点是在处理单位向量场的任何时候都需要牢记在心的一个很好的观点。)按照习惯,我们选择NNN为指向曲线“左侧”的法线,即,如果在任一点处我们考虑一个由切向量和法向量张成的平面,NNN是从TTT往逆时针方向转四分之一得到的。此时也会带来第三个被称为副法线(binormal)的向量B=T×NB = T \times NB=T×N,我们得到一个被称为Frenet框架(Frenet frame)的非常自然的正交坐标系框架。 当我们沿着曲线运动时这个框架如何变化?答案由FrenetSerret公式(FrenetSerret formula)给出: [T′N′B′]⎵Q′∈R3=[0−κ0κ0−τ0τ0]⎵A∈R3×3[TNB]⎵Q∈R3\underbrace{\begin{bmatrix} T' \\ N' \\ B' \end{bmatrix}} _{Q' \in \mathbb{R}^3}= \underbrace{ \begin{bmatrix} 0 & -\kappa & 0 \\ \kappa & 0 & -\tau \\ 0 & \tau & 0 \end{bmatrix} }_{A \in \mathbb{R}^{3 \times 3}} \underbrace{ \begin{bmatrix} T \\ N \\ B \end{bmatrix} }_{Q \in \mathbb{R}^3} Q′∈R3⎣⎡T′N′B′⎦⎤=A∈R3×3⎣⎡0κ0−κ0τ0−τ0⎦⎤Q∈R3⎣⎡TNB⎦⎤ 这里,TTT、NNN和BBB都被解释为行向量,带上标的表示当我们在单位速度下沿着曲线运动的量下的变化。比如,T′=dT(X)T' = dT(X)T′=dT(X),当XXX是III上的正向单位向量。量τ\tauτ被称为扭转(torsion),描述法线和副法线围绕曲线缠绕(twist)的方式。 这个公式一个简洁的证明由Cartan给出。首先,由于向量TTT、NNN和BBB是互相正交的,我们可以非常简单的验证QQT=IQQ^T = IQQT=I,即QQQ为正交矩阵。此时将这个关系微分,这个单位矩阵会消失并且会剩下Q′QT=−(Q′QT)TQ'Q^T = -(Q'Q^T)^TQ′QT=−(Q′QT)T,即矩阵Q′QTQ'Q^TQ′QT是反对称矩阵(skew-symmetric)。但是由于A=Q′QTA = Q'Q^TA=Q′QT,AAA必须也是反对称的。反对称要求AAA的主对角元素必须为0(为什么?),并且我们早就从我们对κ\kappaκ和NNN的定义知道了顶行(因此知道了左列)长什么样。剩下的值A23=−A(32)A_{23} = -A_(32)A23=−A(32)没有被任何方式约束,所以我们简单地给它一个名字:τ∈R\tau \in \mathbb{R}τ∈R。 我们如何看待这个证明?一方面它很容易验证;另一方面,它提供了一点简单的几何理解。比如,为什么NNN同时在TTT和BBB的方向上变化,但是BBB只在NNN的方向上变化?你能想出更多的几何观点嘛? 可视化曲线 圆S\mathcal{S}S曲率是什么?当S\mathcal{S}S有半径rrr时,在单位速度下需要花费2πr2 \pi r2πr的时间去绕这个圆一圈。在这段时间中,切线方向转了2π2 \pi2π的角度。当然,由于TTT有单位长度,TTT上瞬间的变化仅仅通过角度的瞬间变化描述。所以我们最终得到(第三个等号是总变化/时间) κ=∣κN∣=∣dT(X)∣=2π/2πr=1/r\kappa = |\kappa N| = |dT(X)| = 2 \pi /2 \pi r = 1 / r κ=∣κN∣=∣dT(X)∣=2π/2πr=1/r 换句话说,圆的曲率简单地就是半径的倒数。这个现实可以得到一些直观的感受:如果我们看到一个圆变得越来越大,它最终就会看起来像一个曲率为0的直线:\lim_{r\rightarrow \infin} 1/r = 0。类似的,如果我们看到一个圆越来越小,它最后看起来就会像一个有无穷曲率的点:\lim_{r\rightarrow 0} 1/r = \infin。 现在考虑一个平面中的平滑曲线γ\gammaγ。在任意一点p∈γp \in \gammap∈γ处都有一个被成为密切圆(osculating circle)的圆S\mathcal{S}S最好地拟合了γ\gammaγ,意味着它有同样的切向TTT和曲率向量κN\kappa NκN。换句话说,这个圆和这个曲线“到第二阶”一致。(这个短语“到第n阶一致”只是前n阶导数相等说法的速记。)我们如何知道这样的圆存在呢?简单:我们总是可以通过设置r=1/κr = 1/ \kappar=1/κ估计曲率构造一个圆;此外每个圆在方向TTT上有同样的切点。或者说,我们可以考虑一个圆,它穿过点ppp和其它的两个点:一个从左边靠近,一个从右边靠近。因此这三个点同时在γ\gammaγ和S\mathcal{S}S上,一阶和二阶导将在极限上相等(考虑这些点可以被用来获得TTT和κN\kappa NκN一致的有限微分估计)。 这个密切圆的半径和中心通常被分别称为曲率半径(radius of curvature)和曲率中心(center of curvature)。我们可以通过考虑密切面(osculating plane)T×NT \times NT×N对任意R3\mathbb{R}^3R3中的曲线讲述同样的内容,因为这个平面同时包含了正切向量和曲率向量。 对于曲线,我们用正切向量或者(主)法向量的变化项表示曲率不会造成什么不同,因为这两个向量于密切平面中在四分之一圈的旋转意义下等价。然而,对于曲面来说,它通常将曲率考虑为法向量的变化更合理,因为我们通常没有可以在上面操作的明确可以区分出的正切向量。 曲面的曲率 让我们更深入地来了解一下曲面的曲率。这个词“curvature”真正地对应了,对于某个东西被弯曲时,我们对它表示什么意思的日常理解:蛋壳、甜甜圈和某种意大利面食(这个真的不知道怎么翻译,cavatappi pasta)有大量的弯曲;地板、天花板和硬纸板箱就不是。但是对于一些看起来像啤酒瓶的东西呢?沿着其中一个方向这个瓶子快速地在绕着一个圆弯曲;沿着另一个方向它是完全平坦的并且沿着一条直线移动: 这个看待曲率的方式——包含在曲面中的曲线项——是通常我们看待曲率的方式。特别的,让df(X)df(X)df(X)是曲面上某个特定点处的单位切向,并且考虑同时包含df(X)df(X)df(X)和对应的法线NNN的平面。这个平面与曲面交于一条曲线,那么这条曲线的曲率κn\kappa _nκn被称为XXX方向上的法线曲率(normal curvature): 还记得Frenet-Serret公式吗?它告诉我们沿着曲线时法线的变化为dN=κT−τBdN = \kappa T - \tau BdN=κT−τB。我们因此能通过抽出dNdNdN无关的部分得到沿着XXX的法线曲率: κn(X)=df(X)⋅dN(X)∣df(X)∣2\kappa_n(X) = \frac{df(X) \cdot dN(X)}{|df(X)|^2} κn(X)=∣df(X)∣2df(X)⋅dN(X) 这个分母中的因子∣df(X)∣2|df(X)|^2∣df(X)∣2简单地归一化了任何当我们从域MMM到R\mathbb{R}R中遇到的“伸长”。注意到法线曲率是带符号的,意味着曲面可以朝向法线方向弯曲或者反方向弯曲。 主曲率、平均曲率和高斯曲率 在任一给定点处我们可以问:沿着哪个方向这个曲面弯曲得最厉害?沿着我们找到的最大和最小法线曲率κ1\kappa_1κ1和κ2\kappa_2κ2的单位向量X1X_1X1和X2X_2X2被称为主方向(principal directions);曲率κi\kappa_iκi被称为主曲率(principal curvatures)。比如,上面的啤酒瓶可能在标记的点处有主曲率k1=1k_1 = 1k1=1, k2=0k_2 = 0k2=0。 我们同时也使用形状算子(shape operator)项来讨论主曲率,它是一个单射S:TM→TM\mathcal{S}:TM \rightarrow TMS:TM→TM,对于所有的正切向量XXX满足 df(SX)=dN(X)df(SX) = dN(X) df(SX)=dN(X) 形状算子S\mathcal{S}S和 Weingarten 映射dNdNdN本质上表达的是同一个想法:它们都告诉我们沿着方向XXX移动时法线是如何变化的。唯一的不同是S\mathcal{S}S 特指MMM上正切向量项的变化,而dNdNdN告诉我们的是R3\mathbb{R}^3R3中正切向量的变化。这个区别没有上面特定含义因此很多作者并不提及它,并且假设MMM上的正切向量和对应R3\mathbb{R}^3R3中的正切向量是等价的。然而,我们选择更小心一点,因此我们可以更明确的描述各种浸入(immersion)fff上的依赖——这个依赖当你恰好想计算某些东西的时候变得尤其重要!(顺便,为什么我们总是可以使用正切向量项解释NNN的变化?那是因为NNN是单位法线,因此它不可能在法向上伸缩。) 一个关于主方向和主曲率的重要现实是它们(分别)对应形状算子的特指值和特征向量: SXi=κiXi\mathcal{S}X_i = \kappa_i X_i SXi=κiXi 此外,主方向在引导度量的意义上是正交的:g(X1,X2)=df(X1)⋅df(X2)=0\mathsf{g}(X_1, X_2) = df(X_1) \cdot df(X_2) = 0g(X1,X2)=df(X1)⋅df(X2)=0。因此主曲率告诉了我们关于某点处法线曲率的所有事情,因此我们可以使用主方向X1X_1X1和X2X_2X2的线性组合表示任何一个正切向量Υ\UpsilonΥ。特别的,当Υ\UpsilonΥ是一个从X1X_1X1转了角度θ\thetaθ的单位向量偏移,那么它对应的法线曲率为 κn(Υ)=κ1cos2θ+κ2sin2θ\kappa_n(\Upsilon) = \kappa_1 \cos^2\theta + \kappa_2 \sin^2\theta κn(Υ)=κ1cos2θ+κ2sin2θ 你应该能够使用上面的关系简单地验证。然而,通常来说直接使用主曲率是相当不方便的——尤其在离散情况下。 另一方面,两个紧密相关的量——被称为平均曲率(mean curvature)和高斯曲率(Gaussian curvature)将一次又一次出现(并且在离散情况中有一些特别好的解释)。平均曲率HHH是主曲率的算术平均值: H=κ1+κ22H = \frac{\kappa_1 + \kappa_2}{2} H=2κ1+κ2 高斯曲率是几何平均(的平方): K=κ1κ2K = \kappa_1 \kappa_2 K=κ1κ2 那么HHH和KKK的值分别表示关于曲面形状的什么东西呢?可能绝大多数基本解释是高斯曲率类似逻辑“与”(是同时沿着两个方向的曲率吗?)而平均曲率更类似逻辑“或”(沿着至少一个方向的曲率吗?)当然,在这里你必须小心一点因为在κ1=−κ2\kappa_1 = - \kappa_2κ1=−κ2时你也会得到0平均曲率。 看一些0平均曲率和高斯曲率的图片同样是有帮助的。0曲率曲面在数学上已经被研究得很透彻了,因此它们有特殊的名字。0高斯曲率的曲面被成为可扩展曲面(developable surfaces),因为它们可以被“扩展”或者在没有拉伸或撕裂的情况下被平铺到平面上。比如,圆柱体的任意一片是可扩展的,因为主曲率中的一个是0: 0平均曲率的曲面被称为最小曲面(minimal surfaces),因为(就像我们稍后将看到的)它们最小化曲面空间(在确定的约束下)。最小曲面趋向鞍状,因为主曲率有相同的大小但是符号相反: 同时马鞍也是一个有负高斯曲率的很好的曲面例子。有正高斯曲率的曲面长什么样?半球是一个例子: 注意到这个情况下κ1=κ2\kappa_1 = \kappa_2κ1=κ2并且因此主方向并不是唯一定义的——沿着任何方向XXX都能实现最大(或者最小)曲率。曲面上任何一个这样的点被成为脐点(umbilic point)。有很多有趣的理论和关系涉及到曲率,但是这些是基本的事实:曲面的曲率可以由主曲面完整地描述,主曲率是最大和最小的法线曲率。高斯和平均曲率是两个主曲率的简单平均,但是(像我们将要看到的)在实际情况中通常更容易得到。 基本型 由于历史原因,我们应该提到两个对象:第一基本型(first fundamental form)III和第二基本型(second fundamental form)IIIIII。我并不确定这两个形式到底基本在哪里,因为它们除了将度量g\mathsf{g}g和形状算子S\mathcal{S}S组合在一起之外什么也没有表示,他们自己是两个真正基本对象的简单函数:浸入fff和高斯映射NNN。实际上,第一基本型就仅仅是引导度量,即 I(X,Υ):=g(X,Υ)I(X, \Upsilon) \coloneqq \mathsf{g}(X, \Upsilon) I(X,Υ):=g(X,Υ) 第二基本型看起来十分像我们已有的法线曲面的公式: II(X,Υ):=−g(SX,Υ)=−dN(X)⋅df(Υ)II(X,\Upsilon) \coloneqq -\mathsf{g}(\mathcal{S}X, \Upsilon) = -dN(X) \cdot df(\Upsilon) II(X,Υ):=−g(SX,Υ)=−dN(X)⋅df(Υ) 最重要的事情是意识到III和IIIIII并没有引入任何新的几何想法——仅仅对于我们已经看到的东西是换了一种写法。 坐标系中的几何 目前我们已经对于我们一直操作的几何对象给出了相当抽象的描述。比如,我们说过一个浸入f:M→R3f:M \rightarrow \mathbb{R}^3f:M→R3的微分dfdfdf告诉我们当从域M⊂R2M \subset \mathbb{R}^2M⊂R2到图像f(M)⊂R3f(M) \subset \mathbb{R^3}f(M)⊂R3时如何拉伸正切向量。顺便提一下,对于上面的图像,我们可以更精确一点并且用极限项定义df(X)df(X)df(X): dfp(X)=limh→0f(p+hX)−f(p)hdf_p(X) = \lim_{h \rightarrow 0} \frac{f(p + hX) - f(p)}{h} dfp(X)=h→0limhf(p+hX)−f(p) 这个公式仍然有一点抽象——我们可能想要一些更明确东西去在实际中使用。当我们开始在离散曲面上操作时,我们将看到df(X)df(X)df(X)通常有极其明确的意义——比如,它可能与我们三角网格的一条边对应。但是在平滑设定中dfdfdf的一个更典型的表达是雅可比矩阵 J=[∂f1/∂x1∂f1/∂x2∂f2/∂x1∂f2/∂x2∂f3/∂x1∂f3/∂x2]\mathsf{J} = \begin{bmatrix} \partial f^1/ \partial x^1 & \partial f^1/ \partial x^2 \\ \partial f^2/ \partial x^1 & \partial f^2/ \partial x^2 \\ \partial f^3/ \partial x^1 & \partial f^3/ \partial x^2 \end{bmatrix} J=⎣⎡∂f1/∂x1∂f2/∂x1∂f3/∂x1∂f1/∂x2∂f2/∂x2∂f3/∂x2⎦⎤ 这里我们选取R2\mathbb{R}^2R2和R3\mathbb{R}^3R3上的坐标系,然后想象对于一些标量函数f1,f2,f3:M→Rf_1,f_2,f_3:M \rightarrow \mathbb{R}f1,f2,f3:M→R的元组有 f(x1,x2)=(f1(x1,x2),f2(x1,x2),f3(x1,x2))f(x^1, x^2) = (f_1(x^1,x^2), f_2(x^1,x^2), f_3(x^1,x^2)) f(x1,x2)=(f1(x1,x2),f2(x1,x2),f3(x1,x2)) 所以如果你想要计算df(X)df(X)df(X),你可以简单的将JJJ作用到一些向量X=[X1,X2]TX = [X^1, X^2]^TX=[X1,X2]T上。 被认为有害的坐标系表示 你可能早就得到了上面方法的一个反馈:写出来的表达式变得更长更复杂了。但是有其它的好理由去拒绝明确的矩阵表示。最强烈的理由是矩阵可以被用来表示非常多不同种类的目标,并且这些对象可以用非常不同的方式表示。比如,你能猜猜下面这个矩阵表示什么吗? [0110]\begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} [0110] 放弃了?它的确十分明确:它是一个两个顶点上完全图的邻接矩阵。不,等一下——它应该是一个Puali矩阵σx\sigma_xσx,表示沿着xxx轴的旋转角动量。或者这个矩阵表示二面体群D4D_4D4的一个元素?你获得了这样一个想法:当使用矩阵的时候,很容易忘记它们到底是从哪里来的——这让它非常容易遗忘它们应该遵守的规则!(难道你不是早就有了足够多不想丢掉的东西吗?)这里有一个真实的哲学观点是矩阵不是对象:它们只不过是对象的表示!或者改变一些柏拉图说的话:矩阵仅仅是洞穴中墙壁上的阴影,它除了我们想要去照亮的真实对象的黑暗印象以外什么也不能提供。 一个更具体的例子经常在几何学中出现,是线性算子和双线性型的区别。注意一下,线性算子是从一个向量空间到另一个向量空间的映射,即 f:R2→R2;u↦f(u)f:\mathbb{R}^2 \rightarrow \mathbb{R}^2;u \mapsto f(u) f:R2→R2;u↦f(u) 而双线性型是从一对向量到一个标量的映射,即 g:R2×R2→R;(u,v)↦g(u,v)\mathsf{g} : \mathbb{R}^2 \times \mathbb{R}^2 \rightarrow \mathbb{R};(u,v) \mapsto \mathsf{g}(u,v) g:R2×R2→R;(u,v)↦g(u,v) 放上这两个例子让我们想象我们正在一个坐标系统(x1,x2)(x^1,x^2)(x1,x2)上操作,其中fff和g\mathsf{g}g由矩阵A,B∈R2×2\mathsf{A},\mathsf{B}\in \mathbb{R}^{2 \times 2}A,B∈R2×2表示,并且它们的参数由向量u,v∈R2\mathsf{u},\mathsf{v} \in \mathbb{R}^2u,v∈R2表示。换句话说,我们有 f(u)=Auf(u) = \mathsf{Au} f(u)=Au 和 g(u,v)=uTBv\mathsf{g}(u,v) = \mathsf{u^TBv} g(u,v)=uTBv 现在假设我们需要在不同的坐标系(x~1,x~2)(\tilde{x}^1,\tilde{x}^2)(x~1,x~2)中工作,同过基变换P∈R2×2\mathsf{P} \in \mathbb{R}^{2 \times 2}P∈R2×2与第一个联系起来。比如,向量uuu和vvv通过下式进行变换 u~=Puv~=Pv\begin{aligned} \mathsf{\tilde{u}} &= \mathsf{Pu} \\ \mathsf{\tilde{v}} &= \mathsf{Pv} \end{aligned} u~v~=Pu=Pv 我们如何在新的坐标系统中表示映射fff和g\mathsf{g}g?比如我们不能简单的计算Au~\mathsf{A\tilde{u}}Au~,由于A\mathsf{A}A和u~\mathsf{\tilde{u}}u~在不同的坐标系统中表示。我们需要去做的是计算 f(u)=PAu=PAP−1u~f(u) = \mathsf{PAu} = \mathsf{PAP}^{-1}\mathsf{\tilde{u}} f(u)=PAu=PAP−1u~ 和类似的 g(u,v)=uTBv=(P−1u~)TB(P−1v~)=u~T(P−TBP−T)v~\mathsf{g}(u,v) = \mathsf{u^TBv} = \mathsf{(P^{-1}\tilde{u})^T B (P^{-1}\tilde{v})} = \mathsf{\tilde{u}^T (P^{-T}BP^{-T}) \tilde{v}} g(u,v)=uTBv=(P−1u~)TB(P−1v~)=u~T(P−TBP−T)v~ 换句话说,线性算子变成了 A↦PAP−1\mathsf{A \mapsto PAP^{-1}} A↦PAP−1 而双线性型变为 B↦P−TBP−T\mathsf{B \mapsto P^{-T} B P^{-T}} B↦P−TBP−T 所以我们发现了并不是所有的矩阵以同样的方式变换!但是如果我们不断地涂画一些数字的格子,将会非常容易丢失那些应该被作用在对象上的变换。 曲面几何中的标准矩阵 关于刚刚坐标系的告诫,它非常有用地让我们意识到对于几何对象的标准矩阵表示,因为它们给传统结果提供了一个基本联系。我们早就已经看过了对于一个对象的矩阵表示:微分dfdfdf可以被编码称包含浸入fff一阶导数的雅可比矩阵J\mathsf{J}J。关于其它的我们在曲面学习中遇到的对象呢?引导度量g\mathsf{g}g被相当容易的指出,因为它仅仅是一个微分的函数——记住 g(u,v)=df(u)⋅df(v)\mathsf{g}(u,v) = df(u) \cdot df(v) g(u,v)=df(u)⋅df(v) 等价地,如果我们使用一个矩阵I∈R2×2I \in \mathbb{R}^{2 \times 2}I∈R2×2来表示g\mathsf{g}g,那么我们有 uTIv=(Ju)T(Jv)\mathsf{u^T} I \mathsf{v = (Ju)^T (Jv)} uTIv=(Ju)T(Jv) 这意味着 I=JTJI = \mathsf{J^TJ} I=JTJ 我们使用字母“III”表示引导度量的矩阵,它在历史上被用来表示第一基本型——今天更少的作者使用这个术语了。在微分几何更古老的书里面你可能也能看到人们讨论“E\mathsf{E}E”,“F\mathsf{F}F”和“G\mathsf{G}G”,它们分别表示III中特定的元素: I=[EFFG]I = \begin{bmatrix} \mathsf{E} & \mathsf{F} \\ \mathsf{F} & \mathsf{G} \end{bmatrix} I=[EFFG] (能明确为什么“F\mathsf{F}F”出现两次吗?)你可能猜想字母表中第五个、第六个和第七个字母已经完全过时了,因为它们是如此坐标有关的,因此它们自己几乎不带有几何意义。不过,能够意识到这些生物是有用的,因为它们确实在野外出现。 早些时候,我们有提到形状算子,被定义为单射S:TM→TM\mathcal{S}:TM \rightarrow TMS:TM→TM,其满足 dN(X)=df(SX)dN(X) = df(SX) dN(X)=df(SX) 并且第二基本型被定义为 II(u,v)=g(Su,v)II(u,v) = \mathsf{g}(\mathcal{S}u,v) II(u,v)=g(Su,v) (记住相对于g\mathsf{g}g来说S\mathcal{S}S是自共轭的,并且同样地,IIIIII相对于它的参数uuu和vvv来说是对称的。)如果我们让S,II∈R2×2\mathsf{S,II}\in \mathbb{R}^{2\times 2}S,II∈R2×2分别成为S\mathcal{S}S和IIIIII的矩阵表示,那么对于所有的向量u,v∈R3\mathsf{u,v} \in \mathbb{R}^3u,v∈R3我们有 uTIIv=uTISv\mathsf{u^T II v = u^T I S v} uTIIv=uTISv 或者等价地 II=IS\mathsf{II = IS} II=IS II\mathsf{II}II中的元素传统上会和罗马字母表中的小写字母联系起来,记为 II=[effg]\mathsf{II} = \begin{bmatrix} \mathsf{e} & \mathsf{f} \\ \mathsf{f} & \mathsf{g} \end{bmatrix} II=[effg] 在坐标系(x,y)(x,y)(x,y)中将具体表示为(这个内容的推导这里应该是超出了这个文档的范围,如果有谁可以通过这个文档中的内容推导出来的话请告诉我) e=N⋅fxxf=N⋅fxyg=N⋅fyy\begin{aligned} \mathsf{e} &= N \cdot f_{xx} \\ \mathsf{f} &= N \cdot f_{xy} \\ \mathsf{g} &= N \cdot f_{yy} \end{aligned} efg=N⋅fxx=N⋅fxy=N⋅fyy 其中NNN是单位曲面法线,并且fxyf_{xy}fxy表示沿着xxx和yyy方向的二阶偏导。 此时我们可能想停下来并且询问:像IS\mathsf{IS}IS这样的矩阵表示的变换和基变换比较怎么样呢?第一项,I\mathsf{I}I,是一个双线性型,但是第二项S\mathsf{S}S是一个线性映射!就像上面强调的一样,我们不能仅仅通过关注矩阵本身确定答案——我们需要意识到它们到底表示什么。在这个情况下,我们知道IS\mathsf{IS}IS对应第二基本型,所以它应该像其它的双线性型一样变换:IS↦P−1ISP−1\mathsf{IS} \mapsto \mathsf{P^{-1}} IS \mathsf{P^{-1}}IS↦P−1ISP−1 最后,我们可以验证使用矩阵的古典几何表示和我们之前使用微分的表示对应。比如,法线曲率的古典表示为 κn(u)=II(u,u)I(u,u)\kappa_n(u) = \frac{II(u,u)}{I(u,u)} κn(u)=I(u,u)II(u,u) 我们可以将它重写为 uTIIuuTIu=uTISuuTIu=(Ju)T(JSu)(Ju)T(Ju)=df(u)⋅dN(u)∣df(u)∣2\frac{\mathsf{u^T II u}}{\mathsf{u^T I u}} = \frac{\mathsf{u^T IS u}}{\mathsf{u^T Iu}} = \frac{\mathsf{(Ju)^T(JSu)}}{\mathsf{(Ju)^T(Ju)}} = \frac{df(u) \cdot dN(u)}{|df(u)|^2} uTIuuTIIu=uTIuuTISu=(Ju)T(Ju)(Ju)T(JSu)=∣df(u)∣2df(u)⋅dN(u) 直到符号的选择,这个表达式和我们之前通过考虑嵌入在曲面中的曲线得到的表达式一样。]]></content>
<tags>
<tag>math</tag>
<tag>DDG</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DDG(离散微分几何):组合曲面]]></title>
<url>%2Farchives%2F12197.html</url>
<content type="text"><![CDATA[翻译自 DISCRETE DIFFERENTIAL GEOMETRY: AN APPLIED INTRODUCTION 第二章 Combinatorial Surfaces。如果有翻译错误或者不当的地方希望能指出,谢谢~ “Everything should be made as simple as possible, but no simpler.” – Albert Einstein 粗略来讲,曲面(surface)就是一个形状的”外壳/皮“;比如,你可以将一个橙子看作是一个固态球,它的皮可以看作是球状的曲面(尤其当我们将皮考虑为没有厚度的情况)。在我们的日常生活中遇到的各种不同的东西都拥有不同曲面所表示的边界。比如,覆盖甜甜圈的糖浆更像是一个圆环(torus)而不是球。(希望对橙子和甜甜圈讨论能让你对一些几何知识感到“饥饿”。。。HAHAHAHAHA)作为将要真正开始了解曲面微分几何的前奏,我们将由一些很容易从纯粹离散观点理解的对象开始,即组合曲面(combinatorial surfaces),或者说形状的描述仅仅告诉你曲面是如何连接的(connected up)而不告诉你它们在空间的哪个位置(where they are in space)。在离散微分几何中,组合曲面有效扮演了拓扑曲面(topological surfaces)在平滑设置中同样的角色。在这份笔记中我们不会深入到拓扑学,但是在“没有几何(sans geometry)的情况下”使用离散曲面的时候应该能对于拓扑学是讨论什么的给你足够好的感觉。具体而言,我们将讨论几种方法来编码组合曲面的连接:抽象单纯复形(abstract simplicial complex),邻接矩阵(adjacency matrices)和半边网格(halfedge mesh),所有内容都和我们之后要使用的更加丰富的几何对象和算法联系在一起。 在爱因斯坦的提示下,我们将对形状长什么样做一些简单假设,同时仍然保留足够的灵活性去描述在自然世界中能发现的各种对象。这种简化不止能让建立明确几何现象(比如曲率)的描述和定义更容易,同时最终会帮助我们构建不用过多考虑特殊情况和陷阱的高效明确的算法。微分几何的基本简化假设是我们想要去学习的被称为流形(manifold)的形状。 不精确的讲一下这个意思,至少在显微镜下,它们看起来和普通的欧式空间一样。比如,当你站在(球形)的地球表面上,很难告诉你并不是站在一个平坦的平面上。流形的假设是非常强有力的,因为它让我们将许多我们已知在平坦欧式空间中的操作(比如使用向量、微分和积分等)转移到更有趣的曲线空间。实际上有很多种使形状“看起来像欧式空间”的方法,致使微分几何有很多不同的分支(微分拓扑、共形几何、黎曼几何……)。现在,我们想聚焦在曲面完全基础的性质上,即在任意一个点周围你都可以找到一个小领域被称为拓扑盘(topological disk)。 所谓拓扑盘,粗略来说,是指在不撕破、不穿孔且不粘合边的前提下通过形变一个平面中的单位盘可以得到的任一形状。包括旗帜、叶子和手套(半手指手套可能有点问题,因为穿孔了哈哈哈哈哈哈)都是盘形状的样例。一些不属于盘的形状包括圆圈(盘不包括其内部)、实心球、空心球、甜甜圈、指尖陀螺(fidget spinner)和茶壶。比如,对于上图我们有拓扑盘(左)、拓扑环(中)和将有限个多边形沿着它们的边粘合在一起的拓扑(球)结构。 在这一章中我们由定义一个抽象单纯复形开始,它将一个形状分解成简单的构件,比如边、三角形和四面体。任何一个抽象单纯复形都可以由一个关联矩阵(incidence matrices)编码,关联矩阵就是一个大的列表记录了哪些元素和哪些元素关联。虽然这种描述可以刻画一些相当复杂的形状,但是它通常比我们在离散微分几何中真正所需的更具有一般性(more general)。因此我们介绍一下半边网格(halfedge mesh),它是为二维曲面量身定制的,并且可以容易地使用一般的多边形(不仅仅是三角形)描述曲面。半边网格将作为这份笔记中绝大多数算法的基本数据结构。在这一章的结尾,我们将做一些测试,这些测试展现了组合曲面一些有意思的和有用的性质,并且可以通过写一些与组合曲面交互的代码对这些表示如何组合在一起获得一些直观感受。(这份翻译暂时没有翻译习题的计划) 抽象单纯复形 我们如何使用有限的信息编码曲面,使得将球与环区分开来是可能的?现在,让我们忘记形状或者几何(形状大小、厚度等),并且完全聚焦在连通性上:曲面的哪些部分和其它的相连,怎么连接的? 图1. 一个抽象单纯复形具体描述了顶点是怎么连接的,但是没有说它们在空间的哪个位置。比如,上面的两幅图都表示同一个单纯复形,由6个顶点、10条边、5个三角形和1个四面体。 有很多不同的方式描述一个离散曲面的连通性;一个方法是使用单纯复形,它实际上能编码比曲面复杂得多的对象。基本想法开始于顶点集合VVV,我们使用一系列整数来区分这些顶点: V={0,1,2,…,n}V = \{0, 1, 2, \dots, n\} V={0,1,2,…,n} 我们同时也需要一些关于这些顶点如何连接的信息。单纯复形的想法是具体说明这些顶点中“右边邻接其它部分”的子集,被称为k-单纯形(k-simplices)。这个数字k∈Z>=0k \in \mathbb{Z}_{>=0}k∈Z>=0是一个非负整数,告诉我们这个集合内有多少元素:一个抽象的k-单纯形(k-simplex)是(k+1)(k+1)(k+1)个顶点的集合,我们将kkk称为这个单纯形的度。比如,这里有一个三角形或者2-单纯形: {3,4,2}\{3,4,2\} {3,4,2} 这里还有一个1-单纯形: {3,5}\{3,5\} {3,5} 从几何原理上来讲,我们可以将2-单纯形具体说明为三角形,1-单纯形为一条边,如图1左边所示;0-单纯形仅仅包含一个顶点。现在我们并不将顶点与具体的坐标联系起来,比如对于图1,右侧是另一个对这个单纯形的完美描写。因为这个原因所以我们将这些单纯形称作抽象的,它们不将一些具体的形状固定在空间中,而是仅仅(抽象地)告诉我们顶点是如何连接起来的。出于这个原因,我们可以在不必考虑这个东西在空间中看起来怎么样(3-单纯形、4-单纯形、5-单纯形……)的条件下走得尽可能高。同样地,我们(现在)不在乎这些顶点的顺序具体是什么样的:比如{2,3,4}\{2,3,4\}{2,3,4}和{3,2,4}\{3,2,4\}{3,2,4}表示同一个2-单纯形{3,4,2}\{3,4,2\}{3,4,2}。为了方便起见,我们将会经常使用对应的0-单纯形{i}∈K\{i\} \in \mathcal{K}{i}∈K确定任意一个顶点i∈Vi \in Vi∈V。 一个单纯形的任一(非空)子集都是另一个单纯形,我们称之为面(face);一个严格子集被称为一个特有的(proper)面。比如,{2,3}\{2,3\}{2,3}是{3,4,2}\{3,4,2\}{3,4,2}的一个特有面,{2,3,4}\{2,3,4\}{2,3,4}是{3,4,2}\{3,4,2\}{3,4,2}的一个面但不是特有的一个。粗略来说,一个抽象单纯复形就是一个抽象单纯形的集合。然而,我们将在这个集合上放一个非常基本的条件,以此保证我们可以用一个自然的方式使用它,并且最终会帮助我们和平滑曲面联系起来。特别的,我们将会说单纯形的集合K\mathcal{K}K是一个单纯复形,在所有单纯形σ∈K\sigma \in \mathcal{K}σ∈K且每一个面σ′⊆σ\sigma' \subseteq \sigmaσ′⊆σ也属于K\mathcal{K}K的条件下。比如,一系列三角形不能成为一个单纯复形;你必须同时包含它们的边和顶点。我们将通常假设一个单纯复形是有限的(比如,包含有限多个单纯形),虽然原则上你没有理由不能考虑一个无限的复形,比如整个欧式平面的三角网。 一个单纯复形K\mathcal{K}K的子复形(subcomplex)K′\mathcal{K}'K′是一个同样是单纯复形的子集。比如,一条单独的边并不是任一复形的子复形,但是带两个顶点的边是一个子复形。一个复形K\mathcal{K}K是一个纯k-单纯复形(pure k-simplicial complex),当每一个单纯形σ′∈K\sigma'\in \mathcal{K}σ′∈K都被包含在一些度为kkk的单纯形(可能是它自己)中。比如,一系列有顶点和边挂在上面或者独立漂浮在周围的三角形是非纯的: 最后,我们使用一个非常简单的(抽象的)对象结束:一个抽象单纯复形是整数的一个子集,在取子集的操作下是闭合的。这个诱导性的简单对象使得恰好编码任意曲面的拓扑都是可能的,不用担心多么复杂。为了做离散微分几何,我们最终将需要将一些类型的形状与一个单纯复形联系起来。但是现在已经有一些在纯组合设定下我们可以讨论的与曲面相关的有趣的东西。 单纯复形剖析:星、闭包和链环 当使用单纯复形时,能快速和简要地查阅到许多元素和区域是有帮助的。让我们开始于只考虑一个顶点i∈Vi \in Vi∈V。这个顶点的(单纯)星(star),记为St(i)St(i)St(i),是满足i∈σi \in \sigmai∈σ的所有单纯形σ∈K\sigma \in \mathcal{K}σ∈K的集合。考虑下面的例子: 从这个图中,可以感觉到St(i)St(i)St(i)是iii的某种“局部邻域”。然而,这个邻域它自己不是一个单纯复形,因为它没有包含“外面的”边。为了获得这样的一个复形,我们可以考虑St(i)St(i)St(i)的闭包(closure)ClClCl,它是K\mathcal{K}K中包含St(i)St(i)St(i)的最小子复形: 如果我们换个方向考虑,在获得星之前获得闭包呢?换句话说,我们首先考虑K\mathcal{K}K中包含iii的最小子复形作为闭包Cl(i)Cl(i)Cl(i)。由于{i}\{i\}{i}没有特有的面,这个闭包就仅仅是这个顶点自己。如果我们继续获得这个星,我们会得到一个和上面第一次得到的同样的图,即St(Cl(i))=St(i)St(Cl(i)) = St(i)St(Cl(i))=St(i): 这两个集合仅有的区别是外壳边组成的环,这些边起初是不在我们的子复形中的。我们给这个集合一个特殊的名字:链环(link)Lk(i)=Cl(St(i))∖St(Cl(i))Lk(i) = Cl(St(i)) \setminus St(Cl(i))Lk(i)=Cl(St(i))∖St(Cl(i)) (其中A∖BA \setminus BA∖B表示集合差,即所以在A中且不在B中的元素): 更一般的说,对于单纯复形K\mathcal{K}K中的任一子集SSS(不必是子复形),我们有如下定义: 星St(S)St(S)St(S)是K\mathcal{K}K中包含SSS中任一单纯形的所有单纯形集合 闭包Cl(S)Cl(S)Cl(S)是K\mathcal{K}K中包含SSS的最小(即元素最少)子复形 环Lk(S)Lk(S)Lk(S)等于Cl(St(S))∖St(Cl(S))Cl(St(S)) \setminus St(Cl(S))Cl(St(S))∖St(Cl(S)) 另一个紧密相关的对象是纯k-子复形K′⊆K\mathcal{K}' \subseteq \mathcal{K}K′⊆K的边界(boundary)bd(K′)\text{bd}(\mathcal{K}')bd(K′)。边界是K′\mathcal{K}'K′中只属于一个单纯形特有面的所有单纯形所组成集合的闭包。这个定义自然地捕获到了你对集合“边界”可能的想法。比如: 内部(interior)int(K′)=K′∖bd(K′)\text{int} (\mathcal{K}') = \mathcal{K}' \setminus \text{bd}(\mathcal{K}')int(K′)=K′∖bd(K′)是除了边界之外的所有东西(如上图所示)。 一般来讲,这些操作(星、闭包、链环、边界和内部)提供了一个自然的方式在任意维度中讨论和导航任意一种单纯复形。实际上,它们比我们仅仅需要讨论的简单组合曲面在合理程度上更一般。简单说一下,我们将介绍一个被称为半边网格(half edge mesh)的不同方法在组合曲面上导航,它在某些方面更“圆滑“并且在我们不考虑一般情况的时候更容易操作。但是为了这么做,我们首先需要定义我们真正需要用组合曲面表示什么——为了这么做,我们需要星、闭包和链环! 有向单纯复形 目前我们的假设是单纯形中顶点的顺序是无所谓的,并且使用集合来确定单纯形。比如,{i,j,k}\{i,j,k\}{i,j,k}是有顶点i,j,ki,j,ki,j,k的三角形,并且{j,i,k}\{j,i,k\}{j,i,k}或者{k,j,i}\{k,j,i\}{k,j,i}描述了同样一个三角形。但是在很多情况下,我们想要使用不同的方向区分单纯形,因为方向编码了关于我们正在测量和计算的量的一些信息。比如:山底到山顶的海拔变化和山顶到山底的海拔变化是相反的。 为了获取方向的概念,我们以使用有序元组替换无向集合开始。比如,如果i,j∈Vi,j \in Vi,j∈V是一条边的两个顶点,然后我们有两个不同顺序的元组(i,j)(i,j)(i,j)和(j,i)(j,i)(j,i)。第一个元组描述的是从iii到jjj的有向边,然而第二个元组表示的是jjj到iii。对于度更高的单纯形(三角形、四面体等),这个故事讲起来会更复杂一点。比如,考虑三个顶点i,j,k∈Vi,j,k \in Vi,j,k∈V构成的三角形。不仅仅只有一个有向集{i,j,k}\{i,j,k\}{i,j,k},我们现在有六个可能的有序元组: (i,j,k)(i,k,j)(j,k,i)(j,i,k)(k,i,j)(k,j,i)\begin{matrix} (i,j,k) & (i,k,j) \\ (j,k,i) & (j,i,k) \\ (k,i,j) & (k,j,i) \end{matrix} (i,j,k)(j,k,i)(k,i,j)(i,k,j)(j,i,k)(k,j,i) 这些元组的每一个都特定了围绕这个三角形的方法。比如,我们可以先到iii,然后到jjj,然后到kkk。或者我们可以先到jjj,再到iii,再到kkk。如果我们考虑这六种不同的可能,我们注意的是它们分为两个明显的种类:我们“顺时针”或者“逆时针”围绕这个三角形。这两种可能描述了对于我们的三角形来讲的两种可能的方向: 由于我们没有考虑起点,单纯形的一个方向实际上是有序元组的一个等价类(equivalence class)——这种情况下,上面元组的第一列全部是等价的(顺时针),第二列也是全部等价的(逆时针)。因此,为了确定一个有向三角形,我们将给出一个典型的三重索引,而不是单拿出一个特殊的元组出来。比如,我们将说: ijk:={(i,j,k),(j,k,i),(k,i,j)}ijk \coloneqq \{(i,j,k), (j,k,i), (k,i,j)\} ijk:={(i,j,k),(j,k,i),(k,i,j)} 和 ikj:={(i,k,j),(j,i,k),(k,j,i)}ikj \coloneqq \{(i,k,j), (j,i,k), (k,j,i)\} ikj:={(i,k,j),(j,i,k),(k,j,i)} 希望这可以带来直观感受:使用圆括号写的元组描述了一个绕三角形的特殊路径;一个原始的三重索引表示具有等价方向的所有元组。从根本上来说我们总是会使用后者,因为它给了恰好足够的信息去知道我们正在讨论哪个有向单纯形:它的顶点和它的方向。 更一般地,对任意k-单纯形我们有两个可能的方向:顶点所有偶置换的集合,和顶点所以奇置换的集合(这块涉及到置换群)。比如,对于一个边我们只有ij={(i,j)}ij=\{(i,j)\}ij={(i,j)}和ji={(j,i)}ji=\{(j,i)\}ji={(j,i)}。对于一个三角形我们有上面已经给出的两个方向ijkijkijk和jikjikjik。对于一个四面体我们有ijklijklijkl和jikljikljikl。等等。仅仅只有0-单纯形是例外的,只有一种方法去写出顶点列表(即i={i}i = \{i\}i={i})。一个0-单纯形因此仅有一个可能的方向。 如果两个有向单纯形有共同的顶点,那么我们就可以谈论它们的相对方向。比如,有向三角形ijkijkijk和jiljiljil有同样的方向(它们都是“顺时针”的),然而ijkijkijk和ijlijlijl是相对方向: 同样地,边ijijij与ijkijkijk有相同的顺时针方向,然而jijiji的方向是与之相对的。一般情况下,如果两个k-单纯形σ1\sigma_1σ1和σ2\sigma_2σ2有同样的k−1k-1k−1(-单纯形)顶点,那么它们有相同的方向当它们在这些共同顶点上的限制是相对方向的。对于任一有向单纯形σ\sigmaσ,一个特有面σ′\sigma'σ′有相同的方向当σ′\sigma'σ′出现在σ\sigmaσ的一些偶置换中。一个重要的特殊情况是0-和1-单纯形:一个有向边ijijij与jjj有同样的方向,但是与iii有相对的方向;这个惯例刻画了ijijij是从iii到jjj的事实。 一个(抽象)有向单纯复形是一个抽象单纯复形,其中每一个单纯形都分配了一个方向。即,我们开始于一个普通的单纯复形,并且简单的给每一个单纯形从两个方向中选择一个。这些方向上没有任何的条件:它们可以被随意分配。虽然(当可能时)通常方便地假设有公共(k-1)-面(单纯形)的k-单纯形有同样的方向(比如,一个平面三角网中的所有三角形都有顺时针方向)。 单形曲面 就像在这部分开始提到的一样,一个一般的单纯复形比我们需要学习的普通形状(帽子、脸、心脏,香蕉)更具有一般性,它们都可以用曲面完美刻画。相反的,使用抽象单形曲面(abstract simplicial surface)通常已经足够了。一个抽象单形曲面是一个纯单纯2-复形,其中每个顶点的链环是边的一个闭环,或者等价的,其中每个顶点的星是三角形构成的组合盘。实际上每个顶点有一个“盘状”的邻域刻画了拓扑曲面的基本思想;我们因此说这样的一个复形是流形。 不像一般的单纯复形,一个单形曲面不能有像多个三角形在同一个边处相邻这样的特性,或者多个顶点的“锥”在一个顶点处相邻。我们后面将会把这些配置成为非流形(nonmanifold): 我们可以通过允许链环可以是一个简单的边的路径而不是一个闭环,使用边界扩展一点我们对单形曲面的定义: 对于任一单形曲面K\mathcal{K}K,它的边界bd(K)\text{bd}(\mathcal{K})bd(K)将永远是(0个或多个)闭环的一个集合。 一个有向的单形曲面是一个抽象单形曲面,其中我们能给每一个三角形分配一个一致的方向,即其中任意两个有公共边的三角形给定相同的方向。我们后面将假设对于面可以是一致性方向的任一单形曲面有一致性的方向。一致性方向总是可以做到的吗?第一眼看这个问题的时候,它看起来很简单:从任意一个三角形开始,随意给它分配一个方向,现在“向外生长”,给每一个遇到的三角形分配一个一致的方向。这个问题在于,在一些时候,你可能在背后回环了并且发现没有任何一个办法可以给这个新的三角形分配一个一致性的方向,但是它和之前所有的方向都一致。考虑这个组合莫比乌斯带的例子: 这种不可定向(unorientable)的曲面不会经常在自然界中出现——虽然它们值得被意识到它们是能存在的! 我们对于单形曲面的定义简单地扩展到高维:一个(组合或抽象)单形n-流形是一个纯单形n-复形,其中每个顶点的链环是一个单形(n-1)-球。一个单形n-球就是一个n-维球的(单形)三角网格 Sn:={x∈Rn+1:∣x∣=1}\mathbf{S}^n \coloneqq \{ x \in \mathbb{R}^{n+1} : |x|=1 \} Sn:={x∈Rn+1:∣x∣=1} 即n-维空间中到原点单位距离所有点的集合。比如,S2\mathbf{S}^2S2就是一个普通的单位球;S1\mathbf{S}^1S1是一个单位原;S0\mathbf{S}^0S0就只是一对点。一个单形曲面是一个单纯2-流形:每一个链环都是一个单形1-球,即边的一个闭环。一个单纯3-流形是一个四面体网格,其中每一个顶点都被球的一个三角网格围绕。等等。然而一个单纯1-流形怎么样呢?这可能意味着每个顶点的链环是一对点,因此单纯1-流形必须是闭环的集合(你认为为什么呢?)。 从此关于单形曲面我们还有大量更多的东西可以讨论,但是我们现在将着手开始这个事情。尤其是它足够让我们定义半边网格,这将是我们在单形(和更一般的,多面体)曲面导航的基本方式。 邻接矩阵 有一个很好的编码抽象单纯复形的方法是使用邻接矩阵,它将使得在单纯复形上的计算变得更加容易(通过线性代数的方式)。这些矩阵也和离散微分形式(discrete differential forms)联系非常紧密,这种形式给许多我们的几何算法提供了基础。 第一件必须要做的事情是给每一个度的单纯形都分配不同的索引。比如,我们有一个复形K\mathcal{K}K由顶点VVV、边EEE和三角形FFF构成,然后我们可能分配索引0,…,∣V∣−10,\dots,|V|-10,…,∣V∣−1给顶点,分配0,…,∣E∣−10,\dots,|E|-10,…,∣E∣−1给边,0,…,∣F∣−10,\dots,|F|-10,…,∣F∣−1给三角形。每一个顶点要分配给哪一个三角形不是很重要,只要对于每一个度的单纯形每个索引都只使用一次。比如,这有一个单纯2复形,我们标记了所有的顶点、边和面: 为了记录单纯形是怎么连接的,我们将储存一个矩阵A0A_0A0,它将说明哪条边包含了哪几个顶点,另一个矩阵A1A_1A1说明三角形包含哪些边,等等。我们将"1"放在矩阵A0A_0A0的rrr行ccc列中,表示第rrr个三角形中包含第ccc条边。等等。因此,邻接矩阵AkA_kAk的列数和k-单纯形的数量相同;行数是(k+1)-单纯形的数量。这个例子中,我们的矩阵看起来是这样: 这里有一个需要注意的重要事项——对于连接数很少的特别大的复形——大多数元素都将是0。在实际使用中,因此使用稀疏矩阵是完全必要的,即一种有效的仅仅只储存非零元素位置和值的数据结构。稀疏矩阵数据结构的设计是一个完全取决于它自身的有趣的问题,但是在概念上你可以想象就是简单的元组(r,c,x)(r,c,x)(r,c,x)链表,其中r,c∈Nr,c \in \mathbb{N}r,c∈N表示非零元素的行和列索引并且x∈Rx \in \mathbb{R}x∈R给出它的值。 如果我们有一个有向单纯复形,我们可以构建一个带符号的邻接矩阵,它在连接性上加入了相关方向。仅有的改变是每一个非零元素的符号依赖于两个相关单纯形的相对方向:当有相同方向时为+1+1+1;方向相反时为−1-1−1。比如,这里有一个关于一对共享一条边方向一致的三角形的符号邻接矩阵: 半边网格 就像我们上面讨论的那样,我们在自然界中遇到的很多形状都可以用流形或可定向曲面很好的刻画。我们的第三种且是最后一种曲面编码方法,半边网格,利用了这种曲面所具有的特殊结构的优势。从某些方面来讲,这种编码方式相对于邻接矩阵表示来讲具有更低的一般性:我们不能刻画悬挂在三角网格上的边,或者莫比乌斯带状的曲面,或者更高维度的形状(比如体积而不是曲面)。另一方面,它能让我们使用一般的多边形描述组合曲面而不是只用三角形,在一些确定的重要情况中甚至允许我们相比于使用任意单纯复形使用更少的三角形(或多边形)。(正式的,一个半边网格允许我们将一个曲面编码成2维CW复形,具体参看参考文献)。 从我们在单纯曲面中的讨论我们可以注意到一些关于任意有向单纯曲面(我们现在假设没有边界)的东西: 每一条边都被两个多边形包含 围绕一个顶点的边可以赋予一个首尾循环的顺序 事实上,当曲面是由n条边的多边形组成而不仅仅是3条边的三角形时,声明依然成立: 半边网格利用了特殊结构的优势来给曲面提供一个专门的精致的描述。基本想法是对于每一个无向边,i,ji,ji,j中间的{i,j}\{i,j\}{i,j},在我们指定的上下文中我们有两个有向边ij≠jiij \neq jiij≠ji被称为半边。我们将使用HHH表示所有半边的集合;注意对于一个没有边界的曲面∣H∣=2∣E∣|H| = 2|E|∣H∣=2∣E∣,即我们的半边数是边的两倍。我们可以使用半边是如何连接的信息去描述一个有向的单纯曲面。特别的,我们有两个关键函数:twin和next。这个twin函数是映射η:H→H\eta : H \rightarrow Hη:H→H: η(ij)=ji\eta(ij) = ji η(ij)=ji 即它仅仅获得与这个半边有相同顶点但是方向相反的另一个半边。这个next函数是映射ρ:H→H\rho : H \rightarrow Hρ:H→H: ρ(ij)=jk,∀ijk∈K\rho(ij) = jk , \forall ijk \in \mathcal{K} ρ(ij)=jk,∀ijk∈K 即从有向三角形ijkijkijk的半边得到围绕这个三角形的下一个半边。当我们拥有曲面的一些其它描述时(比如一个有向单纯复形或者一对邻接矩阵),这些映射合理地直接计算。 换个角度,我们可以只使用ρ\rhoρ和η\etaη这两个映射而不使用其它任何东西容易地得到这个多边形网格的点、边和面。比如,为了得到面我们可以开始于一些半边i1i2i_1i_2i1i2,并且使用映射ρ\rhoρ得到下一个半边i2i3=ρ(i1i2)i_2i_3 = \rho(i_1i_2)i2i3=ρ(i1i2),然后再下个半边i3i4=ρ(i2i3)i_3i_4 = \rho(i_2i_3)i3i4=ρ(i2i3),然后再下一个,直到我们最后返回到i1i2i_1i_2i1i2。换句话说,网格的面使用“下一个”映射ρ\rhoρ的轨道(orbits)来描述。那么“双胞胎”映射η\etaη的轨道能给我们带来什么呢?开始于ijijij我们得到ji=η(ij)ji = \eta(ij)ji=η(ij),然后ij=η(ji)ij = \eta(ji)ij=η(ji)。因此,η\etaη的轨道描述了边。为了得到顶点,我们要变为使用映射ρ∘η\rho \circ \etaρ∘η,即首先我们获得twin半边,然后获得next半边,然后twin,然后next,…,直到我们回到开始的地方。总结一下: 面是ρ\rhoρ的轨道 边是η\etaη的轨道 顶点是ρ∘η\rho \circ \etaρ∘η的轨道 实际上,任何一对满足一些非常基本性质的映射ρ,η\rho,\etaρ,η都能描述一个有效的组合曲面。所有我们需要的是(1)集合HHH元素个数是偶数,(2)ρ\rhoρ和η\etaη都是这个集合的排列,(3)η\etaη是没有固定点的对合(involution),即对于所有的ij∈Hij \in Hij∈H都有η(η(ij))=ij\eta(\eta(ij)) = ijη(η(ij))=ij,并且对于任一ij∈Hij \in Hij∈H都有η(ij)≠ij\eta(ij) \neq ijη(ij)≠ij。这最后一个条件可以这么通俗理解:它是说你双胞胎的双胞胎是你自己,但是你自己不是你自己的双胞胎。(如果这个条件对于真实的生物上的双胞胎不是成立的,那会是很严肃的问题!)只要ρ\rhoρ和η\etaη满足这些基本条件,我们就可以得到它们的轨迹并且找到一个组合曲面。 实际上,如果我们用特殊的方法标记我们的半边,我们甚至不用真正去担心η\etaη。特别的,假设我们分配0和1给第一对半边,2和3给第二对半边,等等。那么η\etaη就有了一个非常简单的描述:任何一个偶数hhh的twin是h+1h+1h+1,任何一个奇数hhh的twin是h−1h-1h−1。这个曲面的组合将完全由排列ρ\rhoρ定义。总的来说,每一个偶数排列都描述了一个组合曲面!这非常的怪异。但是确是真的!当下一次你遇到一个排列的时候可以思考下这个东西。 注意到甚至对于由三角形组成的曲面,半边网格可以描述这个曲面但是单纯复形不行。考虑如下的样例: 这里我们通过将三角形ijkijkijk的两条边粘合在一起得到了一个锥。如果我们使用一个圆盘补上这个锥的底部,那么我们会得到四个半边:三个半边h0,h1,h2h_0,h_1,h_2h0,h1,h2围绕这个三角形,还有一个半边h3h_3h3是围绕这个盘。这个next映射给定为ρ(h0)=h1,ρ(h1)=h2,ρ(h2)=(h0)\rho(h_0) = h_1, \rho(h_1) = h_2, \rho(h_2) = (h_0)ρ(h0)=h1,ρ(h1)=h2,ρ(h2)=(h0)和ρ(h3)=h3\rho(h_3) = h_3ρ(h3)=h3,即我们有一个绕三角形的闭环和一个绕圆盘的闭环。这个twin映射使用关系η(h0)=h1\eta(h_0) = h_1η(h0)=h1和η(h2)=h3\eta(h_2) = h_3η(h2)=h3,即两个两个三角形半边粘合在一起,和剩下的三角形半边与绕盘半边粘合在一起。没有办法使用单纯复形(有向的或其它)去描述它:一个单纯复形仅仅允许我们确定一个三角形ijkijkijk;我们无法去确定边是如何粘合在一起的。同样的,包括这个盘也完全超出了问题界限:它甚至不是一个普通的多边形,更像是一个“unigon”(这个词不会翻,给了一个查到的链接作为参考)。这里有另一个有趣的例子: 此时我们有了由两个不同的三角形组成的环面,但是只有一个顶点。(你能写出对应的映射η\etaη和ρ\rhoρ吗?)显然没有办法使用单纯复形去描述它:因为一个原因,“集合”{i,i,i}\{i,i,i\}{i,i,i}不是一个集合:它没有三个能区分的顶点。甚至就算我们允许重复(即多重集合(multi-sets)),我们也没有办法在单纯复形中区分两个不同的三角形"iiiiiiiii“和”iiiiiiiii",并且没有办法解释这些三角形是如何粘在一起的。半边网格处理这种情况就很容易,并允许我们使用更少的元素去描述有趣的空间。相对而言,使用单纯复形描述环面至少需要7个顶点,21条边和14个三角形: 这里我们使用两种方式画下了这个三角网格:左边,我们将它画到平面上并且想象它的左/右和上/下粘合在一起。足够令人感到惊奇的是,同样的三角网格可以使用在R\mathbb{R}R中的直线和平坦且不相交的三角形画出来,就像右边展示的一样——这个东西被称为Cs´asz´ar多边形。不管是哪种情况,相对于我们使用半边网格得到的环面的两个三角形的分解它都更加复杂。 在使用水平上,半边描述将给我们在这份笔记中实现的算法提供基本数据结构。通过得到“下一个”和“双胞胎”半边,你可以容易地获得你内心渴望的任何网格。虽然这里有最后一个问题:我们如何处理带边界的曲面(比如盘或者圆环)?似乎违反我们基本公理中的一个,每一条边都恰好被两个多边形包含。这个简单的答案是:仅仅将每个边界部分看作一个带有很多边的多边形。换句话说,把你带边界的曲面转换为不带边界的曲面,通过简单的“补洞”(用一些方法制造一些额外的多边形)。这样你就有了一个不带边界的曲面,你就可以像往常一样处理它了。]]></content>
<tags>
<tag>math</tag>
<tag>DDG</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Laplace 和 Poisson]]></title>
<url>%2Farchives%2F44314.html</url>
<content type="text"><![CDATA[Laplace和Poisson这两个等式分别有非常简单的形式, Δf=0\Delta f = 0 Δf=0 Δf=divg\Delta f = \text{div} \mathbf{g} Δf=divg fff为函数,g\mathbf{g}g为向量场。这是两个偏微分方程。 离散偏微分 先考虑一个离散标量函数f(x,y):N×N→[0,1]f(x,y): N \times N \rightarrow [0,1]f(x,y):N×N→[0,1],定义在一个离散有限的栅格上,其中xxx和yyy都是整数,fff值是[0,1][0,1][0,1]之间的实数。比如这个函数可以表示图像亮度。一种定义fff的离散偏导为 fx(x,y)=f(x+1,y)−f(x,y)f_x(x,y) = f(x+1, y) - f(x,y) fx(x,y)=f(x+1,y)−f(x,y) fy(x,y)=f(x,y+1)−f(x,y)f_y(x,y) = f(x, y+1) - f(x,y) fy(x,y)=f(x,y+1)−f(x,y) 假设f(x,y)f(x,y)f(x,y)是定义在平面上的连续可导函数,那么使用泰勒展开很容易可以看出这些等式接近函数的一阶导。当栅格空间趋近于0或者高阶导数趋近于0的时候,这个近似会变得更加精确。同时离散偏导定义不止这一个形式。由偏导组成的向量为梯度向量,[fx(x,y),fy(x,y)][f_x(x,y), f_y(x,y)][fx(x,y),fy(x,y)],符号为∇f(x,y)\nabla f(x,y)∇f(x,y)。 Poisson等式 泊松等式允许我们构造一个给定方向导数和一些其他约束的函数。在2D离散场景中给定一个向量场,比如两个函数u(x,y)u(x,y)u(x,y)和v(x,y)v(x,y)v(x,y),来定义图像f(x,y)f(x,y)f(x,y)的梯度。理想情况下,我们需要找到函数f(x,y)f(x,y)f(x,y)使得fx(x,y)=u(x,y)f_x(x,y)=u(x,y)fx(x,y)=u(x,y)且fy(x,y)=v(x,y)f_y(x,y)=v(x,y)fy(x,y)=v(x,y)。但是对于每一个点都有两个约束,因此该方程是超定的。更正式的,离散函数fff被定义为如下问题的解: minf∑x,y(fx(x,y)−u(x,y))2+(fy(x,y)−v(x,y))2\min_f \sum _{x,y} (f_x(x,y) - u(x,y))^2 + (f_y(x,y) - v(x,y))^2 fminx,y∑(fx(x,y)−u(x,y))2+(fy(x,y)−v(x,y))2 对于未知量差分这个cost function,并使之为0,有 (还没有搞清楚为什么这么操作) fx(x,y)−u(x,y)−fx(x−1,y)+u(x−1,y)f_x(x,y) - u(x,y) - f_x(x - 1, y) + u(x - 1, y) fx(x,y)−u(x,y)−fx(x−1,y)+u(x−1,y) fy(x,y)−v(x,y)−fy(x,y−1)+v(x,y−1)=0f_y(x,y) - v(x,y) - f_y(x, y - 1) + v(x, y - 1) = 0 fy(x,y)−v(x,y)−fy(x,y−1)+v(x,y−1)=0 f(x+1,y)−2f(x,y)+f(x−1,y)+f(x,y+1)−2f(x,y)+f(x+1,y) - 2f(x,y) + f(x-1,y) + f(x,y+1) - 2f(x,y) + f(x+1,y)−2f(x,y)+f(x−1,y)+f(x,y+1)−2f(x,y)+ f(x,y−1)−u(x,y)+u(x−1,y)−v(x,y)+v(x,y−1)=0f(x, y-1) - u(x,y) + u(x-1,y) - v(x,y) + v(x, y-1) = 0 f(x,y−1)−u(x,y)+u(x−1,y)−v(x,y)+v(x,y−1)=0 则有 fxx(x,y)+fyy(x,y)=ux(x,y)+vy(x,y)f_{xx}(x,y) + f_{yy}(x,y) = u_x(x,y) + v_y(x,y) fxx(x,y)+fyy(x,y)=ux(x,y)+vy(x,y) 因此可以看成离散版本的Poisson方程 Δf(x,y)=div[u(x,y),v(x,y)]\Delta f(x,y) = \text{div}[u(x,y), v(x,y)] Δf(x,y)=div[u(x,y),v(x,y)] 其中Δf(x,y)\Delta f(x,y)Δf(x,y)表示离散Laplace,div\text{div}div表示离散散度操作符。这个关系在Laplace等式上更轻量,对应的离散版本为 Δf(x,y)=0\Delta f (x,y) = 0 Δf(x,y)=0 这个等式在寻找一个函数f(x,y)f(x,y)f(x,y)在邻近点之间有最小的变化。类似的,Poisson方程尝试去寻找一个函数,其梯度最接近一些等式右边给定的向量场;并且Laplace等式在最小二乘下寻找一个带有最小梯度场的函数。 边界条件 对于上面的方程,显然没有唯一解。任何一个常数偏移加在f(x,y)f(x,y)f(x,y)上都是一个有效的解。为了让解唯一,我们需要增加边界条件,能约束等式的解集。有几种常用的边界条件,这里简要的描述两种: Dirichlet(或第一类)边界条件,确定了解f(x,y)f(x,y)f(x,y)在值域边界需要取的值 Neumann(或第二类)边界条件,确定了解在值域边界的法线偏导∂f∂n^=⟨∇f(x,y),n^⟩\frac{\partial f}{\partial \hat{\mathbf{n}}} = \langle \nabla f(x,y), \hat{\mathbf{n}} \rangle∂n^∂f=⟨∇f(x,y),n^⟩ 需要注意的是,在一些离散设置的假设中,这些条件是等价的。同时需要注意的是,混合这两种条件的情况也是存在的。]]></content>
<tags>
<tag>math</tag>
<tag>smooth</tag>
</tags>
</entry>
<entry>
<title><![CDATA[教练!我想学线性代数!!!]]></title>
<url>%2Farchives%2F44395.html</url>
<content type="text"><![CDATA[线性代数很多人都学过了,不过本科的线性代数这门课程基本上只教如何计算,学完之后心里简直不要有太多疑惑。行列式到底是什么?矩阵乘法为什么是这样的形式?特征值特征向量到底有什么意义?线性方程组为什么这样有解那样又没解?当然还有其他的很多问题,这篇文章不一定能解答所有的困惑,但是希望能和大家一起窥探一下线性代数到底是什么。如果有错误或者有其它更好的理解欢迎一起讨论~ 既然是数学还是从定义开始比较好吧 说到线性代数,肯定要先讲线性这个东西,什么是线性我也不能自己胡说八道,翻了一本教材找到几个定义: 线性空间 设XXX是一个非空集合,K\mathbb{K}K是数域(实数域R\mathbb{R}R或复数域C\mathbb{C}C),XXX称为数域K\mathbb{K}K上的线性空间,若∀x,y∈X\forall x,y \in X∀x,y∈X,都有唯一的一个元素z∈Xz \in Xz∈X与之对应,称为xxx与yyy的和,记作 z=x+y;z=x+y ; z=x+y; ∀x∈X,α∈K\forall x \in X,\alpha \in \mathbb{K}∀x∈X,α∈K,都有唯一的一个元素u∈Xu \in Xu∈X与之对应,称为α\alphaα与xxx的积,记作 u=αx.u = \alpha x. u=αx. 且∀x,y,z∈X,α,β∈K\forall x,y,z \in X, \alpha , \beta \in \mathbb{K}∀x,y,z∈X,α,β∈K,上述的加法与数乘运算(称为线性运算)满足下列8条运算规律: x+y=y+xx+y=y+xx+y=y+x (x+y)+z=x+(y+z)(x+y)+z = x+(y+z)(x+y)+z=x+(y+z) 在XXX中存在零元素θ\thetaθ,使得∀x∈X\forall x \in X∀x∈X, θ+x=x;\theta + x = x; θ+x=x; ∀x∈X\forall x \in X∀x∈X,存在负元素−x∈X-x \in X−x∈X,使得 x+(−x)=θ;x + (-x) = \theta; x+(−x)=θ; 1⋅x=x;1 \cdot x = x;1⋅x=x; α(βx)=(αβ)x;\alpha (\beta x) = (\alpha \beta)x;α(βx)=(αβ)x; (α+β)x=αx+βx;(\alpha + \beta)x = \alpha x + \beta x;(α+β)x=αx+βx; α(x+y)=αx+αy.\alpha (x+y) = \alpha x + \alpha y.α(x+y)=αx+αy. 线性子空间 设MMM是线性空间XXX的非空子集,若MMM对XXX上的线性运算封闭,即∀x,y∈M,α,β∈K\forall x,y \in M, \alpha , \beta \in \mathbb{K}∀x,y∈M,α,β∈K,都有 αx+βy∈M,\alpha x + \beta y \in M , αx+βy∈M, 则称MMM是XXX的线性子空间。 线性变换 从域K\mathbb{K}K上的一个线性空间到另一个线性空间的线性变换T:V→WT:V \rightarrow WT:V→W是一个映射,它与加法和标量乘法相容: T(v1+v2)=T(v1)+T(v2),T(cv1)=cT(v1)T(v_1 + v_2) = T(v_1) + T(v_2), T(c v_1) = cT(v_1) T(v1+v2)=T(v1)+T(v2),T(cv1)=cT(v1) 对∀v1,v2∈V\forall v_1, v_2 \in V∀v1,v2∈V及c∈Kc \in \mathbb{K}c∈K成立。 定义太抽象了,来点直观的吧 看完了枯燥的定义之后,我们来点形象一点的东西吧,从形如 y=kxy=kx y=kx 的函数开始。也许会有同学问为什么不是考虑形如y=kx+by=kx+by=kx+b的函数,结合上面给的一些定义看看,答案应该很简单。 那么y=kxy=kxy=kx是什么呢?将它画在坐标系中,它是一条穿过原点的直线;将它看作一个函数,它是一个线性变换;将它看作一系列满足条件的点的集合,它是一个线性(子)空间。私认为,这就是线性最简单最直观的表示了。 让我们回到线性代数中经常提到的多维变量和矩阵中来,在使用线性代数的时候经常是要用来求解形如 Ax=bAx=b Ax=b 的方程。是否和刚刚讨论的东西有点像呢?我就不把说过的一些内容再说一遍了。言归正传,接下来就对每一个点展开讨论一下吧。 矩阵乘法 可能在刚接触线性代数的时候,大家最迷糊的就是为什么矩阵乘法的计算规则是这样的,接下来就开始一番推理吧。 矩阵乘法的形式 讲回刚刚提到的,线性代数使用最多的场景应该就是求解形如 Ax=bAx = b Ax=b 的方程。那么这个方程到底是怎么来的呢?如果你线性代数学的好应该很清楚,这就是一个多元一次方程组(懒得打这个式子来太麻烦了,大家这里脑补一下)。在多元一次方程组中,每一个未知量都要写好多遍啊,好烦啊怎么办,能不能只写一遍。(接下来我要开始编了)好的,我们把未知量提取出来于是有了xT=[x1,x2,…,xn]x^T = [x_1, x_2, \dots, x_n]xT=[x1,x2,…,xn]。那系数和等式右边的量怎么办呢?好的,先搞成AAA和bbb排列起来再说吧。系数通常是要写在未知量左边的,等号两边的位置是不能变的,好了,这个等式形式就先排成这样吧 A[x1x2⋮xn]=bA \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix} = b A⎣⎢⎢⎡x1x2⋮xn⎦⎥⎥⎤=b 通过观察构造计算法则,于是大家节约了很多墨水和草稿纸,过上了幸福的生活。 矩阵乘法的几何意义 我们提起矩阵和向量,通常都会用几维几维这样的字眼。我们知道维度通常被认为是个几何概念,线性这个东西也是由过原点的直线得来,所以线性代数的讨论不能离开几何。 所以再一次让我们回到 Ax=bAx=b Ax=b 这个东西上来。此时我们需要考虑一个问题,这里需要求解的xxx到底是个什么东西?再具体的说明之前我觉得先进行一个简单的推导比较合适 [a1,a2,…,an][x1x2⋮xn]=bx1a1+x2a2+⋯+xnan=b \begin{aligned} [a_1, a_2, \dots, a_n] \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix} &= b \\ x_1 a_1 + x_2 a_2 + \dots + x_n a_n &= b \end{aligned} [a1,a2,…,an]⎣⎢⎢⎡x1x2⋮xn⎦⎥⎥⎤x1a1+x2a2+⋯+xnan=b=b 这个时候我们就需要提到一个概念了,基。回想一下我们刚学向量的时候,向量是什么?向量是一个点的坐标,也是一个带有大小的方向。可以看作是每个坐标轴上投影坐标的组合,也可以看作是带有大小的坐标轴方向的组合。那么问题来了,如果这个点的位置不变,但是坐标系的基变了呢?虽然点是同一个点,但是在不同的坐标系中坐标就不同了。如果[a1,a2,…,an][a_1, a_2, \dots, a_n][a1,a2,…,an]是新的基呢?答案也就应该很明晰了。我们要求解得到的xxx其实就是bbb在新的坐标系中的新坐标。 矩阵乘法其实就是变换坐标系的基。 行列式 为什么要写行列式,理由很简单,因为我们的教材上一开始都上说的行列式。但是很遗憾的是,教材上只有行列式怎么计算。没有哪一本教材解答过行列式是什么,行列式为什么这么计算这样的问题。所以接下来就想聊聊这个东西。 行列式到底是要算什么东西 简单点说是新基和旧基的单位体积比。 行列式为什么这么算 特征值和特征向量 特征值和特征向量与上面说到的矩阵乘法的几何意义还有行列式之间有千丝万缕的联系。 希望能填上的坑有: 矩阵列向量空间和特征向量空间的包含关系 特征向量与特征值的含义(可能要涉及到复数),基变换后方向不变的向量和大小缩放,可以考虑为在标准基下圆的垂直半径变化为椭圆得到的长短轴。 特征值与行列式的关系 (未完待续)]]></content>
<tags>
<tag>math</tag>
</tags>
</entry>
<entry>
<title><![CDATA[screw、wrench与twist的简单描述]]></title>
<url>%2Farchives%2F47288.html</url>
<content type="text"><![CDATA[一个刚体的空间移动可以描述为绕一个轴的旋转和沿着这条轴的平移,这被称为screw移动。一个screw移动使用6个参数定义,其中四个自由度定义了screw的轴,另外两个分别定义了旋转的角度和平移大小。相对而言,定义空间运动的6个参数由定义欧拉角的3个元素和定义平移的3个元素组成。 screw 一个screw是由一对3维向量构成的一个6维向量,比如是forces与torques或线与角速度。 让screw为一个有序对 S=(S,V)\mathsf{S} = (S, V) S=(S,V) 其中SSS和VVV是3维实向量,这些有序对的和与差是逐部分计算的,screw通常被称为对偶向量。关于对偶元素的计算 S⋅T=(S,V)⋅(T,W)=(S⋅T,S⋅W+V⋅T)S×T=(S,V)×(T,W)=(S×T,S×W+V×T)\begin{aligned} \mathsf{S} \cdot \mathsf{T} &= (S, V) \cdot (T, W) = (S \cdot T, S \cdot W + V \cdot T) \\ \mathsf{S} \times \mathsf{T} &= (S, V) \times (T, W) = (S \times T, S \times W + V \times T) \end{aligned} S⋅TS×T=(S,V)⋅(T,W)=(S⋅T,S⋅W+V⋅T)=(S,V)×(T,W)=(S×T,S×W+V×T) wrench 应用牛顿法则到刚体中得到的force和torque(力和力矩)向量,可以被整合为screw,被称为wrench。力有一个作用点和一个作用方向,因此可以在空间中定义一个Plucker坐标并且pitch(俯仰角)为0。另一方面力矩是与空间中任何线都不绑定都纯粹的矩(moment),并且是一个pitch无穷大的screw。这两个量的比定义了这个screw的pitch。 体中的点在进行固定screw运动时,在固定的空间中轨迹形成螺旋形。如果screw的运动pitch为0,则是纯旋转运动;如果pitch为无穷,则是纯平移运动。 screw的一个公共的例子是将力使用到刚体上的wrench。让固定坐标中的PPP表示为力FFF作用点的坐标,则wrench W=(F,P×F)\mathsf{W} = (F, P \times F)W=(F,P×F)为一个screw。作用在这个刚体上其它的力和矩都被简单的加和 R=∑i=1nWi=∑i=1n(Fi,Pi×Fi)\mathsf{R} = \sum _{i=1} ^n \mathsf{W}_i = \sum _{i=1} ^n (F_i, P_i \times F_i) R=i=1∑nWi=i=1∑n(Fi,Pi×Fi) twist twist表示刚体的速度,包括绕轴的角速度和沿轴的线速度。刚体上所有的点都有同样的沿轴速度,但是离轴的距离越远垂直于轴的速度就越大。 为了定义刚体的twist,必须通过空间运动的参数集合定义运动,D(t)=([A(t)],d[t])D(t) = ([A(t)], d[t])D(t)=([A(t)],d[t]),其中AAA是旋转矩阵,ddd是平移向量。这使得固定在一个运动体中的点ppp坐标轨迹为一个弧线P(t)P(t)P(t) P(t)=[A(t)]p+d(t)P(t) = [A(t)]p + d(t) P(t)=[A(t)]p+d(t) 点PPP的速度为 Vp(t)=[dA(t)dt]p+v(t)V_p(t) = \left[ \frac{dA(t)}{dt} \right]p + v(t) Vp(t)=[dtdA(t)]p+v(t) 其中vvv是运动坐标系原点的速度,现在将p=[A(t)T]P(t)−d(t)p = [A(t)^T]P(t) - d(t)p=[A(t)T]P(t)−d(t)带入等式中得到 Vp(t)=[Ω]P(t)+v(t)−[Ω]d(t)Vp(t)=ω×P(t)+v(t)+d(t)×ω\begin{aligned} V_p(t) &= [ \Omega ]P(t) + v(t) - [\Omega]d(t) \\ V_p(t) &= \omega \times P(t) + v(t) + d(t) \times \omega \end{aligned} Vp(t)Vp(t)=[Ω]P(t)+v(t)−[Ω]d(t)=ω×P(t)+v(t)+d(t)×ω 其中[dA(t)dt][AT]\left[ \frac{dA(t)}{dt} \right][A^T][dtdA(t)][AT]为角速度矩阵,并且ω\omegaω为角速度向量。那么screw T=(ω,v+d×ω)\mathsf{T} = (\omega, v + d \times \omega) T=(ω,v+d×ω) 为运动体的twist,向量v+d×ωv + d \times \omegav+d×ω是体中点相对于固定帧原点的速度。存在两个特殊情况: 当ddd为常数的时候,也就是v=0v = 0v=0时,这个twist是绕轴纯旋转,为L=(ω,d×ω)\mathsf{L} = (\omega, d \times \omega)L=(ω,d×ω) 当[Ω]=0[\Omega] = 0[Ω]=0时,这个体没有旋转但是沿着方向vvv滑动,此时twist为纯滑动T=(0,v)\mathsf{T} = (0, v)T=(0,v) 对于卷动关节,旋转轴穿过点qqq并且沿着方向ω\omegaω,这个关节的twist为 ξ={ωq×ω}\xi = \begin{Bmatrix} \omega \\ q \times \omega \end{Bmatrix} ξ={ωq×ω} 对于滑动关节(Prismatic joints),向量vvv定义滑动方向,这个关节的twist定义为 ξ={0v}\xi = \begin{Bmatrix} 0 \\ v \end{Bmatrix} ξ={0v} 来源参考维基百科 Screw theory,后续还有一些推导内容,尤其是将twist看作是李代数元素的那部分对于使用twist做优化更新的理解还是很有帮助的。]]></content>
<tags>
<tag>math</tag>
<tag>twist</tag>
<tag>screw</tag>
<tag>transform</tag>
</tags>
</entry>
<entry>
<title><![CDATA[通过局部/全局优化的快速纹理映射调整]]></title>
<url>%2Farchives%2F53768.html</url>
<content type="text"><![CDATA[W. Li, H. Gong, and R. Yang, “Fast Texture Mapping Adjustment via Local/Global Optimization,” IEEE Transactions on Visualization and Computer Graphics, pp. 1–1, 2018. 问题:给定一系列已经标定的图像的情况下将纹理映射到三角mesh上。 传统方法使用模型参数化的投影纹理映射,本文提出了一种图像空间纹理优化方案,目的是减少可视裂缝或者在纹理或深度边界处的错误alignment。该方案开始于边界上的有效局部纹理调整,然后使用全局矫正步骤来调整由局部运动带来的纹理畸变。 方法主要分为两步,第一步确定mesh的每个三角网格对应的图像,第二步通过两种错误align的情况给出局部约束进行优化。 重点是使用已经标定的输入图像在重建的曲面模型打上纹理。基本方法是将曲面投影到每一个视角来还原颜色。如果标定和曲面几何精度都非常高的话这样的方法可以得到很精确的结果哦,但是对于消费级传感器来说很难。 为了缓减错误align的纹理裂缝,可以混合裂缝附近的几张图像,这类方法的区别就在于如何生成混合使用的权重。混合可以移除sharp裂缝,但是在overlap的地方错误align的话会导致重影和模糊。 和混合的方法不同,将模型曲面投影到图像上并且将纹理问题公式化为图像缝合(stitching)问题。 Overview 需要解决的问题是使用一系列已经标定的图像给3D模型打上纹理。具体一点来说,给定一个有mmm个三角网格的mesh {F1,F2,…,Fm}\{ F_1, F_2, \dots, F_m \}{F1,F2,…,Fm}和nnn张输入图像{I1,I2,…,In}\{ I_1, I_2, \dots, I_n \}{I1,I2,…,In}。目标是对于每一个三角网格FiF_iFi都计算一个纹理坐标Ti={li,ti}T_i = \{ l_i, t_i \}Ti={li,ti},其中li∈{1…n}l_i \in \{ 1 \dots n \}li∈{1…n}指定源图像,ti∈R6t_i \in \mathbb{R}^6ti∈R6是被选择源图像空间中的UV坐标。通过结合输入图像的标定矩阵,TiT_iTi可以有多种选择。 该方法展示了一个最优化{T1,T2…Tm}\{ T_1, T_2 \dots T_m \}{T1,T2…Tm}的框架。这个框架有两个过程(stage)。首先选择TiT_iTi对应的源图像,这个选择基于一系列条件,比如可视顶点的个数、视角和平滑约束。第二个过程是优化纹理坐标tit_iti。 对扫描目标进行纹理映射的公共问题是纹理的错误alignment。错误有两种形式(文中图2),一种是不同输入图像的邻接三角形,另一种是在深度边界上。但是错误的alignment也没事,总是可以通过转移该三角网格的纹理坐标来修复。 预处理 直接将所有的图像给到图像标签过程计算量太大,由于每个三角网格可能有数百上千个潜在的标签可以选择。该方法使用对每个图像中可视三角网格进行计数的简单贪婪方法,如果新的可视三角网格在阈值之上就将这个图像选进源图像集。 另外一个需要考虑的因素是模型的分辨率。如果模型上的一个三角网格在图像上只占非常小的部分,比如只占几个像素,优化过程会变得特别慢而且很难收敛。对于稠密的模型,使用边界坍塌(collapse)方法简化输入模型到一个合适的密度。简化过程是可追踪且可逆的(traceable and reversible)。简化后模型中的纹理坐标可以传播到原始模型中。 图像标签 每一个三角网格都可以由输入图像集中多个视角看到,首先对每个三角网格选择最好的图像源lil_ili。这个选择问题的一个简单的解可以基于三角网格曲面法线和与之关联的每个图像视角之间的夹角决定。具体而言,将三角网格FiF_iFi的源图像写为IliI_{l_i}Ili,其中lil_ili是标签(label)。最优的标签集L={l1,l2…ln}\mathcal{L} = \{ l_1, l_2 \dots l_n\}L={l1,l2…ln}由该costfuction最小化得到: E(L)=∑i∈M(Edata(li)+λ∑j∈NFiEsmooth(li,lj))E(\mathcal{L}) = \sum_{i \in M} \left( E_{data}(l_i) + \lambda \sum_{j \in \mathcal{N}_{F_i}} E_{smooth} (l_i, l_j) \right) E(L)=i∈M∑⎝⎛Edata(li)+λj∈NFi∑Esmooth(li,lj)⎠⎞ 数据项使用了两个主要因素:FiF_iFi对应图像空间的分辨率和三角网格法线和视角方向夹角。公式化为: Edata(li)=−d(Fi,Ili)a(Fi,Ili)E_{data}(l_i) = -d(F_i, I_{l_i})a(F_i, I_{l_i}) Edata(li)=−d(Fi,Ili)a(Fi,Ili) 其中d(Fi,Ili)d(F_i, I_{l_i})d(Fi,Ili)是三角网格FiF_iFi在IliI_{l_i}Ili上的投影到其投影中心的平均距离,这是FiF_iFi在IliI_{l_i}Ili上投影尺寸的估计(这个是我自己理解的,原文的句子说的感觉有歧义)。a(Fi,Ili)a(F_i, I_{l_i})a(Fi,Ili)表示三角网格法线和视线方向的夹角。 第二项为平滑项,其中NFi\mathcal{N}_{F_i}NFi为三角网格FiF_iFi的一圈(1-ring)领域。成对的平滑代价使用Potts Model Esmooth(li,lj)=[li≠lj]E_{smooth}(l_i, l_j) = [l_i \neq l_j]Esmooth(li,lj)=[li≠lj]定义。 这个costfunction可以使用图割方法求解。具体的数据项和平滑有多种选择,可以具体参看引文[7][8][9]。 图像空间优化 在给图像打上标签之后,三角网格的纹理坐标可以通过将它的顶点投影到选择的源图像上获得。这个简单的过程可能会导致可视纹理的错误alignment/裂缝。因此需要一些调整策略,由投影的纹理坐标系开始,增加一些约束去除这些裂缝。目标是在每一个图像基上去优化纹理坐标以满足所有的约束。 过程分为两步:第一步是局部阶段,这一步中对所选择的三角网格计算约束的纹理坐标。第二步是全局阶段,这一步中调整一张图像中所有的纹理坐标和原始的投影纹理坐标尽可能满足所有的约束。 局部优化 两种错误的alignment,第一种是两个源图像之间的,为此增加的约束为图像间约束(inter-image constraint);第二种是单个图像内的,为此增加的约束为图像内约束(intra-image constraint)。 图像间约束 领域三角网格中有不同源图像的三角网格是添加图像间约束的目标。该约束被设计为最小化两个输入图像沿着公共边内容的误匹配。定义为: Eedge(Fi,Fj)=∫Fi∩FjM(ϕFili(x),ϕFjlj(x))dxE_{edge}(F_i, F_j) = \int _{F_i \cap F_j} \mathcal{M}(\phi^{l_i}_{F_i}(x), \phi^{l_j}_{F_j}(x)) dx Eedge(Fi,Fj)=∫Fi∩FjM(ϕFili(x),ϕFjlj(x))dx 其中ϕFili\phi^{l_i}_{F_i}ϕFili是FiF_iFi投影到IliI_{l_i}Ili上的操作符,M(.,.)\mathcal{M}(.,.)M(.,.)是距离测度。当FiF_iFi和FjF_jFj投影到同一张图像上的时候Eedge(Fi,Fj)E_{edge}(F_i, F_j)Eedge(Fi,Fj)应该为000。定义距离测度为颜色差值和梯度差值的和: M(xi,xj)=α∥Ili(xi)−Ilj(xj)∥+(1−α)∥Gli(xi)−Glj(xj)∥\mathcal{M}(x_i, x_j) = \alpha \lVert I_{l_i} (x_i) - I_{l_j} (x_j) \rVert + (1 - \alpha) \lVert G_{l_i}(x_i) - G_{l_j}(x_j) \rVert M(xi,xj)=α∥Ili(xi)−Ilj(xj)∥+(1−α)∥Gli(xi)−Glj(xj)∥ 其中xix_ixi和xjx_jxj分别是对应三角网格FiF_iFi和FjF_jFj公共边界上3D点在图像lil_ili和ljl_jlj上的2D纹理坐标。 为了得到可见的匹配裂缝,寻找合适的纹理坐标转移(Δxi,Δxj)(\Delta x_i, \Delta x_j)(Δxi,Δxj)最小化Eedge(Fi,Fj)E_{edge}(F_i, F_j)Eedge(Fi,Fj)。因此目标函数为: Eopt=κ∫Fi∩FjM(ϕFili(x)+Δxi,ϕFjlj(x)+Δxj)dxE_{opt} = \kappa \int_{F_i \cap F_j} \mathcal{M}(\phi^{l_i}_{F_i}(x) + \Delta x_i, \phi^{l_j}_{F_j}(x) + \Delta x_j) dx Eopt=κ∫Fi∩FjM(ϕFili(x)+Δxi,ϕFjlj(x)+Δxj)dx 其中κ\kappaκ是纹理移动指标,用来惩罚颜色空间噪声造成的不必要的移动和沿着边界同样的平移。定义为: κ=(1+∥Δxi∥2)(1+∥Δxj∥2)\kappa = (1 + \lVert \Delta x_i \rVert ^2)(1 + \lVert \Delta x_j \rVert ^2) κ=(1+∥Δxi∥2)(1+∥Δxj∥2) 在优化之后,设x~i=xi+Δxi\tilde{x}_i = x_i+ \Delta x_ix~i=xi+Δxi为原始纹理坐标xix_ixi的图像间约束,并且设ω~=ΔEopt/Eopt\tilde{\omega} = \Delta E_{opt} / E_{opt}ω~=ΔEopt/Eopt为对应约束权重。 图像内约束 图像内的错误alignment由深度边界和颜色边界不一致造成。首先确认潜在的图像内错误alignment,通过在颜色图像和将3D模型投影到图像上得到的同步深度映射上进行边界检测。对应任意一个三角网格满足(1)有至少一个边界在深度边界上 并且(2)和颜色边界很近(20个像素内),将起标记为使用图像内约束的候选。 将在深度边界上的三角网格边于图像空间中两个端点的2D纹理坐标记为p0p_0p0和p1p_1p1。沿着p0p1→\overrightarrow{p_0 p_1}p0p1的法线移动这两个端点,知道与颜色边界相交,此时记新的纹理坐标为p~0\tilde{p}_0p~0和p~1\tilde{p}_1p~1。因此p~0\tilde{p}_0p~0和p~1\tilde{p}_1p~1分别为p0p_0p0和p1p_1p1构造了图像内约束。对应的权重被设为与距离逆相关: ω~=exp(−(∥p~0−p0∥2+∥p~1−p1∥2)/2ø2)\tilde{\omega} = \exp (- (\lVert \tilde{p}_0 - p_0 \rVert ^2 + \lVert \tilde{p}_1 - p_1 \rVert ^2) / 2 \text{\o} ^2) ω~=exp(−(∥p~0−p0∥2+∥p~1−p1∥2)/2ø2) ø\text{\o}ø为权重因子。 全局优化 全局优化一次在一张图像上进行,所有的纹理坐标记为T={t1,t2,…,tn}T = \{ t_1, t_2, \dots, t_n \}T={t1,t2,…,tn}。全局阶段的costfunction为: E(T~)=λ2∥LT~−ρ∥F2+∑kCk(T~,Tk)E(\tilde{T}) = \frac{\lambda}{2} \lVert \mathbf{L} \tilde{T} - \rho \rVert ^2_F + \sum _k C_k (\tilde{T}, T_k) E(T~)=2λ∥LT~−ρ∥F2+k∑Ck(T~,Tk) 其中L\mathbf{L}L为使用余切权重的拉普拉斯矩阵,ρ=∇2(T)\rho = \nabla ^2(T)ρ=∇2(T)为原始投影纹理坐标系梯度的散度。第一项要求T~\tilde{T}T~需要和原始的TTT相近。λ\lambdaλ是自己定义的参数因子。 第二项是增加的约束。下标kkk被用来区别约束不同的类型。一般情况下,约束的形式为: Ck(T~,Tk)=ωk2∥AkSkT~−BkTk∥F2C_k(\tilde{T}, T_k) = \frac{\omega _k}{2} \lVert \mathbf{A_k S_k} \tilde{T} - \mathbf{B_k} T_k \rVert ^2_F Ck(T~,Tk)=2ωk∥AkSkT~−BkTk∥F2 TkT_kTk为辅助变量,表示在当前约束下的期望纹理坐标,上文中的x~\tilde{x}x~或者p~\tilde{p}p~。并不是所有的纹理坐标都有约束。对于没有约束的kkk,TkT_kTk一个为000。Ak\mathbf{A_k}Ak和Bk\mathbf{B_k}Bk为TkT_kTk的权重矩阵,每一次迭代都由前文中对应的ω~\tilde{\omega}ω~更新为当前计算的TkT_kTk提供可信度。Sk\mathbf{S_k}Sk为选择矩阵,是一个对角阵,当对应TkT_kTk中的纹理坐标非000时对角元素为111,其它情况为000。ωk\omega _kωk是平衡不同约束的非负权重。 一个细节是纹理坐标是以一个三角网格为基础计算的,因此一个顶点可能有几个不同的纹理坐标。为了保证一个输入图像内的纹理连续,增加了约束保证同一个顶点的纹理坐标相等。 颜色混合 图像边界之间的颜色不同还是可以看出来的,使用局部泊松混合进行全局颜色调整。 总结 文章符号有些混乱因此总结一下。在全局优化的costfunction中,kkk有两个值,分布代表两种局部约束(图像间约束和图像内约束);Bk\mathbf{B_k}Bk为其中一种约束下的权重组成的矩阵;TkT_kTk为对应约束下的局部优化后的值,如果对应约束跟这个三角网格没有关系,那么TkT_kTk中对应的值置为000,同时将Sk\mathbf{S_k}Sk中对应的对角元素置为000;没有想通Ak\mathbf{A_k}Ak和Bk\mathbf{B_k}Bk的区别在哪里。]]></content>
<tags>
<tag>textrure mapping</tag>
</tags>
</entry>
<entry>
<title><![CDATA[BundleFusion:使用在线曲面重累积的实时全局一致性3D重建方法]]></title>
<url>%2Farchives%2F12779.html</url>
<content type="text"><![CDATA[A. Dai, M. Nießner, M. Zollhöfer, S. Izadi, and C. Theobalt, “BundleFusion: Real-time Globally Consistent 3D Reconstruction using On-the-fly Surface Re-integration,” arXiv:1604.01093 [cs], Apr. 2016. 重点是位姿的估计,位姿优化后从减去原来的TSDF减去权重然后用新的位姿累积进去的方式很有意思。 现在的在线方法存在的问题: 需要时间计算在线对应,这妨碍真正的实时计算 帧到帧或者帧到model的位姿估计容易失败 只支持基于无结果点的表示,限制了scan的质量和应用 核心是一个鲁棒的位姿估计策略,最优化全局相机位姿集合的每一帧,通过使用一个有效的局部到全局分层方式考虑RGBD输入的全部历史。 需要达到的目的: 高质量的曲面模型,需要有纹理的连续曲面 任意尺度,在小尺度场景和大尺度场景都能保证全局结构和局部高精度 全局模型一致性,矫正漂移、误差和畸变,关键是在线重访问之前的区域或者回环 鲁棒的相机跟踪,跟着通常在特征点稀少的区域失败,因此需要重定位 即时(on-the-fly)模型更新,输入数据需要累积到一个3D表示中,困难是更新模型需要和最新的位姿估计一致 实时频率 主要贡献: 实时的全局位姿alignment框架,考虑了全部输入帧的历史,移除了临时跟踪方法的易失败和不精确性,通过局部到全局的分层优化策略实现任意尺度的能力 稀疏到稠密的alignment策略,保证带有回环的全局一致性结构和高精度的位姿alignment保证局部曲面细节 新的RGBD累积策略保证实时性和连续的3D模型更新在改进全局位姿估计时是可以进行的 大尺度几何和纹理的重建,重访问区域模型修复,从跟踪失败中复原,对漂移鲁棒并且连续回环 Overview 核心是有效的全局位姿优化算法,在每一帧都连续进行位姿优化并且根据新计算的位姿估计更新重建结果。 使用稀疏到稠密的全局位姿优化。先使用稀疏特征对应得到粗糙的全局alignment,稀疏的特征提供了内含的回环检测和重定位。然后通过稠密的光度和几何一致性改进。稀疏对应是使用SIFT特征在所有的输入帧之间对应,意思就是检测到的SIFT关键点与之前所有的帧匹配并小心滤除误匹配以避免错误的回环。 在分层局部到全局位姿优化的过程中,第一层中每连续的n帧结合成一个块(chunk),在第二层中所有的块都互相连接起来并且进行全局优化。和子地图类似。使用当前的时间窗口构造chunk,chunk中的帧有时间顺序,这也是该方法中唯一的时间假设,chunk之间没有时间依赖关系。 全局位姿alignment 输入是由消费级传感器获取的实时RGBD流S={fi=(Ci,Di)}i\mathbf{S}=\{ f_i = (\mathcal{C}_i, \mathcal{D}_i) \}_iS={fi=(Ci,Di)}i,Ci\mathcal{C}_iCi和Di\mathcal{D}_iDi为时空align后的颜色和深度数据。 目标是找到输入序列帧之间的3D对应,然后找到最优的刚体相机变换集{Ti}\{ \mathcal{T}_i \}{Ti}让所有的帧都尽可能align到最好。 变换Ti(p)=Rip+ti\mathcal{T}_i(\mathbf{p}) = \mathbf{R}_i\mathbf{p} + \mathbf{t}_iTi(p)=Rip+ti将第iii帧局部相机坐标映射到世界坐标系,假设第一帧定义世界坐标系。 寻找特征对应 首先使用特征检测进行两帧之间的稀疏对应,特征检测、特征匹配和对应滤除步骤。对于新的每一帧,都检测SIFT特征并且将其和之前看到的所有帧进行匹配。每两帧之间潜在的匹配将被过滤以去除明确错误的部分并得到一系列有效的对应作为全局位姿优化的的输入。 为了使outliers最少,使用几何和光度一致性过滤匹配集。 关键点对应滤波器 对于一对帧fif_ifi和fjf_jfj,检测到一些fif_ifi中点PPP和fjf_jfj中点QQQ的对应,一个关键点对应滤波器找到一系列展现稳定分布和一致性刚体变换的对应。对应被贪婪的收集起来,对于每个新加起来的对应都计算一个刚体变换Tij(p)=(Tj−1∘Ti)(p)\mathcal{T}_{ij}(\mathbf{p}) = (\mathcal{T}^{-1}_j \circ \mathcal{T}_i)(\mathbf{p})Tij(p)=(Tj−1∘Ti)(p),其使用Kabsch算法将当前对应关系PcurP_{cur}Pcur和QcurQ_{cur}Qcur集合的RMSD最小化。通过PcurP_{cur}Pcur和QcurQ_{cur}Qcur点的协方差和它们的交叉协方差检查匹配决定的变换是否会有歧义(比如都在一条直线上等),如果这些条件中任意一个很高就认为系统不稳定。如果由Tij\mathcal{T}_{ij}Tij得到的重投影误差很高或者被判断不稳定,则移除这些对应关系,直到没有这样的情况或者对应关系过少不能确定刚体变换。如果fif_ifi和fjf_jfj之间不能得到有效的变换,那么移除它们之间所有的对应关系。 曲面区域滤波器 该文章认为特征张成的曲面区域足够大,如果张成的区域物理尺寸太小会导致歧义。估计fif_ifi的关键点集PPP和fjf_jfj的关键点集QQQ张成的曲面空间,构造其带方向的bounding box,两个主轴构成面的面积如果过小这个匹配集被移除掉。 稠密验证 最后进行稠密的双边几何和光度验证步骤。对于帧fif_ifi和fjf_jfj,使用关键点对应滤波器得到的相关变换Tij\mathcal{T}_{ij}Tij align fif_ifi和fjf_jfj的坐标系。测量平均深度差异,法线差异和两个方向上的重投影误差去找到有效的像素对应。当一帧新的RGDB图像fif_ifi获取时,它被滤波和下采样的颜色与深度信息Cilow\mathcal{C}^{low}_iCilow和Dilow\mathcal{D}^{low}_iDilow为了效率被缓存。相机的空间坐标PilowP^{low}_iPilow和每个Dilow\mathcal{D}^{low}_iDilow对应的法线NilowN^{low}_iNilow也都被计算和缓存。π\piπ表示下采样图像的相机内参,从fif_ifi到fjf_jfj完整的重投影误差为: Er(fi,fj)=∑x,y∥Tij(pi,x,y)−qj,x,y∥2E_r(f_i,f_j) = \sum_{x,y} \lVert \mathcal{T}_{ij} (\mathbf{p}_{i,x,y}) - \mathbf{q}_{j,x,y} \rVert _2 Er(fi,fj)=x,y∑∥Tij(pi,x,y)−qj,x,y∥2 其中,pi,x,y=Pilow(x,y)\mathbf{p}_{i,x,y} = P^{low}_i (x,y)pi,x,y=Pilow(x,y),qj,x,y=Pjlow(π−1(Tijpi,x,y))\mathbf{q}_{j,x,y} = P^{low}_j (\pi^{-1} (\mathcal{T}_{ij} \mathbf{p}_{i,x,y}))qj,x,y=Pjlow(π−1(Tijpi,x,y))。 然而,它对遮挡误差是敏感的,因此对于很高的深度差值、法线差异或者图像一致性缺少的对应都去除掉。所以在(x,y)(x,y)(x,y)处潜在的的对应如果满足以下要求则被认为有效: ∥Tij(pi,x,y)−qj,x,y∥2τn∥Cilow(x,y)−Cjlow(x,y)∥1]]></content>
<tags>
<tag>3D Reconstruct</tag>
<tag>state estimation</tag>
<tag>RGBD</tag>
</tags>
</entry>
<entry>
<title><![CDATA[KinectFusion算法解读]]></title>
<url>%2Farchives%2F14518.html</url>
<content type="text"><![CDATA[R. A. Newcombe et al., “KinectFusion: Real-Time Dense Surface Mapping and Tracking∗,” p. 10. KinectFusion就不需要多废话介绍了吧 算法结构 曲面测量:预处理过程,由原始的深度测量生成稠密的顶点和法线金字塔 曲面重建更新:全局场景融合过程,通过跟踪新的一帧深度数据得到的位姿,将曲面测累加到场景model中,由TSDF表示 曲面预期:并不是使用帧到帧的位姿估计,是使用帧到model的位姿估计。通过将符号距离函数raycasting到估计帧中来提供一个稠密曲面预期 传感器位姿估计:通过在当前帧和期望的曲面之间进行多尺度的ICP进行 曲面测量 在时刻kkk时获得一帧原始深度图Rk\mathbf{R}_kRk,在图像域u∈U⊂R2\mathbf{u} \in \mathscr{U} \subset \mathbb{R}^2u∈U⊂R2中的每个像素u=(u,v)⊤\mathbf{u}=(u,v)^\topu=(u,v)⊤处都提供对应的已标定深度测量Rk(u)∈R\mathbf{R}_k(\mathbf{u}) \in \mathbb{R}Rk(u)∈R。则有pk=Rk(u)K−1u˙\mathbf{p}_k = \mathbf{R}_k(\mathbf{u}) \mathbf{K}^{-1} \dot{\mathbf{u}}pk=Rk(u)K−1u˙为帧kkk的一个测量点。在原始深度图上进行双边滤波,以此得到降低噪声后的深度图Dk\mathbf{D}_kDk, Dk(u)=1Wp∑q∈UNσs(∥u−q∥2)Nσr(∥Rk(u)−Rk(q)∥2)Rk(q)\mathbf{D}_k (\mathbf{u}) = \frac{1}{W_\mathbf{p}} \sum_{\mathbf{q} \in \mathscr{U}} \mathscr{N}_{\sigma_s} (\lVert \mathbf{u} - \mathbf{q} \rVert _2) \mathscr{N}_{\sigma_r} (\lVert \mathbf{R}_k(\mathbf{u}) - \mathbf{R}_k(\mathbf{q}) \rVert_2) \mathbf{R}_k(\mathbf{q}) Dk(u)=Wp1q∈U∑Nσs(∥u−q∥2)Nσr(∥Rk(u)−Rk(q)∥2)Rk(q) 其中Nσ(t)=exp(−t2σ−2)\mathscr{N}_{\sigma}(t) = \exp(-t^2\sigma^{-2})Nσ(t)=exp(−t2σ−2),WpW_{\mathbf{p}}Wp是归一化常量。u˙\dot{\mathbf{u}}u˙表示齐次坐标,q=π(p)\mathbf{q} = \pi (\mathbf{p})q=π(p)表示透视投影p∈R3=(x,y,z)⊤,q∈R2=(x/z,y/z)⊤\mathbf{p} \in \mathbb{R}^3 = (x,y,z)^\top, \mathbf{q} \in \mathbb{R}^2 = (x/z, y/z)^\topp∈R3=(x,y,z)⊤,q∈R2=(x/z,y/z)⊤。 顶点和法线map 将滤波后的深度图反投影到传感器所在参考帧可以获得顶点map Vk\mathbf{V}_kVk,公式(1) Vk(u)=Dk(u)K−1u˙\mathbf{V}_k (\mathbf{u}) = \mathbf{D}_k(\mathbf{u}) \mathbf{K}^{-1}\dot{\mathbf{u}} Vk(u)=Dk(u)K−1u˙ 在顶点map的领域中使用叉乘得到对应的法向量,公式(2) Nk(u)=v[(Vk(u+1,v)−Vk(u,v))×(Vk(u,v+1)−Vk(u,v))]\mathbf{N}_k (\mathbf{u}) = \mathbf{v} \lbrack (\mathbf{V}_k(u+1, v)-\mathbf{V}_k(u,v)) \times (\mathbf{V}_k(u,v+1) - \mathbf{V}_k(u,v)) \rbrack Nk(u)=v[(Vk(u+1,v)−Vk(u,v))×(Vk(u,v+1)−Vk(u,v))] 其中v[x]=x/∥x∥2\mathbf{v} \lbrack \mathbf{x} \rbrack = \mathbf{x} / \lVert \mathbf{x} \rVert _2v[x]=x/∥x∥2。同时定义了顶点有效性mask:对于每一个深度测量能转换为有效顶点的像素Mk(u)↦1\mathbf{M}_k(\mathbf{u}) \mapsto 1Mk(u)↦1,反之如果深度测量值丢失的话Mk(u)↦0\mathbf{M}_k(\mathbf{u}) \mapsto 0Mk(u)↦0。 曲面金字塔 计算由顶点和法线map金字塔表示的L=3L=3L=3多尺度曲面测量。首先计算深度图金字塔Dl∈[1…L]\mathbf{D}^{l \in [1 \dots L]}Dl∈[1…L]。设底层的深度图为原始的双边滤波后深度图,下采样版本Dl+1\mathbf{D}^{l+1}Dl+1从Dl\mathbf{D}^lDl通过块平均下采样到一半分辨率。深度值使用平均值,仅当和中心像素的值在3σr3\sigma_r3σr之内时,保证平滑不会跨越深度边界。 顶点和法线map金字塔的每一层Vl∈[1…L],Nl∈[1…L]\mathbf{V}^{l \in [1 \dots L]}, \mathbf{N}^{l \in [1 \dots L]}Vl∈[1…L],Nl∈[1…L]使用该层的深度图和公式(1)(2)进行计算。将给定的相机到全局坐标系的仿射变换矩阵Tg,k\mathrm{T}_{g,k}Tg,k使用在曲面测量上,可以得到全局顶点为Vkg(u)=Tg,kV˙k(u)\mathbf{V}^g_k(\mathbf{u}) = \mathrm{T}_{g,k} \dot{\mathbf{V}}_k(\mathbf{u})Vkg(u)=Tg,kV˙k(u),映射到全局中的法线为Nkg(u)=Rg,kNk(u)\mathbf{N}^g_k(\mathbf{u}) = \mathrm{R}_{g,k} \mathbf{N}_k(\mathbf{u})Nkg(u)=Rg,kNk(u),其中 Tg,k=[Rg,ktg,k0⊤1]∈SE3\mathrm{T}_{g,k} = \begin{bmatrix} \mathrm{R}_{g,k} & \mathbf{t}_{g,k} \\ \mathbf{0}^\top & 1 \end{bmatrix} \in \mathbb{SE}_3 Tg,k=[Rg,k0⊤tg,k1]∈SE3 TSDF 每一个连续的深度帧,使用其对应的相机位姿,增量式融合到一个用TSDF表示的单一3D重建中。将融合了配准后帧1…k1 \dots k1…k深度测量值的TSDF表示为Sk(p)\mathbf{S}_k(\mathbf{p})Sk(p),其中p∈R3\mathbf{p} \in \mathbb{R}^3p∈R3是将被重建的3D volume中全局帧的点。TSDF点每个位置都要保存两个元素:当前的截断符号距离值Fk(p)\mathbf{F}_k(\mathbf{p})Fk(p)和权重Wk(p)\mathbf{W}_k(\mathbf{p})Wk(p), Sk(p)↦[Fk(p),Wk(p)]\mathbf{S}_k(\mathbf{p}) \mapsto \lbrack \mathbf{F}_k(\mathbf{p}) , \mathbf{W}_k(\mathbf{p}) \rbrack Sk(p)↦[Fk(p),Wk(p)] 截断 稠密的曲面测量在曲面重建中提供了两个重要约束。首先,假设可以截断深度测量的不确定性,比如真值在测量值的±μ\pm \mu±μ之间。那么对于从相机中心沿着深度图射线的距离rrr来说,r(λRk(u)+μ)的范围内无法获取曲面信息。因此仅仅需要表达曲面测量在∣r−λRk(u)∣≤μ| r - \lambda \mathbf{R}_k (\mathbf{u}) | \leq \mu∣r−λRk(u)∣≤μ范围内的不确定性的区域。可见空间的点到曲面上最近点的距离大于μ\muμ时被截断为最大距离μ\muμ,不可见的点距离超过μ\muμ时不被测量。符号距离函数表达了到曲面最近点的距离。 投影截断符号距离函数 对于一个已知位姿Tg,k\mathrm{T}_{g,k}Tg,k的原始深度图Rk\mathbf{R}_kRk,在全局帧中点p\mathbf{p}p处的投影TSDF[FRk,WRk][\mathbf{F}_{\mathbf{R}_k}, \mathbf{W}_{\mathbf{R}_k}][FRk,WRk]为 FRk(p)=Ψ(λ−1∥tg,k−p∥2−Rk(x))λ=∥K−1x˙∥2x=⌊π(KTg,k−1p)⌋Ψ(η)={min(1,ημ)sgn(η)iff η≥−μnullotherwise\begin{aligned} \mathbf{F}_{\mathbf{R}_k}(\mathbf{p}) &= \Psi \left( \lambda^{-1} \lVert \mathbf{t}_{g,k} - \mathbf{p} \rVert_2 - \mathbf{R}_k(\mathbf{x}) \right) \\ \lambda &= \lVert \mathbf{K}^{-1} \dot{\mathbf{x}} \rVert_2 \\ \mathbf{x} &= \lfloor \pi \left( \mathbf{K} \mathrm{T}_{g,k}^{-1} \mathbf{p} \right) \rfloor \\ \Psi (\eta) &= \begin{cases} \min (1, \frac{\eta}{\mu}) \text{sgn}(\eta) &\text{iff } \eta \geq -\mu \\ null & otherwise \end{cases} \end{aligned} FRk(p)λxΨ(η)=Ψ(λ−1∥tg,k−p∥2−Rk(x))=∥K−1x˙∥2=⌊π(KTg,k−1p)⌋={min(1,μη)sgn(η)nulliff η≥−μotherwise ⌊⋅⌋\lfloor \cdot \rfloor⌊⋅⌋为最近邻查找,而不使用深度值内插。λ\lambdaλ为到归一化平面上的距离。Ψ\PsiΨ进行截断。tg,k\mathbf{t}_{g,k}tg,k为当前相机坐标,∥tg,k−p∥2\lVert \mathbf{t}_{g,k}-\mathbf{p} \rVert_2∥tg,k−p∥2为点在当前坐标系中的坐标。相关推导可以结合公式(1)进行。 相关权重WRk(p)\mathbf{W}_{\mathbf{R}_k} (\mathbf{p})WRk(p)与cos(θ)/Rk(x)\cos (\theta) / \mathbf{R}_k (\mathbf{x})cos(θ)/Rk(x)成比例,θ\thetaθ是局部帧中像素射线方向和曲面法线方向的夹角。 投影TSDF仅仅在FRk(p)=0\mathbf{F}_{\mathbf{R}_k}(\mathbf{p}) = 0FRk(p)=0或者仅仅存在孤立的点(之前在此处没有测量值)时恰好有效。 融合 所有深度图的全局融合是通过将每个深度图分别计算得到的所有独立TSDF进行加权平均,可以被看作由多个噪声的TSDF测量进行去噪得到全局的TSDF。在L2\mathscr{L}_2L2范数下的去噪(融合)曲面结果为作为000交界的逐点符号距离函数F最小化: minF∈F∑k∥WRkFRk−F∥2\min_{\mathrm{F} \in \mathscr{F}} \sum_k \lVert \mathbf{W}_{\mathbf{R}_k} \mathbf{F}_{\mathbf{R}_k} - \mathrm{F} \rVert _2 F∈Fmink∑∥WRkFRk−F∥2 这个解可以由更多数据项使用简单的加权平均增量获得,在每个点{p∣FRk(p)≠null}\{ \mathbf{p} | \mathbf{F}_{\mathbf{R}_k}(\mathbf{p}) \neq null \}{p∣FRk(p)≠null}处定义: Fk(p)=Wk−1(p)Fk−1(p)+WRk(p)FRk(p)Wk−1(p)+WRk(p)Wk(p)=Wk−1(p)+WRk(p)\begin{aligned} \mathbf{F}_k(\mathbf{p}) &= \frac{\mathbf{W}_{k-1}(\mathbf{p}) \mathbf{F}_{k-1}(\mathbf{p}) + \mathbf{W}_{\mathbf{R}_k}(\mathbf{p}) \mathbf{F}_{\mathbf{R}_k}(\mathbf{p})}{\mathbf{W}_{k-1}(\mathbf{p}) + \mathbf{W}_{\mathbf{R}_k}(\mathbf{p})} \\ \mathbf{W}_{k}(\mathbf{p}) &= \mathbf{W}_{k-1}(\mathbf{p}) + \mathbf{W}_{\mathbf{R}_k}(\mathbf{p}) \end{aligned} Fk(p)Wk(p)=Wk−1(p)+WRk(p)Wk−1(p)Fk−1(p)+WRk(p)FRk(p)=Wk−1(p)+WRk(p) 实践中发现,权重WRk(p)=1\mathbf{W}_{\mathbf{R}_k}(\mathbf{p}) = 1WRk(p)=1时也能提供很好的结果。并且,将更新后超过某个值Wη\mathbf{W}_\etaWη的权重截断 Wk(p)←min(Wk−1(p)+WRk(p),Wη)\mathbf{W}_{k}(\mathbf{p}) \leftarrow \min (\mathbf{W}_{k-1}(\mathbf{p}) + \mathbf{W}_{\mathbf{R}_k}(\mathbf{p}) , \mathbf{W}_\eta) Wk(p)←min(Wk−1(p)+WRk(p),Wη) 在有动态目标运动的场景中,可以获得运动的平均曲面重建结果。 原始的深度测量值被用来进行TSDF融合,双边滤波的版本被用在跟踪中。 滤波后移除了高频部分,类似噪声的部分丢失会导致重建更好结果的能力下降,因为细节很多都在高频的部分。 对TSDF进行raycasting得到期望曲面 计算一个稠密点期望曲面,通过将零水平集Fk=0\mathbf{F}_k = 0Fk=0渲染到当前位姿Tg,k\mathrm{T}_{g,k}Tg,k的虚拟相机中。期望曲面储存为(这是重要的,估计位姿的数据关联就是将这些顶点投影到下一帧中)参考帧kkk中顶点和法线map V^k\hat{\mathbf{V}}_kV^k和N^k\hat{\mathbf{N}}_kN^k,并用于相机位姿估计。 当有了全局SDF形式表示的稠密曲面重建之后,便可以对每个像素进行raycast。从像素最小的深度开始,沿着像素对应的射线Tg,kK−1u˙\mathrm{T}_{g,k}\mathbf{K}^{-1}\dot{\mathbf{u}}Tg,kK−1u˙,直到零交界处(可视曲面的+ve+ve+ve到−ve-ve−ve范围)。当到达−ve-ve−ve到+ve+ve+ve范围的背面或者存在正在工作的volume时同样也停止,这两种情况将会导致像素u\mathbf{u}u处没有曲面测量值。 在曲面界面Fk(p)=0\mathbf{F}_k(\mathbf{p}) = 0Fk(p)=0上或者离其非常近的点,TSDF点梯度在该处被假设为正交于零水平集,所以与像素u\mathbf{u}u相关点p\mathbf{p}p处的曲面法线可以从Fk\mathbf{F}_kFk直接用SDF的数值导数计算: Rg,kN^k=N^kg(u)=v[∇F(p)]∇F=[∂F∂x,∂F∂y,∂F∂z]⊤\begin{aligned} \mathrm{R}_{g,k} \hat{\mathbf{N}}_k &= \hat{\mathbf{N}}^g_k (\mathbf{u}) = \mathbf{v} [\nabla \mathbf{F}(\mathbf{p})] \\ \nabla \mathbf{F} &= \lbrack \frac{\partial \mathbf{F}}{\partial x} , \frac{\partial \mathbf{F}}{\partial y}, \frac{\partial \mathbf{F}}{\partial z} \rbrack ^\top \end{aligned} Rg,kN^k∇F=N^kg(u)=v[∇F(p)]=[∂x∂F,∂y∂F,∂z∂F]⊤ 相关原理可以参考原文的参考文献[4],就是算当前帧的像素对应更新后曲面上的哪个点。 相机位姿估计 实时相机定位包含对每一帧新的深度图估计当前相机位姿Tg,k∈SE3\mathrm{T}_{g,k} \in \mathbb{SE}_3Tg,k∈SE3。在这个工作中,使用两个因素带来的优势,使用深度图中所有的数据进行一个基于稠密ICP的位姿估计。首先,由于很高的跟踪帧率,可认为相邻两帧之间的运动非常小。其次,现代GPU硬件可以实现高度并行化计算。该系统中通过align实时曲面测量(Vk,Nk)(\mathbf{V}_k, \mathbf{N}_k)(Vk,Nk)到由前一帧得到的(V^k−1,N^k−1)(\hat{\mathbf{V}}_{k-1}, \hat{\mathbf{N}}_{k-1})(V^k−1,N^k−1)模型预期。 利用期望曲面,估计相机位姿Tg,k\mathrm{T}_{g,k}Tg,k的全局点面能量函数为 E(Tg,k)=∑u∈UΩk(u)≠null∥(Tg,kV˙k(u)−V^k−1g(u^))⊤N^k−1g(u^)∥2\mathbf{E}(\mathrm{T}_{g,k}) = \sum_{ \begin{aligned} \mathbf{u} &\in \mathscr{U} \\ {\Omega}_k(\mathbf{u}) &\neq null \end{aligned}} \lVert \left( \mathrm{T}_{g,k} \dot{\mathbf{V}}_k(\mathbf{u}) - \hat{\mathbf{V}}^g_{k-1}(\hat{\mathbf{u}}) \right)^\top \hat{\mathbf{N}}^g_{k-1}(\hat{\mathbf{u}}) \rVert_2 E(Tg,k)=uΩk(u)∈U≠null∑∥(Tg,kV˙k(u)−V^k−1g(u^))⊤N^k−1g(u^)∥2 通过计算透视投影点,使用投影数据关联算法(就是将储存为期望曲面的顶点投影到该帧上)得到一系列顶点对应{Vk(u),V^k−1(u^)∣Ω(u)≠null}\{ \mathbf{V}_k(\mathbf{u}), \hat{\mathbf{V}}_{k-1}(\mathbf{\hat{u}}) | \Omega(\mathbf{u}) \neq null \}{Vk(u),V^k−1(u^)∣Ω(u)≠null}。设定阈值处理特别不正确的对应 Ω(u)≠null iff{Mk(u)=1,and∥T~g,kzV˙k(u)−V^k−1g(u^)∥≤εd,and⟨R~g,kzNk(u),N^k−1g(u^)⟩≤εθ.\Omega(\mathbf{u}) \neq null \text{ iff} \begin{cases} \mathbf{M}_k(\mathbf{u}) &= &1 ,&\text{and}\\ \lVert \tilde{\mathrm{T}}^z_{g,k} \dot{\mathbf{V}}_k(\mathbf{u}) - \hat{\mathbf{V}}^g_{k-1}(\hat{\mathbf{u}}) \rVert &\leq &\varepsilon_d ,& \text{and} \\ \langle \tilde{R}^z_{g,k} \mathbf{N}_k(\mathbf{u}), \hat{\mathbf{N}}^g_{k-1}(\mathbf{\hat{u}}) \rangle &\leq &\varepsilon_\theta .& \end{cases} Ω(u)≠null iff⎩⎪⎨⎪⎧Mk(u)∥T~g,kzV˙k(u)−V^k−1g(u^)∥⟨R~g,kzNk(u),N^k−1g(u^)⟩=≤≤1,εd,εθ.andand T~g,kz=0\tilde{\mathrm{T}}^{z=0}_{g,k}T~g,kz=0由前一帧的位姿Tg,k\mathrm{T}_{g,k}Tg,k初始化。]]></content>
<tags>
<tag>3D Reconstruct</tag>
<tag>TSDF</tag>
<tag>KinectFusion</tag>
<tag>RGBD</tag>
<tag>kinfu</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TSDF]]></title>
<url>%2Farchives%2F49472.html</url>
<content type="text"><![CDATA[B. Curless and M. Levoy, “A volumetric method for building complex models from range images,” 1996, pp. 303–312. 一个将align后的range image融合的方法,使用累积权重的符号距离函数。 文中算法性质: 不确定性的表示,误差分布是不对称的,其主方向为视线方向 使用所有的range数据 增量式和有序的独立更新 时间和空间有效性 鲁棒性 没有拓扑形式的限制 重建中填满空间的能力 体素融合 算法使用了一个连续隐函数D(x)D(\mathbf{x})D(x),通过采样表示。这个函数是每个点沿着视线方向到最近的range曲面距离的加权和。 融合规则 通过将从range图像1,…,n1, \dots, n1,…,n中获取的符号距离函数d1(x),d2(x),…,dn(x)d_1(\mathbf{x}), d_2(\mathbf{x}), \dots, d_n(\mathbf{x})d1(x),d2(x),…,dn(x)和权重函数w1(x),w2(x),…,wn(x)w_1(\mathbf{x}), w_2(\mathbf{x}), \dots, w_n(\mathbf{x})w1(x),w2(x),…,wn(x)结合来构造这个函数。该方法的结合规则对每个体素给出一个累积符号距离函数D(x)D(\mathbf{x})D(x)和一个累计权重W(x)W(\mathbf{x})W(x)。在离散的体素栅格上表示这些函数并且检测出一个D(x)=0D(\mathbf{x}) =0D(x)=0的等值面。对于不同的range scanning技术,权重的选择是不同的。比如光线三角扫描器(optical triangulation scanners),权重取决于每一个顶点(vertex)法线和视线方向的点积,当视线和切平面夹角越小的时候不确定度越高。mesh边界上的数据具有更高的不确定性,需要更小的权重。结合规则为: D(x)=∑wi(x)di(x)∑wi(x)W(x)=∑wi(x)\begin{aligned} D(\mathbf{x}) &= \frac{\sum w_i(\mathbf{x}) d_i(\mathbf{x})}{\sum w_i(\mathbf{x})} \\ W(\mathbf{x}) &= \sum w_i(\mathbf{\mathbf{x}}) \end{aligned} D(x)W(x)=∑wi(x)∑wi(x)di(x)=∑wi(x) 其中di(x)d_i(\mathbf{x})di(x)和wi(x)w_i(\mathbf{x})wi(x)分别是从range image中得到的符号距离函数和权重函数。为了写成增量式计算: Di+1(x)=Wi(x)Di(x)+wi+1(x)di+1(x)Wi(x)+wi+1(x)Wi+1(x)=Wi(x)+wi+1(x)\begin{aligned} D_{i+1}(\mathbf{x}) &= \frac{W_i(\mathbf{x}) D_i(\mathbf{x}) + w_{i+1}(\mathbf{x}) d_{i+1}(\mathbf{x})}{W_i(\mathbf{x}) + w_{i+1}(\mathbf{x})} \\ W_{i+1}(\mathbf{x}) &= W_i(\mathbf{x}) + w_{i+1}(\mathbf{x}) \end{aligned} Di+1(x)Wi+1(x)=Wi(x)+wi+1(x)Wi(x)Di(x)+wi+1(x)di+1(x)=Wi(x)+wi+1(x) 其中Di(x)D_i(\mathbf{x})Di(x)和Wi(x)W_i(\mathbf{x})Wi(x)是融合第iii帧range image后的累积符号距离函数和权重函数。 截断距离 原则上,距离函数和权重函数都可以无限延长。但是为了保护目标对面的曲面不受影响,该方法减少曲面背后的权重函数。此时需要考虑在哪里减少权重函数。需要保持在曲面背后足够远以保证所有的数据(distance ramp)对零交界面有所贡献,同时也应该足够窄以保证曲面的其它地方不受影响。为了满足这个需求,该方法在距离等于测量的最大不确定间隔的一半时将权重骤减/截断。同样的,曲面前面也不需要扩展太远。 在二维和三维空间中,range测量对应带着权重函数的曲线和曲面,同时signed distance ramps有和传感器不确定性主方向相同的方向。实际上,该方法使用一个固定的点表示符号距离函数,其边界在DminD_{min}Dmin和DmaxD_{max}Dmax之间。DminD_{min}Dmin和DmaxD_{max}Dmax必须一个为负数,一个为正数。 算法流程 首先将所有体素权重设为000,因此新的数据可以覆写初始栅格值。然后通过从采样格(sampled lattice)的最近邻构造三角形来网格化range image。通过丢弃长度超过阈值的边来保证网格化没有横跨不连续的地方。必须对以上提及的每一个顶点计算一个对应的权重。一旦range image转换为了每个顶点带权重的三角mesh,就可以开始更新体素栅格了。 符号距离贡献通过从传感器投射一条射线穿过曲面周围的每一个体素,然后在三角网格mesh上与曲面相交来计算(每个点到曲面的距离)。权重通过相交的三角形顶点权重的线性内插得到。 空洞填补 以上内容描述的算法被设计重建曲面的客观部分,不可见部分将显示为空洞。 算法的关键是将volume中所有的点分为三个状态:不可见、空或者近曲面。曲面前面的部分被看作空D(x)=Dmin,W(x)=0D(\mathbf{x})=D_{min},W(\mathbf{x})=0D(x)=Dmin,W(x)=0,近曲面区域包含000等值面Dmin]]></content>
<tags>
<tag>3D Reconstruct</tag>
<tag>TSDF</tag>
<tag>KinectFusion</tag>
<tag>DynamicFusion</tag>
<tag>mesh</tag>
<tag>voxel</tag>
</tags>
</entry>
<entry>
<title><![CDATA[用于曲面配准的非刚体ICP算法最优化步骤]]></title>
<url>%2Farchives%2F46063.html</url>
<content type="text"><![CDATA[B. Amberg, S. Romdhani, and T. Vetter, “Optimal Step Nonrigid ICP Algorithms for Surface Registration,” in 2007 IEEE Conference on Computer Vision and Pattern Recognition, Minneapolis, MN, USA, 2007, pp. 1–8. Overview 配准是将template warp到target上。该算法使用局部仿射性进行正则化,假设每个顶点都对应一个仿射变换,并且最小化相邻顶点对应仿射变换的差(局部平滑)。由于一对匹配点无法计算得到一个仿射变换,因此引入正则项作为新的约束。 该文章定义了非刚体ICP框架的最优化步骤,扩展的ICP方法适用于非刚体形变并且保留了ICP原本的收敛性。 构造CostFunction 给定template mesh为S=(V,E)\mathcal{S} = \left( \mathcal{V}, \mathcal{E} \right)S=(V,E)拥有nnn个顶点V\mathcal{V}V和mmm条边E\mathcal{E}E的图。Target T\mathcal{T}T为任意一种中3D空间中可以给其中任意一个点找到最近点的表示。 需要求解的数据是每个template顶点对应的仿射变换矩阵Xi\bm{X}_iXi,完整的形式为 X:=[X1…Xn]T\bm{X} \coloneqq \begin{bmatrix} \bm{X}_1 & \dots & \bm{X}_n \end{bmatrix} ^T X:=[X1…Xn]T 距离项 形变后template上的顶点和target之间的距离应该很小,距离项由此构造为 Ed(X):=∑vi∈Vwidist2(T,Xivi)E_d(\bm{X}) \coloneqq \sum_{\bm{v}_i \in \mathcal{V}} w_i \text{dist}^2(\mathcal{T}, \bm{X}_i \bm{v}_i) Ed(X):=vi∈V∑widist2(T,Xivi) vi\bm{v}_ivi为其次向量[x,y,z,1]T\begin{bmatrix} x,y,z,1 \end{bmatrix}^T[x,y,z,1]T,点v\bm{v}v和它在target中最近点的距离为dist(T,v)\text{dist}(\mathcal{T}, \bm{v})dist(T,v)。使用分层边界球方法提高最近点搜索的速度。匹配的可信度为权重wiw_iwi,当某个点没有对应点时,其权重为000。 stiffness项 Stiffness项用来惩罚相邻顶点间仿射变换的不同。使用加权矩阵G:=diag(1,1,1,γ)\bm{G} \coloneqq \text{diag} (1,1,1,\gamma)G:=diag(1,1,1,γ),得到 Es(X):=∑{i,j}∈E∥(Xi−Xj)G∥F2E_s(\bm{X}) \coloneqq \sum_{\{i,j\} \in \mathcal{E}} \lVert (\bm{X}_i - \bm{X}_j)\bm{G} \rVert ^2_F Es(X):={i,j}∈E∑∥(Xi−Xj)G∥F2 landmark项 用来初始化和引导配准,对cost function本身影响不大。给定一系列landmark L={(vi1,l1),…,(vil,ll)}\mathcal{L} = \{ (\bm{v}_{i_1}, \bm{l}_1), \dots , (\bm{v}_{i_l}, \bm{l}_l) \}L={(vi1,l1),…,(vil,ll)}将template顶点映射到target顶点上, El(X):=∑{vi,l}∈L∥Xivi−l∥2E_l(\bm{X}) \coloneqq \sum_{\{\bm{v}_i, \bm{l}\} \in \mathcal{L}} \lVert \bm{X}_i \bm{v}_i - \bm{l} \rVert ^2 El(X):={vi,l}∈L∑∥Xivi−l∥2 完整的CostFunction E(X):=Ed(X)+αEs(X)+βEl(X)E(\bm{X}) \coloneqq E_d(\bm{X}) + \alpha E_s(\bm{X}) + \beta E_l(\bm{X}) E(X):=Ed(X)+αEs(X)+βEl(X) 计算步骤 初始化X0\bm{X}^0X0 对每个stiffness αi∈{α1,…αn}\alpha ^i \in \{ \alpha ^1, \dots \alpha ^n \}αi∈{α1,…αn}都有αi>αi+1\alpha^i > \alpha^{i+1}αi>αi+1 到∥Xj−Xj−1∥]]></content>
<tags>
<tag>non-rigid</tag>
<tag>ICP</tag>
<tag>Registration</tag>
<tag>3D Reconstruct</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DynamicFusion简要解析]]></title>
<url>%2Farchives%2F4028.html</url>
<content type="text"><![CDATA[DynamicFusion所做的工作:基于体素流场,将每帧场景状态变换到固定的(fixed, canonical)frame。 主要贡献:由刚体场景(KinectFusion)发展得到的,保留体素帧融合(volumetric scan fusion)最优特性的一个非刚体变换和融合的方法。 次要贡献:有效表达了体素warp,并实时计算。 Overview 算法有3个核心组成部分: 估算体素model-to-frame的warp场参数 利用计算得到的warp场,将实时深度图融合到canonical空间 调整warp场的结构去获取新的几何 稠密非刚体warp场 使用体素warp场表示动态场景运动,每一个点都对应一个6D的变换W:S↦SE(3)\mathcal{W}:\mathbf{S} \mapsto \mathbf{SE}(3)W:S↦SE(3),每一个canonical点vc∈Sv_c \in \mathbf{S}vc∈S,Tlc=W(vc)\mathbf{T}_{lc} = \mathcal{W}(v_c)Tlc=W(vc)将点从canonical空间变换到实时非刚体形变参考帧。如: (vt⊤,1)⊤=W(vc)(vc⊤,1)⊤(nt⊤,0)⊤=W(vc)(nc⊤,0)⊤\begin{aligned} \left( v_t^\top, 1 \right)^\top &= \mathcal{W}(v_c) \left( v_c^\top, 1 \right)^\top \\ \left( n_t^\top, 0 \right)^\top &= \mathcal{W}(v_c) \left( n_c^\top, 0 \right)^\top \end{aligned} (vt⊤,1)⊤(nt⊤,0)⊤=W(vc)(vc⊤,1)⊤=W(vc)(nc⊤,0)⊤ 对偶四元数内插 由于直接进行稠密计算不现实,计算量太大,warp场计算使用对偶四元数内插(DQB)方式进行,warp函数定义为: W(xc)≡SE3(DQB(xc))\mathcal{W}(x_c) \equiv SE3( \mathbf{DQB} (x_c) ) W(xc)≡SE3(DQB(xc)) 其中SE3(⋅)SE3(\cdot)SE3(⋅)将对偶四元数转换为SE(3)\mathbf{SE}(3)SE(3)变换矩阵, DQB(xc)≡∑k∈N(xc)wk(xc)q^kc∥∑k∈N(xc)wk(xc)q^kc∥\mathbf{DQB} (x_c) \equiv \frac{\sum_{k \in N(x_c)} \mathbf{w}_k(x_c)\mathbf{\hat{q}}_{kc}} {\lVert \sum_{k \in N(x_c)} \mathbf{w}_k(x_c)\mathbf{\hat{q}}_{kc} \rVert} DQB(xc)≡∥∑k∈N(xc)wk(xc)q^kc∥∑k∈N(xc)wk(xc)q^kc q^kc∈R8\mathbf{\hat{q}}_{kc} \in \mathbb{R}^8q^kc∈R8为单位对偶四元数,N(x)N(x)N(x)为点xxx的kkk最近邻变换node,wk:R3↦R\mathbf{w}_k:\mathbb{R}^3 \mapsto \mathbb{R}wk:R3↦R定义了决定每个node影响半径的权重。也就是点xxx的变换通过kkk最近邻node对应变换的单位对偶四元数加权和再归一化,得到对应的单位对偶四元数之后专为变换矩阵。 warp场状态表示 ttt时刻的warp场Wt\mathcal{W}_tWt状态由nnn个形变node Nwarpt={dgv,dgw,dgse3}t\mathcal{N}^t_\mathbf{warp} = \{ \mathbf{dg}_v, \mathbf{dg}_w, \mathbf{dg}_{se3} \}_tNwarpt={dgv,dgw,dgse3}t表示。对于每个node i=1…ni = 1 \dots ni=1…n而言,dgvi∈R3\mathbf{dg}_v^i \in \mathbb{R}^3dgvi∈R3为canonical帧中坐标,dgw\mathbf{dg}_wdgw为半径基权重(radial basis weight),控制warp场内插权重, wi(xc)=exp(−∥dgvi−xc∥2/(2(dgwi)2))\mathbf{w}_i (x_c) = \exp \left(- \lVert \mathbf{dg}^i_v - x_c \rVert ^2 / \left( 2 \left( \mathbf{dg}^i_w \right)^2 \right) \right) wi(xc)=exp(−∥dgvi−xc∥2/(2(dgwi)2)) Tic=dgse3i\mathbf{T}_{ic} = \mathbf{dg}^i_{se3}Tic=dgse3i为node对应的变换。 对于整个warp场而言,可以分离出由于相机运动等原因造成的公共刚体变换。最后完整的warp场函数为 Wt(xc)=TlwSE3(DQB(xc))\mathcal{W}_t(x_c) = \mathbf{T}_{lw} SE3( \mathbf{DQB} (x_c) ) Wt(xc)=TlwSE3(DQB(xc)) 稠密的非刚体曲面融合 在得到model-to-frame的warp场Wt\mathcal{W}_tWt之后,就要更新canonical模型几何了。 TSDF表示 在canonical空间S\mathbf{S}S中重建由采样TSDF V:S↦R2\mathbf{TSDF} \space \mathcal{V} : \mathsf{S} \mapsto \mathbb{R}^2TSDF V:S↦R2表示。S\mathsf{S}S在体素域中S⊂N3\mathsf{S} \subset \mathbb{N}^3S⊂N3,每个体素x∈S\mathbf{x} \in \mathsf{S}x∈S对应一个采样点xcx_cxc。元组V(x)↦[v(x)∈R,w(x)∈R]⊤\mathcal{V}(x) \mapsto \lbrack \mathsf{v}(\mathbf{x}) \in \mathbb{R}, \mathsf{w}(\mathbf{x}) \in \mathbb{R} \rbrack ^\topV(x)↦[v(x)∈R,w(x)∈R]⊤,v(x)\mathsf{v}(\mathbf{x})v(x)为该店所有投影TSDF\mathbf{TSDF}TSDF值的加权平均,w(x)\mathsf{w}(\mathbf{x})w(x)为相关权重和。 TSDF融合 对于给定的实时深度图DtD_tDt,将每个体素中心warp到实时帧中,(xt⊤,1)⊤=Wt(xc)(xc⊤,1)⊤\left( x_t^\top, 1 \right)^\top = \mathcal{W}_t(x_c) \left( x_c^\top, 1 \right)^\top(xt⊤,1)⊤=Wt(xc)(xc⊤,1)⊤,然后通过直接将warp之后的(体素)中心投影到该深度帧中进行TSDF\mathbf{TSDF}TSDF曲面融合操作。 psdf(xc)=[K−1Dt(uc)[uc⊤,1]⊤]z−[xt]z\mathbf{psdf} (x_c) = \lbrack \mathbf{K}^{-1} D_t(u_c) \lbrack u_c^\top, 1 \rbrack ^\top \rbrack _z - \lbrack x_t \rbrack _z psdf(xc)=[K−1Dt(uc)[uc⊤,1]⊤]z−[xt]z 其中uc=π(Kxt)u_c = \pi \left( \mathbf{K}x_t \right)uc=π(Kxt)为体素中心投影对应的像素,通过这种投影将体素和深度图像素对应起来。在实时帧坐标系下,计算使用深度图得到的zzz轴坐标和warp后对应体素中心的zzz轴坐标的差值。 然后就该更新TSDF\mathbf{TSDF}TSDF了, V(x)t={[v′(x),w′(x)]⊤,if psdf(dc(x))>−τV(x)t−1,otherwise\mathcal{V}(\mathbf{x})_t = \begin{cases} \lbrack \mathsf{v}'(\mathbf{x}), \mathsf{w}'(\mathbf{x}) \rbrack ^\top , &\text{if } \mathbf{psdf}(\mathbf{dc}(\mathbf{x})) > - \tau \\ \mathcal{V}(\mathbf{x})_{t-1} , &\text{otherwise} \end{cases} V(x)t={[v′(x),w′(x)]⊤,V(x)t−1,if psdf(dc(x))>−τotherwise dc(⋅)\mathbf{dc}(\cdot)dc(⋅)将离散的体素转变到连续的TSDF\mathbf{TSDF}TSDF域中,截断距离τ>0\tau > 0τ>0, v′(x)=v(x)t−1w(x)t−1+min(ρ,τ)w(x)w(x)t−1+w(x)ρ=psdf(dc(x))w′(x)=min(w(x)t−1+w(x),wmax)w(x)∝1k∑i∈N(xc)∥dgwi−xc∥2\begin{aligned} \mathsf{v}'(\mathbf{x}) &= \frac{\mathsf{v}(\mathbf{x})_{t-1} \mathsf{w}(\mathbf{x})_{t-1} + \min (\rho, \tau) w(\mathbf{x})}{\mathsf{w}(\mathbf{x})_{t-1} + w(\mathbf{x})} \\ \rho &= \mathbf{psdf}(\mathbf{dc}(\mathbf{x})) \\ \mathsf{w}'(\mathbf{x}) &= \min (\mathsf{w}(\mathbf{x})_{t-1} + w(\mathbf{x}), w_{max}) \\ w(\mathbf{x}) &\propto \frac{1}{k} \sum _{i \in N(x_c)} \lVert \mathbf{dg}^i_w - x_c \rVert _2 \end{aligned} v′(x)ρw′(x)w(x)=w(x)t−1+w(x)v(x)t−1w(x)t−1+min(ρ,τ)w(x)=psdf(dc(x))=min(w(x)t−1+w(x),wmax)∝k1i∈N(xc)∑∥dgwi−xc∥2 最后这个这个似乎不应该是dgwi\mathbf{dg}_w^idgwi,而应该是dgvi\mathbf{dg}_v^idgvi,因为后者才是表示3维坐标的量。 估计warp场状态Wt\mathcal{W}_tWt 前面说了Wt\mathcal{W}_tWt的数据结构与如何在曲面融合中使用,但是还没有说如何计算得到,现在开始计算这个东西。 方法是使用给出的深度图DtD_tDt和对应的重建V\mathcal{V}V,通过能量函数最小化估计当前变换dgse3\mathbf{dg}_{se3}dgse3的值: E(Wt,V,Dt,E)=Data(Wt,V,Dt)+λReg(Wt,E)E(\mathcal{W}_t, \mathcal{V}, D_t, \mathcal{E}) = \mathbf{Data}(\mathcal{W}_t, \mathcal{V}, D_t) + \lambda \mathbf{Reg}(\mathcal{W}_t, \mathcal{E}) E(Wt,V,Dt,E)=Data(Wt,V,Dt)+λReg(Wt,E) 其中数据项为稠密的model-to-frame的ICP,正则项惩罚不平滑的运动场,保证由边集E\mathcal{E}E连接的变换node之间的ARAP(as-rigid-as-possible)形变。 数据项 目标是估计非刚体变换参数,每个点对应的Tic\mathbf{T}_{ic}Tic和公共变换Tlw\mathbf{T}_{lw}Tlw,warp canonical体素到实时帧中。 mesh由点-法线对存储在canonical帧中:V^c≡{Vc,Nc}\mathcal{\hat{V}}_c \equiv \{ V_c, N_c \}V^c≡{Vc,Nc},将该mesh用Wt\mathcal{W}_tWt非刚性变换得到实时warp后的点-法线对V^w\mathcal{\hat{V}}_wV^w,warp后的与对应的实时深度数据之间构造数据项。 在model几何和实时帧之间初始的数据对应估计,通过将V^w\mathcal{\hat{V}}_wV^w渲染到使用rasterizing渲染流程的由canonical帧顶点坐标shade的实时帧中(关于rasterizing请参考该链接)。将得到在实时帧中可见的canonical帧的几何:P(V^c)\mathcal{P}(\mathcal{\hat{V}}_c)P(V^c)。 作为非刚体优化的先验,当给定一帧新的数据时,首先使用KinectFusion的稠密ICP方法得到公共变换因子Tlw\mathbf{T}_{lw}Tlw。然后在当前可见的canonical几何对应的当前帧像素区域Ω\OmegaΩ中,构造每个点model-to-frame的point-plane误差为数据项 Data(W,V,Dt)≡∑u∈Ωψdata(n^u⊤(v^u−vlu~))\mathbf{Data}(\mathcal{W}, \mathcal{V}, D_t) \equiv \sum _{u \in \Omega} \psi _\mathbf{data} (\mathbf{\hat{n}}_u^\top (\mathbf{\hat{v}}_u - \mathbf{vl}_{\tilde{u}})) Data(W,V,Dt)≡u∈Ω∑ψdata(n^u⊤(v^u−vlu~)) 其中ψdata\psi _{\mathbf{data}}ψdata 为Tukey惩罚函数, [vl(u)⊤,1]⊤=K−1Dt(u)[u⊤,1]⊤T~u=W(v(u))v^u=T~uv(u)n^u=T~un(u)u~=π(Kv^u)\begin{aligned} \lbrack \mathbf{vl}(u)^\top , 1 \rbrack ^\top &= \mathbf{K}^{-1} D_t(u) \lbrack u^\top , 1 \rbrack ^\top \\ \mathbf{\tilde{T}}^u &= \mathcal{W} (\mathbf{v} (u)) \\ \mathbf{\hat{v}}_u &= \mathbf{\tilde{T}}^u \mathbf{v}(u) \\ \mathbf{\hat{n}}_u &= \mathbf{\tilde{T}}^u \mathbf{n}(u) \\ \tilde{u} &= \pi (\mathbf{K} \mathbf{\hat{v}}_u) \end{aligned} [vl(u)⊤,1]⊤T~uv^un^uu~=K−1Dt(u)[u⊤,1]⊤=W(v(u))=T~uv(u)=T~un(u)=π(Kv^u) 第一个公式计算当前深度图像素对应点的坐标,第二到第四公式计算由canonical帧中warp后的点和法线,第五个公式寻找对应关系。 正则项 数据项计算当前可见的形变,但是还有一个关键问题是当前不可见部分的形变也要估计,因此加入了正则项。 基于形变图模型定义正则项,如果node iii 和 jjj 之间存在边则增加一个ARAP正则项到全局能量中最小化 Reg(W,E)=∑i=0n∑j∈E(i)αijψreg(Ticdgvj−Tjcdgvj)\mathbf{Reg}(\mathcal{W}, \mathcal{E}) = \sum ^n _{i=0} \sum _{j \in \mathcal{E}(i)} \alpha_{ij} \psi_{\mathbf{reg}} (\mathbf{T}_{ic} \mathbf{dg}^j_v - \mathbf{T}_{jc} \mathbf{dg}^j_v) Reg(W,E)=i=0∑nj∈E(i)∑αijψreg(Ticdgvj−Tjcdgvj) ψreg\psi_{\mathbf{reg}}ψreg为Huber惩罚函数,E\mathcal{E}E定义了正则化的图拓扑,αij\alpha_{ij}αij为边的权重αij=max(dgwi,dgwj)\alpha_{ij} = \max (\mathbf{dg}^i_w, \mathbf{dg}^j_w)αij=max(dgwi,dgwj)。 扩大warp场 原有的warp场node无法覆盖新增的数据时,需要扩大warp场。包括增量式更新形变图node Nwarp\mathcal{N}_{\mathbf{warp}}Nwarp并重新计算新的分层边拓扑E\mathcal{E}E。 当 mink∈N(xc)(∥dgvk−vc∥dgwk)≥1\min _{k \in N(x_c)} \left( \frac{\lVert \mathbf{dg}_v^k - v_c \rVert}{\mathbf{dg}^k_w} \right) \ge 1mink∈N(xc)(dgwk∥dgvk−vc∥)≥1 时,被认为不支持的顶点(vertex)。对于不支持的顶点使用下采样寻找新的node(详细描述看论文)。 给到新增的node之后,需要重新计算边。构造一个层数L≥1L \ge 1L≥1的正则图node分层,l=0l = 0l=0层node简单设为Nwarp\mathcal{N} _{\mathbf{warp}}Nwarp。计算l=1l = 1l=1层的正则node,通过在warp场node dgv\mathbf{dg}_vdgv 上,基于半径搜索的下采样到增加的decimation半径 ϵβl,β>1\epsilon \beta ^l,\beta > 1ϵβl,β>1。再次通过DQB使用当前更新的Wt\mathcal{W}_tWt计算初始node变换。反复如此到指定层数。新的边集E\mathcal{E}E开始于l=0l = 0l=0的Nwarp\mathcal{N}_{\mathbf{warp}}Nwarp到位于l=1l = 1l=1的Nreg\mathcal{N}_{\mathbf{reg}}Nreg,finer层中每个node增加的边都连到coarser层中的kkk近邻。]]></content>
<tags>
<tag>non-rigid</tag>
<tag>3D Reconstruct</tag>
<tag>RGBD</tag>
<tag>Dynamic</tag>
</tags>
</entry>
<entry>
<title><![CDATA[statismo代码分析--statismo-build-gp-model]]></title>
<url>%2Farchives%2F23376.html</url>
<content type="text"><![CDATA[使用高斯过程描述形变场,输入的数据只需要参考帧就行。需要设定kernel,由参考帧和kernel生成model 命令行参数 -t, --type TYPE : 指定模型是shape还是deformation -d, --dimensionality : 输入数据的维度,当TYPE为deformation时有效 -k, --kernel KERNEL : 指定高斯过程的kernel,默认只支持gaussian -p, --parameters KERNEL_PARAMETERS : kernel的参数,gaussian只有一个sigma参数 -s, --scale SCALE : 作用在kernel上的scaling参数 -n, --numberofbasisfunctions NUMBER_OF_BASIS_FUNCTIONS : 这个model需要的参数个数 -r, --reference REFERENCE_FILE : reference文件路径 -o, --output-file OUTPUT_FILE : 输出文件路径 -m, --input-model MODEL_FILE : 已有model的文件路径,用于扩展当前已有的model kernel的代码 在文件 utils/statismo-build-gp-model-kernels.h 中添加kernel,在这里kernel是对应的一对点计算得到的一个值,并非对应点的每个元素得到的每个一个对应向量 截取guassian kernel代码 在对应的头文件中定义类似的模板类,内联重载运算符()设定具体的kernel函数 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950// 用法,opt.vKernelParameters指定kernel类型typedef typename DataType::PointType PointType;typedef boost::scoped_ptr MatrixPointerType; MatrixPointerType pKernel;if (isShapeModel == true) { pKernel.reset((statismo::ScalarValuedKernel *) it->second.createKernelShape(opt.vKernelParameters));} else { if (Dimenstionality == Dimensionality2D) { pKernel.reset((statismo::ScalarValuedKernel *)it->second .createKernel2DDeformation(opt.vKernelParameters)); } else { pKernel.reset((statismo::ScalarValuedKernel *)it->second .createKernel3DDeformation(opt.vKernelParameters)); }}//////////////////////////////////////////////////////////////////////////template class GaussianKernel : public statismo::ScalarValuedKernel {public: // 这两个必须要有,并且不能边 typedef typename TPoint::CoordRepType CoordRepType; typedef vnl_vector VectorType; // 只有一个参数sigma GaussianKernel(double sigma) : m_sigma(sigma), m_sigma2(-1.0 / (sigma * sigma)) {} // 内联重载操作符(),定义kernel计算 // kernel是一个二元函数,返回kernel函数值 inline double operator()(const TPoint &x, const TPoint &y) const { VectorType xv = x.GetVnlVector(); VectorType yv = y.GetVnlVector(); VectorType r = yv - xv; return exp((double)dot_product(r, r) * m_sigma2); } std::string GetKernelInfo() const { std::ostringstream os; os < op1->cond1 cond1(no)->io2->ed cond1(yes)->cond2 cond2(yes)->op2->op5 cond2(no)->cond3 cond3(no)->op3->op5 cond3(yes)->op4->op5 op5->cond4 cond4(yes,right)->op6->op7 cond4(no)->io3->op7 op7->io4->ed{"scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12} var code = document.getElementById("flowchart-0-code").value; var options = JSON.parse(decodeURIComponent(document.getElementById("flowchart-0-options").value)); var diagram = flowchart.parse(code); diagram.drawSVG("flowchart-0", options);]]></content>
<tags>
<tag>statismo</tag>
<tag>code</tag>
<tag>SSM</tag>
</tags>
</entry>
<entry>
<title><![CDATA[statismo代码分析--statismo-build-deformation-model]]></title>
<url>%2Farchives%2F26365.html</url>
<content type="text"><![CDATA[输入数据是形变场不是shape,对应的是reference mesh上每个点的形变方向,所以只需要输入数据对应,不需要align。但是最后得到的结果也是描述形变场的model,由这个model得到的形变场最后要加上reference mesh才能得到最后的shape。 命令行参数 -l, --data-list DATA_LIST :指定一个文件路径,这个文件包含所有用来生成模型的形变场文件路径,每一行只写一个文件 -o, --output-file OUTPUT_FILE :输出模型文件的路径 -n, --noise NOISE :指定PPCA(probabilistic principal component analysis)模型的噪声方差,默认为0 -d, --dimensionality :使用数据建立的模型维度,默认为3 代码 代码详细解释请参考另一篇文章st=>start: 开始 ed=>end: 退出 io1=>inputoutput: 输入命令行参数 op1=>operation: 读取命令行参数 cond1=>condition: 参数是否 符合要求? io2=>inputoutput: 无法执行 cond2=>condition: 是否为2维? (否为3维) op2=>operation: 设置为2维运算 op3=>operation: 设置为3维运算 op4=>operation: 读入数据,将第一帧 数据设为reference op5=>operation: 进行PCA io3=>inputoutput: 保存model st->io1->op1->cond1 cond1(no)->io2->ed cond1(yes)->cond2 cond2(yes, right)->op2->op4 cond2(no)->op3->op4 op4->op5->io3->ed{"scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12} var code = document.getElementById("flowchart-0-code").value; var options = JSON.parse(decodeURIComponent(document.getElementById("flowchart-0-options").value)); var diagram = flowchart.parse(code); diagram.drawSVG("flowchart-0", options);]]></content>
<tags>
<tag>statismo</tag>
<tag>code</tag>
<tag>SSM</tag>
</tags>
</entry>
<entry>
<title><![CDATA[statismo代码分析--statismo-build-shape-model]]></title>
<url>%2Farchives%2F34417.html</url>
<content type="text"><![CDATA[statismo中statismo-build-shape-model程序代码的解读 总结一下就是:给出参考帧(因为PCA需要先中心化)之后PCA 流程图 命令行参数 -l, --data-list DATA_LIST :指定一个文件路径,这个文件包含所有用来生成模型的mesh文件路径,每一行只写一个文件 -o, --output-file OUTPUT_FILE :输出模型文件的路径 -p, --procrustes PROCRUSTES_MODE :选择数据对齐(aligned)模式。如果选择reference,所有的数据和指定的参考mesh对齐;如果选择GPA,则和均值对齐 -r, --reference FILE :在PROCRUSTES_MODE选择reference之后,指定参考mesh -n, --noise NOISE :指定PPCA(probabilistic principal component analysis)模型的噪声方差,默认为0 读入mesh文件的代码 1234567891011121314151617typedef itk::MeshFileReader MeshReaderType;typedef vector MeshReaderList;MeshReaderList meshes;meshes.reserve(fileNames.size());for (StringList::const_iterator it = fileNames.begin(); it != fileNames.end(); ++it) { MeshReaderType::Pointer reader = MeshReaderType::New(); reader->SetFileName(it->c_str()); reader->Update(); //这段注释是说Update()这个函数很重要 // itk::PCAModelBuilder is not a Filter in the ITK world, so the pipeline // would not get executed if its main method is called. So the pipeline // before calling itk::PCAModelBuilder must be executed by the means of calls // to Update() (at least for last elements needed by itk::PCAModelBuilder). meshes.push_back(reader);} 关于为什么要用Update()的解释: You only have to call Update() on the last filter in your pipeline. The rest of this answer is the explanation. ITK uses a pipeline execution framework for filters. Assume we have three filters that are connected sequentially like the following: input --> |filter1| --> |filter2| --> |filter3| --> output If you call Update() on filter3, ITK starts from filter3 and checks if the input(s) to each filter have changed. If they have, ITK calls update on them in turn. See slide 5 of this link. 计算平均mesh作为参考mesh的代码 originalMeshes传入的是meshes的指针,也就是align也会影响meshes 12345678910111213141516171819202122232425262728293031typedef itk::Mesh MeshType;///////////////////////////////////////////////////////////////// 将已经读入的mesh都拷贝过来,mesh为之前读入的文件vector originalMeshes;for (MeshReaderList::iterator it = meshes.begin(); it != meshes.end(); ++it) { MeshReaderType::Pointer reader = *it; originalMeshes.push_back(reader->GetOutput());}const unsigned uMaxGPAIterations = 20;const unsigned uNumberOfPoints = 100; // 最多使用这么多个点const float fBreakIfChangeBelow = 0.001f;typedef itk::VersorRigid3DTransform Rigid3DTransformType;typedef itk::Image ImageType;typedef itk::LandmarkBasedTransformInitializer LandmarkBasedTransformInitializerType;typedef itk::TransformMeshFilter FilterType;// 计算参考meshMeshType::Pointer referenceMesh = calculateProcrustesMeanMesh( originalMeshes, uMaxGPAIterations, uNumberOfPoints, fBreakIfChangeBelow);representer->SetReference(referenceMesh);SetIdentity(); landmarkBasedTransformInitializer->SetTransform(transform); landmarkBasedTransformInitializer->InitializeTransform(); // 使用计算得到的刚体变换进行align,这里是mesh上所有的点 typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(movingMesh); filter->SetTransform(transform); filter->Update(); *it = filter->GetOutput(); } return translatedMeshes;} 计算平均mesh的函数 遍历求和,然后遍历求平均 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788template typename MeshType::PointercalculateMeanMesh(std::vector meshes) { // 必须要存在mesh if (meshes.size() == 0) { itkGenericExceptionMacro( < GetNumberOfPoints() * MeshType::PointDimension; vMeshPoints.reserve(uDataSize); for (int i = 0; i < uDataSize; ++i) { CompensatedSummationType sum; vMeshPoints.push_back(sum); } // 遍历每个mesh // 求和 for (typename std::vector::const_iterator i = meshes.begin(); i != meshes.end(); ++i) { typename MeshType::Pointer pMesh = *i; // 验证向量维度(点的个数和点的维度)要一样 if (vMeshPoints.size() != pMesh->GetNumberOfPoints() * MeshType::PointDimension) { itkGenericExceptionMacro( < GetPoints()->Begin(); // sum up all meshes // 遍历这个mesh的每个点 for (; pointData != pMesh->GetPoints()->End(); ++pointData) { const typename MeshType::PointType point = pointData->Value(); // 遍历这个点的每个维度 // 将对应维度的数值加入vMeshPoints中 for (typename MeshType::PointType::ConstIterator pointIter = point.Begin(); pointIter != point.End(); ++pointIter, ++sum) { (*sum) += *pointIter; } } } float fInvNumberOfMeshes = 1.0f / meshes.size(); // 虽然是复制的第一帧mesh,但是后面是直接覆盖 typename MeshType::Pointer pMeanMesh = cloneMesh(pFirstMesh); // write the data to the mean mesh typename MeshPointsVectorType::iterator sum = vMeshPoints.begin(); // 遍历meanmesh的每个点 // 计算平均值 for (typename MeshType::PointsContainer::Iterator pointData = pMeanMesh->GetPoints()->Begin(); pointData != pMeanMesh->GetPoints()->End(); ++pointData) { // 遍历点的每个维度 for (typename MeshType::PointType::Iterator pointIter = pointData->Value().Begin(); pointIter != pointData->Value().End(); ++pointIter, ++sum) { // 计算平均值传给meanmesh *pointIter = sum->GetSum() * fInvNumberOfMeshes; } } return pMeanMesh;} 计算平均mesh和当前参考mesh的差值 每对对应点的距离求和,再除以shape向量的整体维度进行平均 12345678910111213141516171819202122232425template float calculateMeshDistance(typename MeshType::Pointer mesh1, typename MeshType::Pointer mesh2) { // 点数和拓扑要一样(实际上也需要完全对应) if (mesh1->GetNumberOfPoints() != mesh2->GetNumberOfPoints() || mesh1->GetNumberOfCells() != mesh2->GetNumberOfCells()) { itkGenericExceptionMacro( < GetPoints()->Begin(); IteratorType point2 = mesh2->GetPoints()->Begin(); // 每对对应点的距离求和 for (; point1 != mesh1->GetPoints()->End(); ++point1, ++point2) { fDifference += point1->Value().SquaredEuclideanDistanceTo(point2->Value()); } // 除以(mesh点的个数 乘上 点的维度) fDifference /= (mesh1->GetNumberOfPoints() * MeshType::PointDimension); return fDifference;} 将参考帧加入数据管理 1234567891011typedef itk::Mesh MeshType;typedef itk::DataManager DataManagerType;DataManagerType::Pointer dataManager = DataManagerType::New();////////////////////////////////////////////////////////////////////dataManager->SetRepresenter(representer);for (MeshReaderList::const_iterator it = meshes.begin(); it != meshes.end(); ++it) { MeshReaderType::Pointer reader = *it; dataManager->AddDataset(reader->GetOutput(), reader->GetFileName());} 进行PCA并保存model 这个PCA之前没有align的过程??GPA会进行align,但是如果选择reference这里就没有进行align了? 123456789101112// model类型typedef itk::StatisticalModel StatisticalModelType;StatisticalModelType::Pointer model;// 进行PCAtypedef itk::PCAModelBuilder PCAModelBuilder;PCAModelBuilder::Pointer pcaModelBuilder = PCAModelBuilder::New();// 直接就进行PCA了model = pcaModelBuilder->BuildNewModel(dataManager->GetData(), opt.fNoiseVariance);// 保存为文件itk::StatismoIO::SaveStatisticalModel( model, opt.strOutputFileName.c_str()); PCA函数 比较常规的PCA计算过程 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849template PCAModelBuilder::PCAModelBuilder() : Superclass() {}template typename PCAModelBuilder::StatisticalModelType *PCAModelBuilder::BuildNewModel(const DataItemListType &sampleDataList, double noiseVariance, bool computeScores, EigenValueMethod method) const { // 设定数据样本个数 unsigned n = sampleDataList.size(); if (n GetSampleVector().rows(); const Representer *representer = sampleDataList.front()->GetRepresenter(); // Compute the mean vector mu // 求和在平均得到重心 VectorType mu = VectorType::Zero(p); for (typename DataItemListType::const_iterator it = sampleDataList.begin(); it != sampleDataList.end(); ++it) { assert((*it)->GetSampleVector().rows() == p); // all samples must have same number of rows assert((*it)->GetRepresenter() == representer); // all samples have the same representer mu += (*it)->GetSampleVector(); } mu /= n; // Build the mean free sample matrix X0 // 减去重心,中心化 MatrixType X0(n, p); unsigned i = 0; for (typename DataItemListType::const_iterator it = sampleDataList.begin(); it != sampleDataList.end(); ++it) { X0.row(i++) = (*it)->GetSampleVector() - mu; } // build the model // 使用SVD或者特征值分解等方式计算 StatisticalModelType *model = BuildNewModelInternal(representer, X0, mu, noiseVariance, method); ... return model;} st=>start: 开始 ed=>end: 退出 io1=>inputoutput: 输入命令行参数 op1=>operation: 读取命令行参数 cond1=>condition: 参数是否 符合要求? io2=>inputoutput: 无法执行 op2=>operation: 读入mesh文件 cond2=>condition: 输入参数是否设 置了参考mesh? op3=>operation: 按照设置选择已有 mesh为参考mesh op4=>operation: 计算参考mesh (有相关代码详解) op5=>operation: 设定参考mesh并将每 帧mesh加入数据管理 op6=>operation: 进行PCA io3=>inputoutput: 保存model文件 st->io1->op1->cond1 cond1(no)->io2->ed cond1(yes)->op2->cond2 cond2(yes, right)->op3->op5 cond2(no)->op4->op5 op5->op6->io3->ed{"scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12} var code = document.getElementById("flowchart-0-code").value; var options = JSON.parse(decodeURIComponent(document.getElementById("flowchart-0-options").value)); var diagram = flowchart.parse(code); diagram.drawSVG("flowchart-0", options);]]></content>
<tags>
<tag>statismo</tag>
<tag>code</tag>
<tag>SSM</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Bundle Adjustment简述]]></title>
<url>%2Farchives%2F58892.html</url>
<content type="text"><![CDATA[笔者到底想讲些啥? 在SFM(structure from motion)的计算中BA(Bundle Adjustment)作为最后一步优化具有很重要的作用,在近几年兴起的基于图的SLAM(simultaneous localization and mapping)算法里面使用了图优化替代了原来的滤波器,这里所谓的图优化其实也是指BA。其实很多经典的文献对于BA都有深深浅浅的介绍,如果想对BA的全过程做一个全面的更深层次的了解,推荐阅读 Bundle Adjustment —A Modern Synthesis,但是BA的内容确实太多太杂了,刚对其了解的时候往往会陷入其局部的计算中不能自拔,因此笔者准备对其进行一个比较全局一点的介绍,希望读者可以比较轻松的一览BA的全过程而不是陷入其局部的繁琐的计算中,同时也会尽量对其需要的数学工具介绍全面,如有错误和遗漏还望指正。 如果读者对以下内容有基本了解那可就太棒棒了! 射影相机几何模型 对极几何 凸优化 矩阵理论 这才是真的开始 Bundle Adjustment(之后在不引起歧义的情况下用BA代替,你问为什么?笔者懒啊-。-),大概似乎也许好像有近百年的历史了吧(没错,可以称为state-of-art的视觉SLAM在几年前才用上将近上百岁的算法),中文译为光束法平差,大概大家看到更多的翻译可能为束调整、捆集调整或者捆绑调整等等。这么多翻译笔者最喜欢的还是光束法平差,一看就比其它的更专业逼格更高嘛,其它的翻译都太直译了。当然最重要的是光束法平差完美的表达了BA的来源、原理和计算过程,而其他的只是强调了将很多数据放在一起进行优化计算这个事。不信?那我们来分析一下嘛。 所谓bundle,来源于bundle of light,其本意就是指的光束,这些光束指的是三维空间中的点投影到像平面上的光束,而重投影误差(后面会讲这到底是个什么鬼)正是利用这些光束来构建的,因此称为光束法强调光束也正是描述其优化模型是如何建立的。剩下的就是平差,那什么是平差呢?借用一下百度词条 测量平差 中的解释吧。 由于测量仪器的精度不完善和人为因素及外界条件的影响,测量误差总是不可避免的。为了提高成果的质量,处理好这些测量中存在的误差问题,观测值的个数往往要多于确定未知量所必须观测的个数,也就是要进行多余观测。有了多余观测,势必在观测结果之间产生矛盾,测量平差的目的就在于消除这些矛盾而求得观测量的最可靠结果并评定测量成果的精度。测量平差采用的原理就是“最小二乘法”。 平差也就正好表述了为什么需要BA以及BA这个优化过程到底是怎么进行的。 BA模型到底是怎么来的? 感觉前面废话说了一大堆,解释了半天BA的中文翻译,那么BA到底是干嘛的呢?经过前面的铺垫,用一句话来描述BA那就是,BA的本质是一个优化模型,其目的是最小化重投影误差 本质是一个优化模型应该很容易理解,那么什么是重投影误差呢?投影自然是个几何的问题,既然是几何问题那这个时候来个图自然就是最棒棒了! 看!这些五颜六色的线就是我们讲的光束啦!那现在就该说下什么叫重投影误差了,重投影也就是指的第二次投影,那到底是怎么投影的呢?我们来整理一下吧: 其实第一次投影指的就是相机在拍照的时候三维空间点投影到图像上 然后我们利用这些图像对一些特征点进行三角定位(triangulation,很多地方翻译为三角化或者三角剖分等等,当然笔者最喜欢的还是三角定位,显然是利用几何信息构建三角形来确定三维空间点的位置嘛,相关内容请参考对极几何) 最后利用我们计算得到的三维点的坐标(注意不是真实的)和我们计算得到的相机矩阵(当然也不是真实的)进行第二次投影,也就是重投影 现在我们知道什么是重投影了,那重投影误差到底是什么样的误差呢?这个误差是指的真实三维空间点在图像平面上的投影(也就是图像上的像素点)和重投影(其实是用我们的计算值得到的虚拟的像素点)的差值,因为种种原因计算得到的值和实际情况不会完全相符,也就是这个差值不可能恰好为0,此时也就需要将这些差值的和最小化获取最优的相机参数及三维空间点的坐标。 进入数学模式! 感觉像写小说一样写了一堆堆的文字,既然BA是个数学问题,不用数学讲讲好像不太行,接下来就看看BA的数学模型是怎么构建的吧。 对BA有点了解的同学可能知道BA是一个图优化模型,那首先肯定要构造一个图模型了(没学过图论也没事,后面还是会回到一般的优化模型)。既然是图模型那自然就有节点和边了,这个图模型的节点由相机PiP_iPi和三维空间点构成XjX_jXj构成,如果点XjX_jXj投影到相机PiP_iPi的图像上则将这两个节点连接起来。还是来张图吧。 这样就一目了然了。那么我们现在就可以通过这个图来构造优化模型了。 令点XjX_jXj在相机PiP_iPi拍摄到的图像归一化坐标系上的坐标为k(uijT,1)T=Ki−1xijk(u_{ij}^T,1)^T=K_i^{-1}x_{ij}k(uijT,1)T=Ki−1xij,其重投影后的图像归一化坐标系下坐标为k′(vijT,1)T=Ki−1PiXjk'(v_{ij}^T,1)^T=K_i^{-1}P_iX_jk′(vijT,1)T=Ki−1PiXj,其中Ki−1K_i^{-1}Ki−1是为了在计算时能不受相机内参影响kkk和k′k'k′是将齐次坐标转换为非齐次坐标的常数项,可以得到该重投影误差为 eij=uij−vije_{ij}=u_{ij}-v_{ij} eij=uij−vij BA是要将所有重投影误差的和最小化,那么这里自然就要开始求和了。 minRi,ti,Xj∑i,jσij∥uij−vij∥2\min_{R_i,t_i,X_j} \sum_{i,j} \sigma_{ij}\|u_{ij}-v_{ij}\|_2 Ri,ti,Xjmini,j∑σij∥uij−vij∥2 其中当点XjX_jXj在相机PiP_iPi中有投影时σij=1\sigma_{ij}=1σij=1,否则为σij=0\sigma_{ij}=0σij=0。 到此我们就得到了BA优化模型的数学形式了。 接下来就应该开始计算了! 既然是优化模型,那自然就应该用各种优化算法来进行计算了。这里先小小的剧透一下,BA现在基本都是利用LM(Levenberg-Marquardt)算法并在此基础上利用BA模型的稀疏性质来进行计算的,LM算法是最速下降法(梯度下降法)和Gauss-Newton的结合体,至于是怎么结合的接下来就来慢慢介绍了。 提前给定 f(x)=∑i,jσij∥uij−vij∥22\mathbf{f(x)} = \sum_{i,j} \sigma_{ij}\|u_{ij}-v_{ij}\| _2^2 f(x)=i,j∑σij∥uij−vij∥22 为什么用平方呢??还不是因为不用计算平方根了嘛! 最速下降法 如果你对梯度比较熟悉的话,那你应该知道梯度方向是函数上升最快的方向,而此时我们需要解决的问题是让函数最小化。你应该想到了,那就顺着梯度的负方向去迭代寻找使函数最小的变量值就好了嘛。梯度下降法就是用的这种思想,用数学表达的话大概就是这样 xk=xk−1−λ∇f(xk−1)x_k = x_{k-1}-\lambda \nabla \mathbf{f(x_{k-1})} xk=xk−1−λ∇f(xk−1) 其中λ\lambdaλ为步长。 最速下降法保证了每次迭代函数都是下降的,在初始点离最优点很远的时候刚开始下降的速度非常快,但是最速下降法的迭代方向是折线形的导致了收敛非常非常的慢。 Newton型方法 现在先回顾一下中学数学,给定一个开口向上的一元二次函数,如何知道该函数何处最小?这个应该很容易就可以答上来了,对该函数求导,导数为000处就是函数最小处。 Newton型方法也就是这种思想,首先将函数利用泰勒展开到二次项: f(x+δx)≈φ(δx)=f(x)+∇f(x)δx+12δxTH(x)δx\mathbf{f(x + \delta x) \approx \varphi(\delta x) = f(x) + \nabla f(x)\delta x + \frac{1}{2}\delta x^T H(x) \delta x} f(x+δx)≈φ(δx)=f(x)+∇f(x)δx+21δxTH(x)δx H\mathbf{H}H为Hessian矩阵,是二次偏导矩阵。 也就是说Newton型方法将函数局部近似成一个二次函数进行迭代,然后令x\mathbf{x}x在δx\mathbf{\delta x}δx方向上迭代直至收敛,接下来自然就对这个函数求导了: φ′(δx)=∇f(x)+Hδx=0\mathbf{\varphi'(\delta x) = \nabla f(x) + H \delta x = 0 } φ′(δx)=∇f(x)+Hδx=0 ⟹δx=−H−1∇f(x)\Longrightarrow \mathbf{ \delta x = -H^{-1}\nabla f(x) } ⟹δx=−H−1∇f(x) Newton型方法收敛的时候特别快,尤其是对于二次函数而言一步就可以得到结果。但是该方法有个最大的缺点就是Hessian矩阵计算实在是太复杂了,并且Newton型方法的迭代并不像最速下降法一样保证每次迭代都是下降的。 Gauss-Newton方法 既然Newton型方法计算Hessian矩阵太困难了,那有没有什么方法可以不计算Hessian矩阵呢?在范数符号内将eee一阶泰勒展开,我们可以得到 f(x+δx)≈∥e+J(x)δx∥22=δxTJTJδx+2eTJδx+eTe\begin{aligned} \mathbf{f(x + \delta x)} & \approx \mathbf{\| e + J(x) \delta x \|} _2^2\\ & = \mathbf{\delta x^T J^T J \delta x + 2e^T J \delta x + e^Te} \end{aligned} f(x+δx)≈∥e+J(x)δx∥22=δxTJTJδx+2eTJδx+eTe 其中J\mathbf{J}J为Jacobi矩阵,函数eee对xxx求一次偏导而来,梯度也是对向量函数求一次偏导而来。将标量考虑为1×11 \times 11×1的矩阵,将向量考虑为n×1n \times 1n×1的矩阵,其实这些求导都是求Jacobi矩阵。此时不需要计算Hessian矩阵。 同样,二次函数导数为0时取极值,则有 JTJδx=−JTe\mathbf{J^{T} J \delta x = - J^{T} e} JTJδx=−JTe x=x+δx\mathbf{x = x + \delta x} x=x+δx 由此x\mathbf{x}x在δx\mathbf{\delta x}δx方向上迭代直至∥e∥\| e \|∥e∥最小。 同时我们可以发现一个等式,会在下面的LM方法中用到 ∇f(x)=2JTe\mathbf{\nabla f(x)} = 2\mathbf{J^Te} ∇f(x)=2JTe Gauss-Newton方法就避免了求Hessian矩阵,并且在收敛的时候依旧很快。但是依旧无法保证每次迭代的时候函数都是下降的(虽然从上式可以推导出来是下降方向,但是步长可能过长)。 LM方法 LM方法就是在以上方法基础上的改进,通过参数的调整使得优化能在最速下降法和Gauss-Newton法之间自由的切换,在保证下降的同时也能保证快速收敛。 Gauss-Newton最后需要求解的方程为 JTJδx=−JTe\mathbf{J^{T} J \delta x = - J^{T} e} JTJδx=−JTe LM算法在此基础上做了更改,变成了 (JTJ+λI)δx=−JTe\mathbf{(J^{T} J + \lambda I) \delta x = - J^{T} e} (JTJ+λI)δx=−JTe 通过参数λ\lambdaλ的调节在最速下降法和Gauss-Newton法之间切换。做个不很数学的直观分析吧,当λ\lambdaλ很小时,显然和Gauss-Newton法是一样的;当λ\lambdaλ很大时,就变成了这样: λIδx=−JTe\mathbf{\lambda I \delta x = - J^T e} λIδx=−JTe ⟹δx=−λ−1JTe\Longrightarrow \mathbf{\delta x = - \lambda^{-1}J^T e} ⟹δx=−λ−1JTe 然后再看看前面的最速下降法? 这里还存在一个问题,当λ\lambdaλ取某个值的时候可能会导致JJ+λI\mathbf{J^J + \lambda I}JJ+λI不可逆,所以这里变成了 (JTJ+λdiag(JTJ))δx=−JTe\mathbf{(J^{T} J + \lambda diag(J^T J)) \delta x = - J^{T} e} (JTJ+λdiag(JTJ))δx=−JTe 其实LM算法的具体形式就笔者看到的就有很多种,但是本质都是通过参数λ\lambdaλ在最速下降法和Gauss-Newton法之间切换。这里选用的是维基百科上的形式。 LM算法就由此保证了每次迭代都是下降的,并且可以快速收敛。 还没完呢!别忘了还要解方程 LM算法主体就是一个方程的求解,也是其计算量最大的部分。当其近似于最速下降法的时候没有什么好讨论的,但是当其近似于Gauss-Newton法的时候,这个最小二乘解的问题就该好好讨论一下了。以下的讨论就利用Gauss-Newton的形式来求解。 稠密矩阵的最小二乘解 对于形如Ax=bAx=bAx=b的超定参数方程而言,有很多求解方式,伪逆、QR分解、SVD等等,这里不展开谈,想具体了解的可以去查阅矩阵理论相关资料。这些方式都有一个共同的特点,我们都是将AAA看作一般的稠密矩阵,主要得到的解自然非常鲁棒,都是计算量却是和维数的三次方成正比(O(n3)O(n^3)O(n3))。面对BA这种超大规模的优化似乎有点不太实用。 稀疏矩阵的Cholesky分解 稠密矩阵计算起来那么复杂,如果是稀疏矩阵的话利用其稀疏的性质可以大幅减少计算量,对于稀疏矩阵的Cholesky分解就是这样。其分解形式为一个上三角矩阵的转置乘上自身: A≈RTRA \approx R^T R A≈RTR RTRx=bR^TRx = b RTRx=b x=R−1R−Tbx = R^{-1}R^{-T}b x=R−1R−Tb 为什么说我们的矩阵是稀疏的 用一个非常简单的例子来解释吧,考虑有两个相机矩阵P1P_1P1和P2P_2P2、两个空间点X1X_1X1和X2X_2X2,其中X1X_1X1只在P2P_2P2中有投影,X2X_2X2在两个相机(或视角)中都有投影。令优化函数为f(P1,P2,X1,X2)f(P_1,P_2,X_1,X_2)f(P1,P2,X1,X2),此时Jacobi矩阵为 J=[∂f∂P1∂f∂X2∂f∂P2∂f∂X1∂f∂P2∂f∂X2]\mathbf{J} = \left[ \begin{array}{cc|cc} \frac{\partial f}{\partial P_1} & & & \frac{\partial f}{\partial X_2} \\\\ & \frac{\partial f}{\partial P_2} & \frac{\partial f}{\partial X_1} & \\\\ & \frac{\partial f}{\partial P_2} & & \frac{\partial f}{\partial X_2} \end{array} \right] J=⎣⎢⎢⎢⎢⎢⎡∂P1∂f∂P2∂f∂P2∂f∂X1∂f∂X2∂f∂X2∂f⎦⎥⎥⎥⎥⎥⎤ 考虑相机位置(图像数量)和空间点都非常多的情况,不难想象Jacobi矩阵不光是一个稀疏矩阵而且还可以写成形如[A∣B][A|B][A∣B]的分块矩阵。接下来就该利用这些性质正式进入计算了! 开始计算吧! 现在再回到Gauss-Newton最后的超定参数方程吧。既然Jacobi矩阵可以分块那我们就先分块,分块可以有效降低需要计算的矩阵的维度并以此减少计算量。 JTJδx=−JTe\mathbf{J^{T} J \delta x = - J^{T} e} JTJδx=−JTe J=[A∣B]\mathbf{J}=[A|B] J=[A∣B] [A∣B]T[A∣B]δx=−[A∣B]Te[A|B]^T[A|B]\mathbf{\delta x}=-[A|B]^T e [A∣B]T[A∣B]δx=−[A∣B]Te [ATAATBBTABTB][δxAδxB]=−[ATeBTe]\begin{bmatrix} A^TA & A^TB \\\\ B^TA & B^TB \end{bmatrix} \begin{bmatrix} \mathbf{\delta x}_A \\\\ \mathbf{\delta x}_B \end{bmatrix} = - \begin{bmatrix} A^T e \\\\ B^T e \end{bmatrix} ⎣⎡ATABTAATBBTB⎦⎤⎣⎡δxAδxB⎦⎤=−⎣⎡ATeBTe⎦⎤ [UWWTV][δxAδxB]=[eAeB]\begin{bmatrix} U & W \\\\ W^T & V \end{bmatrix} \begin{bmatrix} \mathbf{\delta x}_A \\\\ \mathbf{\delta x}_B \end{bmatrix} = \begin{bmatrix} e _A \\\\ e _B \end{bmatrix} ⎣⎡UWTWV⎦⎤⎣⎡δxAδxB⎦⎤=⎣⎡eAeB⎦⎤ [I−WV−10I][UWWTV][δxAδxB]=[I−WV−10I][eAeB]\begin{bmatrix} I & -WV^{-1} \\\\ 0 & I \end{bmatrix} \begin{bmatrix} U & W \\\\ W^T & V \end{bmatrix} \begin{bmatrix} \mathbf{\delta x}_A \\\\ \mathbf{\delta x}_B \end{bmatrix} = \begin{bmatrix} I & -WV^{-1} \\\\ 0 & I \end{bmatrix} \begin{bmatrix} e _A \\\\ e _B \end{bmatrix} ⎣⎡I0−WV−1I⎦⎤⎣⎡UWTWV⎦⎤⎣⎡δxAδxB⎦⎤=⎣⎡I0−WV−1I⎦⎤⎣⎡eAeB⎦⎤ [U−WV−1WT0WTV][δxAδxB]=[eA−WV−1eBeB]\begin{bmatrix} U-WV^{-1}W^T & 0 \\\\ W^T & V \end{bmatrix} \begin{bmatrix} \mathbf{\delta x}_A \\\\ \mathbf{\delta x}_B \end{bmatrix} = \begin{bmatrix} e _ A - WV^{-1}e _B \\\\ e _B \end{bmatrix} ⎣⎡U−WV−1WTWT0V⎦⎤⎣⎡δxAδxB⎦⎤=⎣⎡eA−WV−1eBeB⎦⎤ {(U−WV−1WT)δA=eA−WV−1eBWTδA+VδB=eB\begin{cases} (U-WV^{-1}W^T) \delta _A = e _ A - WV^{-1}e _B \\\\ W^T \delta _A + V \delta _B = e _B \end{cases} ⎩⎪⎨⎪⎧(U−WV−1WT)δA=eA−WV−1eBWTδA+VδB=eB 由此我们可以先求出δA\delta _AδA,然后代回求出δB\delta _BδB。其中U−WV−1WTU-WV^{-1}W^TU−WV−1WT被称为舒尔补(Schur complement)。分块降维之后的计算就可以利用稀疏的Cholesky分解进行计算了。 注意事项! 以上就基本将BA的整个过程进行了介绍,当然这只是最基础的思路,接下来一些遗漏点进行补充。 李群及李代数 不知道有没有人注意到,在优化迭代的过程中,我们求的值为δx\delta xδx,然后利用x+δxx + \delta xx+δx来更新xxx的值。这里就应该出现一个问题了,对于空间点的坐标和平移向量这么处理自然没有什么问题,但是对于旋转矩阵呢?难道用R+δRR + \delta RR+δR来更新RRR的值吗?好像不太对吧。 对于旋转矩阵RRR而言是不存在加法的,按理讲应该用RδRR \delta RRδR来更新RRR的值,但是优化算法的迭代过程又不能是乘法,这就出现了矛盾。 这里旋转矩阵及相关运算属于李群,此时将旋转矩阵变换到其对应的李代数上进行计算,然后再变回李群。打个不是那么恰当的比方,在计算卷积的时候常常通过傅里叶变换计算乘积然后再反变换回来就是要求的卷积了,这个也是转换到李代数上计算然后再变回李群。具体的推导可以参看李群及李代数相关内容。 协方差矩阵 在我们的推导中是求解方程 JTJδx=−JTe\mathbf{J^{T} J \delta x = - J^{T} e} JTJδx=−JTe 但常常加入信息矩阵(协方差矩阵的逆),令求解方程变为 JTΣx−1Jδx=−JTΣx−1e\mathbf{J^{T} \Sigma_x^{-1} J \delta x = - J^{T} \Sigma_x^{-1} e} JTΣx−1Jδx=−JTΣx−1e 其中Σx\mathbf{\Sigma_x}Σx为协方差矩阵,令其为分块对角阵表示所有观测向量都不相关。 参考文献 Triggs B, McLauchlan P F, Hartley R I, et al. Bundle adjustment—a modern synthesis[C]//International workshop on vision algorithms. Springer Berlin Heidelberg, 1999: 298-372. Hartley R, Zisserman A. Multiple view geometry in computer vision[M]. Cambridge university press, 2003. Barfoot T D. STATE ESTIMATION FOR ROBOTICS[J]. 2017.]]></content>
<tags>
<tag>BA</tag>
</tags>
</entry>
<entry>
<title><![CDATA[四元数及error-state kalman]]></title>
<url>%2Farchives%2F31646.html</url>
<content type="text"><![CDATA[四元数相关的基本推导,主要是扰动相关。然后记下了一些error-state kalman相关的公式。 不做具体说明时,都是右手系 四元数乘法 q⊗p≠p⊗qq \otimes p \neq p \otimes q q⊗p≠p⊗q 其中⊗\otimes⊗为四元数乘法,ppp和qqq为四元数,四元数乘法满足结合律和分配律。四元数乘法为二元运算,可以写成两种等价的形式: q⊗p=[q]Lp=[p]Rqq \otimes p = {[q]}_L p = {[p]}_Rq q⊗p=[q]Lp=[p]Rq [q]L=qwI+[0−qvTqv[qv]×][q]_L = q_wI + \begin{bmatrix} 0 & -q_v^T \\ q_v & [q_v]_{\times} \end{bmatrix} [q]L=qwI+[0qv−qvT[qv]×] [q]R=qwI+[0−qvTqv−[qv]×][q]_R = q_wI + \begin{bmatrix} 0 & -q_v^T \\ q_v & -[q_v]_{\times} \end{bmatrix} [q]R=qwI+[0qv−qvT−[qv]×] 其中q=(qw,qv)q = (q_w, q_v)q=(qw,qv),qwq_wqw为实数,qvq_vqv为其中虚数向量。 左手系和右手系在四元数计算上的区别: 右手系:ij=−ji=kij = -ji = kij=−ji=k, jk=−kj=ijk = -kj = ijk=−kj=i,ki=−ik=jki = -ik = jki=−ik=j 左手系:ij=−ji=−kij = -ji = -kij=−ji=−k,jk=−kj=−ijk = -kj = -ijk=−kj=−i,ki=−ik=−jki = -ik = -jki=−ik=−j Eigen中的四元数运算如果使用方法 coeffs()输出系数,输出的向量是按照(qv,qw)(q_v,q_w)(qv,qw)排列的。在Eigen中四元数的构造函数中,输入参数顺序是按照(qw,qv)(q_w,q_v)(qw,qv)排列的。所以想使用四元数乘法的时候可以构造四元数使用Eigen的四元数相乘函数来避免这些问题。但是想使用Eigen的四元数系数进行四元数乘法运算时(尤其对于论文里面经常提到的Ω\OmegaΩ矩阵),其形式为 [q]L=qwI+[[qv]×qv−qvT0][q]_L = q_wI +\begin{bmatrix}[q_v]_{\times} & q_v \\-q_v^T & 0\end{bmatrix}[q]L=qwI+[[qv]×−qvTqv0] [q]R=qwI+[−[qv]×qv−qvT0][q]_R = q_wI +\begin{bmatrix}-[q_v]_{\times} & q_v \\-q_v^T & 0\end{bmatrix}[q]R=qwI+[−[qv]×−qvTqv0] 当然计算结果自然是按照(qv,qw)(q_v,q_w)(qv,qw)排列的四元数 由于 q⊗r⊗p=[p]R[q]Lrq \otimes r \otimes p = [p]_R[q]_Lr q⊗r⊗p=[p]R[q]Lr q⊗r⊗p=[q]L[p]Rrq \otimes r \otimes p = [q]_L[p]_Rr q⊗r⊗p=[q]L[p]Rr 因此有[p]R[q]L=[q]L[p]R[p]_R[q]_L = [q]_L[p]_R[p]R[q]L=[q]L[p]R 扰动及求导 先交代一些东西 qAB⊗qBC=qACq_{AB} \otimes q_{BC} = q_{AC} qAB⊗qBC=qAC RABRBC=RACR_{AB} R_{BC} = R_{AC} RABRBC=RAC Δq=[112Δθ]+O(∥Δθ∥2)\Delta q = \begin{bmatrix} 1 \\ \frac{1}{2} \Delta \theta \end{bmatrix} + O \left( \| \Delta \theta \|^2 \right) Δq=[121Δθ]+O(∥Δθ∥2) ΔR=I+[Δθ]×+O(∥Δθ∥2)\Delta R = I + [\Delta \theta]_{\times} + O\left( \| \Delta \theta \|^2 \right) ΔR=I+[Δθ]×+O(∥Δθ∥2) 下文可能使用局部到全局坐标系的变换更容易理解 局部扰动 局部扰动也就是把扰动加在目前的坐标系数据下,然后再通过坐标系之间的关系变到其他坐标系下,具体形式为: q~=q⊗Δq\tilde{q} = q \otimes \Delta q q~=q⊗Δq R~=RΔR\tilde{R} = R \Delta R R~=RΔR 其扰动放在右边,其原因为qAB′=qAB⊗qBB′q_{AB'} = q_{AB} \otimes q_{BB'}qAB′=qAB⊗qBB′,可以满足先对局部数据进行扰动调整再通过坐标系关系变换到其他坐标系下。典型的应用为IMU预积分,具体形式为qGIk+1=qGIk⊗qIkIk+1q_{GI_{k+1}} = q_{GI_{k}} \otimes q_{I_{k}I_{k+1}}qGIk+1=qGIk⊗qIkIk+1。旋转矩阵形式类似。其对时间求导形式为: q˙=12Ω(ω)q=12q⊗ω\dot{q} = \frac{1}{2} \Omega(\omega) q = \frac{1}{2} q \otimes \omega q˙=21Ω(ω)q=21q⊗ω R˙=R[ω]×\dot{R} = R[\omega]_{\times} R˙=R[ω]× 其中ω\omegaω为局部角速度(当前坐标系的角速度),Ω(ω)\Omega(\omega)Ω(ω)为[ω]R[\omega]_R[ω]R(不要在意维数的问题)。具体推导如下: q˙=limΔt→0q(t+Δt)−q(t)Δt=limΔt→0q⊗Δq−qΔt=limΔt→0q⊗([1Δθ/2]−[10])Δt=limΔt→0q⊗([0Δθ/2])Δt=12q⊗[0ωL]R˙=limΔt→0R(t+Δt)−R(t)Δt=limΔt→0RΔR−RΔt=limΔt→0R(ΔR−I)Δt=limΔt→0R[Δθ]×Δt=R[ω]×\begin{aligned} \dot{q} &= \lim_{\Delta t \rightarrow 0} \frac{q(t + \Delta t) - q(t)}{\Delta t} \\ &= \lim_{\Delta t \rightarrow 0} \frac{q \otimes \Delta q - q}{\Delta t} \\ &= \lim_{\Delta t \rightarrow 0} \frac{q \otimes \left(\begin{bmatrix}1 \\ \Delta \theta/2 \end{bmatrix} - \begin{bmatrix} 1 \\ 0 \end{bmatrix} \right)}{\Delta t} \\ &= \lim_{\Delta t \rightarrow 0} \frac{q \otimes \left(\begin{bmatrix}0 \\ \Delta \theta/2 \end{bmatrix} \right)}{\Delta t} \\ &= \frac{1}{2} q \otimes \begin{bmatrix} 0 \\ \omega_L \end{bmatrix} \\ \\ \dot{R} &= \lim_{\Delta t \rightarrow 0} \frac{R(t + \Delta t) - R(t)}{\Delta t} \\ &= \lim_{\Delta t \rightarrow 0} \frac{R \Delta R - R}{\Delta t} \\ &= \lim_{\Delta t \rightarrow 0} \frac{R (\Delta R - I)}{\Delta t} \\ &= \lim_{\Delta t \rightarrow 0} \frac{R [\Delta \theta]_{\times}} {\Delta t} \\ &= R[\omega]_{\times} \end{aligned} q˙R˙=Δt→0limΔtq(t+Δt)−q(t)=Δt→0limΔtq⊗Δq−q=Δt→0limΔtq⊗([1Δθ/2]−[10])=Δt→0limΔtq⊗([0Δθ/2])=21q⊗[0ωL]=Δt→0limΔtR(t+Δt)−R(t)=Δt→0limΔtRΔR−R=Δt→0limΔtR(ΔR−I)=Δt→0limΔtR[Δθ]×=R[ω]× 全局扰动 和局部扰动相对,也就是先通过坐标系的关系变换到其他坐标系下再进行扰动。具体形式为: q~=Δq⊗q\tilde{q} = \Delta q \otimes q q~=Δq⊗q R~=ΔRR\tilde{R} = \Delta R R R~=ΔRR 由于qA′B=qA′AqABq_{A'B} = q_{A'A}q_{AB}qA′B=qA′AqAB,也就是扰动没有加在局局部下,是加在了坐标系之间的变换这种全局关系中。其时间导数为: q˙=12[ω]Lq=12ω⊗q\dot{q} = \frac{1}{2} [\omega]_L q = \frac{1}{2} \omega \otimes q q˙=21[ω]Lq=21ω⊗q R˙=[ω]×R\dot{R} = [\omega]_{\times} R R˙=[ω]×R 其中ω\omegaω是全局角速度,也就是要目标坐标系下的角速度,不是当前坐标系下的角速度。 其他的求导 ∂(q⊗a⊗q∗)∂θ=∂(Ra)∂θ=lim∂θ→0R{θ+∂θ}a−Ra∂θ=lim∂θ→0(I+[∂θ]×)Ra−Ra∂θ=−[Ra]×∂(q⊗a⊗q∗)∂q=∂(Ra)∂q=2[qwa+qv×a∣qvTaI+qvaT−aqvT−qw[a]×]\begin{aligned} \frac{\partial (q \otimes a \otimes q^*)}{\partial \theta} &= \frac{\partial (Ra)}{\partial \theta} \\ &= \lim_{\partial \theta \rightarrow 0} \frac{R\{ \theta + \partial \theta \}a - Ra}{\partial \theta} \\ &= \lim_{\partial \theta \rightarrow 0} \frac{(I + [\partial \theta]_{\times})Ra - Ra}{\partial \theta} \\ &= -\left[ Ra \right]_{\times} \\ \\ \frac{\partial (q \otimes a \otimes q^*)}{\partial q} &= \frac{\partial (Ra)}{\partial q} \\ &= 2 \left[ q_w a + q_v \times a | q_v^T a I + q_v a^T - a q_v^T - q_w [a]_{\times} \right] \end{aligned} ∂θ∂(q⊗a⊗q∗)∂q∂(q⊗a⊗q∗)=∂θ∂(Ra)=∂θ→0lim∂θR{θ+∂θ}a−Ra=∂θ→0lim∂θ(I+[∂θ]×)Ra−Ra=−[Ra]×=∂q∂(Ra)=2[qwa+qv×a∣qvTaI+qvaT−aqvT−qw[a]×] 旋转矩阵与四元数关系 (不要在意维数的细节问题) q⊗r⊗q∗=Rrq \otimes r \otimes q^* = Rr q⊗r⊗q∗=Rr R4=[q∗]R[q]L=[q]L[q∗]R=[100R] R_4 = [q^*]_R[q]_L = [q]_L[q^*]_R = \begin{bmatrix} 1 & 0 \\ 0 & R \end{bmatrix} R4=[q∗]R[q]L=[q]L[q∗]R=[100R] 系统运动(局部) 连续时间 论文里面多是给的连续时间的推导结果 true state: p˙t=vt\dot{p}_t = v_t p˙t=vt v˙t=Rt(am−abt−an)+gt\dot{v}_t = R_t(a_m - a_{bt} - a_n) + g_t v˙t=Rt(am−abt−an)+gt q˙t=12qt⊗(ωm−ωbt−ωn)\dot{q}_t = \frac{1}{2} q_t \otimes (\omega_m - \omega_{bt} - \omega_n) q˙t=21qt⊗(ωm−ωbt−ωn) a˙bt=aw\dot{a}_{bt} = a_w a˙bt=aw ω˙bt=ωw\dot{\omega}_{bt} = \omega _w ω˙bt=ωw g˙t=0\dot{g}_t = 0 g˙t=0 其中ama_mam与ωm\omega_mωm为测量值,abta_{bt}abt与ωbt\omega_{bt}ωbt为true bias,ana_nan与ωn\omega_nωn为噪声。 nominal state: p˙=v\dot{p} = v p˙=v v˙=R(am−ab)+g\dot{v} = R(a_m - a_b) + g v˙=R(am−ab)+g q˙=12q⊗(ωm−ωb)\dot{q} = \frac{1}{2} q \otimes (\omega_m - \omega_b) q˙=21q⊗(ωm−ωb) a˙b=0\dot{a}_b = 0 a˙b=0 ω˙b=0\dot{\omega}_b = 0 ω˙b=0 g˙=0\dot{g} = 0 g˙=0 error state: δp˙=δv\dot{\delta p} = \delta v δp˙=δv δv˙=−R[am−ab]×δθ−Rδab+δg−Ran\dot{\delta v} = -R[a_m - a_b]_{\times} \delta \theta - R \delta a_b + \delta g - Ra_n δv˙=−R[am−ab]×δθ−Rδab+δg−Ran δθ˙=−[ωm−ωb]×δθ−δωb−ωn\dot{\delta \theta} = -[\omega_m - \omega_b]_{\times}\delta\theta - \delta \omega_b - \omega_n δθ˙=−[ωm−ωb]×δθ−δωb−ωn δab˙=aw\dot{\delta a_b} = a_w δab˙=aw ωb˙=ωw\dot{\omega_b} = \omega_w ωb˙=ωw δg˙=0\dot{\delta g} = 0 δg˙=0 TODO:δv˙\dot{\delta v}δv˙和δθ˙\dot{\delta \theta}δθ˙的推导有时间补上 离散时间 其实和连续时间没差多少,就是把导数变成已经积分好的x←x+x˙Δtx \leftarrow x + \dot{x}\Delta tx←x+x˙Δt。数值积分方法还可以使用其他的,之后的具体形式都需要重新推导,这里是示例。 nominal state: p←p+vΔt+12(R(am−ab)+g)Δt2p \leftarrow p + v\Delta t + \frac{1}{2} (R(a_m - a_b) + g)\Delta t^2 p←p+vΔt+21(R(am−ab)+g)Δt2 v←v+(R(am−ab)+g)Δtv \leftarrow v + (R(a_m - a_b) + g) \Delta t v←v+(R(am−ab)+g)Δt q←q⊗q{(ωm−ωb)Δt}q \leftarrow q \otimes q\{ (\omega_m - \omega_b)\Delta t \} q←q⊗q{(ωm−ωb)Δt} ab←aba_b \leftarrow a_b ab←ab ωb←ωb\omega_b \leftarrow \omega_b ωb←ωb g←gg \leftarrow g g←g error state: δp←δp+δvΔt\delta p \leftarrow \delta p + \delta v \Delta t δp←δp+δvΔt δv←δv+(−R[am−ab]×δθ−Rδab+δg)Δt+vi\delta v \leftarrow \delta v + (-R[a_m-a_b]_{\times}\delta \theta - R\delta a_b + \delta g) \Delta t + v_i δv←δv+(−R[am−ab]×δθ−Rδab+δg)Δt+vi δθ←RT{(ωm−ωb)Δt}δθ−δωbΔt+θi\delta \theta \leftarrow R^T\{ (\omega_m - \omega_b) \Delta t \}\delta \theta - \delta \omega_b \Delta t + \theta_i δθ←RT{(ωm−ωb)Δt}δθ−δωbΔt+θi δab←δab+ai\delta a_b \leftarrow \delta a_b + a_i δab←δab+ai δωb←δωb+ωi\delta \omega_b \leftarrow \delta \omega_b + \omega_i δωb←δωb+ωi δg←δg\delta g \leftarrow \delta g δg←δg FFF矩阵可以通过以上内容很容易推导出来,具体形式懒得写了。 error state的期望为000,在预测的过程中就是为了计算协方差矩阵,改变当前期望分布]]></content>
<tags>
<tag>math</tag>
<tag>vio</tag>
<tag>slam</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MSCKF相关问题推导]]></title>
<url>%2Farchives%2F4392.html</url>
<content type="text"><![CDATA[MSCKF文章中没有给出的推导和一些疑问。 paper链接 这个逼怕是用的左手系哦!!!!!! 关于状态向量 这个逼的状态向量里面的四元数是从全局坐标系到IMU坐标系变换的GIq^I_GqGIq,也就是IMU坐标系中全局坐标系的方向,也就是全局坐标系中IMU坐标系方向的逆 方向的true state为:qˉ=δqˉ⊗qˉ^\bar{q} = \delta \bar{q} \otimes \hat{\bar{q}}qˉ=δqˉ⊗qˉ^ 导数为:q˙=12ω⊗q\dot{q} = \frac{1}{2} \omega \otimes qq˙=21ω⊗q,文章中的Ω(ω)\Omega(\omega)Ω(ω)证明了这个是左手系 Jacobian矩阵 相机状态的Jacobian矩阵计算,单位四元数的逆就是其共轭四元数 error state角度的Jacobian,导数第二步推导使用分配律,RICR^C_IRIC从IMU坐标变换到相机坐标 qˉ^GC=qˉIC⊗qˉ^GI\hat{\bar{q}}^C_G = \bar{q}^C_I \otimes \hat{\bar{q}}^I_G qˉ^GC=qˉIC⊗qˉ^GI δqˉGC⊗qˉ^GC=qˉIC⊗δqˉGI⊗qˉ^GI\delta \bar{q}^C_G \otimes \hat{\bar{q}}^C_G = \bar{q}^C_I \otimes \delta \bar{q}^I_G \otimes \hat{\bar{q}}^I_G δqˉGC⊗qˉ^GC=qˉIC⊗δqˉGI⊗qˉ^GI δqˉGC⊗qˉIC=qˉIC⊗δqˉGI\delta \bar{q}^C_G \otimes \bar{q}^C_I = \bar{q}^C_I \otimes \delta \bar{q}^I_G δqˉGC⊗qˉIC=qˉIC⊗δqˉGI δqˉGC=qˉIC⊗δqˉGI⊗qˉIC∗\delta \bar{q}^C_G = \bar{q}^C_I \otimes \delta \bar{q}^I_G \otimes {\bar{q}^C_I}^* δqˉGC=qˉIC⊗δqˉGI⊗qˉIC∗ [0,0,0,1]+12[δθC,0]=qˉIC⊗12[δθI,0]⊗qˉIC∗+[0,0,0,1][0, 0, 0, 1] + \frac{1}{2}[\delta \theta ^C, 0] = \bar{q}^C_I \otimes \frac{1}{2}[\delta \theta ^I, 0] \otimes {\bar{q}^C_I}^* + [0, 0, 0, 1] [0,0,0,1]+21[δθC,0]=qˉIC⊗21[δθI,0]⊗qˉIC∗+[0,0,0,1] ∂δθC∂δθI=RIC\frac{\partial \delta \theta ^C}{\partial \delta \theta ^I} = R^C_I ∂δθI∂δθC=RIC error state坐标的Jacobian(我觉得文章里面推的不对!) p^CG=p^IG+RGITpCI\hat{p}^G_C = \hat{p}^G_I + {R^I_G}^T p^I_C p^CG=p^IG+RGITpCI δpCG+pCG=δpIG+pIG+(δRGIRGI)TpCI猜测文章中是:δRGITRGIT\delta p^G_C + p^G_C = \delta p^G_I + p^G_I + \left( \delta R^I_G R^I_G \right)^T p^I_C \quad \text{猜测文章中是:} {\delta R^I_G}^T {R^I_G}^T δpCG+pCG=δpIG+pIG+(δRGIRGI)TpCI猜测文章中是:δRGITRGIT δpCG=δpIG+RGITδRGITpCI−RGITpCI\delta p^G_C = \delta p^G_I + {R^I_G}^T {\delta R^I_G}^T p^I_C - {R^I_G}^T p^I_C δpCG=δpIG+RGITδRGITpCI−RGITpCI δpCG=δpIG−RGIT[δθI]×pCI由于:δRGI=I+[δθI]×+...\delta p^G_C = \delta p^G_I - {R^I_G}^T \left[\delta \theta ^I \right]_{\times} p^I_C \quad \text{由于:} \delta R^I_G = I + \left[\delta \theta ^I \right]_{\times}+ ... δpCG=δpIG−RGIT[δθI]×pCI由于:δRGI=I+[δθI]×+... δpCG=δpIG+RGIT[pCI]×δθI\delta p^G_C = \delta p^G_I + {R^I_G}^T \left[ p^I_C \right]_{\times} \delta \theta ^I δpCG=δpIG+RGIT[pCI]×δθI ∂δpCG∂δpIG=I\frac{\partial \delta p^G_C}{\partial \delta p^G_I} = I ∂δpIG∂δpCG=I ∂δpCG∂δθI=RGIT[pCI]×\frac{\partial \delta p^G_C}{\partial \delta \theta ^I} = {R^I_G}^T \left[p^I_C \right]_{\times} ∂δθI∂δpCG=RGIT[pCI]× Update 根据残差方程 r=HX~+noiser = H \widetilde{X} + noise r=HX+noise 计算error state,先计算特征点重投影误差和Jacobian矩阵,然后再计算error state 计算特征点坐标 多个相机由feature约束,map: feature -> {camera} 特征点的像素 z=1Z[XY]+n z = \frac{1}{Z} \begin{bmatrix} X \\ Y \end{bmatrix} + n z=Z1[XY]+n 特征点在相机坐标系中的坐标有 pf=[XYZ]=R(pf−pC) p_f = \begin{bmatrix} X \\ Y \\ Z\end {bmatrix} = R(p_f - p_C) pf=⎣⎡XYZ⎦⎤=R(pf−pC) 使用最小二乘解得到特征点坐标 先使用首尾两帧三角交汇得到初始解,先通过像素坐标(归一化平面)获得投影方向(各自相机的坐标系)单位长度向量d1d1d1与d2d2d2,即 (u,v,f)→(uf,vf,1)→(u,v,f).normalize()(u,v,f) \rightarrow (\frac{u}{f}, \frac{v}{f}, 1) \rightarrow (u,v,f).normalize() (u,v,f)→(fu,fv,1)→(u,v,f).normalize() 然后获取相机C1C_1C1到C2C_2C2的旋转矩阵RRR与平移ttt,有 x1d1=t+RTx2d2x_1 d_1 = t + R^T x_2 d_2 x1d1=t+RTx2d2 t=d1x1−RTd2x2t = d_1 x_1 - R^T d_2 x_2 t=d1x1−RTd2x2 [d1−RTd2][x1x2]=t \begin{bmatrix} d_1 & - R^T d_2 \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} = t [d1−RTd2][x1x2]=t 则x1d1x_1 d_1x1d1为特征点在第一个相机的坐标系中的坐标,以此坐标为初始值再利用其他帧数据计算特征点坐标 逆深度参数: (u,v,f)→(u/f,v/f,1/f)(u,v,f) \rightarrow (u/f,v/f,1/f)(u,v,f)→(u/f,v/f,1/f),逆深度的逆深度是本身 由计算得到的坐标与相机参数得到重投影误差 计算误差Jacobian 重投影误差线性化有 ri(j)≃HXi(j)X~+Hfi(j)GP~fi+ni(j)r^{(j)}_i \simeq H^{(j)}_{X_i} \widetilde{X} + H^{(j)}_{f_i} {}^G\widetilde{P}_{f_i} + n^{(j)}_i ri(j)≃HXi(j)X+Hfi(j)GPfi+ni(j) 其中 HXi(j)=[02×1502×6⋯Ji(j)⌊CiX^fi×⌋−Ji(j)C(GCiqˉ^)⎵Jacobian wrt pose i⋯] H^{(j)}_{X_i} = \begin{bmatrix} 0_{2 \times 15} & 0_{2 \times 6} & \cdots & \underbrace{\begin{matrix} J^{(j)}_i \lfloor {}^{C_i}\hat{X}_{f_i} \times \rfloor & -J^{(j)}_i C({}_G^{C_i}\hat{\bar{q}}) \end{matrix}}_{Jacobian \ wrt \ pose \ i} & \cdots \end{bmatrix} HXi(j)=[02×1502×6⋯Jacobian wrt pose iJi(j)⌊CiX^fi×⌋−Ji(j)C(GCiqˉ^)⋯] 与 Hfi(j)=Ji(j)C(GCiqˉ^)H^{(j)}_{f_i} = J^{(j)}_i C({}_G^{C_i}\hat{\bar{q}}) Hfi(j)=Ji(j)C(GCiqˉ^) 且有 Ji(j)=∇Cip^fjzi(j)=1CiZ^j[10−CiX^jCiZ^j01−CiY^jCiZ^j] J^{(j)}_i = \nabla_{ {C_i}_{\hat{p}_{f_j} } } z^{(j)}_i = \frac{1}{C_i \hat{Z}_j} \begin{bmatrix} 1 & 0 & - \frac{C_i\hat{X}_j}{C_i\hat{Z}_j} \\ 0 & 1 & - \frac{C_i\hat{Y}_j}{C_i\hat{Z}_j} \end{bmatrix} Ji(j)=∇Cip^fjzi(j)=CiZ^j1⎣⎡1001−CiZ^jCiX^j−CiZ^jCiY^j⎦⎤ 将r(j)r^{(j)}r(j)投影到Hf(j)H^{(j)}_fHf(j)的左零空间,有 ro(j)≃ATHX(j)X~+ATn(j)=H0(j)X~(j)+n0(j)r_o^{(j)} \simeq A^T H^{(j)}_X \widetilde{X} + A^T n^{(j)} = H^{(j)}_0 \widetilde{X}^{(j)} + n^{(j)}_0 ro(j)≃ATHX(j)X+ATn(j)=H0(j)X(j)+n0(j) 由于Hf(j)H^{(j)}_fHf(j)是2Mj×32M_j \times 32Mj×3满秩矩阵,其左零空间维数为2Mj−32M_j - 32Mj−3 Update 由于Jacobian矩阵维度很高,因此考虑使用QR分解降低维度,然后使用标准的EKF更新状态和协方差矩阵]]></content>
<tags>
<tag>vio</tag>
<tag>slam</tag>
</tags>
</entry>
</search>