SLAM性能的一个很重要的指标是 定位精度 ,公开的室内数据集一般会采用 动捕设备 获取设备轨迹真值。但是动捕设备 价格昂贵 , OptiTrack 百万以上,国产的 Nokov >20W。没有动捕设备,我们就很难用 自己采集的数据 评估SLAM的定位性能。 为此,我们搞了一个廉价的轨迹真值捕获装置。用一个固定的 单目相机 获取 平面运动 的 轨迹真值 。我们主要是用这种方法来测室内移动机器人的运动轨迹。 本文给出了原理和 代码 。 注意:只能测 2D 平面内的运动,不是3D。
1. 原理
如下图,支架上固定一个下视的单目相机 ,用来捕捉机器人顶端平贴的一张ArUco码 。ArUco码相对的机器人的位姿是已知的(这个应该很容以做到吧)。我们要解决的问题就是利用单目相机测量ArUco码在平面上的位姿 。实际上,OpenCV提供的ArUco的库,可以实现ArUco码的识别,并提供码 四个角点的像素坐标 。如果我们能计算出到四个角点在平面上的坐标,就可以计算出机器人在平面上的位姿(位置和朝向)。下面就来说一下如何计算一个点在平面上的位置。
平面轨迹真值捕获原理
世界坐标系下,一个3D的点 w p = [ x , y , z ] T {}^w{\mathbf{p}} = {\left[ {x,y,z} \right]^T} w p = [ x , y , z ] T ,投影到图像坐标下,可表示为
λ [ u v 1 ] = [ f x 0 c x 0 0 f y c y 0 0 0 1 0 ] [ r 11 r 12 r 13 t x r 21 r 22 r 23 t y r 31 r 32 r 33 t z 0 0 0 1 ] ⏟ T [ x y z 1 ] ( 1 ) \lambda \left[ {\begin{array}{c} u\\ v\\ 1 \end{array}} \right] = \left[ {\begin{array}{l} {{f_x}}&0&{{c_x}}&0\\ 0&{{f_y}}&{{c_y}}&0\\ 0&0&1&0 \end{array}} \right]\underbrace {\left[ {\begin{array}{c} {{r_{11}}}&{{r_{12}}}&{{r_{13}}}&{{t_x}}\\ {{r_{21}}}&{{r_{22}}}&{{r_{23}}}&{{t_y}}\\ {{r_{31}}}&{{r_{32}}}&{{r_{33}}}&{{t_z}}\\ 0&0&0&1 \end{array}} \right]}_{\bf{T}}\left[ {\begin{array}{c} x\\ y\\ z\\ 1 \end{array}} \right] \ (1) \\ λ u v 1 = f x 0 0 0 f y 0 c x c y 1 0 0 0 T r 11 r 21 r 31 0 r 12 r 22 r 32 0 r 13 r 23 r 33 0 t x t y t z 1 x y z 1 ( 1 )
T \mathbf T T 是外参。我们想要测量的是2D平面内的坐标,为了方便,可以把世界坐标系的原点设在平面之上,Z Z Z 轴垂直于被这个平面(就是ArUco那个平面)。那么 w p {}^w{\bf{p}} w p 的 z = 0 z=0 z = 0 。式(1)就可以变为
λ [ u v 1 ] = [ f x 0 c x 0 f y c y 0 0 1 ] [ r 11 r 12 t x r 21 r 22 t y r 31 r 32 t z ] ⏟ H 3 × 3 [ x y 1 ] ( 2 ) \lambda \left[ {\begin{array}{c} u\\ v\\ 1 \end{array}} \right] = \underbrace {{}\left[ {\begin{array}{c} {{f_x}}&0&{{c_x}}\\ 0&{{f_y}}&{{c_y}}\\ 0&0&1 \end{array}} \right]\left[ {\begin{array}{c} {{r_{11}}}&{{r_{12}}}&{{t_x}}\\ {{r_{21}}}&{{r_{22}}}&{{t_y}}\\ {{r_{31}}}&{{r_{32}}}&{{t_z}} \end{array}} \right]}_{\bf{H}_{3\times 3}}\left[ {\begin{array}{c} x\\ y\\ 1 \end{array}} \right] \ (2) \\ λ u v 1 = H 3 × 3 f x 0 0 0 f y 0 c x c y 1 r 11 r 21 r 31 r 12 r 22 r 32 t x t y t z x y 1 ( 2 )
平面上的点 [ x , y ] T [x,y]^T [ x , y ] T 和图像上的像素 [ u , v ] T [u,v]^T [ u , v ] T 之间就建立了联系。它们之间就是一个单应矩阵 H \mathbf H H 。
我们只要知道这个单应矩阵 ,就可以通过图像像素 获取其在平面上的2D坐标 。
[ x y 1 ] = λ H − 1 [ u v 1 ] ( 3 ) {\left[ {\begin{array}{c} x\\ y\\ 1 \end{array}} \right] = \lambda {H^{ - 1}}\left[ {\begin{array}{c} u\\ v\\ 1 \end{array}} \right]} \ (3)\\ x y 1 = λ H − 1 u v 1 ( 3 )
2. 现在的问题是单应矩阵怎么来的呢?
从式(2)可以看出,只要知道摄像机的内参数 K \bf K K 和外参数 T \bf T T , H \bf H H 就有了。内参数 K \bf K K 好说,大家应该都很熟悉。关键是 T \bf T T ,我们这里用平面靶标来标定。具体的,如下图把一张棋盘格靶标放在ArUco码之上,通过PnP方法 可以计算出靶标坐标系与摄像机坐标系之间的位姿变换 T ′ \bf T ^{'} T ′ 。注意,棋盘格靶标这个平面并不是ArUco码的平面,因为棋盘格靶标有厚度 t t t 。这个厚度 t t t 我们很容易测量,认为是已知的。那么,相机坐标系和ArUco面上的世界坐标系之间的坐标变换就是
T = T ′ [ 1 0 0 0 0 1 0 0 0 0 1 t 0 0 0 1 ] ( 4 ) {\bf{T}}{\rm{ = }}{\bf{T'}}\left[ {\begin{array}{c} 1&0&0&0\\ 0&1&0&0\\ 0&0&1&t\\ 0&0&0&1 \end{array}} \right] \ (4)\\ T = T ′ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 t 1 ( 4 )
至此,我们就得到了外参数 T \bf T T 。通过(2)式也就可以得到单应矩阵 H \bf H H 。
标定单应矩阵H
3. 下面的问题是计算机器人的位姿
如上图,刚才我们提到,在图像上可以得到ArUco码四个角点的像素坐标 ,这里记为
[ u i v i ] , i = 1 ∼ 4 ( 5 ) \left[ {\begin{array}{c} {{u_i}} \\ {{v_i}} \end{array}} \right],i = 1\sim4 \ (5)\\ [ u i v i ] , i = 1 ∼ 4 ( 5 )
使用(3)式,可以分别计算出这四个点在世界坐标系下的坐标(就是平面内的坐标)
[ x i y i ] , i = 1 ∼ 4 ( 6 ) \left[ {\begin{array}{c} {{x_i}} \\ {{y_i}} \end{array}} \right],i = 1\sim4 \ (6) \\ [ x i y i ] , i = 1 ∼ 4 ( 6 )
关于机器人的位置,其实我们只需要一个点的坐标就好了,就是ArUco码中心点的坐标。因此我们把四个点合成为一个点:
[ x c y c ] = 1 4 ∑ i = 1 n [ x i y i ] ( 7 ) \left[ {\begin{array}{c} {{x_c}} \\ {{y_c}} \end{array}} \right] = \frac{1}{4}\sum\limits_{i = 1}^n {\left[ {\begin{array}{c} {{x_i}} \\ {{y_i}} \end{array}} \right]} (7) \\ [ x c y c ] = 4 1 i = 1 ∑ n [ x i y i ] ( 7 )
[ x c , y c ] T [x_c,y_c]^T [ x c , y c ] T 就是机器人的位置。
下面还要计算一下机器人的姿态,在2D平面就是朝向了。用两个点就可以计算出朝向,现在我们手里有四个点,那就平均一下好了。
θ = { arctan 2 ( V 1 [ 2 ] , V 1 [ 1 ] ) + arctan 2 ( V 2 [ 2 ] , V 2 [ 1 ] ) } 2 , V 1 = [ x 1 y 1 ] − [ x 4 y 4 ] , V 2 = [ x 2 y 2 ] − [ x 3 y 3 ] ( 8 ) \begin{gathered} \theta = \frac{{\left\{ {\arctan2 ({{\mathbf{V}}_1}[2],{{\mathbf{V}}_1}[1]){\text{ + }}\arctan2 ({{\mathbf{V}}_2}[2],{{\mathbf{V}}_2}[1])} \right\}}}{2} , \\ {{\mathbf{V}}_1} = \left[ {\begin{array}{c} {{x_1}} \\ {{y_1}} \end{array}} \right] - \left[ {\begin{array}{c} {{x_4}} \\ {{y_4}} \end{array}} \right],{{\mathbf{V}}_2} = \left[ {\begin{array}{c} {{x_2}} \\ {{y_2}} \end{array}} \right] - \left[ {\begin{array}{c} {{x_3}} \\ {{y_3}} \end{array}} \right] \\ \end{gathered} \ (8) \\ θ = 2 { arctan 2 ( V 1 [ 2 ] , V 1 [ 1 ]) + arctan 2 ( V 2 [ 2 ] , V 2 [ 1 ]) } , V 1 = [ x 1 y 1 ] − [ x 4 y 4 ] , V 2 = [ x 2 y 2 ] − [ x 3 y 3 ] ( 8 )
θ \theta θ 就是机器人的朝向。
4. 测量的精度问题
既然要作为真值去用,那就要求要有足够的测量精度。下面我们分析一下测量精度。我们把式(3)展开
[ x y 1 ] = λ [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] ⏟ H − 1 [ u v 1 ] ( 9 ) \left[ {\begin{array}{c} x \\ y \\ 1 \end{array}} \right] = \lambda \underbrace {\left[ {\begin{array}{c} {{h_{11}}}&{{h_{12}}}&{{h_{13}}} \\ {{h_{21}}}&{{h_{22}}}&{{h_{23}}} \\ {{h_{31}}}&{{h_{32}}}&{{h_{33}}} \end{array}} \right]}_{{{\mathbf{H}}^{-1}}}\left[ {\begin{array}{c} u \\ v \\ 1 \end{array}} \right] \ (9) \\ x y 1 = λ H − 1 h 11 h 21 h 31 h 12 h 22 h 32 h 13 h 23 h 33 u v 1 ( 9 )
消去 λ \lambda λ 可以得到
[ x y ] = [ h 11 u + h 12 v + h 13 h 31 u + h 32 v + h 33 h 21 u + h 22 v + h 23 h 31 u + h 32 v + h 33 ] = F ( [ u v ] ) ( 10 ) \left[ {\begin{array}{c} x \\ y \end{array}} \right] = \left[ {\begin{array}{c} {\frac{{{h_{11}}u + {h_{12}}v + {h_{13}}}}{{{h_{31}}u + {h_{32}}v + {h_{33}}}}} \\ {\frac{{{h_{21}}u + {h_{22}}v + {h_{23}}}}{{{h_{31}}u + {h_{32}}v + {h_{33}}}}} \end{array}} \right] = F\left( {\left[ {\begin{array}{c} u \\ v \end{array}} \right]} \right)\ (10) \\ [ x y ] = [ h 31 u + h 32 v + h 33 h 11 u + h 12 v + h 13 h 31 u + h 32 v + h 33 h 21 u + h 22 v + h 23 ] = F ( [ u v ] ) ( 10 )
如果图像u方向上像素提取的精度-方差为 σ u 2 \sigma_u^2 σ u 2 ,v方向的精度-方差为σ v 2 \sigma_v^2 σ v 2 。也就是像素点的协方差为
Σ u v = [ σ u 2 0 0 σ v 2 ] ( 11 ) {{\mathbf{\Sigma }}_{uv}} = \left[ {\begin{array}{c} {{\sigma _u}^2}&0 \\ 0&{{\sigma _v}^2} \end{array}} \right] \ (11)\\ Σ uv = [ σ u 2 0 0 σ v 2 ] ( 11 )
那么测得的平面上点 [ x , y ] T [x,y]^T [ x , y ] T 的协方差就是
Σ x y = ∂ F ∂ [ u , v ] T Σ u v ( ∂ F ∂ [ u , v ] T ) T ( 12 ) {{\mathbf{\Sigma }}_{xy}} = \frac{{\partial F}}{{\partial {{[u,v]}^T}}}{{\mathbf{\Sigma }}_{uv}}{\left( {\frac{{\partial F}}{{\partial {{[u,v]}^T}}}} \right)^T} \ (12) \\ Σ x y = ∂ [ u , v ] T ∂ F Σ uv ( ∂ [ u , v ] T ∂ F ) T ( 12 )
至此就精度分析完了,也就是说标出来单应矩阵 H \bf H H 之后,通过(12)式就可以计算出大致的测量精度。
实际上,也可以把摄像机的内外参带入到(3)式,再结合(12)式,可以获得一个解析的精度表达。这个比较复杂。如果相机安装的时候相对于测量平面仅是绕相机坐标系的x轴旋转一个角度的话,测量区域内的精度分布如下图。如果摄像机正对测量平面的话,测量区域的测量精度是相同的。当然,相机的分辨率越高精度越高。焦距越长精度越高,但是同时可测量的区域也会变小。
我自己使用的相机是大恒MER-302-56U3C相机,2048×1536像素;镜头是kowa LM3NC1M,3.5mm广角镜头;摄像机离测量平面的距离大概是2m。可测量范围大概是4m×3m。如果认为像素的提取精度为1个pixel的话,测量精度约为2mm 。这个精度足够用于SLAM轨迹评估。
5. 关于测量范围
如果相机正对于测量平面,测量区域是一个矩形。如果相机倾斜放置的话,测量区域就是一般的四边形了。上图就是一种。测量区域的大小跟镜头的视场角、相机安装的高度、角度都有关系,比较复杂。总的来说,为了使测量平面内的测量精度一致,应该尽量让相机正对测量平面。那焦距越短,测量视场越大,但是测量精度随之降低。为了获取足够的测量精度,就需要一个高分辨率的相机。为了降低悬挂高度,就需要一个短焦镜头。
6. 代码
标定和测量的全部代码如下
ydsf16/ground_truth_estimation_2d
7. 参考资料
[1] Hartley R , Zisserman A . Multiple View Geometry in Computer Vision[M]. Cambridge University Press, 2003.
[2] 杨东升, 毕树生, 蔡月日,等. 基于平行面多靶标标定的单目大视场平面测量[J]. 光学学报, 2017(10):228-239.
----更多SLAM文章----
杨小东:[PnP]PnP问题之EPnP解法
杨小东:[PnP] PnP问题之DLT解法
杨小东:[ORB-SLAM2]卡方分布(Chi-squared)外点(outlier)剔除
杨小东:[ORB-SLAM2] ORB特征提取策略对ORB-SLAM2性能的影响
杨小东:[PR-3]ArUco EKF SLAM 扩展卡尔曼SLAM
杨小东:[PR-2] PF 粒子滤波/蒙特卡罗定位
----相关代码----
ydsf16 - Overview