自作コースでの問題について

こんにちは。
いつもお世話になっております。

現在choreonoid上で、ロボカップに出てくるContinuousLampというコースを作成し、AizuSpiderを使って走行テストをしています。
しかし、一番奥の坂を登ろうとすると、ロボットが坂をすり抜けてしまい、途中で動かなくなってしまいます。
原因もわからない状態です。

なにか心あたりございませんでしょうか。
ContinuousLamp.cnoid (12.0 KB)
stage.body (4.8 KB)

こちら調査したのですが、どうもExtrusionノードで生成している形状について、面の向き(法線の向き)がモデルの内側を向いてしまっており、それで衝突の処理が想定通りにいかなくなっているようです。

Choreonoid開発版の最新版での検証結果が下図になります。

ここでまず左下のプロパティビューにて、ContinuousLamp_Stageモデルに対して、「表示リンクの選択」をtrueにして、右下の「リンク/デバイスビュー」で選択しているリンクのみを表示するようにし、これによって一番奥の坂の部分だけを表示しています。(この機能は最新版で利用できます。)

そして、シーンビューの設定ダイアログで、「法線の表示」のチェックを入れて、モデルの法線を表示しています。倍率をデフォルトから少し増やして0.1として、法線が分かりやすくなるようにしています。

この表示で緑色の線が法線です。法線はモデルの各頂点に対して付与されています。ただこれはよくみるとモデルの内側を向いているのです。例えば右下の頂点から鉛直上向きに緑の線が出ていますが、これは下の面に対応する法線で、本来下向きに出ていなければならないのですが、上向きにでています。なぜ下の面に対応する法線かというと、法線は面に対して垂直になるので、向きが合うのは下の面のみとなるからです。逆に斜め下方向に出ている法線がありますが、これは斜めになっている上の面に対応する法線です。これも本来上向きになっていなければなりませんが、反対方向になっています。あと横に出ている法線も同様ですね。要は法線が出ている方向を表側、その逆を裏側として衝突の計算を行うのですが、それが逆になっているので、むしろ面の裏側から衝突したかのような力の計算になってしまい、坂にめり込んでいく挙動となっているようです。

本来Extrusionノードで面の向きまで想定通りにできればよいところはあります。手前の坂のところはそのあたり(たまたま)うまくいっているようですね。これは今後改善できないか検討してみます。ただ面の表裏を正しく揃えるのは案外面倒な処理でして、Extrusionノードの実装においてその部分の作り込みが出来ていなかったようです。そのような次第で、Extrusionノードでこの症状になっていることはなかなか分かりにくかったかと思いますし、なんだか落とし穴を作ってしまったようで、申し訳なく思います。

解決方法ですが、Extrusionの頂点の順番などを変えることで、ひょっとすると想定通りの向きにできるかもしれません。他にはIndexedFaceSetを使えば、面の向きを完全に制御できます。面は頂点が半時計回りになる方向が表となります。(オプションでその逆にすることもできます。)

ただし干渉チェックや物理計算の処理的には、ExtrusionやIndexedFaceSetによるメッシュというものは、あまり適切でないところがあります。そこのところはプリミティブの組み合わせで表現する方がよいかもしれません。特にゲーム用の物理エンジンやAGXエンジンについては、プリミティブにした方が計算効率も安定性も向上するようです。

これについて、今回と同様のモデルをプリミティブ(Box)で組んだものが、Choreonoidソースの中にサンプルとして入っています。これはsample/WRS2018/model/TerrainE.body が該当しておりまして、WRSで使用したモデルです。この中に

BLOCK: &BLOCK
  type: Transform
  elements:
    -
      type: Shape
      translation: [ 0.24, 0, 0.195 ]
      geometry: { type: Box, size: [ 0.02, 0.5, 0.11 ] }
      appearance: *APP
    -
      type: Shape
      translation: [ -0.006, 0, 0.1854 ]
      rotation: [ 0, 1, 0, -12.9 ]
      geometry: { type: Box, size: [ 0.494, 0.5, 0.02 ] }
      appearance: *APP
    -
      type: Shape
      translation: [ 0, 0, 0.07 ]
      geometry: { type: Box, size: [ 0.5, 0.5, 0.14 ] }
      appearance: *APP
    -
      type: Shape
      translation: [ 0.19, 0.0, 0.181 ]
      rotation: [ 0, 1, 0, -5 ]
      geometry: { type: Box, size: [ 0.1, 0.5, 0.095 ] }
      appearance: *APP
    -
      type: Shape
      translation: [ 0.0, 0.0, 0.145 ]
      rotation: [ 0, 1, 0, -12.82 ]
      geometry: { type: Box, size: [ 0.32, 0.5, 0.08 ] }
      appearance: *APP

という部分がありますが、ここでBoxプリミティブを5つ組み合わせて坂状の形状を作っています。(このファイルの残りの部分はこれを多数並べたものになります。)

ですのでこの部分を参考というかベースとして、寸法などをアレンジしていただければ、どのような環境(物理エンジン)でもうまくシミュレーションできる坂のモデルを作成できるかと思います。

あと追加のアドバイスなのですが、このContinuousLampモデルについて、モデルを部分部分に分けてそれぞれ別のリンクとしているところは、とてもよいモデリング方法です。基本的にひとつのリンクの中に非凸形状のモデルを入れ込むのはよくありませんし、それをこのように分けることで、干渉・衝突計算の処理を効率化・安定化できます。AISTSimulatorはこのあたりそこまで気を使わなくてもそこそこ動くのですが、やはり処理は重くなりますし、そもそも非凸な形状に対応していない物理エンジンの方が多いです。その場合各リンクごとに凸形状になるようにして、非凸な形状は複数のリンクで構成することになります。

その意味では、このContinuousLampモデルはまだ分け方が足りないところがあります。具体的には、smallhillsの部分は3つに分けた方がよいですし、LeargeWallの部分も壁ごとに分けて、Pollの部分もポールひとつずつに分けた方がよいです。そしてどれもプリミティブで構成するようにすれば、(AGXなどで使用する場合でも)申し分ないかと思います。

あとbasePlateの部分は恐らく何も衝突しないので、形状はなくしてしまってもよいかもしれません。衝突しないのであれば形状が無い方が処理が(若干でも)軽くなりますので。

@nakaoka
対応ありがとうございます。
アドバイス通り頂点の順番を変更し、必要なオブジェクトにはrotationを適用した結果、ロボットが坂を貫通することはなくなりました。
その他いろいろご教授くださり、ありがとうございました。