Unreal Engine 5を使ってみる その7(Open Manipulator)
はじめに
前回、Unreal Engine 5(UE5)でロボットアームのようなものを作成しました。今回はROBOTIS社がオープンソースとして公開している、Open Manipulatorというロボットアームのモデルを作成してみました。
とある企業のロボットアームのモデルも作成したのですが、ライセンスの話が怖いので公開はしていません。その点、オープンソースであることは非常にありがたいですね。
Open Manipulatorは実機をお借りしているので、実際の動作も反映させていきたいなと思っています。
▼OpenSoftware、OpenHardwareと書かれています。STLファイルが公開されています。
https://wiki.ros.org/open_manipulator
▼GitHubのリポジトリはApache-2.0 licenseです。
https://github.com/ROBOTIS-GIT/open_manipulator
▼以前の記事はこちら
3Dモデルの作成
CADデータの取り込み
GrabCADにあったCADデータはSTEP形式だったので、以前の記事で使ったDataSmithで取り込むことができました。
▼GrabCADのデータはこちら
https://grabcad.com/library/openmanipulator-x-frame-set-rm-x52-1
今回はSTL形式のデータを取り込みます。STL形式だとDataSmithでも直接取り込めないようだったので、一旦Blenderで変換します。
STL形式でインポートできて、他の形式でエクスポートできるソフトウェアであれば何でも良いかと思います。普段設計するときに使っているFusion360でも変換できたことがあります。
▼ブラウザ上でFBXに変換できるページもありました。
https://products.aspose.app/3d/jp/conversion/stl-to-fbx
▼Blenderのダウンロードページはこちら
▼Open Manipulatorのデータは、GitHubのリポジトリにある7つのファイルを用います。
https://github.com/ROBOTIS-GIT/open_manipulator/tree/master/open_manipulator_description/meshes

▼Open Manipulatorの細かい部品まで含まれているデータはこちらにあります。今回はネジとかまでは考えません。

Blenderで取り込んで、変換してみます。
▼STL形式でImportします。

右上にあるCollectionに追加されるので、必要なものを選択してExportします。
▼Unreal Engineの画面と似ていますね。

▼FBX形式でExportします。この形式なら、Unreal Engineでそのまま取り込めます。

▼Export直前でも設定を変更できるようです。

Export出来たら、コンテンツドロワーに入れます。
▼Import前にオプションが開きます。

▼インポートされました!

インポートできたデータをそのまま配置してみたのですが、巨大なオブジェクトになっていました。
▼左下のほうに見えている小さいオブジェクトが、サードパーソンキャラクターとかです。

どうやらBlenderとUnreal Engineでは単位が違うようです。
▼調べてみると、こちらに書かれていました。
https://qiita.com/ishiitakeru/items/2adc260d71a6b0a2c640
Blenderの画面右側にある設定欄で、単位を確認することができます。
▼Unit SystemがMetricになっています。メートルですね。

今回はUnreal Engine側のトランスフォームで、縮尺を変更しました。
▼0.001でいい感じのようでした。


▼実際に手で掴むとこのくらいの大きさです。

▼GrabCADで取り込んだデータを並べた様子。大体同じかと思います。

▼ちなみに0.01だと人ぐらいの大きさになります。


ブループリントクラスの作成
Pawnのブループリントクラスとして作成していきます。
▼コンテンツドロワーで右クリックし、ブループリントクラスを作成します。


先程取り込んだCADのデータを追加します。
▼そのままだと、中心に集まっています。

▼ここでも詳細タブでトランスフォームが使えます。

▼部品が小さくて操作しにくいので、カメラ速度は下げておくことをお勧めします。

部品の配置
部品を配置していくのですが、回転軸やジョイントの命名はOpen Manipulatorのドキュメントに合わせておきます。
▼Open ManipulatorのInertiaに関するページはこちら。
https://emanual.robotis.com/docs/en/platform/openmanipulator_x/specification/#inertia
▼データを取り込むと、以下のような構成になります。

ここにJoint1~4を追加します。
▼コンポーネントにスタティックメッシュコンポーネントを追加し、Joint1~4にします。これを回転軸とします。

土台部分からtest_chain_link1~5、掴む部分のtest_chain_link_grip_r/lがあります。これらを親子付けして、土台側の部品が動くと、ハンド側の部品も一緒に動くようにします。
▼以下のように親子付けしました。

それぞれのジョイントの配置と相対位置を設定します。このとき、土台側から設定していきます。親の要素を後で移動すると、子の要素も一緒に移動してしまいます。
▼相対座標かワールド座標かは、トランスフォームで確認できます。

それぞれのパラメータとジョイントの位置、ドキュメントでの軸の設定を載せておきます。(間違えていたり、ずれていたりするかもしれません)
▼Joint1はこちら



▼Joint2はこちら



▼Joint3はこちら



▼Joint4はこちら



各Jointの子になっている部品は、相対位置がすべての軸に対して0になります。それに対し、Gripは調整する必要があります。
▼Gripのパラメータは以下の通りです。



あとはPawnクラスとして所有したときにカメラが有効になるので、見やすい位置に配置しておきます。
▼完成するとこんな感じ。

▼カメラは大きかったので縮小しています。視野とかは変わらないみたいです。

ブループリントの作成
変数の設定
回転軸の操作については、以前の記事とほとんど同様です。キーボードの割り当ては、実際にOpen Manipulatorをキーボードで操作するときのものに合わせています。
▼ROSで操作するときの、Keyboard Teleoperationに合わせました。
▼今回は以下のキーを割り当てます。
y : increase joint 1 angle
h : decrease joint 1 angle
u : increase joint 2 angle
j : decrease joint 2 angle
i : increase joint 3 angle
k : decrease joint 3 angle
o : increase joint 4 angle
l : decrease joint 4 angle
g : gripper open
f : gripper close
▼定義した変数は以下の通りです。後でカメラも操作できるようにします。

▼Increase、Decreaseはそれぞれデフォルト値に1とー1を割り当てています。


▼Joint用のキーは、入力すると変数に状態を保存します。

回転の設定
以前の記事とは違って、回転に使うノードを変更しました。これまではAdd Relative Rotationノードを使っていたのですが、変な挙動を示すことがありました。
▼動画のように、回転できずに振動していることがありました。
調べてみると、回転にはWorld、Local、Relativeに対するノードがあるようです。
▼こちらで議論されていました。gif付きで分かりやすいです。
Relative Rotationの場合、親に対するローカル軸で回転するようです。とはいえ、なぜそんな挙動になるのかはよく分からなかったのですが、Localに対する回転にすれば良さそうです。
今回はAdd Local Rotationノードを使います。
▼Branchで判定を行い、TrueかFalseかに依らず次の判定に移行するようになっています。


▼ここまでで軸の割り当てが間違っていなければ、動画のように動きます。後で動かします。
Gripperの開閉
回転だけでなくGripperの開閉が必要なのですが、タイムラインノードとSet Relative Locationノードで動くようにしました。
▼ノードの構成はこちら。右と左のGripではY軸方向に逆向きに動くので、ー1をかけています。

タイムラインノードはPlayのときに開き、Reverseのときに閉じるように設定します。ダブルクリックすると、編集画面が開きます。
▼開いたときはまだ何もありません。

▼ベクタートラックを追加します。


▼色付きの線を右クリックすると、キーを追加できます。

Gripを配置したときの相対位置を参考に、以下のようにパラメータを調整します。Y軸だけ変化します。
- X:ずっと8150
- Y:0s時点で3600、1s時点で950
- Z:ずっと0
▼設定するとこんな感じ。左右矢印のボタンを押すと、ぴったり収まる大きさに調整してくれます。

カメラの移動
これまでとほとんど同じなのですが、カメラの位置をAdd Relative Locationで移動できるようにしてみました。条件分岐とかは、Jointの時と同じ考え方です。
▼WASDで前後左右に動きます。

▼JointのときのBranchと、カメラのBranchは繋がっています。全体像はこちら。

レベルに配置して動かしてみる
Pawnクラスの所有
新しくBasicレベルを作成しました。
▼新規レベルから作成できます。


▼Open Manipulatorを3台と、ThirdPersonCharacterを配置しました。


▼ThirdPersonCharacterについては無くてもいいのですが、必要であれば「機能またはコンテンツパックを追加」から追加できます。大きさの比較用です。

Pawnクラスとして作成したので、操作するときは所有する必要があります。これも以前の記事と同様です。
▼レベルブループリントに追加します。

▼それぞれキーボードの数字を入力すると所有できるようになっています。

動かしてみる
▼それぞれのPawnクラスに対して、所有して動かしてみました。カメラも動かせます。
▼配置を変えてみました。井戸端会議感があります。
▼開閉もできています。


最後に
前のキューブだけのモデルよりも、よっぽどロボットアームっぽくなりました。性能面を追求したいところです。
ThirdPersonCharacterに比べると、小さく見えるような、ちょうどいい大きさのような気がしてよく分からないですね。もっとカメラを近づけたらそれなりの大きさに見えるのでしょうか。
今回は軸の回転とGripperの開閉をプログラムしただけなので、Collisionの設定やROSで操作するときのPoseの設定なども追加したいところです。複数人やAIでの操作も面白そうです。
軸が増えると計算が難しくなると聞いたので、もしかしたらUnreal Engine上で計算式を作成するのはできないかもしれません。そこはNode-REDを経由した外部ブログラムとの連携を実現させて解決したいなと思っています。