x k = f ( x k − 1 , u k , w k ) \qquad\qquad\qquad x_k = f(x_{k-1}, u_k, w_k) xk=f(xk−1,uk,wk)
参数化举例:
设机器人在二维平面中运动,则位姿可以由x,y,和转角来描述,即 x k = [ x , y , θ ] k T x_k = [x, y, \theta]_k^T xk=[x,y,θ]kT,同时机器人身上的编码器等传感器可得 u k = [ Δ x , Δ y , Δ θ ] k T u_k = [\Delta x, \Delta y, \Delta \theta]_k^T uk=[Δx,Δy,Δθ]kT,则带入上述运动方程可得:
[ x y θ ] k = [ x y θ ] k − 1 + [ Δ x Δ y Δ θ ] k + w k \begin{bmatrix} x\\y\\ \theta \end{bmatrix}_k= \begin{bmatrix} x\\y\\ \theta \end{bmatrix}_{k-1} + \begin{bmatrix} \Delta x\\\Delta y\\\Delta \theta \end{bmatrix}_k + w_k xyθ k= xyθ k−1+ ΔxΔyΔθ k+wk
z k , j = h ( y j , x k , v k , j ) \qquad\qquad\qquad z_{k,j} = h(y_j, x_k, v_{k, j}) zk,j=h(yj,xk,vk,j)
参数化举例:
设机器人装有一个2D激光雷达,能读到距离 r \;r r 和 夹角 θ \; \theta θ 两个值,在2D世界中,记一个landmark(路标点)为 y = [ p x , p y ] T \;y = [p_x, p_y]^T y=[px,py]T 观测数据为 z = [ r , ϕ ] T \; z = [r, \phi]^T z=[r,ϕ]T, 则观测方程可如下:
[ r ϕ ] = [ ( p x − x ) 2 + ( p y − y ) 2 arctan ( p y − y p x − x ) ] + v \begin{bmatrix} r\\ \phi \end{bmatrix} = \begin{bmatrix} \sqrt{(p_x - x)^2 + (p_y - y)^2} \\ \arctan(\frac{p_y - y}{p_x-x}) \end{bmatrix} + v [rϕ]=[(px−x)2+(py−y)2arctan(px−xpy−y)]+v
对视觉SLAM而言,观测方程就是 “对路标点拍摄后,得到了图
像的(具体是图像中的像素)” 的过程
知道运动测量的数据 u \;u u,传感器的数据 z \; z z ,求解定位 x \; x x 和建图 y \; y y 的问题
⇓ ⇓ ⇓ \qquad \qquad \qquad \qquad \qquad \Downarrow \Downarrow \Downarrow ⇓⇓⇓
状态估计问题:带有噪声的数据 ⟹ \; \Longrightarrow\; ⟹内部隐藏的状态变量
噪声: 分为 G u a s s i a n Guassian Guassian 和 N o n − G u a s s i a n Non-Guassian Non−Guassian
两个方程: 分为 L i n e a r Linear Linear 和 N o n − L i n e a r Non-Linear Non−Linear
**线性高斯系统(LG)**是无偏的(样本均值近似等于总体均值),可以用KF求解;
非线性非高斯(NLNG)系统,现在主要用EKF->粒子滤波器->图优化(时间顺序),图优化占资源,且效果明显好,优先选择。
a 和 a ‘ a和a` a和a‘分别是两个坐标系下的同一个点,它们在各自空间用坐标和基底表示如下:
[ e 1 e 2 e 3 ] [ a 1 a 2 a 3 ] = [ e 1 ‘ e 2 ‘ e 3 ‘ ] [ a 1 ‘ a 2 ‘ a 3 ‘ ] \begin{bmatrix} e_1&e_2&e_3 \end{bmatrix}\begin{bmatrix} a_1\\a_2\\a_3 \end{bmatrix}= \begin{bmatrix} e_1^`&e_2^`&e_3^`\end{bmatrix}\begin{bmatrix} a_1^`\\a_2^`\\a_3^`\end{bmatrix} [e1e2e3] a1a2a3 =[e1‘e2‘e3‘] a1‘a2‘a3‘
变换如下:
中间的 R \;R R 称为旋转矩阵,是行列式为1的正交阵,定义 R \;R R 如下:
S O ( n ) \qquad \qquad \qquad SO(n) SO(n) = { R ∈ R n x n ∣ R R T = I , d e t ( R ) = 1 R \in \R^{nxn} | RR^T=I, det(R)=1 R∈Rnxn∣RRT=I,det(R)=1}
解释: S O ( n ) SO(n) SO(n)是特殊正交群(Special Orthogonal Group)。
则带上平移量的变换为:
a ′ = R a + t a'=Ra + t a′=Ra+t 相反旋转为: a = R T a ′ − t = R − 1 a ′ − t a = R^T a' -t= R^{-1}a'-t a=RTa′−t=R−1a′−t
T T T(Transform matrix)
避免如上表示多次变换的形式过长,引入变换矩阵T,如 b = T 1 a c = T 2 b ⟹ c = T 2 T 1 a \quad b=T_1a\quad c=T_2b \Longrightarrow c=T_2T_1a b=T1ac=T2b⟹c=T2T1a
它可以定义如下(引入齐次坐标后):
S E ( 3 ) \qquad \qquad \qquad SE(3) SE(3) = { T = [ R t 0 T 1 ] ∈ R 4 x 4 ∣ R ∈ S O ( 3 ) , t ∈ R 3 T= \begin{bmatrix} \quad R&t&\\\quad0^T&1 \end{bmatrix} \in \R^{4x4} | R \in SO(3), t \in \R^3 T=[R0Tt1]∈R4x4∣R∈SO(3),t∈R3}
同样 T − 1 \quad T^{-1} \quad T−1 表示一个反向的变换,不区分齐次坐标 a ~ \;\tilde{a}\; a~和非齐次坐标 a \;a\; a的区别,默认是符合我们运算的那种。
S O ( 3 ) SO(3) SO(3)用9个量表示旋转,正交且 d e t ( R ) det(R) det(R)为1是它的约束, S E ( 3 ) SE(3) SE(3)也是一样的。因此不够紧凑、约束条件对求解的限制等都成为了它的问题。
旋转向量: 一个三维向量,用一个旋转轴 n \; n n 和 旋转角 θ \; \theta θ 表示.
变换矩阵 \;\;\; 在这种表示下 一个旋转向量+一个平移向量就可以表示。
转换关系: \;\; 通过罗德里格斯(Rodrigues’s Formula)公式
\qquad \qquad \qquad\qquad \qquad 旋转向量 ⟹ \Longrightarrow ⟹ 旋转矩阵
R = cos θ ⋅ I + ( 1 − cos θ ) n n T + sin θ ⋅ n \qquad \qquad \qquad R = \cos\theta \cdot I + (1-\cos\theta)nn^T+\sin\theta \cdot n R=cosθ⋅I+(1−cosθ)nnT+sinθ⋅n^
\qquad \qquad \qquad\qquad \qquad 旋转矩阵 ⟹ \Longrightarrow ⟹ 旋转向量
\qquad 直观,但是有奇异性—“万向锁”(Gimbal Lock),调试观察时可转为欧拉角,一般不用于计算,以下旋转是有先后顺序的,以一种为例(Z-Y-X),表示如下:
y a w yaw yaw - 偏航角 - 绕 Z Z Z 轴转
p i t c h pitch pitch - 俯仰角 - 绕旋转之后的 Y Y Y 轴转
r o l l roll roll - 偏航角 - 绕旋转之后的 X X X 轴转
Quaternion,消除欧拉角的奇异性且紧凑的表示法。以地球纬度 ± 9 0 o \pm 90^o ±90o为例,仅用两个坐标无法表示。四元数用四个数表示旋转。一个实部,三个虚部(或向量)。
q = q 0 + q 1 i + q 2 j + q 3 k \qquad \qquad\qquad \qquad\qquad \qquad\qquad q=q_0 +q_1i+q_2j+q_3k q=q0+q1i+q2j+q3k
满足
f ( x ) = { i 2 = j 2 = k 2 = − 1 i j = k , j i = − k j k = i , k j = − i k i = j , i k = − j f(x)= \begin{cases} i^2=j^2=k^2=-1 \\ij=k,ji=-k \\jk=i,kj=-i \\ki=j,ik=-j \end{cases} f(x)=⎩ ⎨ ⎧i2=j2=k2=−1ij=k,ji=−kjk=i,kj=−iki=j,ik=−j
用四元数表示旋转
\qquad 假设某个旋转是绕单位向量 n = [ n x , n y , n z ] T \;n = [nx , ny , nz ]^T n=[nx,ny,nz]T 进行了角度为 θ \;\theta θ 的旋转,那么这个旋转的四元数形式为:
q = [ cos θ 2 , n x sin θ 2 , n y sin θ 2 , n z sin θ 2 ] ( 1 ) \qquad\qquad\qquad\qquad q=\begin{bmatrix}\cos {\frac{\theta}{2}}, n_x\sin {\frac{\theta}{2}}, n_y\sin {\frac{\theta}{2}}, n_z\sin {\frac{\theta}{2}}\end{bmatrix} \qquad\qquad(1) q=[cos2θ,nxsin2θ,nysin2θ,nzsin2θ](1)
\qquad 要对点 p = [ x , y , z ] p=[x,y,z] p=[x,y,z]进行旋转,轴角法 n , θ n, \theta n,θ,表示这个旋转过程如下:
以上都是欧式变换,除此之外还存在如下几种SLAM中可能用到的变换
静态库.a和动态库.so:所有库都是一些函数打包后的集合,差别在于静态库每次被调用都会生成一个副本,而共享库则只有一个副本,更省空间。
库+头文件的意义: 库文件是一个压缩包,里头带有编译好的二进制函数。为了让别人(或
者自己)使用这个库,我们需要提供一个头文件,基本是这个库的使用说明啦。因此,对于库
的使用者,只要拿到了头文件和库文件,就可以调用这个库。
构建动态库:在CMakeLists.txt中,动态库的编译如下:参数1是名称,3是要被编译成的文件
add_library( slam_shared SHARED SLAM.cpp )
使用该库:如下:
add_executable(useHello useHello.cpp)
target_link_libraries(useHello slam_shared)
是一个很有意思的库,纯用头文件编写,没有.so,.a等库,所以调用的时候只要保证头文件的路径正确即可。在很多资料中头文件包含是这样的
,实际上这个路径不在我们的搜索路径中,三种解决办法:
/usr/include/eigen3/Eigen/下
将eigen3
改名为Eigen
,且将原Eigen
的文件全部上移一层到Eigen
下include_directories( "/usr/include/eigen3" )
即可