アーム型ロボットをUnityで動かしたいと思っています。

アームのハンドの先端(TCP)を自由に動かすためには、TCPの座標から、アームの各関節の角度を計算する必要があります。その計算をする手法を逆運動学(IK)といいますが、moveit というフリーソフトが有名です。しかし、これはROSで使う必要があります。

もっと簡単に、例えば、UnityだけでIKできないのかなと思って調べると、標準でついていました!キャラクターの動きを作るときに普通に使われているみたいです。なるほど~。

また、有料のFinal IK いうアセットもあり、こちらも人気があるようです。

さっそく、標準でのIKの機能を試してみました。いろいろ調べて、やっと2DのハンドをIKで動かすということができましたので、覚書としてまとめます。

画像を準備

ロボットハンドをパワポで作成。背景を透明にしたpng形式で保存(ハンド.png)。

UnityでBoneを作成

Unityで2Dのプロジェクトを作成。Assetsフォルダにハンドの画像をドラッグ&ドロップ。

 

ハンドのアイコンを選択し、右のInspector のSprite Editor をクリック。

 

Sprite Editorが開く。メニューからSkinning Editorを選択。

 

Create Bone を押すとbone を作るモードになるので、下から順にボーンを作っていく。最初画像をダブルクリックしたらこのとんがりが出せるようになったかも。最後は右クリックをして終了。

 

AutoGeometry を押すと、ハンドの画像とboneが紐づく。画像についた色とbone の色が対応している。

 

この時点でbone をドラックで曲げると、画像が追従して曲がることを確認。ctrl+Z で姿勢をもどし、右上の✕を押してSprite Editor を閉じる。閉じるときに保存を聞かれるので保存する。

 

ハンドをSceneのドラッグし、ハンドオブジェクトを作成。場所を整える。

 

Hierarchyウィンドウで、ハンドを選択し、Inspectorから、Add Componentで Sprite Skinを選択、追加されるSprite SkinのプロパティのCreate Bone を押すと、Boneの情報が取り込まれ次のような画面になる。

Sceneのボーンをドラックしてハンドを動かすことができる。inspectorのSprite SkinのReset Bind Pose を押すとハンドの姿勢が戻る。

Unity のIK (逆運動学)を適用

Hierarchyでハンドを選択し、右のInspectorのAdd Component でIK Manager 2Dを追加、+ボタンを押して、Chain(CCD)を選択する。Hierarchyに、New CCDSolver2Dができたことを確認。

 

次に、IKのターゲット、つまり、ハンドが目指す座標を決めるオブジェクトを作成する。

Hierarcyの何もないところを右クリックし、2D Object>Sprites>Circleで、Circleオブジェクトを作成。大きさ、色を整える。

 

bone_4を右クリック>Create Empty で、Empty Objectを作り、Effector という名前にする。位置を、ハンドの先端になるように調節する。

 

Hierarchyで、New CCDSolve2Dを選択する。InspectorのEffectorとTargetの枠に、それぞれ、HierarchyのEffectorとCircle をドラッグして関連付ける。Chain Length は関節の数なのでここでは4にする。Iterationsは繰り返し計算の数で少ないと途中で止まる場合がある。ここは最大の50にしておく。

 

この状態で、Sceneの青いcircleをドラッグすれば、ハンドが追従する。ハンドの先端がcircleに合うように角度の計算が自動で行われているということだ。これぞ逆運動学(IK)。

ハンドをカーソルキーで動かす

ターゲットとなっている青いcircleをプログラムを使ってカーソルで動かしてみる。ハンドがそれに追従するだろうか。

以下のようなC#スクリプトを作り、circleにドラッグして関連付ける。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// カーソルキーで上下左右に移動するターゲット

public class TargetController : MonoBehaviour
{
    float dx = 0;
    float dy = 0;

    void Update()
    {
        dx = Input.GetAxisRaw("Horizontal");
        dy = Input.GetAxisRaw("Vertical");      
    }

    void FixedUpdate()
    {
        transform.Translate(dx * 0.1f, dy * 0.1f, 0);
    }
}

 

再生ボタンを押して、カーソルキーでcircleを動かすと、ハンドも追従した。IKうまくいった。

ただ、関節同士が衝突しないようにするなどはできないのかもしれない。