需要用到的一些知識和假設:
(1) 來源於 github中的討論:
由於IMU累積推算位置的誤差大,程序中粗略地計算了IMU的位置漂移。
_imuPositionShift = _imuCur.position - _imuStart.position - _imuStart.velocity * relSweepTime;
上式成立的前提是認為一個掃描周期內,Lidar的運動是勻速的,上式計算出了非線性誤差部分。
(2) X、Y、Z軸對應俯仰(pitch)、航向(yaw)、橫滾(roll)機動,可知Lidar坐標系為“右下前”坐標系。
(3) 從Lidar系到global IMU系,類似於慣導系統中的C(b->n),即載體系到地理系的轉換。
旋轉順序為:橫滾->俯仰->航向
rotateZXY(point, roll, pitch, yaw);
從global IMU系到Lidar系,旋轉順序正好相反。
rotateYXZ(point, -yaw, -pitch, -roll);
(4) transform代表將k時刻的點雲轉換到k+1時刻下,與視覺slam中的相對位姿定義相同。
坐標轉換與IMU融合
1、 transformToStartIMU
注冊點雲時(MultiScanRegistration.cpp中),當判斷有IMU數據時,會進行一步坐標轉換的預處理,體現在函數transformToStartIMU中。
這個函數進行了三步處理:
(1) rotateZXY(point, _imuCur.roll, _imuCur.pitch, _imuCur.yaw);
將原始點雲從當前Lidar系轉換到global IMU系下;
(2) 補償了_imuPositionShift,也即估算的IMU位置漂移;
(3) rotateYXZ(point, -_imuStart.yaw, -_imuStart.pitch, -_imuStart.roll);
將global IMU系下的點雲轉換到Start時刻的Lidar系下。
經過這個函數的處理,點雲的position部分處於當前Lidar系下一個相對准確的位置上(基於掃描周期內勻速運動的假設),但點雲的Rotation部分是Start時刻Lidar系下觀察所得的,而非處於當前Lidar系下。更清晰地來說,即此時觀察到的點雲坐標,是以當前Lidar的坐標(一個估計值)為原點,而坐標軸是與Start時刻的Lidar系的坐標軸對齊的。
2、 OD初始化:
根據第一次開始掃描時的IMU pitch與roll,作為累積位姿的初始值。
_transformSum.rot_x += _imuPitchStart;
_transformSum.rot_z += _imuRollStart;
Yaw角度和pos部分都未賦初值,即假設開始時刻的偏航角為0,位於global系下的原點位置。
3、 運動估計初值:
(1) _transform.pos -= _imuVeloFromStart * _scanPeriod;
其中,imuVeloFromStart的計算,可知imuVeloFromStart為Start時刻Lidar系下的速度變化矢量:
imuVelocityFromStart = _imuCur.velocity - _imuStart.velocity;
rotateYXZ(imuVelocityFromStart, -_imuStart.yaw, -_imuStart.pitch, -_imuStart.roll);
對於勻速運動假設的一個補償,並且基於運動曲線的連續性,做了遞推形式的計算,可能乘以1/2會更合適?
(2) _transform的rotation部分未賦初值,認為為0。
4、 transformToStart
在進行KDTree最近點搜索前,首先將進行畸變處理后的點雲轉換到每一次掃描的開始時刻。
先根據勻速運動假設計算出當前點時刻Lidar的位移和旋轉。
但這里其實是和前面的轉換transformToStartIMU有些沖突的。因為transformToStartIMU之后點雲所處的坐標軸是與Start時刻的Lidar系的坐標軸對齊的。現在又通過_transform將點雲轉換到start時刻。有一種轉了兩次的感覺。
有一種解釋:認為k次掃描中的旋轉部分大部分由IMU積分獲得,_transform中的旋轉估算的只是一個小量。
5、 transformToEnd
(1) 先進行transformToStart,此時點雲處於start時刻的Lidar系下;
(2) 通過_transform轉換到end時刻的Lidar系下;
(3) rotateZXY(point, _imuRollStart, _imuPitchStart, _imuYawStart);
轉換到global系下
(4) rotateYXZ(point, -_imuYawEnd, -_imuPitchEnd, -_imuRollEnd);
轉換到end時刻的Lidar系下。
總結點雲的旋轉過程從1->5, 可用公式表示為:
6、accumulateRotation
該函數的作用是將計算的兩幀之間的位姿“累加”起來,獲得相對於第一幀的旋轉矩陣,具體公式如下:
7、 pluginIMURotation
該函數與accumulateRotation,聯合起來完成了更新_transformSum的rotation部分的工作。該函數可視為transformToEnd的下部分的逆過程。具體公式如下: