*************************************************
* 2005-01-20 *
* ■~world() QQ:282956838 翻译 大家一起完善:P *
* 有不当的地方可以批评指正,不能嘲笑!很伤自尊的*
* 有修改补充的,请将修改后的文件发给作者一份谢谢 *
* 部分内容有lua添加:QQ::382689788,希望对大家有 *
* 用khzide@163.com 感谢~world 提供大量翻译文档 *
* 使我节省了不少时间 *
*************************************************
--------------------------------------------------------------------------------
ODE v0.5 用户指南
Russell Smith
星期六 2004 年五月 29 日
--------------------------------------------------------------------------------
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.