【Python】物理エンジンMuJoCoの紹介＆MJCFドキュメント【MuJoCoチュートリアル①】

　本記事では、無料のオープンソース物理エンジンである**MuJoCo**の解説およびチュートリアルを記述します。記述は**バージョン3.1.3**（Python: [mujoco 3.1.3](https://pypi.org/project/mujoco/3.1.3/)）に基づきます。

　本記事は[XML Reference](https://mujoco.readthedocs.io/en/stable/XMLreference.html)に基づくMJCF形式の説明を中心に、具体例や検証などを併せた内容となっています。`<actuator>`要素および`mjModel`&`mjData`構造体などについてはあまり取り扱いません。別記事を参照してください。

https://qiita.com/Yayoi-Habami/items/90f42ea10a32eb20fde8

:::note warn
サイズの大きい表はスタイルが崩れやすいため画像として掲載しています。テキストとして見たい場合は、その下の「表をテキスト形式で見る」を押してください。
:::

## 目次

　内容が膨大なため、大見出し＆特に重要な項目のみを以下に示します。詳細はその下の「目次 - 詳細」を参照してください。

<!-- no toc -->
- [目次](#目次)
- [はじめに \& モチベーション](#はじめに--モチベーション)
- [インストール](#インストール)
- [モデリング (MJCF)](#モデリング-mjcf)
  - [MJCFとは](#mjcfとは)
  - [MJCFモデルの構造](#mjcfモデルの構造)
  - [位置と姿勢の指定 (MJCF)](#位置と姿勢の指定-mjcf)
- [モデルの使い方](#モデルの使い方)
  - [`MjModel` \& `MjData` (MJCF)](#mjmodel--mjdata-mjcf)
    - [入出力値の単位（長さ・角度）](#入出力値の単位長さ角度)
- [レンダリングとシミュレーション](#レンダリングとシミュレーション)
- [MJCFの要素 : geom (body)](#mjcfの要素--geom-body)
  - [MJCFの要素 : site (body)](#mjcfの要素--site-body)
- [MJCFの要素 : joint (body)](#mjcfの要素--joint-body)
- [MJCFの要素 : body](#mjcfの要素--body)
- [MJCFの要素 : camera (body)](#mjcfの要素--camera-body)
- [MJCFの要素 : light (body)](#mjcfの要素--light-body)
- [MJCFの要素 : sensor](#mjcfの要素--sensor)
- [MJCFの要素 : tendon](#mjcfの要素--tendon)
- [MJCFの要素 : actuator](#mjcfの要素--actuator)
- [MJCFの要素 : default](#mjcfの要素--default)
- [MJCFの要素 : option](#mjcfの要素--option)
- [MJCFの要素 : keyframe](#mjcfの要素--keyframe)
- [MJCFの要素 : asset](#mjcfの要素--asset)
- [具体例で学ぶ① : keyframeとasset](#具体例で学ぶ--keyframeとasset)
- [具体例で学ぶ② : default, option, タイムステップ幅など](#具体例で学ぶ--default-option-タイムステップ幅など)
- [具体例で学ぶ③ : 接触点・接触力](#具体例で学ぶ--接触点接触力)
- [参考文献](#参考文献)

<details><summary>【目次 - 詳細】</summary><div>

- [目次](#目次)
- [はじめに \& モチベーション](#はじめに--モチベーション)
- [インストール](#インストール)
- [モデリング (MJCF)](#モデリング-mjcf)
  - [MJCFとは](#mjcfとは)
    - [概要](#概要)
    - [モデルの読込（\&保存）](#モデルの読込保存)
  - [MJCFモデルの構造](#mjcfモデルの構造)
    - [キネマティックツリー](#キネマティックツリー)
  - [位置と姿勢の指定 (MJCF)](#位置と姿勢の指定-mjcf)
- [モデルの使い方](#モデルの使い方)
  - [`MjModel` \& `MjData` (MJCF)](#mjmodel--mjdata-mjcf)
    - [`MjModel`](#mjmodel)
    - [`MjData`](#mjdata)
    - [入出力値の単位（長さ・角度）](#入出力値の単位長さ角度)
  - [コンパイル後のモデルのオプション (mjOption, mjVisual, mjStatistic)](#コンパイル後のモデルのオプション-mjoption-mjvisual-mjstatistic)
- [レンダリングとシミュレーション](#レンダリングとシミュレーション)
  - [静止画のレンダリング](#静止画のレンダリング)
  - [シミュレーション](#シミュレーション)
    - [自由度の追加](#自由度の追加)
    - [アニメーションの作成](#アニメーションの作成)
- [MJCFの要素 : geom (body)](#mjcfの要素--geom-body)
  - [概要 : geom (MJCF/body)](#概要--geom-mjcfbody)
  - [属性 : geom (MJCF/body)](#属性--geom-mjcfbody)
  - [body\>geom詳説 : type](#bodygeom詳説--type)
  - [body\>geom詳説 : fromto](#bodygeom詳説--fromto)
  - [MJCFの要素 : site (body)](#mjcfの要素--site-body)
- [MJCFの要素 : joint (body)](#mjcfの要素--joint-body)
  - [概要 : joint (MJCF/body)](#概要--joint-mjcfbody)
  - [属性 : joint (MJCF/body)](#属性--joint-mjcfbody)
  - [body\>joint詳説 : type](#bodyjoint詳説--type)
  - [関節角度・角速度等の取得（`mjData.qpos`と`mjData.qvel`）](#関節角度角速度等の取得mjdataqposとmjdataqvel)
    - [ジョイントタイプと取得できる値](#ジョイントタイプと取得できる値)
    - [ジョイントが複数ある場合の`qpos`、`qvel`](#ジョイントが複数ある場合のqposqvel)
    - [基準位置/角度など（`qpos0`など）](#基準位置角度などqpos0など)
- [MJCFの要素 : body](#mjcfの要素--body)
  - [概要 : body (MJCF)](#概要--body-mjcf)
  - [属性 : body (MJCF)](#属性--body-mjcf)
  - [MJCFの要素 : inertial (body)](#mjcfの要素--inertial-body)
- [MJCFの要素 : camera (body)](#mjcfの要素--camera-body)
  - [概要 : camera (MJCF/body)](#概要--camera-mjcfbody)
  - [属性 : camera (MJCF/body)](#属性--camera-mjcfbody)
  - [body\>camera詳説 : mode](#bodycamera詳説--mode)
- [MJCFの要素 : light (body)](#mjcfの要素--light-body)
  - [概要 : light (MJCF/body)](#概要--light-mjcfbody)
  - [属性 : light (MJCF/body)](#属性--light-mjcfbody)
- [MJCFの要素 : sensor](#mjcfの要素--sensor)
  - [概要 : sensor (MJCF)](#概要--sensor-mjcf)
  - [属性 : sensor (MJCF)](#属性--sensor-mjcf)
  - [使用可能なすべてのセンサの一覧](#使用可能なすべてのセンサの一覧)
  - [各センサの詳細](#各センサの詳細)
    - [詳説 : タッチセンサ（touch）](#詳説--タッチセンサtouch)
    - [詳説 : 力センサ・トルクセンサ（force, torque）](#詳説--力センサトルクセンサforce-torque)
    - [詳説 : 距離センサ（rangefinder）](#詳説--距離センサrangefinder)
    - [詳説 : 光学式モーションキャプチャのマーカー (camprojection)](#詳説--光学式モーションキャプチャのマーカー-camprojection)
- [MJCFの要素 : tendon](#mjcfの要素--tendon)
  - [概要 : tendon (MJCF)](#概要--tendon-mjcf)
  - [属性 : spatial (MJCF/tendon)](#属性--spatial-mjcftendon)
  - [spatialの子要素（site, geom, pulley）](#spatialの子要素site-geom-pulley)
    - [site (MJCF/tendon/spatial)](#site-mjcftendonspatial)
    - [geom（MJCF/tendon/spatial）](#geommjcftendonspatial)
    - [pulley（MJCF/tendon/spatial）](#pulleymjcftendonspatial)
    - [使用例（site、geom、pulley）](#使用例sitegeompulley)
- [MJCFの要素 : actuator](#mjcfの要素--actuator)
  - [概要 : actuator (MJCF)](#概要--actuator-mjcf)
  - [属性 : actuator (MJCF)](#属性--actuator-mjcf)
- [MJCFの要素 : default](#mjcfの要素--default)
  - [概要 : default (MJCF)](#概要--default-mjcf)
  - [属性 : default (MJCF)](#属性--default-mjcf)
- [MJCFの要素 : option](#mjcfの要素--option)
  - [概要 : option (MJCF)](#概要--option-mjcf)
  - [使用可能なオプションの一覧](#使用可能なオプションの一覧)
  - [option詳説 : timestep](#option詳説--timestep)
- [MJCFの要素 : keyframe](#mjcfの要素--keyframe)
  - [概要 : keyframe (MJCF)](#概要--keyframe-mjcf)
  - [属性 : keyframe (MJCF)](#属性--keyframe-mjcf)
- [MJCFの要素 : asset](#mjcfの要素--asset)
  - [概要 : asset (MJCF)](#概要--asset-mjcf)
  - [よく使うasset集](#よく使うasset集)
- [参考文献](#参考文献)

</div></details>

　本記事はMuJoCo公式ドキュメントの[XML Reference](https://mujoco.readthedocs.io/en/stable/XMLreference.html)をベースに記述を行っています。当該ぺージに記載された各項目の実装状況は以下を参照してください。

<details><summary>【XML Reference実装状況】</summary><div>

　XML Referenceの全項目は以下の通りです。このうち、記述を行ったものについてはリンクを貼っています。最も低い階層の要素（compilerやsizeなど）は、親に`mujoco`を持ちます。また、「->」は親子関係を示します。

- compiller -> lengthrange
- size
- statistic
- visual -> global, quality, headlight, map, scale, rgba
- [asset](#mjcfの要素--asset) -> texture, hfield, mesh, plugin(->config), skin, material
- [option](#mjcfの要素--option) -> [flag](#使用可能なオプションの一覧)
- [body](#mjcfの要素--body)
  - [inertial](#mjcfの要素--inertial-body)
  - [joint](#mjcfの要素--joint-body)
  - [freejoint](#mjcfの要素--joint-body)
  - [geom](#mjcfの要素--geom-body) -> plugin(->config)
  - [site](#mjcfの要素--site-body)
  - [camera](#mjcfの要素--camera-body)
  - [light](#mjcfの要素--light-body)
  - plugin -> config
  - composite -> joint, tendon, skin, geom, site, pin, plugin(->config)
  - flexcomp -> edge, contact, pin, plugin(->config)
- deformable
  - flex -> contact, edge
  - skin -> bone
- contact -> pair exclude
- equality -> connect, weld, joint, tendon, flex
- [tendon](#mjcfの要素--tendon)
  - [spatial](#属性--spatial-mjcftendon) -> [site](#site-mjcftendonspatial), [geom](#geommjcftendonspatial), [pulley](#pulleymjcftendonspatial)
  - fixed -> joint
- [actuator](#mjcfの要素--actuator) -> [general](#属性--actuator-mjcf), [motor, position, velocity, intvelocity, damper, cylinder, muscle, adhesion](https://qiita.com/Yayoi-Habami/items/90f42ea10a32eb20fde8), plugin(->config)
- [sensor](#mjcfの要素--sensor) -> [touch, accelerometer, velocimeter, gyro, force, torque, magnetometer, camprojection, rangefinder, jointpos, jointvel, tendonpos, tendonvel, actuatorpos, actuatorvel, actuatorfrc, jointactuatorfrc, ballquat, ballangvel, jointlimitpos, jointlimitvel, jointlimitfrc, tendonlimitpos, tendonlimitvel, tendonlimitfrc, framepos, framequat, framexaxis, frameyaxis, framezaxis, framelinvel, frameangvel, framelinacc, frameangacc, subtreecom, subtreelinvel, subtreeangmom, clock, user, plugin(->config)](#使用可能なすべてのセンサの一覧)
- [keyframe](#mjcfの要素--keyframe) -> [key](#概要--keyframe-mjcf)
- [default](#mjcfの要素--default) -> [mesh, material, joint, geom, site, camera, light, pair, equality, tendon, general, motor, position, velocity, intvelocity, damper, cylinder, muscle, adhesion](#属性--default-mjcf)
- custom -> numeric, text, tuple(->element)
- extension -> plugin(->instance(->config))

</div></details>

## はじめに & モチベーション

　**MuJoCo**（**Mu**lti-**Jo**int dynamics with **Co**ntact）は、環境と相互作用する関節構造の高速かつ正確なシミュレーションを行うことのできる物理エンジンです。当初は有料でしたが、2021年にDeepMindにより買収されたことをきっかけに、現在では**無料のオープンソース物理エンジン**として使用することができます。制御、状態推定、システム同定、機構設計、逆動力学によるデータ解析、機械学習アプリケーションの並列サンプリングなど様々なモデルベース計算の実装が可能です。

　Pythonにおける物理エンジンとしては**PyBullet**（Bullet）が有名です。これと比較しMuJoCoは（特に日本語の）ドキュメントが少なく若干とっつきにくいかと思います。しかし、基本的に多関節ロボットのシミュレーションにおいては計算速度上の利点があり[1]、強化学習等に用いる上で大きな利点を持ちます。詳しくは[別記事](https://qiita.com/Yayoi-Habami/items/8441ce4427e496d2884d)にて解説しましたが、PyBulletと比較して**処理速度が２～３倍速く**、また**正確性や安定性も高い**です。このため、強化学習などで用いる際には大きなアドバンテージとなります。

https://qiita.com/Yayoi-Habami/items/8441ce4427e496d2884d

:::note
　名前に多関節動力学を含むところからわかるように、MuJoCoは多関節の力学、すなわちロボット工学等におけるシミュレーションにおいて高速に処理を行うことが可能です[1]。一方でスイカゲームのようなシミュレーションではPyBullet(Bullet)よりも処理が遅くなるため[1]、関節構造を持たないシステムのシミュレーションを行う上での利点はあまりないようです。これは採用している積分方法（ルンゲクッタ積分と半陰的オイラー積分など）による違いです。
:::

:::note
　MuJoCoで使用される数値はすべて**SI単位系およびSI併用単位**（°）に依ります。例えば、長さや変位としての数値の単位は $\mathrm{m}$ です。角度については $\mathrm{rad}$ と ° を両方使用できるケースと、どちらかしか使用できないケースがあります。既定では入力に関して ° が使用されますが、`<compiler>`の`angle`属性を変更することで $\mathrm{rad}$ を使用できるようになります。
:::

## インストール

　現在（24/03/11; PyPI mujoco 3.1.3）、**Python 3.8以上**の環境であれば、Windows環境であっても`pip`によるインストールが可能なようです。はじめに、適当な環境でMuJoCoをインストールします。一部環境でpipによるインストールが行えない場合は、GitHubのソースコードを直接ビルドすることもできます。[Python Bindings/Building from source](https://mujoco.readthedocs.io/en/stable/python.html#building-from-source)を参照してください。

```pip install mujoco```

　以降は通常のモジュールと同様に使用することができます。基盤であるC APIとの互換性のために、Python APIには一部Pythonicではない構造がありますが、基本的には通常のPythonのモジュールと同様に使用することが可能です。

 ```python:tutorial.py
 import mujoco

 print(mujoco.__version__)  # 3.1.3
 ```

pipによるインストール内容には含まれませんが、[通常のダウンロード](https://github.com/google-deepmind/mujoco/releases)先から取得したライブラリにはGUIアプリも存在します。アプリケーションはOpenGLでレンダリングされ、インタラクティブな操作が可能です。

<details><summary>【GUIアプリケーションについて】</summary><div>

　上述の[ダウンロード](https://github.com/google-deepmind/mujoco/releases)先からダウンロードしたzipファイルの中には`bin/simulate.exe`というアプリケーションが存在します（Windows版）。

　ジョイントの状態等各種の状態値をリアルタイムに見ることができたり、アクチュエータへの制御入力などの各種値をリアルタイムに変更することができたりするなど、MuJoCoの仕組みを理解するのに便利です。

![MuJoCo_XML_GUIアプリケーション_スクショ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/4e28ade5-cd04-aed5-a2ac-88d319cbabee.png)

**図** GUIアプリケーション操作画面

GUIの詳細については次の記事なども参考になるかと思います。

https://qiita.com/sunrise_lover/items/58ea54a33ff5114cb8ef

</div></details>

> なんかLinuxじゃないとインストールできないとか、Docker使わないとだめとか昔ありませんでしたっけ？すごくインストールが簡単になってますね。…そういうのはmujoco_pyのころの話なのかな？

## モデリング (MJCF)

　MuJoCoで読み込むことのできるモデルのファイル形式は2つです。一つがMuJoCo専用の**MJCF**であり、もう一つがより一般的でROSなどで使用される**URDF**です。どちらもXML形式で記述され、ロボットの形状や物理的特性など、様々な要素を詳細にモデリングすることが可能です。

　本記事では、ネイティブの表現であるMJCFについて記述を行います。URDFはMuJoCoだけを使用する場合には必要とならないほか、一応MJCFとURDFの相互変換は可能っぽいので本節では取り扱いません。

:::note
**URDF**：URDF（Unified Robot Description Format）とは、ROSなどで使用される、一般的なロボットの構造を記述するXMLファイル形式です。MuJoCoでは[URDF拡張機能](https://mujoco.readthedocs.io/en/stable/modeling.html#urdf-extensions)により、URDFを直接読み込むことが可能です。ただし、多くの場合URDFはあまり編集されないため、一度URDFファイルをMJCFに変換し、これを編集する方が便利なようです。
:::

<details><summary>【MuJoCoのモデルとは？】</summary><div>

　MuJoCoには「モデル」と呼ばれるエンティティが複数あります。その違いは①ファイルとして保存するかメモリ上に保持するか、②高水準/低水準のどちらか、の２点です。

**表** MuJoCoの「モデル」の種類[3]

||高水準|低水準|
|-|-|-|
|ファイル|MJCF/URDF (XML)|MJB (バイナリ)|
|メモリ|`mjCModel` (C++クラス) | `mjModel` (C構造体)|

すべてのランタイム計算は`mjModel`で行われます。`mjModel`は高水準のモデルがコンパイルされたものであるため処理は高速ですが、バージョン間の互換性は（必ずしも）ありません。`mjCModel`は内部で使用される、MJCFファイルとほぼ一対一に対応するモデルです。現在はこれを外部から使用することはできません。

　以下に`mjModel`を取得するためのすべての経路を示します[3]。ただし、いまのところ2番の順序は使用することができません。将来的に`mjCModel`がCラッパーで記述されれば使用可能となるようです。

1. (テキストエディタ) → MJCF/URDF ファイル → (MuJoCo パーサー → mjCModel → MuJoCo コンパイラ) → mjModel
2. (ユーザコード) → mjCModel → (MuJoCo コンパイラ) → mjModel
3. MJBファイル → (MuJoCoローダ) → mjModel

</div></details>

### MJCFとは

#### 概要

　**MJCF**（おそらく**M**u**J**o**C**o **F**ormat）はMuJoCo専用の物理オブジェクト記述用のフォーマットであり、接触ダイナミクスのシミュレーションに優れるモデルです。ロボットの形状や物理的特性を記述できるだけでなく、**環境や外観など様々な要素を指定することが可能**です。

　以下に、`red_box`と`green_sphere`をMJCFとして定義し、ロードするコードを示します。ここで、**最上位要素が**`<mujoco>`であることMJCFの定義であること、またすべての物理要素は`<worldbody>`の下に定義されることを覚えてください。

```python:load_mjcf_model.py
import mujoco

xml = """
<mujoco>
  <worldbody>
    <geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
    <geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
  </worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
```

上のコードではオブジェクトの位置（`pos`属性）やジオメトリタイプ（`type`属性）が省略されていたりしますが、これはMJCFの属性には**デフォルト値**があるためです。例として、位置（`pos`属性）はデフォルトで`0 0 0`です。

上記コードの最終行では`from_xml_string()`を使用してXMLを**コンパイル**し、`MjModel`の**バイナリインスタンス**を生成しています。

> 定義から、最小のMJCFモデルは`<mujoco/>`となります。

:::note
　モデリングの際は[公式のモデルギャラリー](https://mujoco.readthedocs.io/en/stable/models.html)などが一助になるかと思います。二足歩行ロボットや手、ドローンなど様々なモデルについて、そのコードと完成図・可動例が公開されているため大変参考になります。
:::

#### モデルの読込（&保存）

　**モデルの読み込み**は次の３つの関数によって行うことが可能です。

|関数|読込対象|説明|
|-|-|-|
|`mujoco.MjModel.from_xml_string`|コード上文字列|MJCFまたはURDFの文字列を読み込みます|
|`mujoco.MjModel.from_xml_path`|XMLテキストファイル|MJCFまたはURDFファイルを読み込みます|
|`mujoco.MjModel.from_binary_path`|MJBファイル|MJBファイルを読み込みます|

　MJCFおよびURDFによるモデルはテキストとして読込・**コンパイル**されます。このとき、MJCFとURDFの最上位要素はそれぞれ`<mujoco>`と`<robot>`である必要があります。MJBファイルはコンパイル済みのモデルを保存したものですから、他の２つと比較して読み込み速度が若干速いです。それでも長くて1秒程度の差とのことなので、基本的に`from_binary_path`を使用することはないかと思います（特にPython）。

　**モデルの保存**は関数`mujoco.mj_saveModel`および`mj_saveLastXML`により行うことが可能です。保存されるファイルは`.mjb`形式であり、コンパイル済みのモデルの構造や設定値などが保存されます。また、モデルの**読み込み後に変更を加えた場合には変更後の値が保存**されます。

 ```python
# XMLファイルとして書き出す
mujoco.mj_saveLastXML("arm.xml", model)
# MJBファイルとして書き出す
mujoco.mj_saveModel(model, "example.mjb", None)
```

上述のようにバイナリ化に関してはあまり使用する機会はないのですが、大規模な骨格筋モデル（`<tendon>`と`<muscle>`を多く使用したもの）についてはバイナリファイルとして保存することが推奨されています。これは`<muscle>`のlengthrangeの自動計算にかなり時間がかかるためです。

### MJCFモデルの構造

#### キネマティックツリー

　上の具体例でも示したように、MJCFモデルの主要部分はbody要素による木構造、**キネマティックツリー**（kinematic tree）です。このうち、最上位のボディのみ`<worldbody>`と呼称されます。
 
 　各ツリー構造の親要素と子要素は**ジョイントで接続され**、XMLの親要素と子要素の関係がそのままMJCFのそれに対応します。主要なボディ要素は`<joint>`、`<geom>`、`<site>`、`<camera>`および`<light>`であり、ボディ内で定義された各要素はその**ボディのローカルフレームに固定**されます。ただしキネマティックツリーはボディ要素の木構造ですので、`<default>`や`<option>`などはこれに含まれません。また、`<actuator>`や`<tendon>`なども一つのボディに関連付けられないため（スタンドアロン要素）同様です。

　MJCFモデルは１つまたは複数のキネマティックツリーを中心とし、これにオプションとアセット、スタンドアロン要素を加えたものです。モデルの要素を以下にまとめます。

|モデル要素|説明|
|-|-|
|Options|`mjOption`, `mjVisual`, `mjStatistic`から構成されます。[後述](#コンパイル後のモデルのオプション-mjoption-mjvisual-mjstatistic)します。|
|Assets|Mesh, Skin, Height field, Texture, Materialから構成されます。|
|キネマティックツリー|[Body](#mjcfの要素--body), [Joint](#mjcfの要素--joint-body) (DOF), [Geom](#mjcfの要素--geom-body), [Site](#mjcfの要素--site-body), [Camera](#mjcfの要素--camera-body), [Light](#mjcfの要素--light-body)から構成されます。|
|スタンドアロン要素|[Tendon](#mjcfの要素--tendon), [Actuator](#mjcfの要素--actuator), [Sensor](#mjcfの要素--sensor), [Equality](#mjcfの要素--equality), Flex, Contact pair, Contact exclude, Custom numeric, Custom text, Custom tuple, [Keyframe](#mjcfの要素--keyframe)から構成されます。|

　MuJoCoにおけるジョイントの機能は**親子間に運動の自由度を与える**ことです。このため、子のボディ要素の中に**ジョイントが定義されていない場合**、そのボディは親に溶接されます。一方、これが定義されている場合には子は親から動くことが可能となります。また、MJCFでは**１つのボディが複数のジョイントを含むことができる**ため、ダミーボディを作成せず平面上を移動するボディをモデル化することなども可能です（例: スライダx2とヒンジx1）。

<details><summary>【キネマティックツリーの具体例】</summary><div>

　以下にMJCFコードの具体例を示します（不完全なコードですが）。以下のコードの場合、キネマティックツリーに含まれるのは`<worldbody>...</worldbody>`の部分となります。

　各ボディについて見ていきます。ボディ`"b1"`は`<worldbody>`に固定されているため、`<worldbody>`から動くことができません。一方、ボディ`"b2"`と`"b3"`はどちらもジョイントをもつため、どちらも`<worldbody>`を基準として特定の自由度のもとで動くことが可能です。

```xml
<mujoco>
  <default class="main">
    <geom/>
  </default>

  <worldbody>
    <geom/>
    <body name="b1">
      <geom/>
      <body name="b3">
        <joint/>
        <geom/>
      </body>
    </body>
    <body name="b2">
      <joint/>
      <geom/>
    </body>
  </worldbody>
</mujoco>
```

</div></details>

> 上述のように、`<worldbody>`と`<body>`は要素名こそ違うもののどちらもボディとして扱われます。このため、`<body>`要素を定義していない場合であっても`model.nbody`は1を示します（`MjModel`のインスタンス`model`から見たとき、`<worldbody>`は名前が`"world"`であるボディ）。

:::note
ループを持つ構造を作成したい場合、等式制約（`<equality>`要素）を設定する必要があります。これは各MJCFモデルがキネマティックツリー、すなわち木構造により構築されるためです。
:::

### 位置と姿勢の指定 (MJCF)

　3次元空間において体積を持つ物体の位置と姿勢は、それぞれ3ずつ自由度を持ちます。MJCFではこれを表現するために、位置は1通り、姿勢は5通りの方法を使用することができます。キネマティックツリーのように親要素を持つ要素の場合、位置・姿勢ともに**ローカル座標**における位置姿勢を指定します。

:::note
**MJCFは右手系の座標系**です。
:::

　**位置**は次の1通りの方法で記述することができます。例えばキネマティックツリーの場合、親ボディを基準に`<geom>`、`<joint>`、`<site>`、`<camera>`および`<light>`の位置を指定することができます。

|既定|属性|型|デフォルト値|説明|
|:-:|-|-|-|-|
|✅|pos|real(3)<br> $(x,y,z)$ |`"0 0 0"`|ローカル座標における位置を指定します。|

　**姿勢**は次の5通りの方法で記述することができます。既定ではクォータニオンによる表現（`quat`属性）が使用されており、姿勢を指定するほかの属性でもクォータニオンを用いることが多いです。例えば`<key>`の`qpos`属性は位置と姿勢を指定しますが（例;`"1 3 0 1 0 0 0"`）、後半の4つはクォータニオン $(q_1,q_2,q_3,q_4)$ を意味します。

　`axisangle`および`euler`では角度を指定しますが、この際に**指定する角度の単位は度**（[°]）です。`<compiler>`の`angle`属性を`"degree"`から`"radian"`に変更することで、ラジアンを使用することも可能です。また、コンパイル後の内部表現としてはすべてラジアンが使用されます。

**表** MJCFおよびMuJoCoで使用可能な姿勢の表現

![MuJoCo_XML_表_MJCFおよびMuJoCoで使用可能な姿勢の表現.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/5b3fa274-76b6-6767-c240-d9cc0f9dd59e.png)

<details><summary>【表をテキスト形式で見る】</summary><div>

|既定|属性|型|デフォルト値|説明|
|:-:|-|-|-|-|
|✅|`quat`|real(4)<br> $(q_1,q_2,q_3,q_4)$ |`"1 0 0 0"`|クォータニオンによる表現です。<br>単位ベクトル $(x,y,z)$ を軸として $\theta$ だけ回転したとき $(q_1,q_2,q_3,q_4)=(\cos\frac{\theta}{2}, x\sin\frac{\theta}{2}, y\sin\frac{\theta}{2}, z\sin\frac{\theta}{2})$ |
||`axisangle`|real(4)<br> $(x,y,z,\theta)$ |optional|回転軸のベクトル $(x,y,z)$ と回転角度 $\theta$ を指定します。<br>回転軸が単位ベクトルである必要はありません。また、回転は右向きであることに注意してください（右手系）。|
||`euler`|real(3)<br> $(\alpha,\beta, \gamma)$ |optional|オイラー角です<br> $(\alpha,\beta, \gamma)$ は既定ではおそらくx-y-z系のオイラー角です。`<compiler>`の`eulerseq`から変更することができます。|
||`xyaxes`|real(6)<br> $(x_h,y_h,z_h,x_v,y_v,z_v)$ |optional|回転後の座標系のX軸とY軸を指定します。<br>新しい座標系のx軸は $(x_h,y_h,z_h)$ 、y軸は $(x_v,y_v,z_v)$ により定義され、z軸はその外積として計算されます。<br>カメラフレームの指定に便利です。|
||`zaxis`|real(3)<br> $(x_z,y_z,z_z)$ |optional|回転後の座標系のz軸を指定します。<br>コンパイラが $(0,0,1)$ と $(x_z,y_z,z_z)$ が一致するような最小の回転を見つけ、同時に暗黙的にx,y軸を決定します。<br>z軸を中心とした回転対称性を持つ`<geom>`や、フレームのz軸に沿った方向を持つ`<light>`に便利です。|

</div></details>

## モデルの使い方

　前節ではMJCFという形式の説明を行いました。この節では、その作成したモデルをPython上でどのようにして扱うかについての説明を行います。

### `MjModel` & `MjData` (MJCF)

#### `MjModel`

　上に書いたようにMJCFのモデル`model`はプログラム上でコンパイル済みの、`MjModel`のインスタンスとして利用します。`MjModel`は**時間の経過とともに変化しない**すべての量を保持します。例えば以下のコードでは、`geom`要素の総数、およびそれぞれの色を確認しています。

```python
# geom要素の総数
print(model.ngeom) # 2
# それぞれジオメトリの色 : numpy.ndarray
print(model.rgba)  # array([[1., 0., 0., 1.],
                   #        [0., 1., 0., 1.], dtype=float32)
```

> `model.ngeom`の返す数：`model`内に`<geom>`と`<body>`が一つずつ定義され、その`<body>`の中に3個`<geom>`が定義されていている場合、`ngeom`は`4`を返します。

モデルの各要素については`name`属性や`id`属性を利用してもアクセスすることが可能です（Name access）。ここで、`body`要素についてはXML上の定義にかかわらず`"world"`**が0番目の要素として定義される**ことには注意したいです。

```python
# geom要素の red_box のidを取得
print(model.geom("red_box").id) # 0
# id属性の値からもアクセス可能
print(model.geom(0).name)       # "red_box"

# body要素の world のidを取得
print(model.nbody)              # 1
print(model.body("world").id)   # 0
```

> `model.geom("red_box")`からは、`red_box`ジオメトリでアクセス可能なすべてのプロパティを見ることができます。

#### `MjData`

　`MjModel`が時間の経過とともに変化しない量を扱う一方で、`MjData`は時間により変化する量を保持します。正確には、**時間、一般化位置**(generalized position)**および一般化速度**からなる**状態と、それに依存する量**を`MjData`では扱います。ここで、時間、一般化位置および一般化速度はそれぞれ次のように取得できます。

```python
data = mujoco.MjData(model)

# 左から順に時間、一般化位置、一般化速度
print(data.time, data.qpos, data.qvel) # 0.0 [] []
```

　また、`geom_xpos`を使用することで、各オブジェクトのデカルト座標も取得することができます。
 
　ところで、下の出力結果のうち初めの方を見るとどちらも $(0,0,0)$ を指しており、xmlの定義で`"green_sphere"`の`pos`属性を`".2 .2 .2"`と定義したのと差異があることがわかります。これは各物体の位置関係に基づく位置変化を**明示的に伝播**（特に関節を持つ場合）**させる必要がある**ためであり、このために`mj_kinematics`関数を使用します。今回は、`model`の位置関係を`data`の各要素に伝播させています。

```python
# 運動学適用前
print(data.geom_xpos)    # array([[0., 0., 0.],
                         #        [0., 0., 0.]])

# 運動学適用後
mujoco.mj_kinematics(model, data)
# green_sphereの位置
print(data.geom(1).xpos) # array([.2, .2, .2])
```

#### 入出力値の単位（長さ・角度）

　冒頭でも述べたように、MuJoCoでは**SI単位系およびSI併用単位**（°）を単位とした数値を扱います。このうち**長さや変位**に関する単位は $\mathrm{m}$ 一択であり、またそれに基づく組立単位も $\mathrm{m}$ に準拠したものとなります。このため、入出力で値を操作する際に混乱することはないといえます。

　一方、混乱が生じやすいのが**角度**やそれに基づく単位です。角度については弧度法（ $\mathrm{rad}$ ）と度数法（ ° ）の両方が使用されているため、指定する/出力された値がどちらで表されているか悩ましいケースがあるかと思います。確認したところ、おそらく以下の規則に従っていることがわかりました。

- **入力** : **角度のみ基本的に度数法**、**角速度などは弧度法**
  - `<compiler>`の`angle`属性を`"radian"`にしている場合は角度も $\mathrm{rad}$
    - `<light>`の`cutoff`属性など、ごく一部の属性は上の場合でも度数法で指定
  - `mjData`から書換えを行う場合は弧度法で指定
- **出力 : すべて弧度法**
  - `mjModel`や`mjData`、関数などから得られる値はすべて弧度法
  - 例 : 角度は $\mathrm{rad}$, 角速度は $\mathrm{rad/s}$ 、など

:::note
MuJoCoは内部的に角度をすべてラジアンとして扱っています。このため**MJCFソース上でのみ、例外的に度数法を使える**、と考えた方がよいかと思います。
:::

### コンパイル後のモデルのオプション (mjOption, mjVisual, mjStatistic)

　コンパイル後のモデルのオプション値は、以下の３組にすべて保存されます。ここでいうオプションとは`<option>`で指定可能なものだけではなく、モデル上のすべての設定値（**経時変化しない**すべての値）を意味します。これらの値はシミュレーションを進める前であればいつでも変更可能です。ただし、**一度シミュレーションを開始した後はどのオプションも変更してはいけません**（おおよそ`mj_step`実行後）。

**表** コンパイル後のモデルのオプション．`model`は`mjModel`のインスタンスを示します．

|構造体|インスタンス|説明|
|-|-|-|
|`mjOption`|`model.opt`|物理シミュレーションに影響するすべてのオプションが含まれます。<br>アルゴリズムの選択、パラメタの設定、シミュレーションパイプラインの有効/無効設定、重力等システムレベルの物理特性などが含まれます。|
|`mjVisual`|`model.vis`|すべての可視化オプションが含まれます。ただし、追加のOpenGLレンダリングオプションはこれに含まれません。|
|`mjStatistic`|`model.stat`|コンパイラによって計算されるモデルに関する統計情報が含まれています。|

## レンダリングとシミュレーション

### 静止画のレンダリング

　レンダリングは`Renderer`のインスタンスから`render`関数を使用して行います。ここで、レンダリングの前に、XMLの定義の`<worldbody>...</worldbody>`の中に、光源`<light name="top" pos="0 0 1"/>`を追加しています。

　運動学の適用（`mj_kinematics`）と同様に、レンダリングにおいては`MjData`を`MjModel`に適用する必要があります。これを行う関数が`mj_forward`であり、以下のように書けます。

```python
import mujoco
from PIL import Image

xml = """
<mujoco>
  <worldbody>
    <light name="top" pos="0 0 1"/>
    <geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
    <geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
  </worldbody>
</mujoco>
"""
# ライトを適用したMJCFモデルの作成
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)
renderer = mujoco.Renderer(model)

# 順動力学の適用 (data -> model)
mujoco.mj_forward(model, data)
# シーンを保持する MjvScene を更新する
renderer.update_scene(data)

# 画像の表示
Image.fromarray(renderer.render()).show()
```

上記のコードにより、次の画像を描画することができました。基本的に、位置の変化があった場合には`mujoco.mj_forward`の行以降を、色などの変化のみの場合は`renderer.update_scene`の行以降を再度実行することで画像を更新することができます。

![mujoco_レンダー_赤い立方体と緑の球.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/540575bf-c25f-8e2f-d5cc-81ae48873078.png)

**図** レンダリング結果

> `<light name="top" pos="0 0 1"/>`を追加する：光源を追加しないと、ほとんど真っ黒な画像が出力されることになります。光源を追加することで、上のようにしっかりと色と形がわかる画像を出力することができます。

### シミュレーション

#### 自由度の追加

　前節では静止画をレンダリングしました。次に、今節ではアニメーションを作成する方法を見ていきます。

　はじめに、上で使用したMJCFモデルは**自由度**(DOF)を持たないため、動画を再生しても動きを確認することができません。このため、ここではヒンジジョイントを持つボディを使用することにしましょう。また、`MjOption`を使用して、ジョイントを可視化することにします。**本節では**、`xml`, `model`, `data`, `renderer`はいずれも以下のMJCFモデルに基づくものとします。

```python:render_video.py
import mujoco

xml = """
<mujoco>
  <worldbody>
    <light name="top" pos="0 0 1"/>
    <body name="box_and_sphere" euler="0 0 -30">
      <joint name="swing" type="hinge" axis="1 -1 0" pos="-.2 -.2 -.2"/>
      <geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
      <geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
    </body>
  </worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)
renderer = mujoco.Renderer(model)

# ジョイントの可視化オプションを有効化
scene_option = mujoco.MjvOption()
scene_option.flags[mujoco.mjtVisFlag.mjVIS_JOINT] = True
```

ここで、`"box_and_sphere"`は、`euler="0 0 -30"`属性を使用して、z軸を中心に30°回転されています。これは、アニメーションを見やすくするための操作であり、また**キネマティックツリー**(kinematic tree)のすべての要素の姿勢が、常に親ボディを基準にしていることを示すためでもあります。すなわち、親ボディである`"box_and_sphere"`を回転させることで、その子要素である`"red_box"`と`"green_sphere"`も回転しました。

:::note
　**自由度**(Degree of freedom; DOF)**について**：３次元の世界において、すべての体積を持つ物体は6つの自由度を持ちます（並進３自由度、回転３自由度）。ジョイントは拘束として機能するため、ジョイントで２つの物体を結合すると相対的な自由度が減ぜられます。ただし、MuJoCoでは効率的なシミュレーションを行うために、**ジョイントを使用して明示的に追加されない限りオブジェクトは自由度を持ちません**。

　私たちはジョイントを通してモデルに自由度を設定しますが、ジョイントと自由度は一対一対応するわけではありません（`"ball"`や`"free"`は複数の自由度を持つため）。

　**ジョイントは位置情報を指定するもの**であり、**自由度は速度と力の情報を指定するもの**といえます。すなわち、各自由度は摩擦損失、減衰、アーマチュアの慣性のような**速度に関連する特性**を持ちます。また、システムに作用するすべての一般化力は、自由度の空間（space of DOFs）で表現されます。一方、ジョイントは限界やバネ剛性などの**位置に関連する特性**を持ちます。
:::

#### アニメーションの作成

　さて、可動部位を持つオブジェクトを追加できたので、これを描画していきたいと思います。

　MuJoCoには**アニメーションを描画するための関数が存在しません**。このため、動画を生成する場合には`mujoco.mj_step`を利用して時間を進めつつ、各タイムステップにおいて前節と同様に画像を生成することになります。これを繋げることで、結果として動画を生成することができます。

```python:render_video.py
duration = 3.8  # (seconds)
framerate = 60  # (Hz)

frames = []
mujoco.mj_resetData(model, data)
while data.time < duration:
  # 時間を進める
  mujoco.mj_step(model, data)

  if len(frames) < data.time * framerate:
    # シーンを更新し、画像としてレンダリングする
    renderer.update_scene(data, scene_option=scene_option)
    pixels = renderer.render()
    frames.append(pixels)

# 動画を保存する（任意の関数）
save_video(frames, "output.mp4", framerate=framerate)
```

保存された動画を以下に示します。赤い立方体と緑の球が、ヒンジジョイントを中心に回転していることがわかります。

![mujoco_レンダー_ジョイントを持つ赤い立方体と緑の玉.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/4a4bd2c5-ae8f-7057-584d-57370086e7d7.gif)

**図** ジョイントを持つ赤い立方体と緑の球

<details><summary>【save_video関数について】</summary><div>

　`save_video`関数：`numpy.ndarray`による画像のリストを動画として保存できる関数であればなんでもよいと思います。以下には、OpenCVを使用した`save_video`関数の例を記述します。

```python:video_utils.py
import os

import cv2
import numpy as np
from moviepy.editor import ImageSequenceClip

def save_video(frames, videopath:str, framerate=60, allow_fps_reducing=True):
    """動画を保存する
- frames: ndarrayのlistか、ndarray
- framerate : 動画のfps
- videopath: 保存先のファイルパス、gif/mp4形式のみ
- allow_fps_reducing: gifとして保存する際にfpsを減少（frames枚数を減らす）させることを許すか"""
    # videopathがmp4のものかを確認
    ext = os.path.splitext(videopath)[1]
    assert ext in [".mp4",".gif"]

    # 動画のサイズを取得
    if type(frames)==np.ndarray:
        _, height, width, _ = frames.shape
    else:
        height, width, _ = frames[0].shape
    
    if ext == ".mp4":
        _save_video_as_mp4(frames, videopath, framerate, width, height)
    else:
        save_video_as_gif(frames, videopath, framerate, allow_fps_reducing=allow_fps_reducing)

def _save_video_as_mp4(frames, videopath:str, framerate:int, width:int, height:int):
    # 動画をファイルとして保存
    video_writer = cv2.VideoWriter(videopath, 
                                   cv2.VideoWriter_fourcc(*'mp4v'), 
                                   framerate, 
                                   (width, height))
    for frame in frames:
        # 色の並びをOpenCV用に変換
        image = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        video_writer.write(image)
    video_writer.release()

def save_video_as_gif(frames, videopath:str, fps:int, gif_fps:int=6, *,
                       slow_down_fps:float=1, 
                       decreased_colors:int=-1,
                       allow_overwrite=False, allow_fps_reducing:bool=False):
    """動画をgifとして保存する
- frames: ndarrayのlistか、ndarray
- videopath: 保存先のファイルパス、gif形式のみ
- fps     : 元動画のfps
- gif_fps : 保存するgifのfps
- slow_down_fps     : 1より大きい場合、動画の速度を1/nにする (2の場合、gifの動画fpsを1/2にする)
- decreased_colors  : 1以上の場合、指定された数に減色する (16未満くらいが望ましい; K-Meansのクラスタ数を指定するため、数が大きいほど時間がかかる)
- allow_overwrite   : Falseの場合、名前の後ろに数字 ("_n") を付加する
- allow_fps_reducing: gifとして保存する際にfpsを減少 (frames枚数を減らす) させることを許すか

推奨設定は以下の通り

- gif_fps = 6
- slow_down_fps = 2 (1などでもよい。1未満は非推奨)
- decreased_colors = 12
- allow_overwrite = True
- allow_fps_reducing = True"""
    assert os.path.splitext(videopath)[1] == ".gif"
    print("gifに変換中...")
    fps /= slow_down_fps

    # フレーム数を減らす
    if allow_fps_reducing and (fps > gif_fps):
        step = int(fps / gif_fps)
        frames = frames[::step]
    else:
        gif_fps = min(int(fps), gif_fps)
    
    # 減色する
    if decreased_colors >= 1:
        frames = decrease_colors_by_k_means(frames, decreased_colors)

    # ファイル名を決定する
    newname = os.path.splitext(videopath)[0]+".gif"
    if not allow_overwrite:
        newname = append_index_num_to_avoid_duplication(newname)
    # 保存する
    clip = ImageSequenceClip(frames, fps=gif_fps)
    clip.write_gif(newname)

def decrease_colors_by_k_means(frames:list[np.ndarray], clusters:int=8):
    """K-Means法により画像群の減色を行う
- clusters : クラスター数 (処理時間を考えると16未満くらいが望ましい)"""
    # ndarray(y,x,[R,G,B]) を ndarray(y*x, [R,G,B]) に変形
    totalframe = np.concatenate([x.reshape((-1,3)) for x in frames])
    totalframe = np.float32(totalframe)

    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    _, label, center=cv2.kmeans(totalframe, clusters, None,
                                  criteria=criteria, 
                                  attempts=10, flags=cv2.KMEANS_RANDOM_CENTERS)

    center = np.uint8(center)
    n_shift = 0
    for i,frame in enumerate(frames):
        n_pixels = frame.shape[0] * frame.shape[1]
        res = center[label[n_shift:n_shift+n_pixels].flatten()]
        frames[i] = res.reshape((frame.shape))
        n_shift += n_pixels

    return frames

def append_index_num_to_avoid_duplication(filename:str) -> str:
    """ファイル名の重複を避けるために、ファイル名の末尾に数字を付加する"""
    basename, ext = os.path.splitext(filename)
    newname = filename

    i = 1
    while True:
        if not os.path.exists(newname):
            return newname
        # 数字を変更
        newname = basename + f"_{i}" + ext
        i += 1
```

</div></details>

## MJCFの要素 : geom (body)

　ここまでで、MJCFでモデリングを行う際の概要とそれをPythonで使用する方法について見てきました。ここからは、数節にわたってMJCFボディの基本要素の詳細を記述します。

### 概要 : geom (MJCF/body)

　`<geom>`は`<body>`に取り付けて使用する、幾何学形状・特性を決定するためのオブジェクトです。MuJoCoでは凸の`<geom>`（とそれ同士の衝突）しかサポートしていないため、**非凸のオブジェクトを作成するには凸のジオムの結合として表現する**必要があります。

　`<body>`内で1つまたは複数の`<geom>`が定義されたとき、その`<geom>`(群)はボディの外観と衝突のプロパティを決定します。また、inertiafromgeomが`"false"`でないときには慣性特性の計算にも使用されます。この際には、`<geom>`の①形状と②密度または質量、および③一様密度の仮定が用いられます。

　シミュレーションにおいて、実は必ずしも`<geom>`が必要となるわけではありません。接触力こそ計算されませんが、ボディとジョイントのみからシミュレーションを行うことも可能です。

> inertiafromgeom : `<compiler>`の`inertiafromgeom`属性を意味し、geomから質量と慣性の自動推論を行うかを決定します。既定では`"auto"`であり、質量および慣性要素が指定されていないものについては自動推論を行うようになっています。質量に比べ慣性は大きくなりがちであるので、特にURDFモデルを取得する際などは`"true"`に設定することが推奨されるようです。

### 属性 : geom (MJCF/body)

　以下に代表的な属性を示します。すべての属性については、その下の「使用可能な全属性の一覧」を参照してください。

**表** 主要な属性

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|type|**型：選択式〈** `"sphere"` **〉**<br>`["plane","hfield","sphere","capsule","ellipsoid",` `"cylinder","box","mesh","sdf"]`から選択<br>幾何形状を決定します。[後述](#bodygeom詳説--type)します。|
|size|**型：real(3)〈** `"0 0 0"` **〉**<br>サイズを決定します。[後述](#bodygeom詳説--type)するように、`type`属性により必要なパラメタの数は変動します。ただし、**メッシュを参照しない**`<geom>`**でのみ有効となります**。|
|rgba|**型：real(4)〈** `"0.5 0.5 0.5 1"` **〉**<br>色と透明度を設定します。|
|friction|**型：real(3)〈** `"1 0.005 0.0001"` **〉**<br>接触ペアの接触摩擦パラメタです。`solmix`と`priority`により適用されるかが決定されます。数値の意味は前から順に次の通りです。<br>(1) 滑り摩擦（接平面の両軸沿い）<br>(2) ねじり摩擦（接触法線周り）<br>(3) 転がり摩擦（接平面の両軸周り）<br>💡`friction="0.5"`のように指定すると、後ろの値を変えずに前の値だけを変更することができます。例の場合、摩擦係数は`.5 .005 .0001`として設定されます。|
|mass|**型：real〈** optional **〉**<br>質量を決定し、慣性特性を計算するために使用されます。`density`属性に優先され、指定された場合は質量から密度が計算されるようになります。|
|density|**型：real〈** `"1000"` **〉**<br>密度を決定し、慣性特性を計算するために使用されます。既定の`"1000"`は水の密度にほぼ同じです。|
|fromto|**型：real(6)〈** optional **〉**<br>[後述](#bodygeom詳説--fromto)するように、長さとフレーム位置、方向の指定に使用することが可能です。`"capsule"`、`"box"`、`"cylinder"`、`"elllipsoid"`でのみ指定可能です。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>`<geom>`の位置を指定します。基準点は[後述](#bodygeom詳説--type)します。|
|quat<br>axisangle<br>euler<br>xyaxes<br>zaxis|**型：real(n)〈** `"1 0 0 0"` - (`quat`) **〉**<br>`<body>`のローカル座標の姿勢を指定します。[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節を参照してください。|


<details><summary>【使用可能な全属性の一覧】</summary><div>

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|type|**型：選択式〈** `"sphere"` **〉**<br>`["plane","hfield","sphere","capsule","ellipsoid",` `"cylinder","box","mesh","sdf"]`から選択<br>幾何形状を決定します。|
|contype|**型：int〈** `"1"` **〉**<br>次の属性と合わせて、接触ペアのフィルタリングに使用します。[衝突判定](https://mujoco.readthedocs.io/en/stable/computation/index.html#collision)を参照してください。|
|conaffinity|**型：int〈** `"1"` **〉**<br>接触フィルタリング用のビットマスクです。|
|condim|**型：int〈** `"3"` **〉**<br>接触ペアの接触空間の次元を決定します（ペアの最大値が適用されます）。許容される値とその意味は次の通りです。<br>`"1"` - 摩擦のない接触<br>`"3"` - 規則的な摩擦接触 + 接平面で滑りが対向<br>`"4"` - ソフトコンタクト（`"3"`に加え接触法線周りの回転に対向）<br>`"6"` - 物体の不定転がりを防ぐ（`"4"`に加え接平面の2軸回りの回転）|
|group|**型：int〈** `"0"` **〉**<br>属するグループを指定します。グループ値に基づき、コンパイル時に質量と慣性が計算されます。|
|priority|**型：int〈** `"0"` **〉**<br>2つの衝突する`<geom>`のプロパティの組み合わせ方を決定します。[接触パラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#ccontact)を参照してください。|
|size|**型：real(3)〈** `"0 0 0"` **〉**<br>サイズを決定します。[後述](#bodygeom詳説--type)するように、`type`属性により必要なパラメタの数は変動します。ただし、**メッシュを参照しない**`<geom>`**でのみ有効となります**。|
|material|**型：string〈** optional **〉**<br>指定された場合、(色を除く)視覚的なプロパティを決定するマテリアルを適用します。|
|rgba|**型：real(4)〈** `"0.5 0.5 0.5 1"` **〉**<br>色と透明度を設定します。|
|friction|**型：real(3)〈** `"1 0.005 0.0001"` **〉**<br>接触ペアの接触摩擦パラメタです。`solmix`と`priority`により適用されるかが決定されます。数値の意味は前から順に次の通りです。<br>(1) 滑り摩擦（接平面の両軸沿い）<br>(2) ねじり摩擦（接触法線周り）<br>(3) 転がり摩擦（接平面の両軸周り）<br>💡`friction="0.5"`のように指定すると、後ろの値を変えずに前の値だけを変更することができます。例の場合、摩擦係数は`.5 .005 .0001`として設定されます。|
|mass|**型：real〈** optional **〉**<br>質量を決定し、慣性特性を計算するために使用されます。`density`属性に優先され、指定された場合は質量から密度が計算されるようになります。|
|density|**型：real〈** `"1000"` **〉**<br>密度を決定し、慣性特性を計算するために使用されます。既定の`"1000"`は水の密度にほぼ同じです。|
|shellinertia|**型：選択式〈** `"false"` **〉**<br>`["false","true"]`から選択<br>`"true"`の場合、慣性特性はすべての質量が境界に集中していると仮定して計算されます。この場合、密度は表面密度として計算されます。|
|solmix|**型：real〈** `"1"` **〉**<br>接触パラメタの平均化に使用される重みを指定します。[接触パラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#ccontact)を参照してください。|
|margin|**型：real〈** `"0"` **〉**<br>接触を検知する距離の閾値です。あくまで`mjData.contact`に記録されるだけであり、接触力が生じるか否かとは関係がありません。|
|gap|**型：real〈** `"0"` **〉**<br>アクティブでない接触、すなわち制約ソルバーによって無視されるが、カスタム計算のためにmjData.contactに含まれる接触を有効にするために使用されます。|
|fromto|**型：real(6)〈** optional **〉**<br>[後述](#bodygeom詳説--fromto)するように、長さとフレーム位置、方向の指定に使用することが可能です。`"capsule"`、`"box"`、`"cylinder"`、`"elllipsoid"`でのみ指定可能です。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>`<geom>`の位置を指定します。基準点は[後述](#bodygeom詳説--type)します。|
|quat<br>axisangle<br>euler<br>xyaxes<br>zaxis|**型：real(n)〈** `"1 0 0 0"` - (`quat`) **〉**<br>`<body>`のローカル座標の姿勢を指定します。[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節を参照してください。|
|hfield|**型：string〈** optional **〉**<br>`type`属性が`"hfield"`の場合に指定し、高さフィールドアセットの参照先（`<asset>`の`<hfield>`で読込先パス`file`を指定）を指定します。|
|mesh|**型：string〈** optional **〉**<br>`type`属性が`"mesh"`の場合に**必ず**指定し、メッシュアセットの参照先（`<asset>`の`<mesh>`で読込先パス`file`を指定）を指定します。このとき読み込めるファイルの形式はSTL、OBJおよびMSHです。<br>**プリミティブ**（`"sphere"`、`"capsule"`、`"cylinder"`、`"ellipsoid"`または`"box"`）の場合も指定可能であり、形状寸法がメッシュにフィットするように決定されます。この場合もコンパイル後の`mjModel`では通常のプリミティブと同様に扱われ、メッシュへの参照は記録されません。|
|fitscale|**型：real〈** `"1"` **〉**<br>プリミティブがメッシュにフィットされる場合にのみ使用されます。<br>`"1"` - 自動フィッティング後の結果は変更されません。<br>`"2"` - フィッティング後の`<geom>`のサイズを2倍にします。|
|fluidshape|**型：選択式〈** `"none"` **〉**<br>`["none","ellipsoid"]`から選択<br>形状の楕円体近似に基づく`<geom>`レベルの[流体相互作用モデル](https://mujoco.readthedocs.io/en/stable/computation/fluid.html#flellipsoid)を有効化します。|
|fluidcoef|**型：real(5)〈** `"0.5 0.25 1.5 1.0 1.0"` **〉**<br>流体相互作用モデルの無次元係数 $(C_{D,blunt},C_{D,slender},C_{D,angular},C_K,C_M)$ です。[流体相互作用モデル](https://mujoco.readthedocs.io/en/stable/computation/fluid.html#flellipsoid)を参照してください。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_geom`と同じ要素数を持ちます。[ユーザーパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#cuser)を参照してください。|


> 他に`solref`、`solimp`なる制約ソルバのパラメタも存在するようです。

</div></details>

### body>geom詳説 : type

　プリミティブ5種については、その中心と`pos`の座標が一致します。`"plane"`、`"sphere"`、`"caplule"`および`"box"`のみから構成されたモデルは、**衝突判定の面で最も効率的**です。

　`"box"`を除いたプリミティブは、レンダリングの際にメッシュに分割されて表示されます。このメッシュの密度は`<visual>`の`<quality>`から調整することが可能です。

> 以上の型のうち `"sphere"`、`"capsule"`、`"cylinder"`、`"ellipsoid"`および`"box"`については**プリミティブ**と呼ばれることがあります。MuJoCoはCSG(Constructive Solid Geometry)ベースなんですかね？

以下にボディの要素として定義することのできる`<geom>`の型を示します。

> いずれの`<geom>`についても、`pos`属性は原点 $(0,0,0)$ に設定しています。カメラに関しては、正面から見ているものについてはカメラ位置を $(0,-5,0)$ から原点方向に配置しています。

|type|sizeの型|説明|画像|
|-|-|-|:-:|
|plane|real(3)<br> $(w_x, w_y, s)$ |XY平面です（またはそれに平行な平面）。**ワールド本体かその静的な子要素にのみ配置可能**です。<br>`size`パラメタは平面のx,y軸方向の幅の半分 $w_x, w_y$ と、レンダリングのための正方格子線の間隔 $s$ を指定します。 $w_x$ または $w_y$ が０の場合、要素が0の軸方向の幅は無限大として描画されます。<br>正方格子線の役割は照明と影を改善することです。地面に格子を描画するためには`<texture>`機能を使用してください。<br>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/b8c79c9e-1dd6-b0f0-8f0c-e6a17f6d3b20.png" width=200><br>**図** 平面; `size="1.5 1.5 1"`|
|hfield|無効|グレースケール画像に基づく高さフィールドです。**ワールド本体かその静的な子要素にのみ配置可能**です。<br>`<geom>`の`size`属性は無視され、`<asset>`内`<hfield>`の`size`（real(4); $(w_x,w_y,h,t)$ ）パラメタが使用されます。パラメタはx,y軸方向の幅の半分 $w_x,w_y$ と最大高さ $h$ 、および底面厚み $t$ からなります。ただし、底面厚み $t$ は最小標高点より下の部分の厚みを意味します。例えば`pos`が原点を指し $h=1$ の場合、底面は $z=-1$ 上に形成されます。|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/8cb214ca-608a-ed31-89b1-4bfcf879e8ea.png" width=200><br>**図** `size="1 1 0.3 0.05"`（上）グレイスケール画像（下）高さフィールド|
|sphere|real(1)<br> $(r)$ |球です。<br>`size`パラメタは半径 $r$ を指定します。|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/aba3bbd6-6778-b028-b5f6-94264211a11b.png" width=200><br>**図** 球; `size="1"`|
|capsule|real(2)<br> $(r,h)$ <br>or<br>real(1)<br> $(r)$ |カプセル（二つの半球で覆われた円柱）です。<br>`size`パラメタは半球の半径 $r$ と円柱部分の高さの半分 $h$ を指定します。<br>`fromto`属性を使用した場合は半径 $r$ のみを指定することになります。詳細は[後述](#bodygeom詳説--fromto)します。|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/f192e859-f3e8-70b1-9cdd-6b038f708c03.png" width=200><br>**図** カプセル; `size="1 1"`|
|ellipsoid|real(3)<br> $(r_x,r_y,r_z)$ <br>or<br>real(1)<br> $(r)$ |楕円体です。<br>ローカル座標系のX,Y,Z軸に沿ってスケーリングされた球であり、`size`パラメタは3軸ごとの半径 $(r_x,r_y,r_z)$ を指定します。<br>`fromto`属性を使用した場合は半径 $r$ のみを指定することになります。詳細は[後述](#bodygeom詳説--fromto)します。<br>平面との衝突を除き楕円体の接触判定は汎用の凸型コライダー（collider）により行われます。このため、なめらかであるものの上述したように衝突判定の面で若干効率が劣ります。|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/c101850c-27d6-08b4-1d58-5afb4068e8f1.png" width=200><br>**図** 楕円体; `size="1 0.5 1.5"`|
|cylinder|real(2)<br> $(r,h)$ <br>or<br>real(1)<br> $(r)$ |円柱です。<br>`size`パラメタは半径 $r$ と、高さの半分 $h$ を指定します。また、円柱はZ軸に沿って形成されます。<br>`fromto`属性を使用した場合は半径 $r$ のみを指定することになります。詳細は[後述](#bodygeom詳説--fromto)します。|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/35b4d6d8-99a9-e7d6-2692-a2f14ba00f09.png" width=200><br>**図** 円柱; `size="1 1"`|
|box|real(3)<br> $(w_x,x_y,w_z)$ <br>or<br>real(1)<br> $(w)$ |直方体です。<br>`size`パラメタは各軸方向の幅の半分 $(w_x,x_y,w_z)$ を指定します。<br>`fromto`属性を使用した場合は横幅の半分 $w$ のみを指定することになります。詳細は[後述](#bodygeom詳説--fromto)します。|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/d4cb642d-c94c-322d-2af6-a50e8d48c1c0.png" width=200><br>**図** 立方体; `size="1 1 1"`|
|mesh|無効|メッシュファイルに基づくメッシュです。<br>`<geom>`の`size`属性は無視され、メッシュファイルのデータがそのまま出力されます。また、`<asset>`内`<mesh>`の`scale`（real(3); $(r_x,r_y,r_z)$ ）パラメタを使用して拡大/縮小を行うことも可能です。`scale`に負の値を指定した場合、メッシュはその方向について反転されます。<br>生成されるメッシュの中心は、既定ではメッシュファイルの原点となります。|省略|
|sdf|無効|符号付距離フィールド(signed distance field; SDF)を定義します。使用するためには、[SDFのプラグイン](https://mujoco.readthedocs.io/en/stable/programming/extension.html#exwriting)を定義する必要があります。|省略|

　このうち`"mesh"`および`"hfield"`は、その形状データを外部から取得する必要があります（`vertex`属性で指定した場合を除く）。ただし、ボディの`<geom>`には参照先を指定する属性がないため、下記のように`<asset>`内`<mesh>`/`<hfield>`で`file`属性を指定することになります。

　メッシュとして使用できるファイルの種類は**STL**、**MSH**および**OBJ**です。また、`<mesh>`の`vertex`属性から直接頂点を指定することも可能です（例: [car.xml](https://github.com/google-deepmind/mujoco/blob/main/model/car/car.xml)）。メッシュを使用する際には、はじめに`<asset>`でファイルを指定し、これを`<geom>`の`"mesh"`属性として呼び出す必要があります。これは高さフィールドの場合も同様です（ファイルの種類は**PNG**またはカスタムバイナリファイル）。以下に簡易で不完全なコードを示します。

```xml
<asset>
  <mesh name="forearm" file="forearm.stl" scale=".5 .5 .5"/>
  <hfield name="terrain" file="terrain.png" size="50 50 10 1"/>
</asset>

<worldbody>
  <geom type="hfield" hfield="terrain"/>
  <body pos="0 0 1"/>
    <joint type="hinge" axis="1 0 0"/>
    <geom type="mesh" mesh="forearm"/>
  </body>
</worldbody>
```

上記のファイルでは、`terrain.png`をもとに高さフィールドを定義し、`forearm.stl`をもとにメッシュを定義しています。また、メッシュについてはx,y,z軸方向にそれぞれ0.5倍の縮小を行っています。

### body>geom詳説 : fromto

　`fromto`属性は`"capsule"`、`"box"`、`"cylinder"`、`"elllipsoid"`でのみ指定可能な、形状を決定するのに使用される寸法の一つです。`size`のみを使用して形成した場合、上の4ジオメトリはz方向にしか伸ばすことができません。`fromto`属性を使用することで、始点と終点による形状の決定が可能となります。

　`fromto`属性の型はreal(6)： $(x_s,y_s,z_s,x_e,y_e,z_e)$ です。`fromto`を使用した場合、ジオメトリは始点 $(x_s,y_s,z_s)$ から終点 $(x_e,y_e,z_e)$ に向かって押し出しのような形で形成されることになります。また、`size`要素で指定できるのは2端点を結ぶ線分に垂直な方向の、半径 $r$ または横幅の半分 $w$ となります。以下にカプセルの場合の例を示します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/37426446-cfa6-c409-6067-a45f0b99b0d3.png" width=200>

**図** カプセル; `size=".25" fromto="-0.8 0 -0.6 0.8 0 0.6"`

### MJCFの要素 : site (body)

　`<site>`は**接触判定と質量をもたない**`<geom>`です。質量がないため慣性特性の計算にも使用されません。レンダリングはされます。主な役割は**ボディ上の相対的な位置を指定すること**であり、センサ位置の指定や腱の接続点指定、アクチュエータのスライダクランクトランスミッションの構築などに利用されます。

　`<geom>`の一種であるため属性も似通っています。定義可能な属性は以下の通りです。このうち、`type`属性については`<geom>`と比べ使用可能な型の種類が少ないです（`["sphere","capsule","ellipsoid","cylinder","box"]`の５種）。

- 使用可能な属性 : `name`, `class`, `type`, `group`, `size`, `material`, `rgba`, `fromto`, `pos`, `user`, および`quat`, `axisangle`, `xyaxes`, `zaxis`, `euler`

<details><summary>【使用可能な全属性の詳細】</summary><div>

ほぼ再掲です。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|type|**型：選択式〈** `"sphere"` **〉**<br>`["sphere","capsule","ellipsoid","cylinder","box"]`から選択<br>幾何形状を決定します。[前述](#bodygeom詳説--type)のとおりです。|
|group|**型：int〈** `"0"` **〉**<br>属するグループを指定します。ビジュアライザによりグループ全体のレンダリングを有効/無効化することが可能です。|
|size|**型：real(3)〈** `"0.005 0.005 0.005"` **〉**<br>サイズを決定します。[前述](#bodygeom詳説--type)したように、`type`属性により必要なパラメタの数は変動します。|
|material|**型：string〈** optional **〉**<br>指定された場合、(色を除く)視覚的なプロパティを決定するマテリアルを適用します。|
|rgba|**型：real(4)〈** `"0.5 0.5 0.5 1"` **〉**<br>色と透明度を設定します。|
|fromto|**型：real(6)〈** optional **〉**<br>[前述](#bodygeom詳説--fromto)したように、長さとフレーム位置、方向の指定に使用することが可能です。`"capsule"`、`"box"`、`"cylinder"`、`"elllipsoid"`でのみ指定可能です。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>`<geom>`の位置を指定します。基準点は[前述](#bodygeom詳説--type)のとおりです。|
|quat<br>axisangle<br>euler<br>xyaxes<br>zaxis|**型：real(n)〈** `"1 0 0 0"` - (`quat`) **〉**<br>`<body>`のローカル座標の姿勢を指定します。[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節を参照してください。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_site`と同じ要素数を持ちます。[ユーザーパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#cuser)を参照してください。|


</div></details>

## MJCFの要素 : joint (body)

### 概要 : joint (MJCF/body)

　`<body>`内で`<joint>`が定義されたとき、その`<joint>`は親のボディと当該ボディの間に動きの自由度を作ります。1つの`<body>`に対して`<joint>`は複数定義することができ、この場合定義された順で空間変換が適用されます。一方`<joint>`が定義されていない場合、その**ボディは親ボディに固定**されます。

　ジョイントには`<joint>`と`<freejoint>`の2種類があります。前者は`"free"`型、`"ball"`型、`"slide"`型および`"hinge"`型の4種類に分類されます。一方`<freejoint>`は内部的に`"free"`型の`<joint>`として扱われます。いずれもフリージョイントは`<worldbody>`を親に持つボディの下でのみ定義可能です。

　モデルで定義されたすべてのジョイントの位置と方向は`mjData.qpos`から取得できます。（直線）速度と角速度は`mjData.qvel`から取得できます。どちらもキネマティックツリーで記述した順番にデータが格納されます。ただし、これら2つのベクトルの次元は、フリージョイントまたはボールジョイントが使用されている場合には異なります。

> `<worldbody>`に対して`<joint>`を定義することはできません。

### 属性 : joint (MJCF/body)

　下の表に代表的な属性を示します。すべての属性については、その下の「使用可能な全属性の一覧」を参照してください。

:::note
　`<freejoint>`では`name`**属性と**`group`**属性の2つのみを指定可能です**。これは、`<freejoint>`は以下のデフォルト値を持つ`<joint>`として扱われるためです。

```xml
<joint type="free" stiffness="0" damping="0" frictionloss="0" armature="0"/>
```
:::

**表** 主要な属性

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|type|**型：選択式〈** `hinge` **〉**<br>`["free","ball","slide","hinge"]`から選択<br>動きの自由度と動き方を決定します。[後述](#bodyjoint詳説--type)します。|
|group|**型：int〈** `"0"` **〉**<br>属するグループの番号です。カスタムタグや、グループごとの可視化/非可視化の選択に使用することができます。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>`<joint>`が定義されたボディの座標系におけるジョイントの位置です。<br>フリージョイントの場合無視されます。|
|axis|**型：real(3)〈** `"0 0 1"` **〉**<br>`"hinge"`の回転軸と`"slide"`の移動方向を指定します。ただし、**ベクトルの長さが** $10^{-14}$ **以上となる**ように値を設定してください。<br>フリージョイントおよびボールジョイントでは無視されます。|
|limited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>ジョイントの可動範囲に制限をかけるかを決定します。可動範囲は`range`属性で指定します。<br>`"false"` - 制限は無効になります。<br>`"true"` - 関節の制限は有効になります。<br>`"auto"` - `range`属性が定義され、`<compiler>`の`autolimits`属性が`"true"`であれば制限は有効になります（既定では`autolimits`は有効）。|
|range|**型：real(2)〈** `"0 0"` **〉**<br>関節の可動範囲です。<br>`ref`による指定がない場合、基準（0）は初期状態です。フリージョイントでは無効になります。<br>`"ball"` - （二つ目の値で）基準構成に対する最大回転角度[°]を指定します。一つ目の値は0に設定する必要があります。<br>`"slide"` - 基準に対する可動量の下限と上限を指定します。<br>`"hinge"` - 基準に対する可動量の下限と上限を指定します（単位:度）|
|ref|**型：real〈** `"0"` **〉**<br>関節の基準位置/角度です。<br>設定した基準に対する変位量は`mjModel.qpos0`に格納されます。<br>`"slide"`と`"hinge"`でのみ有効です。詳細は[後述](#基準位置角度などqpos0など)します。|
|frictionloss|**型：real〈** `"0"` **〉**<br>乾性摩擦による摩擦損失です。この`<joint>`のつくるすべての自由度に対して同じ値となります。<br>正の値を設定することで有効になりますが、一応フリージョイントでも有効にすることが可能です。|


<details><summary>【使用可能な全属性の一覧】</summary><div>

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|type|**型：選択式〈** `hinge` **〉**<br>`["free","ball","slide","hinge"]`から選択<br>動きの自由度と動き方を決定します。[後述](#bodyjoint詳説--type)します。|
|group|**型：int〈** `"0"` **〉**<br>属するグループの番号です。カスタムタグや、グループごとの可視化/非可視化の選択に使用することができます。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>`<joint>`が定義されたボディの座標系におけるジョイントの位置です。<br>フリージョイントの場合無視されます。|
|axis|**型：real(3)〈** `"0 0 1"` **〉**<br>`"hinge"`の回転軸と`"slide"`の移動方向を指定します。ただし、**ベクトルの長さが** $10^{-14}$ **以上となる**ように値を設定してください。<br>フリージョイントおよびボールジョイントでは無視されます。|
|springdamper|**型：real(2): $(\tau,\zeta)$ 〈** `"0 0"` **〉**<br>ジョイントの質量-ばね-ダンパ系の時定数 $\tau$ と減衰比 $\zeta$ を指定します。<br>両方の値が正の場合、指定された値から剛性と減衰の値が計算され、`stiffness`属性と`damping`属性を自動的に**上書き**します。|
|limited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>ジョイントの可動範囲に制限をかけるかを決定します。可動範囲は`range`属性で指定します。<br>`"false"` - 制限は無効になります。<br>`"true"` - 関節の制限は有効になります。<br>`"auto"` - `range`属性が定義され、`<compiler>`の`autolimits`属性が`"true"`であれば制限は有効になります（既定では`autolimits`は有効）。|
|actuatorfrclimited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>作用するアクチュエータ力をクランプするかを決定します。力をクランプする範囲は`actuatorfrcrange`属性で指定します。ただし`"slide"`および`"hinge"`でのみ有効です。<br>`"false"` - クランプは無効になります。<br>`"true"` - クランプは有効になります。<br>`"auto"` - `actuatorfrcrange`属性が定義され、`<compiler>`の`autolimits`属性が`"true"`であれば制限は有効になります（既定では`autolimits`は有効）。|
|stiffness|**型：real〈** `"0"` **〉**<br>関節の硬さ（剛性）を指定します。<br>値が正の場合、`springref`属性で与えられる平衡位置を持つばねが作成されます。ばねの力は他の受動的な力と合わせて計算されます。|
|range|**型：real(2)〈** `"0 0"` **〉**<br>関節の可動範囲です。<br>`ref`による指定がない場合、基準（0）は初期状態です。フリージョイントでは無効になります。<br>`"ball"` - （二つ目の値で）基準構成に対する最大回転角度[°]を指定します。一つ目の値は0に設定する必要があります。<br>`"slide"` - 基準に対する可動量の下限と上限を指定します。<br>`"hinge"` - 基準に対する可動量の下限と上限を指定します（単位:度）|
|actuatorfrcrange|**型：real(2)〈** `"0 0"` **〉**<br>アクチュエータの総力をクランプする範囲の下限と上限を指定します。（関節にかかるアクチュエータの力の下限と上限を指定）<br>`"slide"`と`"hinge"`でのみ有効です。|
|margin|**型：real〈** `"0"` **〉**<br>制限が有効になる距離の閾値です。制約ソルバが力を生成する基準です。|
|ref|**型：real〈** `"0"` **〉**<br>関節の基準位置/角度です。<br>設定した基準に対する変位量は`mjModel.qpos0`に格納されます。<br>`"slide"`と`"hinge"`でのみ有効です。詳細は[後述](#基準位置角度などqpos0など)します。|
|springref|**型：real〈** `"0"` **〉**<br>関節のばねが平衡となる関節位置または角度を指定します。<br>基準に対する変位量は`mjModel.qpos_spring`に格納されます（上記`mjModel.qpos0`と同様）。詳細は[後述](#基準位置角度などqpos0など)します。|
|armature|**型：real〈** `"0"` **〉**<br>この`<joint>`によりつくられるすべての自由度のamature inertia（またはrotor inertia / reflected intertia）を指定します。<br>一般化座標の慣性行列の対角に追加される定数であり、ローター（モータの可動部）の慣性を増幅させます。|
|damping|**型：real〈** `"0"` **〉**<br>この`<joint>`により生成されるすべての自由度に適用される減衰です。<br>制約ソルバにより計算される摩擦損失とは異なり、単に速度に線形な受動力です。減衰の値が大きいと、数値積分器が不安定になることがあります。|
|frictionloss|**型：real〈** `"0"` **〉**<br>乾性摩擦による摩擦損失です。この`<joint>`のつくるすべての自由度に対して同じ値となります。<br>正の値を設定することで有効になりますが、一応フリージョイントでも有効にすることが可能です。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>[ユーザパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#cuser)です。次元は`njnt_user`に同じです。|

> 他に制約ソルバに関するパラメタである`solreflimit`、`solimplimit`、`solreffriction`および`solimpfriction`があります。詳細は[ソルバーパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#csolver)を参照してください。

</div></details>

### body>joint詳説 : type

　以下にボディの要素として定義することのできる`<joint>`の型を示します。ただし、[概要](#概要--joint-mjcfbody)節にも記述したように、`<freejoint>`は`"free"`として定義済みの`<joint>`に等しいです。このため、下記の`"free"`の説明は`<freejoint>`にも当てはまります。

|type|自由度|説明|
|-|-|-|
|free|並進 $\times 3$ <br>回転 $\times 3$ |　他のボディに全く固定されていない、自由な「ジョイント」を定義します。このため、`<worldbody>`の子ボディでのみ定義可能です。<br>　ジョイントの位置はボディの中心に一致し、ジョイントの位置と姿勢はボディのグローバルな位置と姿勢に一致します。<br>※この`<joint>`を持つボディには、他の`<joint>`を定義することはできません。|
|ball|回転 $\times 3$ |　ボールジョイントを定義します。<br>　回転中心は上記`pos`属性に指定された点となります。<br>※この`<joint>`を持つボディには、他の回転自由度を持つ`<joint>`を定義することはできません。|
|slide|並進 $\times 1$ |　スライドジョイントを定義します。<br>　位置`pos`とスライド方向`axis`により定義されます。|
|hinge|回転 $\times 1$ |　ヒンジジョイントを定義します。<br>　位置`pos`と回転軸`axis`により定義されます。回転は指定された軸を中心に、指定された位置を通るように行われます。|

### 関節角度・角速度等の取得（`mjData.qpos`と`mjData.qvel`）

#### ジョイントタイプと取得できる値

　前述のように、関節位置/角度は`data.qpos`で、速度/角速度は`data.qvel`で取得します（`data = mujoco.MjData(model)`）。取得できるジョイントの位置/角度と速度/角速度は、ジョイントの型により以下のように変化します。

**表** 各ジョイントのタイプと`qpos`/`qvel`で取得できる値

|種類|型|説明|
|-|-|-|
|`"free"`|qpos : real(7)<br> $(x,y,z,q_1,q_2,q_3,q_4)$ <br>qvel : real(6)<br> $(v_x,v_y,v_z,\omega_x,\omega_y,\omega_z)$ |`qpos`では各軸方向の位置 $(x,y,z)$ と、クォータニオンによる姿勢表現 $(q_1,q_2,q_3,q_4)$ を取得できます。<br>`qvel`では各軸方向の速度 $(v_x,v_y,v_z)$ と、各軸方向の角速度 $(\omega_x,\omega_y,\omega_z)$ を取得できます。<br>※[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節でもふれたように、MuJoCoでの既定の姿勢表現は、1つめの要素を $\cos\frac{\theta}{2}$ とするクォータニオンです。|
|`"ball"`|qpos : real(4)<br> $(q_1,q_2,q_3,q_4)$ <br>qvel : real(3)<br> $(\omega_x,\omega_y,\omega_z)$ |`qpos`ではクォータニオンによる姿勢表現 $(q_1,q_2,q_3,q_4)$ を取得できます。<br>`qvel`では各軸方向の角速度 $(\omega_x,\omega_y,\omega_z)$ を取得できます。|
|`"slide"`|qpos : real(1)<br> $(x)$ <br>qvel : real(1)<br> $(v)$ |`qpos`では基準位置からの変位 $x$ を取得できます。<br>`qvel`では移動軸方向への移動速度 $v$ を取得できます。|
|`"hinge"`|qpos : real(1)<br> $(\theta)$ <br>qvel : real(1)<br> $(\omega)$ |`qpos`では基準位置からの変位角 $\theta$ を取得できます。**ラジアンによる表現です**。<br>`qvel`では回転軸まわりの角速度 $\omega$ を取得できます。|

|`"free"`|`"ball"`|`"slide"`|`"hinge"`|
|:-:|:-:|:-:|:-:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/b256eb40-35c1-eb41-637b-9f08e72843f1.png">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/5ce9d4a9-5c55-91f0-5d32-c2fe3c72b53c.png">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/a45d4dc1-54fc-92be-6306-830e535e4cef.png">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/52c27be4-46ed-adcd-8e5c-ed81d81f05b2.png">|

**図** 各`<joint>`と可視化時の形状．

:::note
　`mjData.qpos`などを使用する方法のほかに、[センサ](#mjcfの要素--sensor)を使用することでもジョイントの角度や角速度などを取得することが可能です。センサを使用した方法では出力値に白色ノイズをかけたり、カットオフを設定したりすることも可能です。このため、現実世界への応用を考えた最適化などの際にはこちらを使用するとよいかもしれません。
:::

#### ジョイントが複数ある場合の`qpos`、`qvel`

　`mjData.qpos`と`mjData.qvel`から取得できる値は、**すべてのジョイントの状態値を1次元の配列に格納したもの**となります。このとき、格納される順番はMJCFコード上で`<joint>`が**宣言された順**となります。

　ここでは、シンプルな腕のモデルを使用し、どのように値が取得されるのかについて見ていきます。モデルに含まれる関節について、定義順にこれを以下に示します。ここで、例えば`"hinge"`(x軸)は、x軸を回転軸とするヒンジジョイントを指すことにします。

||肩|肘(1)|肘(2)|手首(1)|手首(2)|缶|
|:-:|-|-|-|-|-|-|
|種類|`"ball"`|`"hinge"`<br>(y軸)|`"hinge"`<br>(x軸)|`"hinge"`<br>(y軸)|`"hinge"`<br>(z軸)|`"free"`|
|`qpos`要素数|4|1|1|1|1|7|
|`qvel`要素数|3|1|1|1|1|6|

<details><summary>【詳細 : MJCFモデルとコード】</summary><div>

MJCFモデルはMuJoCoドキュメントのOverviewページにあるexamle.xml[3]をベースにしています。

```xml:arm_and_tendon.xml
<mujoco model="arm-and-tendon">
  <visual>
    <scale jointlength="1" jointwidth=".05"/>
    <rgba joint="0 1 0 1"/>
  </visual>

  <default>
    <geom rgba=".8 .6 .4 .3"/>
  </default>

  <asset>
    <texture type="skybox" builtin="gradient" rgb1="1 1 1" rgb2=".6 .8 1" width="256" height="256"/>
  </asset>

  <worldbody>
    <camera name="side" pos="0 -2.3 .2" xyaxes="1 0 0 0 .02 .97"/>
    <camera name="above" pos=".2 .9 1.25" xyaxes="-1 0 0 0 -1 1.5"/>
    <light pos="0 1 1" dir="0 -1 -1" diffuse="1 1 1"/>
    <body pos="0 0 1">
      <joint type="ball"/>
      <geom type="capsule" size="0.06" fromto="0 0 0  0 0 -.4"/>
      <body pos="0 0 -0.4">
        <joint axis="0 1 0"/>
        <joint axis="1 0 0"/>
        <geom type="capsule" size="0.04" fromto="0 0 0  .3 0 0"/>
        <body pos=".3 0 0">
          <joint axis="0 1 0"/>
          <joint axis="0 0 1"/>
          <geom pos=".1 0 0" size="0.1 0.08 0.02" type="ellipsoid"/>
          <site name="end1" pos="0.2 0 0" size="0.01"/>
        </body>
      </body>
    </body>

    <body pos="0.3 0 0.1">
      <joint type="free"/>
      <geom size="0.07 0.1" type="cylinder"/>
      <site name="end2" pos="0 0 0.1" size="0.01"/>
    </body>
  </worldbody>

  <tendon>
    <spatial limited="true" range="0 0.6" width="0.005">
      <site site="end1"/>
      <site site="end2"/>
    </spatial>
  </tendon>
</mujoco>
```

```python
model = mujoco.MjModel.from_xml_path("arm_and_tendon.xml")
data = mujoco.MjData(model)
renderer = mujoco.Renderer(model, 480, 300)
mujoco.mj_forward(model, data) # 0.0 sの際にモデルを描画する用 

n_seconds = 1
fps_video = 30  # 動画自体のfps
fps_frame = 150 # 動画を1/5倍速にする
n_frames = int(n_seconds * fps_frame)
option = mujoco.MjvOption()
option.flags[mujoco.mjtVisFlag.mjVIS_JOINT] = True # ジョイントを可視化

frames = []
sim_time, qposs, qvels = np.zeros(n_frames), [], [] # matplotlibで描画する用
for i in range(n_frames):
    while data.time * fps_frame < i:
        mujoco.mj_step(model, data)
    # 描画
    renderer.update_scene(data, camera="side", scene_option=option)
    frames.append(renderer.render())
    # グラフ描画用
    sim_time[i] = data.time
    qposs.append(data.qpos.copy())
    qvels.append(data.qvel.copy())

save_video(frames, "output.mp4", fps_video)
qposs, qvels = np.stack(qposs), np.stack(qvels)
# グラフ描画...
```

</div></details>

　このモデルについて、0秒時点での状態とその挙動を描画したものを以下に示します。`<geom>`を半透明に設定しているため、すべての`<joint>`の位置と向きを見ることができます。上に述べたように、`<joint>`の定義順は肩の根本から肘、手先、缶です。また`<tendon>`も定義されていますが、これは`mjData.qpos`および`mjData.qvel`には格納されません。

|初期状態|動作図|
|-|-|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/ed35b742-bcc0-914f-8dd2-1c5f74ca7d5b.png" width=200>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/a348ae0f-162c-7b79-0b41-3bc2ef2af59b.gif" width=240>|

**図** MJCFモデル（左）0秒時点（右）1秒経過時点までの挙動．半透明な部分が`<geom>`であり、緑の部分は`<joint>`を可視化したもの

![mujoco_腕と腱と缶？_関節角度.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/3a6ceb15-c7c8-4e63-77ca-d16f34650fff.png)


**図** 1秒経過時点までの各関節の角度（左）肩関節のクォータニオン（中）肘関節の角度（右）手首関節の角度

　さて、1秒経過時点での`mjData.qpos`と`mjData.qvel`に格納された値は次のようになっています。（見やすさのために値の切り捨てを行っています。また上の記述も含めて、正確には1秒までの描画は行っていません）

```python
data.time # 0.994
data.qpos # [ 0.995  0.     0.094  0.     1.523   0.
          #   0.187  0.    -0.458  0.    -0.522  -0.999
          #   0.    -0.010  0.     ]
data.qvel # [ 0.     1.077  0.    -2.250  0.      2.201
          #   0.    -0.854  0.    -1.174  0.     23.316
          #   0.    ]
```

qposの方は要素数15の1次元配列に、qvelの方は要素数13の1次元配列となっていることがわかると思います。また、qposの最初の4要素は肩のクォータニオン、5～6番目は肘、7～8番目は手首、残りは缶の位置姿勢となっています。

　このように、`mjData.qpos`と`mjData.qvel`の結果はすべての結果を結合したものですので、**実際に使用する際にはこれを分割する**ことになります（例えば肘関節の角度は`data.qpos[4:6]`）。

#### 基準位置/角度など（`qpos0`など）

　ジョイントには２種類の基準位置/角度を設定することができます。一つが`ref`で設定する**関節の基準位置/角度**であり、関節位置/角度を $0$ とする点を決定します。もう一つが`springref`で設定する**関節のばねが平衡となる関節位置/角度**であり、すべての関節と腱のバネが静止した長さを達成する点を決定します。

　はじめに`ref`による基準位置/角度について記述します。[先述](#ジョイントが複数ある場合のqposqvel)の腕のモデルの場合、肘が90°曲がった状態で作成されていますが、デフォルトではこの状態が基準（数値 $0$ ）として扱われます。本来は肘が伸びた状態を $0$ として設定したいので、初期構成が90°に対応するように、ジョイントのref属性を使用して指定することができます。

<details><summary>【refによる基準位置/角度変更の詳細と具体例】</summary><div>

　`ref`の値を変更すると、実行時に関節位置ベクトルは基準ポーズに対して相対的に解釈されます。特に、ジョイントによって適用される空間変換の量は、mjData.qpos - mjModel.qpos0です。`ref`**属性はスカラージョイント**（**スライドとヒンジ**）**にのみ適用されます**。ボールジョイントの場合、`mjModel.qpos0`に保存されるクォータニオンは常に $(1,0,0,0)$ で、これはヌル回転に対応します。フリージョイントの場合、フローティングボディの初期グローバル3D位置とクォータニオンが`mjModel.qpos0`に保存されます。

　以下は肘を伸ばした状態を $0$ とし、肘を曲げる方向を正方向とした腕のモデルです。肘のY軸ジョイントの向きを逆転させ、`ref`を $90$ に設定しています。以下のモデルを使用すると、0秒時点での`mjData.qpos`が $1.571\space\mathrm{rad}$ となっており、また肘を伸ばした状態で $0\space\mathrm{rad}$ が出力されるようになっています（`mjModel.qpos0`は約 $1.57$ ）。`ref`が規定値の場合の`mjData.qpos`の値は[前節]((#ジョイントが複数ある場合のqposqvel))のグラフを参照して下さい。

```xml:arm_and_tendon_v2.xml
<mujoco model="arm-and-tendon">
  <default>
    <geom rgba=".8 .6 .4 1"/>
  </default>

  <asset>
    <texture type="skybox" builtin="gradient" rgb1="1 1 1" rgb2=".6 .8 1" width="256" height="256"/>
  </asset>

  <worldbody>
    <camera name="side" pos="0 -2.3 .2" xyaxes="1 0 0 0 .02 .97"/>
    <light pos="0 1 1" dir="0 -1 -1" diffuse="1 1 1"/>
    <body pos="0 0 1">
      <joint type="ball"/>
      <geom type="capsule" size="0.06" fromto="0 0 0  0 0 -.4"/>
      <body pos="0 0 -0.4">
        <joint axis="0 -1 0" ref="90"/>
        <joint axis="1 0 0"/>
        <geom type="capsule" size="0.04" fromto="0 0 0  .3 0 0"/>
        <body pos=".3 0 0">
          <joint axis="0 1 0"/>
          <joint axis="0 0 1"/>
          <geom pos=".1 0 0" size="0.1 0.08 0.02" type="ellipsoid"/>
        </body>
      </body>
    </body>
  </worldbody>
</mujoco>
```

</div></details>

　次に`springref`による基準位置/角度について説明します。この属性ではすべての関節と腱のスプリングが静止した長さになる状態を指定します。バネの力は関節の構成がバネの基準状態から外れたときに発生し、外れた量に対して線形になります。基準状態は`mjModel.qpos_spring`に保存されます。`"slide"`と`"hinge"`については`springref`を設定可能ですが、`"ball"`とフリージョイントについてはこの値はモデルの初期設定に対応します。

## MJCFの要素 : body

### 概要 : body (MJCF)

　MJCFモデルにおいて`<body>`は主要素であり、[キネマティックツリー](#キネマティックツリー)を構成するために使用されます。**最上位の要素**`<worldbody>`と、**それより下の要素**`<body>`に分類されます。

　ボディは**質量と慣性特性を持ち**ますが、幾何学的特性は持ちません（幾何学的特性はボディに取り付けた`<geom>`から決定されます）。また、**各ボディはそれぞれ２つの座標系を持ちます**。一つはボディの定義と他の要素の相対位置を決定する座標系であり、もう一つは慣性フレームです。慣性フレームについては[inertial](#mjcfの要素--inertial-body)節を参照してください。

　各時間ステップで、MuJoCoは順運動学（forward kinematics）を再帰的に計算し、グローバルなデカルト座標（global Cartesian coordinates）ですべての体の位置と向きを得ます。これらは、以降のすべての計算の基礎となります。

### 属性 : body (MJCF)

　下表に`<body>`および`<worldbody>`で設定可能なすべての属性を示します。

**表** すべての属性（`<body>`および`<worldbody>`）

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。<br>`<worldbody>`では既定で`"world"`です。|
|childclass|**型：string〈** optional **〉**<br>すべての子孫要素に適用する`<default>`のクラス名を指定します。ただし、子孫が独自にクラス名を指定した場合には適用外となります。詳しくは[default](#mjcfの要素--default)節を参照してください。|
|mocap|**型：選択式〈** `"false"` **〉**<br>`["false","true"]`から選択<br>`<body>`をmocapボディとして扱うかを指定します。<br>　この属性が`"true"`に設定されたボディは、`mjData.mocap_pos`および`mjData.mocap_quat`から位置と姿勢を変更することが可能になります。また、インタラクティブビジュアライザーのマウス操作でボディを動かすことも可能となります。[flag](#mjcfの要素--option)の`mocap`属性も参照してください。<br>ジョイントを持たない`<worldbody>`の子ボディにのみ設定可能です。|
|pos|**型：real(3)〈** optional **〉**<br>`<body>`のローカル座標の原点位置を指定します。指定されない場合、`"0 0 0"`（親ローカル座標の原点位置）に設定されます。|
|quat<br>axisangle<br>euler<br>xyaxes<br>zaxis|**型：real(n)〈** `"1 0 0 0"` - (`quat`) **〉**<br>`<body>`のローカル座標の姿勢を指定します。[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節を参照してください。|
|gravcomp|**型：real〈** `"0"` **〉**<br>`<body>`の重量に対する重力補正力の割合を指定します。<br>重力補正力は`<body>`の重心に働きます。<br>`"1"`に設定した場合、重量に等しい上向きの力が働き重力と打消しあいます。<br>`"1"`より大きい値の場合、浮力を生み出します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nbody_user`と同じ要素数を持ちます。[ユーザーパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#cuser)を参照してください。|

### MJCFの要素 : inertial (body)

　`<body>`に対して`<inertial>`要素を定義することで、**明示的にボディの質量と慣性特性を指定**することができます。この要素が指定されていないボディの質量および慣性特性は、定義された`<geom>`から自動的に計算されます。また、この要素は一つの`<body>`に対して**最大一つまで定義可能**です。

下表に`<inertial>`で設定可能なすべての属性を示します。

**表** すべての属性（`<inertial>`）

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|pos|**型：real(3)〈** required **〉**<br>慣性フレームの原点位置を指定します。<br>慣性フレームの定義から、`pos`は質量中心位置でもあります。<br>`<inertial>`要素を定義すると自動推論が無効化されるため、`<geom>`から推測可能な場合でもこの属性は必須です。|
|quat<br>axisangle<br>euler<br>xyaxes<br>zaxis|**型：real(n)〈** `"1 0 0 0"` - (`quat`) **〉**<br>慣性特性のための座標系の向きを指定します。<br>[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節を参照してください。|
|mass|**型：real〈** required **〉**<br>`<body>`の質量を指定します。**負の値を指定することはできません**。<br>MJCFでは複数のジョイントを設定するためのダミーボディは不要であり、また接合のためには`<site>`要素が存在します。このため、MJCFにおいて**質量が０の**`<body>`**を定義する意味はありません**。|
|diaginertia|**型：real(3): $(I_{11},I_{22},I_{33})$ 〈** optional **〉**<br>対角慣性行列で、慣性フレームに対するボディの慣性を指定します。<br>対角慣性行列 $I$ の対角要素 $I_{11},I_{22},I_{33}$ を指定します(?)。**この属性または次の属性のどちらかについて、必ず定義する必要があります**。|
|fullinertia|**型：real(6): $(I_{11},I_{22},I_{33},$ $I_{12},I_{13},I_{23})$ 〈** optional **〉**<br>慣性行列の対角要素 $I_{11},I_{22},I_{33}$ および右上の要素 $I_{12},I_{13},I_{23}$ を指定します。<br>コンパイル時に慣性行列 $I$ から慣性フレームの向きと対角慣性をが計算されます。**慣性行列** $I$ **が正定値でない場合、エラーが発生**します。|


<details><summary>【慣性フレームと慣性行列について】</summary><div>

　何らかの座標系に対して、 $x,y,z$ 軸周りの**慣性モーメント** $I_{xx},I_{yy},I_{zz}$ と、**慣性乗積**(product of inertia)モーメント $I_{xy},I_{xz},I_{yz}$ を用いて、**慣性行列** $I$ は次のように表されます。

```math
I=\left[\begin{array}{ccc}
  I_{xx}&I_{xy}&I_{xz}\\
  I_{xy}&I_{yy}&I_{yz}\\
  I_{xz}&I_{yz}&I_{zz}
\end{array}\right]
```

　ここで、例えば $I_{xx}=\int_V \rho(y^2+z^2)dV$ であり、 $I_{xy}=-\int_V \rho xy dV$ です。上から、慣性行列は**対称行列**であることがわかります。このため、`fullinertia`属性では対角要素 $I_{xx},I_{yy},I_{zz}$ と右上の要素 $I_{xy},I_{xz},I_{yz}$ を指定することで完全な慣性行列を指定しています。

　座標系の取り方によっては慣性行列 $I$ が対角行列となることがあります。このような座標系を**慣性フレーム**（inertia frame; 原点が質量中心位置に一致し、系の軸がボディの慣性主軸に一致する）といいます。このため、`diaginertia`属性では対角要素 $I_{xx},I_{yy},I_{zz}$ により対角慣性行列を指定しています。

```math
I=\begin{bmatrix}
  I_{xx}&0&0\\
  0&I_{yy}&0\\
  0&0&I_{zz}
\end{bmatrix}
```

　ですから、`diaginertia`属性で慣性行列を指定した場合、`pos`と`quat`等から指定した**座標系は慣性フレームとして扱われ**（ると思われ）ます。一方、`fullinertia`属性で慣性行列を指定した場合、`pos`と`quat`等から指定した座標系は慣性フレームとは別のものとして扱われ、**コンパイラが別途慣性フレームを計算する**ことになります。

</div></details>

　[geomの概要](#概要--geom-mjcfbody)でも書きましたが、`<inertial>`を使用すると、次のように`<geom>`を一切含まないボディを定義することも可能になります。…あんまり用途が思いつかないですが、`<geom>`を含まないボディについて接触力の計算を省略することで、計算速度を向上させたりするんでしょうか？

```xml
<mujoco>
  <worldbody>
    <body pos="0 0 0" name="mybody">
      <inertial pos="0 0 0" mass="1" diaginertia=".01 .01 .01"/>
      <joint pos="0 0 -.8"/>
    </body>
  </worldbody>
</mujoco>
```

## MJCFの要素 : camera (body)

### 概要 : camera (MJCF/body)

　`<camrea>`要素はレンダリングを行うために用いられるカメラを作成します（定義しない場合にもカメラ自体は存在します）。カメラは主に**フレームの原点位置と姿勢の2点を調整**することになります。下図に示すように、カメラのフレームの原点はレンダリング画像の中央に位置し、右方向がX軸、上方向がy軸となります。また、**カメラの方向はZ軸の負の向き**であることに注意してください。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/65a8a4e2-135b-a947-a449-66e131a6605e.png" width=200>

**図** レンダリング結果と`<camera>`のフレーム．中央に示した座標系は`<camera>`のフレームのもの

不完全な光学系を持つ実際のカメラをモデリングする場合、水平方向と垂直方向に別々の焦点距離を指定したり、非中心主点を指定したりすることが可能です。

> 明示的に追加せずに使用できるカメラは、GUIアプリケーションでマウスを使用して自由に動かすことができます。GUIアプリケーションについては[インストール](#インストール)節を参照してください。

### 属性 : camera (MJCF/body)

　下表に`<camera>`の代表的な属性を示します。すべての属性については、その下の「使用可能な全属性の一覧」を参照してください。

**表** 代表的な属性（`<camera>`）

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|mode|**型：選択式〈** `"fixed"` **〉**<br>`["fixed","track","trackcom","targetbody","targetbodycom"]`から選択<br>カメラが何を見るかを指定します。<br>より正確には、ワールド座標における`<camera>`の位置と姿勢が、順運動学でどのように計算されるかを指定します。[後述](#bodycamera詳説--mode)します。|
|target|**型：string〈** optional **〉**<br>カメラのターゲットとなるボディの`name`属性を指定します。<br>`mode`が`"targetbody"`か`"targetbodycom"`の場合に必要です。|
|fovy|**型：real〈** `"45"` **〉**<br>カメラの垂直方向の視野を指定します。optionにかかわらず、**単位は度**です。<br>水平方向の視野はこの値と画面サイズから自動的に計算されます。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>カメラのフレームの原点位置を指定します。|
|quat<br>axisangle<br>euler<br>xyaxes<br>zaxis|**型：real(n)〈** `"1 0 0 0"` - (`quat`) **〉**<br>`<camera>`のローカル座標の姿勢を指定します。[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節を参照してください。<br>**カメラの場合は**`xyaxes`**が特に便利**であり、レンダリング画面の横軸と縦軸をそれぞれ直接指定することが可能です。|

<details><summary>【使用可能な全属性の一覧】</summary><div>

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|mode|**型：選択式〈** `"fixed"` **〉**<br>`["fixed","track","trackcom","targetbody","targetbodycom"]`から選択<br>カメラが何を見るかを指定します。<br>より正確には、ワールド座標における`<camera>`の位置と姿勢が、順運動学でどのように計算されるかを指定します。[後述](#bodycamera詳説--mode)します。|
|target|**型：string〈** optional **〉**<br>カメラのターゲットとなるボディの`name`属性を指定します。<br>`mode`が`"targetbody"`か`"targetbodycom"`の場合に必要です。|
|fovy|**型：real〈** `"45"` **〉**<br>カメラの垂直方向の視野を指定します。optionにかかわらず、**単位は度**です。<br>水平方向の視野はこの値と画面サイズから自動的に計算されます。|
|resolution|**型：int(2): $(res_w,res_h)$ 〈** `"1 1"`**〉**<br>ピクセル単位でのカメラの解像度です。<br>これらの値はレンダリングには使用されません。レンダリングコンテキストを作成するときに必要な解像度を保存します。|
|focal|**型：real(2)〈** `"0 0"` **〉**<br>長さ単位でのカメラの焦点距離です。<br>`fovy`属性と競合します。詳細は[カメラ](https://mujoco.readthedocs.io/en/stable/modeling.html#ccamera)を参照してください。|
|focalpixel|**型：int(2)〈** `"0 0"` **〉**<br>ピクセル単位でのカメラの焦点距離です。<br>`fovy`属性と競合します。また`focal`と`focalpixel`の両方が指定された場合、`focalpixel`の値が使用されます。|
|principal|**型：real(2)〈** `"0 0"` **〉**<br>長さ単位でのカメラの主点（principal point）です。<br>`fovy`属性と競合します。|
|principalpixel|**型：real(2)〈** `"0 0"` **〉**<br>ピクセル単位でのカメラの主点です。<br>`fovy`属性と競合します。また`principal`と`principalpixel`の両方が指定された場合、`principalpixel`の値が使用されます。|
|sensorsize|**型：real(2)〈** `"0 0"` **〉**<br>長さ単位でのカメラセンサのサイズです。<br>`fovy`属性と競合します。またこの属性を指定した場合、`resolution`と`focal`を必ず指定する必要があります。|
|ipd|**型：real〈** `"0.068"` **〉**<br>瞳孔間距離（Inter-pupilary distance）です。<br>ステレオコピックレンダリングの際のみ有効となります。距離はX軸方向に沿って、原点から指定した距離÷2だけ左右に離れます。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>カメラのフレームの原点位置を指定します。|
|quat<br>axisangle<br>euler<br>xyaxes<br>zaxis|**型：real(n)〈** `"1 0 0 0"` - (`quat`) **〉**<br>`<camera>`のローカル座標の姿勢を指定します。[位置と姿勢の指定](#位置と姿勢の指定-mjcf)節を参照してください。<br>カメラの場合は`xyaxes`が特に便利であり、レンダリング画面の横軸と縦軸をそれぞれ指定することができます。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_cam`と同じ要素数を持ちます。[ユーザーパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#cuser)を参照してください。|

</div></details>

### body>camera詳説 : mode

　以下に`<camrea>`の`mode`属性として定義できるすべてのモードについて示します。

|mode|説明|
|-|-|
|fixed|　`<camera>`の定義されているボディに対して、`pos`と`xyaxes`等が固定されるモードです。|
|track|　カメラの位置がワールド座標でボディからある程度オフセットされ、姿勢はワールド座標上で固定されるモードです。<br>　例えばカメラをボディの上に配置してボディの移動や回転に合わせてカメラを追尾させたときに、カメラの向きを一定にするために使用することができます。|
|trackcom|　`track`にほぼ同じですが、オフセットが`<camera>`の定義されているボディの重心を基準として行われるモードです。<br>`<worldbody>`に定義した場合にはモデル全体の質量中心を基準とするため、モデル全体を追尾することが可能です。|
|targetbody|　カメラ位置がボディに固定され、カメラの向きが常に`target`で指定したボディに向くモードです。<br>　動く物体を定点から観測する際に役立ちます。|
|targetbodycom|　`targetbody`にほぼ同じですが、カメラがターゲットの重心を向くモードです。|

## MJCFの要素 : light (body)

### 概要 : light (MJCF/body)

　`<light>`要素はライトを作成します。**定義されたボディに合わせて動く**ため、固定するためには`<worldbody>`で定義する必要があります。またライトの効果は**可算型**であり、追加するほどシーンが明るくなります。ただし、同時に有効化できるのは最大8個までです。

　ライトの種類にはスポットライトと指向性ライトの2種類があります。**スポットライト**（`directional="false"`）は`pos`点を中心に全方向に光を放射するライトであり、既定ではこちらに設定されています。**指向性ライト**（`directional="true"`）は`dir`で指定された方向へ前面に光を放つライトであり、太陽のような光です。後者でも`pos`は指定可能ですが、実質無意味です。

|ライト|なし|スポット|指向性<br> $(0,0,-1)$ |指向性<br> $(1,0,-1)$ |
|-|:-:|:-:|:-:|:-:|
||<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/a8162526-ab36-5660-c338-3ebc97d97be5.png" width=140>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/3d397ef1-a80d-39a6-ebd6-645a3079069f.png" width=140>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/b607e4ba-a7e6-5685-89b6-eeb8327c874b.png" width=140>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/811807cb-7111-85c7-42ab-76a93338adff.png" width=140>|

**図** ライトの種類と光・影の形状．上方向が $z$ 軸、右方向が $x$ 軸、原点は球の下の黒点．平面は $z=0$ ．ライト位置は $(0,0,3)$ 、球は半径 $0.25$ で中心位置 $(0,0,0.5)$ . 指向性の欄の数字は光の指向性のベクトル

### 属性 : light (MJCF/body)

　下表に`<camera>`の代表的な属性を示します。すべての属性については、その下の「使用可能な全属性の一覧」を参照してください。

**表** 代表的な属性（`<camera>`）

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|mode|**型：選択式〈** `"fixed"` **〉**<br>`["fixed","track","trackcom","targetbody","targetbodycom"]`から選択<br>ワールド座標系におけるライトの位置と姿勢が、順運動学でどのように計算されるかを指定します。カメラの`mode`属性と同様です。[mode属性についての節](#bodycamera詳説--mode)を参照してください。|
|target|**型：string〈** optional **〉**<br>追尾ターゲットとなるボディの`name`属性を指定します。<br>`mode`が`"targetbody"`か`"targetbodycom"`の場合に必要です。|
|directional|**型：選択式〈** `"false"` **〉**<br>`["false","true"]`から選択<br>`"true"`の場合、光は指向性を持ちます。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>ライトの位置を定義します。<br>スポットライトの場合、この点を中心に光が放射されます。|
|dir|**型：real(3)〈** `"0 0 -1"` **〉**<br>ライトの方向ベクトル $(x,y,z)$ です。|

<details><summary>【使用可能な全属性の一覧】</summary><div>

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|mode|**型：選択式〈** `"fixed"` **〉**<br>`["fixed","track","trackcom","targetbody","targetbodycom"]`から選択<br>ワールド座標系におけるライトの位置と姿勢が、順運動学でどのように計算されるかを指定します。カメラの`mode`属性と同様です。[mode属性についての節](#bodycamera詳説--mode)を参照してください。|
|target|**型：string〈** optional **〉**<br>追尾ターゲットとなるボディの`name`属性を指定します。<br>`mode`が`"targetbody"`か`"targetbodycom"`の場合に必要です。|
|directional|**型：選択式〈** `"false"` **〉**<br>`["false","true"]`から選択<br>`"true"`の場合、光は指向性を持ちます。|
|castshadow|**型：選択式〈** `"true"` **〉**<br>`["false","true"]`から選択<br>物体の影を描画するかをしていします。<br>物体に光が当たった際に影を作るのはライト側の機能ですが、これはレンダリングの手間が増えるため使用には注意が必要です。<br>スポットライトの場合は錐状に影が広がり、一方指向性ライトの場合は影の範囲は拡大しません。|
|active|**型：選択式〈** `"true"` **〉**<br>`["false","true"]`から選択<br>ライトのオンオフを設定します。<br>実行時にライトのオンオフを切り替えるために使用できます。|
|pos|**型：real(3)〈** `"0 0 0"` **〉**<br>ライトの位置を定義します。<br>スポットライトの場合、この点を中心に光が放射されます。|
|dir|**型：real(3)〈** `"0 0 -1"` **〉**<br>ライトの方向ベクトル $(x,y,z)$ です。|
|attenuation|**型：real(3)〈** `"1 0 0"` **〉**<br>OpenGLの定数減数係数、線形減衰係数、二次減衰係数です。<br>デフォルトでは減衰なしです。|
|cutoff|**型：real〈** `"45"` **〉**<br>スポットライトのカットオフ角度です。<br>optionに関わらず**単位は度**です。|
|exponent|**型：real〈** `"10"` **〉**<br>スポットライトの指数です。<br>カットオフの柔らかさをコントロールします。|
|ambient|**型：real(3)〈** `"0 0 0"` **〉**<br>スポットライトの周囲の色です。|
|diffuse|**型：real(3)〈** `"0.7 0.7 0.7"` **〉**<br>光の拡散色です。|
|specular|**型：real(3)〈** `"0.3 0.3 0.3"` **〉**<br>ライトの鏡面反射色です。|


</div></details>

## MJCFの要素 : sensor

### 概要 : sensor (MJCF)

　MuJoCoでは加速度センサやジャイロセンサなど、約40種類のセンサをシミュレートすることが可能です。加えて、コールバック関数を使用することで、ユーザ定義のセンサを作成することも可能です。また、いずれのセンサの値も内部計算では使用されません。

　MJCFでは、センサーは親要素の`<sensor>`と、子要素（`<accelerometer>`や`<gyro>`などなど）から構成されます。ただし、`<sensor>`要素は以下の下位要素をまとめる役割しか持たず（グルーピング要素）、センサの具象は子要素で定義することになります。

　自由落下する物体と、ワールド座標系にそれぞれ固定したセンサを題材にして、以下にコード例を示します。Pythonでは**センサデータの取得を**`mjData.sensor("sensorname").data`**から行う**ことができます。`np.ndarray`として返されますが、ミュータブルなオブジェクトですので`.copy()`して使用することをお勧めします。また、C++ネイティブな方法として`mjData.sensordata`を使用することも可能ですが、その利便性は大きく劣ります。

:::note
`mjData.sensor("sensorname").data`は1次元の値についても（`float`などではなく）`np.ndarray`として返します。
:::

　また、加速度センサなどのセンサは**ローカル座標系の値を取得**します。ワールド座標系のセンサ`worldacc`とローカル座標系のセンサ`inneracc`を比較するとこれがわかりやすいです。このようなセンサの場合、センサを接続する対象も重要となります。

```xml:two_acc_sensors.xml
<mujoco>
  <worldbody>
    <body pos="0 0 0">
      <freejoint/>
      <geom size="1"/>
      <site name="inner"/>
    </body>
    <site name="world"/>
  </worldbody>

  <sensor>
    <accelerometer name="inneracc" site="inner"/>
    <accelerometer name="worldacc" site="world"/>
  </sensor>
</mujoco>
```

```python
model = mujoco.MjModel.from_xml_path("two_acc_sensors.xml")
data = mujoco.MjData(model)
mujoco.mj_forward(model, data)

# pythonでのセンサデータ取得
print(data.sensor('inneracc').data) # [0. 0. 0.]
print(data.sensor('worldacc').data) # [0. 0. 9.81]
# C++ベースのセンサデータ取得
print(data.sensordata)              # [0. 0. 0. 0. 0. 9.81]
```

### 属性 : sensor (MJCF)

　上述のように`<sensor>`要素は以下の下位要素をまとめる役割しか持たないため、定義可能な属性が存在しません。具象は`<accelerometer>`や`<gyro>`などの子要素で定義しますが、その種類は膨大であり、またその属性も様々です。このうち、全てのセンサ型に共通の属性は以下の4つです。

**表** すべてのセンサ型に共通の属性

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|noise|**型：real〈** `"0"` **〉**<br>センサ出力に付加されるゼロ平均ガウスノイズの標準偏差です。<br>ノイズの乗ったセンサデータもその型は変化しません。例えばクォータニオンや単位ベクトルは正規化されたままですし、非負数は非負のままです。|
|cutoff|**型：real〈** `"0"` **〉**<br>この値が正の場合、センサ出力の絶対値を制限します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_sensor`と同じ要素数を持ちます。[ユーザーパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#cuser)を参照してください。|


　また、**各センサは接続可能な対象が限定されています**。センサにより、接続可能な対象が`<site>`、`<body>`、`<joint>`、`<tendon>`または`<actuator>`のいずれか1種類に限定されるものと、いくつかの対象に接続可能なものが存在します。代表的なものについては下表に記述しますが、これらの詳細は[すべてのセンサ](#使用可能なすべてのセンサの一覧)節を参照してください。また、全センサは合計40種類ありますが、それらすべてについても[すべてのセンサ](#使用可能なすべてのセンサの一覧)節を参照してください。

- 接続対象が`<site>`のもの（[センサ (site)](#siteにのみ接続可能なセンサ)を参照）
  - touch : タッチセンサ
  - accelerometer : ３軸加速度センサ
  - velocimeter : ３軸速度センサ
  - gyro : ３軸角速度センサ
  - rangefinder : 距離センサ
  - camprojection : 光学式モーションキャプチャ（のマーカー、カメラは`<camera>`を使用）
- 接続対象が`<joint>`のもの（[センサ (joint)](#jointにのみ接続可能なセンサ)）
  - jointpos : ジョイントの位置/角度センサ（`"hinge"`と`"slide"`対象）
  - jointvel : ジョイントの速度/角速度センサ（`"hinge"`と`"slide"`対象）
- そのほかのセンサ
  - clock : シミュレータ上の時刻を出力するセンサ

:::note
　いくつかのセンサは`mjData`から取得可能な値を測定値として出力します。そのような測定量に対してもわざわざ`<sensor>`要素を利用する理由としては、`noise`で簡単に白色ノイズをかけられる点や、`cutoff`を設定できる点なのかな、と思います。
:::

> MuJoCo にはオフスクリーンレンダリング機能があり、カラーカメラと深度カメラの両方のセンサをシミュレートすることができます。これは標準のセンサーモデルには含まれておらず、コードサンプル[simulate.cc](https://mujoco.readthedocs.io/en/stable/programming/samples.html#sasimulate)に示されているように、プログラムで行う必要があります。

### 使用可能なすべてのセンサの一覧

<details><summary>【使用可能なセンサ型の一覧（対象:site）】</summary><div>

#### siteにのみ接続可能なセンサ <!-- omit in toc -->

下表に、本項目のすべてのセンサが持つ属性を示します。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|noise|**型：real〈** `"0"` **〉**<br>センサ出力に付加されるゼロ平均ガウスノイズの標準偏差です。<br>ノイズの乗ったセンサデータもそのデータ型や値の範囲は変化しません。例えば正の値だけを返すセンサであれば、ノイズ付与後も正の値を返します。|
|cutoff|**型：real〈** `"0"` **〉**<br>この値が正の場合、センサ出力の絶対値を制限します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_sensor`と同じ要素数を持ちます。|
|site|**型：string〈** required **〉**<br>センサを接続する`<site>`の名前を指定します。|

　`<site>`にのみ接続可能なセンサは以下の9つです。説明欄に「固有の属性」が記述されているセンサ型については、上の属性に加えてその属性を指定する必要があります。

|センサ|戻り値の型|説明|
|-|-|-|
|touch|real|タッチセンサです。<br>センサーの感知領域は`<site>`の体積内であり、その中で生じた接触力(法線力)の合計値[N]を取得します。**測定値は非負のスカラー**です。詳しくは[後述](#詳説--タッチセンサtouch)します。<br>💡**MuJoCoでは面接触は存在しない**ため、`<site>`の**体積/面積はある程度大きくする必要があります**。こちらも[後述](#詳説--タッチセンサtouch)します。|
|accelerometer|real(3)<br> $(a_x,a_y,a_z)$ |３軸加速度センサです<br>**取り付けた**`<site>`**のローカル座標系に基づき**直線加速度 $(a_x,a_y,a_z)$ を出力します。測定値には**重力を含みます**。<br>センサの取り付け位置は`<site>`の中心です。|
|velocimeter|real(3)<br> $(v_x,v_y,v_z)$ |３軸速度センサです。<br>**取り付けた**`<site>`**のローカル座標系に基づき**直線速度 $(v_x,v_y,v_z)$ を出力します。<br>センサの取り付け位置は`<site>`の中心です。|
|gyro|real(3)<br> $(\omega_x,\omega_y,\omega_z)$ |３軸角速度センサ（ジャイロセンサ）です。<br>**取り付けた**`<site>`**のローカル座標系に基づき**角速度 $(\omega_x,\omega_y,\omega_z)$ を出力します。<br>センサの取り付け位置は`<site>`の中心です。<br>💡**慣性計測ユニット**(**IMU**)をシミュレートするため、同じ`<site>`に取り付けられた加速度計とよく組み合わされて使用されます。|
|force|real(3)<br> $(f_x,f_y,f_z)$ |３軸力センサです。<br>取り付けた`<site>`のローカル座標に基づき、子ボディ-親ボディ間の相互作用力 $(f_x,f_y,f_z)$ を出力します。子から親の方向の力を正とし、親ボディに取り付けた場合の符号は逆になります。詳細は[後述](#詳説--力センサ・トルクセンサforce-torque)します。<br>センサの取り付け位置は`<site>`の中心です。|
|torque|real(3)<br> $(\tau_x,\tau_y,\tau_z)$ |３軸トルクセンサです。<br>取り付けた`<site>`のローカル座標系に基づきトルクを測定します。詳細は`force`に同じです。<br>💡既定では**ねじり摩擦と転がり摩擦が計算されない**ため、常に出力は`[0 0 0]`となります。有効にするためには対象の`<geom>`の`condim`属性を４以上に設定してください。|
|magnetometer|real(3) <br> $(\Phi_x,\Phi_y,\Phi_z)$ |３軸磁気センサです。<br>取り付けた`<site>`のローカル座標系に基づき、磁束[Wb]を測定します。<br>センサの取り付け位置は`<site>`の中心です。|
|rangefinder|real|距離センサです。<br>最も近い`<geom>`との距離を測定します。**測定方向は**`<site>`**のZ軸正方向**です。測定方向に`<geom>`が**存在しない場合は** $-1$ を出力します。<br>`<site>`中心が`<geom>`内に入り込んだ場合はその`<geom>`の表面までの距離を出力します（`<geom>`内部を密なものとしては測定しない）。ただし、センサの`<site>`と**同じボディの**`<geom>`**は測定対象から除外**されます。|
|camprojection|real(2)<br> $(x_{res},y_{res})$ |カメラ画像上の`<site>`の位置を取得できるセンサです。光学式モーションキャプチャのいちカメラのようなものです。<br>カメラ上での`<site>`の位置 $(x_{res},y_{res})$ を出力します。原点 $(0,0)$ はカメラ画像左上であり、`<site>`がカメラ外の場合も値は測定されます。詳細は[後述](#詳説--光学式モーションキャプチャのマーカー-camprojection)します。<br>固有の属性<br>- `site` : モーションキャプチャのマーカーです。 <br>- `camera` : 使用する`<camera>`を指定します。測定値は当該カメラの`resolution`属性に依存します。|

</div></details>

<details><summary>【使用可能なセンサ型の一覧（対象 : body）】</summary><div>

#### bodyにのみ接続可能なセンサ <!-- omit in toc -->

下表に、本項目のすべてのセンサが持つ属性を示します。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|noise|**型：real〈** `"0"` **〉**<br>センサ出力に付加されるゼロ平均ガウスノイズの標準偏差です。<br>ノイズの乗ったセンサデータもその型は変化しません。例えば正の値だけを返すセンサであれば、ノイズ付与後も正の値を返します。|
|cutoff|**型：real〈** `"0"` **〉**<br>この値が正の場合、センサ出力の絶対値を制限します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_sensor`と同じ要素数を持ちます。|
|body|**型：string〈** required **〉**<br>センサを接続する`<body>`の名前を指定します。ただし、当該ボディは運動学的サブツリー（kinematic subtree）のルートボディである必要があります（指定したボディの先のツリー要素を計測対象とする）。|

`<body>`にのみ接続可能なセンサは以下の3つです。

|センサ|戻り値の型|説明|
|-|-|-|
|subtreecom|real(3)|指定されたボディをルートボディとするkinematic subtreeの重心を、グローバル座標のもとで取得します。|
|subtreelinvel|real(3)|指定されたボディをルートボディとするkinematic subtreeの重心速度を、グローバル座標のもとで取得します。|
|subtreeangmom|real(3)|指定されたボディをルートボディとするkinematic subtreeの重心まわり角運動量を、グローバル座標のもとで取得します。|

</div></details>

<details><summary>【使用可能なセンサ型の一覧（対象 : joint）】</summary><div>

#### jointにのみ接続可能なセンサ <!-- omit in toc -->

下表に、本項目のすべてのセンサが持つ属性を示します。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|noise|**型：real〈** `"0"` **〉**<br>センサ出力に付加されるゼロ平均ガウスノイズの標準偏差です。<br>ノイズの乗ったセンサデータもその型は変化しません。例えば正の値だけを返すセンサであれば、ノイズ付与後も正の値を返します。|
|cutoff|**型：real〈** `"0"` **〉**<br>この値が正の場合、センサ出力の絶対値を制限します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_sensor`と同じ要素数を持ちます。|
|joint|**型：string〈** required **〉**<br>センサを接続する`<joint>`の名前を指定します。<br>`jointlimit~`系を除き取り付け可能なジョイント型には制限があります。また、すべての測定値は`mjData.qpos`や`mjData.qvel`などからのコピー、またはその合成値です。|

`<joint>`にのみ接続可能なセンサは以下の8つです。

|センサ|戻り値の型|説明|
|-|-|-|
|jointpos|real|ジョイントの位置/角度を計測するセンサです。スカラージョイント（`"slide"`/`"hinge"`）にのみ接続可能です。|
|jointvel|real|ジョイントの速度/角速度を計測するセンサです。スカラージョイント（`"slide"`/`"hinge"`）にのみ接続可能です。|
|jointactuatorfrc|real|力センサです。ジョイントに付加された**アクチュエータによる一般化力**を測定します。重力等は考慮されず、また複数のアクチュエータが動作に関与する場合でも単一のスカラー量を取得します。<br>スカラージョイント（`"slide"`/`"hinge"`）にのみ接続可能です。|
|ballquat|real(4)|ボールジョイントの姿勢をクォータニオンで測定するセンサです。|
|ballangvel|real(3)|ボールジョイントの角速度センサです。各軸回りの角速度[rad/s]を返します。|
|jointlimitpos|real|ジョイントの制限（`range`）に違反した量を取得します（取得される値は`mjData.efc_pos - mjData.efc_margin`に等しい）。<br>`range`では下限値と上限値の2つの値を設定しますが、ジョイントの状態が`range`内のとき測定値は $0$ となります。どちらかに違反した場合、その超過量を負数として取得します。また、両方同時に違反した場合は、最初の値に対する超過量を取得します。<br>基本的に超過量はごくわずか（例 : `-0.0003` ）です。|
|jointlimitvel|real|速度に関する`jointlimitpos`です。出力は`mjData.efc_vel`からコピーされます。違反していない場合の測定値 $0$ です。|
|jointlimitfrc|real|拘束力に関する`jointlimitpos`です。出力は`mjData.efc_force`からコピーされます。違反していない場合の測定値 $0$ です。|

</div></details>

<details><summary>【使用可能なセンサ型の一覧（対象 : tendon）】</summary><div>

#### tendonにのみ接続可能なセンサ <!-- omit in toc -->

下表に、本項目のすべてのセンサが持つ属性を示します。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|noise|**型：real〈** `"0"` **〉**<br>センサ出力に付加されるゼロ平均ガウスノイズの標準偏差です。<br>ノイズの乗ったセンサデータもその型は変化しません。例えば正の値だけを返すセンサであれば、ノイズ付与後も正の値を返します。|
|cutoff|**型：real〈** `"0"` **〉**<br>この値が正の場合、センサ出力の絶対値を制限します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_sensor`と同じ要素数を持ちます。|
|tendon|**型：string〈** required **〉**<br>センサを接続する`<tendon>`の名前を指定します。|

`<tendon>`にのみ接続可能なセンサは以下の5つです。

|センサ|戻り値の型|説明|
|-|-|-|
|tendonpos|real|腱の長さセンサです。|
|tendonvel|real|腱の速度センサです。|
|tendonlimitpos|real|腱の制限に違反した量を取得します（取得される値は`mjData.efc_pos - mjData.efc_margin`に等しい）。<br>限界制約に対する違反量を負数として取得し、腱の状態が制約内の場合の測定値は $0$ となります。
|tendonlimitvel|real|速度に関する`tendonlimitpos`です。出力は`mjData.efc_vel`からコピーされます。違反していない場合の測定値 $0$ です。|
|tendonlimitfrc|real|拘束力に関する`tendonlimitpos`です。出力は`mjData.efc_force`からコピーされます。違反していない場合の測定値 $0$ です。|

</div></details>

<details><summary>【使用可能なセンサ型の一覧（対象 : actuator）】</summary><div>

#### actuatorにのみ接続可能なセンサ <!-- omit in toc -->

下表に、本項目のすべてのセンサが持つ属性を示します。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|noise|**型：real〈** `"0"` **〉**<br>センサ出力に付加されるゼロ平均ガウスノイズの標準偏差です。<br>ノイズの乗ったセンサデータもその型は変化しません。例えば正の値だけを返すセンサであれば、ノイズ付与後も正の値を返します。|
|cutoff|**型：real〈** `"0"` **〉**<br>この値が正の場合、センサ出力の絶対値を制限します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_sensor`と同じ要素数を持ちます。|
|actuator|**型：string〈** required **〉**<br>センサを接続する`<actuator>`の名前を指定します。<br>すべてのセンサ出力値は`mjData.actuator_length`、`mjData.actuator_velocity`または`mjData.actuator_force`からのコピーです。|

`<actuator>`にのみ接続可能なセンサは以下の3つです。

|センサ|戻り値の型|説明|
|-|-|-|
|actuatorpos|real|長さセンサです。<br>各アクチュエータの長さを計測します。すべてのアクチュエータに取り付け可能です。|
|actuatorvel|real|速度センサです。<br>各アクチュエータの速度を計測します。すべてのアクチュエータに取り付け可能です。|
|actuatorfrc|real|力センサです。<br>アクチュエータによる力のスカラー量を取得します。アクチュエータによる一般化力（スカラー力×モーメントアームベクトル）ではありません。すべてのアクチュエータに取り付け可能です。|

</div></details>

<details><summary>【使用可能なセンサ型の一覧（対象 : 複数）】</summary><div>

#### いくつかの対象に接続可能なセンサ <!-- omit in toc -->

下表に、本項目のすべてのセンサが持つ属性を示します。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|noise|**型：real〈** `"0"` **〉**<br>センサ出力に付加されるゼロ平均ガウスノイズの標準偏差です。<br>ノイズの乗ったセンサデータもその型は変化しません。例えば正の値だけを返すセンサであれば、ノイズ付与後も正の値を返します。|
|cutoff|**型：real〈** `"0"` **〉**<br>この値が正の場合、センサ出力の絶対値を制限します。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_sensor`と同じ要素数を持ちます。|
|objtype|**型：選択式〈** required **〉**<br>`["body","xbody","geom","site","camera"]`から選択<br>センサを接続するオブジェクト型を選択します。このうち、前二つの違いは以下の通りです。<br>`"body"` : 指定したボディの慣性系を参照します<br>`"xbody"` : 指定したボディの座標系を使用します。通常は親ボディに接続したジョイント原点を原点とする座標系です|
|objname|**型：string〈** required **〉**<br>センサを取り付けるオブジェクトの名前を指定します。|
|reftype|**型：選択式〈** optional **〉**<br>`["body","xbody","geom","site","camera"]`から選択<br>使用する参照座標系（frame-of-reference）が設定されているオブジェクトの型を指定します。<br>`reftype`および`rafname`が**指定された場合**、センサはその座標系に従い測定値を出力します。一方、**指定されない場合**はグローバル座標系に従いセンサの測定値が出力されます。<br>❕**framelinaccおよびframeangaccはこの属性を持ちません**。|
|refname|**型：string〈** optional **〉**<br>使用する参照座標系（frame-of-reference）が設定されているオブジェクトの名前を指定します。<br>❕**framelinaccおよびframeangaccはこの属性を持ちません**。|

本項目のセンサは以下の9つです。

|センサ|戻り値の型|説明|
|-|-|-|
|framepos|real(3)|オブジェクトの位置を、グローバル座標または参照座標系のもとで取得するセンサです。|
|framequat|real(4)|オブジェクトの姿勢を、グローバル座標または参照座標系のもとで取得するセンサです。|
|framexaxis|real(3)|オブジェクトの座標系のX軸ベクトルを、グローバル座標または参照座標系のもとで取得するセンサです。|
|frameyaxis|real(3)|オブジェクトの座標系のY軸ベクトルを、グローバル座標または参照座標系のもとで取得するセンサです。|
|framezaxis|real(3)|オブジェクトの座標系のZ軸ベクトルを、グローバル座標または参照座標系のもとで取得するセンサです。|
|framelinvel|real(3)|オブジェクトの速度ベクトルを、グローバル座標または参照座標系のもとで取得するセンサです。|
|frameangvel|real(3)|オブジェクトの角速度ベクトルを、グローバル座標または参照座標系のもとで取得するセンサです。例えばX軸回りに $3 \space\mathrm{rad/s}$ で回転しているとき、測定値は `[3 0 0]` となります。|
|framelinacc|real(3)|オブジェクトの加速度ベクトルを、グローバル座標のもとで取得するセンサです。|
|frameangacc|real(3)|オブジェクトの角加速度ベクトルを、グローバル座標のもとで取得するセンサです。|

</div></details>

<details><summary>【使用可能なセンサ型の一覧（そのほか）】</summary><div>

#### そのほかのセンサ <!-- omit in toc -->

　上の６種類に当てはまらないセンサとして、`<clock>`と`<user>`、`<plugin>`の３種類が存在します。

- `<clock>`
    - シミュレータ上の時刻を出力します
    - `name`、`noise`、`cutoff`および`user`属性を持ちます。各属性は[他のセンサと同様](#属性--sensor-mjcf)です
- `<user>`
    - ユーザが詳細を設定可能なセンサです
    - 詳細は[sensor/user](https://mujoco.readthedocs.io/en/stable/XMLreference.html#sensor-user)を参照してください
- `<plugin>`
    - [engine plugin](https://mujoco.readthedocs.io/en/stable/programming/extension.html#explugin)に関連付けられるセンサです
    - 詳細は[sensor/plugin](https://mujoco.readthedocs.io/en/stable/XMLreference.html#sensor-plugin)を参照してください

</div></details>

### 各センサの詳細

　上に紹介した40近くのセンサのうち、その効果や測定のロジックがわかりにくいものについて詳細の記述を行います。ただし、使用頻度が低そうなものについては記述していないこともあります。

　記事の量が膨大になるため、すべての記述は折りたたんでいます。

#### 詳説 : タッチセンサ（touch）

<details><summary>【タッチセンサの詳細】</summary><div>

　タッチセンサは`<site>`の体積内にアクティブなセンサ領域を持つセンサです。センサ領域内に生じたすべての接触力（法線力）の総和[N]を取得します。**測定値は非負のスカラー**です。接触点が`<site>`と同じボディの`<geom>`に生じている場合もその接触力は測定値に含まれます。また、接触点が体積の外側にあるが接触力のベクトルがセンサ領域と公差している場合、これも測定値に含まれます。

　幅 $1\space\mathrm{m}, 10^3\space\mathrm{kg}$ の立方体を $0.5\space\mathrm{m}$ から落下させ、タッチセンサの検証を行いました。模式図は下図を参照してください。下図左ではセンサ領域幅と立方体の幅を等しく、下図右ではセンサ領域幅を半分にしています。**MuJoCoでは面接触が存在しない**ため、下図右では測定値は0のままとなっています（MuJoCo立方体の接触力は、垂直に落下した場合四隅に働くため）[7]。このため、`<site>`**の体積/面積はある程度大きくする必要があります**。

　また、上記の例において安定後の測定値は $9810$ ですが、これは立方体が地面から受ける反力

$${(1 \space\mathrm{m})}^3 \times 1000 \space\mathrm{kg/m^3} \times 9.81\space\mathrm{m/s^2} = 9810\space\mathrm{N}$$

に等しいことがわかります。`<site>`のサイズを変更せずに中心座標`pos=".5 0 -.9"`とすると測定値は $4905$ となり、接触点の半分がセンサ領域内から消えた分だけ測定値が減少します。ここから、体積内の力の総和を測定値として出力していることがわかります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/a0810e57-0e2b-df94-66fb-d4352b570782.png" width=450>

**図** 赤が落下する物体、緑がタッチセンサの感知領域．感知領域は衝突判定を持ちません．赤い立方体が垂直に落下した場合に接触力は立方体の四隅に働くため、右は接触力を検知できません

```xml
<mujoco>
  <worldbody>
    <camera name="fixed" pos="0 -5 -.5" xyaxes="1 0 0 0 0 1"/>
    <light pos="0 0 3"/>
    <geom type="plane" size="10 10 10" pos="0 0 -1"/>
    <site type="box" name="touch" size=".5 .5 .1" pos="0 0 -.9" rgba="0 1 0 1"/>

    <body pos="0 0 0">
      <freejoint/>
      <geom type="box" size=".5 .5 .5" pos="0 0 0" rgba="1 0 0 1"/>
    </body>
  </worldbody>

  <sensor>
    <touch name="touch" site="touch"/>
  </sensor>
</mujoco>
```

</div></details>

<details><summary>【付録：検知領域が狭い場合の障害】</summary><div>

　上に述べたように、`<touch>`の面積は検査対象の面と比べてある程度大きくする必要があります。具体例として、接触面積に対する`<site>`の断面積を変化させたときの変化を下図に示します。`<site>`の断面積を大きくした場合と比べると、`<site>`の断面積が小さい方は謎の乱れが現れていることがわかります。このような障害を防ぐためにも、`<site>`の断面積は接触面積よりも大きくした方がよいです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/d6e1275d-6970-6362-69c2-d0b0e19e8938.png" width=500>

**図** 同じ条件下で`<site>`の面積を変化させた場合の変化．(左) 接触面積と`<site>`の面積が同じ（右）`<site>`の直径が接触面直径の1.5倍

　ちなみに上図左の青い領域は以下のようになっています。以下を見ると大体50Hzくらいで振動してることがわかります。なんでですかね？

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/799b918e-8fec-294d-27de-7a4fe5999028.png" width=200>

**図** 上図左、青い領域の拡大図

</div></details>

#### 詳説 : 力センサ・トルクセンサ（force, torque）

<details><summary>【力/トルクセンサの詳細】</summary><div>

　`<force>`,`<torque>`は子ボディと親ボディとの相互作用を計測する３軸力/トルクセンサです。取り付けた`<site>`のローカル座標に基づき、子ボディ-親ボディ間の相互作用力/トルクを出力します。子から親の方向を正とし、`<site>`を親ボディに取り付けた場合の符号は逆になります。

> 一つの親に対して複数の子が定義可能であるため、基本的にこのセンサは子ボディに接続した方が便利です。

　接触する`<geom>`の`condim`属性により取得できる値が変化します。力センサで取得できるのは接触における**垂直抗力と接線摩擦力**(x2)であり、トルクセンサで取得できるのは**ねじり摩擦と転がり摩擦**(x2)です。既定では`condim="3"`であり、ねじり摩擦と転がり摩擦は計算されません。このため、常に`<torque>`の測定値はゼロとなります。トルクセンサでの測定を行うためには、`condim`の値を最低でも4（ねじり摩擦を有効化）、もしくは6（全摩擦を有効化）とする必要があります。

 > 重力等による力はととんど計測されず、アクチュエータにより加えられた力を計測するっぽい…？

</div></details>

#### 詳説 : 距離センサ（rangefinder）

<details><summary>【距離センサの詳細】</summary><div>

　`<rangefinder>`は最も近い`<geom>`との距離を測定する距離センサです。**測定方向は**`<site>`**のZ軸正方向**であり、測定方向に`<geom>`が**存在しない場合は** $-1$ を出力します。

　このセンサは`<geom>`を中空の物体として扱うため、`<site>`中心が`<geom>`内に入り込んだ場合はその`<geom>`の表面までの距離を出力します。ただし、センサの`<site>`と**同じボディの**`<geom>`**は測定対象から除外**されます。また、不可視の(alpha=0の？)`<geom>`も感知対象となります。

　このセンサは可視化すると分かりやすいので、以下に距離センサを接続した振り子の例を示します。この例では、約 $1\space\mathrm{m}$ の回転する棒の先に距離センサが取り付けられており、センサは回転中心の反対を向いています。既定で距離センサのラインは可視化されるようで、以下の動画では黄緑色の線が測定線です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/cd12e543-a7fa-ed86-6726-2f21d53748d2.gif" width=400>

**図** 緑の立方体がセンサを接続した`<site>`．センサの測定値が $-1$ でないときには黄緑の線が描画されている

```xml:swinging_rangefinder.xml
<mujoco>
  <worldbody>
    <camera name="fixed" pos="0 -5 -.5" xyaxes="1 0 0 0 0 1"/>
    <light pos="0 0 3"/>
    <geom type="plane" size="0 0 10" pos="0 0 -1"/>
    <body pos="0 0 0">
      <joint type="hinge" axis="0 1 0"/>
      <geom type="cylinder" size=".01" fromto="0 0 0 .707 0 .707" rgba="1 0 0 1"/>
      <site type="box" name="f" size=".1 .1 .1" pos=".707 0 .707" zaxis="1 0 1" rgba="0 1 0 1"/>
    </body>
  </worldbody>

  <sensor>
    <rangefinder name="f" site="f"/>
  </sensor>
</mujoco>
```

</div></details>

#### 詳説 : 光学式モーションキャプチャのマーカー (camprojection)

<details><summary>【camprojectの詳細】</summary><div>

　`<camprojection>`はカメラ画像上の`<site>`の位置を取得できるセンサです。光学式モーションキャプチャのいちカメラ（camera）とマーカー（site）のようなものです。`site`属性でモーションキャプチャのマーカー位置を指定し、`camera`属性で使用するカメラを指定します。

　センサはカメラ上での`<site>`の位置 $(x_{res},y_{res})$ を出力します。原点 $(0,0)$ はカメラ画像左上であり、`<site>`がカメラ外の場合も値は測定されます。測定値は当該`<camera>`の`resolution`属性に依存します。既定では`"1 1"`ですので、画面上の位置と測定値の関係は以下のようになります。また、カメラ画面の外に`<site>`がある場合も座標は出力されるため、例えば`<site>`が画面外左に位置する場合には $x_{res}<0$ となります。

　また、**カメラの後方にある点も投影される**ため、必要に応じてこれをフィルタリングするべきかもしれません。この場合、カメラを参照フレームとする`framepos`を使用するのが便利です。このとき、z座標の正の値がカメラの後方位置を意味します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/168c323a-5ae3-065c-73cb-5c36bc068972.png" width=200>

**図** `<camprojection>`におけるカメラ上の位置と測定値の関係

　振り子を使用した`<camproject>`の例を以下に示します。カメラの`resolution`は既定のまま（`"1 1"`）計測しました。

**表** `<camprojection>`の出力．値は抜粋です

|時刻 [s]|0|0.37|0.63|0.90|
|-|-|-|-|-|
|座標|(0.784, 0.014)|(0.902, 0.293)|(0.479, 0.701)|(0.099, 0.267)|

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/7fd50d2f-7e0f-2d03-2ec0-3204f5829581.gif" width=300>

**図** ．`<site>`は緑の立方体．

```xml:pendulum_tracking.xml
<mujoco>
  <worldbody>
    <camera name="fixed" pos="0 -3 -.5" xyaxes="1 0 0 0 0 1"/>
    <light pos="0 0 3"/>
    <geom type="plane" size="0 0 10" pos="0 0 -1"/>

    <body pos="0 0 0">
      <joint type="hinge" axis="0 1 0"/>
      <geom type="cylinder" size=".01" fromto="0 0 0 .707 0 .707" rgba="1 0 0 1"/>
      <site type="box" name="tip" size=".1 .1 .1" pos=".707 0 .707" zaxis="1 0 1" rgba="0 1 0 1"/>
    </body>
  </worldbody>

  <sensor>
    <camprojection name="f" site="tip" camera="fixed"/>
  </sensor>
</mujoco>
```

</div></details>

## MJCFの要素 : tendon

### 概要 : tendon (MJCF)

　MuJoCoの特徴的な要素の一つが`<tendon>`であり、（基本的に当たり判定のない）腱や紐をシミュレートすることができます。`<tendon>`は`<sensor>`や`<actuator>`などと同じく子要素で具象を定義する（役割をグルーピングとする）MuJoCo要素です。子要素には`<site>`を最短パスで結び、ばね力などもシミュレートすることのできる`<spatial>`と、`<joint>`の角度/変位（の和）を仮想的な"腱の長さ"として扱える`<fixed>`の２種類が存在します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/b2b1fb90-4725-3800-a9cd-2885e3b8f760.png" width=400>

**図** `<spatial>`による腱の例[8]．モデルは[tendon.xml](https://mujoco.readthedocs.io/en/3.1.3/_static/tendon.xml)を参照

　`<tendon>`は`<sensor>`や`<actuator>`などと同じくキネマティックツリーの外で定義を行う要素です。`<tendon>`自体は属性を持たず、その`<spatial>`や`<fixed>`を子要素としてまとめる役割を持ちます。

　以下に簡単な`<tendon>`を使用したモデルの例を示します。以下の腱`<spatial>`は固定された`"end1"`サイトと立方体`"floating"`（`"end2"`サイト）をつないでおり、下図のようにひもでつないだような挙動を実現することができます。また`stiffness`属性を設定することでゴムのような挙動も実装することができます（下図右）。

|ばね力なし|ばね力あり|
|:-:|:-:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/4614aebe-1811-7d9d-7723-c48913c21ca5.gif" width=300>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/a789e4dc-df38-cc8f-e212-9b08057ec2d1.gif" width=300>|

**図** `<tendon>`を使用したモデル（`springlength="0 .1" stiffness="200"`を設定）

```xml:box_and_tendon.xml
<mujoco>
  <worldbody>
    <site name="end1" size=".01"/>
    <body name="floating" pos="0 0 -.2">
      <freejoint/>
      <geom type="box" size=".1 .1 .1"/>
      <site name="end2" size=".01"/>
    </body>
  </worldbody>

  <tendon>
    <spatial limited="true" range="0 0.6" width="0.005">
      <site site="end1"/>
      <site site="end2"/>
    </spatial>
  </tendon>
</mujoco>
```

> 本項目では`<spatial>`のみ扱います。`<fixed>`の方については[XML Reference/tendon/fixed](https://mujoco.readthedocs.io/en/3.1.3/XMLreference.html#tendon-fixed)を参照してください。`<fixed>`では`<joint>`の角度/変位の和を仮想的な"腱の長さ"として扱えます。このため、例えばプルバックカーを作ることなども可能です（[car.xml](https://github.com/google-deepmind/mujoco/blob/b1a941db8b1e22ef93c01eb0f0744ece1abfdf6b/model/car/car.xml#L53); 車を後ろに引きタイヤを逆回転させると、その`<joint>`の回転分だけ弾性エネルギーが溜まる）。

### 属性 : spatial (MJCF/tendon)

　`<spatial>`は指定された２つの`<site>`を結ぶ（当たり判定を持たない）ひも状の要素です。障害物や経由点を指定した場合、腱はそれらを満たす最小のパスを通ります。本項目では`<tendon>`の子要素の一つである`<spatial>`の持つ属性について説明します。また本項目では取り扱いませんが、もう一つの`<tendon>`の子要素である`<fixed>`についてもその属性の大部分を共有しています。

　下表に`<spatial>`の代表的な属性を示します。すべての属性については、その下の「使用可能な全属性の一覧」を参照してください。

**表** 代表的な属性（`<spatial>`）

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|range|**型：real(2)〈** `"0 0"` **〉**<br>許容される腱の長さの下限と上限を指定します。|
|width|**型：real〈** `"0.003"` **〉**<br>レンダリングに使用される`<tendon>`の断面半径です。ただし、障害物の周囲では幅を縮小してレンダリングされます。|
|rgba|**型：real(4)〈** `"0.5 0.5 0.5 1"` **〉**<br>色と透明度を設定します。値が指定された場合、`material`は無効になります。|
|springlength|**型：real(2)〈** `"-1 -1"` **〉**<br>　ばねが静止する位置を指定します。`stiffness`等が指定されていない場合意味を持ちません。<br>　１つまたは２つの値を設定することが可能です。設定する場合、**正の値**とする必要があります。<br>・未設定 - 腱の静止時長さは`mjModel.qpos0`から決定されます。<br>・１つの値 - 静止時の腱長を指定します<br>・２つの値 - deadband範囲を指定します。腱長がこの範囲内にあるとき、作用する力は０になります|
|stiffness|**型：real〈** `"0"` **〉**<br>剛性です。<br>正値が設定された場合、腱に沿ってばね力が作用するようになります。このとき、力は腱の伸びに対して線形です。|

<details><summary>【使用可能な全属性の一覧】</summary><div>

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`のクラス名です。|
|group|**型：int〈** `"0"` **〉**<br>属するグループの番号です。カスタムタグや可視化の有効/無効の切り替えに使用可能です。|
|limited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>`range`属性による長さの制限の適用可否を決定します。<br>`"true"` - `range`による長さの制限を強制します<br>`"false"` - 長さの制限を無効にします<br>`"auto"` - `range`属性が指定されている場合にのみ長さの制限を設けます|
|range|**型：real(2)〈** `"0 0"` **〉**<br>許容される腱の長さの下限と上限を指定します。|
|margin|**型：real〈** `"0"` **〉**<br>`range`で指定した下限/上限と腱長との差の絶対値が、`range`を下回らないように制約ソルバが処理を行います。詳細は[ソルバーパラメタ](https://mujoco.readthedocs.io/en/3.1.3/modeling.html#csolver)を参照してください。|
|frictionloss|**型：real〈** `"0"` **〉**<br>正値に設定した場合、乾性摩擦による摩擦ロスが生じるようになります。|
|width|**型：real〈** `"0.003"` **〉**<br>レンダリングに使用される`<tendon>`の断面半径です。ただし、障害物の周囲では幅を縮小してレンダリングされます。|
|material|**型：string〈** optional **〉**<br>外観を設定するために使用する`<material>`を指定します。|
|rgba|**型：real(4)〈** `"0.5 0.5 0.5 1"` **〉**<br>色と透明度を設定します。値が指定された場合、`material`は無効になります。|
|springlength|**型：real(2)〈** `"-1 -1"` **〉**<br>　ばねが静止する位置を指定します。`stiffness`等が指定されていない場合意味を持ちません。<br>　１つまたは２つの値を設定することが可能です。設定する場合、**正の値**とする必要があります。<br>・未設定 - 腱の静止時長さは`mjModel.qpos0`から決定されます。<br>・１つの値 - 静止時の腱長を指定します<br>・２つの値 - deadband範囲を指定します。腱長がこの範囲内にあるとき、作用する力は０になります|
|stiffness|**型：real〈** `"0"` **〉**<br>剛性です。<br>正値が設定された場合、腱に沿ってばね力が作用するようになります。このとき、力は腱の伸びに対して線形です。|
|damping|**型：real〈** `"0"` **〉**<br>減衰係数です。<br>正値が設定された場合、腱に沿って減衰力が作用されるようになります。このとき、力は腱の速度に線形です。<br>`<joint>`の減衰と異なり`<tendon>`の減衰は暗黙的に積分されないため、可能な限り`<joint>`の減衰を使用するべきです。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_tendon`と同じ要素数です。[ユーザパラメタ](https://mujoco.readthedocs.io/en/3.1.3/modeling.html#cuser)を参照してください。|

> また、制約ソルバに関連したパラメタである`solreflimit`、`solimplimit`、`solreffriction`および`solimpfriction`もあるようです。詳細は[ソルバーパラメタ](https://mujoco.readthedocs.io/en/3.1.3/modeling.html#csolver)を参照してください。

</div></details>

### spatialの子要素（site, geom, pulley）

#### site (MJCF/tendon/spatial)

　`<site>`要素は`<tendon>`が通過/接続する点を指定するために使用します。`<tendon>`**の両端はどちらも**`<site>`**を設定する必要があります**。`<site>`要素は以下の１属性のみを持ちます。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|site|**型：string〈** required **〉**<br>`<tendon>`が通過/接続する`<site>`を指定します。|

#### geom（MJCF/tendon/spatial）

　`<geom>`要素は腱の障害物を設定するために使用します。障害物に対し、`<tendon>`のパスは回り込むように設定されます。ただし、最小長のパスが`<geom>`に接触しない場合には効果を持ちません。障害物としては円柱と球の２種類の`<geom>`を使用することが可能ですが、このうち円柱については無限の長さを持つものとして扱われます。

　**複数の障害物を使用した場合、腱を**`<site>`**で分割する必要があります**（そうしないと腱のレベルで反復ソルバが必要となるため）。[以下の具体例](#使用例sitegeompulley)における`"s5"`がこれに相当します。

　`<geom>`要素は以下の２属性を持ちます。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|geom|**型：string〈** required **〉**<br>障害物として設定する`<geom>`の名前です。<br>`"sphere"`と`"cylinder"`の`<geom>`のみを参照可能です。|
|sidesite|**型：string〈** optional **〉**<br>　指定した場合、実行時に指定された`<site>`に近いパスが自動的に選択されます。<br>　動作時に腱のパスが「反対側へ移動する」のを防ぐために使用されます。`<site>`が`<geom>`の内側にある場合、腱のパスは`<geom>`の内部を通過するように設定されます。詳細は[後述](#使用例sitegeompulley)します。<br>　`<geom>`の実装を行う際、この属性の指定が必要になることがよくあります。|

#### pulley（MJCF/tendon/spatial）

　この要素は腱のパスに分岐を作成します。腱に複数の分岐が存在する場合、各分岐の長さが分岐を開始した滑車要素の`divisor`属性で除算され、合計値を腱長として扱います。`<pulley>`要素は以下の１属性のみを持ちます。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|divisor|**型：real〈** required **〉**<br>`<pulley>`を始点とする各分岐の長さはこの値で除算されます。分岐先でまた分岐が生じる場合、その除数は各分岐の`divisor`の値を掛けたものとなります。|

> １つのパスから２つの平行な分岐を生成する`<pulley>`の場合、根本のパスの除数値は $1$ になり、`<pulley>`に続く２つの分岐先の除数値は $2$ になります。そのうちの１つが別の`<pulley>`によってさらに分割される場合、それぞれの新しい分岐先はの除数は $4$ になります（すべての`divisor`を $2$ に設定していた場合）。
> 
> １つの腱に分岐が存在しない場合、その腱全体の除数は $1$ となります。

#### 使用例（site、geom、pulley）

　[tendon.xml](https://mujoco.readthedocs.io/en/3.1.3/_static/tendon.xml)をもとに、`<site>`、`<geom>`および`<pulley>`の使用方法を見てみましょう。

　下図の例では、`"s1"`を始点として以下のようなパスが形成されていることがわかります。このように、`<spatial>`の子要素は`<pulley>`**を区切りとしたブロックを構成**し、各ブロックは上下端の`<site>`が末端として選択されます。分岐を作成する場合、`<pulley>`以前に分岐点（`"s3"`）を定義し、`<pulley>`の真下でこれを始端としてそれぞれ宣言することになります。

- 本流 : (始端)`"s1"`→`"s2"`→`"s3"`(末端; 除数 $1$ )
  - 分岐1 : (始端)`"s3"`→`"s4"`(末端; 除数 $2$ )
  - 分岐2 : (始端)`"s3"`→`"s5"`→`"s6"`(末端; 除数 $2$ )

　また、[障害物](#geommjcftendonspatial)節でも述べたように、**複数の障害物を使用した場合には腱を**`<site>`**で分割する必要があります**。すなわち、`<site>`の間に２つ以上の`<geom>`が挿入されないように`<site>`を挟む必要があります（今回の例では`"s5"`がこれに相当; `"g2"`と`"g3"`が隣り合わないようにしている）。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/b2b1fb90-4725-3800-a9cd-2885e3b8f760.png" width=400>

**図** `<spatial>`による腱の例[8]．このうち`<tendon>`部分を以下に示す

```xml
<mujoco>
  ...
  <tendon>
    <spatial range="0 .33" limited="true" width=".002" rgba=".95 .3 .3 1">
      <!-- 本流 -->
      <site site="s1"/>
      <site site="s2"/>
      <geom geom="g1"/>
      <site site="s3"/>

      <!-- 分岐1 -->
      <pulley divisor="2"/>
      <site site="s3"/>
      <geom geom="g2" sidesite="side2"/>
      <site site="s4"/>

      <!-- 分岐2 -->
      <pulley divisor="2"/>
      <site site="s3"/>
      <geom geom="g2" sidesite="side2"/>
      <site site="s5"/>
      <geom geom="g3" sidesite="side3"/>
      <site site="s6"/>
    </spatial>
  </tendon>
</mujoco>
```

## MJCFの要素 : actuator

内容が多岐にわたるため、詳細は別記事にて紹介します。

https://qiita.com/Yayoi-Habami/items/90f42ea10a32eb20fde8

### 概要 : actuator (MJCF)

　アクチュエータもMuJoCoの特徴的な要素のうちの一つです。`<sensor>`や`<tendon>`などと同様に、アクチュエータもグルーピング要素`<actuator>`とその下の具象から構成されます。各アクチュエータはすべて**単一入力単一出力**（SISO）です。PD制御などで２つ以上の制御入力をとるアクチュエータを必要とする場合、２つ以上のアクチュエータを同じ対象に接続する必要があります。

#### アクチュエータの種類

　MuJoCoには非常に柔軟ですべての種類のアクチュエータを作成可能な`<general>`と、これを扱いやすくした**アクチュエータショートカット**（`<motor>`等９種）が存在します。前者はあまりにも設定項目が多く扱いにくので、基本的には後者を使用することになると思います。

<details><summary>【MuJoCoで使用可能なアクチュエータ要素】</summary><div>

|要素|説明|
|-|-|
|general|**接続対象**: `<joint>`,`<tendon>`,`<site>`,`<body>`<br>すべての種類のアクチュエータを作成可能なアクチュエータ要素です。<br>これを除いた他のアクチュエータ要素はすべて**ショートカット**であり、`dynprm`～`biastype`＆`dynprm`～`biasprm`の6種パラメタ(等)を自動で調整したものとなります。|
|motor|**接続対象**: `<joint>`,(+α?)<br>DD(ダイレクトドライブ)モータです。<br>（DDモータ : 減速機構を使用せず駆動するモータです。一般的なサーボに比べ低速・高トルクです。ただしMuJoCoではこれが最も「一般的な」モータです。）|
|position|**接続対象**: `<joint>`,`<site>`,(+α?)<br>ポジションサーボです。<br>物体の把持など、位置決めが重要なケースに使用します。ジョイントを所望の位置へ駆動させるように、現在位置と目標位置の差に基づき、トルクを連続的に調整します。また、slider-crankトランスミッション（簡易なスライダークランク機構）にも使用されます。|
|velocity|**接続対象**: `<joint>`,(+α?)<br>速度サーボです。<br>ホイールの回転制御など、関節速度の維持が重要なケースに使用します。所望の関節速度を達成するように、現在のジョイント速度と目標速度の差に合わせてトルクを連続的に調整します。|
|intvelocity|**接続対象**: `<joint>`,(+α?)<br>積分速度サーボです。<br>`<velocity>`は対象の速度に直接フィードバックを返すアクチュエータですが、`<intvelocity>`は積分器と位置フィードバックを統合したアクチュエータです。<br>制御性が失われることを防ぐため、`actrane`を設定することが望ましい（ことが多い）です。|
|damper|**接続対象**: `<joint>`,(+α?)<br>アクティブダンパーです。<br> 出力 $F_i$ は速度 $v_i$ と制御入力 $u_i>0$ に負比例（ $F_i=-k_v\times v_i\times u_i$ ）します。 $k_v>0$ は速度フィードバックゲインです。<br>💡使用する場合、optionの`integrator`属性を`implicit`または`implicitfast`にすることが推奨されます。|
|cylinder|**接続対象**: `<joint>`<br>空気圧シリンダまたは油圧シリンダのモデリングに使用されます。|
|muscle|**接続対象**: `<tendon>`,(`<joint>`)<br>筋肉です。|
|adhesion|**接続対象**: `<body>`<br>吸着力を発生させるアクチュエータです。ヤモリの足や吸盤などをシミュレートできます。<br>吸着力は接触面積に比例しません。|
|plugin|**接続対象**: 様々<br>[engine plugin](https://mujoco.readthedocs.io/en/stable/XMLreference.html#actuator-plugin)に関連したアクチュエータ要素です。ユーザ定義のアクチュエータを使用することができます。詳細はリンクを参照してください。|

</div></details>

実はMuJoCoはアクチュエータの型として`<general>`以外を持たないため、ほかの要素は単なるショートカットにすぎません。各ショートカットには独自の属性が存在しますが、内部的には`<general>`に変換された時点で共通属性へと変換されます。例えば`<position>`には固有の`kp`属性がありますが、これは`gainprm`と`biasprm`を調整するのに使用されます。このため、`<general>`の各属性を適切に設定することですべてのショートカットアクチュエータを作成することも可能です。

#### アクチュエータの制御

　アクチュエータへの制御入力は`mjData.ctrl`を通して行います。`mjData.ctrl`には各アクチュエータへの制御入力がMJCF上で定義された順に格納されています。プログラム上ではこれらの値を書き換えることでアクチュエータを制御することになります。デフォルトではすべての制御入力は０です。

　例として１つの`<motor>`を持つモデルを以下に示します。その下のPythonコードでは、1秒以降にこの`"hmotor"`に対して制御入力 $-100$ を加えています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734488/139cce06-b708-9475-5978-d5652ba39ef7.gif" width="250">

**図** モータの接続された回転体．1秒以降は制御入力 $u_i=-100$

> "hinge"ジョイントの軸ベクトルが $(0,1,0)$ なので、反時計回り方向への力を与えたいときの制御入力は負となります。

```xml:m_pendulum.xml
<mujoco>
  <asset>
    <texture type="skybox" builtin="gradient" rgb1="1 1 1" rgb2=".6 .8 1" width="256" height="256"/>
  </asset>

  <worldbody>
    <camera name="fixed" pos="0 -5 0" xyaxes="1 0 0 0 0 1"/>
    <body>
      <joint name="hinge" type="hinge" axis="0 1 0"/>
      <geom type="capsule" size=".03" fromto="0 0 0 1 0 1"/>
    </body>
  </worldbody>

  <actuator>
    <motor name="hmotor" joint="hinge" gear="1"/>
  </actuator>
</mujoco>
```

```python
model = mujoco.MjModel.from_xml_path("m_pendulum.xml")
renderer = mujoco.Renderer(model, 640, 480)
data = mujoco.MjData(model)
mujoco.mj_forward(model, data)

# 初期化
n_frames, fps = 180, 60
frames = []
mujoco.mj_resetData(model, data)

# レンダリング
for i in range(n_frames):
    while data.time < i/fps:
        mujoco.mj_step(model, data)

    # 1秒経過時点で"hmotor"に-100の制御入力を与える
    if i == fps:
        data.ctrl[0] = -100

    renderer.update_scene(data, "fixed")
    frames.append(renderer.render())

save_video(frames, "output.mp4", fps)
```

### 属性 : actuator (MJCF)

　`<sensor>`要素と同様に`<actuator>`はグルーピング要素であり、具象は`<motor>`や`<position>`などの子要素で定義します。以下には、全種類のアクチュエータ要素で指定可能な属性を示します。ただし、下表には代表的なもののみを示しています。すべての属性については、その下の「全アクチュエータ共通の属性の一覧」を参照してください。

:::note
以下に示す属性はすべてのアクチュエータショートカットに共通する属性であると同時に、`<general>`アクチュエータで指定可能な属性です。上述のようにMuJoCoは内部的に`<general>`以外のアクチュエータタイプを持ちません。このため、ショートカットのみで指定可能な属性（`kp`や`kv`など）はすべて以下の属性に置換されます。
:::

**表** 代表的な全アクチュエータ共通の属性（`<general>`の属性）

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`の名前です。|
|ctrllimited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>アクチュエータへの制御入力をクランプするか指定します。<br>`"true"` - 制御入力は実行時自動的に`ctrlrange`にクランプされます。<br>`"false"` - 制御入力はクランプされません。<br>`"auto"` - `<compiler>`の`autolimits`が有効かつ`ctrlrange`が定義されている場合にクランプされます。<br>制御入力のクランプはoption/flagの`clampctrl`属性からグローバルに無効化することも可能です。|
|forcelimited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>アクチュエータの力出力をクランプするか指定します。<br>`"true"` - 制御入力は実行時自動的に`forcerange`にクランプされます。<br>`"false"` - 制御入力はクランプされません。<br>`"auto"` - `<compiler>`の`autolimits`が有効かつ`forcerange`が定義されている場合にクランプされます。|
|ctrlrange|**型：real(2)〈** `"0 0"` **〉**<br>制御入力をクランプする範囲を指定します。<br>１番目の値が最小値、２番目の値が最大値です。|
|forcerange|**型：real(2)〈** `"0 0"` **〉**<br>力出力をクランプする範囲を指定します。<br>１番目の値が最小値、２番目の値が最大値です。|
|gear|**型：real(6)〈** `"1 0 0 0 0 0"` **〉**<br>アクチュエータの長さをスケールします。<br>出力トルク/力の倍率を指定します。一部のトランスミッションタイプの場合は、出力が働く軸の指定も兼ねます。詳細は[後述](https://qiita.com/Yayoi-Habami/items/90f42ea10a32eb20fde8#gear)します。|
|joint<br>jointparent<br>site<br>refsite<br>body<br>tendon<br>cranksite<br>slidersite|**型：string〈** optional **〉**<br>アクチュエータを接続するオブジェクトを指定します。選択された属性によりトランスミッションのタイプが決定されます。**この８属性のうち１つ**（種類によっては**２つ**）**を設定する必要があります**。詳細は[後述](https://qiita.com/Yayoi-Habami/items/90f42ea10a32eb20fde8#概要トランスミッション)します。<br>`joint` - jointトランスミッションを構築します。大体のアクチュエータで指定可能です。<br>`jointparent` - `joint`とほとんど同じです。<br>`site`(&`refsite`) - siteトランスミッションを構築します。`<position>`などで指定可能です。<br>`body` - bodyトランスミッションを構築します。`<adhesion>`などで指定可能です。<br>`tendon` - tendonトランスミッションを構築します。`<muscle>`などで指定可能です。<br>`cranksite`&`slidersite` - slider-crankトランスミッションを構築します。両方の指定が必要であり、`<position>`などで指定可能です。|

<details><summary>【全アクチュエータ共通の属性の一覧（generalの属性）】</summary><div>

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|name|**型：string〈** optional **〉**<br>名前です。|
|class|**型：string〈** optional **〉**<br>適用したい`<default>`の名前です。|
|group|**型：int〈** `"0"` **〉**<br>属するグループを指定します。カスタムタグおよび可視化時に使用できます。|
|ctrllimited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>アクチュエータへの制御入力をクランプするか指定します。<br>`"true"` - 制御入力は実行時自動的に`ctrlrange`にクランプされます。<br>`"false"` - 制御入力はクランプされません。<br>`"auto"` - `<compiler>`の`autolimits`が有効かつ`ctrlrange`が定義されている場合にクランプされます。<br>制御入力のクランプはoption/flagの`clampctrl`属性からグローバルに無効化することも可能です。|
|forcelimited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>アクチュエータの力出力をクランプするか指定します。<br>`"true"` - 制御入力は実行時自動的に`forcerange`にクランプされます。<br>`"false"` - 制御入力はクランプされません。<br>`"auto"` - `<compiler>`の`autolimits`が有効かつ`forcerange`が定義されている場合にクランプされます。|
|actlimited|**型：選択式〈** `"auto"` **〉**<br>`["false","true","auto"]`から選択<br>アクチュエータに関連する内部状態（activation; 活性化状態）をクランプするか指定します。<br>`"true"` - 制御入力は実行時自動的に`actrange`にクランプされます。<br>`"false"` - 制御入力はクランプされません。<br>`"auto"` - `<compiler>`の`autolimits`が有効かつ`actrange`が定義されている場合にクランプされます。|
|ctrlrange|**型：real(2)〈** `"0 0"` **〉**<br>制御入力をクランプする範囲を指定します。<br>１番目の値が最小値、２番目の値が最大値です。|
|forcerange|**型：real(2)〈** `"0 0"` **〉**<br>力出力をクランプする範囲を指定します。<br>１番目の値が最小値、２番目の値が最大値です。|
|actrange|**型：real(2)〈** `"0 0"` **〉**<br>内部状態（activiation; 活性化状態）をクランプする範囲を指定します。<br>１番目の値が最小値、２番目の値が最大値です。詳細は[活性化クランピング](https://mujoco.readthedocs.io/en/stable/modeling.html#cactrange)を参照してください。|
|lengthrange|**型：real(2)〈** `"0 0"` **〉**<br>アクチュエータのトランスミッションの実現可能な範囲を指定します。[後述](#可動範囲の制限length-range)します。|
|gear|**型：real(6)〈** `"1 0 0 0 0 0"` **〉**<br>アクチュエータの長さをスケールします。<br>出力トルク/力の倍率を指定します。一部のトランスミッションタイプの場合は、出力が働く軸の指定も兼ねます。詳細は[後述](https://qiita.com/Yayoi-Habami/items/90f42ea10a32eb20fde8#gear)します。|
|cranklength|**型：real〈** `"0"` **〉**<br>コネクティングロッド（コンロッド）の長さを指定します。<br>slider-crankトランスミッションの場合にのみ使用し、値は正である必要があります。|
|joint<br>jointparent<br>site<br>refsite<br>body<br>tendon<br>cranksite<br>slidersite|**型：string〈** optional **〉**<br>アクチュエータを接続するオブジェクトを指定します。選択された属性によりトランスミッションのタイプが決定されます。**この８属性のうち１つ**（種類によっては**２つ**）**を設定する必要があります**。詳細は[後述](https://qiita.com/Yayoi-Habami/items/90f42ea10a32eb20fde8#概要トランスミッション)します。<br>`joint` - jointトランスミッションを構築します。大体のアクチュエータで指定可能です。<br>`jointparent` - `joint`とほとんど同じです。<br>`site`(&`refsite`) - siteトランスミッションを構築します。`<position>`などで指定可能です。<br>`body` - bodyトランスミッションを構築します。`<adhesion>`などで指定可能です。<br>`tendon` - tendonトランスミッションを構築します。`<muscle>`などで指定可能です。<br>`cranksite`&`slidersite` - slider-crankトランスミッションを構築します。両方の指定が必要であり、`<position>`などで指定可能です。|
|user|**型：real(n)〈** `"0 0 ..."` **〉**<br>`nuser_actuator`と同じ要素数<br>詳細は[ユーザーパラメタ](https://mujoco.readthedocs.io/en/stable/modeling.html#cuser)を参照してください。|
|actdim|**型：real〈** `"-1"` **〉**<br>活性化状態の次元です。<br>`"-1"` - `dyntype`に従い次元を設定します<br>1より大きい値 - ユーザ定義の活性化ダイナミクスのみで設定可能です。|
|dyntype|**型：選択式〈** `"none"` **〉**<br>`["none","integrator","filter","filterexact","muscle","user"]`から選択<br>アクチュエータの活性化ダイナミクスを指定します。`"none"`の場合、アクチュエータは活性化状態を持ちません。<br>詳細は[後述](#アクチュエータのモデル)します。|
|gaintype|**型：選択式〈** `"fixed"` **〉**<br>`["fixed","affine","muscle","user"]`から選択<br>　力生成メカニズムの出力（のゲイン(係数)）を決定します。MuJoCoのアクチュエータモデルでは次のように出力トルク/力が決定されます。ここで $w_i,u_i$ はそれぞれ活性化状態と制御入力です。また、 $Gain, Bias$ はそれぞれゲイン(係数)とバイアス項ですが、実際の値は`gaintype`/`biastype`により変化します。詳細は[後述](#アクチュエータのモデル)します。<br>　　 $F_i = Gain\times (w_i \space or\space u_i) + Bias$ |
|biastype|**型：選択式〈** `"none"` **〉**<br>`["none","affine","muscle","user"]`から選択<br>力生成メカニズムの出力（のバイアス項）を決定します。詳細は[後述](#アクチュエータのモデル)します。|
|dynprm|**型：real(10)〈** `"1 0 ... 0"` **〉**<br>活性化ダイナミクスのパラメタです。組み込みのアクチュエータショートカットの場合、**筋肉を除き**１番目の値のみ使用します。`"muscle"`の場合は３つ目までの値が使われます。|
|gainprm|**型：real(10)〈** `"1 0 ... 0"` **〉**<br>ゲインのパラメタです。組み込みのアクチュエータショートカットの場合、**筋肉を除き**１番目の値のみ使用します。`"muscle"`の場合は９つめまでの値が使われます。|
|biasprm|**型：real(10)〈** `"1 0 ... 0"` **〉**<br>バイアス項のパラメタです。バイアス型が`"affine"`の場合は３つめまでの値を使用します。`"none"`の場合値は使用されず（ $Bias=0$ ）、`"muscle"`の場合は９つ目まで使用されます。|
|actearly|**型：選択式〈** `"false"` **〉**<br>`["false","true"]`から選択<br>`"true"`を指定すると、力の計算は現在の値ではなく次の活性化変数の値を使用するようになります。このフラグを設定すると、制御と加速の間の遅延が1タイムステップ短くなります。|

</div></details>

:::note
各アクチュエータショートカットにおいて`actdim`～`biasprm`の７属性は自動的に設定されます。したがって、通常は**これ以外の属性**＆**ショートカット固有の属性**のみを指定することになります。
:::

## MJCFの要素 : default

### 概要 : default (MJCF)

　MJCFにおいて、`<default>`要素は変更を全体に伝播させる機能を持ちます。特定の要素の初期値（例えば色）を変更したい場合に使用することができます。

　下記では`<default>`を使用して、`<geom>`の色の初期値を設定しています（不完全なコードですが）。

**表** 下記MJCFコードによる各`<geom>`の色

|`type`属性|`rgba`属性|
|-|-|
|`"box"`|`"1 0 0 1"`|
|`"ellipsoid"`|`"0 1 0 1"`|
|`"sphere"`|`"0 0 1 1"`|
|`"cylinder"`|`"1 0 0 1"`|

```xml:multiple_defaults.xml
<mujoco>
    <default class="main">
        <geom rgba="1 0 0 1"/>
        <default class="sub">
            <geom rgba="0 1 0 1"/>
        </default>
    </default>

    <worldbody>
        <geom type="box"/>
        <body childclass="sub">
            <geom type="ellipsoid"/>
            <geom type="sphere" rgba="0 0 1 1"/>
            <geom type="cylinder" class="main"/>
        </body>
    </worldbody>
</mujoco>
```

　上記のように、`<default>`は入れ子構造に定義することが可能です。このような`<default>`はツリー構造をなしますが、各`<default>`は**一意の名前をつける必要があります**（上記の`"main"`,`"sub"`など; 最上位の`<default>`を除く）。デフォルト値は大体以下のルールにより適用されているようです。

- 属性の値が個別に定義済みのものはそれを適用（`"sphere"`）
  - `<default>`名を個別に指定されているものも含む（`"cylinder"`）
- 最上位（`class="main"`）の`<default>`の値は全体に適用（`"box"`）
- 親要素が`<default>`名を指定した場合、子要素はそれを適用(`<body>`下、`"ellipsoid"`)

最上位の`<default>`の値（`<geom>`の`rgba="1 0 0 1"`）は全体に適用されるため、最上位で定義した場合にその属性を未定義にすることはできません。ボディの慣性など(?)一部の属性は未定義としたいこともありますので、そういった属性についてはアクティブな`<default>`では未定義にする必要があります（下位分類で既定すべき(?)）。

### 属性 : default (MJCF)

　`<default>`要素自体には設定可能な属性は下記の一つしかありません。

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|class|**型：string〈** required **〉**<br>名前です。最上位を除き必須です。<br>すべてのデフォルトについて一意な名前を付ける必要があります。<br>各要素でデフォルト値を有効にする際に使用されます。|

次に`<default>`の中に定義される要素について、デフォルト値を設定することのできる属性を下に示します。下表からわかるように、**要素ごとにデフォルト値を設定可能な属性は異なります**。例えば`<geom>`や`<joint>`、`<light>`などは`name`属性と`class`属性を除きすべてのデフォルト値を設定可能ですが、`<mesh>`は`scale`属性以外のデフォルト値は設定不可能です。

<details><summary>【デフォルト値を設定可能な属性】</summary><div>

alphabetical order

|要素|設定可能な属性|
|-|-|
|adhesion|`name`、`class`、`body`以外すべて|
|camera|`name`、`class`、`mode`、`target`以外すべて|
|cylinder|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|
|dumper|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|
|equality|`active`、`solref`、`solimp`のみ<br>サブ要素にかかわらず、すべての等式制約に共通の属性を設定します。|
|general|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|
|geom|`name`、`class`以外すべて|
|intvelocity ※|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|
|joint|`name`、`class`以外すべて|
|light|`name`、`class`以外すべて|
|material|`name`、`class`以外すべて|
|mesh|`scale`のみ|
|motor ※|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|
|muscle|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|
|pair|`name`、`class`、`geom1`、`geom2`以外すべて|
|position ※|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|
|site|`name`、`class`以外すべて|
|tendon|`name`、`class`以外すべて<br>すべてのタイプに共通する、全ての腱のサブ要素の属性を設定可能です。|
|velocity ※|`name`、`class`、`joint`、`jointinparent`、`site`、`refsite`、`tendon`、`slidersite`、`cranksite`以外すべて|

:::note
※ : アクチュエータショートカットを使用して一般要素の属性を設定します。これらはすべて同じ基本属性を設定し、以前の設定を書き換えます。このため、同じ`<default>`内でこれらを**複数を使用する意味はありません**。
:::

</div></details>

## MJCFの要素 : option

### 概要 : option (MJCF)

　`<option>`要素はシミュレーション時のオプションを設定するために使用されます。`MjModel.opt`は構造体`mjOption`ですが、`<option>`の要素はこれと一対一で対応しています。実行時（プログラミングコード）に`MjOption`を使用して設定を変更することも可能ですが、もう一つの選択肢としてこちらを使用することも可能です。

　オプションのうち、`"enable"`と`"disable"`の2値で表されるものとして**Flag**オプションが存在します。この要素はシミュレーションパイプラインの特定の部分を有効化/無効化します。以下に、flag型のオプションと通常のオプションの2パターンについて、その具体例を示します。

```xml
<mujoco>
  <option timestep=".001">
    <flag energy="enable" contact="disable"/>
  </option>
</mujoco>
```

### 使用可能なオプションの一覧

　以下に代表的なオプションを示します。すべてのオプションについては、その下の「使用可能な全オプションの一覧」を参照してください。

**表** 主要なオプション

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|timestep|**型：real〈** `"0.002"` **〉**<br>シミュレーションのタイムステップ幅 $\mathrm{[s]}$ です。小さいほど精度と安定性が向上しますが、1ステップのシミュレーションに必要なCPU時間よりも大きい必要があります(積分器が`"RK4"`の場合はCPU時間の4倍; リアルタイム性能のため)。|
|gravity|**型：real(3)〈** `"0 0 -9.81"` **〉**<br>重力加速度のベクトルを設定します。既定のワールド方向ではZ軸は上向きです。他の機能にも影響するため、**基本的に変更することは推奨されません**。|
|wind|**型：real(3)〈** `"0 0 0"` **〉**<br>風の速度ベクトルを設定します。この値はボディに作用する粘性力、揚力、抗力を計算するために使用されます。|
|density|**型：real〈** `"0"` **〉**<br>媒体(medium; 空気等)の密度を設定します（水の場合約 $1.2$ です）。**ボディの(幾何学的)密度とは異なります**。揚力や抗力を計算するために使用されます。|
|integrator|**型：選択式〈** `"Euler"` **〉**<br>`["Euler","RK4","implicit","implicitfast"]`から選択<br>使用する数値積分器を選択します。前から順に「半陰的オイラー法（semi-implicit Euler method）」、「固定ステップ4次ルンゲクッタ法（fixed-step 4-th order Runge Kutta method）」、「陰解法速度オイラー法（Implicit-in-velocity Euler method）」、「コリオリ項と遠心項を無視する陰解法速度オイラー法」です。|

<details><summary>【使用可能な全オプションの一覧】</summary><div>

|属性|型:〈デフォルト値等〉<br>説明|
|-|-|
|timestep|**型：real〈** `"0.002"` **〉**<br>シミュレーションのタイムステップ幅 $\mathrm{[s]}$ です。小さいほど精度と安定性が向上しますが、1ステップのシミュレーションに必要なCPU時間よりも大きい必要があります(積分器が`"RK4"`の場合はCPU時間の4倍; リアルタイム性能のため)。|
|apirate|**型：real〈** `"100"` **〉**<br>API rate $\mathrm{[Hz]}$. 外部APIが更新関数の実行を許可するレートです。|
|impratio|**型：real〈** `"1"` **〉**<br>摩擦力を実際の値よりも大きくするかを決定する値です。1より大きい場合、摩擦力は本来の値よりも大きくなります。<br>（楕円摩擦円錐の摩擦と、通常の高速インピーダンスの比率を決定します）|
|gravity|**型：real(3)〈** `"0 0 -9.81"` **〉**<br>重力加速度のベクトルを設定します。既定のワールド方向ではZ軸は上向きです。他の機能にも影響するため、**基本的に変更することは推奨されません**。|
|wind|**型：real(3)〈** `"0 0 0"` **〉**<br>風の速度ベクトルを設定します。この値はボディに作用する粘性力、揚力、抗力を計算するために使用されます。|
|magnetic|**型：real(3)〈** `"0 -0.5 0"` **〉**<br>地磁気のベクトルを設定します。磁力計センサにより計測が可能です。|
|density|**型：real〈** `"0"` **〉**<br>媒体(medium; 空気等)の密度を設定します（水の場合約 $1.2$ です）。**ボディの(幾何学的)密度とは異なります**。揚力や抗力を計算するために使用されます。|
|viscosity|**型：real〈** `"0"` **〉**<br>媒体の粘度を設定します（空気は約 $2e-5$ 水は約 $9e-4$ です）。デフォルトの積分器`"Euler"`では、減衰を表現する際にジョイントの減衰を設定する方が安定します。|
|o_margin|**型：real〈** `"0"` **〉**<br>[接触オーバーライド](https://mujoco.readthedocs.io/en/stable/modeling.html#coverride)が有効な場合、接触ペアのmarginパラメタを置き換えます。|
|integrator|**型：選択式〈** `"Euler"` **〉**<br>`["Euler","RK4","implicit","implicitfast"]`から選択<br>使用する数値積分器を選択します。前から順に「半陰的オイラー法（semi-implicit Euler method）」、「固定ステップ4次ルンゲクッタ法（fixed-step 4-th order Runge Kutta method）」、「陰解法速度オイラー法（Implicit-in-velocity Euler method）」、「コリオリ項と遠心項を無視する陰解法速度オイラー法」です。後ろの選択肢ほど計算速度は速いですが、一部の正確性は失われます。|
|cone|**型：選択式〈** `"pyramidal"` **〉**<br>`["pyramidal", "elliptic"]`から選択<br>接触摩擦円錐の型を選択します。`"elliptic"`の方がより現実的ですが、`"pyramidal"`の方が高速で安定的です。|
|jacobian|**型：選択式〈** `"auto"` **〉**<br>`["dense", "sparse", "auto"]`から選択<br>制約ヤコビアンとそこから計算される行列のタイプを選択します。`"auto"`の場合、自由度が60未満の場合は`"dense"`として、60以上の場合には`"sparse"`として計算されます。|
|solver|**型：選択式〈** `"Newton"` **〉**<br>`["PGS","CG","Newton"]`から選択<br>[制約ソルバ](https://mujoco.readthedocs.io/en/stable/computation/index.html#soalgorithms)のアルゴリズムを選択します。|
|iterations|**型：int〈** `"100"` **〉**<br>制約ソルバの最大反復回数を決定します。大規模な系で多くの相互作用が発生する場合、反復回数はより多くする必要があります。|
|tolerance|**型：real〈** `"1e-8"` **〉**<br>反復ソルバの早期終了に使用される許容閾値を設定します。`"0"`に設定すると早期終了が無効になります。詳細については[制約ソルバ](https://mujoco.readthedocs.io/en/stable/computation/index.html#soalgorithms)を参照してください。|
|ls_iterations|**型：int〈** `"50"` **〉**<br>`"CG"`/`"Newton"`制約ソルバーが実行するラインサーチの最大反復回数です。|
|ls_tolerance|**型：real〈** `"0.01"` **〉**<br>ラインサーチアルゴリズムの早期終了に使用される許容閾値です。|
|noslip_iterations|**型：int〈** `"0"` **〉**<br>Noslipソルバーの最大反復回数です。`"0"`に設定された場合、後処理ステップが無効化されます。|
|noslip_tolerance|**型：real〈** `"1e-6"` **〉**<br>Noslipソルバーの早期終了に使用される許容閾値です。|
|mpr_iterations|**型：int〈** `"50"` **〉**<br>凸メッシュの衝突に使用されるMPRアルゴリズムの最大反復回数です。geomのアスペクト比が非常に大きい場合を除き、ほとんど調整する必要はありません。|
|mpr_tolerance|**型：real〈** `"1e-6"` **〉**<br>MPRアルゴリズムの早期終了に使用される許容閾値です。|
|sdf_iterations|**型：int〈** `"10"` **〉**<br>符号付き距離フィールド（signed distance field）衝突に使用される、初期…あたりの反復回数です。|
|sdf_initpoints|**型：int〈** `"40"` **〉**<br>符号付き距離フィールドの衝突で衝突点を見つけるために使用される開始点の数です。|
|actuatorgroupdisable|**型：`int(31)`〈** optional **〉**<br>無効にするアクチュエータグループのリストです。[この動画](https://youtu.be/H9qG9Zf2W44)に詳しいです。|

> 他に `o_solref`, `o_solimp`, `o_friction`も存在します。ソルバーパラメタのようですが、デフォルト値は設定されていません。

</div></details>

　flag型のオプションについて、以下に代表的なものを示します。すべてのオプションについては、その下の「使用可能な全オプションの一覧 (flag)」を参照してください。**以下の表においては、値をデフォルトから変更した際に何が起こるかの説明**を記述しています。

**表** 主要なオプション (flag)

|オプション|デフォルト値|デフォルト値から変化させた場合|
|-|-|-|
|gravity|`"enabled"`|重力（`mjOption.gravity`）を`"0 0 0"`に設定した場合と同様になります。|
|energy|`"disabled"`|運動エネルギーと位置エネルギーの計算を有効化します。エネルギーは`mjData.energy`に保存されます。`mjData.energy[2]`のデータは位置エネ、運動エネの順で格納されます。|
|fwdinv|`"disabled"`|順動力学と逆動力学の自動比較を有効化します。`mj_forward`または`mj_step`の後、自動的に逆動力学が計算され、適用された力の差が`mjData.solver_fwdinv[2]`に格納されます。最初の値が関節空間での不一致の相対ノルムで、次の値は拘束空間での値です。|
|sensornoise|`"disabled"`|センサーノイズのシミュレーションを有効にします。ノイズは平均0のガウスノイズであり、標準偏差は各センサのノイズパラメタにより決定されます。|

<details><summary>【使用可能な全オプションの一覧 (flag)】</summary><div>

|オプション|default|デフォルト値から変化させた場合|
|-|-|-|
|constraint|`"enabled"`|制約ソルバに関連するすべての標準計算（以下の4つ）が無効になります。このため、拘束力は発生しません。|
|equality|`"enabled"`|等式制約に関するすべての標準計算が無効になります。|
|frictionloss|`"enabled"`|摩擦損失の制約に関するすべての標準計算が無効になります。|
|limit|`"enabled"`|ジョイントと腱の制限制約に関するすべての標準計算が無効になります。|
|contact|`"enabled"`|衝突検出と接触拘束に関連するすべての標準計算が無効になります。|
|passive|`"enabled"`|受動的な力が無効になります。<br>（ジョイントと腱のスプリングダンパー、流体力学的な力、および`mjcb_passive`コールバックによるカスタム受動力）|
|gravity|`"enabled"`|重力（`mjOption.gravity`）を`"0 0 0"`に設定した場合と同様になります。|
|clampctrl|`"enabled"`|アクチュエータへの制御入力のクランピングを無効にします（有効にするよう設定しているものを含め）。|
|warmstart|`"enabled"`|制約ソルバのウォームスタートを無効にします。|
|filterparent|`"enabled"`|2つのgeomが親と子であるコンタクトペアのフィルタリングを無効にします。|
|actuation|`"enabled"`|アクチュエータに関するすべての力を無視します。|
|refsafe|`"enabled"`|シミュレーションタイムステップに対して`solref[0]`が小さすぎることによる不安定性を防ぐ安全メカニズムを有効にします。<br>（`solref[0]`は拘束の安定化に使用される仮想的なスプリングダンパの剛性）|
|sensor|`"enabled"`|センサに関するすべての計算を無効化します。シミュレーション開始前に無効にした場合はセンサの値がすべてゼロを、実行時に無効にした場合は最後に計算された値を保持します。|
|midphase|`"enabled"`|衝突が許可されているすべてのgeomのペアが衝突チェックされるようになります（計算速度の低下）。|
|eulerdamp|`"enabled"`|オイラー積分器のジョイント減衰に関する暗黙積分を無効にします。詳細は[MuJoCo 数値積分](https://mujoco.readthedocs.io/en/stable/computation/index.html#geintegration)を参照してください。|
|override|`"disabled"`|[Contact override](https://mujoco.readthedocs.io/en/stable/modeling.html#coverride)を有効化します。|
|energy|`"disabled"`|運動エネルギーと位置エネルギーの計算を有効化します。エネルギーは`mjData.energy`に保存されます。`mjData.energy[2]`のデータは位置エネ、運動エネの順で格納されます。|
|fwdinv|`"disabled"`|順動力学と逆動力学の自動比較を有効化します。`mj_forward`または`mj_step`の後、自動的に逆動力学が計算され、適用された力の差が`mjData.solver_fwdinv[2]`に格納されます。最初の値が関節空間での不一致の相対ノルムで、次の値は拘束空間での値です。|
|invdiscrete|`"disabled"`|`"RK4"`以外のすべての積分器に対して、`mj_inverse`による離散時間逆動力学を有効にします。|
|sensornoise|`"disabled"`|センサーノイズのシミュレーションを有効にします。ノイズは平均0のガウスノイズであり、標準偏差は各センサのノイズパラメタにより決定されます。|
|multiccd|`"disabled"`|（実験的な機能です）<br>[libccd](https://mujoco.readthedocs.io/en/stable/computation/index.html#cochecking)ベースの多重接触衝突検出を有効にします。凸凹な物体の衝突の安定化につながる可能性があります。|
|island|`"disabled"`|制約の島（constraint islands）の検出を有効化します。物理には影響せず、可視化にのみ使用することが可能です。|

</div></details>

### option詳説 : timestep

　timestepオプションはシミュレーション時のタイムステップ幅を決定します。タイムステップ幅は**シミュレーション全体に影響を与える重要なパラメタ**です。値が小さいほど精度と安定性が向上する一方で、シミュレーションにかかる時間や発散の可能性が増大するなどの欠点も存在します。特に、多くの浮動オブジェクトが存在する場合は接触の量も増大するため、その計算負荷も増大します。また、**安定性はソルバのパラメタにも依存する**ため、モデルによっては両方の設定の組み合わせを調整する必要があります。

　また、リアルタイム性能を達成するためには、時間ステップを1ステップあたりのCPU時間より大きくする必要があります（RK4積分器を使用する場合は4倍）。シミュレーションによってCPU時間は変化するため、時間ステップを変更する際には前もって検証を行う必要があります。

　実際に検証した結果については、[タイムステップ幅のシミュレーション結果への影響](#タイムステップ幅のシミュレーション結果への影響)節にも記述しています。

> 最適化関連のシミュレーションでは、タイムステップ幅を可能な限り大きくすることが求められます。許容範囲は要求によっても変化するので、値を決めるのは結構難しいと思いますが…

## MJCFの要素 : keyframe

### 概要 : keyframe (MJCF)

　**Keyframe**は、シミュレーション状態変数のスナップショットであり、システムを特定の状態へリセットする際に役立ちます。関節位置のベクトル、ジョイントの速度、アクチュエータの起動、シミュレーション時間の指定が可能です。ただし、(標準では)軌跡データを保存することはできません。そもそもKeyframeは軌跡データを保存するためのものではないため、そのように使用しない方がよいと思います。

　未定義のキーフレーム属性は`qpos`属性を除きすべてのデータが０に設定されます。また、プログラムの実行時に`mjModel.key_time`などのメンバを通してKeyframeのデータを設定することも可能です。

<details><summary>【mjModelのキーフレーム関連メンバ】</summary><div>

　プログラム実行時に以下の`mjModel`メンバからキーフレームの値を取得・変更することが可能です。`name_keyadr`を除いたすべてのメンバは、Pythonで実行した場合いずれも`float64`の`np.ndarray`として使用されます。したがって、例えばキーフレームを3つ定義した場合に`key_qpos`のサイズは`(3,7)`となります。

|メンバ名|型|説明|
|-|-|-|
|`name_keyadr`|int(n)<br>(nkey × 1)|各keyframeの名前のポインタです。名前本体は`mjModel.names`に`\x00`区切りのバイナリとして格納されています。下に取得方法の例を示します。|
|`key_time`|mjtNum(n)<br>(nkey × 1)|各keyframeの`time`です。|
|`key_qpos`|mjtNum(n)<br>(nkey × nq)|各keyframeの`qpos`です。|
|`key_qvel`|mjtNum(n)<br>(nkey × nv)|各keyframeの`qvel`です。|
|`key_act`|mjtNum(n)<br>(nkey × na)|各keyframeの`act`です。|
|`key_ctrl`|mjtNum(n)<br>(nkey × nu)|各keyframeの`ctrl`です。|
|`key_mpos`|mjtNum(n)<br>(nkey × 3*nmocap)|各keyframeの`mpos`です。|
|`key_mquat`|mjtNum(n)<br>(nkey × 4*nmopcap)|各keyframeの`mquat`です。|

> `mjtNum` : `double`または`float`の`typedef`定義です。基本的に倍精度（`double`）を前提にすればよいと思います。したがって、Pythonで使用する場合には基本的に`float64`として扱われます。

```python
model = mujoco.MjModel.from_xml_path("input.xml")

# modelから、0番目のkeyの名前を取得する
name = mujoco.id2name(model,
                      mujoco.mjtObj.mjOBJ_KEY,
                      0)
```

</div></details>

<details><summary>【keyframeの使用例】</summary><div>

　キーフレームの使用例を以下に示します。キーフレームはプログラム実行の際に、関数`mujoco.mj_resetDataKeyframe`を用いて適用します。

　以下MJCFモデルの場合、モデル内には浮遊体が１つあるだけですので、`qpos`で指定する値は位置 $(x,y,z)$ と姿勢 $(q_1,q_2,q_3,q_4)$ だけとなります。また、同様に`qvel`は速度 $(v_x,v_y,v_z)$ と各軸回りの角速度 $(\omega_x,\omega_y,\omega_z)$ だけを指定することになります。qposおよびqvelの詳細は、[ジョイントが複数ある場合のqpos、qvel](#ジョイントが複数ある場合のqposqvel)節を参照してください。

```xml:spinning_sphere.xml
<mujoco>
  <worldbody>
    <body name="top" pos="0 0 .02">
      <freejoint/>
      <geom name="ball" type="sphere" size=".02" />
    </body>
  </worldbody>

  <keyframe>
    <key name="spinning" qpos="0 0 0.02 1 0 0 0" qvel="0 0 0 0 1 200" />
  </keyframe>
</mujoco>
```

```python
model = mujoco.MjModel.from_xml_path("spinning_sphere.xml")
data = mujoco.MjData(model)
mujoco.mj_forward(model, data)

# キーフレームを使用して初期位置と初速度を与える
mujoco.mj_resetDataKeyframe(model, data, 0)  # 0番目のkeyを使用して初期化

# シミュレーションを進める
...
```

</div></details>

### 属性 : keyframe (MJCF)

　Keyframeは子要素に`<key>`を持ち、各`<key>`が個別の初期化用データとなります。`<key>`の持つ属性は以下の8つであり、すべて任意です。このうち、qposおよびqvelの詳細は[ジョイントが複数ある場合のqpos、qvel](#ジョイントが複数ある場合のqposqvel)節を参照してください。

**表** すべての属性（`<key>`）

|属性|型|例|説明|
|-|-|-|-|
|`name`|string|`"foobar"`|キーフレームの名前|
|`time`|real|`"0"`|シミュレーションの時刻を指定し、`mjData.time`にコピーされます|
|`qpos`|real(nq)|`"0 0 1 1 0 0 0"`<br>(浮遊体×1の場合)|ジョイント状態を指定し、`mjData.qpos`にコピーされます|
|`qvel`|real(nv)|`"0 0 0 1 1 0"`<br>(浮遊体×1の場合)|ジョイント速度を指定し、`mjData.qvel`にコピーされます|
|`act`|real(na)|`"0 0 ..."`|アクチュエータを指定された値で起動します。`mjData.act`にコピーされます|
|`ctrl`|real(nu)|`"0 0 ..."`|コントロールを指定します。`mjData.ctrl`にコピーされます|
|`mpos`|real(n)<br>(3*nmocap)|`mjModel.body_pos`に同|ボディ位置 $(x,y,z)$ を指定し、`mjData.mocap_pos`にコピーされます|
|`mquat`|real(n)<br>(4*nmocap)|`mjModel.body_quat`に同|ボディ姿勢のクォータニオン $(q_1,q_2,q_3,q_4)$ を指定し、`mjData.mocap_quat`にコピーされます|

## MJCFの要素 : asset

### 概要 : asset (MJCF)

　**asset**は（それ自体はモデル要素ではありませんが）モデル要素が参照することのできるプロパティの一つです。メッシュやスキン、高さフィールド（Height field）、テクスチャ、マテリアルから構成されます[3]。

　**メッシュ**と**スキン**はどちらもOBJファイルやSTLファイルから読み込むことのできるものであり、このうちスキンは実行時に形状を変形することができます。（おそらく）メッシュは物理的性質を持ち、一方スキンは外観にのみ影響を持つようです。**高さフィールド**は(グレースケール化された)PNGファイル、またはバイナリファイルから読み込むことができます。地図の標高画像のようにして地形を生成できるものと思われます。**テクスチャ**はPNGファイルや既定のものから読み込むことができます。**マテリアル**は`geom`, `site`, `tendon`の外見を制御するために使用することができます。

詳細は[XML Reference/asset](https://mujoco.readthedocs.io/en/stable/XMLreference.html#asset)を参照してください。

### よく使うasset集

　以下によく使うアセットをいくつか提示します。`<mesh>`や`<hfield>`に関しては[geom>type](#bodygeom詳説--type)節を参照してください。

チェック柄のアセットです。地面に張り付けて使用されることが多いです。

<details><summary>【チェック柄のアセット】</summary><div>

```xml
<asset>
  <texture name="grid" type="2d" builtin="checker" rgb1=".1 .2 .3"
   rgb2=".2 .3 .4" width="300" height="300"/>
  <material name="grid" texture="grid" texrepeat="8 8" reflectance=".2"/>
</asset>
```

　ビルトインのチェック柄テクスチャを、`"grid"`というマテリアルとして定義しています。地面に適用する場合、以下のように使用します。

```xml
<geom size="1 1 .5" type="plane" material="grid"/>
```

</div></details>

空（背景色）を青くするアセットです。

<details><summary>【青空のアセット】</summary><div>

```xml
<asset>
  <texture type="skybox" builtin="gradient" rgb1="1 1 1" rgb2=".6 .8 1" width="256" height="256"/>
</asset>
```

　空を青くするアセットです。これをMJCFモデルに記述するだけで空の色が変わります。`rgb2`属性を変更することで色を青から変更することも可能です。

</div></details>

## 参考文献

[0] [google-deepmind, mujoco, GitHub](https://github.com/google-deepmind/mujoco)

[1] Tom Erez, Yuval Tassa and Emanuel Todorov. "Simulation Tools for Model-Based Robotics: Comparison of Bullet, Havok, MuJoCo, ODE and PhysX"

[2] [Intuitive Explanation of Tippe Top Effect?](https://physics.stackexchange.com/questions/213290/intuitive-explanation-of-tippe-top-effect)

[3] [MuJoCo Documentation, Overview](https://mujoco.readthedocs.io/en/stable/overview.html)

[4] [tutorial.ipynb - Colaboratory](https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/tutorial.ipynb)

[5] [カオス理論](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%82%AA%E3%82%B9%E7%90%86%E8%AB%96#:~:text=%E3%82%AB%E3%82%AA%E3%82%B9%E7%90%86%E8%AB%96%EF%BC%88%E3%82%AB%E3%82%AA%E3%82%B9%E3%82%8A%E3%82%8D%E3%82%93%E3%80%81%E8%8B%B1,%E3%82%AB%E3%82%AA%E3%82%B9%E5%8A%9B%E5%AD%A6%E3%81%A8%E3%82%82%E3%81%84%E3%81%86%E3%80%82)

[6] [MuJoCo Documentation, Modeling](https://mujoco.readthedocs.io/en/latest/modeling.html)

[7] [Touch Sensor not able to capture contact force During Collision. #228](https://github.com/google-deepmind/mujoco/issues/228)

[8] [MuJoCo Documentation, XML Reference](https://mujoco.readthedocs.io/en/3.1.3/XMLreference.html)