这一部分主要是介绍一些在ODE中,跟刚体相关的函数
5.1 刚体的创建和销毁
dBodyID dBodyCreate (dWorldID);
在一个指定的world的原点(0,0,0)处创建一个质量参数默认的刚体,并且返回其ID(dBodyID)。
void dBodyDestroy (dBodyID);
销毁一个指定的刚体,同时与其相连接的所有关节都会被遣送到“地狱边境”(也就是说,它们跟仿真的对象不再保持连接,也不会影响到仿真对象,但并不是说会删除掉)。
5.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);
上面这些函数分别是用来设置和获取刚体的位置,旋转角度,线速度和角速度。在设置了一组刚体之后,如果新的配置参数跟当前的关节/约束不一致的话,仿真的输出就为undefined。当获取某一个属性的值时,返回的值指向内部的数据结构,因此表示这些值的向量在有任何改变作用到刚体的系统结构之前都是有效的。
dBodyGetRotation () 返回一个4x3的旋转矩阵。
5.3 质量和力
void dBodySetMass (dBodyID, const dMass *mass);
void dBodyGetMass (dBodyID, dMass *mass);
在ODE中,我们通过下面两个函数来设置和获取刚体的质量。
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);
上面的这些函数用来给系统中的刚体施加力(以相对的或者绝对的坐标)。这些力被累加到每一个刚体上,并且在每一个时间步结束后累加器会被清零。
***RelForce和***RelTorque函数施加一个相对于其自身坐标系的力向量到刚体上。
***ForceAtPos和***ForceAtRelPos函数通过一个额外的位置向量(分别以全局和相对于刚体自身的坐标)施加力到刚体上一个指定的点(即受力点)。
其它的函数施加力到刚体的质心上。
const dReal * dBodyGetForce (dBodyID);
const dReal * dBodyGetTorque (dBodyID);
在ODE中,如果要获取施加在指定刚体上的合力和扭矩的话,可以通过上面的两个函数分别得到,它们返回一个指向含有3个值的dReal型数组的指针。
void dBodySetForce (dBodyID b, dReal x, dReal y, dReal z);
void dBodySetTorque (dBodyID b, dReal x, dReal y, dReal z);
前面我们介绍了怎样获取施加在刚体上的合力和扭矩,上面的两个函数则是用来设置施加在刚体上的合力和扭矩的累积向量的。它们主要用于对处于无效状态的刚体再一次被激活之前进行力和扭矩的清零,当然在这种情况下也可以通过调用施加力的函数给处于无效状态的刚体施加力和扭矩。
5.4 运动学的状态
void dBodySetDynamic (dBodyID);
void dBodySetKinematic (dBodyID);
int dBodyIsKinematic (dBodyID);
处于“运动学”状态(代替默认的“动力学”状态)的刚体是不会自动停止的,就像是它们拥有无限的质量。这就意味着它们不会对任何力(例如:重力、约束或用户施加的力)做出任何反应,只是简单地随着速度到达下一个位置。它们的目的是控制刚体不要受到其他刚体的作用,而是通过施加在刚体之上的关节(不只是接触关节)相互作用。跟阻碍刚体间相互移动的接触关节相比,在一个连接到world(null body)的关节上使用接触的移动参数相对比较高效。处于运动学状态的两个刚体之间的约束,或者阻碍一个处于运动学状态的刚体和world的约束可以被完全忽略掉。
注:给一个处于运动学状态的刚体设置重量将会让它再一次进入动力学状态,我们可以通过调用dBodySetDynamic函数恢复刚体的源重量。因此要获取刚体的重量,在使用dBodySetMass之后随即调用dBodyGetMass是比较划算的。
5.5 功能函数(实例函数)
void dBodyGetRelPointPos (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
void dBodyGetRelPointVel (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
void dBodyGetPointVel (dBodyID, dReal px, dReal py. dReal pz, dVector3 result);
上面的功能函数可以通过指定刚体(dBodyID)上一点(px,py,pz)获取这一点的在全局坐标系中的位置或速度(返回值在result中)。dBodyGetRelPoint***函数的参数(px,py,pz)在刚体的相对坐标系中,而dBodyGetPointVel函数的参数(px,py,pz)是一个全局坐标。
void dBodyGetPosRelPoint (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
这个函数的功能跟dBodyGetRelPointPos相反,它需要给定一个全局坐标系(x,y,z)中的位置,然其返回值(保存在result中)是一个相对于刚体自身坐标系中的位置坐标。
void dBodyVectorToWorld (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
void dBodyVectorFromWorld (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
如果我们需要在body的坐标系统和world的坐标系统之间进行坐标转换时,可以通过上面的两个函数实现,转换后的坐标保存在result中。
5.6 自动化的启用和禁用
在ODE中,每个刚体都可以被启用和禁用。启用刚体会使它参与到仿真过程中,而禁用的话刚体就会被关闭并且在一个仿真步期间不再更新和它相关的数据。新创建的刚体通常处于启用状态。
如果一个已被禁用的刚体通过关节和一个处于启用状态的刚体相连的话,在下一个仿真步开始时它就会被再次启用。被禁用的刚体不会花费CPU时间,因此如果想要加快仿真速度的话,可以在刚体空闲的时候禁用它们。而且,如果auto-disable处于启动状态的话,这个功能就可以被自动执行。
如果一个刚体的auto-disable标志已被打开,它会在发生下面的情况时自动关闭:
- 刚体在给定的仿真步之内一直处于空闲状态
- 刚体在给定的模拟时间之内一直处于空闲状态
void dBodyEnable (dBodyID);
void dBodyDisable (dBodyID);
上面的这两个函数用来手动地启用和禁用指定刚体。注意:如果一个被禁用的刚体通过关节和一个处于启用状态的刚体相连的话,在下一个仿真步开始的时候,它就会被再次启用。
int dBodyIsEnabled (dBodyID);
返回刚体的状态。如果处于启用状态,返回 1,否则返回 0
void dBodySetAutoDisableFlag (dBodyID, int do_auto_disable);
int dBodyGetAutoDisableFlag (dBodyID);
设置或获取刚体的auto-disable标志。如果do_auto_disable是一个非0值的话,刚体就会在空闲了足够长的时间之后自动关闭。
void dBodySetAutoDisableLinearThreshold (dBodyID, dReal linear_threshold);
dReal dBodyGetAutoDisableLinearThreshold (dBodyID);
设置或获取用于自动禁用刚体的线速度阈值。如果刚体的线速度低于这个阈值的话,它就会被认定为处于空闲状态。当然,如果将阈值设为dInfinity时,就不用再考虑它的线速度了。
void dBodySetAutoDisableAngularThreshold (dBodyId, dReal angular_threshold);
dReal dBodyGetAutoDisableAngularThreshold (dBodyID);
这两个函数和上面介绍的设置和获取线速度阈值的函数类似,用于设置或获取用于自用禁用刚体的角速度阈值。同样,如果刚体的角速度低于这个阈值的话,它就会被认定为处于空闲状态。要是将阈值设为dInfinity,也就不用再考虑它的角速度了。
void dBodySetAutoDisableSteps (dBodyID, int steps);
int dBodyGetAutoDisableSteps (dBodyID);
设置和获取用于自动禁用刚体的空闲步计数器的值,如果设为0的话,自动禁用函数就不会考虑仿真步了。
void dBodySetAutoDisableTime (dBodyID, dReal time);
dReal dBodyGetAutoDisableTime (dBodyID);
设置和获取用于自动禁用刚体的空闲时间计数器的值,如果设为0的话,自动禁用函数也就不用考虑刚体已经空闲的时间了。
void dBodySetAutoDisableAverageSampleCount (dBodyID, unsigned int average_samples_count);
int dBodyGetAutoDisableAverageSampleCount (dBodyID);
还未写入。。。
void dBodySetAutoDisableDefaults (dBodyID);
设置刚体的auto-disable参数,默认采用其所在的world参数。
void dBodySetMovedCallback (dBodyID, void (*callback) (dBodyID));
上面这个函数用来给指定刚体注册一个回调函数,只要该刚体一移动它就会被调用(如果刚体没有被禁用的话)。这个函数在将ODE和3D引擎整合起来时非常有用,因为只要任何一个ODE刚体以移动,与之关联的所有3D场景也都会变化。另外,回调函数必须得是符合void callback(dBodyID)的模式。
5.7 阻尼
在ODE中,阻尼主要服务于两个目的:降低仿真的不稳定性、允许刚体转为空闲状态(使自动禁止刚体成为可能)。刚体被创建时会采用它所处world的当前阻尼系数,如果将系数(scale)设为0的话阻尼就被禁用了。下面介绍它是怎样被实现的:
在每一个时间步长结束时,通过测量线速度和角速度跟相应阈值的大小关系,如果都大于其阈值的哈,就给它们乘以1-scale。所以如果scale取负值的话,实际上就会使速度增大,并且如果速度的值大到一定程度的话,就会使刚体在每个仿真步中处于振荡状态,进而使系统变得不稳定。
注意:刚体的速度只会在stepper函数将其移除之后发生衰减。只有在stepper(用于移动刚体)函数生成关节约束之后,才可以应用阻尼到刚体上,否则就可能会在约束之间引进新的误差。
另外需要注意的一点:只有在刚体移动的回调函数被调用之后,阻尼才会起到作用,因为在这一步仍然可能会用到刚体在仿真步中获得的精确速度。你甚至可以使用回调函数创建你自己定制的阻尼函数。
dReal dBodyGetLinearDamping (dBodyID);
dReal dBodyGetAngularDamping (dBodyID);
void dBodySetLinearDamping (dBodyID, dReal scale);
void dBodySetAngularDamping (dBodyID, dReal scale);
设置和获取刚体的阻尼系数。在给刚体设置阻尼系数之后,刚体就会忽略掉其所在world的阻尼系数知道的BodySetDampingDefaults()函数被调用。如果没有设置刚体的阻尼系数,获取函数就会返回其所在world的阻尼系数。
void dBodySetDamping (dBodyID, dReal linear_scale, dReal angular_scale);
这个函数可以用来一次性同时设置刚体的线阻尼系数和角阻尼系数。
dReal dBodyGetLinearDampingThreshold (dBodyID);
dReal dBodyGetAngularDampingThreshold (dBodyID);
void dBodySetLinearDampingThreshold (dBodyID, dReal threshold);
void dBodySetAngularDampingThreshold (dBodyID, dReal threshold);
设置/获取刚体的阻尼生效阈值,只有在线/角速度大于该阈值的时候才生效。
void dBodySetDampingDefaults (dBodyID);
重置阻尼设置到当前的world设置。
dReal dBodyGetMaxAngularSpeed (dBodyID);
void dBodySetMaxAngularSpeed (dBodyID, dReal max_speed);
你也可以限制角速度的最大值。跟阻尼函数不同的是,角速度是在刚体移动之前生效的。也就是说,这样做会在关节时间引进新的误差以使刚体旋转的过快(想想汽车的车轮吧),所以你或许会愿意给它们指定一个非常高的限制(默认采用dInfinity)。
5.8 其它一些刚体函数
void dBodySetData (dBodyID, void *data);
void *dBodyGetData (dBodyID);
用于设置和获取刚体的user-data指针。
void dBodySetFiniteRotationMode (dBodyID, int mode);
这个函数用来控制在每一个时间步长之内刚体的姿态更新方式。其中mode参数可以是:
- 0:采用“无限”的姿态更新方式。虽然利于计算,但可能会在刚体高速旋转时引起错误,特别是当这些刚体还联接着其它刚体。新创建的刚体默认都使用0。
- 1:一种“有限”的姿态更新方式。这种方式虽然较耗费计算机资源,但是对高速旋转的刚体来说就更为精确。要注意的是,尽管高速旋转的刚体会引起许多不用类型的错误,但是这种模式只会修正其中一个错误的源头。
int dBodyGetFiniteRotationMode (dBodyID);
返回一个指定刚体当前的有限旋转模式(0 or 1)。
void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z);
给指定刚体设置有限旋转轴,这个轴只有在刚体的有限旋转模式被设定之后才有意义。如果设为原点(0,0,0),刚体就会展现出完整的有限旋转;如果是一个非零值,刚体的旋转只会在轴线方向表现出部分有限旋转,同时在其正交方向表现出无限旋转。
这样做可以缓和一定程度上由刚体的高速自旋引起的误差来源。例如,对一个高速旋转的汽车车轮而言,你就可以通过调用这个函数,以车轮的铰接轴为参数来改善它的行为。
void dBodyGetFiniteRotationAxis(dBodyID, dVector3 result);
返回一个指定刚体当前的有限旋转轴。
int dBodyGetNumJoints (dBodyID);
返回联接到一个指定刚体上的关节数。
dJointID dBodyGetJoint (dBodyID, int index);
通过给定一个index参数来获取联接到刚体上的一个关节。有效的index取值范围为[0,n),其中n是dBodyGetNumJoints函数的一个返回值。
dWorldID dBOdyGetWorld (dBodyID);
获取联接到一个指定刚体的world。
void dBodySetGravityMode (dBodyID b, int mode);
int dBodyGetGravityMode (dBodyID b);
设置/获取刚体是否受其所在world重力影响的信息。如果mode为一个非零值,则表示受其影响;如果是0,则表示不受其影响。新创建的刚体默认会受到其所在环境的重力影响。
dGeomID dBodyGetFirstGeom (dBodyID);
dGeomiD dBodyGetNextGeom (dGeomID);
获取跟一个指定刚体相关的所有几何体的访问权。通过调用dBodyGetFirstGeom()来获取对第一个几何体的访问权,然后以前一个几何体为参数调用dBodyGetNextGeom()函数来获取对紧接着的下一个几何体的访问权。