ODE v0.5 用戶指南

 1. 介紹
ode是一個免費的具有工業品質的用於模擬關節連接的剛體動力學的庫。 舉例來說,模擬地面上的車輛,有腿的動物, 虛擬環境中的可移動物體,它是快速,靈活和強健的。而且它有內建的碰撞檢測系統。 ode由Russell Smith 在一些貢獻者的幫忙下開發的。
如果剛體模擬對你來講沒有多大意思,檢出什麼是物理SDK。(If ``rigid body simulation'' does not make much sense to you, check out What is a Physics SDK?)

這是0.5版本的ode的用戶手冊,儘管版號比較低,但它已經是相應成熟和穩定了。

1.1. 特徵
ode 在模擬用關節連接的剛體結構方面做的很好。 當各種不同形狀的剛體被各種不同類型的關節連接在一起的時候,一個被以關節連接的結構被建立。 例如地面上的車輛 (輪子被連接到底盤) ,有腿的動物 (腿被連接到身體) 或很多的物件。
ode被設計被用於交互式或即時摹擬。 對模擬虛擬現實環境中的可移動物體做的特別地好。因爲它快速, 強健和穩定.而且使用者在模擬環境中有完全的自由改變結構。

ode使用一個高度穩定的積分器,所以可以控制摹擬錯誤不會太大。 它的實際意義是被模擬的系統不會沒有理由的 " 崩潰" 。 (相信我,如果你不小心,這在很多其他模擬器上將會發生) ode強調在物理準確度上的速度和穩定度。

ode有硬連接。 這意謂,每當二個實體碰撞時,一個特別的非滲透約束被用。 替代選擇,在許多其他的模擬器中用, 是使用虛擬的彈簧表現觸體。 這難以做對事情和極端地錯誤-俯伏的。

ode有一個內建的膨脹檢測系統。 如果你想要,你可以不用它而使用你自己的碰撞檢測。 目前的碰撞原型包括 球, 盒子, 圓柱體, 平面,射線和三角形的網格  -以後會加入更多的碰撞物件。 ode的碰撞系統通過"spaces"的概念實現相交物體的快速識別.

特徵:

3.概念
3。1背景
這裏我寫一些關於剛體的動態仿真的背景信息,同時請參看SIGGRAPH tutorial.
3.2剛體
一個剛體從模擬的角度說具有各種屬性,有些屬性隨時間而變化:
●物體參照點的座標頂點(x,y,z),該點必須是此物體的重心。
●參照點的線性速度,一個矢量(vx,vy,vz)
●物體的方向,代表物體的quaternion(qs,qx,qy,qz)或者一個3X3的旋轉矩陣
●用來描述隨時間變化的方向的變化量的角速度矢量(wx,wy,wz)
其他物體屬性常常是固定的:
●物體的質量
●參照點物體的質心的位置,在現在的方案中質心必須與參照點物體的位置一致,即與物體的position座標一致.
●單位矩陣,描述物體的質量是如何分存於重心的。
每個物體都內含一個(x,y,z)座標系統,物體隨它而移動和旋轉。參加圖1.
這個座標系統以物體的重心爲原點,在ode中,部分值是相對於物體座標系統的,其它的相對於全局座標系統。

注意:物體的形狀不是動態屬性(insofar除外),只有在碰撞檢驗的時候才關心物體的形狀這一細節
3。2。1島和物體失效
   “物體”通過“關節”而聯繫在一起,一個物體“島”是一組不能分開的物體-換種說法就是每個物體在模擬時島內的物體是連接在一起的
    在“世界”的模擬步中每個“島”是作爲獨立物體對待的,認識到這點非常重要,如果在模擬時有N個相似的“島”,那麼它的時間複雜度爲O(N)。
    每個物體可以被設置爲“有效”enabled和“無效”disabled,無效的物體被有效地“關掉”,在每個模擬步中無須更新狀態。當發現物體不運動或與模擬無關時,將物體設置爲無效是一個非常有效的提高計算速度的方法。
    如果在一個島內有若干個物體被設爲有效時,則島內所有物體在下一個模擬步中將變爲有效。要使一個島無效必須先讓島內的物體均無效纔行。如果一個無效的島接觸到另一個有效的物體,則整個島將有效,就象一個關節將一個有效物體連接到島上一樣。
3。3綜合
按時間模擬剛體系統的過程被稱爲整合,每個整合步按當前時間和步進時間進行,從而調節每一個新時間的物體狀態。在整合時有兩種主要的關心的問題“
●要多精確?就是需要與現實世界的狀態有多相似
●要多穩定?就是,在計算時非物理因素引起的計算錯誤?
ODE的當前的整合相當穩定,但是在模擬步不是很小的情況下不是特別精確。對於大多數應用來說ODE足已應付。ODE的行爲模擬在幾乎所有的應用中都很出色。然而ODE在未來的版本完善精確度之前不應用於工程應用。
3。4力的聚集
    在每個整合步之間用戶可以調用函數來給剛體施加力,這些力將合起來成爲“力聚集”作用於剛體對象之上。當下一個整合步發生時,所作用與物體上的力合在一起推動物體。每一個整合步後力聚集將變爲零。
3。5聯結和約束
一個節點就像現實中門軸,用來連接兩個物體。在ODE中聯結非常普遍:它是兩個物體之間相互作用的關聯,以使物體間只能保持相應的位置和方向。這種關聯叫作約束--聯結和約束兩個次常常可以換用常見的有ball and socket,hinge,slider約束
示例中第一個是ball 和 socket 關聯,第二個是門柚,在門軸方向上關聯部分。第三個是piston 和 socket(槽)活塞式關聯。
任何時候整合系統將根據力聚集計算所有關節的步進,它同時保持所有的節點關聯。
每個節點的參數中都有一個數字用來控制它的幾何體,一個示例是球-活塞式節點的位置,這個函數用於設置節點的所有全局座標參數。這樣做的一個結果就是節點必須在被連接前被正確的移動位置。
3。6聯結組
一個聯結組是一種特別的容器用來管理世界上的聯結s,聯結可以加到一個組,當這些聯結不再需要作爲一個整體是可以用一個函數很快的銷燬。然而,組中的獨個聯結在這個組爲空前不能單獨銷燬。當需要從世界中在任何時間步中從組中添加或刪除關聯節點時,這是最有用的。
3。7聯結錯誤和錯誤消減參數(ERP)
   當一個聯結關聯兩個物體時,物體需要處在合理的位置和方向上。然而有可能物體所處位置是聯結不能匹配。這種聯結錯誤在兩中情況下發生:
1、如用戶給一個物體設置了錯誤的位置/方向,而另一個物體的不匹配。
2、在模擬是,物體從他們的位置移位了。(見圖)
   以下是減小聯結錯誤的機制:在每模擬步中對物體施加特殊的力以使之回到正確的位置。這個力有erro reduction parameter來控制,值在(0-1)間。
    ERP指出在下一個模擬步中聯結錯誤須調整的比例。如果ERP=0則不調整,物體最終將飄離模擬過程。如果ERP=1則模擬將在下一個模擬步中盡力修正所有的錯誤。然而將ERP設置爲1並不推薦,在很多情況下並不是每個聯結錯誤都需要修正的,建議設置ERP爲0.1-0.8,默認爲0.2。
    可以用一個全局的ERP變量來影響所有的聯結,也可以讓一些聯結具有局部的ERP變量。
3.8柔約束和約束力混合(CFM)
   自然的大多數約束事“硬的”,這就意味着這樣的約束描述條件將永遠不不能違反,舉個例子,球必須始終再soket種,hinge的兩給部分必須始終對其的。再實際的約束中可能被無意識的錯誤的介入到系統中而違反,但是ERP可以更正這些錯誤。
    不是所有的約束都是“硬的”,設計的一些“軟”約束又是會被違背,第二種事CFM值,可以描述如下
3.8.2如何使用ERP和CFM
    ERP和CFM可以被獨立的設置到一些joints裏。它們能被設置到對接joints,再限制joint和各種其他位置,來控制jonit和joint limit柔軟讀和彈性度
    如果CFM設置成0,約束將很牢固,如果CFM設置爲一個正值,可能將違反約束的“推動它pushing on it”(例如:對於對接連接將使兩個物體連到一起)。換句話說約束將是柔的,而且軟度隨CFM變量的增大而更柔。其本質上是約束將
    注意:將cfm設置爲負值將帶來不好的影響,如不穩定,請不要這樣做。
    通過調整ERP和CFM我們能達到各種效果。如你可以模擬彈簧約束,將兩個物體用一個彈簧聯繫。或者你可以模擬更柔性的約束。無需震動。事實上,erp和cmf可以用來完成任何想要的彈簧和減震係數的效果。如果你的彈性係數是Kp減震係數是Kd,則相應的ODE係數爲ERP=hKp/(hKp+Kd)  cfm=1/(hKp+Kd) 其中h是setpsize。這些值將完成彈簧減震系統模擬的一樣的效果
    增大DFM,特別是全局的CFM將減少模擬中的算術錯誤。如果系統是near-singular的,則能夠顯著的提高穩定性。事實上如果系統是mis-behaving的,要做的第一件事就是增大全局的CFM。
3.9碰撞處理
bodies之間的碰撞或bodies和靜態環境之間的碰撞按以下方法處理:
1、在每個模擬步之前,用戶調用碰撞檢查(collsion detection)函數去決定事什麼碰上什麼。這些函數將返回一個contact point的列表(list),每個(接觸點)contact point指明一個空間位置,一個曲面法向量(normal vector),和一個有效深度(penetration depth)
2、系統將爲每一個接觸點創建一個特定的接觸聯結(contact joint)。一些關於聯結的額外信息將給contact jiont,例如當前接觸面的摩擦力,以及強弱程度以及其他各種屬性
3、contact joints被放入到一個joint“group”,這樣以允許非常快速的從系統裏添加和移除,模擬速度隨着接觸數量的上升而下降,所以有不同的策略用來限制接觸點的數量
4、一個simulation step 發生
5、將所用的contact joints重系統中移除。
注意內建的碰撞檢測函數並不一定要使用,其他的碰撞檢測庫也能使用,只要它能提供正確的接觸點的信息。
3.10典型的模擬代碼
一個典型的模擬將按下面的方式進行:
1、創建一個仿真世界dynamics world
2、在動態世界中創建物體bodies
3、設置所有物體的狀態,如位置等
4、在動態世界中創建聯結joints
5、將聯結與物體綁定attach
6、爲所有的聯結設置參數parameter
7、按需要創建一個碰撞世界clllision world和碰撞幾何體collisiion geometry objects
8、創建一個聯結組joints group來保持接觸聯結contact joints
9、Loop
 1、按需給物體施加力
 2、按需調整joint的參數
 3、調用碰撞檢測
 4、爲每個碰撞點collision point創建一個接觸聯結,並將其放入到contact joint group(8)
 5、執行一個模擬步simulate step
 6、移除在contact joint group中所有聯結joints
10、銷燬動態(1)和碰撞世界(7)
3.11物理模型
| fT | <= mu * | fN |
4.數據類型和轉換
4.1 基本數據類型
ode 庫可以用單或雙精度數據類型來build. 單精度數據快並且佔用更少的內存,但會出現更多的數學錯誤。你可以用它來創建一個不是很精確和穩定的系統。floating point 是 dReal.  其它的使用dVector3, dVector4, dMatrix3, dMatrix4, dQuaternion.
4.2 對象和標識
有不同種類的對象可以被創建:
  dWorld -一個仿真世界
 dSpace - 一個碰撞世界
 dBody  - 一個剛體
 dGeom - 幾何體(爲了碰撞)
 dJoint - 節點
 dJointGroup - 節點組
函數處理對象返回對象ID, ID類型包括 dWorldID, dBodyID.
4.4 C 相對 C++
odE庫寫於C++.但安支持於簡單C函數環境。
5.World
"世界"對象是一個容納剛性物體和joints的容器。再不同世界中的對象不能相互影響,例如再不同世界中的兩個剛體不能碰撞
一個世界中所有的物體存在與同一個時間點,這就是通過分離的時間模擬不同頻率的一個原因,大多數程序只需要一個世界。
dWorldID dWorldCreate();創建一個新的空的世界並返回一個ID數。
void dWorldDestroy (dWorldID);銷燬一個世界和裏面的所有東西。包括所有的body、不屬於一個joint group的joint。屬於一個joint group的joint將變得無效,可以調用銷燬,例如用dJointGroupEmpty.
void dWorldSetGravity (dWorldID, dReal x, dReal y, dReal z);
void dWorldGetGravity (dWorldID, dVector3 gravity);
設置和得到世界的全局重力加速度vector。單位是m/s/s,則地球的重力加速度就是(0,0,-9.81),假設+z向上. 缺省的是無重力加速度i.e. (0,0,0).
void dWorldSetERP (dWorldID, dReal erp);
dReal dWorldGetERP (dWorldID);
設置和得到世界的全局ERP值,典型值是0.1--0.8. The default is 0.2.
void dWorldSetCFM (dWorldID, dReal cfm);
dReal dWorldGetCFM (dWorldID);
設置和得到世界的全局CFM,典型值是10-9 -- 1. 缺省是單精度時10-5 if , 或雙精度時10-10。
void  dWorldSetAutoDisableFlag (dWorldID, int do_auto_disable);
int   dWorldGetAutoDisableFlag (dWorldID);
void  dWorldSetAutoDisableLinearThreshold (dWorldID, dReal linear_threshold);
dReal dWorldGetAutoDisableLinearThreshold (dWorldID);
void  dWorldSetAutoDisableAngularThreshold (dWorldID, dReal angular_threshold);
dReal dWorldGetAutoDisableAngularThreshold (dWorldID);
void  dWorldSetAutoDisableSteps (dWorldID, int steps);
int   dWorldGetAutoDisableSteps (dWorldID);
void  dWorldSetAutoDisableTime (dWorldID, dReal time);
dReal dWorldGetAutoDisableTime (dWorldID);
設置和得到缺省的新創建的bodies的auto-disable參數.見6.5對特性auto-disable的描述。缺省的是:
AutoDisableFlag = disabled
AutoDisableLinearThreshold = 0.01
AutoDisableAngularThreshold = 0.01
AutoDisableSteps = 10
AutoDisableTime = 0

void dWorldImpulseToForce (dWorldID, dReal stepsize,dReal ix, dReal iy, dReal iz, dVector3 force);
如果你象對一個剛體施加一個線性的或角方向的的推力,你可以再調用dBodyAdd...前用這個函數去將需要的推力轉化成force/torque vector,而不用一個力或扭矩。這個函數是將給定的一個力(ix,iy,iz)轉換到force vector中,當前的算法只是簡單的比例縮放impluse by 1/stepsize,1/stepsize是下一個step的size。這個函數中要用到dWorldID是爲了將來的力的計算中可能作爲綜合參數用作world的屬性。

void dCloseODE();
用來回收那些通過普通銷燬函數不了回收的內存空間,比如dWorldDestroy函數。你可以再程序的最後使用它來防止內存泄漏。

5.1. Stepping Functions

void dWorldStep (dWorldID, dReal stepsize);
Step the world. 這裏用一個“大矩陣”的方法耗時間類似m^3和內存類似m^2,這個m是約束行的總數。對於大系統這種方法會很慢,當這是目前最精確的方法。
void dWorldQuickStep (dWorldID, dReal stepsize);
Step the world. 這裏用一個迭代器(iterations)的方法耗時間爲類似m*N而內存爲類似m,這個m是約束行的總數(total number of constraint rows),而N是迭代器的數量。對大系統會比用dWorldStep快,但是精度會低一點。
QuickStep非常適合與對象堆棧特別是當在使用auto-disable屬性。無論如何,它對near-singular系統而言具有非常差的精度。near-singular系統會在使用高摩擦contact,motors,或確定精度near-singular系統是發生。舉例:一個多腿的機器人站在地上是可能就是near-singular。
這裏有些方法來克服QuickStep不精確問題:
 增大CFM。
 減小你係統種contacts的數量(如:爲機器人或生物使用最小數量的腳)。
 不要過多的爲contacts使用摩擦friction。
 適當的使用contact slip。
 避免運動學上的循環(儘管kinematic loops對有腿生物是不可避免的)。
 不要對motor使用過大的力strength。
 用基於力的motor來代替基於速度的motor。Use force-based motors instead of velocity-based motors.
 增加QuickStep iterations的數量也有一點點幫助,但是但你的系統是一個真正的near singular時沒多大幫助。

void dWorldSetQuickStepNumIterations (dWorldID, int num);
int dWorldGetQuickStepNumIterations (dWorldID);
設置得到QuickStep方法種沒步使用的迭代器的數量。越多迭代器越精確也越慢。缺省爲20個iterations.

5.2. Contact Parameters
void dWorldSetContactMaxCorrectingVel (dWorldID, dReal vel);
dReal dWorldGetContactMaxCorrectingVel (dWorldID);
設置得到contacts允許產生的最大的正確速度。缺省時無窮大,即沒限制。減小這個數值可以幫助防止"popping" of deeply embedded objects。
void dWorldSetContactSurfaceLayer (dWorldID, dReal depth);
dReal dWorldGetContactSurfaceLayer (dWorldID);
設置得到所有幾何物體的表面深度。contact按給定的允許沉入物體表面。缺省的值爲0,可以增大一點點如0.001來幫助防止物體接觸時不停跳動的問題(jittering problems)。
6. 剛體函數
6.1. 創建和銷燬物體
dBodyID dBodyCreate(dWorldID);

在指定世界中位置(0,0,0)創建一個物體,它使用默認的參數集合創建,返回物體ID.

void dBodyDestroy(dBodyID);

註銷一個物體,所有關聯到它的關節都將變的無用。(即它們不影響仿真環境,但它們也不會被刪除)

6.2. 位置和方位
void dBodySetPosition;(dBodyID , dReal x , dReal y,dReal z)
void dBodySetRotation;(dBodyID,const dMatrix3 R)
void dBodySetQuaternion;(dBodyID,const dQuaternion q)
void dBodySetLinearVel;(dBodyID , dReal x , dReal y,dReal z)
void dBodySetAngularVel;(dBodyID , dReal x , dReal y,dReal z)
const dReal* dBodyGetPosition(dBodyID);
const dReal* dBodyGetRotation(dBodyID);
const dReal* dBodyGetQuaternion(dBodyID);
const dReal* dBodyGetLinearVel(dBodyID);
const dReal* dBodyGetAngularVel(dBodyID);

這些函數設置或返回物體的位置, 旋轉, 線速度和角速度。 在設置了一組物體後, 如果在新的設置中與當前的連接/約束不一致的話,摹擬的結果將變的不明確。當返回時,返回的是指向內部數據結構的一個指針,所以當做任何改變時這些改變會影響到物體.
dBodyGetRotation 返回一個4x3 旋轉矩陣


6.3. 質量和力
void dBodySetMass (dBodyID, const dMass *mass);
void dBodyGetMass (dBodyID, dMass *mass);

設置/返回物體的質量

void dBodyAddForce            (dBodyID , dReal fx , dReal fy,dReal fz);
void dBodyAddTorque           (dBodyID , dReal fx , dReal fy,dReal fz);
void dBodyAddRelForce         (dBodyID , dReal fx , dReal fy,dReal fz);
void dBodyAddRelTorque        (dBodyID , dReal fx , dReal fy,dReal fz);
void dBodyAddForceAtPos       (dBodyID , dReal fx , dReal fy , dReal fz,
                                        dReal px , dReal py,dReal pz);
void dBodyAddForceAtRelPos    (dBodyID , dReal fx , dReal fy , dReal fz,
                                        dReal px , dReal py,dReal pz);
void dBodyAddRelForceAtPos    (dBodyID , dReal fx , dReal fy , dReal fz,
                                        dReal px , dReal py,dReal pz);
void dBodyAddRelForceAtRelPos(dBodyID , dReal fx , dReal fy , dReal fz,
                                        dReal px , dReal py,dReal pz);

給物體加力(絕對/相對座標) 。 力被聚集到每一個物體上,當經過一個時間步長時力集聚將變爲0.
...RelForce and ...RelTorque functions  力向量將相對於物體自已的座標系統。

 ...ForceAtPos and ...ForceAtRelPos  在一個外部點施加一個力(在全局或物體相對座標系), 其它函數都是作用於物體的質心的。


const dReal* dBodyGetForce(dBodyID);
const dReal* dBodyGetTorque(dBodyID);

返回力聚集和扭矩向量,返是一個指向3個dReal的數組指針,它是指向內部結構的,所以對它的改變會影響到剛體。

void dBodySetForce;(dBodyID b , dReal x , dReal y,dReal z)
void dBodySetTorque;(dBodyID b , dReal x , dReal y,dReal z)

設置物體的力和扭矩向量,它可以在物體有效前將其力和扭矩向量置爲0,
設定本文力和轉矩聚積矢量。 在他們被使恢復現役之前 , 這大概是有用爲移除的本文對準零位力和轉矩,在增加力的功能被拜訪他們的外殼中當他們被移除的時候。
6.4. 公用程式
無效 dBodyGetRelPointPos(dBodyID , dReal px , dReal py , dReal pz,
                          dVector3 結果);
無效 dBodyGetRelPointVel(dBodyID , dReal px , dReal py , dReal pz,
                          dVector3 結果);
無效 dBodyGetPointVel    (dBodyID , dReal px , dReal py , dReal pz,
                          dVector3 結果);

公用程式輪流在一個本文 (px , py,pz) 上的一個點並且返回全球的座標那個點的位置或速度的功能.(在結果中) dBodyGetRelPointXXX 功能有本文的點比較的座標,而且 dBodyGetPointVel 功能有全球的座標點。

無效 dBodyGetPosRelPoint(dBodyID , dReal px , dReal py , dReal pz,
                          dVector3 結果);

這是 dBodyGetRelPointPos 的相反。 資訊科技輪流全球的座標 (x , y,z) 的一個點並且返回本文- 比較的座標 (結果) 的點位置。

無效 dBodyVectorToWorld(dBodyID , dReal px , dReal py , dReal pz,
                           dVector3 結果);
無效 dBodyVectorFromWorld(dBodyID , dReal px , dReal py , dReal pz,
                           dVector3 結果);

給予一個被表達在本文 ( 或世界) 中坐橋系統 (x , y,z) 的矢量, 替換它到世界 ( 或本文) 坐橋系統 (結果).
6.5. Automatic Enabling and Disabling
    每個body可以被啓用和禁用。啓用的body參與模擬,而禁用的body被關閉,而且在一個simulation step種不會更新狀態。新的body創建時始終時啓用狀態。一個禁用的body通過joint聯結到一個啓用物體上後在下一個simulation step時自動被重啓用
被禁用body不消耗CPU時間,因此要提高速度就要當物體處於rest時disabled掉。這個事情可以交給auto-disabled特性自動處理。
    如果一個body將auto-disable標誌打開,當它空閒了一個給定的模擬步時時它將自動的被disabled。
    它也可以通過給定一個模擬時間來時狀態處於idle空閒。
    當一個body的線速度和角速度低於一個給定極限值時將被認爲時空閒的。因而沒個body有5個auto-disabled參數:一個enabled標誌,一個空閒步數,一個空閒時間,一個線/角速度的閾值。新創建的body的這些參數從world上得到。
    一下時設置和得到body的enable/disable參數的函數。
    void dBodyEnable (dBodyID);
    void dBodyDisable (dBodyID);
    手動的啓用禁用body。注意一個禁用的body通過joint聯結到一個啓用物體上後在下一個simulation step時自動被重啓用。

int dBodyIsEnabled (dBodyID);如果一個body被enabled返回1時否則返回0。
void  dBodySetAutoDisableFlag (dBodyID, int do_auto_disable);
int   dBodyGetAutoDisableFlag (dBodyID);
設置和得到body的auto-disable標誌。如果一個auto-disable非零則body將在idle了足夠長的時間後自動disabled。
void  dBodySetAutoDisableLinearThreshold (dBodyID, dReal linear_threshold);
dReal dBodyGetAutoDisableLinearThreshold (dBodyID);
linear_threshold設爲無窮大dInfinity時將不考慮線速度因素。

void  dBodySetAutoDisableAngularThreshold (dBodyID, dReal angular_threshold);
dReal dBodyGetAutoDisableAngularThreshold (dBodyID);
angular_threshold設爲無窮大dInfinity時將不考慮角速度因素。

void  dBodySetAutoDisableSteps (dBodyID, int steps);
int   dBodyGetAutoDisableSteps (dBodyID);
設steps爲零時不考慮該因素

void  dBodySetAutoDisableTime (dBodyID, dReal time);
dReal dBodyGetAutoDisableTime (dBodyID);
設time爲零時不考慮該因素

void  dBodySetAutoDisableDefaults (dBodyID);
從world那裏得到缺省的auto-disable參數

6.6. Miscellaneous Body Functions輔助的body函數
void  dBodySetData (dBodyID, void *data);
void *dBodyGetData (dBodyID);
設置和得到body的用戶數據指針user-data pointer.
 

void dBodySetFiniteRotationMode (dBodyID, int mode);
這個函數時用來控制每step中更新body的orientation的方式。mode可以是:
0:“infinitesimal”無窮小。這是計算速度最快的方式,但是當body高速轉動是會偶爾導致不精確,特別是這些bodies joined to 其他bodies。這是body創建時的缺省值。
This function controls the way a body's orientation is updated at each time step. The mode argument can be:
1:“finite”,這是一個比較昂貴的計算方式,但在高速轉動時會更加精確。注意高速轉動可以導致很多種類型的計算錯誤,它只是可以解決其中的一個錯誤。

int dBodyGetFiniteRotationMode (dBodyID);返回當前的mode(0 or 1).

void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z);
設置body的有限旋轉軸,只有當用了finite rotation mode 1 時纔有效,如果軸是(0,0,0)full finite rotations are performed on the body。如果這個軸不是零,body

If this axis is nonzero, the body is rotated by performing a partial finite rotation along the axis direction followed by an infinitesimal rotation along an orthogonal direction.
××××××
這能減輕body快速旋轉導致的確定錯誤源的錯誤。例如,一個車的輪子轉的很快時,你可以調用這個函數,用車輪hinge axis作爲參數來提高其性能。This can be useful to alleviate certain sources of error caused by quickly spinning bodies.

void dBodyGetFiniteRotationAxis (dBodyID, dVector3 result);返回這個軸

int dBodyGetNumJoints (dBodyID b);返回與這個bodyjoint的body數

Return the number of joints that are attached to this body.

dJointID dBodyGetJoint (dBodyID, int index);通過索引返回與這個body相關的joints,索引從0 to n-1 ,n是通過dBodyGetNumJoints得到的。

void dBodySetGravityMode (dBodyID b, int mode);
int dBodyGetGravityMode (dBodyID b);
設置和得到body是不是受world的重力加速度的影響。如果mode爲非零,受影響,爲零不受。新建的body總是受影響的。

10. Collision Detection
    ODE有兩個主要的組件:一個動態的仿真引擎和一個碰撞檢測引擎。各個body的形狀信息給到的碰撞引擎。每一個模擬步它將給出那些物體相接觸,並將這些信息用接觸點contact point的形式傳遞給用戶。用戶就可以按照這些點的信息在body間創建contact joints。使用ODE的碰撞檢測是可選項--如果可以通過其他的碰撞檢測系統來得到正確的contact信息的話。
10.1. Contact points
    如果兩個body接觸了,或者一個body接觸了它所處環境中的一個靜態物體,接觸用一個或多個contact points來表示。每一個接觸點有一個相應的dContactGeom結構:
struct dContactGeom {
  dVector3 pos;       // 接觸位置,世界座標
  dVector3 normal;    // 法向量,一個單位向量,垂直與接觸面
  dReal depth;        // 穿透深度,兩個物體相互穿透的深度,爲零時叫grazing contact,就是說剛剛碰着,這很少見,
  dGeomID g1,g2;      // 碰撞幾何體。
};

    協定是,如果body1是沿着法向量移動一段深度(或者同樣的body2沿着法向量的反方向移動一段深度)則contact depth減爲零。這就意味着法向量指入body1。
    在現實生活中,兩個body間的接觸是個複雜的問題。用接觸點來表示只是個近似的方法。接觸“片”或接觸“面”可能更確切些,但是用這樣的東西來表現對於快速仿真來說是個挑戰。
    每一個接觸點模擬中都將降低速度,因此有時因爲速度的原因我們被迫的忽略接觸點。例如,當兩個盒子相碰爲了正常表現這種狀態有許多接觸點需要表示出來,但是我們可能只選擇關鍵的3個。

10.2. Geoms
    幾何體對象(或geoms)是碰撞系統中的基本對象。一個geom能代表一個獨立的剛體(如一個球或盒子),或者它能一組其他的geoms——這種特別種類的geom叫做“space”,任何geom能與其他geom碰撞而產生零個或多個接觸點。spaces具有讓他們包含的geoms之間發生碰撞產生內部接觸點。
    geoms可以是可置位的和不可置位的。一個可置位的geom有一個位置向量和一個3*3的旋轉矩陣,就象一個剛體body,可以模擬期間被改變。一個不可置位就沒有上面說的這些功能。例如,可以用來表示環境中的不能移動的靜態物體,spaces是不可置位geoms,因爲它裏面包含得geom有可能自己的位置和方向,並不意味這space也有。
    在剛體仿真中使用碰撞引擎,可置位的geoms與剛性body對象聯繫在一起。這就允許碰撞引擎可以從bodies上得到geoms的位置和方向。注意geoms和bodies的區別是geoms具有幾何屬性(尺寸,形狀,位置和方向)但是沒有動態屬性(如速度和質量),一個body和一個geom聯繫再一起表示出了仿真對象的全部屬性。
    每一個geom是一個類的實例,如球體,平面,盒子。這是一些內建的類,在下面作描述,你也可以定義你自己的類。
    可置位geoms的參照點是它們的位置控制點。標準類的參照點通常與geom的質心是一致的,這個特性允許標準類可以容易的與動態bodies關聯。如果還需要其他的參考點,有一個transformation對象可以封裝到geom中。
    所有的應用到geoms的概念和函數將在下面描述,

10.3. Spaces
    一個space是一個容納着其他geoms的不可置位geom。有點象剛性body中“world”的概念,只是它是應用與碰撞而不是動力學。space對象的存在使得碰撞檢測加快,沒有spaces,你可能在你的仿真中要通過調用dCollide來從每對geoms中獲得接觸點來創建contacts。N個geoms的複雜度是o(N^2),這是非常昂貴的計算。
    一個更好的方法是將geoms插入到space中調用dSpaceCollide。space將執行碰撞篩選,意味着快速的標出哪些對geoms存在潛在相交。這些對將返回給一個回調函數,可以順序對他們調用dCollide。這樣就從那些無用的dCollide測試中節省了大量時間,因爲傳遞給回調函數的的對的數量將是每一個對象——對象對的數量的一小部分。
    spaces能夠容納其他的spaces。這是對劃分一個碰撞環境到幾個層次來進一步優化碰撞檢測速度是非常有效。下面將有更細節的描述。

10.4. General geom functions
The following functions can be applied to any geom.
void dGeomDestroy (dGeomID);
銷燬一個geom,首先從它所在的space中移除。這個函數可以用來銷燬所有類型的geom,但創建一個geom時你必須調用各自類型的的函數。當一個space被銷燬,如果它的cleanup mode是1(默認)則裏面所有的geoms將自動銷燬。


Destroy a geom, removing it from any space it is in first. This one function destroys a geom of any type, but to create a geom you must call a creation function for that type.
When a space is destroyed, if its cleanup mode is 1 (the default) then all the geoms in that space are automatically destroyed as well.

void dGeomSetData (dGeomID, void *);
void *dGeomGetData (dGeomID);
這些是設置和得到保存在geom中用戶定義數據指針。


void dGeomSetBody (dGeomID, dBodyID);
dBodyID dGeomGetBody (dGeomID);
    這些函數是設置和得到body相聯繫的可置位的geom。設置一個body到一個geom時將自動的將body和geom的位置向量和旋轉矩陣相關聯,所以要設置某一個的位置和方向將給兩個對象都賦值。將爲0的bodyid給一個geom並給定它自己的位置和轉向,獨立與body。如果geom以前就與一個body相關聯,那麼它的新的位置/轉向設置給爲當前body的位置/轉向。
    爲不可置位geom調用這些函數將導致在調試時出現一個運行時錯誤。

void dGeomSetPosition (dGeomID, dReal x, dReal y, dReal z);
void dGeomSetRotation (dGeomID, const dMatrix3 R);
void dGeomSetQuaternion (dGeomID, const dQuaternion);
    爲一個可置位geom設置位置向量,轉向矩陣或四元數。這些函數類似與dBodySetPosition, dBodySetRotation and dBodySetQuaternion.如果個geom被聯繫到一個body上,則body的position / rotation / quaternion將被改變。爲不可置位geom調用這些函數將導致在調試時出現一個運行時錯誤。

const dReal * dGeomGetPosition (dGeomID);
const dReal * dGeomGetRotation (dGeomID);
void dGeomGetQuaternion (dGeomID, dQuaternion result);
    前兩個時返回geom的位置向量,轉向矩陣的指針。返回的值是內部數據結構,則向量將在對geom作任何變化前都是有效的。如果geom是連到body上的,將返回body的位置向量,轉向矩陣的指針,即dBodyGetPosition or dBodyGetRotation的返回值是唯一的。
    dGeomGetQuaternion將geom's quaternion拷貝到result中,如果geom是連到body上的將返回body的四元數,即就象調用dBodyGetQuaternion的結果一樣。爲不可置位geom調用這些函數將導致在調試時出現一個運行時錯誤。

void dGeomGetAABB (dGeomID, dReal aabb[6]);
返回一個包圍着geom的aabb(axis aligned bounding box)。aabb陣列具有(minx, maxx, miny, maxy, minz, maxz)。如果geom是space,一個包含所有geoms的包圍盒將返回。如果能確定geom在上次計算包圍盒時沒有移動,這個函數可能返回一個預計算的緩存的包圍盒。

int dGeomIsSpace (dGeomID);
Return 1 if the given geom is a space, or 0 if not.
dSpaceID dGeomGetSpace (dGeomID);
Return the space that the given geometry is contained in, or return 0 if it is not contained in any space.
int dGeomGetClass (dGeomID);
Given a geom, this returns its class number. The standard class numbers are:
dSphereClass Sphere
dBoxClass Box
dCCylinderClass Capped cylinder
dCylinderClass Regular flat-ended cylinder
dPlaneClass Infinite plane (non-placeable)
dGeomTransformClass Geometry transform
dRayClass Ray
dTriMeshClass Triangle mesh
dSimpleSpaceClass Simple space
dHashSpaceClass Hash table based space
用戶定義類將返回他們自己的成員。

void dGeomSetCategoryBits (dGeomID, unsigned long bits);
void dGeomSetCollideBits (dGeomID, unsigned long bits);
unsigned long dGeomGetCategoryBits (dGeomID);
unsigned long dGeomGetCollideBits (dGeomID);
設置和得到geom的“category”範疇和“collide”的bitfields,spaces用這些bitfield來統配哪些geoms將發生交互。bitfield要保證有32bits。新建的geom缺省的category 和collide值have all bits set.

void dGeomEnable (dGeomID);
void dGeomDisable (dGeomID);
int dGeomIsEnabled (dGeomID);
啓用禁用一個geom。禁用的geoms將完全被dSpaceCollide和dSpaceCollide2忽略,雖然他們仍然是space的成員。如果geom是啓用的dGeomIsEnabled()返回1,否則返回0。新建geom是啓用狀態。
 
10.5. Collision detection
    一個碰撞檢測world是被一個創建的space以及向裏面添加的geom後創建的。在每個模擬步我們都想產生一個所有的geom相互接觸的contacts的列表。有三個函數用來做這件事情:
    a、dCollide讓兩個geom交叉併產生接觸點contact points;
    b、dSpaceCollide決定在一個space中的那些geom對有潛在相交,併爲候選的“對”調用回調函數。它步直接產生接觸點,因爲也許用戶想對一些對進行特殊處理——如忽略他們或不同的接觸產生策略。這些決定在回調函數中作出,可以選擇是否爲這些對調用dCollide。
    c、dSpaceCollide2決定在一個space中的那些geoms與另一個space中的geoms有潛在相交,併爲候選的“對”調用回調函數。它也能對一個單獨的不隸屬與space的geom進行測試。這個函數對存在一個碰撞層次時時很有用的,即當一個space中包括有另一個space時。
    碰撞系統設計得可以給用戶具有最大得靈活性來決定在那些對象間進行檢測。這就是爲什麼有3個碰撞函數得原因,例如,一個函數產生所有接觸點。
    space中可以包含space。這些下級space將代表
一個space可以包含其它space,
These sub-spaces will typically represent a collection of geoms (or other spaces) that are located near each other. This is useful for gaining extra collision performance by dividing the collision world into hierarchies. Here is an example of where this is useful:

假如在一些地形上有兩輛行駛的汽車,每個汽車都是有很多個幾何體組成,如果所有的幾何體都被插入到相同的space,則碰撞檢測時間將與這引起車的幾何體總數成正比,或是這些數目的平方,這依賴於採用哪種space類型。

一個加速的方法是爲每個汽車創建一個space, 將每個汽車的幾何體都加入進來,然後將這個space加入到最外層space, 在最外層space每個時間步中調用dSpaceCollide進行車之間的碰撞測試,(實際是在它們的綁定盒上進行的),然後再在汽車的space中調用dSpaceCollide2進行進行幾何體之間的碰撞測試。這有利於加減少浪費的時間。

If space hierarchies are being used then the callback function may be called recursively(遞歸), e.g. if dSpaceCollide calls the callback which in turn calls dSpaceCollide with the same callback function. In this case the user must make sure that the callback function is properly reentrant(重入).

Here is a sample callback function that traverses through all spaces and sub-spaces, generating all possible contact points for all intersecting geoms:

  void nearCallback (void *data, dGeomID o1, dGeomID o2)
  {
    if (dGeomIsSpace (o1) || dGeomIsSpace (o2)) {
      // colliding a space with something
      dSpaceCollide2 (o1,o2,data,&nearCallback);
      // collide all geoms internal to the space(s)
      if (dGeomIsSpace (o1)) dSpaceCollide (o1,data,&nearCallback);
      if (dGeomIsSpace (o2)) dSpaceCollide (o2,data,&nearCallback);
    }
    else {
      // colliding two non-space geoms, so generate contact
      // points between o1 and o2
      int num_contact = dCollide (o1,o2,max_contacts,contact_array,skip);
      // add these contact points to the simulation
      ...
    }
  }

  ...

  // collide all objects together
  dSpaceCollide (top_level_space,0,&nearCallback);

A space callback function is not allowed to modify a space while that space is being processed with dSpaceCollide or dSpaceCollide2. For example, you can not add or remove geoms from a space, and you can not reposition the geoms within a space. Doing so will trigger a runtime error in the debug build of ODE. 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章