該博客實時更新於我的Github。
在機器人局部路徑規划中,需要實時躲避運動或者靜態的障礙物,這個過程涉及到碰撞檢測這個問題,本文主要討論這個問題。
碰撞檢測問題也是游戲開發中經常遇到的問題,一個游戲場景中可能存在很多物體,它們之間大多屬於較遠位置或者相對無關的狀態,那么一個物體的碰撞運算沒必要遍歷這些物體,我們可以使用一個包圍一個或多個物體的多邊形來討論碰撞問題,這樣子可以節省重要的計算量和時間。
在真實的物理系統中,一般需要在運算速度和精確性上做取舍。盡管非常精確的碰撞檢測算法可以精確地表示和解決碰撞問題,但是在路徑規划初期對碰撞只需要有一個初步的估計,比如是否會發生碰撞,碰撞的大概程度如何,以免把大量的精力浪費在碰撞檢測問題上,從而降低了在其他方面的注意力。本文主要利用游戲中用到的碰撞檢測方法,來解決碰撞檢測的初步估計,或者對碰撞精確度要求不高的場合,將不規則的物體投影成較規則的物體進行碰撞預測及檢測。
AABB介紹
目前,成功的3D游戲普遍采用的碰撞檢測是BSP樹以及AABB(Axially Aligned Bounding Box)包裝盒方式。BSP樹是用來控制檢測順序和方向的數據描述。這里不再詳細討論。AABB檢測方法采用一個描述用的立方體或者球形體包裹住3D物體對象的整體(或者主要部分),我們可以根據包裝盒的距離、位置等信息來計算是否發生碰撞。注意:出於計算量和方便性考慮,AABB中常用的包裝盒形狀是球體和長方體,但是在其它特殊場合,其他形狀也可以作為包裝盒。
坐標軸平行(Axially-aligned)不僅指盒體與世界坐標軸平行,同時也指盒體的每個面都和一條坐標軸垂直,這樣一個基本信息就能減少轉換盒體時操作的次數。AABB技術在當今的許多游戲中都得到了應用,開發者經常用它們作為模型的檢測模型。
二維場景中的AABB包圍盒具備特點(下圖中的所有坐標系均采用右手直角坐標系):
- 表現形式為四邊形,即用四邊形包圍物體。
- 四邊形的每一條邊,都會與坐標系的軸垂直。
三維場景中的AABB包圍盒特點:
- 表現形式為六面體。
- 六面體中的每條邊都平行於一個坐標平。
其中,為了更明顯的展示AABB包圍盒的特點,在最右側展示了一個OBB(Oriented Bounding Box)包圍盒,也稱作有向包圍盒。AABB包圍盒與OBB包圍盒的最直接的區別就是,AABB包圍盒是不可以旋轉的,而OBB包圍盒是可以旋轉的,也就是有向的。
三維場景中物體的AABB包圍盒是一個六面體,雖然有8個頂點,但是對於規則的AABB立方體,我們僅需要知道兩個頂點(xmin,ymin,zmin)和(xmax,ymax,zmax)就可以得到AABB的中心點、邊長等屬性,具體不再詳述。三維物體的AABB包圍盒的八個頂點依舊可以用兩個頂點來標識,如下圖所示。
球體碰撞預測及檢測
球體是碰撞檢測中最簡單的數學模型,我們只需要直到兩個球體的球心和半徑就可以進行檢測。
球體碰撞的優點是非常適用於需要快速檢測的游戲,因為它不需要精確的碰撞檢測算法,執行速度相對較快,不會給CPU帶來過大的計算負擔。球體碰撞的另一個劣勢是只適用於近似球形物體,如果物體非常窄或者非常寬,該碰撞檢測算法將會失效,因為會在物體實際發生碰撞之前,碰撞檢測系統就發出碰撞信號。
球體樹
為了解決包容球精確度不高的問題,人們又提出了球體樹的方法。如下圖所示,球體樹實際上是一種表達3D物體的層次結構。對一個形狀復雜的3D物體,先用一個大球體包容整個物體,然后對物體的各個主要部分用小一點的球體來表示,然后對更小的細節用更小的包容球體,這些球體和它們之間的層次關系就形成了一個球體樹。
舉例來說,對一個游戲中的人物角色,可以用一個大球來表示整個人,然后用中等大小的球體來表示四肢和軀干,然后用更小的球體來表示手腳等。這樣在對兩個物體進行碰撞檢測時,先比較兩個最大的球體。如果有重疊,則沿樹結構向下遍歷,對小一點的球體進行比較,直到沒有任何球體重疊,或者到了最小的球體,這個最小的球體所包含的部分就是碰撞的部分。
速度錐
在實際碰撞檢測中,我們需要提前預估碰撞的危險程度,通過將運動物體碰撞處理為兩個球體,在已知球體的球心、半徑、運動矢量后,就可以預估出沿着當前運動趨勢的最近距離和對應時間。為方便理解,如下圖所示,以二維平面上的兩個圓形為例建立相對運動坐標系,討論碰撞檢測問題,可以擴展到3維空間的球體中。
在二維平面內,障礙物的碰撞預測如下,其中DCPA表示最近距離的值,TCPA表示在最近時刻的時間。
碰撞預測C#源代碼:
// C# 代碼
public static ARPA CPACalculation(double USVGeo_x, double USVGeo_y, double OBSGeo_x, double OBSGeo_y, double USV_Speed, double USV_Azimuth, double OBS_Speed, double OBS_Azimuth)
{
// 計算距離
double distance = GeographyBase.GeographyTransfer.CalLength(USVGeo_x,USVGeo_y,OBSGeo_x,OBSGeo_y);
// 計算方位
double UAV2OBSAzimuth = GeographyBase.GeographyTransfer.CalAzimuth(USVGeo_x, USVGeo_y, OBSGeo_x, OBSGeo_y);
double interAngle = USV_Azimuth - OBS_Azimuth; //取值范圍在[-180, 180]之間
if (interAngle > 180)
interAngle -= 360;
if (interAngle < -180)
interAngle += 360;
// 相對速度
double RelativSpeed = Math.Sqrt(Math.Pow(USV_Speed, 2) + Math.Pow(OBS_Speed, 2) - 2 * USV_Speed * OBS_Speed * Math.Cos(interAngle / 180.0 * Math.PI));
// 相對航向
double angleQ = Math.Acos((Math.Pow(RelativSpeed, 2) + Math.Pow(USV_Speed, 2) - Math.Pow(OBS_Speed, 2)) / (2 * RelativSpeed * USV_Speed)) * 180.0 / Math.PI;
double RelativeAzimuth = 0;
if (interAngle > 0)
RelativeAzimuth = USV_Azimuth + angleQ;
else
RelativeAzimuth = USV_Azimuth - angleQ;
// 相對舷角的計算
double bearing = UAV2OBSAzimuth - RelativeAzimuth;
double DCPA = distance * Math.Sin(bearing * Math.PI / 180.0);
double TCPA = distance * Math.Cos(bearing * Math.PI / 180.0) / RelativSpeed;
ARPA arpa = new ARPA();
arpa.DCPA = DCPA;
arpa.TCPA = TCPA;
arpa.SailSpeed = OBS_Speed;
arpa.SailAngle = OBS_Azimuth;
arpa.TargetDistance = distance;
arpa.TargetAzimuth = UAV2OBSAzimuth;
return arpa;
}
立方體碰撞檢測--AABB
AABB對物體的方向很敏感,同一物體的不同方向,AABB也可能不同(由於球體只有一個自由度,所以檢測球對物體方向不敏感)。
當物體在場景中移動時,它的AABB也需要隨之移動,當物體發生旋轉時,有兩種選擇:用變換后的物體來重新計算AABB,或者對AABB做和物體同樣的變換。如果物體沒有發生扭曲,可以通過“變換后AABB”重新計算,因為該方法要比通過“變換后的物體”計算快得多。可以利用矩陣變化加快新的AABB的計算速度,具體可以參考適合新手的3d碰撞檢測
AABB靜態檢測
AABB的靜態檢測比較簡單,檢測兩個靜止包裝盒是否相交,它是一種布爾測試,測試結果只有相交或者不相交。這里我們還提供了獲取相交范圍信息的方法,一般來說,這種測試的目的是為了返回一個布爾值。
在一維坐標軸中,兩線段A和B相交的條件是:
- 線段A在坐標軸上的最大值Amax不小於線段B在坐標軸上的最小值Bmin;
- 線段B坐標軸上的最大值Bmax不小於線段A在坐標軸上的最小值Amin;
即 (Amax-Bmin)*(Bmax-Amin)>0
基於上述事實,二維場景中AABB碰撞檢測原理:
在上圖中,分別做物體A與物體B在X,Y軸方向的投影,物體A的Y軸方向最大點坐標為Y1,最小點坐標Y2,X軸方向最小點坐標X1,最大點坐標X2,物體B同理。圖中紅色區域為物體A與物體B投影的重疊部分。
二維場景中AABB碰撞檢測具有如下規則:物體A與物體B分別沿兩個坐標軸做投影,只有在兩個坐標軸都發生重疊的情況下,兩個物體才意味着發生了碰撞。
即,若Y軸方向上(Y1-Y4)*(Y3-Y2)>0,X軸方向上(X4-X1)*(X2-X3)>0,那么證明物體A與物體B發生重合,否則證明物體A和B並未發生重合。
三維場景中AABB碰撞檢測原理:
三維場景中物體的AABB包圍盒是一個六面體,其坐標系對於二維坐標系來講只是多了一個Z軸,所以實際上在三維場景中物體的AABB碰撞檢測依然可以采用四個點信息的判定來實現,即從物體A的八個頂點與物體B的八個頂點分別選出兩個最大與最小的頂點進行對比。碰撞的示意如下圖:
三維場景中AABB碰撞檢測具有如下規則:物體A與物體B分別沿三個坐標軸做投影,只有在三個坐標軸都發生重疊的情況下,兩個物體才意味着發生了碰撞。
實現代碼如下,其中min和max數組是另一個AABB的最小點和最大點,最后返回碰撞檢測結果和碰撞部分的AABB。
運動多面體
在使用單步碰撞檢測時,存在時間步長較大時會發生兩個物體完全穿透而算法卻未檢測出來的問題,如下圖所示。通常的解決方法是產生一個4D空間,在單位時間步長內,在物體運動的開始和結束時間之間產生一個4D超多面體,又稱運動多面體,用於穿透測試。
對一個三維物體網格化處理后,需要對三維物體內的子網格做碰撞監測,子網格是規則的立方體。在單位時長內,連接開始和結束時刻物體的最大包絡線得到的就是運動多面體。其中,通過求取垂直物體運動方向上的寬度就可以得到包絡線的寬度,可以應用旋轉的方法。
AABB碰撞檢測算法雖然計算方法簡單,速度快,但是僅適用於精度要求不高的場合中。相對於AABB碰撞檢測,還有一種更逼近物體並更為精確的一種算法--OBB碰撞檢測。
OBB
未完待續
參考文獻和資源(不分先后)
[1] Gottschalk, Stefan, Ming C. Lin, and Dinesh Manocha. "OBBTree: A hierarchical structure for rapid interference detection." Proceedings of the 23rd annual conference on Computer graphics and interactive techniques. ACM, 1996.
三維物體AABB碰撞檢測算法
適合新手的3d碰撞檢測
船舶碰撞危險度的計算方法比較(非匿名)