상세 컨텐츠

본문 제목

Hiking: 3주차 Jacobian IK 적용

Computer Graphics/Hiking

by Banjosh 2025. 4. 18. 02:13

본문

1. Model에 Static Rigidbody와 Mesh collider 적용

우선 IK를 위해 Target의 정보를 얻어와야한다. 이를 위해 Raycasting 기능을 활용하기 위해 Target이 될 수 있는 Model들에 대해 static rigidbody를 만들어주었고, 여기에 mesh 기반 collider를 적용하였다.

 

[구와 박스 모델 렌더링]

 

[Physix Visual Debugger에서 본 Model의 collider]

 

2. Raycasting으로 Target의 정보를 얻어오는 기능 구현

Target이 될 수 있는 Model들에 Collider를 적용한 후, physix의 raycasting 기능을 도입하였다. EndEffector에서 Ray를 발사하여 Target을 발견한 경우, 발견한 Target의 정보를 저장하는 기능을 구현하였다. 현재는 Foot IK를 위해 발 끝에서 Ray를 발사하게 하였으며, 디버깅을 위해 발사한 Ray가 target에 부딪힌 경로와 target의 normal 방향을 시각화하는 기능을 추가하였다.

 

아래는 디버깅을 위해 Raycasting을 시각화한 영상으로 초록색은 EndEffector 에서 발사한 Ray가 Target에 부딪힌 경로, 파란색은 Target의 Normal 벡터를 나타낸다.

 

 

 

3. Phase 기반 애니메이션 관리

IK를 적용하면 walk 애니메이션 진행시 발이 바닥에 닿는 순간과 발을 떼는 순간에 맞춰 애니메이션과 IK를 블렌딩하여 적용하여야한다. 이 타이밍을 계산하기 위해 walk 애니메이션 진행시 현재 진행되고 있는 walk 애니메이션의 위치를 알기 위해 walkPhase를 만들었다.

walkPhase = currentTime / walkDuration 으로 walk 애니메이션의 진행 정도를 나타낸다. 

walkPhase 가 0.0 ~ 0.5인 경우 왼발이 앞으로 나가는 경우이고, 0.5 ~ 1.0인 경우 오른발이 앞으로 나가는 경우이다.

후에 IK를 구현하면 이 타이밍을 고려하여 IK와 애니메이션의 blending 정도를 고려할 계획이다.

 

추가로 애니메이션 전환시 blending alpha를 walkPhase에 맞춰 변경하였다.

변경 이전

 - alphaBlending을 정해진 속도에 맞게 0.0 -> 1.0으로 변경하였다.

 - 모든 애니메이션은 시작시 0번 keyframe 부터 재생하였다.

 

변경 이후

 - alphaBlending을 walkPhase가 0.5 혹은 1.0이 되는 곳에서 1.0이 될 수 있게 속도를 조절하였다.

 - idle -> walk 전환시 기존의 walkPhase부터 애니메이션을 시작하여 자연스러운 느낌을 냈다.

 - walk -> idle시 alpha bleding이 0.5혹은 1.0이 되는 곳에서 1.0이 되기 떄문에 발을 땅에 디뎠을때 동작이 마무리된다.

 

변경사항을 통해 다음과 같은 효과가 나타났다.

- 왼발로 걷다 idle 상태가 되면 다음엔 오른발, 오른발로 걷다 idle 상태가 되면 다음엔 왼발로 걷는다.

- walk 도중에 idle 상태로 돌아가다가 다시 walk로 돌아가는 경우 현재 walkPhase의 walk애니메이션부터 진행되므로 자연스러운 모습이 연출된다.

- 나중에 Foot IK 적용시 발 위치 예측 및 궤적 생성을 진행해야하는데 walk 상태가 발을 디딜때까지는 자연스럽게 이어지므로 중간에 끊기는 경우를 고려하지 않아도 된다.

- IK와 애니메이션 블렌딩 비율 설정시에도 중간에 walk 가 종료하거나 처음부터 시작하는 경우를 고려하지 않아도 된다.

 

현재 다소 아쉬운 부분이 있는데 애니메이션이 전환된 타이밍의 walkPhase가 0.5나 1.0 직전인경우 alphaBlending이 0.0f에서 1.0f 까지 바로 변하기 때문에 자연스럽게 블랜딩이 이루어지지 않는다는 점이다. 이는 IK 적용 후 IK와 애니메이션 블랜딩 단계에 들어가서 수정할 계획이다. (혹은 다시 alphaBlending과 walkPhase를 분리할 예정)

 

아래는 walkPhase를 적용하기 전과 후 영상이다.

기존엔 walk 애니메이션 전환시 반드시 왼발부터 나가는 것과 걷는 도중 멈췄다 걸으면 다시 처음부터 walk 애니메이션이 재생되는 어색함이 있다.

 

walkPhase 적용 후에는 중간에 멈췄다가 다시 걸어도 동작이 자연스럽게 이어지는 느낌이 있다.

 

 

4. Jacobian Matirx를 이용한 Left Foot IK 기본 틀 구현

이번주 마지막 작업으로 Left Foot Chain을 만들어 Jacobian Matrix를 활용한 Foot IK의 기본 틀을 완성하였다.

우선 Jacobian Matrix로 IK를 적용하는 과정을 살펴보자.

 

 

1. Chain의  Target, EndEffector 계산

우선 Target에 대한 정보를 얻어야하기 때문에 Raycasting으로 Target을 찾아 정보를 가져온다.

이때 EndEffector보다 위에서 Ray를 쏘는 이유는  Target이 EndEffector보다 위에 있는 경우에 대응하기 위해서이다. 

 

2. Jacobian Matrix 계산

다음으로 Jacobian Matrix 즉 J를 구한다. 우선 등장인물 부터 확인해보자

- v(x, y) : EndEffector -> Target 벡터

- θ1 ~ θ4 : 각 관절의 각도

우리가 구해야할 것은 J에 있는 각 θ에 따른 v(x, y)의 변화량이다.

아래 그림을 통해 J1을 미세하게 회전시켰을때 v(x, y)가 어떻게 변하는지 볼 수 있고 이 방법으로 J를 구한다. 

 

여기서 Jacobian Matrix에 대해 자세히 알고 넘어가야한다.

우리가 Jacobian Matrix를 다룰때 미분을 쓰는데 이는 Jacobian Matrix가 미소영역에서 정의되기 때문이다.

Jacobian Matrix는 비선형 변환을 미소 영역으로 들어가 여러개의 선형 변환의 합으로 표현하는 Matrix이다.

예를들면 우리가 구하려는 v(x, y)를 미소영역으로 들어가 dv(x, y)를 이동하기 위해 dθ1, dθ2, dθ3, dθ4가 얼마나 움직이는지 구하는 행렬이다. 이때 J = f(θ1, θ2, θ3, θ4)로 비선형 변환이지만 비선형 변환을 확대하다보면 선형변환이 발견된다는 개념을 통해 θ1, θ2, θ3, θ4에의해 변화되는 v(x, y)의 선형변환을 4개 구하고 이를 합쳐 1개의 변환을 구하는 것이다.

각 선형변환은 사실 서로의 영향을 받기 때문에 거시적으로 들어가는 순간 4개의선형변환의 합은 비선형 변환이 될 수 없다.

따라서 서로의 영향을 받지 않을 수 있는 미소영역에서의 선형변환을 적용하여 합치는 거고 이렇게 만들어진 비선형 변환을 통해 dv(x, y)의 값을 구할 수 있다는게 Jacobian Matrix이다.

 

우리는 이렇게 Jacobian Matrix인 J를 구할 수 있다.

 

3. Jacobian Matrix 의 역행렬을 구해 Joint 별 각도 도출

우리는  v(x, y)를 알고 있기 때문에 J를 역행렬로 넘겨 아래와 같이 θ1, θ2, θ3, θ4를 구해야한다.

하지만 위 J는 n x n 의 정방행렬도 아니고 Full Rank를 보장할 수 없기에 역행렬이 존재하지 않는다. 

역행렬이 없으면 해가 없거나 무수히 많은데, 우리는 해가 없으면 가장 근사한 값을 구하고, 해가 무수히 많으면 그 중 1개를 골라주는 유사역행렬이란 것을 만들어 줄 것이다. 나는 이를 DLS라는 방법을 이용해 구현하였다.

 

DLS란 Damped Least-Square의 약자로 우선 Least-Square가 무엇인지 부터 살펴보자.

Least-Square란 여러 데이터 집합에서 가장 Best Fit을 찾는 방법 중 하나이다. 이는 식 유도과정을 보면 이해가 된다.

 

우선 이 식으로부터 시작해보자 우리가 지금까지 행렬로 썼던 식을 표현한 것이다. v는 EndEffector에서 Target까지의 vector, J는 Jacobian Matrix Theta는 모든 θ들의 벡터이다.

우리는 위 식의 해가 없을 수 있다는 가정하에 아래와 같이 오차 벡터 e를 추가하여 e가 최소가 되게하는 해(θ 변수들)를 찾을 것이다.

e의 값이 최소가 되는 것을 찾기 떄문에 이를 제곱을 하면 다음과 같은 식이 나온다.

여기서 우항을 θ의 2차 함수라 생각해보자. 2차함수는 미분한 값이 0일때 최솟값이므로( θ의 계수가 J * JT이므로 양수) θ에 대해 편미분을 진행한 뒤 이를 0으로 만드는 θ를 찾는 식을 만들어 보자.

우선 위 식을 전개하면 다음과 같다

그리고 이를 θ에 대해 편미분을 하면 다음과 같다

편미분의 결과가 0이 나온 경우 θ에 관한 식으로 만들면 결과는 다음과 같다.

오차 벡터 e가 최소가 되는 경우의 식이 다음과 같이 나오고 위 식은 우리가 만들고 싶던 모양이 된다.

 우리는 θ = J-1 * v를 구하고 싶었는데 J-1 자리에 (JTJ)-1 * JT 가 나타나게 되었고, 이게 우리가 찾으려던 유사역행렬 J+가 된다.

 

그 결과 다음는 다음과 같다.

 

사실 위 식은 그냥 LS고 DLS는 저기에 Dampping을 추가한 이 식이다.

감쇠를 추가한 이유는 JTJ가 역행렬이 존재하지 않는 경우를 대비해 대각 성분에 λ^2을 추가해줌으로써 역행렬을 확정시키는 것이다.(특이값 분해에 의해 JTJ의 고유값은 0이상이 확정, 고유값 분해를 통해  JTJ에  λ^2 * I를 추가하면 고유값이 0보다 큰 양수인게 확정

 

 

4. DLS로 구한 각도 업데이트

DLS를 통해 dθ1, dθ2, dθ3, dθ4를 구했으면 이를 통해 각도를 업데이트 해준다.

 

5. 반복 여부 확인

만약 업데이트한 각도에 의해 EndEffector가 Target에 접근하였다면 반복을 종료한다.

만약 최대 반복횟수에 도달했다면 그 경우에도 반복을 종료한다.

위 경우들이 아니라면 다시 위 과정을 반복한다.

 

아래 영상은 Left Foot IK를 적용한 것이다. 현재 관절의 제약조건을 적용하지 않아서 이상하지만 EndEffector가 Target에 잘 위치하게 되는것을 볼 수 있다. 

 

 

5. dθ 구하는 경우 Joint 마다 가중치를 두어 구하기

우리가 발을 땅에 붙이는 경우를 생각해보면 고관절과 무릎 관절의 각도를 통해 위치를 대략 잡고 발목과 발가락 등은 미세조정의 역할을 맡는다. 이와 같이 가중치를 두어서 dθ를 구하면 EndEffector가 Target을 찾아가는데 가중치가 높은 관절을 많이 움직이게 된다.

 

아직 관절 제약이 없지만, 위의 영상과 달리 고관절과 무릎 관절을 좀 더 쓰는 것을 볼 수 있다. (이전 영상에서는 발바닥이 아예 하늘을 봄)

 

 

 

6. 현재 진행중인 단계 - 후처리 방법으로 관절 제약 적용

 관절 제약을 추가하기 위해 가장 먼저 quaternion + Euler 방법을 사용해보았다.

가장 간단하게 생각한 방법으로 quaternion을 x, y, z 축 회전으로 분리한 후 IK 결과를 반영하여 clamping 한 후에 다시 quaternion으로 만들어 쓰는 방법으로 구현하였다.

 

 하지만 이 방법은 문제가 있었다. 만약 IK를 통해 현재 x축 회전 dθ를 구했다고 해보자. 그러면 위 방법대로 기존 quaternion을 x, y, z축 회전으로 분리하여 여기에 x축 회전인 dθ를 x축 회전에 합친 후 다시 quaternion을 만드는 경우 의도한 것과 다른 결과가 발생한다. 그 이유는 우리가 적용하려던 dθ의 회전은 앞서 기존 quaternion의 x -> y -> z회전 후 적용하려던 것인데 그냥 분해후 기존 x값에 dθ를 더해버리면 새로만든 quaternion에서 dθ를 처음 x축 회전에 적용해버린다. 이는 x -> y -> z -> x(dθ적용) 순서가 아닌 x (dθ 적용) -> y -> z 순서로 회전이 적용되게 되고 결과값이 달라지게 된다.

 

 그렇다면 x -> y -> z -> x(dθ 적용) 순서를 맞추기 위해 기존 quaternion에 x축으로 dθ만큼 회전시키는 quaternion을 새로 만들어 곱하는 것은 어떨까? 이러면 결과는 우리가 생각대로 적용이 되는것은 맞다. 하지만 다음 단계에서 이를 다시 분해할때 문제가 발생한다. 우리가 quaternion으로 만든것은 x -> y -> z -> x(dθ) 순서의 결과이고 그렇게 만들어지 quaternion 결과를 분해하는 경우 x -> y -> z 순서로 회전했다는 가정하에 euler 분해를 진행한다. 그 결과 우리가 생각한 x + dθ, y, z가 아닌 전혀 다른 각도들로 분해가 되고 이렇게 통제할 수 없는 euler 각들을 clamping하는 것은 불가능하다는 것을 깨달았다. 

 

 그렇게 다음 방법으로 현재 공부중인 것이 Twist-Swing 분해이다. 

Twist-Swing 분해란 Quaternion을 특정 축에 대한 회전과 나머지 회전으로 분리하는 것으로, 그 결과 축에 대한 회전 Twist quaternion과 나머지 회전인 Swing quaternion이 나오게 된다. euler회전은 순서 기반이기 때문에 이를 반영하지 못하는 quaternion에서는 오류가 발생할 수 밖에 없었는데 Twist-Swing 분해는 위치 기반이기 때문에 순서에 대한 오류가 발생하지 않는다. 즉 현재 쿼터니언을 분해해서 새로운 쿼터니언을 만들고, 다음에 또 이를 분해하여 사용해도 문제가 없는 것이다.

 

7. 추가적으로 고려할 만한 것들

- DLS에 SVD 추가

 SVD란 특이값 분해를 말한다. 특이값이란 특정 방향의 데이터가 결과에 얼마나 영향을 주는지 나타내는 값이다. 특이값이 0인 경우 해가 무수히 많거나 없는 경우이고, 이게 뜻하는 것은 해당 방향으로의 변환은 결과에 아무런 영향을 주지 않는다는 것이다. 현재는 DLS에서 감쇠를 추가하여 특이값이 0인 경우에 값이 발산하는 것을 막고 있는데, 이 방법은 특이값이 0인 경우 뿐만 아니라 모든 경우에 들어가므로 좋은 방법은 아니다. 

 DLS에 SVD를 적용하게 되면 특이값이 0에 가까울 수록 값을 0에 수렴하여 기존 각도에서 변화를 안하게 만들 수 있고(결과에 영향을 주지 않는 관절은 최대한 움직임을 줄인다), 특이값이 클수록 감쇠를 최소한으로 하여 원하는 만큼 변화를 적용할 수 있게 만들어 준다. SVD를 적용한 유사역행렬은 다음과 같다. 

σ 는 특이값을 나타내고 기존의 SVD의 역행렬은 1 / σ 를 곱해주는데 그러면 특이값이 0인 경우 발산하므로 위와 같이 σ가 클 수록 1에 가깝고, σ가 0에 가까울 수록 실제로 0에 가깝게 만들어주는 로직을 통해 감쇠를 적용하고 있다.

 

- null-space projection

DLS-SVD는 특이값이 0인 θ에 대한 움직임을 최소화하는 방법이라면 null-space projection은 특이값이 0인 null-space의 값은 결과에 영향을 주지 않으므로 원하는 값으로 커스텀하겠다는 방법이다. 이는 원하는 자세가 있을때 특이값이 0인 부분을 조절하여 보정하고 싶을때 사용하면 좋을 방법이다.

 

8. 앞으로 해야할 일

1. 관절 제한

2. target normal에 수직되게 발고정

3. walk phase에 맞게 IK와 animation blending

4. 오른쪽 발에도 IK 적용 

5. 골반 위치 보정

6. 다음 발 위치 및 궤적 예측

 

 

관련글 더보기