モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

目次目次を開く/閉じる

Python と Open Dynamics Engine を用いた動力学シミュレーション

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2020/04/19
最終更新最終更新
2024/10/04
記事区分記事区分
一般公開

目次

    インフラ構築と自動化が得意。TerraformとAnsibleでインフラを自動構築するお仕事が多め

    Open Dynamics Engine (ODE) を用いた動力学シミュレーションを行います。ここでは ode-python を利用して Python から実行します。

    インストール

    以下は Linux 環境の例です。

    ODE ビルド

    ODE に付属する DrawStuff をビルドするためには OpenGL と GLUT が必要です。

    sudo apt install -y libgl1-mesa-dev libglu1-mesa-dev
    

    cmake を利用してビルドします。

    sudo apt install -y cmake
    

    ソースコードをこちらからダウンロードします。動的ライブラリとしてビルドします。

    mv ode-0.16.1.tar.gz ode-0.16.1.tar
    tar xvf ode-0.16.1.tar
    mkdir -p mybuild && cd mybuild
    cmake ../ode-0.16.1 -DBUILD_SHARED_LIBS=ON -DODE_WITH_DEMOS=ON
    make
    

    DrawStuff は make install でインストールできないため手動でコピーしてインストールします。

    sudo cp libdrawstuff.so /usr/local/lib/
    sudo cp -d libode.so.0.16.1 libode.so /usr/local/lib/
    

    DrawStuff 利用時に必要になるためテクスチャファイルも適当な場所に保存しておきます。

    sudo cp -r ode-0.16.1/drawstuff/textures /usr/local/share/ode-0.16.1-drawstuff-textures
    sudo chmod -R a+x /usr/local/share/ode-0.16.1-drawstuff-textures
    

    ode-python のインストール

    python -m pip install ode-python
    

    ボールの自由落下

    ode-python では ctypes によるバインディングが行われています。ポインタ渡しでは byref を利用します。

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import odepy
    import drawstuffpy as ds
    import ctypes
    import random
    
    def Main():
        # ODE では動力学計算用の world と
        # 衝突計算用の space を利用します。
        world, space, ground, contactgroup = CreateWorld()
    
        geoms = [AddBall(world, space)['geom']]
        geomColors = [[random.random(), random.random(), random.random()]]
        tDelta = 0.01
    
        # 衝突が予想されるタイミングで呼び出される関数です。
        def __NearCallback(data, o1, o2):
            o1IsGround = ctypes.addressof(ground.contents) == ctypes.addressof(o1.contents)
            o2IsGround = ctypes.addressof(ground.contents) == ctypes.addressof(o2.contents)
            if not (o1IsGround or o2IsGround):
                return
            N = 10
            contacts = (odepy.dContact * N)()
            n = odepy.dCollide(o1, o2, N, ctypes.byref(contacts[0].geom), ctypes.sizeof(odepy.dContact))
            for i in range(n):
                contact = contacts[i]
                contact.surface.mu = float('inf')
                contact.surface.mode = odepy.dContactBounce
                contact.surface.bounce = 0.95
                contact.surface.bounce_vel = 0.0
                c = odepy.dJointCreateContact(world, contactgroup, ctypes.byref(contact))
                odepy.dJointAttach(c, odepy.dGeomGetBody(contact.geom.g1), odepy.dGeomGetBody(contact.geom.g2))
    
        # tDelta で world の時間が進む毎に呼び出される関数です。
        def __StepCallback(pause):
            if pause != 1:
                odepy.dSpaceCollide(space, 0, odepy.dNearCallback(__NearCallback))
                odepy.dWorldStep(world, tDelta)
                odepy.dJointGroupEmpty(contactgroup)
            for geom, color in zip(geoms, geomColors):
                body = odepy.dGeomGetBody(geom)
                ds.dsSetColor(*color)
                if odepy.dGeomGetClass(geom) == odepy.dSphereClass:
                    r = odepy.dGeomSphereGetRadius(geom)
                    pos = odepy.dBodyGetPosition(body)
                    rot = odepy.dBodyGetRotation(body)
                    ds.dsDrawSphereD(pos, rot, r)
                elif odepy.dGeomGetClass(geom) == odepy.dCapsuleClass:
                    r = odepy.dReal()
                    l = odepy.dReal()
                    odepy.dGeomCapsuleGetParams(geom, ctypes.byref(r), ctypes.byref(l))
                    pos = odepy.dBodyGetPosition(body)
                    rot = odepy.dBodyGetRotation(body)
                    ds.dsDrawCapsuleD(pos, rot, l.value, r.value)
                else:
                    raise Exception('Not supported geom class: {}'.format(odepy.dGeomGetClass(geom)))
    
        # シミュレーションを開始します。
        RunDrawStuff(__StepCallback)
        # ODE を終了するための手続きを行います。
        DestroyWorld(world, space)
    
    def AddBall(world, space, m=1.0, r=0.1, pos=[0, 0, 2.0]):
        mass = odepy.dMass()
        odepy.dMassSetZero(ctypes.byref(mass))
        odepy.dMassSetSphereTotal(ctypes.byref(mass), m, r)
        body = odepy.dBodyCreate(world)
        odepy.dBodySetMass(body, ctypes.byref(mass))
        geom = odepy.dCreateSphere(space, r)
        odepy.dGeomSetBody(geom, body)
        # body は world に属します。geom は space に属します。
        ball = {'body': body, 'geom': geom}
        odepy.dBodySetPosition(ball['body'], *pos)
        return ball
    
    def RunDrawStuff(stepCallback, pathToTextures='/usr/local/share/ode-0.16.1-drawstuff-textures', w=400, h=225, cameraXyz=[3.0, 0.0, 1.0], cameraHpr=[-180.0, 0.0, 0.0]):
        def __StartCallback():
            xyz = (ctypes.c_float * 3)()
            hpr = (ctypes.c_float * 3)()
            for i, v in enumerate(cameraXyz):
                xyz[i] = v
            for i, v in enumerate(cameraHpr):
                hpr[i] = v
            ds.dsSetViewpoint(xyz, hpr)
        fn = ds.dsFunctions()
        fn.version = ds.DS_VERSION
        fn.start = ds.dsStartCallback(__StartCallback)
        fn.step = ds.dsStepCallback(stepCallback)
        fn.path_to_textures = ctypes.create_string_buffer(pathToTextures.encode('utf-8'))
        ds.dsSimulationLoop(0, ctypes.byref(ctypes.POINTER(ctypes.c_char)()), w, h, fn)
    
    def CreateWorld():
        odepy.dInitODE()
        world = odepy.dWorldCreate()
        odepy.dWorldSetGravity(world, 0, 0, -9.8)
        space = odepy.dHashSpaceCreate(0)
        ground = odepy.dCreatePlane(space, 0, 0, 1, 0)
        contactgroup = odepy.dJointGroupCreate(0)
        return world, space, ground, contactgroup
    
    def DestroyWorld(world, space):
        odepy.dSpaceDestroy(space)
        odepy.dWorldDestroy(world)
        odepy.dCloseODE()
    
    if __name__ == '__main__':
        Main()
    
    Likeボタン(off)0
    詳細設定を開く/閉じる
    アカウント プロフィール画像

    インフラ構築と自動化が得意。TerraformとAnsibleでインフラを自動構築するお仕事が多め

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    >>さらに詳しくステッカーを贈る
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      ログインする

      関連記事