-->
Phase 4-1에서 그리퍼+MoveIt2를 붙여 open→pre_reach→reach_target까지는 완전히 성공했는데, 마지막 close(붙잡기) 단계에서 원기둥이 중심을 못 잡고 옆으로 빠져나가는 문제가 남아있었다. IK나 오프셋 문제가 아니라 "완전 대칭 position-control 그리퍼가 둥근 표면을 잡을 때 두 손가락이 완벽히 동시에 닿기 어렵다"는 물리적 한계였다. 오늘은 이 문제를 정면 돌파(force/compliance control)하지 않고, 물체 형태를 바꿔서 우회하는 방향으로 진행했다.
① 왜 모양만 바꾸는 걸로는 부족한가
처음엔 "원기둥 → 박스"로 SDF의 geometry 태그만 바꾸면 될 거라 생각했는데, 그렇게 간단하지 않았다.
이 팔은 관절 2개(어깨/팔꿈치)가 전부 Z축 회전인 2자유도 평면팔이라, 방향(orientation)을 따로 제어할 수 없는 position-only IK를 쓴다. 그 말은 그리퍼가 최종적으로 향하는 각도가 shoulder + elbow 합으로 자동 결정된다는 뜻 — 손가락이 열고 닫히는 축(폐쇄축)도 이 각도에 그대로 종속된다.
Phase 4-1 최종 reach_target(shoulder=-0.4081, elbow=1.9018) 기준으로 폐쇄축 각도를 구하면:
접근축 각도 = -0.4081 + 1.9018 = 1.4937 rad (≈85.6°)
폐쇄축 각도 = 1.4937 + π/2 = 3.0645 rad (≈175.6°)박스를 아무 각도로나 놔두면 손가락이 모서리나 꼭짓점을 때리게 되고, 그건 원기둥이랑 별 차이가 없다. 손가락이 정말 "면"으로 닿으려면 박스의 평평한 면이 폐쇄축과 정면으로 마주보게 미리 돌려놔야 한다.
② yaw 계산 + 뜻밖의 대칭
정사각 단면(0.06m × 0.06m) 박스로 정하고, 폐쇄축(3.0645rad)에 한쪽 면이 정렬되도록 yaw를 계산했다.
yaw = 폐쇄축 각도 mod (π/2), 최소 절댓값으로 정규화
= 3.0645 - π
= -0.0771 rad (약 -4.4°)여기서 재미있는 우연이 하나 있었다. 이 팔은 접근축과 폐쇄축이 항상 서로 수직이기 때문에, 정사각 단면 박스를 폐쇄축에 맞춰 정렬하면 접근축 방향 반너비도 자동으로 폐쇄축 방향 반너비와 똑같이 0.03m가 된다. 즉 이전 원기둥의 반지름(0.03m, 모든 방향에서 동일)과 새 박스의 두 방향 반너비가 정확히 일치한다.
그 덕분에 hand_link 구 충돌 여유 계산(pre_reach/reach_target IK 값)이나 그리퍼 open(0.05m)/closed(0.032m) 값을 하나도 다시 계산할 필요가 없었다 — worlds/room.world의 geometry 태그와 pose의 yaw 값, 그리고 관성값(inertia, 원기둥→박스 공식이 달라서 재계산)만 바꾸면 끝나는 수정이었다.
③ 실행 검증 — 이번엔 진짜 잡혔다
WSL2에서 moveit_demo.launch.py를 재실행해서 박스가 받침대 위에 올바른 위치·각도로 뜨는 걸 확인한 뒤, 순서대로 실행했다.
ros2 run fk_2link_demo gripper_demo open
ros2 run fk_2link_demo gripper_demo reach # pre_reach → reach_target
ros2 run fk_2link_demo gripper_demo close
원기둥 때 미끄러져 빠지던 지점에서, 이번엔 박스가 중심을 잃지 않고 안정적으로 집혔다.
여기서 그냥 넘어가기 아쉬워서 home까지 추가로 보내봤다 — 팔을 완전히 편 자세로 되돌리는 명령인데, close 상태를 그대로 유지한 채였다.
ros2 run fk_2link_demo gripper_demo home
결과는 기대 이상이었다. 박스가 그리퍼에서 떨어지지 않고 hand_link에 붙은 채로 home 위치까지 함께 이동했다. 원래 있던 받침대 자리는 빈 채로 남았다.


close 직후의 정적인 상태뿐 아니라 팔이 크게 움직이는 동안에도 그립이 유지된다는 걸 확인했다는 점에서, close 스크린샷 한 장보다 훨씬 설득력 있는 검증이었다. force/compliance control을 도입하지 않고 물체 형태 교체만으로 Phase 4-1의 그립 불안정 문제를 해결한 셈이다.
④ (보너스) RViz Plan/Execute랑 gripper_demo.py는 뭐가 다른가
이번에 검증하면서 궁금해진 게 있었다. Phase 4-1까지는 RViz의 MoveIt2 패널에서 매번 Plan 누르고 확인하고 Execute 누르는 식으로 힘들게 움직였는데, 이번엔 ros2 run fk_2link_demo gripper_demo open 한 줄로 끝났다. 원리를 정리해봤다.
RViz Plan/Execute는 move_group이 OMPL로 충돌 검사를 하면서 현재 자세→목표 자세까지 여러 waypoint로 된 경로를 매번 새로 계산(Plan)한 뒤, 그 결과를 컨트롤러의 FollowJointTrajectory 액션으로 보낸다(Execute). 반면 gripper_demo.py는 이 계획 단계를 통째로 건너뛰고, 점(waypoint) 하나짜리 trajectory("이 관절값으로 N초 안에 가라")를 컨트롤러 액션에 바로 쏜다. 목표 관절값도 그때그때 계산하는 게 아니라 이전 세션에 IK로 미리 구해서 코드 상수(REACH_TARGET_SHOULDER 등)로 박아둔 값이다. 그래서 planning 시간 없이 CLI 한 줄로 재현 가능한 것.
여기서 하나 걸리는 부분을 발견했다. OMPL은 원래 "충돌 객체로 등록된" 장애물만 피해간다 — 그런데 target_bottle은 Gazebo world 파일에만 있는 모델이고, MoveIt2 planning scene에 충돌 객체로 등록돼 있지 않다. OMPL 입장에선 이 물체가 아예 존재하지 않는 셈이라, 경로가 그 위를 그냥 통과해버릴 수 있다. 실제로 Phase 4-1에서 reach_target으로 한 번에 보냈다가 팔이 물체를 스치고 지나간 게 이 문제였고, 그래서 pre_reach라는 중간 지점을 사람이 손으로 만들어 우회시킨 거였다. "장애물을 알아서 피해서 최적 경로를 계산해준다"는 건 planning scene에 등록된 물체에 한해서만 성립하는 이야기였다 — 이건 다음에 제대로 하려면 target_bottle을 충돌 객체로도 등록해야 하는, 아직 안 끝난 숙제로 남겨둔다.
다음 편 예고
각진 물체 + yaw 정렬로 그립 안정성 문제는 해결됐다. 다만 Phase 4-1부터 남아있는 물리적 한계는 그대로다 — 관절 2개가 전부 Z축 회전이라 팔이 항상 같은 높이에서 수평 이동만 가능하고, 위로 "들어 올리는" 진짜 pick-and-place는 여전히 못 한다. 관절을 추가해서 이 한계를 정면 돌파할지, 아니면 이 상태를 인정하고 로드맵대로 Phase 5(이동/SLAM/Nav2)로 넘어갈지 다음 세션에서 결정할 예정이다.
'로봇 > ROS2 공부' 카테고리의 다른 글
| [ROS2 공부 Phase 4-1] 그리퍼 달고 MoveIt2로 물체 잡으러 가기 (버그 사냥기) (0) | 2026.09.02 |
|---|---|
| [ROS2 공부 Phase 3-1, 3-2] 손목 카메라 달고 YOLO로 물체 찾아보기 (0) | 2026.08.29 |
| [ROS2 공부 Phase 2-2] 방 world 만들고, RViz2+Gazebo 동시 확인하고, 계속 움직여보기 (0) | 2026.08.29 |
| [ROS2 공부 Phase 2-1] Gazebo 시뮬레이션 첫걸음 — 용어 정리 + 삽질 기록 (0) | 2026.08.25 |
| [ROS2 공부 Phase 1] 토픽(Topic) 통신으로 로봇 팔 자세 뿌려보기 (0) | 2026.08.25 |








