2
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?

More than 3 years have passed since last update.

PyODE チュートリアル 2

Last updated at Posted at 2020-05-07

PyODEのサンプル実行例

本家 PyODE 様サイト の「Tutorial 2」を実行してみました。

--
他のチュート実施記事はこちら ↓
   ・「Tutorial 1」(落下物体、Matplotlib描画を追加)
   ・「Tutorial 3」(多体接触、OpenGL)

--

本題の「Tutorial 2」ですが、
元の内容は
 ・3個のオブジェクトをヒンジ2個でつないで振り子を構成。
 ・1個のヒンジに角速度/トルクを設定
 ・可視化は Pygame による 2D 描画

ここでは角速度の設定はコメントアウトしました。
また、画面サイズや色づけなど変更して GIF 動画出力してみました。

##環境など
・Win10
・Python 3.8.0 64bit
・PyODE 0.15.2
・Pygame 1.9.6
・Pillow 7

##「Tutorial 2」をそのまま実行する際の修正
筆者環境では PyODE サイトのコードそのままでは実行エラーした。

pygameエラーメッセージ抜粋
pygame.draw.circle(srf, rgb, coord( xyz ), 20, 0)
TypeError: integer argument expected, got float

対処:
  コードの冒頭にあるcoord()というルーチンで、
  return される値2つをそれぞれ int() で囲いました。

pygameエラー対処
def coord(x,y):
    "Convert world coordinates to pixel coordinates."
    return int(320+170*x), int(400-170*y)

###結果
実行すると Pygame による2D動画表が表示される。
ここでは GIF 形式でファイル保存したものを掲載します。
tutorial_2.gif

ODEのオブジェクトは3個の 〇 だけです。
線は描画しているだけ。
黒いオブジェクトは空間に固定されています。(このオブジェクトは無くてもよいのです。)
ジョイント設定、描画設定等には変更加えてあります。

###コード(全部)

不要部分とか残っていますが
小さいコードなので丸ごと載せときます。

Python3
# pyODE example 2: Connecting bodies with joints
##  Modified: Window-sizing, jointing-condition, and others.

import pygame
from pygame.locals import *
import ode


W = 640
H = 640
CX = 320
CY = 320
S = 80.
def coord(xyz):
    (x,y,z) = xyz
    "Convert world coordinates to pixel coordinates."
    return int( CX +S*x ), int( CY -S*y)



from PIL import Image, ImageDraw

def storeImage( srfc, images ):
    if NI_MAX <= len(images):
        return
    s = srfc
    buf = s.get_buffer()
    im = Image.frombytes( "RGBA", s.get_size(), buf.raw )
    B,G,R,A = im.split()
    img = Image.merge("RGBA",(R,G,B,A))
    images.append(img)

def gif(images):
    print(' @ gif(), ')
    savepath = 'tutorial_2.gif'

    image0 = images[0]
    image_end = images[-1]
    for i in range( 5 ):
        images.insert(0,image0)
    for i in range( 5 ):
        images.append(image_end)

    images[0].save( savepath, save_all=True, append_images=images[1:], optimize=not False, duration=100, loop=0 )
    print(' Exported : [%s]'%savepath)

NI_MAX = 10000
images=[]



# Initialize pygame
pygame.init()

# Open a display
srf = pygame.display.set_mode( (W,H) )

#

# Create a world object
world = ode.World()
world.setGravity((0,-9.81,0))

#

# Create two bodies
body0 = ode.Body(world)
M = ode.Mass()
M.setSphere(2500, 0.05)
body0.setMass(M)
body0.setPosition( (0,0,0) )

body1 = ode.Body(world)
M = ode.Mass()
M.setSphere(2500, 0.05)
body1.setMass(M)
body1.setPosition( (1,1,0) )

body2 = ode.Body(world)
M = ode.Mass()
M.setSphere(2500, 0.05)
body2.setMass(M)
body2.setPosition( (0,2,0) )

bodys = [body0,body1,body2]


# Connect body0 with the static environment
if True:
    j0 = ode.BallJoint(world)
    j0.attach(body1, ode.environment)
    j0.setAnchor( body0.getPosition() )
if False:
    j0 = ode.HingeJoint(world)
    j0.attach(body1, ode.environment)
    j0.setAnchor( body0.getPosition() )
    j0.setAxis( (0,0,1) )

    #j0.setParam(ode.ParamVel, 3)
    #j0.setParam(ode.ParamFMax, 22)

# Connect body1 with body0
j01 = ode.BallJoint(world)
j01.attach(body0, body1)
j01.setAnchor( body0.getPosition() )

# Connect body2 with body1
j12 = ode.BallJoint(world)
j12.attach(body1, body2)
j12.setAnchor( body1.getPosition() )

joints = [j0, j01, j12]




RGBs = { body0:(63, 63, 63), body1:(127, 63, 63), body2:(63, 127, 63) }


####################################################
# Simulation loop...

fps = 50
dt = 1.0/fps
loopFlag = True
clk = pygame.time.Clock()

ti = -1
while loopFlag:
    ti += 1
    events = pygame.event.get()
    for e in events:
        if e.type==QUIT:
            loopFlag=False

        if e.type==KEYDOWN:
            gif( images )
            loopFlag=False

    # Clear the screen
    srf.fill((255,255,255))


    for joint in joints:
        if joint is j0:
            continue
        rgb = (127,127,127)
        
        SIMPLE=False
        if SIMPLE:
            p0 = coord( joint.getBody(0).getPosition() )
            p1 = coord( joint.getBody(1).getPosition() )
        else:    ####        joint.attach(body, ode.environment)の場合のアンカ―位置への対応と、線の色分け
            b_move = None
            b0 = joint.getBody(0)
            if b0:
                if b0.getPosition() != joint.getAnchor():
                    b_move = b0
            if None is b0:
                p0 = coord( joint.getAnchor() )
                if None is p0:
                    p0 = coord( body0.getPosition() )
            else:
                p0 = coord( b0.getPosition() )
            b1 = joint.getBody(1)
            if b1:
                if b1.getPosition() != joint.getAnchor():
                    b_move = b1
            if None is b1:
                p1 = coord( joint.getAnchor() )
                if None is p1:
                    p1 = coord( body0.getPosition() )
            else:
                p1 = coord( b1.getPosition() )
        
            rgb = RGBs[b_move]

        lw = 5
        pygame.draw.line( srf, rgb, p0,p1, lw)

    for body in bodys:
        xyz = body.getPosition()
        rgb = RGBs[body]
        pygame.draw.circle(srf, rgb, coord( xyz ), 20, 0)
    
    pygame.display.flip()

    if ti % 4 == 0:
        storeImage(srf,images)

    # Next simulation step
    world.step(dt)

    # Try to keep the specified framerate    
    clk.tick(fps)
    
    #if not loopFlag:
    #    gif( images )

※ GIF画像の最大フレーム数は、画像ストア数上限 ```NI_MAX`` に依存。

2
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
2
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?