OpenRAVE IKFast で IK を解くサンプル
[履歴] [最終更新] (2019/08/21 22:20:37)
最近の投稿
注目の記事

概要

産業用ロボットには、垂直多関節ロボット (Articulated)水平多関節ロボット (SCARA) があります。ロボットにはジョイントとリンクがあり、ジョイントの関節値を指定することで制御できます。

  • 関節値を指定して、位置姿勢を求める場合を順運動学 (Forward Kinematics) とよびます。
  • 位置姿勢を指定して、関節値を求める場合を逆運動学 (Inverse Kinematics) とよびます。

例えば、Motion planning でロボットのアームの先端にあるデバイス (エンドエフェクタ、マニピュレータ) の位置姿勢を指定したときに取り得る関節値を求めるためには、後者の IK を解きます。IK を解くためには OpenRAVE の IKFast モジュールが利用できます。

目標点 (IK goal) の準備

ロボットに行わせたいタスクによって、目標となるマニピュレータの位置姿勢 IK goal を決めます。これを動的に生成する必要がある場合もありますが、ここではマニピュレータを固定位置に動かしたいとします。向きを指定する場合は Transform6D で解きます。向きを指定しない場合は Translation3D で解きます。

また、OpenRAVE Custom XML Format で記述された puma ロボットを利用することにします。

openrave.py -i src/robots/puma.robot.xml

Uploaded Image

ロボット名を取得

robot.GetName()

ジョイントの数を取得 (DOF; degrees of freedom)

robot.GetDOF()

ジョイントの値を取得、設定

robot.GetDOFValues()
robot.SetDOFValues([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7])

リンクの変換行列を取得

T = robot.GetLinks()[1].GetTransform()

変換行列で位置姿勢を設定

from openravepy import matrixFromAxisAngle
from numpy import pi

# ワールド座標系における Z 軸まわりに 45度回転
Tz = matrixFromAxisAngle([0, 0, pi/4])
robot.SetTransform(Tz.dot(robot.GetTransform()))

ログレベルを変更

RaveSetDebugLevel(DebugLevel.Verbose)

環境を複数扱うことができます。

env2 = Environment()
robot2 = env2.ReadRobotXMLFile(filename='src/robots/puma.robot.xml')
env2.AddKinBody(robot2)

関節値に設定できる値の範囲を調べる

manip = robot.GetActiveManipulator()
lower,upper = robot.GetDOFLimits(manip.GetArmIndices())

ロボット自身の衝突および環境との衝突を調べる

robot.CheckSelfCollision()
env.CheckCollision(robot)

環境のファイル保存

env.Save('newenv.zae', Environment.SelectionOptions.Everything)

IK を解く

マニピュレータが目標点にあるときに取ることができる関節値を、周囲の環境との衝突も考慮して計算します。解は複数存在し得ります。

Transform6D

マニピュレータの位置だけでなく向きも指定して IK を解きます。

# マニピュレータを取得
manip = robot.GetActiveManipulator()

# 初回のみ ikmodel を生成します。時間がかかりますが一度計算しておけば IK を解く際に再利用できます。
ikmodel = databases.inversekinematics.InverseKinematicsModel(robot, iktype=IkParameterization.Type.Transform6D)
ikmodel.autogenerate()

# 目標点を設定します。
Tgoal = numpy.array([
    [ 0, -1,  0, -0.21],
    [-1,  0,  0,  0.04],
    [ 0,  0, -1,  0.92],
    [ 0,  0,  0,     1]])

# 衝突を考慮しつつ IK を解きます。
sol = manip.FindIKSolution(Tgoal, IkFilterOptions.CheckEnvCollisions)

# 関節値をロボットに設定してみます。
robot.SetDOFValues(sol, manip.GetArmIndices())
env.UpdatePublishedBodies()

関節値を指定すると、確かに目標点に指定した位置姿勢にマニピュレータが移動されました。

tManipToWorld = manip.GetEndEffectorTransform()

In [25]: tManipToWorld[:3,:3]
Out[25]: 
array([[ 6.66133815e-15, -1.00000000e+00, -4.31315374e-15],
       [-1.00000000e+00, -6.66133815e-15,  1.57684927e-15],
       [-1.57684927e-15,  4.31315374e-15, -1.00000000e+00]])

In [26]: tManipToWorld[:,3]
Out[26]: array([-0.21,  0.04,  0.92,  1.  ])

Translation3D

マニピュレータの位置だけを指定して IK を解きます。複数の解が存在し得ります。

# マニピュレータを取得
manip = robot.GetActiveManipulator()

# 今回は Translation3D を指定します。
ikmodel = databases.inversekinematics.InverseKinematicsModel(robot,iktype=IkParameterization.Type.Translation3D)
ikmodel.autogenerate()

# 目標点を設定します。
# ここでは以下のような関節値を設定したときの
robot.SetDOFValues([2.58, 0.547, 1.5, -0.7], [0,1,2,3]) # set the first 4 dof values
# マニピュレータの位置を目標点とします。向きは指定しません。
tManipToWorld = manip.GetEndEffectorTransform()
ikparam = IkParameterization(tManipToWorld[0:3,3], ikmodel.iktype) # build up the translation3d ik query

# IK を解きます。
sols = manip.FindIKSolutions(ikparam, IkFilterOptions.CheckEnvCollisions)
raveLogInfo('%d solutions' % len(sols))

# 目標点の位置に印を表示してみます。
h = env.plot3(tManipToWorld[0:3,3], 10) # plot one point

# 関節値をロボットに設定してみます。
for sol in sols:
    robot.SetDOFValues(sol, manip.GetArmIndices())
    env.UpdatePublishedBodies()
    time.sleep(10.0/len(sols))

ikmodel を保存して次回以降に利用するためには以下のようにします。

In [13]: ikmodel.save()
openravepy.databases.inversekinematics: save, inversekinematics generation is done, compiled shared object: /home/xxx/.openrave/kinematics.9559e67e82872bf8fa24cbb50bdc4388/ikfast0x1000004a.Translation3D.x86_64.0_1_2_f3_4_5.so

以下のようにして読み込みます。ikmodel.autogenerate() は不要です。

In [2]: ikmodel.load()
openravepy.databases.inversekinematics: GetDefaultIndices, found 3-intersection centered on index 4
Out[2]: True

RRT で経路計画を行う

OpenRAVE には RRT (Rapidly-exploring random tree) でロボットの軌道 trajectory を計画する機能があります。これを利用して、IK で解いた関節値になるようにロボットを移動させてみます。OpenRAVE には RRT を両端から行う BiRRT (Bi-directional RRTs) が実装されています。

# create the interface for basic manipulation programs
manipprob = interfaces.BaseManipulation(robot)

# DOF Value を指定します。
manipprob.MoveManipulator(goal=[-1, 0, 1, 1, 1, 1])

In [29]: robot.GetDOFValues()
Out[29]: 
array([-1.00000000e+00, -1.11022302e-15,  1.00000000e+00,  1.00000000e+00,
        1.00000000e+00,  1.00000000e+00,  2.11177436e-14])

Uploaded Image

IK と RRT をまとめて行う

マニピュレータの位置姿勢を Transform6D で指定して IK を解いて関節値を得て、RRT で関節値になるようにロボットを移動させる、という二つのことをまとめて行うためには以下のようにします。

ikmodel = databases.inversekinematics.InverseKinematicsModel(robot, iktype=IkParameterization.Type.Transform6D)
if not ikmodel.load():
    ikmodel.autogenerate()

manipprob = interfaces.BaseManipulation(robot)
Tgoal = numpy.array([
    [ 0, -1,  0, -0.21],
    [-1,  0,  0,  0.04],
    [ 0,  0, -1,  0.92],
    [ 0,  0,  0,     1]])

res = manipprob.MoveToHandPosition(matrices=[Tgoal], seedik=10) # call motion planner with goal joint angles

軌道 trajectory を取得

res = manipprob.MoveToHandPosition(matrices=[Tgoal], seedik=10) # call motion planner with goal joint angles

ロボットを実際に移動させる前に、計画した軌道を取得することができます。

traj = manipprob.MoveToHandPosition(matrices=[Tgoal], execute=False, outputtrajobj=True)

軌道は複数の waypoint で区切られています。

raveLogInfo('traj has %d waypoints, last waypoint is: %s' % (traj.GetNumWaypoints(), repr(traj.GetWaypoint(-1))))

ロボットを移動させるためには以下のようにします。

robot.GetController().SetPath(traj)

各 waypoint における関節値を取得するためには以下のようにします。関節値の他に、速度やタイムスタンプなどの情報が含まれています。

for i in range(traj.GetNumWaypoints()):
    data=traj.GetWaypoint(i)
    dofvalues = traj.GetConfigurationSpecification().ExtractJointValues(data, robot, robot.GetActiveDOFIndices())
    raveLogInfo('waypoint %d is %s' % (i, dofvalues))

OpenRAVE の関連機能

動力学シミュレータ ode (Open Dynamics Engine) の利用

# ode の読み込み
physics = RaveCreatePhysicsEngine(env, 'ode')
env.SetPhysicsEngine(physics)

# シミュレーションを停止しておきます。
env.StopSimulation()

# 重力を設定します。
physics.SetGravity(numpy.array((0, 0, -9.8)))

# ロボットの土台が落下しないようにしてみます。
robot.GetLinks()[0].SetStatic(True)

# シミュレーションを開始してみます。
env.StartSimulation(timestep=0.001)

# 5秒間だけジョイントの一つにトルクを加えてみます。右方向に回転します。
robot.GetDOFTorqueLimits()
for i in range(500):
    robot.SetDOFTorques([100, 0, 0, 0, 0, 0, 0], True)
    time.sleep(0.01)

# シミュレータを終了します。
env.SetPhysicsEngine(None)

Uploaded Image

IK goal を動的に生成する例

IK goal を動的に生成する例として、ワークをマニピュレータで把持する場合を考えます。ロボットとマグカップが存在する環境をロードします。

openrave.py -i src/data/lab1.env.xml

マグカップの一つを把持対象として設定します。把持対象とマニピュレータの組で Grasping Model を生成します。対象を把持できる可能性のあるマニピュレータの姿勢をすべて生成するため、モデルの作成には時間がかかります。Box などの簡単な形状の把持対象よりも Cylinder などの場合の方が時間がかかります。

target = env.GetKinBody('mug1')
gmodel = databases.grasping.GraspingModel(robot, target)
if not gmodel.load():
    gmodel.autogenerate()

Uploaded Image

モデルを解いて把持セットを出力します。複数の解が存在し得りますが、一つだけ取得してみます。存在しないこともあります。

validgrasps, validindicees = gmodel.computeValidGrasps(returnnum=1)

このように、把持セットは把持対象に応じて動的に生成されます。動的に生成された把持セットが IK goal となり ikmodel に入力されます。RRT まで含めて IK を解いてみます。把持対象を把持するための軌道において、把持対象とロボットの衝突は回避 collisionfree=True する設定にしてみます。移動後に把持する際には衝突します。

gmodel.moveToPreshape(validgrasps[0])
Tgoal = gmodel.getGlobalGraspTransform(validgrasps[0], collisionfree=True)
manipprob = interfaces.BaseManipulation(robot)
manipprob.MoveToHandPosition(matrices=[Tgoal])

Uploaded Image

関連ページ
    概要 こちらのページでインストールした Open Dynamics Engine (ODE) のジョイント操作について記載します。 ヒンジジョイント ロボットにはジョイントとリンクがあります。ODE に実装されているジョイントの一つにヒンジジョイントがあります。以下の例では二つの立方体をヒンジジョイントで結合して、ヒンジジョイントに角速度を与えています。
    概要 ロボットアームは複数のリンクがジョイントで結合されており、先端にマニピュレータが存在します。Open Dynamics Engine (ODE) を用いたシミュレーションを行い、ジョイントの関節値とマニピュレータの位置姿勢の関係を把握します。 順運動学によるマニピュレータの位置姿勢の計算 回転行列と回転ベクトルの変換のために以下のパッケージを利用しています。
    概要 ロボットアームのマニピュレータの位置姿勢が与えられたときに、ジョイントの関節値を求める逆運動学について、3自由度の場合を対象として解析解と数値解を用いる場合を記載します。 解析解 3 自由度の場合は解析的に逆運動学を解くことができます。以下は 4 つ存在する解析解のうちの一つを利用しています。詳細はこちらの書籍をご覧ください。
    概要 Python で数学的なことを試すときに利用される Matplotlib/SciPy/pandas/NumPy についてサンプルコードを記載します。 Matplotlib SciPy pandas NumPy チュートリアル Installing packages Quickstart tutorial