超時空要塞マクロスのゼントラーディ軍の汎用兵器であるリガードです。ダチョウを思わせるそのフォルムですが、その特徴はなんといっても”逆関節”の脚でしょう。あの歩行を再現してみたいと思っていましたが、工作の技術に自信がないのでシミュレーションでの再現に挑戦することにしました。Open Dynamics Engineを使っていろいろやっていこうと思います。
歩くリガードを表示するための第一歩として、STLデータをPythonで表示させてみました。見た目を気にする作者としては外観は気になるところです。最近は3Dプリンタの普及も相まっていろいろなものの3Dデータが公開されています。今回のリガードはhttp://www.thingiverse.com/thingでSTLデータを使わせてもらっています。
0.なぜPython?
Open Dynamics Engineを使ってシミュレーションを行うには、大きくわけてC言語でコードを作成するか、Pythonを利用するかの2つでしょう。日頃は残業続きで会社に拘束されている時間が長いので、会社と自宅の両方で使用できる環境が必要でした。C言語のコンパイラ類は管理者権限が必要で、会社では使用できません。ですから、管理者権限が不要なPythonで開発環境を構築することにしました。
1.Pythonの準備
Pythonについては、既に多くの方が説明されていますので、詳細はそちらを見ていただくとして、参考書が多い3系を選択しました。後で後悔するのですが・・・。
まずはPythonのインストールです。
python-3.4.4.msi
をインストールしました。次にhttp://www.lfd.uci.edu/~gohlke/pythonlibs/から
- numpy-1.10.4-cp34-none-win32.whl
- ode-0.13.1-cp34-none-win32.whl
- pygame-1.9.2a0-cp34-none-win32.whl
- cgkit-2.0.0-cp34-none-win32.whl
をpipを使ってインストールしました。
http://pyopengl.sourceforge.net/へ行って、
- PyOpenGL
- PyOpenGL_accelerate
- PyOpenGL Demo
- OpenGLContext
をインストールすれば準備完了です。
2.結果
STLデータはバイナリファイルなので、
http://sukhbinder.wordpress.com/2013/11/28/binary-stl-file-reader-in-python-powered-by-numpy/
で紹介されている”numpyを使う方法”を参考にしました。実は、3系を選択したのですがcgkitでエラーが大量に出るのです。初心者の私ではさばききれなかったので、numpyを使う方法にしました。
ここから
import os
import struct
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import pygame
from pygame.locals import *
import numpy as np
from struct import unpack
NormalV =[]
NodePoint = []
Vertex1 = []
Vertex2 = []
Vertex3 = []
Eye_from_x = 60.0
Eye_from_y = 50.0
Eye_from_z = 100.0
Center_X =0.
Center_Y =0.
Center_Z =0.
T_Color = [0.0, 1.0, 0.0, 1.0] #Height
def BinarySTL(fname):
fp = open(fname, ‘rb’)
Header = fp.read(80)
nn = fp.read(4)
Numtri = unpack(‘i’, nn)[0]
#print nn
record_dtype = np.dtype([
(‘normals’, np.float32,(3,)),
(‘Vertex1’, np.float32,(3,)),
(‘Vertex2’, np.float32,(3,)),
(‘Vertex3’, np.float32,(3,)) ,
(‘atttr’, ‘<i2’,(1,) )
])
data = np.fromfile(fp , dtype = record_dtype , count =Numtri)
fp.close()
Normals = data[‘normals’]
Vertex1= data[‘Vertex1’]
Vertex2= data[‘Vertex2’]
Vertex3= data[‘Vertex3’]
p = np.append(Vertex1,Vertex2,axis=0)
p = np.append(p,Vertex3,axis=0) #list(v1)
Points =np.array(list(set(tuple(p1) for p1 in p)))
return Header,Points,Normals,Vertex1,Vertex2,Vertex3
def draw_box():
global NormalV,Vertex1,Vertex2,Vertex3
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, T_Color)
glPushMatrix()
glBegin(GL_TRIANGLES)
for i in range(len(NormalV)):
glNormal3f(NormalV[i][0],NormalV[i][1],NormalV[i][2])
glVertex3f(Vertex1[i][0],Vertex1[i][1],Vertex1[i][2])
glVertex3f(Vertex2[i][0],Vertex2[i][1],Vertex2[i][2])
glVertex3f(Vertex3[i][0],Vertex3[i][1],Vertex3[i][2])
glEnd()
glPopMatrix()
def prepare_GL():
global quadratic
quadratic = gluNewQuadric()
glViewport(0,0,640,480)
glClearColor(0.8,0.8,0.9,0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_LIGHTING)
glEnable(GL_LIGHTING)
glEnable(GL_NORMALIZE)
glShadeModel(GL_FLAT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective (90,1.3333,0.1,1000.)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glLightfv(GL_LIGHT0,GL_POSITION,[0,0,1,0])
glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1])
glLightfv(GL_LIGHT0,GL_SPECULAR,[1,1,1,1])
glEnable(GL_LIGHT0)
glDisable(GL_CULL_FACE)
gluLookAt (Eye_from_x, Eye_from_y, Eye_from_z, Center_X, Center_Y, Center_Z, 0.0, 0.0, 1.0)
def init_GL():
glutInit ([])
glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH)
x = 0
y = 0
width = 640
height = 480
glutInitWindowPosition (x, y);
glutInitWindowSize (width, height);
glutCreateWindow (“test”.encode(“cp932”))
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
def _keyfunc (c, x, y):
global Eye_from_x ,Eye_from_y, Eye_from_z,Center_Z
if c == “p”.encode(“cp932”):
Center_Z = Center_Z + 5.0
elif c == “l”.encode(“cp932”):
Center_Z = Center_Z – 5.0
if c == “j”.encode(“cp932”):
Eye_from_x = Eye_from_x + 5.0
elif c == “f”.encode(“cp932”):
Eye_from_x = Eye_from_x – 5.0
elif c == “b”.encode(“cp932”):
Eye_from_z = Eye_from_z + 5.0
elif c == “c”.encode(“cp932”):
Eye_from_z = Eye_from_z – 5.0
elif c == “u”.encode(“cp932”):
Eye_from_y = Eye_from_y + 5.0
elif c == “r”.encode(“cp932”):
Eye_from_y = Eye_from_y – 5.0
elif c == “q”.encode(“cp932”):
sys.exit (0)
else:
print (“key missed”)
glutPostRedisplay()
def _drawfunc ():
green = [0.0, 1.0, 0.0, 1.0]
blue = [0.0, 0.0, 1.0, 1.0]
red = [1.0, 0.0, 0.0, 1.0]
prepare_GL()
draw_box()
glutSwapBuffers ()
def _idlefunc ():
glutPostRedisplay ()
#main program loop
def main():
global NormalV,Vertex1,Vertex2,Vertex3
fname = “Zentradi_Battlepod_-_Low_Poly.stl” # “porsche.stl”
head,NodePoint,NormalV,Vertex1,Vertex2,Vertex3 = BinarySTL(fname)
print (len(NormalV))
max_x = -99999999999.
min_x = 99999999999.
for i in range(len(NormalV)):
if (max_x < Vertex1[i][0]): max_x = Vertex1[i][0]
if (min_x > Vertex1[i][0]): min_x = Vertex1[i][0]
if (max_x < Vertex2[i][0]): max_x = Vertex2[i][0]
if (min_x > Vertex2[i][0]): min_x = Vertex2[i][0]
if (max_x < Vertex3[i][0]): max_x = Vertex3[i][0]
if (min_x > Vertex3[i][0]): min_x = Vertex3[i][0]
print (max_x,min_x)
max_y = -99999999999.
min_y = 99999999999.
for i in range(len(NormalV)):
if (max_y < Vertex1[i][1]): max_y = Vertex1[i][1]
if (min_y > Vertex1[i][1]): min_y = Vertex1[i][1]
if (max_y < Vertex2[i][1]): max_y = Vertex2[i][1]
if (min_y > Vertex2[i][1]): min_y = Vertex2[i][1]
if (max_y < Vertex3[i][1]): max_y = Vertex3[i][1]
if (min_y > Vertex3[i][1]): min_y = Vertex3[i][1]
print (max_y,min_y)
max_z = -99999999999.
min_z = 99999999999.
for i in range(len(NormalV)):
if (max_z < Vertex1[i][2]): max_z = Vertex1[i][2]
if (min_z > Vertex1[i][2]): min_z = Vertex1[i][2]
if (max_z < Vertex2[i][2]): max_z = Vertex2[i][2]
if (min_z > Vertex2[i][2]): min_z = Vertex2[i][2]
if (max_z < Vertex3[i][2]): max_z = Vertex3[i][2]
if (min_z > Vertex3[i][2]): min_z = Vertex3[i][2]
print (max_z,min_z)
Center_X = (max_x+min_x)/2.
Center_Y = (max_y+min_y)/2.
Center_Z = (max_z+min_z)/2.
init_GL()
glutKeyboardFunc (_keyfunc)
glutDisplayFunc (_drawfunc)
glutIdleFunc (_idlefunc)
glutMainLoop ()
if __name__ == ‘__main__’:
main()
ここまで。
結果はこんな感じです。前方から見たものです。
おなじく側方から。
最後は後方からです。
と、表示までは達成することができました。この後は、ODEで扱える形式に変換する作業と、歩行シミュレーション用のモデルの作成です。