lio-sam,緊耦合的雷達和IMU雷射slam方法
imageProjection部份:
主要的功能為點雲去畸變,需要點雲中每個點包含有time這個標簽。
首先imuQueue將每一個imu原始資訊變換到雷達系下,依次入列。
imuRotX,imuRotY,imuRotZ,以及imuTime這幾個佇列裏面存放的是相鄰兩幀之間的imu角速度積分得到的旋轉角度,第一個為0。
這裏cloudInfo的初始值由OdomMsg提供。本來imu用來進行角度畸變去除,odom進行平移畸變去除。但是由於平移畸變太小,所以沒有用。
ProjectPointCloud(), 此函式的主要作用為利用去畸變得到的點雲做rangeMat的投影。根據點的實際位置算出其相應的行列座標。
cloudExtraction(), 此函式的主要作用為統計有效點的數量,利用之前的rangeMat,只取在其上有效的點。點的總數量為count。另外,該函式還根據不同的ring獲得了每一個ring的起始點雲索引。
最後,publishClouds()函式的作用為釋出提取的有效的點雲以及cloudInfo。
cloudInfo包含如下資訊:
imuPreintegration部份
transfusion這個物件主要的作用為視覺化軌跡和odom,作用不是很大,subLaserOdometry接受的話題為mapping部份的裏程計訊息,然後作為一個低頻的lidar odom入列。
subImuOdometry接受的話題為預積分後的imu裏程計訊息,這個訊息作為一個高頻的訊息,首先入列。然後根據當前時刻和上一幀的雷達裏程計時刻之間的imu odom訊息。根據座標變換得到兩個時刻之間的座標增量。用該增量乘之前的雷達裏程計,釋出imu頻率的odom,以及path。
IMUPreintegration物件是imu預積分的主要物件。該物件訂閱imu的原始訊息以及lio_sam/mapping/odometry_incremental。
其中odometryHandler主要作用為接受低頻的mapping的odom訊息,加入imu預積分的因子圖中,然後最佳化imu預積分。註意,由於imu的訊息在另一個行程中不斷入列,所以在while迴圈結束後需要繼續進行一下imu預積分(當imu頻率不是很高的時候)。
imuIntegratorImu_->resetIntegrationAndSetBias(prevBiasOdom);
imuHandler的作用就是根據每次更新的bias以imu的頻率輸出。
FeatureExtraction部份
主要的功能為提取去畸變後的角點和平面點。
calculateSmoothness()根據公式計算每一個點雲的光滑度,這裏註意,當點在建築物的邊緣上時,曲率很大,必然是角點。
markOccludedPoints()主要的功能為濾除遮擋點,遮擋點定義如下,A點和B點是相鄰點,但是A點在B點後面很多,所以A點之前的5個點很容易被遮擋,因此他們被濾除。平行點。當雷射照射到一個和雷射入射角接近的平面時,很容易誤辨識角點,所以這些點被濾除。
extractFeatures()首先空間區域被均勻分成六份,每個區域的點按照光滑度有小到大排列,每個區域至多取20個角點。另外,當取得一個角點時,為防止聚集,他周圍10個相鄰像素的點都會被濾除。
publishFeatureCloud() 該函式的主要作用為釋出特征點雲。
cloudInfo包含有cloud_corner和cloud_surface兩個點雲。
mapOptimization部份
主要的功能就在於構建因子圖實作全域最佳化,重點的函式是幀間匹配的LM演算法。
laserCloudHandler()接受imageProjection節點中去畸變後的點雲,該點雲有三部份的訊息,去畸變的點雲的角點和平面點,imu的原始資訊,imu裏程計的資訊。
如果odomAvailiable,transFinal這個齊次矩陣存放的是增量後的map系下的初值,然後賦值給到transformTobeMapped的其他項。
如果imuAvailable,那麽重復上述操作,應該是防止上述的odom不可用。
- cornerOptimization(),角點最佳化函式。首先將當前幀的一個角點變化到map系,然後尋找離他最近的五個角點,判斷這五個角點是否在一條直線上。如果是,保留三個變量,原始角點,距離變量,flag。
- surfOptimization(),平面點最佳化函式。首先選擇最近的五個平面點,判斷其是否可以組成平面,如果可以,則保留原始平面點,距離變量,flag。
- combineOptimizationCoeffs(),聯合最佳化函式。將上述的角點和平面點的相關資訊一起推入laserCloudOri和coeffSel兩個量中。
- LMOptimization(),LM最佳化過程,最繁瑣的一塊。使用的是張繼的LOAM的一套,推導很麻煩,反正一句話,最佳化了transformTobeMapped這個量,但說是LM,看起來是牛頓歐拉法。
- transformUpdate(),更新最佳化後得到的幀間變換。但註意這個也是更新transformTobeMapped,不同之處在於加權了IMU的原始資訊,為啥怎麽相信IMU呢?。
- addOdomFactor(),將相鄰兩幀之間的關鍵幀加入因子圖,增量用transformTobeMapped的值
- addGPSFactor(),如果沒有關鍵幀,或者首尾關鍵幀距離小於5m,不添加gps因子,位姿共變異數很小,沒必要加入GPS數據進行校正,每隔5m添加一個GPS裏程計。
- addLoopFactor(),閉環邊對應兩幀的索引,閉環邊的位姿變換。加入因子圖後,清空loopIndexQueue();
- 加入cloudKeyPoses3D以及cloudKeyPoses6D。poseCovariance保留的是位姿的共變異數,判斷是否需要加GPS因子用的。最後更新一下transformTobeMapped。cornerCloudKeyFrames和surfCloudKeyFrames壓入當前幀的角點和平面點資訊。
- updatePath(),把當前幀的位姿資訊壓入globalPath。
loopClosureThread()回環執行緒
回環執行緒需要用到icp,這是一個非常耗時的操作,因此回環需要精確確定,而且回環的間隔要較大,這裏不只是兩個節點要相隔一段時間,也需要兩個回環之間不要靠太近。
- detectLoopClosureExternal,檢測外部回環的程式,這裏沒有用到
- detectLoopClosureDistance,判斷回環兩個節點之間的時間戳
- loopFindNearKeyframes(),提取key附近的點雲包括角點和平面點。之後liyongICP得到兩個關鍵幀之間的位姿變換關系,加入因子圖。loopIndexQueue壓入當前資訊,loopIndexContainer壓入防止多次將一個回環加入因子圖和展示。
visualizeGlobalMapThread()執行緒
以一定的頻率釋出GlobalMap,和提供saveMap的服務saveMapService