Skip to content

Commit 18f1dc4

Browse files
committed
Merge pull request #251 from DavidB-CMU/master
Add convenience function to compute forward kinematics
2 parents 12d352a + 0e4218b commit 18f1dc4

File tree

2 files changed

+104
-2
lines changed

2 files changed

+104
-2
lines changed

src/prpy/util.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,44 @@ def CheckJointLimits(robot, q):
10271027
description='position')
10281028

10291029

1030+
def GetForwardKinematics(robot, q, manipulator=None, frame=None):
1031+
"""
1032+
Get the forward kinematics for a specific joint configuration,
1033+
relative to the OpenRAVE world frame.
1034+
1035+
@param openravepy.robot robot: The robot object.
1036+
@param list q: List or array of joint positions.
1037+
@param manipulator
1038+
@param string frame: Get the end effector transform relative to a
1039+
specific frame e.g. '/right/wam_base'
1040+
@returns T_ee: The pose of the end effector (or last link in the
1041+
serial chain) as a 4x4 matrix.
1042+
"""
1043+
if manipulator == None:
1044+
manipulator = robot.GetActiveManipulator()
1045+
1046+
T_ee = None
1047+
1048+
# Save the robot state
1049+
sp = openravepy.Robot.SaveParameters
1050+
robot_saver = robot.CreateRobotStateSaver(sp.LinkTransformation)
1051+
1052+
with robot_saver:
1053+
robot.SetActiveDOFValues(q)
1054+
T_ee = manipulator.GetEndEffectorTransform()
1055+
# Robot state is restored
1056+
1057+
if frame != None:
1058+
link = robot.GetLink(frame)
1059+
if link == None:
1060+
raise ValueError('Failed to get link \'{:s}\''.format(frame))
1061+
1062+
T_ref_frame = link.GetTransform()
1063+
T_ee = numpy.dot(numpy.linalg.inv(T_ref_frame), T_ee)
1064+
1065+
return T_ee
1066+
1067+
10301068
def ConvertIntToBinaryString(x, reverse=False):
10311069
"""
10321070
Convert an integer to a binary string.

tests/test_util.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ def test_SampleTimeGenerator_StepOfTwo(self):
565565
decimal=7, err_msg=error, \
566566
verbose=True)
567567

568-
def test_SampleTimeGenerator_(self):
568+
def test_SampleTimeGenerator_EndPointMoreThanHalfStepSize(self):
569569
# Check that the sequence includes the end-point when it's
570570
# distance from the previous point is more than half the
571571
# step-size. This is required for collision-checking
@@ -581,7 +581,7 @@ def test_SampleTimeGenerator_(self):
581581
decimal=7, err_msg=error, \
582582
verbose=True)
583583

584-
def test_SampleTimeGenerator_(self):
584+
def test_SampleTimeGenerator_EndPointLessThanHalfStepSize(self):
585585
# Check that the end-point is excluded when it's less than
586586
# half the step-size from the previous value.
587587
expected_sequence = [0.0, 2.0, 4.0]
@@ -595,7 +595,70 @@ def test_SampleTimeGenerator_(self):
595595
decimal=7, err_msg=error, \
596596
verbose=True)
597597

598+
599+
# GetForwardKinematics()
600+
601+
def test_GetForwardKinematics_UseActiveManipulator(self):
602+
# Zero configuration, uses the the active manipulator by default
603+
q0 = numpy.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
604+
T_ee = prpy.util.GetForwardKinematics(self.robot, q0)
605+
606+
expected_T_ee0 = numpy.array([[1.0, 0.0, 0.0, 0.07 ],
607+
[0.0, 1.0, 0.0, 0.0 ],
608+
[0.0, 0.0, 1.0, 2.168],
609+
[0.0, 0.0, 0.0, 1.0 ]])
610+
error = 'The end-effector transform ' + str(T_ee) + \
611+
' does not equal the expected end-effector' + \
612+
' transform ' + str(expected_T_ee0)
613+
numpy.testing.assert_array_almost_equal(T_ee, expected_T_ee0, decimal=7, \
614+
err_msg=error, verbose=True)
615+
616+
def test_GetForwardKinematics_SpecifyManipulator(self):
617+
# A different configuration, specifying the manipulator to use
618+
q1 = numpy.array([-1.8, 0.0, -0.7, 2.0, 0.5, 0.2, 0.0])
619+
manip = self.robot.GetActiveManipulator()
620+
T_ee = prpy.util.GetForwardKinematics(self.robot, q1, manip)
621+
622+
expected_T_ee1 = numpy.array([[ 0.7126777, 0.3653714, -0.5988272, -0.3313395],
623+
[-0.0541115, -0.8224716, -0.5662263, -0.3259651],
624+
[-0.6994014, 0.4359404, -0.5663864, 1.4394693],
625+
[ 0.0, 0.0, 0.0, 1.0 ]])
626+
error = 'The end-effector transform ' + str(T_ee) + \
627+
' does not equal the expected end-effector' + \
628+
' transform ' + str(expected_T_ee1)
629+
numpy.testing.assert_array_almost_equal(T_ee, expected_T_ee1, decimal=7, \
630+
err_msg=error, verbose=True)
631+
632+
def test_GetForwardKinematics_SpecifyFrame(self):
633+
# Get the FK with respect to the base frame ('segway') of the
634+
# manipulator chain. This should give the same result as not
635+
# specifying the reference frame at all.
636+
q1 = numpy.array([-1.8, 0.0, -0.7, 2.0, 0.5, 0.2, 0.0])
637+
manip = self.robot.GetActiveManipulator()
638+
frame = 'segway'
639+
T_ee = prpy.util.GetForwardKinematics(self.robot, q1, manip, frame)
640+
641+
expected_T_ee2 = numpy.array([[ 0.7126777, 0.3653714, -0.5988272, -0.3313395],
642+
[-0.0541115, -0.8224716, -0.5662263, -0.3259651],
643+
[-0.6994014, 0.4359404, -0.5663864, 1.4394693],
644+
[ 0.0, 0.0, 0.0, 1.0 ]])
645+
646+
# We need to transform the expected result along the z-axis
647+
# because the Segway-WAM model is strange
648+
T_segway = self.robot.GetLink('segway').GetTransform()
649+
expected_T_ee2 = numpy.dot(numpy.linalg.inv(T_segway), expected_T_ee2)
650+
651+
error = 'The end-effector transform ' + str(T_ee) + \
652+
' does not equal the expected end-effector' + \
653+
' transform ' + str(expected_T_ee2)
654+
numpy.testing.assert_array_almost_equal(T_ee, expected_T_ee2, decimal=7, \
655+
err_msg=error, verbose=True)
656+
657+
598658
class Test_GetPointFrom(unittest.TestCase):
659+
"""
660+
Unit Tests for GetPointFrom()
661+
"""
599662
def setUp(self):
600663
self.env = openravepy.Environment()
601664

@@ -641,5 +704,6 @@ def test_GetPointFrom_List(self):
641704
list_result = prpy.util.GetPointFrom(list_coord)
642705
numpy.testing.assert_array_almost_equal(list_result, expected_coord)
643706

707+
644708
if __name__ == '__main__':
645709
unittest.main()

0 commit comments

Comments
 (0)