hello, robot

시뮬레이터에서 로봇 팔이 물체를 집지 못할 때 제일 먼저 의심하게 되는 건 제어예요. 목표 좌표가 틀렸거나 정렬이 안 맞았겠지 싶어서 오차를 계속 줄이게 되죠.

그런데 정렬 오차를 2밀리미터까지 좁혀도 그리퍼가 닫히기만 하고 물체는 제자리에 있는 상황이 있어요. 이때는 제어가 아니라 물리 모델을 봐야 해요.

시뮬Gazebo · 강체 물리 대상3 cm 큐브 그리퍼비대칭 — 죠 하나만 움직임 판정스톨 각도 + 물체 실좌표
정렬 오차
2 mm
여기까지 좁혀도 실패
패드 1 cm
0.431
rad · 닿기 한참 전에 멈춤
패드 제거
0.096
rad · 표면까지 닫힘
판정 임계
−0.05
rad · 헛집기 −0.07 차단
물체 최대 z
0.182
m · 바닥은 0.015

네 층에서 각각 실패해요

증상원인조치
충돌 형상그리퍼가 물체를 통과얇은 트라이메시의 접촉 판정 불안정기본 도형으로 교체
접촉 면적들면 빠짐죠가 호를 그려 점 접촉패드 근사 또는 동작 보상
반송 자세드는 중에 놓침어깨만 올리면 그리퍼 피치가 기움손목으로 흡수
판정헛집기를 성공으로 셈임계가 완전 닫힘에 가까움대조군으로 임계 재설정

얇은 트라이메시는 관통돼요

로봇 링크의 충돌 형상(collision geometry)은 보통 CAD에서 나온 STL 메쉬를 그대로 써요. 보기에는 정확하지만 물리 엔진 입장에서는 다뤄야 할 삼각형이 수천 개인 데다, 그리퍼 손가락처럼 얇고 볼록하지 않은(non-convex) 형상은 접촉 판정이 불안정해요.

증상은 관통이에요. 한 물리 스텝에서 손가락이 물체 앞에 있다가 다음 스텝에서 반대편으로 넘어가면, 그 사이에 접촉이 생성되지 않아 힘이 전달되지 않아요.

해결은 충돌 형상을 기본 도형(primitive)으로 바꾸는 거예요. 박스나 실린더는 접촉 계산이 해석적으로 닫혀 있어서 얇아도 안정적이에요. 비주얼 메쉬는 그대로 두고 충돌만 교체하면 보이는 모양은 유지돼요.

<collision name="fixed_finger_tip">
    <origin xyz="-0.0157 -0.0002 -0.0895" rpy="0 0 0"/>
    <geometry><box size="0.0156 0.0158 0.0299"/></geometry>
</collision>

여기서 박스 치수를 눈대중으로 정하면 새로운 문제가 생겨요. 실제 손가락보다 2센티미터 길게 잡힌 박스는 화면에 보이지 않는 채로 물체를 먼저 쳐서 넘어뜨려요. STL 정점을 파싱해 링크 좌표계 기준 경계 상자를 계산하고, 손가락 하나를 끝·중간·뿌리 세 박스로 나눠 근사하면 형상과 충돌이 일치해요.

점으로 닿으면 들 수 없어요

관통이 사라져도 물체를 들어올리는 순간 빠지는 문제가 남아요. 원인은 그리퍼 구조예요. 죠 하나만 움직이는 비대칭 그리퍼는 죠가 호를 그리며 닫히기 때문에, 강체끼리는 접촉이 점에 가깝게 생겨요. 점 접촉에서는 마찰이 하중을 버티지 못해요.

실물에서는 손가락 안쪽 고무 패드가 눌리면서 면으로 닿아 이 문제를 해결해요. 시뮬레이터에는 그 변형이 없으니, 손끝 충돌 박스를 접촉면 방향으로 약 1센티미터 키워서 눌린 패드를 강체로 근사하는 방법이 있어요.

패드 근사 두께죠가 멈추는 각도해석
1 cm0.431 rad물체에 닿기 한참 전에 멈춤
0.5 cm0.374 rad중간
없음0.096 rad표면까지 실제로 닫힘

죠가 더 깊이 들어간다는 건 물체 표면에 그만큼 가깝게 닿는다는 뜻이에요. 파지는 패드 근사로 성립하지만, 보이는 손가락과 실제 충돌면 사이에 간격이 생겨서 물체가 공중에 떠 보이는 부작용이 따라와요.

들어올릴 때 자세가 바뀌면 빠져요

파지가 성립해도 들어올리는 동안 놓치는 경우가 있어요. 어깨 관절 하나만 올리면 팔 전체가 호를 그리면서 그리퍼의 절대 자세(피치)가 같이 기울어지고, 물체가 죠 사이에서 빠져나가요.

어깨를 조금씩 나눠 올리면서 감소분만큼 손목을 반대로 굽히면 그리퍼 자세가 유지돼요. 관절 공간에서 보상하는 방식이라 역기구학 없이도 돼요.

while lf > 0.151:
    lf = max(0.15, lf - 0.07)
    wf = wrist0 + (lift0 - lf)      # 어깨가 내린 만큼 손목이 흡수
    move_arm({'arm_shoulder_lift': lf, 'arm_wrist_flex': wf}, 1.5)

이 단계와 중간 재조임이 들어간 뒤로는 패드 근사를 빼도 파지가 유지됐어요. 물리 모델의 부족분을 형상으로 메우는 대신 동작으로 메운 셈이에요.

힘 센서 없이 파지를 판정하기

집었는지 아닌지는 어떻게 알까요. 그리퍼에 힘 센서가 없다면 조인트 각도로 판정할 수 있어요. 허공에서 닫으면 완전 닫힘 각도까지 가지만, 물체를 물면 중간에서 멈춰요. 이 스톨(stall) 각도가 파지 성립의 신호예요.

임계를 어디에 둘까
완전 닫힘에 가까운 값을 임계로 두면 모서리를 살짝 걸친 상태도 성공으로 잡혀요.
-0.12-0.08-0.04+0.00+0.04+0.08그리퍼 스톨 각도 (rad) — 왼쪽일수록 더 닫힘임계 −0.05모서리 헛집기 −0.073 cm 큐브 정상 파지 +0.07 이상완전 닫힘에 가까운 값을 임계로 두면 모서리를 살짝 걸친 상태도 성공으로 잡혀요.

3센티미터 큐브의 정상 파지는 항상 +0.07 rad 이상이 나오는데 모서리 헛집기는 −0.07 rad 근처였어요. 임계를 −0.05 rad로 올리자 가짜 성공이 걸러졌어요. 정상값만 보고 임계를 정하면 이 간격이 안 보여요. 실패 사례의 분포를 같이 재야 갈리는 자리가 나와요.

상대 기준 재조임이 만드는 래칫

물체를 물었을 때 목표 각도가 실제 접촉 각도보다 깊으면, 컨트롤러는 목표에 닿으려고 계속 밀어넣어요. 그리퍼가 물체를 파고드는 현상이 이렇게 생겨요.

접촉을 감지한 뒤 목표를 “현재 각도 − 여유”로 재설정하는 대응이 자연스러워 보이는데, 여기에 함정이 있어요. 들어올리는 동안 미끄러짐을 막으려고 재조임을 여러 번 하게 되는데, 매번 기준이 현재 각도라서 조일 때마다 여유만큼 더 깊이 들어가는 래칫이 돼요.

재조임 다섯 번
같은 다섯 번인데 기준을 어디에 두느냐로 결과가 갈려요.
0.050.070.090.110.1312345재조임 횟수그리퍼 각도 (rad)계속 파고듦정체상대 기준 — 현재 각도 − 여유절대 기준 — 최초 접촉 각도 재명령

파지 순간의 최초 접촉 각도를 절대 바닥으로 한 번만 산출하고, 이후 재조임은 그 값을 다시 명령하는 방식으로 바꾸면 각도가 더 내려가지 않아요. 제어에서 상대 보정을 반복하면 누적된다는 건 알고 있어도, 로그의 숫자가 매번 조금씩만 움직이면 놓치기 쉬운 종류의 버그예요.

검증은 물체 좌표로

파지 판정 각도가 임계를 넘었다고 성공이라 부르면 위험해요. 죠 사이에 낀 채 끌려가는 상태와 제대로 물린 상태가 각도만으로는 구분되지 않거든요.

시뮬레이터에서는 물체의 실제 좌표를 조회할 수 있으니 이걸 최종 근거로 써요. 운반 중 물체의 z가 바닥 높이(0.015 m)에서 0.18 m까지 올라온 것을 확인하면 진짜로 들렸다는 뜻이에요.

최대 물체 z: 0.181662

실물에서는 이 진리값이 없기 때문에, 시뮬레이션 단계에서 각도 기준과 좌표 기준이 언제 어긋나는지 미리 파악해 두는 편이 안전해요. 각도가 임계를 넘었는데 물체 좌표는 그대로인 사례를 모아 두면, 실물에서 쓸 판정 임계를 어디에 둘지 정하는 근거가 돼요.