3DロボットシミュレータのWebotsでTrack（履帯）を使う

## 概要
ロボットシミュレータのWebotsで，~~みんな大好き~~Track（履帯，いわゆるキャタピラ）を使うロボットを作ろうとしたところ，
参考文献がほぼ英語の公式ドキュメントしかなく，わからん過ぎて苦労しました．ので，備忘録を兼ねて記事にまとめます．

※公式ドキュメント等
[Track - Webots Reference Manual](https://cyberbotics.com/doc/reference/track)
[TrackWheel - Webots Reference Manual](https://cyberbotics.com/doc/reference/trackwheel)
[track.wbt - cyberbotics/webots github]()

この記事では，以下のような簡単な仕様のTrack付きロボットを作ることをゴールとします．

* 直方体の本体と，二つのTrack（履帯）からなるロボットを作る
* ロボットの持つTrackは，同じ大きさのTrackWheelを二つと，LinearMotorを持つ
* Geometries Animationを使って履帯の動きを表現する

なお，本記事作成で用いた環境は以下のものです．

* Windows 10
* Webots R2021a
* Controllerの実装: C++
* ノートPC（ThinkPad X13 Gen1, AMD RYZEN Pro 7, 16GB RAM）


## 作業手順
### ワールドの作成
今回は履帯付きのロボットを走らせることが目的なので，ワールドはウィザードを使ってサクッと作ってしまいます．
Webots上部メニューの`Wizards` > `New Project Directory...` から，ある程度の環境がそろったワールドを手軽に作成できます．
名称以外基本デフォルトで大丈夫ですが，途中チェック項目が並ぶところで `Add a rectangle arena`にチェックを入れると最初から床面が生成されてさらに楽です．
プロジェクトを作成すると，図のように床面のある環境が表示されます．
![trackTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166634/2e98c2aa-cb3c-ad55-f519-7ff77e37b291.png)

### 筐体の作成
#### 本体の作成
履帯の動きを見たいので，本体は適当に作ります．

 1. Webots左側のメニュー（Scene Tree）内の適当な項目をクリックしてScene Treeを選択する．
 1. 右クリック > `Add new...` で `Base Nodes` > `Robot` を選択し，ロボットノードを追加．
 1. ちょっと持ち上げていたいので，Robotの`transition`を`(0, 0.05, 0)`に指定
 1. 作成したRobotのchildreをダブルクリックし，Shapeを指定して追加
 1. 当たり判定のboundingObjectをShapeと同じにしたいので，Shapeをクリックして表示される`DEF`入力欄に任意の名前を入れ，Shapeに名前を付ける．  
 (本記事では`DEF`を"`BOX`"に指定します)
 1. ShapeのgeometryをダブルクリックしてBox型を指定する．本記事では大きさを`(0.2, 0.05, 0.1)`とした．
 1. （オプショナル）さすがにさみしいので見た目をマシにするために立体表面の描画情報を追加する．  
 Shapeのappearanceをダブルクリックして`Appearance`を指定 → `Appearance`の`material`をダブルクリックして`Material`を指定
 1. Robotの`boundingObject`をダブルクリックし`USE` > Shapeにつけた名前（ここでは`BOX`）を指定し，当たり判定を付加する
 1. Robotのphysicsをダブルクリックし，Physicsを指定する

ここまで同じ手順を行うと，下図のように少しだけ宙に浮いたボックスが表示されます．
また，**この状態を保存したうえで**シミュレーションを実行すると，箱が落下して床の上に落ちる様子が見られます．
![trackTest_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166634/64950a0a-745d-a030-a959-fa2336d024af.png)

#### 履帯の作成
履帯をくっつける本体ができたので，次は履帯を作ります．
Robotのchildrenを右クリックして`Add new`を押し， `Base nodes` > `Track` を追加します.  

これだけでは何も表示されないので，適切なパーツを追加していきます．

##### `TrackWheel`の追加
 `TrackWheel`は履帯の内外に設置され，ベルトを動かしたり形作ったりする車輪です．
実際の履帯ではその役割によって駆動輪や転輪など複数種類があるのですが，Webotsではこれらの区別はなくすべて`TrackWheel`として扱われます．
今回は先述のとおり，二つの`TrackWheel`を一つの履帯の中で使用します．

1. Trackのchildrenをダブルクリックし，`BaseNode` > `TrackWheel`を追加します．
1. `TrackWheel`の`position`を`(0.1, 0)`に(`Track`の制約上`TrackWheel`は二方向にしか位置調整できません)，`radius`を`0.03`に指定します．
1. このままだと`TrackWheel`は何も描画されないので，その位置や回転がわかるようにします．  
`TrackWheel`は見た目によらず円柱の車輪系になるため，見た目は回転していることがわかりやすいよう`box`を指定します．  
`TankWheel`の`children`に`Shape`を追加し，`geometry`を`(0.035, 0.035, 0.03)`サイズの`Box`にします`appearance`も同様に`Apparance` > `Material`まで指定します．
1. `TrackWheel`を複製します．コピーし，`Track`の`children`にペーストします．
1. 二つある`TrackWheel`のうちどちらかの`Transition`を`(-0.1, 0)`に指定し，筐体後部に移します．

ここまでできると，下図のようによくわからない状態になっているはずです．
![trackTest_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166634/7cd6866f-6fe3-629e-fd4b-2d8971420504.png)


##### ベルトの追加
Webotsに用意されるGeometry Animationを使えば，配置した`TrackWheel`間をつなぐように設置され，回転するベルトを簡単に作ることができます．

1. `Track`の`animatedGeometry`に，ベルトとして`TrackWheel`を取り囲む図形を指定します．ここでは，`geometry`に`(0.02, 0.003, 0.03)`サイズの`box`を，`appearance`に`diffuseColor`が`(0.1, 0.1, 0.1)`の`Material`を持つ`Appearance`を指定します．
1. `Track`の`geometriesCount`には，1.で指定した図形がいくつ用いられるかの数を思惟します．ここでは`20`に指定します．

ここまで操作すると，下図のようになります．ベルトらしきものが描画され，それっぽくなってきました．
なお，このベルトの立体は見た目だけのものであり，実際の履帯の振る舞い・物理シミュレーション結果には関係ないことにご注意ください．
![trackTest_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166634/5a5d84d6-76b1-da13-e6a2-042d8f169f4f.png)

##### 履帯の当たり判定を設定
地面に動力を伝える，当たり判定の部分を`boundingObjectとして追加し設定します．
履帯の形に合わせる必要はないのですが，今回はCylinder２つとBox１つを使い，履帯と同じ形のboundingObjectを作ります．

1. `View` > `Optional Rendering` > `Show All Bounding Objects`を押し，boundingObjectsが見えるようにします．
1. Trackの`boundingObject`に，`Group`を指定します．
1. `Group`の`children`に，`(0.2, 0.06, 0.03)`サイズの`box`を追加します．
1. `Group`の`children`に，`Transform`を追加し，`transition`を`(0.1, 0, 0)`に，`rotation`を`(1, 0, 0, -1.5708)`にします．
1. `Transform`の`children`に，`height`が0.03, `radius`が0.03の`Cylinder`を追加します．
1. `Transform`を複製して，`transition`を`(-0.1, 0, 0)`にします．なお複製の際に`Cylinder`が消えてしまうので，これもコピペなどで追加します
1. `Track`の`physics`に`Physics`を指定します．
1.`Track`を複製します．片方の`transition`を`(0, 0, -0.05)`に，もう片方を`(0, 0, 0.05)`にし，車体の両側に設置してやります．

ここまでやると，下図のようになるはずです．また**保存してから**シミュレーションを実行すると，履帯で接地する様子が見られます．
もう完成は目前です．
![trackTest_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166634/ed801d1e-d4b7-0316-be9c-d86a27b73f94.png)

###挙動の設定

#### モータの回転を設定
あとは履帯が回転するようにしてやればおｋです．
本当はもう１ステップありますが，とりあえずこれをやってやると本体が動くようになります．

1. モータを追加します．`Track`の`device`に`LinearMotor`を指定し，`name`に適当な名前を入れます．  
これをもう片方の履帯にもやってやります．なお，`name`はController作成時に使うので左右のモータで違う名前になる様にしてやってください．
本記事では，`leftMotor`および`rightMotor`としました．
1. コントローラを作成します．`Wizards` > `New Robot Controller...`から，お好みの言語と名前を指定します．  
本記事ではC++を用い，`trackController.cpp`という名前のファイルを作成します．
作成したコントローラは，`Robot`の`controller`で指定して本体と紐づけてやります．
1. コントローラを実装します．大体以下のようにしてやるとよいです．

```cpp
//L10
#include <webots/Robot.hpp>
#include <webots/Motor.hpp> //追加

//L25付近
  Robot *robot = new Robot();
//以下を追加
  Motor *leftMotor, *rightMotor;
  leftMotor = robot->getMotor("leftMotor");
  rightMotor = robot->getMotor("rightMotor");
  leftMotor->setPosition(INFINITY);
  rightMotor->setPosition(INFINITY);
  leftMotor->setVelocity(0.1);
  rightMotor->setVelocity(0.1);
//以上を追加
```
あとは**ワールドを保存して**，ビルド，実行します．
（Webotsの動画書き出し機能で出力したので，実際よりだいぶなめらかな映像になってます）
![プレゼンテーション1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166634/3ad74d0f-5a4e-802b-8249-eaf4ee32e258.gif)
いいですね，~~僕は履帯が好きなので~~めっちゃテンション上がります

#### 摩擦の設定
一見ここまでで完成したように思えますが，この段階では履帯の摩擦の設定ができておらず，両モータの出力値の値が同じ場合のみ正しく動くようになっています．
試しに，片方のモータ値にマイナスをつけ，その場で旋回（超信地旋回）させようとするとうまく動かないことがわかると思います．
ですので，正しく旋回ができるよう，最後に履帯ー地面間の摩擦を設定してやります．

webotsでは，摩擦の設定は

- 摩擦の設定をしたい物体に素材名をつける
- `WorldInfo`から素材名を二つ指定し，その素材名を持つ物体間の摩擦係数を設定する

という手順で行います．ですので，ここで行う作業は以下のようになります．

1. `Track`の`contactMaterial`に適当な素材名を指定します．ここでは"trackMaterial"とします．これを両方の履帯について行います．
1. `WorldInfo`の`contactProperties`に，`ContactProperties`を追加します．
1. 追加した`ContactProperties`の`material2に先ほど指定した素材名（ここでは"trackMaterial"）を指定します．
1. `ContactProperties`の`coulombFriction`を500に，`forceDependentSlip`を1に指定します．  

ここまで完了したら，左右モータに正負逆の値を入れてやると超信地旋回をするようになります．
![hoge.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166634/7daae839-4fb9-b15c-908f-63f64a0e721b.gif)

お疲れさまでした！

## おわりに
車輪とかと同様にサクッとつけられるかなと思っていたんですが，結構いろいろやることがあって大変でした．
忘れんうちに記事にまとめられてよかったなという感じがします．

なお，最後に指定したパラメータの値ですが，動いたのでオッケーって感じで正直最適な値をいくらにすればいいのかわかってません．
特に`forceDependentSlip`に関しては，ほかのworldだと0のまんまでも超信地旋回できたりするので・・・・謎です
~~できたやつもそもそも履帯の回転量に対して旋回量少ない気がするし・・・・~~
もう少し調べてみて，わかったら追記などしていこうと思います．

※本記事で作成したコードをgithubに公開しました
[https://github.com/ken551/webots_track_test/tree/main](https://github.com/ken551/webots_track_test/tree/main)