Pinocchio에서 관절 몇 개를 잠근 축소 모델을 만들 때는 잠글 관절 목록과 그 관절을 고정할 기준 자세를 함께 지정합니다. 같은 손목 관절을 잠그더라도 곧게 편 자세에 고정했는지, 꺾인 자세에 고정했는지에 따라 나머지 관절이 움직일 때 도구가 따라가는 위치와 방향이 달라집니다.
buildReducedModel의 reference_configuration은 이 기준 자세를 전달하는 인자입니다. 단순히 계산을 시작할 초기 추정값을 주는 용도로 읽으면 중요한 차이를 놓칩니다. 이 호출은 일부 관절의 움직임을 제거한 새 모델을 만들며, 선택한 관절은 지정한 자세에 묶입니다. 공식 축소 모델 예제는 이 동작을 UR5 손목 관절 세 개로 보여줍니다.
관절 목록은 무엇을 잠글지, 기준 자세는 어디에 잠글지 정한다
예제의 원본 모델은 URDF에서 읽어 옵니다. 이어 wrist_1_joint, wrist_2_joint, wrist_3_joint라는 이름이 모델에 있는지 확인하고, 각각을 관절 ID로 바꿔 잠금 목록을 만듭니다. 기준 자세에는 어깨와 팔꿈치에 해당하는 값으로 0, 손목에 해당하는 값으로 1을 넣습니다.
여기서 두 입력의 역할은 다릅니다. 잠글 관절 ID 목록이 축소할 자유도를 선택합니다. 원본 모델에 맞는 전체 자세 배열은 그 관절을 어느 위치에 고정할지 정하는 기준입니다. 배열에 어깨 값도 들어 있다는 이유로 어깨까지 잠기는 것은 아닙니다. 잠금 대상은 별도 목록에 지정돼 있습니다.
따라서 원본 자세에서 잠글 관절의 원소를 먼저 지운 뒤 reference_configuration에 넣는 방식은 예제의 입력 구조와 다릅니다. 축소를 만드는 시점에는 원본 모델과 원본 모델에 맞는 자세를 전달하고, 결과로 축소된 모델을 받습니다.
이 구분은 잠긴 관절이 사라지면 연결 구조도 모두 사라진다고 생각할 때 특히 중요합니다. Pinocchio의 관절 설명은 URDF의 fixed joint를 움직이는 관절 대신 모델의 작업 프레임으로 취급한다고 설명합니다. 움직임을 나타내는 자유도와, 링크 사이의 위치·방향을 표현할 기준은 서로 다른 역할입니다.
팔꿈치를 펴서 잠근 팔과 꺾어서 잠근 팔
다음은 가상의 평면 2링크 팔 계산입니다. 첫 링크 길이를 0.4m, 두 번째를 0.3m로 두고, 어깨는 계속 움직일 수 있지만 팔꿈치는 잠근다고 가정합니다. 어깨 각도가 0일 때 끝점은 다음처럼 달라집니다.
| 팔꿈치를 고정한 각도 | 어깨에서 본 끝점 좌표 | 남아 있는 움직임 |
|---|---|---|
| 0도: 두 링크가 일직선 | x = 0.7m, y = 0m | 어깨 회전 |
| 90도: 두 번째 링크가 위쪽 | x = 0.4m, y = 0.3m | 어깨 회전 |
두 경우 모두 어깨 하나만 움직입니다. 그러나 어깨가 같은 각도로 회전해도 끝점이 그리는 원의 반지름과 도구 방향은 달라집니다. 관절 수가 같아도 축소 모델의 운동학이 달라지는 이유입니다.
실제 손목을 잠그는 문제도 같은 원리로 읽을 수 있습니다. 도구의 질량이나 메시를 작게 만드는 것이 아니라, 손목 뒤의 구조를 특정 자세에 고정한 상태로 남은 관절의 운동을 계산하는 것입니다. 도구가 손목 회전축에서 떨어져 있다면 고정 각도 변화가 도구의 위치에도 영향을 줍니다. 축 위에 있어 위치가 같더라도 방향은 달라질 수 있습니다.
계산 모델, 화면에 보이는 모델, 충돌 모델을 함께 선택한다
공식 예제는 buildReducedModel을 세 가지 방식으로 호출합니다. 반환값의 차이는 화면 표시나 충돌 형상까지 축소된 구조에 맞출 필요가 있는지를 반영합니다.
| 호출에 전달하는 모델 | 반환되는 결과 | 사용할 상황 |
|---|---|---|
| 원본 운동학·동역학 모델 | 축소 모델 | 표시나 충돌 형상 없이 모델 계산만 하는 경우 |
| 원본 모델과 visual 모델 | 축소 모델과 축소 visual 모델 | 축소된 로봇을 표시하는 경우 |
| 원본 모델과 geometry 모델 목록 | 축소 모델과 축소 geometry 목록 | visual·collision 모델을 함께 다루는 경우 |
세 번째 방식의 핵심은 출력 geometry 목록이 입력 목록의 순서를 따른다는 점입니다. 아래는 예제의 호출 구조를 변수명만 간단히 바꾼 것입니다. model, visual_model, collision_model은 원본 모델에서 읽은 객체이고, joint_ids와 q_ref는 각각 잠글 관절 ID 목록과 원본 모델에 맞는 기준 자세라고 가정합니다.
geometry_models = [visual_model, collision_model]
reduced_model, reduced_geometry = pin.buildReducedModel(
model,
list_of_geom_models=geometry_models,
list_of_joints_to_lock=joint_ids,
reference_configuration=q_ref,
)
reduced_visual = reduced_geometry[0]
reduced_collision = reduced_geometry[1]
여기서 0번을 visual로 해석하는 이유는 함수가 언제나 visual을 먼저 반환해서가 아닙니다. 입력을 [visual_model, collision_model] 순서로 만들었기 때문입니다. 입력 순서를 바꾸면 반환값을 해석하는 순서도 함께 바뀝니다.
모델만 반환하는 첫 번째 호출 뒤에 원본 geometry 객체를 그대로 새 모델의 geometry로 취급하면, 서로 다른 모델 구조를 섞게 됩니다. 화면 표시나 충돌 형상이 필요하다면 해당 geometry도 함께 전달하는 호출을 선택하는 편이 의도가 분명합니다. 다만 collision 모델을 함께 반환받는 단계와 실제 충돌 검사를 수행하는 단계는 별개입니다. 이 예제는 축소된 geometry를 만드는 호출을 보여줍니다.
URDF 안에서도 visual·collision·inertial 정보의 용도가 나뉘는 배경은 URDF와 SDF의 로봇 모델 표현 차이에서 이어 볼 수 있습니다.
RobotWrapper의 기본 자세는 명시한 자세와 다를 수 있다
예제의 네 번째 경로는 RobotWrapper의 buildReducedRobot입니다. 앞의 buildReducedModel 호출 1~3은 관절 ID를 받지만, 이 래퍼 예제는 이름과 ID를 섞은 목록을 전달합니다. 기준 자세를 생략하면 neutral configuration을 사용한다는 주석도 있습니다.
현재 로봇이 멈춰 있는 자세에 손목을 고정하려는 경우라면, neutral 자세를 선택한 경우와 목적이 다릅니다. 기준 자세를 명시하면 어느 모양으로 잠근 모델인지 호출 자체에서 읽을 수 있습니다. 반대로 neutral 자세가 원하는 고정 형태라면 래퍼의 기본 동작이 그 선택에 해당합니다. 이를 모든 buildReducedModel 오버로드가 기준 자세를 생략할 수 있다는 뜻으로 넓혀 읽지는 않아야 합니다.
결국 축소 모델을 선택하는 기준은 남길 관절 수만이 아닙니다. 어떤 관절을 어떤 자세에 고정하며, 계산에 필요한 geometry를 어디까지 함께 바꿀지가 하나의 모델 선택을 이룹니다. 이 세 가지가 정해져야 같은 입력 자세를 두고 계산 모델과 화면 속 로봇이 같은 구조를 가리키게 됩니다.
이 글은 2026년 10월 15일 확인한 공식 문서와 커밋 700472f50e3349d81860b48b174a9f97d26fa7d9의 예제를 해석했습니다. Pinocchio 실행이나 실제 로봇 실험은 수행하지 않았으며, 표의 수치는 위에서 정의한 가상 평면 팔의 기하 계산입니다.