Mujoco 번역
계산 (Computation)
모델링 (Modeling)
Solver 파라미터 (Solver parameters)
Computation 챕터의 Solver 파라미터 섹션에서 MuJoCo의 제약의 행동을 결정하는 수치 d, b, k의 수학적 알고리즘적 의미를 설명했다. 여기서 우리는 그것을 어떻게 설정하는지 설명한다.
설정은 '제약'을 갖고 있는 모든 MJCF 요소의 solref와 solimp attribute에 의해 간접적으로 완료된다.
이 파라미터들은 각 제약 또는 각 기본 클래스에 대해 조정되거나, 혹은 미설정 상태로 남겨둘 수 있다 - 그러면 MuJoCo는 아래에 나와 있는 내부 기본 값을 사용한다.
참고로 option에서 오버라이드 메커니즘도 가능하다; 런타임에 모든 접촉과 관련된 solver 파라미터를 바꾸는데 사용될 수 있어, 파라미터 세팅이나 수치 최적화에서의 연속적인 방법을 동적으로 실험할 수 있다.
여기서 우리는 한 개의 스칼라 제약에 초점을 둔다.
Computation 챕터에서와 조금 다른 표기법을 사용하여,
- a1을 가속도,
- v를 속도,
- r을 위치 또는 유수 (마찰 차원에서 0으로 정의된다),
- b와 k는 각각 스프링에서의 참조 가속도 aref = -b*v - k*r을 나타내기 위한 단단함, 댐핑을 나타낸다.
- d는 상수 응답(임피던스),
- a0은 제약 힘이 없을 때의 가속도라고 한다. (원래 가속도)
앞서 분석에서 우리는 제약 공간에서의 다이나믹스가 대략
- a1 + d * (b v + k r) = (1 - d) * a0
- 가속도 + 상수 * (단단함 * 속도 + 댐핑 * 위치) = (1 - 상수) * 원래 가속도
으로 나타냄을 밝혔다.
다시, 유저가 조정할 수 있는 파라미터는 d, b, k(상수, 단단함, 댐핑) 이다. 나머지 양들은 시스템의 상태이며 각 시간 스텝에서 자동으로 계산된다.
먼저 우리는 응답 상수 d에 대해 설명한다.
- d는 반드시 0~1 사이여야 한다; 내부적으로 MuJoCo는 0.0001 ~ 0.9999로 자른다.
- 이것이 solver가 a0과 aref 사이를 상호작용 할 수 있게 해준다.
- 작은 d값은 부드럽고 약한 제약이고, 큰 d값은 강하고 딱딱한 제약이다.
- 유저는 d를 상수로 설정하거나, 아니면 위치 의존적인 함수 d(r)로 설정할 수 있다.
- 위치 의존적 응답은 물체 주변의 부드러운 접촉 레이어로 사용하거나, 혹은 큰 위반일수록 강해지는 equality 제약을 정의할 수 있다. (따라서 예제에서의 backlash를 근사할 수 있다)
- 함수 d(r)의 모양은 요소별로 정의할 수 있는 solimp 벡터 파라미터로 정의할 수 있다.
solimp: real(5), "0.9 0.95 0.001 0.5 2"
다섯 개의 숫자는 (dmin, dmax, width, midpoint, power)이다. 이것은 함수 d(r)의 파라미터를 결정한다. (최소 d, 최대 d, 너비, 중간점, 차수)
MuJoCo 2.0 이전에서는 3개였으며, 거기에 함수의 형태를 결정하는 한 개의 추가 전역 옵션이 있었다.
MuJoCo 2.0에서는 이전 버전의 호환성을 유지하며 응답 함수를 확장했다.
이전 버전에서 앞의 3개만 설정 가능했고, 기본값은 똑같다.
뒤의 2개의 기본값은 이전 버전과 똑같은 함수의 모양 (시그모이드)를 생성한다.
- 새롭게 정의된 뒤의 2개는 함수의 이동(shift)나 조여짐(skew)을 조정할 수 있다.
- 아래 그림은 사실은 두 반사된 시그모이드(???)를 보여주고 있으며, 이는 응답 함수 d(r)이 r의 절대값에 의존하기 때문이다.
- 이 유연성은 원격 접촉 힘에 대한 더 나은 조절을 하기 위해 추가되었으며, 또한 다른 제약을 위해서도 사용될 수 있다.
- (함수를 생성하기 위한 다항 스플라인의) 차수는 1과 같거나 커야한다.
- (반사 지점을 결정하는) 중간점은 0과 1 사이여야 하고, 단위는 너비이다.
- 참고로 차수가 1이면, 함수는 중간점과 관계 없는 선형이 된다.
(그림)
이 그림은 가로 축의 r의 제약 위반에 대한 세로 축의 응답 d(r)를 보인다.
- equality 제약에 대해서는, r은 제약 위반과 같다 (양수 또는 음수 가능).
- 마찰 손실 또는 타원뿔의 마찰 차원에 대해서는, r은 항상 0이다.
- 제한(limit), 타원뿔의 법선 방향, 사각뿔의 모든 법선 방향에 대해서는 r은 (제약 또는 접촉) 거리에서 제약이 활성화 되는 margin을 뺀 값이다; 접촉에 대해서 이 margin은 실제로 margin-gap이다.
- 따라서 제한과 접촉 제약은 r이 음수일 때 활성화된다.
다음은 단단함 k와 댐핑 b에 대해 설명한다.
여기에서의 아이디어는 위의 질량-스프링-댐퍼 시스템에서의 시상수와 댐핑 비율에 대한 파라미터의 재조정이다.
"시상수"에 의해 우리는 자연 주파수 / 댐핑 비율 의 평균을 구한다 (???)
1차 다이나믹스를 가진 유수가 0인 제약이나 질량-스프링-댐퍼 분석은 적용하지 않는다 (???)
그런 경우에는 시상수는 등속의 지수 감쇠에 대한 rate 이며 (???) 댐핑 비율은 무시된다.
이 형식에서, MuJoCo 2.0은 단단함과 댐핑을 더 직접적으로 정의할 수 있는 2차 형식을 제공한다. (???)
solref: real(2), "0.02 1"
이 속성은 숫자의 부호에 의해 두 가지의 형식으로 결정된다.
- 두 숫자가 둘 다 양수이면 (timeconst, dampratio) = (시상수, 댐핑 비율)로 결정된다.
- 모든 MuJoCo에서 가능하다.
- 이 경우 우리는 적절한 스케일링 후 k, b를 계산하기 위해 질량-스프링-댐퍼 모델을 쓴다.
- 참고로 활성화된 (effective) 단단함 d(r)*k와 댐핑 d(r)*b는 거리 r에 대한 함수인 응답 d(r)에 의해 스케일 된다.
- 따라서 우리는 d에 대한 스케일링을 완전히 되돌리기 전까지는 지정된 질량-스프링-댐퍼 속성을 항상 만족할 수는 없다. (???)
- 하지만 스케일링을 완전히 되돌리는 것은 원하지 않을 것인데, 왜냐하면 그것은 상호작용 속성을 파괴할 것이고, 특히 제한 d=0이 더 이상 제약을 비활성화 하지 않는다.
- 대신에 우리는 단단함과 댐핑을 스케일하여, 댐핑 비율을 상수로 유지하고, d(r)이 작아짐에 따라 시상수를 증가시킨다.
- 스케일링 수식은
- b = 2 / (dmax * timeconst)
- 단단함 = 2 / (최대 d * 시상수)
- k = d(r) / (dmax * dmax * timeconst * timeconst * dampratio * dampratio)
- 댐핑 = d의 함수 / (최대 d의 제곱 * 시상수의 제곱 * 댐핑 비율의 제곱)
- 시상수 파라미터는 시뮬레이션 타임 스텝보다 최소 2배 이상 커야 한다.
- 아니면 시스템은 산술 integrator에 대해 너무 단단할 것이고 (특히 오일러 integration) 불안정할 것이다.
- 이건 내부적으로 지켜지며, refsafe를 false로 하면 무시할 수 있다.
- 댐핑 비율 파라미터는 보통 1로 설정한다. (치명적 댐핑???)
- 작은 값은 under-댐핑 또는 통통 튀는 제약이 될 것이고, 큰 값은 over-댐핑되는 제약이 될 것이다.
- 그 외는 (-stiffness, -damping) = (-단단함, -댐핑)으로 결정된다.
- MuJoCo 2.0에서 도입되었다.
- 이 것은 원상 회복(???, restitution)에 대한 더욱 직접적인 조정을 가능하게 한다.
- 우리는 여전히 조금 스케일링을 적용하므로 같은 숫자들이 다른 응답들에 대해 사용될 수 있지만, 더 이상 스케일링이 r에 의존하지 않고 두 숫자는 더 이상 상호작용하지 않는다.
- 스케일링 수식은
- b = damping / dmax
- 단단함 = 댐핑 / 최대 d
- k = stiffness / (dmax * dmax)
- 댐핑 = 단단함 / (최대 d의 제곱)
액추에이터 길이 범위 (Actuator length range)
MuJoCo 2.0에서, mjModel.actuator_lengthrange 필드는 액추에이터의 구동 가능한 길이의 범위를 담고 있다 (혹은 더 정확하게, 액추에이터의 전달 가능 범위).
이것은 밑에서 설명할 근육 액추에이터 시뮬레이션에 필요하다. 여기서는 actuator_lengthrange의 의미 및 설정법에 촛점을 맞춘다.
정확한 물리적 혹은 기하학적 양인 mjModel의 다른 필드와는 다르게, actuator_lengthrange는 추정값이다.
이 값은 직관적으로 생각하면 모델의 모든 "구동 가능한" 조합들에 대해서의 액추에이터의 전달력이 도달할 수 있는 길이를 의미한다.
하지만 MuJoCo의 제약은 soft 방식이므로, 원칙상 어떤 조합도 구동 가능하다고 할 수 있다.
이와 동시에 우리는 근육 모델링을 위해 잘 정의된 범위도 필요하다.
이 범위를 정하기 위한 방법은 세 가지가 있다:
- 모든 액추에이터에 새로운 속성값인 lengthrange 값을 명시적으로 제공한다.
- 액추에이터가 붙어 있는 joint나 tendon의 제한값으로부터 복사 해 온다.
- 아래에서 설명하는 대로, 자동으로 계산한다. 이 계산에는 많은 옵션들이 있고, XML option 노드의 lengthrange에서 설정 가능하다.
액추에이터 길이 범위의 자동 계산은 컴파일 시간에 완료되고, 결과는 컴파일 된 모델의 mjModel.actuator_lengthrange에 저장된다.
이 후 모델이 저장되면 (XML이나 MJB로), 다음 번 로딩에서 재계산이 필요 없다. 대규모 근골격 모델을 로드 할 때 컴파일러에서의 계산이 느려질 수 있기 때문에, 이 처리는 중요하다.
이 계산만을 위해 컴파일러의 멀티스레딩 구현을 하였다(각각의 액추에이터에 대해 여러 스레드에서 병렬 처리). 이는 MuJoCo 라이브러리를 리눅스나 macOS에서 링크할 때 '-pthread' 플래그가 필요하게 된 이유이다.
자동 계산은 수정된 물리 시뮬레이션에 의존한다.
- 각각의 액추에이터에 대해서 우리는 힘(최소 길이를 계산할 때 음의 힘, 최대 길이를 계산할 때 양의 힘)을 액추에이터의 전달 가능 범위에 대해 적용하고,
- 시뮬레이션의 불안정성을 피하기 위해 댐핑된 환경 하에서 시뮬레이션을 진행하며,
- 안정될 때까지 충분한 시간을 주고, 결과를 기록한다.
이는 모멘텀 하에서의 경사 하강법과 비슷하다. 실제로 우리는 명시적 경사(그라디언트) 기반 최적화 기법으로 실험도 해 보았지만, (주어진 여러 soft 제약 하에서) 무엇을 최적화 해야 하는지 명확하지 않다는 문제가 있었다.
시뮬레이션을 사용하여, 우리는 본질적으로 물리 환경이 우리에게 무엇을 최적화해야 하는지 말하도록 한다.
염두에 두어야 할 것은, 이것은 여전히 최적화 프로세스이며, 아마도 계산된 파라미터를 재조정 할 필요도 있을 것이다.
우리는 대부분의 모델에서 동작할 보수적인 기본값을 제공하지만, 동작하지 않을 경우, 미세 조정을 위해 lengthrange 속성값을 이용하면 된다.
이 기능을 사용 할 때 모델의 기하학(geometry)를 염두에 두는 것이 중요하다.
여기에서의 암묵적 가정은 구동 가능한 액추에이터의 길이는 제한되어 있다는 것이다.
게다가 우리는 접촉을 제한 요소로서 고려하지 않는다 (사실 우리는 이 시뮬레이션에서 접촉을 비롯해 정적(passive) 힘, 중력, 마찰 손실, 액추에이터 힘을 내부적으로 비활성화시킨다).
그 이유는 접촉이 있는 모델은 얽힐 수 있고 많은 지역 최적해를 만들 수 있기 때문이다.
따라서 액추에이터는 모델에 정의되어 있는 (이 시뮬레이션에서 활성화 되어 있는)관절 혹은 tendon의 제한, 또는 기하학(geometry)에 의해 제한되어야 한다.
후자(기하학)를 설명하기 위해, 한 쪽은 world에, 다른 한 쪽은 world에 붙어 있는 힌지 관절 주변으로 회전하는 물체에 매달려 있는 tendon을 생각해보자.
- 이 경우 tendon의 최소 및 최대 길이는 잘 정의된다.
- 공간에서 부착 점의 궤도가 그리는 원의 크기에 의존된다.
- 사용자에 의해 정의된 관절이나 tendon에 길이 제한이 없음에도 불구하고 말이다.
그러나 만약 액추에이터가 관절 혹은 관절과 equal한 tendon 그 자체에 부착되어 있다면, 제한이 없어지게 된다.
이런 경우는 컴파일러가 에러를 내지만, 에러의 이유가 길이가 수렴하지 않아서인지 아니면 액추에이터 길이가 무제한인지 말해줄 수는 없다.
이 내용들은 너무 복잡하게 들리는데, 우리가 가능한 매우 특이한 경우를 설명하고 있기 때문이다.
실제로(현실적으로)는 길이 범위는 거의 모든 경우에 spatial tendon에 붙은 근육 액추에이터에서 사용될 것이며, 또한 아마도 모델의 관절 제한이 있을 것이기 때문에, 효과적으로 근육 액추에이터의 길이를 잘 제한시킬 것이다.
만약 어떤 모델에서 수렴 에러가 발생한다면, 아마 그 이유는 모델에 관절 제한을 넣는 것을 깜빡 했었을 것이다.
근육 액추에이터 (Muscle Actuators)
MuJoCo 2.0에서, 우리는 생물학적 근육을 모델링하기 위한 도구를 제공한다. 최소한의 노력으로 근육을 추가하고 싶은 사용자는 액추에이터 섹션에 한 줄의 XML으로 구현할 수 있다.
<actuator>
<muscle name="mymuscle" tendon="mytendon">
</actuator>
생물학적 근육은 서로 매우 달라 보이지만, 신기하게도 어떤 스케일링을 적용하면 전부 비슷한 형태로 움직인다. 우리의 기본 세팅은 그 스케일링을 적용하므로, 사용자는 파라미터 조정 없이도 쓸만한 근육 모델을 획득할 수 있다.
당연히 더 상세한 모델은 이 섹션에서 설명할 파라미터들의 조정이 필요하다.
근육 모델이 꽤 정교하지만, 여전히 MuJoCo 액추에이터의 한 타입으로 존재하며 다른 모든 액추에이터와 같은 컨벤션을 따른다.
근육은 general을 사용하여 정의할 수 있지만, 단축어 muscle이 더욱 편리하다.
다른 모든 액추에이터와 같이, 힘 생성 메커니즘과 전달 등은 독립적으로 정의된다. 그렇긴 하지만, 근육은 tendon 또는 관절에 부착되었을 때에만 (생물학)물리학적 의미를 갖는다.
여기서는 tendon 에 부착되었다고 가정한다.
먼저 우리는 길이와 길이 스케일링에 대해 설명한다.
전달체 (예: MuJoCo tendon)의 구동 가능한 길이의 범위가 중요한 역할을 한다; 상단의 길이 범위 섹션을 참조하라.
- 생체역학에서, 근육과 tendon은 일렬로 연결되어 근육-tendon 액추에이터를 구성한다.
- MuJoCo에서는 이와 다르다
- 공간적(spatial) 속성 (특히 길이와 속도)를 가진 것이 tendon이고
- tendon을 당기는 추상적 힘 생성 메커니즘을 의미하는 것이 근육이다.
따라서 MuJoCo에서의 tendon 길이는, 생체역학에서의 근육+tendon 길이와 같다.
- 여기서는 생물학적 tendon이 상수 길이 LT로 딱딱하게 고정되어 있다고 가정하고, 생물학적 근육 길이 LM은 시간에 따라 변한다고 취급한다.
- 따라서 MuJoCo의 tendon 길이는, 생물학적 tendon과 근육 길이의 합이다:
- actuator_length = LT + LM
- 액추에이터 길이 = 생물학적 tendon 길이(LT) + 생물학적 근육 길이(LM)
또 다른 중요한 상수는 근육의 휴식기 최적 길이(L0)이다.
- 이는 근육이 속도가 0일 때, 동적 힘(active force)을 최대로 생성할 때의 근육 길이 LM이다.
우리는 사용자에게 L0과 LT를 직접 묻지 않는다.
- 왜냐하면 공간적 복잡성한 tendon의 경로 및 감쌈(wrapping) 조건 하에서 이 값들을 알아내는 것이 어렵기 때문이다.
- 대신 우리는 아래와 같이 L0과 LT를 계산한다.
- LT+LM(actuator_length)의 가용 범위는 계산은 위의 길이 범위 계산에서 언급했다.
- 여기에다가, 우리는 사용자에게 (여전히 모르는) 상수 L0으로 스케일 된 근육 길이 LM 의 가용 범위를 지정하도록 묻는다.
- 이것은 range 속성으로 정해지며, 기본 (스케일 된) 범위 값은 (0.75, 1.05) 이다.
- 이제 우리는 실제 및 스케일된 길이의 범위를 이용해서 두 상수 L0과 LT를 계산할 수 있다:
- (actuator_lengthrange[0] - LT) / L0 = range[0]
- (actuator_lengthrange[1] - LT) / L0 = range[1]
- (역주) 따라서 계산해 보면
- L0 = (actuator_lengthrange[1] - actuator_lengthrange[0]) / (range[1] - range[0])
- LT = ((actuator_lengthrange[0] * range[1]) - (actuator_lengthrange[1] * range[0])) / (range[1] - range[0])