多光學雷達外參⾃動化標定演算法及程式碼實例
光學雷達是⽬前⾃動駕駛系統中的核⼼感測器之⼀,但是由於其資訊密度低、存在垂直盲區等問題,⼚商⼤多在其L4級⾃動駕駛系統中搭配多組光學雷達,下圖為通⽤(Cruise)的⾃動駕駛汽⻋,采⽤了多光學雷達以彌補lidar+camera的不⾜,使⽤多光學雷達進⾏環境感知的前提是對各雷達的外參進⾏精準的標定,本⽂介紹⼀種基於NDT演算法的⾃動多光學雷達標定技術,並且給出了程式碼實例以及測試數據(rosbag)供讀者實踐。
NDT演算法詳解
感測器外參標定本質上是獲得兩個感測器的位移量(x,y,z )和旋轉量(roll,pitch,yaw ),三維空間中可以⽤⼀個⻬次變換矩陣(Homogeneous transformation matrix)來描述這樣的變換關係,在三維數據處理領域,點雲配准(Point Cloud Registration)就是⽤於處理兩個點雲間位姿匹配問題的⼀類x, y, z roll, pitch, yaw⽅法,其中NDT(Normal Distribution Transform,正態分布變換)和ICP(Iterative Closest Point, 迭代最近點演算法)是其中的代表。本⽂主要基於NDT演算法介紹多光學雷達外參標定的⽅法和實踐。

其中,α 為yaw ,β 為pitch , γ為roll 。

假定ND體素k中包含有m個點,那麼這個ND體素中所有點的均值μk和協⽅差矩陣Σk計算公式為:

其中pki為ND體素k中的點i,即
,那麼該ND體素內點的概率密度函數f(k)可以表示為:

NDT匹配的準確度和ND體素格的劃分尺⼨相關,采⽤的ND體素尺⼨越⼩,相應的NDT匹配精度也會越⾼,所以在利⽤NDT演算法進⾏掃描匹配定位時需要對匹配精度和演算法實時性間進⾏取捨,但是在本例中,我們使⽤NDT進⾏多光學雷達標定為離線任務,所以可以將ND Voxel設置相對⼩⼀些以提⾼最終的精度。
NDT掃描匹配演算法
,旋轉參數向量表⽰變換後的點雲相對當前輸⼊點雲在姿態⻆上的旋轉量,平移參數向量表⽰在(x, y, z)三個⽅向的平移量。那麼對於輸⼊點雲⽽⾔,每個點進⾏三維坐標轉換的計算公式如下:

NDT演算法⾸先對⽬標點雲進⾏正態分布變換,得到⽬標點雲的所有ND體素,接著需要輸⼊當前輸⼊點雲的初始位姿(x, y, z, roll, pitch, yaw),將此位姿作為初始搜索位置。在感測器標定中,這個初始變換位姿也就是我們對於多個光學雷達平移量和旋轉量的粗略估計,可以通過簡單的測量得到(精度不需要太⾼,我們最終將通過NDT演算法得到⾼精度的結果)。

其中,
是輸⼊點雲在經過三維坐標變換參數θ轉換後的點,μi是與該輸⼊點相對應的⽬標點雲ND體素格的均值,Σi是相應的ND體素內的協⽅差矩陣。擬合度F itness(P , θ)數值越⼤說明輸⼊點雲和⽬標點雲在該位置越匹配,通過搜索參數向量 得到⼤的擬合度,這是⼀個典型的⾮線性最優化問題,NDT演算法使⽤⽜頓法來求解最優參數。⽜頓法是⼀種最⼩化⽬標函數的⽅法,所以⽬標函數f (θ)為:

其中θ = (α, θ, γ, dx, dy , dz )T,是輸⼊點雲到⽬標點雲的三維坐標轉換參數。通過迭代⽜頓法,不斷調整θ向量,使得f (θ)⼩於⼀個閾值,則稱NDT參數優化已經收斂,根據此時的變換參數向量θ即可確定輸⼊點雲在⽬標點雲中的姿態,也就是lidar A到lidar B的TF關係。
多光學雷達標定程式碼實例
在理解NDT演算法後,我們知道這類基於點雲配準的多光學雷達標定⽅法需要給⼀個初始姿態估計,這個初始估計要求的精度不⾼,可以簡單的測量甚⾄估計得到,下載後台提供的rosbag和程式碼倉庫,通過rosbag info我們發現該bag包含以下數據:

這個bag包含了6個光學雷達的數據,我們選取其中⼀個Lidar作為主要的坐標系(TF中的parentframe),建⽴起其他5個lidar到它的TF變換關係,就完成了6光學雷達的外參標定。選取 /hesai/front_high 作為parent frame,我簡單的⽬測估計了其他5顆光學雷達到該雷達的姿態變換關係並且寫到了項⽬的 cfg/child_topic_list ⽂件中:


voxel_size :輸⼊點雲的降取樣尺度,在執⾏NDT演算法之前我們通常會對輸⼊點雲進⾏預處理,如體素降取樣,以加速計算; ndt_epsilon :該參數定義了搜索的最⼩變化量,該參數尺度過⼤會造成最終的收斂不穩定,過⼩容易造成收斂速度過慢的問題; ndt_resolution :⽬標點雲的ND體素的尺⼨,單位為⽶; ndt_iterations :使⽤⽜頓法優化的迭代次數,迭代次數越多計算量越⼤。
理解好參數以後簡要的過⼀下程式碼,本⽂實例主要基於PCL庫,在主函數中,我⽤了⼀個頻率為10Hz的loop:


對應的回調 PointsCallback 中我們並沒有寫程式碼的業務邏輯,僅僅只是獲取點雲數據並保存於內部成員變數中,並對輸⼊點雲做了Voxel Grid降取樣:

點雲的配准我們寫在了循環中的 PerformNdtOptimize() 函數中,⾸先判定成員變數是否已經接受到點雲消息,接著初始化NDT的參數、輸⼊點雲和⽬標定義:

如果沒有初始估計,則從配置⽂件中讀取位移量和歐拉⻆換算成⻬次變換陣:

執⾏NDT點雲配准,得到新的變換矩陣,基於新的變換矩陣對輸⼊點雲進⾏位姿調整,並且發布該debug點雲以在rviz中可視化:


提⽰:你需要安裝catkin_tools才能夠使⽤ catkin build 指令進⾏編譯,你可以選擇:
1. 安裝catkin_tools:
sudo apt-get install Python-catkin-tools
2. 或者使⽤ catkin_make 進⾏編譯。
編譯完成後啟動roscore:
roscore
在另⼀個終端中啟動rviz(使⽤我提供的rviz配置⽂件):
rviz -d rviz/multi_lidar_calibration.rviz
運⾏
通過Rviz可以看到⼤概⽤時不到5秒中左右,兩顆光學雷達即標定完成,如下圖:

並且可以得到相應的TF關係,如下圖:

程式的輸出:


將收斂後程式的輸出結果中的1.00938 -0.478343 -0.442721 1.36447 0.0686235 -0.080712 /fh /lf 10 拷⻉到 ⽂件launch/tf.launch中的args中,我們可以通過ros tf中的static_transform_publisher將6顆光學雷達的 數據融合在⼀個坐標系下。
修改multi_lidar_calibrator.launch中的points_child_src名稱為其他data的topic,執⾏程式5次即可完成所有 lidar的標定,得到5組外參(⻬次矩陣、TF、平移量和四元數均能得到),將結果添加到tf.launch⽂件 中後,我們就可以通過Rviz看到6顆光學雷達的標定結果了,執⾏tf.launch並運⾏bag:
rosbag play multi_lidar.bag -l --clock




