모션 캡처 BVH 발 미끄러짐(Foot Sliding) AI 자동 클린업 툴 구현 가이드
모션 캡처 BVH에서 발생하는 지면 발 미끄러짐을 AI로 자동 탐지하고 고치는 클린업 파이프라인을 소개합니다. 접촉 판정, 발 위치 고정, IK 보정, 루트 보정, 품질 검증과 Unity 연동까지 실전 구현 기준으로 정리합니다.

핵심 요약
모션 캡처 BVH의 Foot Sliding은 발이 지면에 닿아 있어야 하는 구간에서 발의 수평 속도가 남아 발생합니다. BVH의 계층과 프레임 채널은 관절 움직임을 나타내지만 접촉 여부 같은 의미 라벨을 직접 담지는 않으므로, AI나 규칙으로 접촉을 추정한 뒤 접촉 구간별 발 위치 고정과 IK 보정을 적용해 수작업을 줄이는 파이프라인을 구성할 수 있습니다.
이 툴이 해결하는 문제는 무엇인가?
BVH는 관절 계층과 프레임별 모션 채널을 저장하지만 특정 프레임에서 발이 지면에 닿았는지 나타내는 의미 라벨은 보통 포함하지 않습니다. 따라서 리타게팅 과정에서 발이 지면에 닿은 뒤에도 수평으로 움직이는 Foot Sliding이 발생할 수 있습니다.
자동 클린업 툴은 다음 순서로 문제를 처리합니다.
- BVH를 파싱하고 월드 좌표의 발 궤적을 계산합니다.
- 발 높이, 수평 속도, 발목 회전, 무릎 상태를 이용해 접촉 확률을 예측합니다.
- 접촉 구간의 발 위치를 고정하고 다리 체인에 IK를 적용합니다.
- 접촉 시작과 종료 지점에서 보정값을 블렌딩해 무릎 팝과 급격한 포즈 변화를 줄입니다.

설명용 합성 이미지이며 실제 BVH 측정 결과나 실행 화면이 아닙니다.
Foot Sliding은 왜 발생하는가?
| 원인 | 관찰되는 현상 | 자동 클린업 대응 |
|---|---|---|
| 센서 노이즈 | 발목 위치가 미세하게 흔들림 | 저역 통과 필터와 구간 단위 앵커링 |
| 리타게팅 비율 차이 | 원본과 대상 캐릭터의 다리 길이가 다름 | 대상 리그 기준으로 접촉 위치 재계산 |
| 루트 이동 오차 | 발이 고정되어야 할 때 캐릭터 전체가 밀림 | 루트 수평 이동량을 제한적으로 보정 |
| 접촉 이벤트 불일치 | 발이 닿기 전이나 뗀 뒤에도 고정됨 | 접촉 시작·종료 히스테리시스 적용 |
| 경사면과 계단 | 평평한 지면 기준 보정으로 발이 관통함 | 지면 평면 또는 충돌 메시 기준 보정 |
핵심은 발 위치를 무조건 고정하는 것이 아닙니다. 먼저 발이 실제로 지면에 닿았는지 판정해야 합니다. 점프 중인 발이나 발끝만 닿는 동작까지 고정하면 오히려 애니메이션이 부자연스러워집니다.
AI 자동 클린업은 어떤 원리로 작동하는가?
실전에서는 AI가 모든 보정을 직접 생성하도록 만들기보다 AI 분류기와 결정론적 IK 솔버를 결합하는 방식이 관리하기 쉽습니다. 모델은 접촉 여부를 예측하고 보정기는 발 위치를 만족하는 관절 회전을 계산합니다.
| 단계 | 입력 | 출력 | 권장 구현 |
|---|---|---|---|
| 특징 추출 | 발 위치와 속도, 발목 회전, 무릎 높이 | 프레임 특징 벡터 | Python 3.11, NumPy |
| 접촉 예측 | 현재 프레임 전후 10프레임의 특징 | 왼발·오른발 접촉 확률 | 작은 MLP 또는 1D Temporal CNN |
| 구간 생성 | 접촉 확률 시계열 | 접촉 시작·종료 프레임 | 히스테리시스와 최소 구간 길이 |
| 포즈 보정 | 접촉 구간과 리그 체인 | 수정된 회전 채널 | Damped Least Squares IK |
| 검증 | 수정 전후 궤적 | 품질 지표와 경고 | 수평 속도·지면 관통 검사 |
접촉 확률은 다음처럼 표현할 수 있습니다.
여기서 x는 발 높이, 발의 수평 속도, 수직 속도, 발목 피치와 롤, 무릎 높이, 루트 속도를 포함하는 특징 벡터입니다. 60fps 데이터라면 k=10으로 설정해 총 21프레임의 양방향 문맥을 사용할 수 있습니다. 이 설정은 현재 프레임 뒤의 10프레임도 참조하므로 오프라인 처리나 10프레임 지연이 가능한 경우에 해당합니다. 지연 없는 런타임 처리가 필요하면 과거 프레임만 쓰는 인과적 모델로 바꾸거나 접촉 확률을 미리 계산해야 합니다.
flowchart TD
A[BVH 파일 입력] --> B[월드 좌표 변환]
B --> C[발 궤적과 특징 추출]
C --> D[AI 접촉 확률 예측]
D --> E[접촉 구간 생성]
E --> F[발 앵커 위치 계산]
F --> G[IK와 루트 보정]
G --> H[BVH 재저장 및 품질 리포트]
접촉 구간은 어떻게 안정적으로 생성할까?
모델 출력만 바로 이진화하면 프레임 경계에서 접촉 상태가 반복적으로 뒤집힐 수 있습니다. 다음 규칙을 함께 적용하는 것이 좋습니다.
- 접촉 시작 임계값은
P(contact) >= 0.7로 설정합니다. - 접촉 종료 임계값은
P(contact) <= 0.3으로 설정합니다. - 최소 접촉 길이는 5프레임으로 제한합니다.
- 2~3프레임의 짧은 틈은 같은 접촉 구간으로 병합합니다.
- 발 높이는 캐릭터 스케일에 맞춰 정규화합니다.
아래 수치는 시작 예시일 뿐 보편적인 기준은 아닙니다. 캐릭터 크기와 프레임레이트, 동작 데이터에 맞춰 보정해야 합니다. 이 구조를 사용하면 AI의 작은 예측 오차가 최종 BVH의 깜빡임으로 확대되는 것을 줄일 수 있습니다.
BVH 발 접촉 탐지와 IK 보정은 어떻게 구현할까?
1단계: BVH를 월드 좌표로 변환한다
BVH의 회전 순서와 채널 순서를 먼저 정확하게 해석해야 합니다. ROOT에 이동 채널이 있으면 캐릭터 전체의 위치를 결정하고 자식 관절의 회전 채널은 부모 좌표계 기준으로 누적됩니다. 발 위치는 다음과 같은 계층 변환으로 계산합니다.
프레임마다 발목이나 발끝 관절의 월드 위치를 저장하고 프로젝트의 단위가 미터인지 센티미터인지 확인합니다. 단위가 섞이면 높이 임계값과 속도 임계값이 모두 잘못 작동합니다. 아래 규칙 기반 코드는 foot_position이 Y-up, 미터 단위의 월드 좌표이고 floor_y도 같은 좌표계의 높이라는 전제입니다. BVH의 원본 축이 다르면 먼저 축 변환과 단위 변환을 적용하세요.
import numpy as np
def estimate_velocity(position, fps):
position = np.asarray(position, dtype=float)
if len(position) < 2:
raise ValueError('at least two position samples are required')
velocity = np.zeros_like(position, dtype=float)
velocity[1:] = (position[1:] - position[:-1]) * fps
velocity[0] = (position[1] - position[0]) * fps
return velocity
def detect_rule_contact(foot_position, floor_y, fps=60.0):
foot_position = np.asarray(foot_position, dtype=float)
velocity = estimate_velocity(foot_position, fps)
horizontal_speed = np.linalg.norm(velocity[:, [0, 2]], axis=1)
height = foot_position[:, 1] - floor_y
near_floor = height >= -0.02
near_floor &= height <= 0.04
low_speed = horizontal_speed <= 0.08
return near_floor & low_speed
위 코드는 학습 데이터의 초기 라벨을 만들기 위한 규칙 기반 기준선입니다. 수동으로 정제한 접촉 라벨을 추가해 작은 분류 모델을 학습하면 발끝 끌기, 쪼그려 앉기, 빠른 보행처럼 단순 임계값만으로 구분하기 어려운 동작을 개선할 수 있습니다.
2단계: AI 모델로 접촉 확률을 예측한다
학습 데이터에는 최소한 다음 정보를 포함하는 것이 좋습니다.
| 데이터 | 설명 |
|---|---|
| 원본 BVH | 다양한 속도와 동작의 모션 캡처 |
| 접촉 라벨 | 왼발·오른발의 접촉 여부와 시작·종료 프레임 |
| 리그 정보 | 관절 계층, 오프셋, 회전 순서, 캐릭터 스케일 |
| 지면 정보 | 평면 지면, 경사면, 계단 등의 조건 |
출력은 이진값보다 연속적인 접촉 확률이 유용합니다. 확률을 사용하면 불확실한 구간을 자동으로 보류하거나 품질 리포트에서 검수 대상으로 표시할 수 있습니다. 예를 들어 0.45~0.65 구간은 자동 수정하지 않고 아티스트 확인 목록으로 보낼 수 있습니다.
3단계: 접촉 구간의 발 위치를 고정한다
접촉 구간의 앵커 위치는 첫 프레임 하나만 사용하는 것보다 안정된 프레임들의 중앙값을 이용하는 편이 노이즈에 강합니다.
평평한 지면에서는 앵커의 높이를 지면 높이로 설정하고 수평 좌표를 유지합니다. 경사면에서는 지면을 다음 평면으로 표현할 수 있습니다.
법선 벡터 n이 정규화되어 있다면 발 위치를 지면으로 투영하는 식은 다음과 같습니다.
이후 발목과 무릎으로 구성된 다리 체인에 IK를 적용합니다. Damped Least Squares 방식은 자코비안 특이점 근처에서 역행렬 문제를 완화하는 데 쓰이지만, 감쇠 계수와 관절 제한을 포함한 구현에 따라 결과가 달라집니다.
J는 다리 체인의 자코비안이고 e는 현재 발 위치와 목표 앵커 위치의 오차입니다. λ는 감쇠 계수이며 값이 클수록 움직임이 부드러워지지만 목표 위치 오차가 커질 수 있습니다.
4단계: 접촉 경계에서 보정값을 블렌딩한다
접촉 구간 전체를 갑자기 고정하면 발이 한 프레임에서 튀거나 무릎이 꺾일 수 있습니다. 구간 시작과 종료 지점에서 보정 가중치를 Smoothstep으로 보간합니다.
실제 적용 시에는 발 위치뿐 아니라 IK 회전 보정량에도 같은 가중치를 곱합니다. 루트 보정은 다리 길이와 상체 움직임을 훼손할 수 있으므로 기본값을 낮게 두고 두 발이 동시에 접촉한 구간에서만 제한적으로 활성화하는 것이 안전합니다.
for each contact segment [a, b) where b is exclusive:
anchor = median(foot_position[a:b], axis=0)
anchor[1] = floor_height
for frame in range(a, b):
weight = smoothstep_at_boundary(frame, a, b - 1)
target = blend(original_foot_position[frame], anchor, weight)
solve_damped_least_squares_ik(target)
Unity에서 클린업 결과를 어떻게 사용할까?
오프라인으로 수정한 BVH를 다시 내보내는 방식이 가장 재현성이 높습니다. 런타임에만 보정해야 한다면 Unity Animator.OnAnimatorIK에서 접촉 확률을 가중치로 사용할 수 있습니다. 이 콜백과 SetIKPosition은 Humanoid Avatar를 사용하고 Animator Controller 레이어의 IK Pass를 켠 경우에만 동작합니다. 스크립트는 Animator가 붙은 GameObject에 연결하고 Animator가 켜져 애니메이션을 재생 중이어야 합니다. 중심 문맥 모델의 접촉 확률을 쓴다면 앞서 설명한 프레임 지연 또는 사전 계산 조건도 적용됩니다.
void OnAnimatorIK(int layerIndex)
{
if (!cleanupEnabled) return;
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, leftFootWeight);
animator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootTarget);
animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, rightFootWeight);
animator.SetIKPosition(AvatarIKGoal.RightFoot, rightFootTarget);
}
leftFootTarget과 rightFootTarget은 Unity 월드 좌표여야 하며, 이 코드는 런타임 포즈만 보정하고 BVH 파일을 다시 쓰지는 않습니다.
런타임 방식은 원본 애니메이션을 보존한다는 장점이 있지만 최종 BVH나 FBX에 보정된 회전을 기록하지는 않습니다. 게임 내 재생 품질이 목적이면 런타임 IK를 사용하고 시네마틱이나 외부 DCC 납품이 목적이면 오프라인 베이크 결과를 저장해야 합니다.

화면의 그래프와 검증 상태는 설명용 합성 예시이며 실제 측정 결과가 아닙니다.
자동 보정 결과는 어떤 지표로 검증할까?
시각적 확인만으로는 짧은 미끄러짐이나 지면 관통을 놓치기 쉽습니다. 접촉 구간을 기준으로 다음 지표를 자동 계산하면 품질 검수가 빨라집니다.
| 지표 | 의미 | 예시 경고 기준 |
|---|---|---|
| 접촉 중 평균 수평 속도 | 발이 고정된 정도 | 0.03m/s 초과 |
| 접촉 중 누적 미끄러짐 | 시작점 대비 발 이동량 | 0.05m 초과 |
| 지면 관통 깊이 | 발바닥이 지면 아래로 들어간 정도 | 0.02m 초과 |
| IK 회전량 | 원본 포즈와 보정 포즈의 차이 | 관절별 제한 초과 |
| 무릎 위치 변화량 | 무릎 팝과 과도한 굽힘 확인 | 캐릭터별 기준 초과 |
접촉 중 평균 수평 속도는 다음과 같이 계산할 수 있습니다.
자동화된 품질 리포트에는 파일명, 접촉 구간, 왼발·오른발별 지표, 낮은 신뢰도 구간, IK 제한 초과 여부를 함께 기록하는 것이 좋습니다. 이렇게 하면 아티스트는 전체 애니메이션을 다시 재생하지 않고 문제가 있는 프레임만 검수할 수 있습니다.
AI 자동 클린업의 실패를 줄이는 방법은 무엇인가?
다음 동작은 단순한 평면 지면 모델에서 오판이 자주 발생합니다.
- 점프와 착지: 발 높이가 낮아지는 순간을 접촉으로 잘못 판정할 수 있습니다.
- 발끝 접촉: 발바닥 전체가 아니라 발끝만 닿는 동작을 고정하면 발목이 부자연스러워집니다.
- 계단과 경사면: 하나의
floor_y만 사용하면 발이 지면에 관통합니다. - 웅크리기: 무릎 높이와 발목 속도가 평소 보행과 달라 별도 데이터가 필요합니다.
- 빠른 회전: 발이 실제로 회전하며 이동하는 동작을 미끄러짐으로 오인할 수 있습니다.
이를 방지하려면 모델의 예측 확률뿐 아니라 리그 제약과 지면 충돌 결과를 함께 사용해야 합니다. 또한 자동 수정 전후의 영상을 나란히 보여주고 신뢰도가 낮은 구간은 자동 승인하지 않는 검수 단계를 남겨두는 편이 안전합니다.
자주 묻는 질문
AI만 사용하면 IK 솔버가 필요 없나요?
대부분의 경우 필요합니다. AI는 접촉 상태와 목표 발 위치를 예측하는 데 적합하고 관절 회전과 다리 길이 제약을 만족시키는 작업은 IK 솔버가 더 안정적으로 처리합니다.
발 높이와 속도 임계값은 고정해도 되나요?
캐릭터 스케일과 프레임레이트에 따라 달라집니다. 미터 단위로 정규화하고 캐릭터 발 길이 또는 신장에 비례해 임계값을 계산하는 것이 좋습니다.
BVH를 수정한 뒤에도 원본 프레임 수를 유지해야 하나요?
기존 게임 파이프라인과 동기화해야 한다면 프레임 수와 프레임 시간은 유지하는 것이 안전합니다. 보간이나 리샘플링이 필요할 때는 먼저 클린업하고 마지막 단계에서 수행해야 접촉 구간이 다시 흔들리는 것을 줄일 수 있습니다.
정리
BVH Foot Sliding 자동화의 핵심은 AI가 모든 애니메이션을 새로 생성하는 데 있지 않습니다. 발 접촉 확률을 정확히 분류하고 접촉 구간의 발 앵커를 계산한 뒤 Damped Least Squares IK와 경계 블렌딩으로 최소한의 보정을 적용하는 하이브리드 구조가 실용적입니다.
최종 툴에는 접촉 확률, 수정된 발 궤적, IK 회전량, 품질 지표를 함께 표시하는 리포트를 포함하세요. 그러면 자동화 속도와 아티스트의 통제력을 동시에 확보할 수 있습니다.



