🐛 ソフトロボット#

ボリューム筋肉シミュレーション#

Genesisでは、ソフトロボットのためのMPMおよびFEMを使用したボリューム筋肉シミュレーションをサポートしています。以下の例では、正弦波制御信号で駆動される球体のボディを持つ非常にシンプルなソフトロボットを示しています。

import numpy as np
import genesis as gs


########################## 初期化 ##########################
gs.init(seed=0, precision='32', logging_level='debug')

########################## シーンを作成 ##########################
dt = 5e-4
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        substeps=10,
        gravity=(0, 0, 0),
    ),
    viewer_options= gs.options.ViewerOptions(
        camera_pos=(1.5, 0, 0.8),
        camera_lookat=(0.0, 0.0, 0.0),
        camera_fov=40,
    ),
    mpm_options=gs.options.MPMOptions(
        dt=dt,
        lower_bound=(-1.0, -1.0, -0.2),
        upper_bound=( 1.0,  1.0,  1.0),
    ),
    fem_options=gs.options.FEMOptions(
        dt=dt,
        damping=45.,
    ),
    vis_options=gs.options.VisOptions(
        show_world_frame=False,
    ),
)

########################## エンティティ ##########################
scene.add_entity(morph=gs.morphs.Plane())

E, nu = 3.e4, 0.45
rho = 1000.

robot_mpm = scene.add_entity(
    morph=gs.morphs.Sphere(
        pos=(0.5, 0.2, 0.3),
        radius=0.1,
    ),
    material=gs.materials.MPM.Muscle(
        E=E,
        nu=nu,
        rho=rho,
        model='neohooken',
    ),
)

robot_fem = scene.add_entity(
    morph=gs.morphs.Sphere(
        pos=(0.5, -0.2, 0.3),
        radius=0.1,
    ),
    material=gs.materials.FEM.Muscle(
        E=E,
        nu=nu,
        rho=rho,
        model='stable_neohooken',
    ),
)

########################## 構築 ##########################
scene.build()

########################## 実行 ##########################
scene.reset()
for i in range(1000):
    # 制御信号を生成
    actu = np.array([0.2 * (0.5 + np.sin(0.01 * np.pi * i))])

    # 筋肉の駆動を設定
    robot_mpm.set_actuation(actu)
    robot_fem.set_actuation(actu)
    scene.step()

以下のような結果が得られます:

ほとんどのコードは通常の変形可能エンティティをインスタンス化するのと比較して標準的ですが、以下の2つの違いがあります:

  • ソフトロボット robot_mpmrobot_fem をインスタンス化する際に、それぞれ gs.materials.MPM.Musclegs.materials.FEM.Muscle を使用します。

  • シミュレーションを進める際には、筋肉の駆動を設定するために robot_mpm.set_actuation または robot_fem.set_actuation を使用します。

デフォルトでは、ロボット全体のボディをカバーする筋肉が1つだけあり、筋肉の方向は地面に対して垂直([0, 0, 1])に設定されています。

次の例では、筋肉のグループと方向を設定することで、ミミズが前方に這う動きをシミュレーションする方法を示します(完全なスクリプトは tutorials/advanced_worm.py にあります)。

########################## エンティティ ##########################
worm = scene.add_entity(
    morph=gs.morphs.Mesh(
        file='meshes/worm/worm.obj',
        pos=(0.3, 0.3, 0.001),
        scale=0.1,
        euler=(90, 0, 0),
    ),
    material=gs.materials.MPM.Muscle(
        E=5e5,
        nu=0.45,
        rho=10000.,
        model='neohooken',
        n_groups=4,
    ),
)

########################## 筋肉を設定 ##########################
def set_muscle_by_pos(robot):
    # MPMについては、位置情報を取得して筋肉を設定
    if isinstance(robot.material, gs.materials.MPM.Muscle):
        pos = robot.get_state().pos
        n_units = robot.n_particles
    elif isinstance(robot.material, gs.materials.FEM.Muscle):
        # FEMについては、要素の位置情報を取得
        pos = robot.get_state().pos[robot.get_el2v()].mean(1)
        n_units = robot.n_elements
    else:
        raise NotImplementedError

    pos = pos.cpu().numpy()
    pos_max, pos_min = pos.max(0), pos.min(0)
    pos_range = pos_max - pos_min

    # 上部/下部および前部/後部を分割
    lu_thresh, fh_thresh = 0.3, 0.6
    muscle_group = np.zeros((n_units,), dtype=int)
    mask_upper = pos[:, 2] > (pos_min[2] + pos_range[2] * lu_thresh)
    mask_fore = pos[:, 1] < (pos_min[1] + pos_range[1] * fh_thresh)
    muscle_group[ mask_upper &  mask_fore] = 0 # 上部前方
    muscle_group[ mask_upper & ~mask_fore] = 1 # 上部後方
    muscle_group[~mask_upper &  mask_fore] = 2 # 下部前方
    muscle_group[~mask_upper & ~mask_fore] = 3 # 下部後方

    # 筋肉の方向を定義
    muscle_direction = np.array([[0, 1, 0]] * n_units, dtype=float)

    # 筋肉の設定
    robot.set_muscle(
        muscle_group=muscle_group,
        muscle_direction=muscle_direction,
    )

set_muscle_by_pos(worm)

########################## 実行 ##########################
scene.reset()
for i in range(1000):
    # 筋肉の駆動を設定
    actu = np.array([0, 0, 0, 1. * (0.5 + np.sin(0.005 * np.pi * i))])

    worm.set_actuation(actu)
    scene.step()

以下のような結果が得られます:

このコードスニペットの主なポイントは以下の通りです:

  • 材料 gs.materials.MPM.Muscle を指定する際に、追加の引数 n_groups = 4 を指定します。これは、このロボットに最大4つの異なる筋肉が存在できることを意味します。

  • 筋肉の設定には robot.set_muscle を使用します。この関数は muscle_groupmuscle_direction を入力として受け取ります。どちらも長さが n_units に一致し、MPMにおける n_units は粒子数を、FEMにおける n_units は要素数を表します。

    • muscle_group は整数の配列(例: 0 から n_groups - 1)で、ロボットのボディのユニットが属する筋肉グループを示します。

    • muscle_direction は筋肉方向を指定したベクトルの浮動小数点数配列です。

  • このミミズの例では、ボディを4つの部分(上部前方、上部後方、下部前方、下部後方)に分割し、lu_threshfh_thresh を使って閾値を設定しました。

  • 4つの筋肉グループが設定された後、set_actuation を通じて制御信号を設定する際は、入力信号は形状 (4,) の配列となります。

ハイブリッド(剛体とソフトの組み合わせ)ロボット#

もう一つのソフトロボットのタイプとして、剛体の内部骨格を使用してソフトな外皮を駆動する、いわばハイブリッドロボットがあります。Genesisはすでに剛体とソフト体の両方の動力学を実装しているため、ハイブリッドロボットにも対応しています。以下の例では、ソフトスキンで覆われた2リンクの骨格を持ち、剛体のボールを押すハイブリッドロボットを示します。

import numpy as np
import genesis as gs


########################## 初期化 ##########################
gs.init(seed=0, precision='32', logging_level='debug')

######################## シーンを作成 ########################
dt = 3e-3
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        substeps=10,
    ),
    viewer_options= gs.options.ViewerOptions(
        camera_pos=(1.5, 1.3, 0.5),
        camera_lookat=(0.0, 0.0, 0.0),
        camera_fov=40,
    ),
    rigid_options=gs.options.RigidOptions(
        dt=dt,
        gravity=(0, 0, -9.8),
        enable_collision=True, # 衝突を有効化
        enable_self_collision=False, # 自己衝突を無効化
    ),
    mpm_options=gs.options.MPMOptions(
        dt=dt,
        lower_bound=( 0.0,  0.0, -0.2),
        upper_bound=( 1.0,  1.0,  1.0),
        gravity=(0, 0, 0), # 重力補償を模倣
        enable_CPIC=True,
    ),
    vis_options=gs.options.VisOptions(
        show_world_frame=True, # ワールドフレームを表示
        visualize_mpm_boundary=False, # MPM境界の可視化を無効化
    ),
)

########################## エンティティ ##########################
scene.add_entity(morph=gs.morphs.Plane())

robot = scene.add_entity(
    morph=gs.morphs.URDF(
        file="urdf/simple/two_link_arm.urdf",
        pos=(0.5, 0.5, 0.3),
        euler=(0.0, 0.0, 0.0),
        scale=0.2,
        fixed=True, # 固定されたロボット
    ),
    material=gs.materials.Hybrid(
        material_rigid=gs.materials.Rigid(
            gravity_compensation=1., # 重力補償
        ),
        material_soft=gs.materials.MPM.Muscle( # 筋肉グループの設定を有効化
            E=1e4,
            nu=0.45,
            rho=1000.,
            model='neohooken',
        ),
        thickness=0.05, # スキンの厚さ
        damping=1000.,
        func_instantiate_rigid_from_soft=None,
        func_instantiate_soft_from_rigid=None,
        func_instantiate_rigid_soft_association=None,
    ),
)

ball = scene.add_entity(
    morph=gs.morphs.Sphere(
        pos=(0.8, 0.6, 0.1),
        radius=0.1,
    ),
    material=gs.materials.Rigid(rho=1000, friction=0.5), # 剛体球
)

########################## 構築 ##########################
scene.build()

########################## 実行 ##########################
scene.reset()
for i in range(1000):
    dofs_ctrl = np.array([
        1. * np.sin(2 * np.pi * i * 0.001),
    ] * robot.n_dofs)

    # 自由度の速度を制御
    robot.control_dofs_velocity(dofs_ctrl)

    scene.step()

以下のような結果が得られます:

ポイント#

  • ハイブリッドロボットは、gs.materials.Hybrid を使用して指定できます。この材料は gs.materials.Rigid(剛体)と gs.materials.MPM.Muscle(筋肉)の両方から構成されます。ここではMPMのみがサポートされており、Muscle クラスである必要があります。これは、ハイブリッド材料が内部的に Muscle 用の muscle_group 機能を再利用しているためです。

  • 制御に関しては、内部の剛体骨格から駆動されるため、剛体ロボットと似たインターフェースを使用します。例えば、control_dofs_velocitycontrol_dofs_forcecontrol_dofs_position などがあります。また、制御の次元は内部骨格の自由度(DoFs)と同じです(上記の例では2つ)。

  • スキンの形状は内部骨格の形状によって決定されます。thickness パラメータを使用して骨格を包むスキンの厚みを設定します。

  • デフォルトでは、骨格の形状に基づいてスキンを生成します。これは morph(この例では urdf/simple/two_link_arm.urdf)で指定されています。gs.materials.Hybrid の引数 func_instantiate_soft_from_rigid は、剛体形状に基づいてスキンを具体的にどのように生成するかを定義します。デフォルトの実装は、genesis/engine/entities/hybrid_entity.py にある default_func_instantiate_soft_from_rigid です。独自の関数を実装することも可能です。

  • morphURDF ではなく Mesh の場合、メッシュがソフトな外部形状を指定し、内部骨格はスキンの形状に基づいて生成されます。これは func_instantiate_rigid_from_soft で定義されています。デフォルト実装 default_func_instantiate_rigid_from_soft もあり、基本的には3Dメッシュのスケルトン化を実装しています。

  • gs.materials.Hybrid の引数 func_instantiate_rigid_soft_association は、それぞれの骨格パーツがスキンとどのように関連付けられるかを決定します。デフォルト実装では、骨格パーツに最も近いスキンの粒子を見つける方法を提供しています。