SLAM1
- 向量和坐标
- 欧式变换
- [[#欧式变换#基本旋转与平移|基本旋转与平移]]
- [[#欧式变换#齐次坐标|齐次坐标]]
- [[#欧式变换#Eigen|Eigen]]
- [[#欧式变换#旋转矢量|旋转矢量]]
基础数学知识补习
向量和坐标
任意向量a在基(e1,e2,e3)的坐标为
a=[e1,e2,e3]⎣⎡a1a2a3⎦⎤=a1e1+a2e2+a3e3
左手坐标系:
通过矢量积x×y 的结果沿z方向
x×y=∣∣e1a1b1e2a2b2e3a3b3∣∣=⎣⎡a2b3−a3b2a3b1−a1b3a1b2−a2b1⎦⎤=⎣⎡0a3a2−a30a1a2a10⎦⎤b=a∧b
欧式变换
旋转加平移——刚体运动
基本旋转与平移
[e1,e2,e3]⎣⎡a1a2a3⎦⎤=[e1′,e2′,e3′]⎣⎡a1′a2′a3′⎦⎤
刚体运动,坐标基矢与该坐标系下的矢量的内积是不变量。
旋转矩阵
⎣⎡a1a2a3⎦⎤=[e1,e2,e3]T[e1′,e2′,e3′]⎣⎡a1′a2′a3′⎦⎤=⎣⎡e1Te1′e2Te1′e3Te1′e1Te2′e2Te2′e3Te2′e1Te3′e2Te3′e3Te3′⎦⎤=Ra′
两个坐标系的基的内积构成旋转矩阵,两个向量的积等于其夹角的余弦值也称为方向余弦矩阵。他是行列式为1的正交矩阵。n维旋转矩阵构成的集合为一个特殊正交群(Special Orthogonal Group)。转置矩阵为原矩阵的相反旋转。
SO(n)=R∈Rn×n∣RRT=I,det(R)=1
平移
在原矢量上加上一个向量即可
欧式坐标变换
a′=Ra+t
坐标系的下表从右向左读,如下式中为从2到1的旋转矩阵
a1=R12a2+t12
齐次坐标
[a′1]=[R0Tt1][a1]=T[a1]
在每一个三维向量的末尾添加1,就可以变成齐次坐标计算。T为变换矩阵。
b=T1a,c=T2b,c=T1T2a
特殊欧式群SE()(Special Euclidean Group)
该矩阵的逆为
T−1=[RT0T−RTt1]
Eigen
安装eigen
1 2 3 4 5 6
| git clone https://github.com/eigenteam/eigen-git-mirror.git cd eigen-git-mirror mkdir build cd build cmake .. sudo make install
|
基本使用方法
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
|
Matrix<double, MATRIX_SIZE, MATRIX_SIZE> matrix_NN = MatrixXd::Random (MATRIX_SIZE, MATRIX_SIZE);
matrix_NN = matrix_NN * matrix_NN.transpose();
Matrix<double, MATRIX_SIZE, 1> matrix_b = MatrixXd::Random (MATRIX_SIZE, 1);
clock_t time_start = clock();
Matrix<double, MATRIX_SIZE, 1> x = matrix_NN.inverse()*matrix_b;
cout << "time consumption of normal inverse is " << 1000*(clock()-time_start)/(double)CLOCKS_PER_SEC << "ms" << endl;
cout<< "x = " << endl << x << endl;
time_start = clock();
x = matrix_NN.colPivHouseholderQr ().solve(matrix_b);
cout << "time consumption of Qr decomposition is " << 1000*(clock()-time_start)/(double)CLOCKS_PER_SEC << "ms" << endl;
time_start = clock();
x = matrix_NN.ldlt().solve(matrix_b);
cout << "time consumption of cholesky decomposition is " << 1000*(clock()-time_start)/(double)CLOCKS_PER_SEC << "ms" << endl;
cout<<"x is "<<endl<<x<<endl;
|
我记得一年级的时候老师反复强调求解线性方程组的时候,要解方程,不要求逆
Fx=b
一定不要通过x=F−1b 去计算而是要直接解方程。
至于QR分解,cholesky分解我属实是忘记了。
旋转矢量
SO(3)有9个量,大于旋转的3个自由度。
T有16个量,大于变换的6自由度。表达方式冗余。
旋转向量:一个轴+一个旋转角——向量方向与轴一致,大小等于旋转角。
设旋转轴的单位向量为n,旋转角为θ,则旋转向量为nθ,其与旋转矩阵R的关系由罗德里格斯公式导出:
R=cosθI+(1−cosθ)nnT+sinθn∧
其中n^为向量到反对称矩阵的转换。
转角有
θ=arccos2tr(R)−1
对于旋转轴上的向量而言,其为R的特征向量。换句话讲,可以通过求解特征方程再归一化得到旋转轴。
a=kn,Ra=a=kn
欧拉角
把旋转分为三个绕不同的轴的旋转。如ZYX旋转:
- 偏航角yaw、俯仰角pitch、横滚角roll
-
[r,p,y]T三维向量可以形容任何旋转。
万向锁问题
![20240624144310.png]
俯仰角为±90度时,第一次和第三次旋转公用一个轴,丢失一个自由度。
四元数
(避免歧义性又不想带有冗余性)
q=q0+q1i+q2j+q3k
⎩⎨⎧i2=j2=k2=−1ij=k,ji=−kjk=i,kj=−iki=j,ik=−j
q=[s,v]T,s=q0,v=[q1,q2,q3]T
单位四元数可以表示任意旋转。
将三维空间的点用一个虚四元数表示,单位四元数q指定旋转。
p=[0,x,y,z]T
旋转后的p’可以表示为
p′=qpq−1
p’的虚部即为旋转之后的坐标。
四元数乘法可以写成矩阵乘法。 q=[s,v]T
q+=[sv−vTsI+v∧],q⊕=[sv−vTsI−v∧]
可证
q1q2=q1+q2=q2⊕q1
此时,
p′=q+pq−1=q+p+q−1=q+q−1⊕p
代入有
q+(q−1)⊕=[10T0vvT+s2I+2sv∧+(v∧)2]
其右下角的元给出了四元数到旋转矩阵的变换关系
R=vvT+s2I+2sv∧+(v∧)2
为什么呢?
因为
[10T0vvT+s2I+2sv∧+(v∧)2][sp,vp]T=[spRvp]
其中v是原来的向量,R是旋转矩阵,对应可得。
tr(R)=tr(vvT)+3s2+2s tr(v∧)+tr(v∧2)=4s2−1
θ=arccos(2s2−1)
θ=2arccoss
旋转轴:(如果vp=v,得到该向量本身就不变,即为旋转轴,除以模长)
[nx,ny,nz]T=[q1,q2,q3]T/sin2θ
Eigen几何模块计算
四元数计算:使用前要归一化。
Eigen的各种数据类型都有单精度和双精度,必须手动转换,否则会报错。
如果世界坐标系W中有两个坐标系R1,R2。
其中R1的位姿为q1=[0.35,0.2,0.3,0.1]T,t1=[0.3,0.1,0.1]T。
R2的位姿为q2=[−0.5,0.4,−0.1,0.2]T,t2=[−0.1,0.5,0.3]T。
四元数能够描述旋转角度和旋转轴,因此还需要一个位移矢量t来描述位置
如果p在1中的坐标是P1=[0.5,0,0.2]T,则p在世界坐标系中的坐标PW=T1−1P1
再将其转换到2的坐标系中,应有P2=T2PW
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int main(int argc, char **argv){ Quaterniond q_1 = Quaterniond(0.35, 0.2, 0.3, 0.1); Quaterniond q_2 = Quaterniond(-0.5, 0.4, -0.1, 0.2); q_1.normalize(); q_2.normalize(); Vector3d t_1 = Vector3d(0.3, 0.1, 0.1); Vector3d t_2 = Vector3d(-0.1,0.5,0.3); Vector3d p = Vector3d(0.5,0,0.2); Isometry3d T1w(q_1); Isometry3d T2w(q_2); T1w.pretranslate(t_1); T2w.pretranslate(t_2); Vector3d p2 = T2w * T1w.inverse() * p; cout << "p2 = " << p2.transpose() << endl;
return 0; }
|
可视化
安装pangolin
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| git clone https://github.com/stevenlovegrove/Pangolin.git cd Pangolin
// 安装Pangolin所需依赖项 sudo apt-get install libglew-dev sudo apt-get install libboost-dev libboost-thread-dev libboost-filesystem-dev sudo apt-get install libepoxy-dev
mkdir build cd build cmake .. make -j$(nproc) sudo make install
|