mechArm 270のUnity ArticulationBodyをゼロから再設計した話|関節制御パラメータの見直し
問題を修正し続けることをやめた。ロボットアームをゼロから作り直すことにした。
このプロジェクトについて
iPhoneで撮影した現実空間をUnityでデジタルツイン化し、ロボットアームの動作をシミュレーションで最適化してから実機に展開するパイプラインを個人で構築している。
ターゲットはシンプルだ。ロボットアームがコーヒーカップを掴み、台座の上に置く。その一連の動作を、iPhoneとUnityだけで設計できることを証明する。
この記録は「正しい土台を作るために一度全部崩した」話だ。
シーンの座標系をゼロ原点に統一した
最初にやったのは、ロボットアームの位置を X=0, Y=0, Z=0 に設定することだった。
それまでロボットは X=-0.25, Y=0, Z=-1 という「なんとなく置かれた」位置にいた。この状態でカップや台座の座標を決めても、数値に根拠がない。関節角度の計算も、「あの位置にいるロボット」に対するアドホックな解にしかならない。
ロボットを原点に置くことで、全てのオブジェクトの座標が「ロボットからの相対距離」という意味を持つようになる。
ロボットアーム: X= 0, Y= 0, Z= 0
コーヒーカップ: X=-0.15, Y= 0, Z= 0.2 (右前方15cm、前方20cm)
台座(Cube): X= 0.15, Y= 0.0125, Z= 0.2 (左前方15cm、前方20cm)
PlaceTarget: X= 0.15, Y= 0.025, Z= 0.2 (台座の上面)台座のY座標 0.0125 は、CubeのScaleが Y=0.025(高さ2.5cm)なので、その半分が中心高さ、上面は 0.025 になる計算だ。PlaceTargetをその上面に合わせることで「カップを台の上に置く」という体験が数値として成立した。
3Dスキャンモデルの向きを修正した
iPhoneで3Dスキャンしたコーヒーカップのモデルは、書き出し時の向きの問題で横倒しになっていた。
UnityのPrefab編集モードで Rotation X=-90, Z=-90 を設定して正立させた。さらに取手の向きをロボットアーム側に向けた。
取手の向きはビジュアルの問題ではない。アームが「どこから掴むか」の根拠になる。取手がロボットの反対側を向いていれば、エンドエフェクター(アームの先端)が取手に届く姿勢が変わる。把持角度の設計はここから始まる。
ロボットアームの「傾き問題」を根本から解決した
しばらく前から、シミュレーションを再生するたびにロボットアームの第一関節(link1)がZ軸方向に傾いて崩れる問題が続いていた。
修正パッチを当て続けても解決しなかった。軸の計算を変えても、パラメータを調整しても、再生のたびに崩れる。
原因を特定するため、Elephant Robotics(ロボットメーカー)の公式GitHubリポジトリからURDFファイル(ロボットの物理構造を定義するXMLファイル)を取得した。
git clone https://github.com/elephantrobotics/mycobot_ros.git mecharm_official公式のjoint定義を確認すると、構造自体は正しかった。問題は、プロジェクトに取り込み済みのロボットデータが壊れていたことだ。
判断は明快だった。修正ではなく再構築。
公式URDFからロボットを新規インポートした
URDFとは「Unified Robot Description Format」の略で、ロボットの関節構造・リンク長・物理特性を定義するXMLファイルだ。これをUnityに読み込むことで、実機と同じ構造のロボットが3D空間に生成される。
UnityのURDF Importerを使って、公式データからロボットを新規にインポートした。
Import Robot from Selected URDF file
Axis Type: Y Axis
Mesh Decomposer: Unityインポート直後、URDFインポーターに付属するControllerスクリプトが暴走してジョイントが吹き飛んだ。このスクリプトを無効化し、物理パラメータを手動で設定した。
Stiffness(剛性): 20000
Damping(減衰): 2000
Force Limit(力制限): 50000
Motion: Free(制限なし)
Drive Type: Force
Stiffness(剛性)とDamping(減衰)はこのプロジェクトの「物理の憲法」だ。これまでの試行錯誤で、この値を変更するとシミュレーションが崩壊することが判明している。コードからは絶対に触れない。InspectorのUIからのみ設定する。
ベースリンクに Immovable(固定)を設定してアームを地面に固定し、プレイモードで動作確認した。
第一関節を40度回転させてもアームが傾かなかった。旧データの設定が壊れていたことが確定した。
新しいロボットアームの名前を mecharm270m5_0_0 に変更した(モデル名とインデックスを含む命名規則)。
手動ティーチングで4つの姿勢角度を取得した
新しいロボットに対して、手動でティーチングを行った。
ティーチングとは、ロボットが通過すべき姿勢を手動で作って角度データを記録することだ。工場の産業用ロボットでは実機を実際に動かして教えるが、Unityのシミュレーションではスライダーをマウスで動かして角度を調整できる。
そのために JointTeacher.cs という専用スクリプトを作った。やっていることはシンプルで、InspectorのスライダーをFixedUpdateで各関節に流し込むだけだ。
public float j0, j1, j2, j3, j4, j5;
void FixedUpdate()
{
float[] targets = { j0, j1, j2, j3, j4, j5 };
for (int i = 0; i < 6; i++)
{
var drive = joints[i].xDrive;
drive.target = targets[i]; // drive.targetのみ更新(物理の憲法遵守)
joints[i].xDrive = drive;
}
}このスクリプトをアタッチしてプレイモードを開始し、J0〜J5の値を調整してアームを操作した。取得した4つの姿勢がこれだ。
HOME(初期姿勢): J0= 0, J1= 0, J2= 0, J3=0, J4= 0, J5=0
GRASP(把持): J0= 40, J1=90, J2=-36, J3=0, J4=-50, J5=0
LIFT(持ち上げ): J0= 40, J1=65, J2=-20, J3=0, J4=-42, J5=0
PLACE(設置): J0=-40, J1=85, J2=-40, J3=0, J4=-50, J5=0HOME はアームが直立した初期状態。全関節ゼロ度。
GRASP はエンドエフェクターがカップの横に届く姿勢。J0=40度でカップ方向に旋回し、J1=90度で大きく前傾する。
LIFT はカップを持ち上げた中間姿勢。GRASPから直接PLACEに向かうと、アームがオブジェクトに衝突するリスクがある。一旦上方向に退避してから移動する。人間が物を持つとき、自然に少し上に持ち上げてから横移動するのと同じ動作だ。
PLACE はエンドエフェクターが台座(Cube)の上面に来る姿勢。J0=-40度(マイナス方向)になっているのは、台座がロボットの左前方に配置されているためだ。
この記録から得た教訓
修正し続けるより、作り直した方が速いことがある。
link1の傾き問題は、何度パッチを当てても解決しなかった。根本原因はインポート済みデータの破損だった。URDFから再構築することで、全ての角度データが1セッションで揃った。
これは前回の記録で書いた「計算で動かすより、正解を流し込む方が速いことがある」と同じ構造の教訓だ。既存のものを直し続けるコストより、正しい状態から始めるコストの方が低いケースがある。
精度はあとからお金で解決できる。まず体験が成立する状態を作ることが優先だ。
次のステップ
取得した4つの角度データを SmartTrajectoryController.cs(制御スクリプト)に反映し、pick and placeの一連動作をテストする。
HOME → GRASP → LIFT → PLACEこの4ステートが5次多項式の滑らかな軌道で繋がれば、デモ動作の骨格が完成する。5次多項式とは、加加速度(ジャーク)を最小化する軌道の計算方法で、人間の腕の動きに近い滑らかさを実現できる。実機ロボットの軌道計画でも広く使われる手法だ。
