UnityシミュレーションでmechArm 270がブロックを掴んで箱に入れた
LOG137R2R2R2026/4/10

UnityシミュレーションでmechArm 270がブロックを掴んで箱に入れた

Unityの中でロボットアームが赤いブロックを掴み、白い箱に入れた。その瞬間、デジタルツインが「動くおもちゃ」から「検証環境」に変わった気がした。


💡 Key Takeaway
実機で動作確認済みのジョイント角度データをそのままUnityシミュレーションに移植したら、ピック&プレースが成立した。現実→シミュレーションの逆引きが、今日初めて機能した。

やったこと

起きたこと

判断

赤ブロック・白箱をUnityシーンに追加

Rigidbodyで床に落下 → 原因特定・修正

Collider設計の基礎を再確認

実機ポーズCSVをUnity用に変換

CSV読み込みで即座に動作

実機データの移植性が確認できた

MotionLogPlayerにアタッチ/デタッチ実装

グリッパーがブロックを運べるようになった

物理的な「掴む」を仮想的に表現

BoxCollider追加・重力落下

ブロックが箱の中に落ちた

シミュレーション成立

オブジェクトを作ることから始めた

— 掴むものと、置く場所を、まずシーンに用意する。

今まで coffeecup_3_arm1 という既存の3Dスキャンモデルで動作確認してきた。でも実機で今使っているのは、24mm角の赤いプラスチックブロックと、80mm角の白い箱だ。

シミュレーションと実機を揃えるためには、Unityの中にも同じオブジェクトが必要になる。

RedBlock_arm1 と WhiteBox_arm1 をCubeの組み合わせで作った。

RedBlock_arm1:
  Scale: (0.024, 0.024, 0.024)  ← 24mm角
  Component: BoxCollider(衝突判定)
  Component: Rigidbody
    Mass: 0.05
    Use Gravity: true
    Is Kinematic: true(初期状態)
    Collision Detection: Continuous

WhiteBox_arm1は空のGameObjectを親にして、5つのCubeを子として構成した。

WhiteBox_arm1(親・Position基準)
  └ Bottom   Scale: (0.080, 0.002, 0.080)  LocalPosition: (0, 0.001, 0)
  └ Front    Scale: (0.080, 0.043, 0.002)  LocalPosition: (0, 0.0215, 0.039)
  └ Back     Scale: (0.080, 0.043, 0.002)  LocalPosition: (0, 0.0215, -0.039)
  └ Left     Scale: (0.002, 0.043, 0.076)  LocalPosition: (-0.039, 0.0215, 0)
  └ Right    Scale: (0.002, 0.043, 0.076)  LocalPosition: (0.039, 0.0215, 0)

5つの子オブジェクトそれぞれにBoxColliderを追加することで、箱の内側でブロックが止まる衝突判定が成立する。

ここで最初の問題が出た。Rigidbodyをつけたブロックが、再生した瞬間にY=-556まで落下した。重力で床を突き抜けた。BoxColliderを追加して床との衝突判定を持たせることで解決した。

実機のデータをそのまま使った

— 「動作確認済みのポーズ」が、シミュレーション用CSVになった。

実機mechArm 270-Piで確定済みのジョイント角度がある。

HOME:           [0, 0, 0, 0, 0, -30]
APPROACH_PICK:  [-54.22, 81.91, -30.14, -8.08, -50.27, -29.44]
PICK:           [-46.93, 102.74, -43.33, 3.6, -60.11, -31.9]
APPROACH_PLACE: [59.85, 30.0, 27.94, 10.01, -45.17, -35.33]
PLACE:          [54.49, 45.17, 26.63, 2.9, -70.57, -29.26]

これをUnityのCSVフォーマットに変換した。

timestamp,label,J1,J2,J3,J4,J5,J6
2026-01-01 00:00:00.000000,HOME,0,0,0,0,0,-30
2026-01-01 00:00:04.000000,GRIPPER_OPEN,0,0,0,0,0,-30
2026-01-01 00:00:08.000000,APPROACH_PICK,-54.22,81.91,-30.14,-8.08,-50.27,-29.44
2026-01-01 00:00:12.000000,PICK,-46.93,102.74,-43.33,3.6,-60.11,-31.9
2026-01-01 00:00:16.000000,GRIPPER_CLOSE,-46.93,102.74,-43.33,3.6,-60.11,-31.9
2026-01-01 00:00:20.000000,APPROACH_PLACE,59.85,30.0,27.94,10.01,-45.17,-35.33
2026-01-01 00:00:24.000000,PLACE,54.49,45.17,26.63,2.9,-70.57,-29.26
2026-01-01 00:00:28.000000,GRIPPER_OPEN_PLACE,59.85,30.0,27.94,10.01,-45.17,-35.33
2026-01-01 00:00:32.000000,HOME_RETURN,0,0,0,0,0,-30

motion_log_block.csv という名前でStreamingAssetsに配置し、MotionLogPlayerに読み込ませた。再生した瞬間にアームが動いた。実機のデータがそのまま使えた。

「掴む」を仮想的に表現した

— 物理的なグリッパーの力は、コードで代替する。

Unityのシミュレーションでグリッパーがブロックを「掴む」ためには、物理的な摩擦力ではなくコードで解決する必要がある。

MotionLogPlayer.cs に2つの処理を追加した。

// GRIPPER_CLOSE: ブロックをEndEffectorの子にアタッチ
if (_labels[i] == "GRIPPER_CLOSE")
{
    Rigidbody rb = cupObject.GetComponent<Rigidbody>();
    if (rb != null) rb.isKinematic = true; // 重力無効
    cupObject.transform.SetParent(endEffector, worldPositionStays: true);
}

// GRIPPER_OPEN_PLACE: ブロックをデタッチ、重力で落下
if (_labels[i] == "GRIPPER_OPEN_PLACE")
{
    cupObject.transform.SetParent(null, worldPositionStays: true);
    Rigidbody rb = cupObject.GetComponent<Rigidbody>();
    if (rb != null)
    {
        rb.isKinematic = false;
        rb.useGravity = true; // 重力有効→自然落下
    }
}

GRIPPER_CLOSEでブロックをEndEffectorの子にする。アームが動けばブロックも追従する。GRIPPER_OPEN_PLACEでデタッチして重力を有効にする。ブロックは自然に落ちる。

ブロックが箱に入った

— 何度も詰まって、何度も直して、最後に成立した。

途中でいくつかの問題が出た。CSVのパスが間違っていてファイルが読めなかった。ブロックにBoxColliderがなくて床を突き抜けた。WhiteBox_arm1の壁にBoxColliderがなくてブロックが箱を素通りした。

一つ一つ直した。

最終的に、再生を押したら。

グリッパーが開いてブロックに近づく。グリッパーが閉じてブロックをアタッチする。アームが箱の上まで運ぶ。グリッパーが開いてブロックをデタッチする。ブロックが重力で箱の中に落ちる。

シミュレーションが成立した。

いった!

この成立が意味すること

実機で確定したジョイント角度データがある。そのデータをCSVに変換したら、Unityでも同じ動作が再現できた。

これが本質だ。

実機→シミュレーションの移植が機能した。逆方向(シミュレーション→実機)は確認済みだった。今両方向が繋がった。

R2R2R(Real2Render2Real)のループ構造が、ブロックと箱という小さなスケールで、初めて完全に動いた。

※ プロジェクト名「R2R2R」は、UC Berkeley・Toyota Research Instituteの研究「Real2Render2Real」(Yu, Fu et al., 2025)の概念に触発されています。現実空間をデジタルツイン化し、仮想空間での試行から得た知見を実世界のモーター駆動へ還元する——その設計と実装過程を毎日記録しています。技術アプローチおよび目的は独自のものです。