태그 보관물: ODE

Open Dynamic Engine

ode-latest-userguide

 

[개념 알기]
강체 (rigid body) Attribute

– 시간에 따라 변화하는 속성
1. 기준점의 위치벡터(x,y,z), 기준점은 강체의 질량 중심과 일치해야 한다.
2. 기준점의 선속도 벡터(vx, vy, vz)
3. 회전, 사원수(qw, qx, qy, qz) or 3×3 행열
4. 각속도 벡터 (wx, wy, wz), 시간에 따른 회전 변화

– 시간에 따라 변화하지 않는 속성
1. 질량 mass
2. 기준점에서 질랴의 중심의 위치
3. 관성 행열, 질량의 분산을 나타내는 3×3 행열

개념적으로 강체는 x,y,z 좌표틀을 포함하며, 좌표틀은 강체와 함께 움직인다.

– 강체의 그룹
강체는 관절에 의해 서로 연결되며, 연결된 그룹들을 island라고 하여 윌드 안에서 각각 따로 처리한다.
island 그룹내부의 강체들은 서로 연관을 가지게 되므로 하나가 활성화 되면 다름 Step 연산시
모든 강체들이 활성화 되게 된다. 모두 비활성화 되었을경우 연산이 Skip된다.

– 관절과 구속
관절은 Joint, Hinge등이 있이며, 두 강체 사이에 강제된 하나의 관계이다.
서로에 대해 특정한 위치나 회전을 가질수 있게 구속(Constraint) 한다.

– 구속 타입
— 구상 관절 (ball and socket joint)
하나의 강체 (공) 과 다른 하나의 강체 (소켓)이 같은 위치에서 서로 관계한다.
— 경첩 관절 (hing joint)
경첩의 두 부분이 같은 위치에 있도록 경첩축을 따라 정렬되도록 제한한다.
— 미닫이 관절 (slider joint)
피스톤과 소켓처럼 동일 선상에 오도록 구속한다.

– 관절 그룹
관절 그룹은 관절들을 하나의 그룹으로 보관하는 Container이다. 추가가 가능하며 더이상 필요없을시
빠르게 관절 그룹을 소멸한다.

– 관절 오류
관절의 구속 속성이 충족되지 않을경우 관절오류가 발생할수 있다.
!사용자가  강체의 위치/회전을 올바로 설정하지 않은 경우
!시뮬레이션 도중, 오류값이 점차 증가하여 원래 위치에서 강체가 떨어져 보이는 경우
이런 관절 오류를 줄이는 메카니즘이 ERP이다 (error reduction parameter)

– ERP : error reduction parameter)
ERP 시뮬레이션의 다음 스텝중에 수정할 관절 오류의 비율로서 [0.0~1.0] 사이값을 같는다.
0.0은 어떤 조정도 되지 않는 상태이며, 1은 다양한 내부값을 회손시킬수 있으므로 (0.1~0.8)사이값으로 설정하는것 이 좋다.

– CFM : constraint force mixing
대부분의 구속은 본질적으로 hard하다. 변경되지 않는 값이다. 그러나, 종종 부드러운 구속들이 있으며
이런 부드러운 재질을 시뮬레이션 하기 위해 두 강체가 서로 충돌할경우 어느정도 자연스러운 관통을 하용할수 있다.
이때 하용되는 것이 CFM (구속 힘 혼합)이다.

모든 관절에 대한 구속 방정식은

 

J * v = c

이다
J – 관절이 시스템으로 부터 제거한 모든 자유도에 대한 1개의 행을 가진 야코비언(Jacobian) 행열
v – 강체의 속도 벡터
c – 결과 벡터
다음 Step상태에서 관절 구속을 유지하기 위해 강체에 적용되는 힘은   force =  J-transpose * lambda 이며,
ODE에서는

J * v = c + CFM * lambda

식을 사용한다.
CFM은 구속의 결과로 발생하는 구속력을 혼합하며, 대각행열이다. 0이 아닌 양의 CFM값은 본래의 구속 방정식을
CFM과 구속에 필요한 복원력(restoring force) lambda의 곱에 비례하는 양 만큼 변경된다.

(JM-inverse J-transpose + CFM/h) lambda = c/h

CFM은 단순히 본래 시스템 행렬의 대각행열을 추가한다. CFM값은 특이성을 없애는 추가이점을 가지고 있어 인수분해의
정확성도 개선된다.

– ERF와 CFM의 사용법
ERF와 CFM은 관절들에 독립적으로 설정된다. 관절의 푹신함(스펀지)과 탄성력(용수철)을 조절하기 위해
접촉 관절이나, 관절 한계들에 다양하게 설정이 가능하다.

CFM : 0 – 딱딱한 구속
+(양수) – 부드러운 밀어붙이기 구속, 증가할수록 부드러움이 증가한다.  CFM * 구속 복원력 = 변환,
-(음수) – 불안정한 결과를 보인다.

용수철상수 (spring constant) kp 와 감쇠상수(damping constant) kd에 대응하는 ODE상수 식은 :

ERP = h * kp / ( h * kp + kd )
CFM = 1 / ( h * kp + kd )

여기서 h는 stepsize이다. 이 값은 부정확한 1차 적분으로 시뮬레이션 되는 용수철-완충 시스템과 같은 효과를 보여준다.
CFM을 증가시키면 시뮬레이션의 수치적 오류를 줄여줄수 있다. 시스템 오동작시 CFM을 증가시키는것이 선행되어야 한다.

[충돌처리]
충돌처리는 다음과 같이 처리된다.
1. 각 시뮬레이션 스템전, 충돌중인 물체를 알아내기 위해 충돌 검사 함수를 호출한다.
이 함수는 접촉점 리스트를 반환하며, 각 접촉점은 공간상의 (위치, 표면 법선 벡터, 관통 깊이)등을 명시한다.
2. 특수한 접촉 관절이 각 접촉점에 생성된다. 접촉 관절은 접촉에 관련한 추가 정보가 생성된다.
접촉면이 얼마나 탄력적이나, 얼마나 부드럽냐 같은 다양한 정보 속성들이 제공된다.
3. 접촉 관절들이 그룹으로 묶인다. 그룹은 시스템에 관절을 추가/삭제를 용이하게 한다.
접촉이 늘어날수록 시스템 과부하가 발생하므로, 접촉면의 수를 제한하는 것이 바람식하다.
4. 시뮬레이션을 한다 (timeStep)
5. 접촉관절 그룹을 시스템에서 제거한다.

[전형적 시뮬레이션 처리]
1. 동역학 월드를 구성한다.
2. 동역학 월드 내의 강체들을 생성한다.
3. 강체들의 위치,상태등을 설정한다.
4. 동역학 월드 안에 관절들을 생성한다.
5. 강체에 관절을 부착한다.
6. 모든 관절들의 속성을 설정한다.
7. 필요시, 충돌월드와 충돌 기하 구조 오브젝트를 생성한다.
8. 접촉 관절들을 보관할 한개의 그룹을 생성한다.
9. loop
9-1. 필요에 따라 강체들에 힘을 적용한다.
9-2. 필요에 따라 관절 매개 변수들을 조정한다.
9-3. 충돌 검사를 호출한다.
9-4. 모든 충돌점에 대해 접촉 관절을 생성하고, 접촉 관절 그룹에 추가한다.
9-5. 시뮬레이션 Step을 진행한다.
9-6. 접촉 관절 그룹 안에 있는 모든 관절들을 제거한다.
10. 동역학 월드와 충돌 월드를 소멸한다.

[ODE에서 사용되는 방법및 근사들에 대한 논의]
– 마찰 근사
쿨롱 마찰 모델은 단순하며, 접촉점에서의 마찰을 모델링 하기에 효과적이다.
이것은 접촉점에 나타나는 수직력과 접선력 사이의 단순환 관계 법칙이다.

|fT| = mu * |fN|

fN : 수직력 벡터, fT : 법선력 벡터, mu : 마찰 계수
이 방적식은 “마찰 원뿔”을 정의한다. fN을 축으로 접촉점을 정점으로 하는 원뿔에서 전체 마찰력 벡터가 원뿔내에 있으면
마찰력이 접촉면들이 움직이는 것을 충분히 방지한다. 힘 벡터가 원뿔의 표면 상에 있으면, 마찰력은 접촉면들이 미끄러지는 것을
막지 못하면, 움직이게 된다. 따라서 매개변수 mu 는 수직력에 대한 접선력의 최대 비율을 나타낸다.

1. ODE에서 mu는 접촉시 접선의 마찰 방향들 중 한쪽에서 나타낼수 있는 최대한의 마찰력을 표현한 것으로, 수직력과는 관계가 없어
비현실적이나, 유용하게 또한 가장 싸게 시뮬레이션 할수 있는 힘한계값으로 적절한 값이 선택되어야 한다.
2. 마찰 원뿔은 방향에 정렬된 마찰 피라미드에 의해 근사된다. ODE는 모든 접촉이 마찰이 없는 것처럼 가정하고 수직력을 계산한후,
마찰력에 대해 최대 한계 fM을 계산한다.
|fM| = mu * |fN|
이런 고정된 한계들을 가진 전체 시스템에 대한 해를 구한다. 이것은 mu가 고정되지 않은 진짜 마찰 피라미드와는 다르다.
이런 근사법은 mu를 일반적인 쿨롱 마찰 계수와 같은 비율로 사용하며, 1.0의 일정한 값으로 설정될수 있다.

[자료형과 규칙]
ODE 라이브러리는 단정도/배정도 실수를 사용할 수 있으며, 단정도는 빠르나 수치 오류가 크고, 배정도는 그 반대이다.

– 오브젝트와 ID
1. dWorld : 동역학 좌표계
2. dSpace : 충돌 공간
3. dBody : 강체
4. dGeom : 충돌 기하 구조
5 dJoint : 관절
6 dJointGroup : 관절 그룹
– 오브젝트들은 오브젝트 ID를 반환하며, dWorldID, dBodyID등.. 이 있다.

[ODE 사용 : World]
– 월드
윌드 오브젝트는 강체와 관절들의 컨테이너이다.
다른 월드에 있는 오브젝트들과는 상호작용을 할수 없다.
월드 안의 모든 오브젝트들은 같은 시간 지점에 존재하기에, 별개의 월드를 사용하는 방법은 시스템들을 다른 비율 시간으로
시뮬레이션 하는 것이다.

대 부분은 하나의 월드가 필요할 뿐이다.
dWorldID dWorldCreate();
: 새로운 비어있는 월드를 하나 생성하고 ID를 리턴한다.
void dWorldDestroy (dWorldID);
: 월드를 소멸시키고 그 안의 모든 것을 소멸시킨다.
모든 강체와 관절 그룹의 일부분이 아닌 모든 관절들을 포함한다.
관절 그룹에 속한 관절들은 비활성화될 것이다. 그리고 [[#dJointGroupEmpty|dJointGroupEmpty]]를 호출하여 소멸될 수 있다.

void dWorldSetGravity (dWorldID, dReal x, dReal y, dReal z);
void dWorldGetGravity (dWorldID, dVector3 gravity);
: 월드의 전역 중력 벡터를 설정/리턴한다.
단위는 m/s/s, 따라서 +z를 위로 가정했을 때 지구의 중력 벡터는 (0,0,-9.81)이다.
기본값은 중력이 없는 상태, 즉 (0,0,0)이다.

void dWorldSetERP (dWorldID, dReal erp);
dReal dWorldGetERP (dWorldID);
: 전역 오류 감쇠 매개 변수(ERP; error reduction parameter) 값을 설정/리턴한다.
ERP는 각 시간 스텝에서 오류를 얼마나 많이 정정할 수 있는 지를 제어한다.
값의 범위는 일반적으로 0.1에서 0.8이다. 기본값은 0.2이다.

void dWorldSetCFM (dWorldID, dReal cfm);
dReal dWorldGetCFM (dWorldID);
: 전역 구속 힘 혼합(CFM; constraint force mixing) 값을 설정/리턴한다.
일반적인 값은 10-9에서1 사이에 있다. 단정도를 사용하면 기본값이 10-5이고,
배정도를 사용하면 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);
: 새로 생성되는 강체에 대한 기본 자동-비활성화 매개 변수들을 설정/리턴한다.
자동-비활성화 특성에 대해 [[#자동 활성화와 비활성화|6.5절]]을 보라.  기본 매개 변수들은 다음과 같다:
*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);
: 강체에 선 또는 각 충격량을 적용하고 싶을 때, 힘이나 돌림힘 대신에 원하는 충격량을 힘/돌림힘 벡터로 변환하기 위해 이 함수를 사용할 수 있다.
: 이 함수는 원하는 충격량을 (ix,iy,iz)로 설정하고, 변환한 힘을 force 벡터에 넣는다.
현재의 알고리즘은 단순히 충격량을 stepsize로 나누기만 한다. 여기서 stepsize는 다음 스텝의 크기이다.
: 이 함수는 dWorldID를 받는다. 향후에 힘 계산이 월드의 속성으로 설정된 적분기 매개 변수들에 좌우될지도 모르기 때문이다.

void dCloseODE();
: ODE에 의해 사용된 메모리를 해제한다. 프로그램 종료 전에 사용하면 된다.

[ODE 사용 : Step]
void dWorldStep (dWorldID, dReal stepsize);
: 월드를 한 스텝 진행한다. 이것은 m3 차수의 시간을 소모하고 m2 차수의 메모리를 사용하는 “큰 행렬(big matrix)” 법을 사용한다.
여기서 m는 전체 구속 행 수이다.
: 커다란 시스템에 대해서 이것은 아주 많은 메모리를 사용하고 매우 느릴 것이다. 하지만 현재 가장 정확한 방법이다.

void dWorldQuickStep (dWorldID, dReal stepsize);
: 월드를 한 스텝 진행한다. m*N 차수의 시간과 m 차수의 메모리를 소모하는 반복적인 방법을 사용한다.
여기서 m는 전체 구속 행 수이고 N는 반복횟수이다.
: 커다란 시스템에 대해서 이 함수가 [[#dWorldStep|dWorldStep]]보다 훨씬 더 빠르다. 하지만 정확성은 더 떨어진다.
: QuickStep는 오브젝트 더미들에 좋은 성능을 발휘한다. 특히 자동-비활성화 특성이 함께 사용되면 더 좋다.
그러나 근-특이 시스템(near-singular systems)들에 대해서는 취약한 정확도를 가진다.
고-마찰 접촉, 모터, 특정 관절구조를 사용할 때 근-특이 시스템이 나타날 수 있다.
예를 들면, 다수의 다리를 가진 로봇이 땅위에 서있을 때 근-특이성을 가진다.
: QuickStep의 부정확성 문제를 극복하기 위한 방법들이 있다 –
*CFM을 늘린다.
*시스템에서 접촉 수를 줄인다(즉, 로봇이나 생물의 발에 대한 최소한의 접촉 수를 사용한다).
*접촉시 과도한 마찰을 사용하지 않는다.
*적절한 접촉 미끄러짐을 사용한다.
*운동학 루프를 피한다(그러나, 운동학 루프는 다리가 달린 생물체에서는 피할 수가 없다).
*과도한 모터 힘을 사용하지 않는다.
*속도 기반 모터 대신 힘 기반 모터를 사용한다.
: QuickStep의 반복 횟수를 증가하는 것이 조금 도움이 될 것이다. 하지만 시스템이 특이점에 가깝다면 별 도움이 되지 않을 것이다.

void dWorldSetQuickStepNumIterations (dWorldID, int num);
int dWorldGetQuickStepNumIterations (dWorldID);
: QuickStep 함수가 스텝 당 실행하는 횟수를 설정/리턴한다.
더 많은 반복은 더 정확한 해를 줄 것이다. 하지만 계산 시간은 더 오래 걸릴 것이다.
기본값은 20이다.

[ODE 사용 : 접속 매개 변수]
void dWorldSetContactMaxCorrectingVel (dWorldID, dReal vel);
dReal dWorldGetContactMaxCorrectingVel (dWorldID);
: 접촉이 생성하는 허락된 최대 적정 속도를 설정/리턴한다. 기본값은 무한대이다.
이 값을 줄이는 것은 깊게 묻힌 오브젝트들이 “튀는(popping)” 것을 방지하는 데 도움을 줄 수 있다.

void dWorldSetContactSurfaceLayer (dWorldID, dReal depth);
dReal dWorldGetContactSurfaceLayer (dWorldID);
: 모든 기하 구조 오브젝트의 표면 층의 깊이를 설정/리턴한다.
접촉시 정지할 때까지 표면을 주어진 깊이까지 파고 드는 것을 허락한다.
이것을 증가하는 것은 반복적인 접촉에 대해 떨림 문제(jittering problems)를 방지하는 데 도움을 줄 수 있다.

[ODE 사용 : 강체 함수]

[ODE 사용 : 강체 생성과 소멸]
dBodyID dBodyCreate (dWorldID);
: 주어진 월드의 (0,0,0) 위치에 기본 질량을 가진 강체를 생성하고 ID를 리턴한다.

void dBodyDestroy (dBodyID);
: 강체를 소멸시킨다. 강체에 부착된 모든 관절들을 림보(limbo)에 넣는다.
(즉, 시뮬레이션에 영향을 주지는 않으나 제거또한 되지 않는다.)

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);
: 강체의 위치, 회전, 선속도와 각속도를 설정하거나 얻는다.
여러 강체들을 설정한 후, 새로운 구성이 현재의 관절/구속과 어울리지 않을 경우 시뮬레이션의 결과는 알 수 없다.
dBodyGet-함수를 사용할 때,   리턴 값은 내부 자료 구조에 대한 포인터이다.
그러므로 변경된 값들이 모두 강체 시뮬레이션 구조체에 반영되기까지는 유효하다.
: dBodyGetRotation는 4×3 회전 행렬을 리턴한다.

[ODE 사용 : 질량과 힘]
void dBodySetMass (dBodyID, const dMass *mass);
void dBodyGetMass (dBodyID, dMass *mass);
: 강체의 질량을 설정/리턴한다(질량 함수들을 보라).

void dBodyAddForce            (dBodyID, dReal fx, dReal fy, dReal fz);
void dBodyAddRelForce         (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 dBodyAddTorque           (dBodyID, dReal fx, dReal fy, dReal fz);
void dBodyAddRelTorque        (dBodyID, dReal fx, dReal fy, dReal fz);
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 와 …RelTorque 함수들은 강체의 기준틀에 관련된 힘 벡터들을 받아들인다.
: …ForceAtPos 와 …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으로 만들 때 주로 유용하다.

[ODE 사용 : 지원 함수]
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);
: 강체의 한 점(px,py,pz)를 받아서 그 점의 위치와 속도를 전역 좌표로 result에 리턴한다.
dBodyGetRelPointXXX 함수들은 강체 좌표계의 점을 인자로 받는다.
dBodyGetPointVel 함수들은 전역 좌표계에서 점을 인자로 받는다.

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);
: 강체(월드) 좌표계에서 표현된 벡터 (x,y,z)를 받아서, 월드(강체) 좌표로 변환하여
그 결과를 result에 리턴한다.

[ODE 사용 : 활성/비활성 함수]
모든 강체는 활성화되거나 비활성화될 수 있다.
활성화된 강체들은 시뮬레이션에 참가하고, 반면 비활성화된 강체들은 꺼진 상태라서 시뮬레이션 스텝 동안 갱신되지 않는다.
새로운 강체들은 항상 활성화된 상태로 생성된다.
관절을 통해 활성화된 강체에 연결된 비활성화된 강체는 다음 시뮬레이션 스텝에서 자동적으로 재활성화된다.
비활성화된 강체는 CPU 시간을 소모하지 않는다. 따라서 시뮬레이션 속도를 높이기 위해 강체가 정지된 상태에
이르면 비활성화돼야 한다. 이것이 자동-비활성화 특성으로 자동으로 행해진다.

어떤 강체가 자동-비활성화 플랙을 켜놓았다면, 다음 경우에 자동으로 비활성화될 것이다.

-주어진 시뮬레이션 스텝 수 동안 유휴상태인 경우.
-주어진 시뮬레이션 시간 동안 유휴상태인 경우.

강체의 선속도와 각속도의 양이 모두 주어진 경계 이하에 있을 때 유휴 상태에 있다고 생각할 수 있다.
따라서, 모든 강체는 5개의 자동-비활성화 매개 변수들을 가진다

: 활성화된 Flag, 유휴 스텝 카운트, 유휴 시간, 그리고 선/각 속도 경계.

새로 생성된 강체들은 월드로부터 이러한 매개 변수들을 가진다.
다음 함수들은 강체의 활성화/비활성화 매개 변수를 설정/리턴한다.

void dBodyEnable (dBodyID);
void dBodyDisable (dBodyID);
: 강체를 수동으로 활성화/비활성화 시킨다.
관절을 통해 활성화된 강체에 연결된 비활성화된 강체는 다음 시뮬레이션 스텝에서 자동으로
재활성화될 것이다.

int dBodyIsEnabled (dBodyID);
: 강체가 현재 활성화돼 있으면 1를 리턴하고, 그렇지 않으면 0을 리턴한다.

void  dBodySetAutoDisableFlag (dBodyID, int do_auto_disable);
int   dBodyGetAutoDisableFlag (dBodyID);
: 강체의 자동-비활성화 플랙을 설정/리턴한다. 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  dBodySetAutoDisableDefaults (dBodyID);
: 강체의 자동-비활성화 매개 변수들을 기본값으로 설정한다.

[ODE 사용 : 강체 도움 함수]
void  dBodySetData (dBodyID, void *data);
void *dBodyGetData (dBodyID);
: 강체의 사용자 데이타 포인터를 설정/리턴한다.

void dBodySetFiniteRotationMode (dBodyID, int mode);
:이 함수는 강체의 회전이 매 시간 스텝마다 갱신되는 방식을 제어한다.
mode 인자는 다음과 같다:
-0: “무한소(無限小; infinitesimal)”의 회전 갱신을 사용한다.
계산이 빠르지만, 빠른 속도로 회전하는 강체에 대해 가끔 부정확한 결과를 유발할 수 있다.
특히 다른 강체에 연결된 강체의 경우 두드러진다. 새로 생성된 강체에 대해 기본값으로 사용된다.
-1: “유한(有限; finite)”한 회전 갱신을 사용한다.
계산하는 데 더 많은 비용이 들지만, 빠른 속도의 회전에 대해 더 정확하다.
그렇지만 빠른 속도의 회전은 시뮬레이션에서 많은 오류를 야기할 수 있다.
그리고 이 모드는 여러 오류들 중 한가지만 고칠 수 있을 것이다.

int dBodyGetFiniteRotationMode (dBodyID);
: 강체의 현재 유한 회전 모드를 리턴한다. (0 또는 1).

void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z);
: 강체의 유한 회전 축을 설정한다. 유한 회전 모드가 설정되었을 때만 의미가 있다
([[#dBodySetFiniteRotationMode |dBodySetFiniteRotationMode]] 참고).
: 축이 (0,0,0)이면, 강체에 완전한 유한 회전을 수행한다.
: 축이 영이 아니면, 강체는 축방향을 따라 부분적인 유한 회전을 하고나서,
직각 방향을 따라 무한소의 회전을 한다.
: 이는 빠르게 회전하는 강체에 발생하는 오류들을 완화시키는 데 유용하다.
예를 들어, 빠른 속도로 회전하는 차바퀴가 있다면, 바퀴의 움직임을 개선하기위한
인자로서 바퀴의 경첩 관절축에 이 함수를 호출할 수 있다.

void dBodyGetFiniteRotationAxis (dBodyID, dVector3 result);
: 강체의 현재 유한 회전축을 리턴한다.

int dBodyGetNumJoints (dBodyID b);
: 강체에 부착된 관절의 수를 리턴한다.

dJointID dBodyGetJoint (dBodyID, int index);
: 강체에 부착된 index번째 관절을 리턴한다. 유효한 인덱스는 0에서 n-1사이이다.
여기서 n은 [[#dBodyGetNumJoints|dBodyGetNumJoints]]에 의해 리턴된 값이다.

void dBodySetGravityMode (dBodyID b, int mode);
int dBodyGetGravityMode (dBodyID b);
: 강체가 월드의 중력에 의해 영향을 받는지 여부를 설정/리턴한다.
mode가 0이 아니면 중력의 영향을 받고, 0이면 영향을 받지 않는다.
새로 생성된 강체는 항상 중력의 영향을 받는다.

[ODE 사용 : 관절과 함수]

[ODE 사용 : 관절의 생성과 소멸]
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 dJointCreateFixed (dWorldID, dJointGroupID);
dJointID dJointCreateAMotor (dWorldID, dJointGroupID);
: 주어진 타입의 새로운 관절을 생성한다.
이 관절은 처음에 강체에 연결된 상태가 아니기 때문에 “림보(limbo)”(시뮬레이션에 아무런 영향을 주지 않는)에 존재한다. 일반적으로 관절 그룹 ID는 0이다. 0이 아니면 특정 관절 그룹에 할당된다.
접촉 관절은 주어진 dContact 구조체를 가지고 초기화된다.

void dJointDestroy (dJointID);
: 관절을 소멸시키고 강체와 연결을 끊고 월드로부터 제거한다. 그러나, 관절이 그룹의 일원이라면,
이 함수는 아무 효력이 없다. – 그런 관절을 소멸시키려면 그룹을 비우거나 소멸시켜야 한다.

dJointGroupID dJointGroupCreate (int max_size);
: 관절 그룹을 생성한다. max_size 인자는 현재 사용되지 않으며 0으로 설정해야 한다.
이는 이전 버전과의 호환성을 위해 남겨진 것이다.

void dJointGroupDestroy (dJointGroupID);
: 관절 그룹을 소멸시킨다. 그룹 안의 모든 관절들이 소멸할 것이다.

void dJointGroupEmpty (dJointGroupID);
: 관절 그룹을 비운다. 그룹 안의 모든 관절들이 소멸하지만, 그룹 자체는 소멸하지 않을 것이다.
[ODE 사용 : 관절 기타 함수]
void dJointAttach (dJointID, dBodyID body1, dBodyID body2);
: 새 강체에 관절을 부착한다. 관절이 이미 부착되어있으면, 예전의 강체로부터 떼어낸다.
관절을 하나의 강체에만 부착하려면, body1 또는 body2를 0으로 설정하면된다.
– 0인 강체는 정적 환경을 말한다. 두 강체를 모두 0으로 설정하면 관절이 “림보”에 들어간다.
즉, 시뮬레이션에 영향을 주지 않는다.
: 경첩 관절-2 같은 몇몇 관절들은 제대로 작동하려면 두 개의 강체에 부착되어야 한다.

void dJointSetData (dJointID, void *data);
void *dJointGetData (dJointID);
: 관절의 사용자 데이타 포인터를 설정/리턴한다.

int dJointGetType (dJointID);
: 관절의 타입을 얻는다. 다음 상수들 중 하나가 리턴된다:

dJointTypeBall : 구상 관절(ball-and-socket joint)
dJointTypeHinge : 경첩 관절(hinge joint)
dJointTypeSlider : 미닫이 관절(slider joint)
dJointTypeContact : 접촉 관절(contact joint)
dJointTypeUniversal : 십자축 관절(universal joint)
dJointTypeHinge2 : 경첩-2 관절(hinge-2 joint)
dJointTypeFixed : 고정 관절(fixed joint)
dJointTypeAMotor : 각모터 관절(angular motor joint)

dBodyID dJointGetBody (dJointID, int index);
: 관절이 연결한 강체들을 리턴한다. index가 0이면 “첫번째” 강체를 리턴한다.
이는 [[#dJointAttach|dJointAttach]]의 body1 인자와 일치한다.
index가 1이면 “두번째” 강체를 리턴한다. 이는 [[#dJointAttach|dJointAttach]]의 body2 인자와 일치한다.
: 리턴된 강체 ID 중 하나가 0이면, 관절이 강체를 정적 환경과 연결한 것이다.
두 강체 ID 모두 0이면, 관절은 “림보”에 있는 것이고, 시뮬레이션에 영향을 주지 않는다.

void dJointSetFeedback (dJointID, dJointFeedback *);
dJointFeedback *dJointGetFeedback (dJointID);
: 월드의 시간 스텝 동안, 각각의 관절에 의해 적용되는 힘들이 계산된다.
이 힘들은 연결된 강체에 직접 더해지고, 사용자는 보통 관절이 얼마나 많은 힘을 부여하는 지 알 길이 없다.
: 이러한 정보가 필요하다면, 사용자는 dJointFeedback 구조체를 할당해서 그 포인터를
dJointSetFeedback() 함수에 넘길 수 있다.

피드백 정보 구조체는 다음과 같이 정의된다:
typedef struct dJointFeedback
{
dVector3 f1;       // force that joint applies to body 1
dVector3 t1;       // torque that joint applies to body 1
dVector3 f2;       // force that joint applies to body 2
dVector3 t2;       // torque that joint applies to body 2
} dJointFeedback;

: 시간 스텝 중에 관절에 부착된 피드백 구조체는 관절의 힘과 돌림힘 정보로 채워질 것이다.
dJointGetFeedback() 함수는 현재 피드백 구조체 포인터를 리턴하거나 아무것도 사용되지 않으면(이게 기본값), 0을 리턴한다.
관절에 대한 피드백을 비활성화시키기위해 dJointSetFeedback()에 0을 인자로 넘길 수 있다.

: API 디자인에 잠깐 주목해보자. 사용자가 이러한 구조체들에 할당을 하도록 하는 것이 이상해 보일 수도 있다.
그냥 각 관절에 정적으로 데이타를 저장하면 안되나? 그렇게 하지 않는 이유는 모든 사용자가 피드백 정보를 사용하지 않을 것이고,
심지어 모든 사용자가 사용하더라도 모든 관절이 필요로 하지는 않을 것이기 때문이다.
정적으로 정보를 저장하는 것은 메모리를 낭비하게된다. 특히 앞으로 이 구조체가 추가 정보를 저장하기 위해 크기가 커질 수도 있다.

: 그럼 사용자가 요청할 때 ODE가 직접 구조체를 할당하면 되지 않나?
그렇게 하지 않는 이유는 (시뮬레이션 단계마다 생성되고 소멸하는) 접촉 관절은 피드백을 요청할 경우 메모리
할당에 많은 시간이 걸릴 것이기 때문이다. 사용자로 하여금 할당하도록 하는 것이 더 나은 정책이다.
즉, 사용자는 단순히 고정 배열로부터 구조체를 할당하면 된다.

: 이러한 API에 대한 대안은 관절-힘 콜백 함수를 갖는 것이다. 물론 잘 동작하지만 약간의 문제를 가지고 있다.
첫째, 콜백 함수는 API를 더럽히고 때때로 사용자가 데이타를 얻기위해 부자연스러운 왜곡을 거쳐야한다.
둘째, ODE가 시뮬레이션 과정 중에 변경되면 안좋은 결과를 갖게 될 수 있다.
이러한 상황에 대비책을 마련하거나 디버깅 검사가 필요하다 – 이것은 복잡한 문제다.

int dAreConnected (dBodyID, dBodyID);
: 유용한 함수 – 두 강체가 관절에 의해 서로 연결되어 있다면 1을 리턴하고, 그렇지 않으면 0을 리턴한다.

int dAreConnectedExcluding (dBodyID, dBodyID, int joint_type);
: 유용한 함수 – 두 강체가 joint_type이 아닌 관절에 의해 연결되어 있다면 1을 리턴하고,
그렇지 않으면 0을 리턴한다. joint_type은 dJointTypeXXX 상수이다.
이 함수는 두 강체 사이에 접촉 관절을 추가할 지를 결정할 때 유용하다 – 만약 두 강체가 이미 접촉 관절이
아닌 다른 관절로 연결되어있다면, 접촉 관절을 추가하는 것은 적절하지 않을 것이다.
그러나 접촉 관절을 가지고 있는 강체 사이에 접촉 관절을 더 추가하는 것은 괜찮다.
[ODE 사용 : 관절 매개 변수 설정 함수]

[ODE 사용 : 구상 관절]
구상 관절(ball and socket joint)

void dJointSetBallAnchor (dJointID, dReal x, dReal y, dReal z);
: 관절 고정점을 설정한다. 관절은 두 강체를 연결하기 위해 고정점을 유지한다. 입력값은 월드 좌표계의 좌표이다.

 

void dJointGetBallAnchor (dJointID, dVector3 result);
: 월드 좌표계에서 body1의 관절 고정점을 얻는다. 관절이 완벽하게 만족되면, body2의 점과 똑같을 것이다.

void dJointGetBallAnchor2 (dJointID, dVector3 result);
: 월드 좌표계에서 body2의 관절 고정점을 얻는다. 관절이 완벽하게 만족되면 [[#dJointGetBallAnchor|dJointGetBallAnchor]]와 [[#dJointGetBallAnchor2 | dJointGetBallAnchor2]]의 결과값이 반올림 오차내에서 같다고 볼 수 있다.
[ODE 사용 : 경첩 관절]
경첩 관절(hinge joint)

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 | dJointGetHingeAnchor]]의 리턴값과 똑같을 것이다. 그렇지 않으면, 리턴값이 약간 다를 것이다. 이것은 관절이 얼마나 떨어져있는지를 알아내는 데 사용할 수 있다.

void dJointGetHingeAxis (dJointID, dVector3 result);
: 경첩 관절축 매개 변수를 얻는다.

dReal dJointGetHingeAngle (dJointID);
dReal dJointGetHingeAngleRate (dJointID);
: 경첩 관절의 각과 시간 변화율을 얻는다. 각도는 두 강체 또는 강체와 정적 환경의 사잇각으로 -pi와 pi 사이의 값이다.
: 경첩 관절 고정점 또는 축이 설정되었을 때, 부착된 강체의 현재 위치가 검사되고 그 위치는 0도가 될 것이다.
[ODE 사용 : 미닫이 관절]
미닫이 관절

void dJointSetSliderAxis (dJointID, dReal x, dReal y, dReal z);
: 미닫이 관절 축 매개 변수를 설정한다.

 

void dJointGetSliderAxis (dJointID, dVector3 result);
: 미닫이 관절 축 매개 변수를 얻는다.

dReal dJointGetSliderPosition (dJointID);
dReal dJointGetSliderPositionRate (dJointID);
: 미닫이 관절의 선형 위치와 시간 변화율을 얻는다.
: 축이 설정되면, 부착된 강체의 현재 위치가 검사되고 그 위치는 0이 될것이다.
[ODE 사용 : 십자축 관절]
십자축 관절

십자축 관절은 추가적인 회전 자유도를 억압한 볼-소켓 관절과 비슷하다.
body1에 axis1을 설정하고, body2에 axis1에 수직인 axis2를 설정하고 그 상태를 유지한다.
바꿔 말하면, 두 축에 수직인 방향에 대한 두 강체의 회전이 같다.
그림에서, 두 강체는 십자가로 연결되어있다. axis1은 body1에 부착되고, axis2는 body2에 부착된다.
십자가는 두 축을 직각으로 유지한다. 그러므로 body1을 잡고 비틀면, body2 또한 비틀어진다.
십자축 관절 축이 서로 수직이고 완충 지점에서 견고하게 연결된 경첩-2 관절과 동일하다.
십자축 관절은 자동차에서 볼 수 있다. 엔진은 자신의 축을 따라 회전하는 굴대와 손잡이에 영향을 준다.
굴대의 방향을 바꾸고 싶을 때가 있을 것이다. 그런데 문제는 굴대를 구부리면, 굽은 곳 이후의 부분은 자신의 축을 따라 회전하지 않을 것이다.
그래서 구부러진 위치를 잘라버리고 그 부분에 십자축 관절을 넣으면, 첫번째 굴대의 각도만큼 두번째 굴대를 회전시키는 구속력을 사용할 수 있게 된다.
팔을 쫙 뻗고 있는 사람을 상상해보라. 팔을 위 아래와 앞 뒤로 움직일 수 있지만 팔의 축에 대해 회전할 수는 없다.
다음은 십자축 관절 함수들이다:

 

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의 관절 고정점을 얻는다. 관절이 완벽하게 만족된다면,
[[#dJointGetUniversalAnchor | dJointGetUniversalAnchor]]의 리턴값이 반올림 오차범위내에서 같다고 볼 수 있다.
[[#dJointGetUniversalAnchor2 | dJointGetUniversalAnchor2]]는 관절이 얼마나 떨어져 있는지를 알아보는 데 사용할 수 있다.

void dJointGetUniversalAxis1 (dJointID, dVector3 result);
void dJointGetUniversalAxis2 (dJointID, dVector3 result);
: 십자축 관절 축 매개 변수를 얻는다.
[ODE 사용 : 경첩-2 관절]

경첩-2 관절은 각자 다른 축을 가진 두개의 경첩 관절이 연속으로 연결되어있는 것과 같다.
예를 들면, 위 그림을 자동차 핸들이라고 보면, 하나의 축이 바퀴의 방향을 틀고,
다른 축은 바퀴가 회전하도록 한다.
경첩-2 관절은 한개의 고정점과 두개의 경첩 관절 축을 가진다.
axis1은 body1에 상대적으로 특화된 것이고(body1이 차라면 axis1은 핸들이다),
axis2는 body2에 상대적으로 특화된 것이다(body2가 바퀴라면 axis2는 바퀴 축이다).

 

axis1은 관절 한계들과 한 개의 모터를 가질 수 있고, axis2는 한 개의 모터만을 가질 수 있다.
axis1은 완충 장치 축 기능을 할 수 있다. 즉, 구속이 그 축을 따라 압력을 가할 수 있다.
axis1이 axis2에 수직인 경첩-2 관절은 완충 장치가 추가된 십자축 관절과 동일하다.

void dJointSetHinge2Anchor (dJointID, dReal x, dReal y, dReal z);
void dJointSetHinge2Axis1 (dJointID, dReal x, dReal y, dReal z);
void dJointSetHinge2Axis2 (dJointID, dReal x, dReal y, dReal z);
: 경첩-2 고정점과 고정축 매개 변수들을 설정한다. axis1과 axis2가 절대로 같은 선상에 있어선 안된다.

void dJointGetHinge2Anchor (dJointID, dVector3 result);
: 월드 좌표계에서 body1의 관절 고정점을 얻는다. 관절이 완벽하게 만족되면, body2의 고정점과 똑같을 것이다.

void dJointGetHinge2Anchor2 (dJointID, dVector3 result);
: 월드 좌표계에서 body2의 관절 고정점을 얻는다. 관절이 완벽하게 만족되면,
[[#dJointGetHinge2Anchor | dJointGetHinge2Anchor]]의 리턴값과 똑같을 것이다.
조건을 완벽하게 만족하지 않으면, 값이 약간 다를 것이다. 이것은 관절이 얼마나 떨어져 있는지를 알아내는 데 사용될 수 있다.

void dJointGetHinge2Axis1 (dJointID, dVector3 result);
void dJointGetHinge2Axis2 (dJointID, dVector3 result);
: 경첩-2 축 매개 변수를 얻는다.

dReal dJointGetHinge2Angle1 (dJointID);
dReal dJointGetHinge2Angle1Rate (dJointID);
dReal dJointGetHinge2Angle2Rate (dJointID);
: 경첩-2 axis1과 axis2에 대한 각도와 시간 변화율을 얻는다.
: 고정점과 축이 설정될 때, 부착된 강체의 현재 위치가 검사되고, 그 위치는 0이 될 것이다.
[ODE 사용 : 고정 관절]
고정 관절은 두 강체 또는 강체와 정적 환경 사이에 고정된 상대적인 위치와 회전을 유지한다.
이 관절을 사용하는 것은 디버깅을 제외하고 실제로 결코 좋은 생각이 아니다.
두 강체를 서로 달라붙게 할 바에는 하나의 강체로 표현하는 것이 더 낫다.

void dJointSetFixed (dJointID);
: 강체 사이의 현재 상대적인 위치와 회전을 상기하기 위해 이미 부착된 고정 관절에 이 함수를 호출한다.
[ODE 사용 : 접촉 관절]
접촉 관절은 body1과 body2가 접촉점에서 서로 파고드는 것을 방지한다.

이것은 강체가 접촉면의 법선 방향으로 “나가는” 속도만를 가지도록 허용함으로써 가능하다.
접촉 관절은 일반적으로 한 시간 스텝의 수명을 갖고, 충돌 감지에 응답하여 생성되고 소멸된다.
접촉 관절은 법선에 수직인 두 마찰 방향에 특별한 힘을 적용함으로써 접촉면에 마찰을 시뮬레이션할 수 있다.
접촉 관절을 생성할 때, dContact 구조체를 제공해야 한다. 이것의 정의는 다음과 같다:

 

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

geom – 충돌 함수에 의해 설정된다. 충돌 절에서 설명된다.
fdir1 – 마찰력이 적용되는 방향을 나타내는 “첫번째 마찰 방향” 벡터이다. 이것은 단위 벡터여야 하며 접촉 법선에 수직이여야 한다(일반적으로 이것은 접촉면의 접선이다). surface.mode에 [[#dContactFDir1 | dContactFDir1]]플랙이 설정되었을 때 정의되어야 한다. “두번째 마찰 방향”은 접촉 법선과 fdir1에 모두 수직인 벡터이다.
surface – 사용자에 의해 설정되는 구조체이다. 충돌 표면의 속성들을 정의한다. 다음과 같은 멤버를 가진다:

*int mode – 접촉 플랙. 항상 설정돼 있어야 한다.
다음 Flag들의 하나 이상의 조합으로 설정된다:

dContactMu2
– 설정되지 않으면, 두 마찰 방향에 대해 mu를 사용한다. 설정되면, 마찰 방향1에 대해 mu를 사용하고, 마찰 방향2에 대해 mu2를 사용한다.  dContactFDir1
– 설정되면, fdir1를 마찰 방향1로 하고, 그렇지 않으면 자동으로 접촉 법선에 수직인 마찰 방향1를 계산한다
(이 경우 도출된 방향은 예측할 수 없다).
dContactBounce
– 설정되면, 접촉면은 탄력을 가지게 되어 강체가 서로 되튄다. 정확한 탄력도는 bounce 매개 변수에 의해 제어된다.
dContactSoftERP
– 설정되면, 접촉 법선의 오류 감쇠 매개 변수는 soft_erp 매개 변수로 설정된다. 이는 표면을 부드럽게 만든다.
dContactSoftCFM
– 설정되면, 접촉 법선의 구속력 혼합 매개 변수는 soft_cfm 매개 변수로 설정된다. 이는 표면을 부드럽게 만든다.
dContactMotion1
– 설정되면, 접촉면은 강체들의 움직임과는 독립적으로 움직이는 것처럼 가정한다. 이것은 표면위를 움직이는 일종의 컨베이어 벨트와 같다.
motion1은 마찰 방향1에서 표면 속도를 정의한다.
dContactMotion2
– 마찰 방향2에 대한 것을 빼고, 위와 동일.
dContactSlip1
– 마찰 방향1에서의 힘-종속-미끄럼(FDS; force-dependent-slip).
dContactSlip2
– 마찰 방향2에서의 힘-종속-미끄럼(FDS; force-dependent-slip).
dContactApprox1_1
– 마찰 방향1에 대한 마찰 피라미드 근사(friction pyramid approximation)를 사용한다.
설정되지 않으면 상수-힘-한계 근사(constant-force-limit approximation)가 사용된다 (mu가 힘 한계이다).
dContactApprox1_2
– 마찰 방향2에 대한 마찰 피라미드 근사(friction pyramid approximation)를 사용한다.
설정되지 않으면 상수-힘-한계 근사(constant-force-limit approximation)가 사용된다 (mu가 힘 한계이다).
dContactApprox1
– dContactApprox1_1, dContactApprox1_2와 동일하다.

*dReal mu : 쿨롱(Coulomb) 마찰 계수. 0에서 dInfinity까지의 범위안에 있어야 한다.
0은 마찰이 없는 접촉을, dInfinity는 절대로 미끄러지지 않는 접촉을 나타낸다.
마찰이 없는 접촉은 마찰이 있는 접촉보다 계산 시간이 덜 걸리고, 무한 마찰 접촉은 유한 마찰 접촉보다 계산 비용이 더 싸다.
값이 항상 설정되어 있어야 한다.
*dReal mu2 : 마찰 방향2에 대한 선택적인 쿨롱 마찰 계수 (0..dInfinity). mode에 대응하는 플랙이 설정되어 있을때만 유효하다.
*dReal bounce : 복원 매개 변수 (0..1). 0은 표면이 전혀 탄력적이지 않다는 것을 의미하고, 1은 최대 탄력도이다.
mode에 대응하는 플랙이 설정되어 있을때만 유효하다.
*dReal bounce_vel : 되튐에 필요한 최소한의 접촉 속도 (단위 m/s). 접촉 속도가 이보다 이하이면 사실상 되튐 매개 변수가 0이된다.
mode에 대응하는 플랙이 설정되어 있을때만 유효하다.
*dReal soft_erp : 접촉 법선의 “부드러움(softness)” 매개 변수. mode에 대응하는 플랙이 설정되어 있을때만 유효하다.
*dReal soft_cfm : 접촉 법선의 “부드러움(softness)” 매개 변수. mode에 대응하는 플랙이 설정되어 있을때만 유효하다.
*dReal motion1,motion2 : 마찰 방향1, 2에서 표면 속도 (단위 m/s). mode에 대응하는 플랙이 설정되어 있을때만 유효하다.
*dReal slip1,slip2 : 마찰 방향1, 2에 대한 힘-종속-미끄럼(FDS; force-dependent-slip) 계수.
mode에 대응하는 플랙이 설정되어 있을때만 유효하다.

FDS는 접촉면들이 표면의 접선 방향으로 적용되는 힘에 비례하는 속도로 서로를 지나가도록 하는 효과이다.

마찰 계수 mu가 무한대일 때 접촉점을 고려해보자.
통상적으로, 서로를 미끄러져 지나가도록 하기 위해, 힘 f가 두 접촉면들에 적용되면,
접촉면들이 움직이지 않을 것이다. 그러나, FDS 계수가 양수 k값으로 설정되면, k*f의 등속도에 도달하여 접촉면들이 서로를 지나갈 것이다.
이는 보통의 마찰 효과와는 사뭇 다르다는 점을 주목하라
– 힘은 접촉면 서로에 관하여 등가속(constant acceleration)을 일으키지 않는다
등속도에 도달하기위해 짧은 가속을 일으킨다.

이것은 몇가지 상황을 모델링하는 데 유용하다, 특히 타이어가 그렇다. 예를 들어 길에 정차된 차를 보자.
차를 앞으로 밀면 움직이기 시작한다 (즉, 타이어가 구르기 시작한다).
수직 방향으로 차를 밀면 꿈쩍도 하지 않는다. 타이어가 그 방향으로는 구르지 않기 때문이다.
그러나 – 만약 차가 v속도로 이동중에, 수직 방향으로 힘 f를 적용하면 타이어가 f*v에 비례하는 속도로 길위를 미끄러진다(그렇다, 실제로 일어난다).

ODE에서 이것을 모델링하기 위해서는 타이어-길 접촉 매개 변수들을 다음과 같이 설정한다
– 타이어가 구르는 방향에서 마찰 방향1을 설정하고, 마찰 방향2에서 FDS 미끄럼 계수를 k*v로 설정한다.
v는 타이어가 구르는 속도이고, k는 실험에서 얻을 수 있는 타이어 매개 변수이다.

FDS는 쿨롱 마찰의 끈적임/미끄럼 효과와는 완전히 별개다 – 두 모드가 하나의 접촉점에 같이 사용될 수 있다.
[ODE 사용 : 각모터 관절]
각모터 관절(angular motor)은 제어되는 두 강체의 상대적인 각속도를 허용한다.
각속도는 세개의 축으로 제어될 수 있다, 돌림힘 모터와 멈춤(torque motors and stops)이 축들에 대한 회전을 위해 설정된다
(아래의 “멈춤과 모터 매개 변수들(stops and motor parameters)”항을 보라).
이것은 주로 볼 관절과 결합할 때 유용하다 (볼 관절은 각자유도들을 전혀 구속하지 않는다),
하지만 각제어(angular control)가 필요한 어느 상황에서든 사용될 수 있다.
볼 관절로 각모터를 사용하기 위해서, 볼 관절이 부착된 두 강체에 각모터를 간단히 부착하기만 하면 된다.

각모터는 다른 모드들에서 사용될 수 있다. dAMotorUser 모드에서는,
각모터가 제어하는 축들을 사용자가 직접 설정한다.
dAMotorEuler 모드에서는, 각모터가 상대적인 회전에 상응하는 오일러 각들(euler angles)을 계산한다,
돌림힘 모터와 멈춤이 오일러 각으로 설정되는 것을 허용한다.
오일러 각을 가진 각모터 관절은 그림 10에 나와있다.

 

그림에서, a0, a1, a2는 각모터를 제어하는 세 축이다. 녹색 축은 body1에 고정된다.
파란색 축은 body2에 고정된다. body1 축들로부터 body2 축들을 얻기위해 아래 순서의 회전들을 수행한다

 

*a0축에 대해 theta<sub>0</sub>만큼 회전한다.
*a1축에 대해 theta<sub>1</sub>만큼 회전한다 (a<sub>1</sub>는 원래 위치로부터 회전되었다).
*a2축에 대해 theta<sub>2</sub>만큼 회전한다 (a<sub>2</sub>는 원래 위치로부터 두번 회전되었다).

오일러 각들을 사용할 때 중요한 제약 조건이 있다: theta1각은 – pi/2 … pi/2 범위 밖에 있어선 안된다.
만약 범위를 넘으면 각모터 관절이 불안정해질 것이다 (+/- pi/2에 특이점이 있다).
따라서 axis1에 적절한 멈춤을 설정해야 한다.

void dJointSetAMotorMode (dJointID, int mode);
int dJointGetAMotorMode (dJointID);
: 각모터 모드를 설정(리턴)한다. mode 매개 변수는 다음 상수들 중 하나여야 한다:

dAMotorUser  – 각모터 축과 관절 각 설정이 완전히 사용자에 의해 제어된다. 이것이 기본 모드이다.
dAMotorEuler – 오일러 각들이 자동 계산된다. 축 a1 또한 자동 계산된다.
이 모드에서 각모터 축들은 올바르게 설정되야 한다. 초기에 이 모드가 설정되면,
강체의 현재 상대적인 회전들은 0에서 모든 오일러 각들과 일치한다.

void dJointSetAMotorNumAxes (dJointID, int num);
int dJointGetAMotorNumAxes (dJointID);
: 각모터에 의해 제어되는 각 축들의 수를 설정/리턴한다. 인자 num은 0부터 3까지의 범위내에 속한다.
0은 관절을 비활성화한다. dAMotorEuler 모드에서는 자동적으로 3으로 설정된다.

void dJointSetAMotorAxis (dJointID, int anum, int rel, dReal x, dReal y, dReal z);
void dJointGetAMotorAxis (dJointID, int anum, dVector3 result);
int dJointGetAMotorAxisRel (dJointID, int anum);
: 각모터 축들을 설정/리턴한다. anum 인자는 변경할 축(0,1, 또는 2)을 선택한다.
각각의 축은 세 개의 “상대 회전(relative orientation)” 모드들 중 하나를 가질 수 있고,
rel에 의해 선택된다:
*0 – 축이 전역 틀에 고정된다.
*1 – 축이 첫번째 강체에 고정된다.
*2 – 축이 두번째 강체에 고정된다.
: 축 벡터 (x,y,z)는 rel값에 관계없이 항상 전역 좌표계에서 설정된다. 두개의 GetAMotorAxis 함수들이 있다,
하나는 축을 리턴하고 하나는 관련 모드를 리턴한다.

dAMotorEuler 모드에 대해:
– *axis0과 axis2만 설정하면 된다. axis1은 자동으로 매 시간 스텝마다 계산된다.
– *axis0과 2만 반드시 서로 수직이어야 한다.
– *axis0은 첫번째 강체에, axis2는 두번째 강체에 고정되어야 한다.

void dJointSetAMotorAngle (dJointID, int anum, dReal angle);
: 각모터에게 축 anum에 대한 현재 각도를 알려준다. 이 함수는 dAMotorUser 모드에서만 호출되어야 한다,
왜냐하면 이 모드에서 각모터는 관절 각을 알 수 있는 방법이 없기 때문이다.
각 정보는 축을 따라 멈춤이 설정되었을 때 필요하다. 그러나 각모터에는 필요하지 않다.

dReal dJointGetAMotorAngle (dJointID, int anum);
: 축 anum에 대한 현재 각도를 리턴한다. dAMotorUser 모드에서 이것은 [[#dJointSetAMotorAngle | dJointSetAMotorAngle]]로 설정한 값이다.
dAMotorEuler 모드에서 이것은 오일러 각과 일치한다.

dReal dJointGetAMotorAngleRate (dJointID, int anum);
: 축 anum에 대한 현재 각 변화율을 리턴한다. dAMotorUser 모드에서 정보가 충분치 않을 때 이것은 항상 0이다.
dAMotorEuler 모드에서는 오일러 각 변화율과 일치한다.

[일반]
관절 기하구조 매개 변수를 설정하는 함수들은 관절이 강체에 부착된 후에 호출되어야 한다.
이때 강체가 올바른 위치에 있어야 한다. 그렇지 않으면 관절이 올바르게 초기화되지 않을 것이다.
관절이 미리 부착되어 있지 않으면, 이 함수들은 아무 일도 하지 않는다.

매개 변수를 얻는 함수에 대해, 시스템이 조정되어 있지 않으면 (즉, 몇가지 관절 오류가 있다면),
고정점/축이 body1([[#dJointAttach | dJointAttach]]함수에서 body1을 0으로 지정한 경우에는 body2)에 대해서만
적절한 값을 가질 것이다.

모든 관절에 대한 고정점은 기본값이 (0,0,0)이다. 모든 관절에 대한 기본 축은 (1,0,0)이다.
축이 설정될 때, 단위 길이로 정규화된다. 축을 얻는 함수들은 이처럼 조정된 단위 길이의 축을 리턴한다.
관절의 각 또는 위치를 측정할 때, 0값은 서로에 대한 강체들의 초기 위치와 일치한다.
관절의 각 또는 위치(또는 변화율)를 직접 설정하는 함수는 없다,
그대신 대응하는 강체의 위치와 속도를 설정해야 한다.
[ODE 사용 : 멈춤과 각모터 매개 변수]
관절이 처음 생성될 때, 관절이 아무 제약없이 멋대로 움직이는 것을 막을 수가 없다.
예를 들면, 경첩 관절은 360도로 움직일 수 있고, 미닫이 관절은 어떤 길이로든 움직일 수 있다.
관절에 멈춤을 설정하여 이러한 운동 범위를 제한할 수 있다. 이는 관절 각(또는 위치)이 하한 멈춤 값 이하로
내려가거나 상한 멈춤 값 이상으로 올라가지 않게 해준다.
0의 관절 각(또는 위치)은 강체의 초기 위치와 상응한다.
많은 관절 타입들이 멈춤 뿐만아니라 모터를 가질 수 있다.
모터는 관절의 회전축(또는 미닫이 관절)이 원하는 속도에 도달하도록 관절의 자유도에 돌림힘(또는 힘)을 적용한다.
모터에는 힘 한계가 있다, 힘 한계는 주어진 최대 힘/돌림힘 이상을 관절에 적용할 수 없다는 것을 의미한다.
모터는 2개의 매개 변수를 가진다 – 원하는 속도, 그리고 그런 속도에 도달하기위해 쓸 수 있는 최대 힘이다.

이것은 실세계의 모터, 엔진 또는 서보모터(servo)를 아주 단순화한 모델이다.
그러나, 이것은 관절에 연결되기 전에 변속기(gearbox)로 변속이 되는 모터를 모델링하는 데 상당히 유용하다.
그러한 장치들은 흔히 원하는 속도를 설정하여 제어되고, 그러한 속도(관절에 가할 수 있는 특정한 힘의 양에 대응하는)를
얻기 위해 최대 힘을 발생할 수 있다.

모터는 관절에 마른(또는 쿨롱) 마찰을 정확히 모델링하는 데 사용되기도 한다.
단순히 원하는 속도를 0으로 설정하고 최대 힘을 어떤 상수값으로 설정한다
– 그러면 모든 관절 운동이 그 힘에 의해 방해를 받을 것이다.

관절 멈춤과 모터를 사용하는 것에 대한 대안은 힘을 강체 자체에 적용하는 것이다.
모터 힘을 적용하는 것은 쉽다, 그리고 관절 멈춤은 억제된 용수철 힘으로 에뮬레이트될 수 있다.
그러나 힘을 직접적으로 적용하는 것은 종종 좋은 접근법이 아니며 조심스럽게 다루지 않으면 가혹한
안정성 문제를 일으킬 수 있다.
원하는 속도를 얻기 위해 강체에 힘을 적용하는 경우를 생각해 보자.
이 힘을 계산하기 위해서 현재 속도에 대한 정보를 사용한다:

force = k * (desired speed – current speed)

이것은 몇가지 문제를 갖고 있다.
첫째, 매개 변수 k는 반드시 수동으로 조율되어야 한다. 이 값이 너무 낮으면 강체는 원하는 속도에 도달하는 데
시간이 오래 걸릴 것이다. 너무 높으면 시뮬레이션이 불안정해질 것이다.
둘째, k를 잘 선택했더라도 강체가 속도를 내는데 약간의 시간 스텝이 걸릴 것이다.
셋째, 다른 “외부의” 힘이 강체에 적용되면, 원하는 속도에 결코 도달할 수 없게 된다
(좀더 복잡한 힘 방정식이 필요하겠으나 추가 매개 변수와 또다른 문제점을 갖게 될 것이다).

관절 모터는 이러한 모든 문제를 해결한다
– 강체가 한번의 시간 스텝에서 속도를 낼 수 있고, 허용된 양 이상의 힘을 받지 않는다.
관절 모터는 사실 구속 조건으로서 수행되기 때문에 추가 매개 변수가 필요없다.
그리고 적절한 힘을 얻기 위해 시간 스텝를 미리 내다볼 수 있다.
이는 계산 비용이 더 들지만 더욱 견고하고 안정적이며 설계하는 데 시간이 덜 든다.
방대한 강체 시스템에서 특히 그렇다.
비슷한 인자들이 관절 멈춤에 적용된다.
[ODE 사용 : 매개 변수 함수]
다음은 관절에서 멈춤과 모터 매개 변수들을 설정하는 함수들이다

void dJointSetHingeParam (dJointID, int parameter, dReal value);
void dJointSetSliderParam (dJointID, int parameter, dReal value);
void dJointSetHinge2Param (dJointID, int parameter, dReal value);
void dJointSetUniversalParam (dJointID, int parameter, dReal value);
void dJointSetAMotorParam (dJointID, int parameter, dReal value);
dReal dJointGetHingeParam (dJointID, int parameter);
dReal dJointGetSliderParam (dJointID, int parameter);
dReal dJointGetHinge2Param (dJointID, int parameter);
dReal dJointGetUniversalParam (dJointID, int parameter);
dReal dJointGetAMotorParam (dJointID, int parameter);
: 각 관절 타입에 대해 한계/모터 매개 변수들을 설정/리턴한다

dParamLoStop
– 하한 멈춤 각 또는 위치. 이것을 -dInfinity(기본값)로 설정하면 하한 멈춤을 끈다. 회전가능한 관절에 대해, 효과를 보기위해서는 멈춤 각이 반드시 – pi이상이어야 한다.

dParamHiStop
– 상한 멈춤 각 또는 위치. 이것을 dInfinity(기본값)로 설정하면 상한 멈춤을 끈다. 회전 가능한 관절에 대해, 효과를 보기 위해서는 멈춤 각이 반드시 pi이하이어야 한다. 상한 멈춤이 하한 멈춤보다 작다면, 둘다 효력을 잃게 될 것이다.

dParamVel
– 원하는 속도 (각 또는 선 속도).

dParamFMax
– 모터가 원하는 속도를 얻기 위해 사용하는 최대 힘 또는 돌림힘. 반드시 0보다 같거나 커야한다. 0(기본값)으로 설정하면 모터를 끈다.

dParamFudgeFactor
– 현재 관절 멈춤/모터 구현은 작은 문제가 하나 있다: 관절이 하나의 멈춤에 있고 모터가 멈춤으로부터 관절을 움직이도록 설정되어 있을 때, 한번에 너무 많은 힘이 적용되면 “뛰는(jumping)” 운동을 하게 된다. 이러한 과다한 힘을 조절하기위해 조작 계수(fudge factor)를 사용한다. 이 계수는 0과 1(기본값) 사이의 값을 가진다. 관절에서 뛰는 운동이 너무 눈에 띄면, 값을 줄일 수 있다. 이 값이 너무 작으면 모터가 관절을 멈춤 상태에서 벗어나 움직이도록 할 수가 없다.

dParamBounce
– 멈춤의 탄력도. 0..1 범위의 복원 매개 변수이다. 0은 멈춤이 전혀 탄력적이지 않음을 의미하고, 1은 최고의 탄력도를 의미한다.

dParamCFM
– 멈춤이 아닐 때 구속 힘 혼합 (CFM; constraint force mixing) 값이 사용된다.

dParamStopERP
– 멈춤에 의해 사용되는 오류 감쇠 매개 변수(ERP; error reduction parameter).

dParamStopCFM
– 멈춤에 의해 사용되는 구속 힘 혼합(CFM) 값. ERP 값과 함께 스펀지나 부드러운 멈춤을 위해 사용된다. 이것은 움직이지 않는 관절을 위한 것이므로, 힘을 받은 관절이 한계에 다다를 때 예상대로 작동하지 않는다.

dParamSuspensionERP
– 완충 장치의 오류 감쇠 매개 변수 (ERP). 현재 경첩-2 관절에서만 구현되었다.

dParamSuspensionCFM
– 완충 장치의 구속 힘 혼합(CFM) 값. 현재 경첩-2 관절에서만 구현되었다.
: 주어진 관절에 어떤 매개 변수가 구현되지 않았다면, 그 값을 설정하는 것은 아무 효과가 없다.
: 이 매개 변수 이름들은 다음에 숫자(2또는 3)이 올 수 있다.
즉, 경첩-2 관절에서 두번째 축, 또는 각모터 관절에서 세번째 축을 예로 들 수 있다.
상수 dParamGroup은 다음과 같이 정의 될 수 있다: dParamXi = dParamX + dParamGroup * (i-1)
[ODE 사용 : 관절에 힘/돌림힘을 설정]
모터는 관절에 속도를 직접적으로 설정하는 것을 허용한다.
그러나, 대신 관절에 돌림힘이나 힘을 설정하고 싶을 때도 있다. 다음 함수들이 그런 일을 한다.
모터에 영향을 주지 않지만 모터에 부착된 강체에 [[#dBodyAddForce | dBodyAddForce]]/[[#dBodyAddTorque | dBodyAddTorque]]를 호출한다.

dJointAddHingeTorque(dJointID joint, dReal torque)
: 경첩 축에 대해 torque 크기 만큼의 돌림힘을 적용한다.
즉, 경첩 축의 방향으로 body1에 돌림힘을 적용하고 반대 방향으로 body2에 같은 크기의 힘을 적용한다.
이 함수는 단순히 [[#dBodyAddTorque | dBodyAddTorque]]의 래퍼 함수이다.

dJointAddUniversalTorques(dJointID joint, dReal torque1, dReal torque2)
: 십자축 관절의 axis1에 대해 torque1을 적용하고, axis2에 대해 torque2를 적용한다.
이 함수는 단지 [[#dBodyAddTorque | dBodyAddTorque]]의 래퍼함수이다.

dJointAddSliderForce(dJointID joint, dReal force)
: 미닫이 관절의 방향으로 force 크기 만큼의 힘을 적용한다.
즉, 미닫이 관절의 축방향으로 body1에 force의 크기를 가진 힘을 적용한다.
그리고 반대 방향으로 body2에 같은 크기의 힘을 적용한다.
이 함수는 단지 [[#dBodyAddForce | dBodyAddForce의 래퍼함수이다.

dJointAddHinge2Torques(dJointID joint, dReal torque1, dReal torque2)
: 경첩2 관절의 axis1에 대해 torque1을 적용하고, axis2에 대해 torque2를 적용한다.
이 함수는 단지 [[#dBodyAddTorque | dBodyAddTorque]]의 래퍼함수이다.

dJointAddAMotorTorques(dJointID joint, dReal torque0, dReal torque1, dReal torque2)
: 각모더의 축0에 대해 torque0을, axis1에 대해 torque1을, axis2에 대해 torque2를 적용한다.
만약 모터가 세 개 이하의 축을 가지면, 나머지 돌림힘은 무시된다.
이 함수는 단지 [[#dBodyAddTorque | dBodyAddTorque]]의 래퍼함수이다.