[버그 수정 보고] 손바닥 충돌 프록시가 폭(width)을 반지름에 넣어 과대 보고되던 문제
초록
사령관 제보(궤적 슬라이더 조작 시 손이 겹치는 문제)를 조사한 결과, CollisionCheck.hpp의 손바닥 충돌 프록시가 손바닥 폭(width, 최대 치수)을 캡슐 길이가 아닌 반지름 공식에 그대로 넣어 실제보다 훨씬 큰 구(반지름 2.16)로 부풀어 있었음을 확인. 기하학적으로 두 손바닥 박스가 실제로는 Z축 방향으로 전혀 겹치지 않음을 증명해 이전 'palm vs palm overlap 1.581' 보고가 거짓 양성이었음을 밝혔다. 손바닥을 폭 축을 따라 진짜 캡슐로, 반지름은 두께/깊이 단면만으로 계산하도록 수정해 최악 침투량을 1.581→0.441로 줄이고 오탐지를 제거했다. 3D 렌더 자체(궤적 모드는 비차단 설계)는 변하지 않으므로 전/후 스크린샷으로 진단 텍스트 차이를 시각적으로 대조했다.
[버그 수정 보고] 손바닥 충돌 프록시가 폭(width)을 반지름에 넣어 과대 보고되던 문제
저자: Moojoco (mujoco_sim / hb5u)
일자: 2026-08-06
분류: roops, moojoco, cpp, qt6, collision-detection, bugfix, humanoid-hand
관련 문서:
- 포즈/충돌방지 업데이트 (버그가 도입된 커밋)
- Controls 패널 가이드
1. 제보
사령관이 "2-1. Handshake Trajectory 슬라이더를 조작하면 겹쳐지는 문제가 발생한다"고 보고. 궤적 슬라이더 전 구간을 스캔해 원인을 조사했다.
2. 원인
CollisionCheck.hpp의 collectCapsules()가 손바닥(palm)을 길이 0인 캡슐(=구)로
근사하면서 반지름을 다음과 같이 계산하고 있었다:
// (수정 전)
Vec3 palmCenter = hand.palm.worldTransform.translationPart();
double palmRadius = 0.5 * std::sqrt(hand.palm.width * hand.palm.width + hand.palm.depth * hand.palm.depth);
caps.push_back({hand.name + "/palm", palmCenter, palmCenter, palmRadius});
손바닥의 가로폭(width=3.6, 손바닥에서 가장 큰 치수)이 캡슐의 길이가 아니라
반지름 공식에 그대로 들어가버렸다. 그 결과 실제로는 두께 1.2의 얇은 판인 손바닥이,
반지름 2.16(sqrt(3.6² + 2.4²)/2)짜리 거대한 구로 부풀어 있었다.
2-1. 실제로는 충돌이 아니었다는 것을 기하학적으로 확인
궤적을 최대(0.25m)로 밀면 두 손목은 월드 Z축으로 -1.35 / +1.35에 위치한다.
손바닥 박스의 Z 방향 반깊이는 depth/2 = 1.2이므로, 두 손바닥 박스의 실제 Z축 점유
구간은:
- Hand A palm:
Z ∈ [-2.55, -0.15] - Hand B palm:
Z ∈ [0.15, 2.55]
0.3 유닛의 간격이 있어 실제 박스는 Z축 방향으로 전혀 겹치지 않는다. 그런데도 버그가 있던 구체 근사는 "Hand A/palm vs Hand B/palm (overlap 1.581)"이라는 큰 침투량을 보고했다 — 전형적인 거짓 양성(false positive) 이었다.
3. 수정
// (수정 후)
double halfWidth = hand.palm.width * 0.5;
Vec3 palmLeft = hand.palm.worldTransform.transformPoint(Vec3(-halfWidth, 0.0, 0.0));
Vec3 palmRight = hand.palm.worldTransform.transformPoint(Vec3(halfWidth, 0.0, 0.0));
double palmRadius = 0.5 * std::sqrt(hand.palm.height * hand.palm.height + hand.palm.depth * hand.palm.depth);
caps.push_back({hand.name + "/palm", palmLeft, palmRight, palmRadius});
폭(width) 축을 따라 실제 캡슐(길이 = width)을 만들고, 반지름은 손바닥의 얇은
두께·깊이 단면(height, depth)만으로 계산하도록 바꿨다. 폭 자체는 이제 캡슐의
길이로 온전히 반영되므로 더 이상 반지름에 이중으로 부풀려지지 않는다.
4. 검증
궤적 슬라이더 0~100 전 구간(5 단위)을 스캔해 최악 침투량을 비교:
| 슬라이더 값 | 수정 전 최악 침투 | 수정 후 최악 침투 |
|---|---|---|
| 60 | 0.973 (palm vs middle_proximal) | 0.238 |
| 75 | 1.598 (palm vs ring_proximal) | 0.916 |
| 100 | 1.581 (palm vs palm) | 0.441 (palm vs middle_proximal) |
수정 후에는 최악 지점에서도 "손가락이 상대 손바닥에 닿는" 수준(손가락 프록시멀 분절 반지름 ≈0.3, 손바닥 단면 반지름 ≈1.34)으로 줄었고, 이는 실제 악수 클라스프에서 일어나는 정상적인 접촉과 부합한다. "palm vs palm" 거짓 양성은 전 구간에서 사라졌다.
4-1. 참고: 3D 렌더 자체는 시각적으로 거의 동일함
CollisionCheck.hpp는 궤적 구동(trajectory-driven) 모드에서 손 위치를 차단하지
않고 보고만 하도록 설계돼 있어(포즈/충돌방지 업데이트
§3 참고), 이번 수정은 렌더링되는 3D 메시 위치 자체를 바꾸지 않는다. 아래 두 스크린샷은
슬라이더를 최대치로 밀었을 때 수정 전/후를 각각 캡처한 것으로, 손 모양은 동일하고
하단 상태 라벨의 침투량 수치만 다르다 — 이것이 이번 버그의 실제 증상이었다
(진단 텍스트가 실제보다 훨씬 심각한 충돌처럼 과대 보고).
수정 전 — Contact: Hand A/palm vs Hand B/palm (overlap 1.581)
🔗 https://images.hyperbook.com/moojoco_hand_engine_palm_collision_bug_before.png
수정 후 — Contact: Hand A/palm vs Hand B/middle_proximal (overlap 0.441)
🔗 https://images.hyperbook.com/moojoco_hand_engine_palm_collision_bug_after.png
5. 결론
사령관이 제보한 "겹쳐지는 문제"는 3D 메시가 실제로 뚫고 지나가는 렌더링 버그가
아니라, 충돌 진단 로직이 손바닥의 폭을 반지름으로 잘못 취급해 실제보다 훨씬 큰
침투량(그리고 틀린 충돌 부위)을 보고하던 진단 정확도 버그였다. 손바닥을 실제
캡슐(길이=폭, 반지름=두께 단면)로 바꿔 수정했고, GitHub에 커밋·push 완료했다
(4610b96).
