作成日
2020/04/19最終更新
2024/10/18記事区分
一般公開Open Dynamics Engine (ODE) のジョイント操作について記載します。
ヒンジジョイント
ロボットにはジョイントとリンクがあります。ODE に実装されているジョイントの一つにヒンジジョイントがあります。以下の例では二つの立方体をヒンジジョイントで結合して、ヒンジジョイントに角速度を与えています。
初期位置
しばらくすると 45 度だけ回転した状態になります。
angle=-45.0000000715, angleRate=6.19802999323e-09
angle=-45.0000000687, angleRate=4.95843873643e-09
sample.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import odepy
import drawstuffpy as ds
import ctypes
import random
import numpy as np
def Main():
world, space, ground, contactgroup = CreateWorld()
# 立方体を二つ設置します。
size = 0.5
box1 = AddBox(world, space, pos=[0, size / np.sqrt(2), 2.0], size=size)
box2 = AddBox(world, space, pos=[0, -size / np.sqrt(2), 2.0], size=size)
geoms = [box1['geom'], box2['geom']]
geomColors = [[random.random(), random.random(), random.random()] for geom in geoms]
# ヒンジジョイントの作成。
joint = AddHinge(world, box1['body'], box2['body'])
def __NearCallback(data, o1, o2):
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 = 1.0
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))
def __StepCallback(pause):
# ヒンジジョイントの回転角度と角速度を出力
angle = odepy.dJointGetHingeAngle(joint) * 180 / np.pi # [rad] -> [deg]
angleRate = odepy.dJointGetHingeAngleRate(joint) # [rad/s]
print('angle={}, angleRate={}'.format(angle, angleRate))
tDelta = 0.01
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.dBoxClass:
lengths = odepy.dVector3()
odepy.dGeomBoxGetLengths(geom, lengths)
pos = odepy.dBodyGetPosition(body)
rot = odepy.dBodyGetRotation(body)
ds.dsDrawBoxD(pos, rot, lengths)
else:
raise Exception('Not supported geom class: {}'.format(odepy.dGeomGetClass(geom)))
RunDrawStuff(__StepCallback)
DestroyWorld(world, space)
def AddHinge(world, body1, body2):
# ヒンジジョイント
joint = odepy.dJointCreateHinge(world, 0)
odepy.dJointAttach(joint, body1, body2)
# 二つの立方体の中間点を初期位置として作成
pos1 = odepy.dBodyGetPosition(body1)
pos2 = odepy.dBodyGetPosition(body2)
odepy.dJointSetHingeAnchor(joint,
(pos1[0] + pos2[0]) / 2.0,
(pos1[1] + pos2[1]) / 2.0,
(pos1[2] + pos2[2]) / 2.0)
# z 軸方向を回転軸として設定
odepy.dJointSetHingeAxis(joint, 0, 0, 1)
# 最大の回転角度
odepy.dJointSetHingeParam(joint, odepy.dParamLoStop, -np.pi / 4.0)
odepy.dJointSetHingeParam(joint, odepy.dParamHiStop, np.pi / 4.0)
# ジョイントに角速度を与えてみます。
odepy.dJointSetHingeParam(joint, odepy.dParamVel, -1.0) # 角速度 [rad/s]
odepy.dJointSetHingeParam(joint, odepy.dParamFMax, 200) # 最大トルク [Nm]
# ジョイントにトルクを与えることもできます。
# odepy.dJointAddHingeTorque(joint, -400.0) # トルク [Nm]
# odepy.dJointSetHingeParam(joint, odepy.dParamFMax, 200) # 最大トルク [Nm]
return joint
def AddBox(world, space, pos, size, m=1.0):
mass = odepy.dMass()
odepy.dMassSetZero(ctypes.byref(mass))
odepy.dMassSetBoxTotal(ctypes.byref(mass), m, size, size, size)
body = odepy.dBodyCreate(world)
odepy.dBodySetMass(body, ctypes.byref(mass))
geom = odepy.dCreateBox(space, size, size, size)
odepy.dGeomSetBody(geom, body)
box = {'body': body, 'geom': geom}
odepy.dBodySetPosition(box['body'], *pos)
# z 軸方向に 45 度回転させておきます。
R = odepy.dMatrix3()
odepy.dRFromAxisAndAngle(R, 0, 0, 1, np.pi / 4.0)
odepy.dBodySetRotation(box['body'], R)
return box
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()
スライダージョイント
ODE に実装されているスライダージョイントのサンプルコードです。
初期位置
縮んだ状態
sample.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ctypes import addressof
from ctypes import byref
from ctypes import sizeof
from ctypes import c_float
from ctypes import c_char
from ctypes import POINTER
from ctypes import create_string_buffer
from random import random
from numpy import pi
import odepy
import drawstuffpy as ds
step = 0
def Main():
world, space, ground, contactgroup = CreateWorld()
# 立方体を二つ設置します。
box1 = AddBox(world, space, pos=[-1.0, -1.0, 1.5], size=1.0)
box2 = AddBox(world, space, pos=[-1.0, 1.0, 1.5], size=1.0)
geoms = [box1['geom'], box2['geom']]
geomColors = [[random(), random(), random()] for geom in geoms]
# スライダージョイントの作成。
joint = AddSlider(world, box1['body'], box2['body'])
def __NearCallback(data, o1, o2):
# 今回、地面との衝突以外は無視します。
o1IsGround = addressof(ground.contents) == addressof(o1.contents)
o2IsGround = addressof(ground.contents) == addressof(o2.contents)
if not (o1IsGround or o2IsGround):
return
N = 10
contacts = (odepy.dContact * N)()
n = odepy.dCollide(o1, o2, N, byref(contacts[0].geom), sizeof(odepy.dContact))
for i in range(n):
contact = contacts[i]
contact.surface.mu = float('inf')
contact.surface.mode = odepy.dContactBounce
contact.surface.bounce = 1.0
contact.surface.bounce_vel = 0.0
c = odepy.dJointCreateContact(world, contactgroup, byref(contact))
odepy.dJointAttach(c, odepy.dGeomGetBody(contact.geom.g1), odepy.dGeomGetBody(contact.geom.g2))
def __StepCallback(pause):
# スライダージョイントの伸縮と伸縮速度を出力
pos = odepy.dJointGetSliderPosition(joint) # [m]
posRate = odepy.dJointGetSliderPositionRate(joint) # [m/s]
# スライダージョイントの伸縮を時間に応じて制御してみます。
global step
step += 1
stepCycle = 200
if 0 <= step % stepCycle and step % stepCycle <= 10:
print('pos=%d, posRate=%d' % (pos * 100, posRate))
target = 1.0 # 縮む
else:
target = 0 # 初期位置
if pause != 1:
kp = 25.0
fmax = 400
u = kp * (target - pos)
odepy.dJointSetSliderParam(joint, odepy.dParamVel, u)
odepy.dJointSetSliderParam(joint, odepy.dParamFMax, fmax)
# tDelta 時間進めます。
tDelta = 0.01
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.dBoxClass:
lengths = odepy.dVector3()
odepy.dGeomBoxGetLengths(geom, lengths)
pos = odepy.dBodyGetPosition(body)
rot = odepy.dBodyGetRotation(body)
ds.dsDrawBoxD(pos, rot, lengths)
else:
raise Exception('Not supported geom class: {}'.format(odepy.dGeomGetClass(geom)))
RunDrawStuff(__StepCallback)
DestroyWorld(world, space)
def AddSlider(world, body1, body2):
# スライダージョイント
joint = odepy.dJointCreateSlider(world, 0)
odepy.dJointAttach(joint, body1, body2)
# y 軸方向を伸縮方向として設定
odepy.dJointSetSliderAxis(joint, 0, 1, 0)
odepy.dJointSetSliderParam(joint, odepy.dParamLoStop, 0.0) # 最大の伸び [m]
odepy.dJointSetSliderParam(joint, odepy.dParamHiStop, 1.0) # 最大の縮み [m]
return joint
def AddBox(world, space, pos, size, m=1.0):
mass = odepy.dMass()
odepy.dMassSetZero(byref(mass))
odepy.dMassSetBoxTotal(byref(mass), m, size, size, size)
body = odepy.dBodyCreate(world)
odepy.dBodySetMass(body, byref(mass))
geom = odepy.dCreateBox(space, size, size, size)
odepy.dGeomSetBody(geom, body)
box = {'body': body, 'geom': geom}
odepy.dBodySetPosition(box['body'], *pos)
# z 軸方向に 45 度回転させておきます。
R = odepy.dMatrix3()
odepy.dRFromAxisAndAngle(R, 0, 0, 1, pi / 4.0)
odepy.dBodySetRotation(box['body'], R)
return box
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 = (c_float * 3)()
hpr = (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 = create_string_buffer(pathToTextures.encode('utf-8'))
ds.dsSimulationLoop(0, byref(POINTER(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()
関連記事
- Python コードスニペット (条件分岐)if-elif-else sample.py #!/usr/bin/python # -*- coding: utf-8 -*- # コメント内であっても、ASCII外の文字が含まれる場合はエンコーディング情報が必須 x = 1 # 一行スタイル if x==0: print 'a' # 参考: and,or,notが使用可能 (&&,||はエラー) elif x==1: p...
- Python コードスニペット (リスト、タプル、ディクショナリ)リスト range 「0から10まで」といった範囲をリスト形式で生成します。 sample.py print range(10) # for(int i=0; i<10; ++i) ← C言語などのfor文と比較 print range(5,10) # for(int i=5; i<10; ++i) print range(5,10,2) # for(int i=5; i<10;...
- ZeroMQ (zmq) の Python サンプルコードZeroMQ を Python から利用する場合のサンプルコードを記載します。 Fixing the World To fix the world, we needed to do two things. One, to solve the general problem of "how to connect any code to any code, anywhere". Two, to wra...
- Matplotlib/SciPy/pandas/NumPy サンプルコードPython で数学的なことを試すときに利用される Matplotlib/SciPy/pandas/NumPy についてサンプルコードを記載します。 Matplotlib SciPy pandas [NumPy](https://www.numpy
- pytest の基本的な使い方pytest の基本的な使い方を記載します。 適宜参照するための公式ドキュメントページ Full pytest documentation API Reference インストール 適当なパッケージ