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.draw.circle(srf, rgb, coord( xyz ), 20, 0)
TypeError: integer argument expected, got float
対処:
コードの冒頭にあるcoord()というルーチンで、
return される値2つをそれぞれ int() で囲いました。
def coord(x,y):
"Convert world coordinates to pixel coordinates."
return int(320+170*x), int(400-170*y)
###結果
実行すると Pygame による2D動画表が表示される。
ここでは GIF 形式でファイル保存したものを掲載します。
ODEのオブジェクトは3個の 〇 だけです。
線は描画しているだけ。
黒いオブジェクトは空間に固定されています。(このオブジェクトは無くてもよいのです。)
ジョイント設定、描画設定等には変更加えてあります。
###コード(全部)
不要部分とか残っていますが
小さいコードなので丸ごと載せときます。
# 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`` に依存。