0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity物理エンジンで挑む!産業用ロボット風クレーンゲームの制作記録 〜 その2:感圧センサー編

Last updated at Posted at 2025-12-28

その1:ジョイント編 の振り返り

Unityで産業用ロボット風クレーンを制作しました。摩擦を確実に確保するため、全可動部を物理演算で駆動させています。前回はHingeJointの角度制限をハックすることで移動時の揺れを克服しました。今回の「その2」では、圧力センサーを用いた最適な把持力(はじりょく)制御の実装を目指します。

摩擦力対重力の関係

二つの爪に挟まれたワークが落下しない条件を物理的に整理します。

ワークの質量を $m$、重力加速度を $g$、爪がワークを押す力を $F$、爪とワークの間の摩擦係数を $\mu$ とすると、ワークにかかる重力が最大静止摩擦力 $2\mu F$ よりも小さければワークは落ちません。
$$2\mu F > mg$$

計算例:みかん1個(約100g)にかかる重力は約1Nです。摩擦係数 $\mu$ を0.6とした場合、爪がワークを押す力 $F$ が約1N以上あれば理論上は保持できます。

今回は、以下の動作仕様を目指します:

  • 角速度: 50度/秒で指を回転。
  • 目標把持力: 3N(振動等による滑り落ちを考慮し、理論値1Nに対し安全圏の3倍を設定)。

感圧センサーの実装

今回の方式は、指をトルクで押し付けるのではなく、HingeJointの角度制限(Limits)を動かして強引に回転させる方式です。そのため、爪先の力が目標の3Nに達した瞬間に回転を止める(あるいは微調整する)ための「センサー」が必要になります。

Unityでは、Collision.impulse を利用することで感圧センサーを擬似的に実装できます。

Collision impulseとは

Unityのマニュアルによれば、Collision.impulse は衝突を解決するために加えられた合計の力積(Impulse)です。力積 $I$ は、力 $F$ と時間 $\Delta t$ の積で表されます。$$I = F \cdot \Delta t$$逆に言えば、力積を時間(FixedDeltaTime)で割ることで、そのフレームでかかっている力を算出できます。$$F = \frac{I}{\Delta t}$$

PressureSensor.cs

Bones of the robot (13).jpg

この理論に基づき、接触時の力と圧力を計算するスクリプトを作成し、指のオブジェクトにアタッチします。

using UnityEngine;

public class PressureSensor : MonoBehaviour
{
    private float _lastForce;
    public float LastForce => _lastForce;
    
    private bool _isColliding;
    public bool IsColliding => _isColliding;

    private void OnCollisionEnter(Collision collision)
    {
        _isColliding = true;
        CalculateForce(collision);
    }

    private void OnCollisionStay(Collision collision)
    {
        CalculateForce(collision);
    }

    private void OnCollisionExit(Collision collision)
    {
        _isColliding = false;
        _lastForce = 0f;
    }

    private void CalculateForce(Collision collision)
    {
        var impulse = collision.impulse.magnitude;
        if (collision.contactCount > 0)
        {
            // 力積をFixedDeltaTimeで割り、ニュートン(N)換算の力を求める
            _lastForce = impulse / Time.fixedDeltaTime;
        }
    }
}

指定された力で爪をワークへ押し付ける動作の実現

物理挙動の精密化(Project Settings)

正確な制御のために、Unityの物理エンジン設定を調整します。

1. Fixed Delta Time の短縮

デフォルトの0.02s(50Hz)から 0.01s(100Hz) へ変更し、物理計算の解像度を高めます。

Project Settings => Time
Screenshot 2025-12-28 at 14.07.05.jpg

2. Default Contact Offset の調整

デフォルトの0.01(1cm)では、接触前から反発が始まってしまいます。これを 0.0005(0.5mm) まで下げ、見た目通りの接触で力が検知されるようにします。

Screenshot 2025-12-28 at 16.02.21.jpg

3. Friction Type の選択

より正確な物理挙動のためには One/Two Directional が理想ですが、本プロジェクトでは安定性を優先し Patch Friction Type を採用しました。

Project Settings => Physics => Friction Type
Screenshot 2025-12-28 at 13.18.38.jpg

注意: Patch Frictionの場合、Unity上の設定値は現実の約半分として扱われるため、摩擦係数0.3の設定で現実の0.6相当となります。

力に応じた角度制限(Limits)の動的制御

制御のキモは、HingeJointの limits.min/max を「目標の力」に合わせて微増減させる点にあります。

Bones of the robot (15).jpg

  1. 接近: 指を閉じ方向に回転させる。
  2. 減速: 衝突を検知したら、角速度を1/200(slowDownDenominator)に落とし、オーバーシュートを防ぐ。
  3. フィードバック: 目標値(3N)を超えたら、角度をわずかに戻す(forceReductionDenominator)。

ArmController.cs(主要ロジック抜粋)

void FixedUpdate()
{
    // 接触中は制御を安定させるために回転速度を大幅に落とす
    float gripSpeedAdjustment = (_pressureSensorL.IsColliding && _pressureSensorR.IsColliding) 
        ? gripSpeed / slowDownDenominator : gripSpeed;

    if (Keyboard.current.leftArrowKey.isPressed)
    {
        _targetGripAngle = Mathf.MoveTowards(_targetGripAngle, closedAngle, gripSpeedAdjustment * Time.deltaTime);
    }

    // 目標値(targetForce)を超えた場合、角度を微調整して力を一定に保つ
    if (_targetGripAngle - _lastGripAngle > 0 && 
        _pressureSensorL.LastForce > targetForce && _pressureSensorR.LastForce > targetForce)
    {
        _targetGripAngle = _lastGripAngle - (_pressureSensorL.LastForce + _pressureSensorR.LastForce - 2 * targetForce) 
            / forceReductionDenominator;
    }

    SetHingeLimits(_hingeL, _targetGripAngle);
    SetHingeLimits(_hingeR, _targetGripAngle);
    _lastGripAngle = _targetGripAngle;
}

private void SetHingeLimits(HingeJoint hinge, float angle)
{
    var limits = hinge.limits;
    limits.min = angle - 0.01f; // ガタつき防止のため隙間を最小限に
    limits.max = angle;
    hinge.limits = limits;
}

動作確認

クレーンゲームとしてのピック動作が劇的に安定しました。

設定したターゲットフォースを維持してワークを掴み続けることができます。

把持力を1N以下に設定すると、持ち上げた瞬間にスルリと滑り落ちます。まさに物理学の教科書通りの挙動です。

「掴めたはずなのに、振動で滑り落ちる」というクレーンゲーム特有の絶妙な難しさを再現することができました。

まとめと次のステップ

今回はHingeJointと力積計算を組み合わせることで、擬似的なサーボモータと圧力センサーの系を構築しました。

次は、より高度なロボティクスシミュレーションに向けた手法である ArticulatedBody を使った実装にチャレンジします。

(2025/12/28 追記)今月参加した国際ロボット展、産業用ロボットの世界では、感圧センサーのことを力覚センサーと呼んでいた。あるブースでは、ロボットの指先にかかる力の大きさを力覚センサーで検知し食料品を柔らかく掴んでいた。

本記事で紹介した制作物

以下のGitHubプロジェクトで公開します。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?