diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee94c4a7f669d1f7b06d1e106bf09c4ca27e05dc..28c26aedfc09f39afdf92131a6c5d92d6a2b92e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,6 +65,7 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/sot/dynamics/humanoid_robot.py ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/sot/dynamics/tools.py ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/sot/dynamics/solver.py + ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/sot/dynamics/parser.py DESTINATION ${PYTHON_SITELIB}/dynamic_graph/sot/dynamics ) diff --git a/src/dynamic_graph/sot/dynamics/parser.py b/src/dynamic_graph/sot/dynamics/parser.py new file mode 100755 index 0000000000000000000000000000000000000000..0faad54e95d97672494bff8b84c58b43f5370fa2 --- /dev/null +++ b/src/dynamic_graph/sot/dynamics/parser.py @@ -0,0 +1,295 @@ +# -*- coding: utf-8 -*- + +# Copyright 2010 CNRS +# Author: Florent Lamiraux +# +# Release under LGPL license: see COPYING.LESSER at root of the project. +# + +import xml.dom.minidom as dom +from dynamic_graph.sot.dynamics.dynamic import Dynamic +from dynamic_graph.sot.tools.se3 import SE3, R3, SO3 + +class Parser (object): + """ + Parser to build dynamic_graph.sot.dynamics.dynamic.Dynamic entities. + + Format is kxml, Kineo CAM robot description format. + """ + robotFloatProperties = ['GAZEORIGINX', 'GAZEORIGINY', 'GAZEORIGINZ', + 'GAZEDIRECTIONX', + 'GAZEDIRECTIONY', + 'GAZEDIRECTIONZ', + 'ANKLEPOSINLEFTFOOTFRAMEX', + 'ANKLEPOSINLEFTFOOTFRAMEY', + 'ANKLEPOSINLEFTFOOTFRAMEZ', + 'SOLELENGTH', 'SOLEWIDTH', + 'LEFTHANDCENTERX', + 'LEFTHANDCENTERY', + 'LEFTHANDCENTERZ', + 'LEFTTHUMBAXISX', + 'LEFTTHUMBAXISY', + 'LEFTTHUMBAXISZ', + 'LEFTFOREFINGERAXISX', + 'LEFTFOREFINGERAXISY', + 'LEFTFOREFINGERAXISZ', + 'LEFTPALMNORMALX', + 'LEFTPALMNORMALY', + 'LEFTPALMNORMALZ'] + + robotStringProperties = ['WAIST', 'CHEST', 'LEFTWRIST', + 'RIGHTWRIST', 'LEFTANKLE', 'RIGHTANKLE', 'GAZE'] + + jointFloatProperties = ['MASS', 'COM_X', 'COM_Y', 'COM_Z', + 'INERTIA_MATRIX_XX', 'INERTIA_MATRIX_YY', + 'INERTIA_MATRIX_ZZ', 'INERTIA_MATRIX_XY', + 'INERTIA_MATRIX_XZ', 'INERTIA_MATRIX_YZ'] + + jointTypes = ['HPP_FREEFLYER_JOINT', 'HPP_ROTATION_JOINT', + 'HPP_TRANSLATION_JOINT', 'HPP_ANCHOR_JOINT'] + + jointType = {'HPP_FREEFLYER_JOINT':'freeflyer', + 'HPP_ROTATION_JOINT':'rotation', + 'HPP_TRANSLATION_JOINT':'translation', + 'HPP_ANCHOR_JOINT':'anchor'} + + robotTag = "HPP_HUMANOID_ROBOT" + + def __init__(self, entityName, filename): + self.entity = Dynamic(entityName) + self.filename = filename + + def parse (self): + dom1 = dom.parse(self.filename) + hNode = dom1.getElementsByTagName(self.robotTag)[0] + for p in self.robotStringProperties: + value = self.findStringProperty(hNode, p) + setattr(self, p, value) + + for p in self.robotFloatProperties: + value = self.findFloatProperty(hNode, p) + setattr(self, p, value) + + self.entity.createRobot() + rootJointNode = self.findRootJoint(hNode) + self.createJoint(rootJointNode, hNode.nodeName) + # Set specific joints. + self.entity.setSpecificJoint(str(self.WAIST), 'waist') + self.entity.setSpecificJoint(str(self.CHEST), 'chest') + self.entity.setSpecificJoint(str(self.LEFTWRIST), 'left-wrist') + self.entity.setSpecificJoint(str(self.RIGHTWRIST), 'right-wrist') + self.entity.setSpecificJoint(str(self.LEFTANKLE), 'left-ankle') + self.entity.setSpecificJoint(str(self.RIGHTANKLE), 'right-ankle') + self.entity.setSpecificJoint(str(self.GAZE), 'gaze') + + # Hand parameters. + handCenter = (self.LEFTHANDCENTERX, + self.LEFTHANDCENTERY, + self.LEFTHANDCENTERZ) + thumbAxis = (self.LEFTTHUMBAXISX, + self.LEFTTHUMBAXISY, + self.LEFTTHUMBAXISZ) + + forefingerAxis = (self.LEFTFOREFINGERAXISX, + self.LEFTFOREFINGERAXISY, + self.LEFTFOREFINGERAXISZ) + + palmNormal = (self.LEFTPALMNORMALX, + self.LEFTPALMNORMALY, + self.LEFTPALMNORMALZ) + + self.entity.setHandParameters(False, handCenter, thumbAxis, + forefingerAxis, palmNormal) + # Compute values for right hand + handCenter = self.handSymmetry(handCenter) + thumbAxis = self.handSymmetry(thumbAxis) + forefingerAxis = self.handSymmetry(forefingerAxis) + palmNormal = self.handSymmetry(palmNormal) + self.entity.setHandParameters(True, handCenter, thumbAxis, + forefingerAxis, palmNormal) + + # Foot parameters. + soleLength = self.SOLELENGTH + soleWidth = self.SOLEWIDTH + anklePosition = (self.ANKLEPOSINLEFTFOOTFRAMEX, + self.ANKLEPOSINLEFTFOOTFRAMEY, + self.ANKLEPOSINLEFTFOOTFRAMEZ) + + self.entity.setFootParameters(False, soleLength, soleWidth, + anklePosition) + + anklePosition = (anklePosition[0], -anklePosition[1], anklePosition[2]) + self.entity.setFootParameters(True, soleLength, soleWidth, + anklePosition) + + # Gaze parameters. + gazeOrigin = (self.GAZEORIGINX, self.GAZEORIGINY, self.GAZEORIGINZ) + gazeDirection = (self.GAZEDIRECTIONX, self.GAZEDIRECTIONY, + self.GAZEDIRECTIONZ) + + self.entity.setGazeParameters(gazeOrigin, gazeDirection) + self.entity.initializeRobot() + return self.entity + + def createJoint (self, node, parentName): + sotJointType = self.jointType[node.nodeName] + jointName = self.findStringProperty(node, 'NAME') + properties = {} + for p in self.jointFloatProperties: + try: + properties[p] = self.findFloatProperty(node, p) + except RunTimeError: + print ('warning: ' + p + + ' property not specified, set to 0.') + properties[p] = 0. + + position = self.findJointPosition(node) + # Remember position of wrists. + if jointName == self.LEFTWRIST: + self.leftWristPosition = position[:] + if jointName == self.RIGHTWRIST: + self.rightWristPosition = position[:] + # Find dof bounds. + bounds = self.findJointBounds(node, jointName) + + self.entity.createJoint(jointName, sotJointType, position) + # set mass center of mass and inertia matrix of attached body + self.entity.setMass(jointName, properties['MASS']) + com = (properties['COM_X'], properties['COM_Y'], properties['COM_Z']) + self.entity.setLocalCenterOfMass(jointName, com) + ixx = properties['INERTIA_MATRIX_XX'] + iyy = properties['INERTIA_MATRIX_YY'] + izz = properties['INERTIA_MATRIX_ZZ'] + ixy = properties['INERTIA_MATRIX_XY'] + ixz = properties['INERTIA_MATRIX_XZ'] + iyz = properties['INERTIA_MATRIX_YZ'] + inertiaMatrix = ((ixx, ixy, ixz), + (ixy, iyy, iyz), + (ixz, iyz, izz)) + self.entity.setInertiaMatrix(jointName, inertiaMatrix) + # set bounds of degrees of freedom + for index in range(len(bounds)): + bound = bounds[index] + self.entity.setDofBounds(jointName, index, bound[0], bound[1]) + self.attachJointToParent(parentName, jointName) + # recursively create child joints + childJoints = filter(lambda n:n.nodeName in self.jointTypes, + node.childNodes) + for childJoint in childJoints: + self.createJoint(childJoint, jointName) + + def attachJointToParent(self, parentName, jointName): + if parentName == self.robotTag: + self.entity.setRootJoint(jointName) + else: + self.entity.addJoint(parentName, jointName) + + def findRootJoint (self, hNode): + rJoint = filter(lambda n:n.nodeName in self.jointTypes, + hNode.childNodes) + if len(rJoint) == 0: + raise RuntimeError("Robot should have at least one joint.") + if len(rJoint) > 1: + raise RuntimeError("Robot should have exactly one root joint.\n" + + "This one has " + str(len(rJoint)) + ".") + return rJoint[0] + + def findJointBounds(self, node, jointName): + dofList = filter(lambda n: n.nodeName == 'DOF', node.childNodes) + bounds = [] + for dof in dofList: + dofName = self.findStringProperty(dof, 'NAME') + minValue = -1e-6 + maxValue = 1e-6 + try: + minValue = self.findFloatProperty(dof, 'DOF_MIN_VALUE') + except RunTimeError: + print ("min value of dof %s of joint %s is not specified." % + (dofName, jointName)) + print ("Set to -1e-6") + try: + maxValue = self.findFloatProperty(dof, 'DOF_MAX_VALUE') + except RunTimeError: + print ("max value of dof %s of joint %s is not specified." % + (dofName, jointName)) + print ("Set to 1e-6") + + bounds.append((minValue, maxValue)) + return bounds + + def findStringProperty (self, node, prop): + return self.findProperty(node, prop, str) + + def findFloatProperty (self, node, prop): + return self.findProperty(node, prop, float) + + def findIntProperty (self, node, prop): + return self.findProperty(node, prop, int) + + def findProperty(self, node, prop, cast): + properties = filter(lambda n: n.nodeName == 'PROPERTY', node.childNodes) + theProperty = filter (lambda p: p._attrs['stringId'].nodeValue == prop, + properties) + if len(theProperty) != 1: + raise RuntimeError(prop + + ' should be specified once and only once.') + theProperty = theProperty[0] + value = filter(lambda n:n.nodeType == n.TEXT_NODE, + theProperty.childNodes) + if len(value) != 1: + raise RuntimeError('One and only one name should be specified for ' + + prop) + value = value[0] + return cast(value.data) + + def findJointPosition(self, node): + tag = 'CURRENT_POSITION' + posNode = filter(lambda n: n.nodeName == tag, + node.childNodes) + if len(posNode) == 0: + print ("Position of joint not specified: tag " + tag + ",") + print ("Setting to identity") + return ((1.,0.,0.,0.),(0.,1.,0.,0.),(0.,0.,1.,0.),(0.,0.,0.,1.)) + + if len(posNode) > 1: + raise RuntimeError("'CURRENT_POSITION' specified more than once") + + posNode = posNode[0] + if len(posNode.childNodes) != 1 and (posNode.childNodes[0].typeNode == + posNode.TEXT_NODE): + raise RunTimeError("Position matrix ill defined") + + # Remove spurious characters at beginning and end of matrix string + data = posNode.childNodes[0].data.strip('\n\t ') + # Split by lines and remove spurious characters at begginning and end of + # each line. + lines = map (lambda l: l.strip('\t '), data.split('\n')) + # Split each line between spaces + matrix = map (lambda l: l.split(' '), lines) + # cast each matrix element into float + matrix = map (lambda l: map (float, l), matrix) + # transform list into tuple + return tuple (map (tuple, matrix)) + + def handSymmetry(self, vector): + """ + Conversion of local coordinates from left hand to right hand + + Input: + - a vector: locally expressed in the local frame of left wrist, + Return: + - a vector: locally expressed in the local frame of the right wrist. + + The conversion is done in such a way that input and output are + symmetric with respect to plane (xz) in global frame. + """ + # input vector expressed in global frame + vector = R3(vector) + globalLeftVector = SE3(self.leftWristPosition) * vector + globalRightVector = R3(globalLeftVector) + globalRightVector = R3(globalRightVector[0], + -globalRightVector[1], + globalRightVector[2]) + output = SE3(self.rightWristPosition).inverse()*globalRightVector + return tuple(output) +