이 자료는
4족 로봇을 위한 기초자료로서 Bullet3D의 라이브러리 이해를 위해서 정리한 내용임
위의 내용은 로봇암의 한쪽 부분을 설명하기 위한 자료
오른쪽의 마젠타 색상의 사각형이 고정부 그기서 부터 옆으로 관절1, 관절2 형태로 취해진다.
관절2(파란색)을 키보드로 움직이면 중간의 관절1의 각도가 변해야 하는게 목표이다.
조금 움직였을때의 모습
관절2번을 아래로 회전하게 되면서 관절1번의 각도가 변한 모습이다.
위의 화면은 카메라 뷰에서 본 화면이며
객체마다 Axis가 표기 되어 있는데
빨간색 - X
녹색 - Y
파란색 - Z (화면 뒤쪽)
아래는 관련된 코드이다.
화면에 보이는 객체는 일단 모델링 데이터가 존재 해야 하기 때문에 그 모델링 데이터는
btCollisionShape* m_shapes[2];//BODYPART_COUNT];
이걸로 관리 된다.
이 포인터 객체에 해당 데이터가 생성이 된다.
m_shapes[0] = new btBoxShape(btVector3(fBodySize, fBodySize, fBodySize));
m_shapes[1] = new btBoxShape(btVector3(fBodySize, fBodySize, fBodySize));
이 객체들은 나중에 화면에 객체를 표현하기 위한 인스턴스 객체들이다.
아래 코드는 화면에 보여지는 3개의 객체를 만든 내용이다.
// 높이 0.5에 기본 위치를 잡는다.
float fHeight = 1.0;
btTransform offset; offset.setIdentity();
offset.setOrigin(positionOffset);
// root
// 중앙의 둥근원형을 만든다.
btVector3 vRoot = btVector3(btScalar(0.), btScalar(fHeight), btScalar(0.));
btTransform transform;
transform.setIdentity();
transform.setOrigin(vRoot);
// 무게를 0으로 하면 고정형태의 객체가 만들어 진다.
m_bodies[0] = localCreateRigidBody(btScalar(0.), offset*transform, m_shapes[0]);
// 다리1
transform.setIdentity();
btVector3 vBoneOrigin = btVector3(1.0, 1.0, 0);
transform.setOrigin(vBoneOrigin);
btVector3 vToBone = (vBoneOrigin - vRoot).normalize();
btVector3 vAxis = vToBone.cross(vUp);
m_bodies[1] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[1]);
// 다리 2
transform.setIdentity();
vBoneOrigin = btVector3(2.0, 1.0, 0);
transform.setOrigin(vBoneOrigin);
vToBone = (vBoneOrigin - vRoot).normalize();
vAxis = vToBone.cross(vUp);
m_bodies[2] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[1]);
위의 내용으로 화면에 보여지는 객체를 만들고 나면 이제 객체들을 연결하기 위한 조인트를 연결해 줘야 한다.
이걸 하지 않으면 무게 1짜리가 바닥으로 바로 떨어지게 된다. 중력이 있기 때문에.
아래 코드는 Hing를 추가 하는 모습이다.
나중에 서보 모터를 연동하기 위함이기 때문에 Hing라는 걸로 연결하도록 한다.
btHingeConstraint* hingeC;
//btConeTwistConstraint* coneC;
btTransform localA, localB, localC, localD;
float fAngle = 0;
float fSin = sin(fAngle);
float fCos = cos(fAngle);
// hip joints
localA.setIdentity();
localB.setIdentity();
localA.getBasis().setEulerZYX(0,0,0);
localA.setOrigin(btVector3(0.5, -0.5, 0));
localB.getBasis().setEulerZYX(0,0,0);
localB.setOrigin(btVector3(-0.5, -0.5, 0));
hingeC = new btHingeConstraint(*m_bodies[0], *m_bodies[1], localA, localB);
hingeC->setLimit(DEG2RAD(0), DEG2RAD(90));
m_joints[0] = hingeC;
m_ownerWorld->addConstraint(m_joints[0], true);
// hip joints
localA.setIdentity();
localB.setIdentity();
localA.getBasis().setEulerZYX(0,0,0);
localA.setOrigin(btVector3(0.5, -0.5, 0));
localB.getBasis().setEulerZYX(0,0,0);
localB.setOrigin(btVector3(-0.5, -0.5, 0));
hingeC = new btHingeConstraint(*m_bodies[1], *m_bodies[2], localA, localB);
hingeC->setLimit(0.0f, 0.0f);
m_joints[1] = hingeC;
m_ownerWorld->addConstraint(m_joints[1], true);
위의 코드와 같이 구성하게 되면 Hing로 3개의 객체가 연결이 되게 된다.
위의 모델에서 관절2를 움직이기 위한 코드는 아래와 같이 작성하면 된다.
// 해당 모델을 선택된 상태로 가져와야 한다.
// 이걸 하지 않으면 객체는 업데이트 자체가 되지 않는다. (왜 인지는 모르겠다.)
m_rigs[0]->m_bodies[2]->setActivationState(DISABLE_DEACTIVATION);
// Hing 객체를 찾는다. (미리 알고 있기 때문에 배열값을 바로 가져온다.)
btHingeConstraint* hingeC = static_cast<btHingeConstraint*>(m_rigs[0]->GetJoints()[1]);
if( hingeC )
{
btScalar hingeAngle = hingeC->getHingeAngle(); // 참고용
btScalar hingeLow = hingeC->getLowerLimit(); // 참고용
btScalar hingeHigh = hingeC->getUpperLimit(); // 참고용
// 이걸 보면 알 수 있듯이 해당 객체의 Hing의 각도를 그냥 하나로 고정시켜서 키보드 값으로 이동 가능하게 처리 하였다.
hingeC->setLimit(DEG2RAD(n_TestAngle), DEG2RAD(n_TestAngle));
>> hingeC->setLimit(DEG2RAD(n_TestAngle), DEG2RAD(n_TestAngle), 0, 1, 0); 이걸로 고치면 움직임이 조금더 빠르다. 소프트한게 없어지고..
// 모델 생성시 아래 코드도 그냥 0으로 바꾸면 움직임이 조금더 빨라진다.
for (i = 0; i < 3; ++i)
{
m_bodies[i]->setDamping(0, 0); // 이 값이 크지면 중력의 영향을 천천히 받아서 모델이 천천히 떨어진다.
m_bodies[i]->setDeactivationTime(0);
m_bodies[i]->setSleepingThresholds(0, 0);
}
// 아래 코드는 굳이 하지 않아도 동작은 한다.
hingeC->enableAngularMotor(false, 0, 0);
}
아래는 관련 코드입니다.
BulletTestApp_분석용_Constraint.zip