ODE手册(6)关节类型和相关函数(上)
这一部分主要是介绍一些在ODE中,跟关节相关的函数
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 返回值是有效的吗?
关于反馈结构体中非常奇怪的、不一致的力和力矩的问题已经提交到邮件列表中(例如:July 2010, April 2009,February 2009,September 2008)。
显然只要你的系统带有或多说少的负载性,反馈系统就会产生不适当的值。约束求解只要能产生正确的结果也不会去在意力是否均匀分布了(就是说当它们施加到各自的刚体上时就算是正确的力了)。
6.3.2 API设计的一点注解
需要用户自己去申请这些反馈结构体看起来是很奇怪的,为什么不静态地为每一个关节存储这些数据呢?原因就是并不是说所有的用户都会去使用关节的反馈信息,即使会使用到也不是所有的关节都需要这些数据。去静态地存储这些数据就是白白地浪费内存,特别是这些结构体会在以后的过程中为了存储更多的额外数据而不断增长。
为什么ODE不去自己分配反馈结构体,而需要用户请求时才这样做呢?原因就是如果需要返回反馈信息的话,连接关节(在每一个时间步中都需要创建和销毁)就需要花费更多的时间用于分配内存。之所以让用户来分配就意味着可以提供更好的分配策略,例如给它们简单地分配一个固定的数组。
对这种API的替代方法就是使用一个关节力的回调函数,虽然可以解决的问题,但仍然存在一点问题。首先,回调函数会破坏掉ODE的APIs,并且有时会需要用户自己去检查一些不自然的弯曲以将数据保存在正确的地方。其次,这样会使得ODE在每一个仿真步中的改变显露出来(后果很严重),你将不得不需要一种方法去阻止它或者需要一定的调试去检查它(这会使问题更加复杂化)。