1. 程式人生 > >ODE手冊(6)關節型別和相關函式

ODE手冊(6)關節型別和相關函式

6.1 關節的建立和銷燬

dJointID dJointCreateBall (dWorldID, dJointGroupID);

dJointID dJointCreateHinge (dWorldID, dJointGroupID);

dJointID dJointCreateSlider (dWorldID, dJointGroupID);

dJointID dJointCreateContact (dWorldID, dJointGroupID, const dContact *);

dJointID dJointCreateUniversal (dWorldID, dJointGroupID);

dJointID dJointCreateHinge2 (dWorldID, dJointGroupID);

dJointID dJointCreatePR (dWorldID, dJointGroupID);

dJointID dJointCreatePU (dWorldID, dJointGroupID);

dJointID dJointCreatePiston (dWorldID, dJointGroupID);

dJointID dJointCreateFixed (dWorldID, dJointGroupID);

dJointID dJointCreateAMotor (dWorldID, dJointGroupID);

dJointID dJointCreateLMotor (dWorldID, dJointGroupID);

dJointID dJointCreatePlane2D (dWorldID, dJointGroupID);

一個新建立的給定型別的關節,因為它並沒有連線到任何一個剛體上,所以就會被初始化為“監獄模式(也就是說對當前的模擬是沒有任何作用的)”。一般情況下分配給它的關節組ID為0,如果分配給它的是一個指定關節組的非零ID,那麼就需要使用一個給定的dContact結構來初始化連線關節。

void dJointDestroy (dJointID);

如果需要銷燬一個關節、斷開和它連線的剛體或者是把它從world中移除,就可以使用上面的dJointDestroy函式。需要注意的是,如果銷燬的關節式一個關節組的一員的話,這個函式時無能為力的,因為需要銷燬的關節它所在的組必須為空或者是已被銷燬的。

dJointGroupID dJointGroupCreate (int max_size);

上面的函式用來建立一個關節組,引數max_size在現在的版本中已經不再使用,但為了保證程式的向後相容性,請將其置為0。

void dJointGroupDestroy (dJointGroupID);

void dJointGroupEmpty (dJointGroupID);

dJointGroupDestroy函式用來銷燬一個關節組,包含在該組中的所有關節也同時被銷燬掉。dJointGroupEmpty函式則只是銷燬掉關節組中的所有關節,並不將關節組銷燬掉,也就是說它的作用是清空一個關節組。

6.2 其它一些關節函式

在對關節模型的處理中,通常會需要對關節進行各種各樣的操作以完成不同場景的不同需求。下面扼要地介紹一些常用的關節函式,以幫助對關節模型的瞭解。

void dJointAttach (dJointID, dBodyID body1, dBodyID body2);

有時候我們會需要將一個關節從一個已連線的剛體上分離下來之後連線到另一個剛體上,或者需要給關節連線一個新建立的剛體等等,這樣的操作就可以通過上面的函式來實現。在對一個處於連線狀態的關節進行此操作時,首先會斷開已連線的剛體,然後再將它連線到一個新的剛體上。另外,如果只需要給關節連線一個剛體,就需要將body1或者body2置為0(0在這裡代表的是一個靜態的環境)。如果將這兩個引數都置為0的話,關節就被轉變為“監獄模式”,也就是說它不會對當前的模擬產生任何作用,當然在再次啟用它的時候就需要做一些必要的設定工作。

注意:像hinge-2這樣的關節需要被連線到兩個剛體之上時才會起作用。

void dJointEnable (dJointID);

void dJointDisable (dJointID);

int dJointIsEnabled (dJointID);

上面的函式分別用來啟用、禁用和獲取關節的狀態。被禁用的關節在模擬時會被完全忽略掉,但並不會丟失已經計算出的資訊(如描點和軸心)。

void dJointSetData (dJointID, void *data);

void *dJointGetData (dJointID);

設定和獲取關節的user-data指標。

int dJointGetType (dJointID);

在ODE中,關節被分為不同的型別以適應不同場合的需求。通過的JointGetType函式可以獲取到指定關節的型別,返回的關節型別必定為下面的型別場數之一。

dJointTypeBall A ball-and-socket joint.
dJointTypeHinge A hinge joint.
dJointTypeSlider A slider joint.
dJointTypeContact A contact joint.
dJointTypeUniversal A universal joint.
dJointTypeHinge2 A hinge-2 joint.
dJointTypeFixed A fixed joint.
dJointTypeAMotor An angular motor joint.
dJointTypeLMotor A linear motor joint.
dJointTypePlane2D A Plane 2D joint.
dJointTypePR A Prismatic-Rotoide joint.
dJointTypePU A Prismatic-Universal joint.
dJointTypePiston A Piston joint. 

dBodyID dJointGetBody (dJointID, int index);

返回給定關節所連線到的剛體。index = 0 時返回第一個剛體,和dJointAttach函式的引數body1相一致;如果index = 1 則返回第二個剛體,和dJointAttach函式的引數body2相一致。當這兩個值有一個為0時表示該關節只連線了一個剛體,如果兩個值均為0則表示該關節處於“監獄模式”,當然不會對模擬產生任何作用。

int dAreConnected (dBodyID, dBodyID);

如果兩個剛體被同一個關節連線,使用上面的函式時就會返回1,否則返回0;

int dAreConnectedExcluding (dBodyID, dBodyID, int joint_type);

上面的函式用來判斷連線兩個剛體的關節型別是不是joint_type,如果是返回0,如果不是則返回1。其中joint_type是一個dJointTypeXXX型的常量。這個函式在判斷是否在兩個剛體之間新增連線關節時是非常有用的,如果它們已經被非連線關節的關節連線就不適合再新增連線關節,但是卻可以給易筋經被連線關節連線的剛體之間新增更多的連線關節。

6.3 關節反饋

void dJointSetFeedback (dJointID, dJointFeedback *);

dJointFeedback *dJointGetFeedback (dJointID);

在一個world時間步長之內,施加在每一個關節上的所有力都會被計算到,這些力會直接新增到其所約束的剛體上,而且通常使用者是無法辨別每一個關節到底會施加多大的力給剛體。如果需要獲取這些資訊的話就需要使用者自己去申請一個dJointFeedback結構體並且把它的指標傳遞給寒素dJointSetFeedback()。反饋資訊的結構體定義如下:

typedef struct dJointFeedback {

  dVector3 f1; // 施加到body1上的力

  dVector3 t1; // 施加到body1上的力矩

  dVector3 f2; // 施加到body2上的力

  dVector3 t2; // 施加到body2上的力矩

}

在每個時間步長之內,跟每一個關節相關的所有的反饋結構體都會儲存相應關節的力和力矩資訊。dJointGetFeedback()函式會返回指定關節當前反饋結構體的指標,如果關節未被使用到的話就返回0(系統預設)。可以通過給dJointSetFeedback()函式傳遞引數0以金庸關節的反饋資訊。需要注意的是,一旦設定了關節的反饋結構體,就不再需要使用dJointGetFeedback()函式來獲取相應的反饋資訊,只需要在每個模擬步結束之後去檢視之前定義的結構體資訊。獲取函式只有在需要多次對特定的關節啟用反饋時才是有用的,對這種情況而言,首先得檢查關節是否已經設有一個反饋但還沒有設定另一個,這時就會禁用設定的第一個反饋。

6.3.1 返回值是有效的嗎?

顯然只要你的系統帶有或多說少的負載性,反饋系統就會產生不適當的值。約束求解只要能產生正確的結果也不會去在意力是否均勻分佈了(就是說當它們施加到各自的剛體上時就算是正確的力了)。

6.3.2 API設計的一點註解

需要使用者自己去申請這些反饋結構體看起來是很奇怪的,為什麼不靜態地為每一個關節儲存這些資料呢?原因就是並不是說所有的使用者都會去使用關節的反饋資訊,即使會使用到也不是所有的關節都需要這些資料。去靜態地儲存這些資料就是白白地浪費記憶體,特別是這些結構體會在以後的過程中為了儲存更多的額外資料而不斷增長。

為什麼ODE不去自己分配反饋結構體,而需要使用者請求時才這樣做呢?原因就是如果需要返回反饋資訊的話,連線關節(在每一個時間步中都需要建立和銷燬)就需要花費更多的時間用於分配記憶體。之所以讓使用者來分配就意味著可以提供更好的分配策略,例如給它們簡單地分配一個固定的陣列。

對這種API的替代方法就是使用一個關節力的回撥函式,雖然可以解決的問題,但仍然存在一點問題。首先,回撥函式會破壞掉ODE的APIs,並且有時會需要使用者自己去檢查一些不自然的彎曲以將資料儲存在正確的地方。其次,這樣會使得ODE在每一個模擬步中的改變顯露出來(後果很嚴重),你將不得不需要一種方法去阻止它或者需要一定的除錯去檢查它(這會使問題更加複雜化)。

6.4 關節引數設定函式

6.4.1 球窩關節(Ball and Socket)

如圖 4所示即為球窩關節示意圖:

圖 4:球窩關節

void dJointSetBallAnchor (dJointID, dReal x, dReal y, dReal z);

設定關節的錨點位置。關節會努力保持每個剛體都連線在這一點上,函式輸入的座標指定為世界座標。如果沒有剛體連線到關節上,這個函式就不會起任何作用。

void dJointGetBallAnchor (dJointID, dVector3 result);

獲取關節的錨點位置(世界座標)。這個函式返回的是錨點在body1上的位置,當然如果關節的約束完美無缺的話,這個函式所返回的值和下面的函式(獲取錨點在body2上的位置)返回的值就是一致的。

void dJointGetBallAnchor2 (dJointID, dVector3 reslut);

可以試想一下,球窩關節的作用就是盡力去保證dJointGetBallAnchor()的值和dJointGetBallAchor2()的值相等。如果關節的約束是理想的,這兩個函式的返回值在舍入誤差的範圍之內必然是相等的。當然同時使用這兩個函式就可以知道當前狀態下關節之間(球和窩之間)分開的距離。

6.4.2 合頁關節(Hinge)

如圖 5所示即為合頁關節示意圖:

圖 5:合頁關節

預設軸心為:Axis 1:x=1, y=0, z=0

void dJointSetHingeAnchor (dJointID, dReal x, dReal y, dReal z);

設定合頁的錨點引數。關節會努力保持每個剛體都連線在這一點上,輸入座標值指定為世界座標。如果沒有剛體連線到關節上,則此函式無效。

void dJointSetHingeAxis (dJointID, dReal x, dReal y, dReal z);

設定合頁的軸心引數。如果沒有剛體連線到關節上,則此函式無效。

void dJointGetHingeAnchor (dJointID, dVector3 result);

獲取指定合頁關節的錨點在世界座標系中的位置。返回錨點在body1上的位置,如果關節的約束是理想的,其和錨點在body2上的位置是相同的。

void dJointGetHingeAnchor2 (dJointID, dVector3 result);

獲取指定合頁關節的錨點在世界座標系中的位置。返回錨點在body2上的位置,如果關節的約束是理想的,其和dJointGetHingeAnchor函式的返回值是相同的。如果關節約束不理想,這兩個值就會由輕微的差別。可以用來判斷鉸鏈連線的兩個剛體分離的距離。

void dJointGetHingeAxis(dJointID, dVector3 result);

獲取合頁的軸心引數。

dReal dJointGetHingeAngle (dJointID);dReal dJointGetHingeAngleRate (djointID);

獲取合頁的角度和它對時間的導數。角度表示的是兩個剛體時間的角度或者剛體和靜態環境之間的角度,範圍為[-pi,pi]。當合頁的錨點或者軸心被設定後,就會檢查兩個剛體當前的連線角度並且這個角度必然是0。

6.4.3 插銷關節(Slider)

如圖 6所示即為插銷關節示意圖:

圖 6:插銷關節

預設軸心為:Axis:x=1, y=0, z=0

void dJointSetSliderAxis (dJointID, dReal x, dReal y, dReal z);

設定插銷關節的軸心引數。

void dJointGetSliderAxis (dJointID, dVector3 result);

獲取插銷關節的軸心引數。

dReal dJointGetSliderPosition (dJointID);
dReal dJointGetSliderPositionRete (dJointID);

獲取插銷關節的線性位置(也就是滑動關節“伸展”)和其對時間的導數(伸展率)。如果軸心被設定為從body1指向body0,關節的位置和伸展率將會隨著兩個剛體之間距離的增長而增長。

6.4.4 萬向輪關節(Universal)

如圖 7 所示即為萬向輪關節示意圖:

圖 7:萬向輪關節

萬向輪關節就像是一個球窩關節,只是多了一個額外約束自由旋轉的角度。在body1上指定axis1,在body2上指定與axis1正交的axis2,萬向輪關節會保障它們的正交性。換句話說,兩個剛體的旋轉方向的正交性和兩根軸的的正交性是相同的。

在上圖中,兩個剛體被一個十字架連線起來。axis 1連線到body1上,axis2連線到body2上十字架保證了兩根軸成90度夾角。所以如果你抓住body1並且扭動它,body2也會同時扭動。

當hinge-2關節的兩根軸相互垂直是就等價於一個通用關節,並且在懸架處呈現出完美的剛性連線特性。

萬向輪關節出現於汽車工業中,汽車的發動機產生一個沿著它自己的軸旋轉的軸(驅動軸),在某種情況下你會需要去改變軸的方向。問題就來了,如果只是彎曲驅動軸,彎曲後的驅動軸是不能在繞著它自己的軸旋轉的。所以如果把它從彎曲的位置截斷,再插入一個萬向輪關節就可以將這種約束施加到另一個軸上使得它能夠和驅動軸旋轉同樣的角度。

這種關節的另一種應用就是用來連線一種簡單的虛擬生物的肢體和身體。想象一個人伸直它的手臂,同時能夠同時上下、前後移動它,但是不能繞著它自己的軸旋轉。

預設軸心: Axis 1: x=1, y=0, z=0

      Axis 2: x=0, y=1, z=0

需要特別注意的是,兩根軸必須是正交的。如果將第一個軸的值設為第二個軸的值(反之亦然)就會導致在執行時出現錯誤,即使隨後立即改變了第二個軸的值。

下面是一些與萬向輪關節相關的函式:

void dJointSetUniversalAnchor (dJointID, dReal x, dReal y, dReal z);

設定萬向輪關節的錨點。關節會努力保持每個剛體都連線在這一點上,輸入的軸心座標必須是世界座標。如果沒有剛體連線到關節上,則此函式無效。

void dJointSetUniversalAxis1 (dJointID, dReal x, dReal y, dReal z);
void dJointSetUniversalAxis2 (dJointID, dReal x, dReal y, dReal z);

設定萬向輪關節的錨點和軸心引數。axis1和axis2必須相互垂直。

void dJointGetUniversalAnchor (dJointID, dVector3 result);

獲取指定鉸鏈關節的錨點在世界座標系中的位置,返回其在body1上的位置。如果約束是理想的,返回的值將會和錨點在body2上的位置相同。

void dJointGetUniversalAnchor2 (dJOintID, dVector3 result);

獲取指定鉸鏈關節的錨點在世界座標系中的位置,返回其在body2上的位置。

void dJointGetUniversalAxis1 (dJointID, dVector3 result);
void dJointGetUniversalAxis2 (dJointID, dVector3 reslut);

獲取萬向輪關節的軸心引數。

dReal dJointGetUniversalAngle1 (dJointID);
dReal dJointGetUniversalAngle2 (dJointID);
dReal dJointGetUniversalAngles (dJointID, dReal *angle1, dReal *angle2);
dReal dJointGetUniversalAngle1Rate (dJointID);
dReal dJointGetUniversalAngle2Rate (dJointID);

獲取萬向輪關節的旋轉角度以及其對時間的導數。角度表示一個剛體和十字架之間的角度,或者十字架和靜態環境之間的角度,取值範圍為[-pi,pi]。當萬向輪關節的錨點或者軸心被設定之後,就會檢查兩個剛體當前的連線角度並且這個角度必然是0。

6.4.5 鉸鏈-2關節(Hinge-2)

鉸鏈-2關節示意圖如圖 8所示:

圖 8:鉸鏈-2關節

鉸鏈-2關節如同兩個合頁關節以不用的軸心串聯連線在一起。如上圖所示的小車的轉向輪,一個軸允許輪子進行轉向,另一個軸則允許它進行旋轉。鉸鏈-2關節包含一個錨點和兩根軸,axis1與body1相關(如果body1作為底盤的話,axis1將作為轉向軸),axis2跟body2相關聯(如果body2作為車輪的話,axis2將作為車輪軸)。

axis1可以被關節約束並且可以有一個發動機,而axis2只能有一個發動機。

axis1可以被定義為一個懸掛軸,也就是說它的約束可以沿著軸向壓縮。

鉸鏈-2關節的axis1軸和axis2軸是相互垂直的,就如同給萬向輪關節添加了一個懸架。

它的預設軸心為:Axis 1:x=1, y=0, z=0; Axis 2:x=0, y=1, z=0

void dJointSetHinge2Anchor (dJointID, dReal x, dReal y, dReal z);

上面的函式用來設定hinge-d關節的錨點引數。錨點用於保持關節連線的各部分成為一個整體,座標引數必須是世界座標。如果沒有剛體連線到關節上,使用這個函式就是無效的。

void dJointSetHinge2Axis1 (dJointID, dReal x, dReal y, dReal z);
void dJointSetHinge2Axis2 (dJointID, dReal x, dReal y, dReal z);

設定關節的軸心引數,axis1和axis2不能沿著同一條線。

void dJointGetHinge2Anchor (dJointID, dVector3 result);
void dJointGetHinge2Anchor2 (dJointID, dVector3 result);

上面兩個函式分別用來獲取錨點在body1和body2上的位置,返回的座標為世界座標。

void dJointGetHinge2Axis1 (dJointID, dVector3 result);
void dJointGetHinge2Axis2 (dJointID, dVector3 result);

獲取hinge-2關節的軸心引數。

dReal dJointGetHinge2Angle1 (dJointID); dReal dJointGetHinge2Angle1Rate (dJointID);
dReal dJointGetHinge2Angle2Rate (dJointID);

獲取hinge-2關節的角度(繞axis1和axis2)及其對時間的導數。在設定了關節的的錨點或者軸心之後,系統會自行檢查關節連線到剛體上的位置,以保證這兩個夾角為0。

注意:當前還沒有函式用於獲取第二個角的值(即車輪繞其軸的轉角)。

6.4.6 PR 關節(Prismatic and Rotoide)

PR關節即插銷關節和合頁關節的結合體,其示意圖如下圖所示:

圖 9:PR關節

PR關節是插銷(Slider)關節和合頁關節(Hinge)的結合,它用於增加模擬中的剛體數量。通常你是不能將兩個ODE關節直接連線在一起的,它們需要有一個剛體做中間媒介。這種型別的關節的一個應用例項就是可以用它建立一個液壓活塞。

你可以通過設定這種關節的錨點來設定body2相對於合頁關節的位置(就如同合頁關節一般)。在這中關節被建立之後,系統會去計算body1和合頁關節之間的偏移距離。通過檢視關節的位置就會得到剛體的偏移位置。

第一個軸是插銷軸,稱為axis R,它的引數通過dParamX標誌訪問。

第二根軸是合頁軸,稱為axis P,它的引數通過dParamX2表示訪問。

系統中預設的PR關節軸心為:Axis R:x=1, y=0, z=0; Axis P: x=0, y=1, z=0

void dJointSetPRAxis1 (dJointID, dReal x, dReal y, dReal z);
void dJointGetPRAxis1 (dJointID, dVector3 result);

設定/獲取插銷關節的軸心。

void dJointSetPRAxis2 (dJointID, dReal x, dReal y, dReal z);
void dJointGetPRAxis2 (dJointID, dVector3 result);

設定/獲取合頁關節的軸心。

void dJointSetPRAnchor (dJointID, dReal x, dReal y, dReal z);
void dJointGetPRAnchor (dJointID, dVector3 result);

設定PR關節的錨點,錨點座標必須為世界座標。如果沒有body2連線到關節上,則使用這個函式時無效的。

dReal dJointGetPRPosition (dJointID);

獲取PR關節的線性位置(也就是插銷關節的擴充套件)。在關節的軸心被設定後,系統就會自行檢查body1的當前位置和錨點,以保證該位置為0。它表示從body1到合頁關節之間的長度:position = (Prismatic axis) dot_product [(body1 + offset) - (body2 + anchor2)]。需要注意的是axis P和axis R並不是平行的,因為這是一個新的關節。

6.4.7 PU關節(Prismatic-Universal)

PU關節示意圖如圖 10所示:

圖 10:PU關節

PU關節是對插銷關節和萬向輪關節的結合,它提供一個自由度的平移和兩個自由度的旋轉。1號軸為萬向輪的1號軸,可以通過dParamX1或者dParamX(不包括dParamFMax1)標誌來訪問它的引數。2號軸萬向輪的2號軸,可以通過dParamX2(不包括dParamFMax2)標誌來訪問它的引數。3號軸是插銷關節的軸,可以通過dParamX2標誌來訪問它的引數。

系統提供預設的軸心引數為:Axis 1:x=0, y=1, z=0; Axis 2:x=0, y=0, z=1; Axis 3:x=1, y=0, z=0

dReal dJointGetPUPosition (dJointID);

該函式用於獲取PU關節的線性位置(也就是插銷關節的伸展長度)。當錨點被設定後,系統會自行檢查body1的當前位置和錨點的位置以確定PU關節的線性位置為0(initial_offset)。

position = {(Prismatic axis) dot_product [body1 - anchor]} - initial_offset

dReal dJointGetPUPositionRate (dJointID);

獲取PU關節的線性位置對時間的導數。

void dJointSetPUAnchor (dJointID, dReal x, dReal y, dReal z);
void dJointGetPUAnchor (dJointID, dVector3 result);

設定PU關節的錨點引數,關節會保證body2和萬向輪關節之間的距離固定不變。如果關節上沒有連線body2則這個函式的使用將是無效的。

void dJointSetPUAnchorDelta (dJointID, dReal x, dReal y, dReal x, dReal dx, dReal dy, dReal dz);

設定PU關節的錨點以及每一個剛體之間的相對位置,假設body1在當前位置position + [dx, dy, dz](dz,dy,dz均為世界座標系座標)。就好像是在設定插銷關節已經伸展或者壓縮的長度。在設定錨點之後,如果呼叫了dJointGetPUPosition函式就會返回這樣一個結果:sqrt(dx*dx + dy*dy + dz*dz) * Normalize[axis3 dot_product (dx,dy,dz)]

void dJointSetPUAxis1 (dJointID, dReal x, dReal y, dReal z);
void dJointGetPUAxis1 (dJointID, dVector3 result);
void dJointSetPUAxis2 (djointID, dReal x, dReal y, dReal z);
void dJointGetPUAxis2 (dJointID, dVector3 result);

設定/獲取萬向輪關節的軸心引數,axis1和axis2必須是相互垂直的。

void dJointSetPUAxis3 (dJointID, dReal x, dReal y, dReal z);
void dJointGetPUAxis3 (dJointID, dVector3 result);

設定/獲取插銷關節的軸心引數。

void dJointSetPUAxisP (dJointID, dReal x, dReal y, dReal z);
void dJointgetPUAxisP (dJointID, dVector3 result);

上面這組函式的作用和dJOintSetPUAxis3相同。

void dJointGetPUAngles (dJointID, dReal *angle1, dReal *angle2);
dReal dJointGetPUAngle1 (dJointID);
dReal dJointGetPUAngle2 (dJointID);
dReal dJointGetPUAngle1Rate (dJointID);
dReal dJOintGetPUAngle2Rate (dJointID);

獲取萬向輪關節的角度和其對時間的導數,角度表示剛體和十字架之間的角度,或者靜態環境和十字架時間的角度,取值範圍為[-pi,pi]。當萬向輪的錨點或者軸心被設定之後,系統會自行檢查關節所連線剛體的當前位置,以保證萬向輪的角度為0。

6.4.8 活塞關節

活塞關節示意圖如圖 11所示:

圖 11:活塞關節

活塞關節類似於插銷關節,只是它可以繞著平移軸旋轉而已。

系統預設的軸心引數為:Axis: x=1, y=0, z=0

void dJointSetPistonAnchor (dJointID, dReal x, dReal y, dReal z);
void dJointGetPistonAnchor (dJointID, dVector3 result);

設定活塞關節的錨點引數。關節會盡力保持這一點相對固定在body2上,座標引數必須是世界座標。如果關節上沒有連線任何剛體,則這個函式不會起任何作用。

void dJointGetPistonAnchor2 (dJointID, dVector3 result);

以世界座標的形式獲取關節的錨點座標,返回的是錨點在body2上的座標。如果關節的約束較為理想的話,這個函式返回的值和dJointGetPistonAnchor返回的值相同。否則,兩個返回值之間會有輕微的不同,這個差值可以用來確定關節目前分開的多遠。

void dJointSetPistonAxis (dJointID, dReal x, dReal y, dReal z);
void dJointGetPistonAxis (dJointID, dVector3 result);

設定/獲取關節的軸心引數。在活塞關節的軸心設定之後,系統會自行檢查相關聯的剛體,並且將旋轉角度初始化為0。

void dJointSetPistonAxisDelta (dJointID j, dReal x, dReal y, dReal z, dReal dx, dReal dy, dReal dz);
dReal dJointGetPistonPosition (dJointID);
dReal dJointGetPistonPosition (dJointID);

獲取活塞關節的線性位置(也就是稜柱的伸展長度)。在關節的錨點設定之後,系統會自行檢查body1的當前position和錨點,並且將position初始化為0(initial_offset)(也就是說呼叫函式dJointGetPUPosition獲取body1相對於錨點的位置將會返回0.0)。position是body1和錨點之間的直線距離,position = {(Prismatic axis) dot_product [body1 - anchor]} - initial_offset。

dReal dJointGetPistonAngle (dJointID);
dReal dJointGetPistonAngleRate (dJointID);

獲取關節的旋轉角度以及其對時間的導數(旋轉率)。角度是兩個剛體之間的相對角度,或者是剛體和靜態環境之間的相對角度,取值範圍為[-pi, pi]。唯一可能的旋轉軸就是用函式dJointSetPistonAxis定義的。在活塞關節的錨點或者軸心設定之後,系統會自行檢查剛體間的position和angle,並將其初始化為0。

void dJointAddPistonForce (dJointID j, dReal force);

這個函式用於給活塞關節新增一個力並且這個力會作用在兩個剛體上,力的作用點位於剛體的質心上,方向和活塞的旋轉軸相一致。

6.4.9 固定關節

固定關節用於維持兩個剛體或者剛體和靜態環境之間保持一定的相對位置和方向。在實際應用中,除了用於除錯,這絕不是一個好方法。如果你需要將兩個剛體粘結在一起時,還不如將兩個剛體合為一個整體。

void dJointSetFixed (dJointID);

在固定關節已經獲得了一個需要的相對偏移量和兩個剛體之間的相對方位之後才可以使用這個函式。

6.4.10 接觸關節

接觸關節示意圖如圖 12 所示:

圖 12:接觸關節

接觸關節用於阻止body1和body2在接觸面上相互貫穿,它只允許剛體在接觸面的法線方向用於一個向外的速率。接觸關節通常會有一個時間步長的生命週期,它的建立和刪除完全是為了用於對碰撞檢測的響應。

通過在與接觸面法線方向正交的兩個摩擦力方向上施加特殊的力,就可實現對接觸面上摩擦力的模擬。在接觸關節被建立之後,必須以供一個結構體dContact,定義如下:

struct dContact {
  dSurfaceParameters surface;
  dContactGeom geom;
  dVector3 fdir1;
};

geom: 由碰撞函式定義的子結構體,在碰撞篇中會有所介紹。

fdir1: 第一摩擦力方向向量,用於定義一個沿著摩擦力方向的方向向量,必須是單位長度且方向正交於接觸面法線方向(所以通常情況下它正切於接觸面),只有在標誌dContactFDir1被設定為surface.mode時它才可以被定義。第二摩擦力方向向量是一個與接觸面法線方向和fdir1方向相互正交的方向向量。

surface:由使用者自己定義的一個子結構體,它的成員用於定義碰撞面的屬性,定義如下:

struct dSurfaceParameters {
  int mode;
  dReal mu;
  dReal mu2;
  dReal bounce;
  dReal bounce_vel;
  dReal soft_erp;
  dReal soft_cfm;
  dReal motion1, motion2, motionN;
  dReal slip1, slip2;
};

mode: 接觸標誌,必須設定,可以是以下一個或多個標誌的結合:

dContactMu2 If not set, use mu for both friction directions. If set, use mu for friction direction 1, use mu2 for friction direction 2.
dContactFDir1 If set, take fdir1 as friction direction 1, otherwise automatically compute friction direction 1 to be perpendicular to the contact normal (in which case its resulting orientation is unpredictable).
dContactBounce If set, the contact surface is bouncy, in other words the bodies will bounce off each other. The exact amount of bouncyness is controlled by the bounce parameter.
dContactSoftERP If set, the error reduction parameter of the contact normal can be set with the soft_erpparameter. This is useful to make surfaces soft.
dContactSoftCFM If set, the constraint force mixing parameter of the contact normal can be set with thesoft_cfm parameter. This is useful to make surfaces soft.
dContactMotion1 If set, the contact surface is assumed to be moving independently of the motion of the bodies. This is kind of like a conveyor belt running over the surface. When this flag is set, motion1 defines the surface velocity in friction direction 1.
dContactMotion2 The same thing as above, but for friction direction 2.
dContactMotionN The same thing as above, but along the contact normal.
dContactSlip1 Force-dependent-slip (FDS) in friction direction 1.
dContactSlip2 Force-dependent-slip (FDS) in friction direction 2.
dContactApprox1_1 Use the friction pyramid approximation for friction direction 1. If this is not specified then the constant-force-limit approximation is used (and mu is a force limit).
dContactApprox1_2 Use the friction pyramid approximation for friction direction 2. If this is not specified then the constant-force-limit approximation is used (and mu is a force limit).
dContactApprox1 Equivalent to both dContactApprox1_1 and dContactApprox1_2

mu:庫侖摩擦係數,必須設定,範圍為[0, dInfinity]。0表示無摩擦接觸,dInfinity表示從來不會滑動的接觸(硬接觸)。注意,計算無摩擦接觸花費的時間要少於有摩擦的接觸,無限摩擦接觸又要比有限摩擦接觸的花費少。

mu2:作用於摩擦方向2上的,可選的庫侖摩擦係數,範圍為[0, dInfinity]。只有在相關的標誌設為mode時才可以使用。

bounce:反彈係數(0..1),0意味著表面沒有一點彈性,1表示最大彈性。只有在相關的標誌設為mode時才可以使用。

bounce_vel: 反彈所需要的最小的傳入速度,傳入速度低於這個值時反彈係數為0。只有在相關的標誌設為mode時才可以使用。

soft_erp: 接點法線柔性引數。只有在相關的標誌設為mode時才可以使用。

soft_cfm: 接點法線柔性引數。只有在相關的標誌設為mode時才可以使用。

motion1,motion2,motionN: 沿著摩擦力1,摩擦力2,接點法線方向的表面速率。只有在相關的標誌設為mode時才可以使用。

slip1,slip2: 對摩擦力1和摩擦力2的FDS(Force Dependent Slip)係數。只有在相關的標誌設為mode時才可以使用。

FDS表示在物體移動時,因為受到側向的外力而產生側向滑動的現象,滑動距離正比於物體移動的速度和外力的大小,因此FDS也可稱為側向力滑動。

試想這樣一個接觸點,它的摩擦力因子mu無限大。通常情況下,如果一個力f作用於這樣的兩個接觸面上,它們之間是不會有相互滑動的。然而,如果FDS因子被設定為一個正值K,這時兩個面之間就可以相互滑動,並且以一個穩定的相互滑動速度K*f滑動。

需要注意的是,這和正常的摩擦效果是不同的:摩擦力不會在接觸面上產生一個恆定不變的加速度,它會產生一個短暫的加速度以實現速度的穩定。

這在為一些特殊的情形如輪胎建模是特別有用。例如試想一輛在道路上停止的汽車,沿著車行駛的方向推動會使它開始移動(也就是說輪胎會