參考資料:
1.Code:hdl_graph_slam
1. hdl_slam中的地面約束
在hdl_slam
中的每一幀關鍵幀都會對應提取一個地面參數;
建圖開始的時候將一個Floor Plane Node
設置爲true,並固定,參數設置爲決定的垂直於地面的法向量[0,0,1,0]
後面的每一幀keyframe
的地面參數coeffs
都要和Floor Plane Node
構建平面誤差,如下所示:
void computeError() override {
const g2o::VertexSE3* v1 = static_cast<const g2o::VertexSE3*>(_vertices[0]); //T:[R,t]
const g2o::VertexPlane* v2 = static_cast<const g2o::VertexPlane*>(_vertices[1]); //[0,0,1,0]
Eigen::Isometry3d w2n = v1->estimate().inverse();// T.inverse()= T_iw;i是當前幀,w是世界座標系
Plane3D local_plane = w2n * v2->estimate();// T_iw*[0 0 1 0]= 將一個絕對的垂直於Z的法向量,投影到當前幀;
_error = local_plane.ominus(_measurement);//將[0 0 1 0]變換到當前幀之後,和當前幀的地面的法向量 做ominus 作爲誤差
}
思路是,根據當前第i幀keyframe的位姿,將初始的嚴格垂直於地面的法向量[0,0,1,0]變換到第i幀的座標系下;
並用這個參數來初始化Plane3D local_plane;其底層對*
操作符進行了重載;具體如下:
inline Plane3D operator*(const Isometry3& t, const Plane3D& plane){
Vector4 v=plane._coeffs;
Vector4 v2;
Matrix3 R=t.rotation();
v2.head<3>() = R*v.head<3>();
v2(3)=v(3) - t.translation().dot(v2.head<3>());
return Plane3D(v2);
};
可以看到,傳入參數t
就是,plane
就是[0,0,1,0],經過一個變換,變成到當前幀雷達座標系下的一個Vector4d值。
再使用這個值和當前幀點雲擬合出來的地面點,進行一個操作,獲得誤差,誤差的具體定義如下:
inline Vector3 ominus(const Plane3D& plane){
//construct the rotation that would bring the plane normal in (1 0 0)
Matrix3 R=rotation(normal()).transpose();
Vector3 n=R*plane.normal();
number_t d=distance()-plane.distance();
return Vector3(azimuth(n), elevation(n), d);
}
那麼平面誤差是如何定義的呢?
下面則是重點推導部分。
2. g2o中的平面誤差推導
首先,在三維平面下,平面方程可以使用4個參數來表示:
所以,如果想要達到hdl_slam中對地面的約束,就需要藉助平面方程,對地面點雲進行約束;
1.當前幀點雲的地面方程:
那麼,當前幀雷達點雲的地面方程擬合可以借鑑這篇文章[透徹理解]由最小二乘到SVD分解,那麼可以獲得當前幀雷達點雲(第i幀)的地面方程爲:
該參數記做
2.全局座標系下的點雲地面方程:
hdl_slam對此約束的處理思路是,最開始初始化一個全局地面方程參數爲[0,0,1,0]
,記爲:
也即,,該參數記做
3. 平面方程的座標系統一
上面構建的兩個平面方程,其座標系並不統一,因此需要統一座標系;按照hdl_slam的理解,我們將世界座標系下的平面方程投影到當前第i幀雷達座標系下,(該位姿視作已知):
然後在第幀雷達座標系下進行平面方程的誤差構建,即如下代碼部分:
Eigen::Isometry3d w2n = v1->estimate().inverse();
Plane3D local_plane = w2n * v2->estimate();
_error = local_plane.ominus(_measurement);
4. g2o中的平面誤差
對兩個平面參數進行操作,分爲以下幾步:
-
1.
Matrix3 R=rotation(normal()).transpose();
- 以的向量(前三維,也即法向量)爲基底,將轉換爲旋轉矩陣。
-
Vector3 n=R*plane.normal();
- 將轉換到以爲基底的旋轉空間中去,得到向量n;
- 將轉換到以爲基底的旋轉空間中去,得到向量n;
-
對n計算仰角
azimuth
和方位角elevation
-
計算
-
誤差組合爲
Vector3(azimuth(n), elevation(n), d)