00001 import xml.dom.minidom
00002 from xml.dom.minidom import Document
00003
00004 def add(doc, base, element):
00005 if element is not None:
00006 base.appendChild( element.to_xml(doc) )
00007
00008 def pfloat(x):
00009 return ("%.12f"%x).rstrip('0').rstrip('.')
00010
00011 def set_attribute(node, name, value):
00012 if value is None:
00013 return
00014 if type([]) == type(value) or type(()) == type(value):
00015 value = " ".join( [pfloat(a) for a in value] )
00016 elif type(value) == type(0.0):
00017 value = pfloat(value)
00018 elif type(value) != type(''):
00019 value = str(value)
00020 node.setAttribute(name, value)
00021
00022 def short(doc, name, key, value):
00023 element = doc.createElement(name)
00024 set_attribute(element, key, value)
00025 return element
00026
00027 def children(node):
00028 children = []
00029 for child in node.childNodes:
00030 if child.nodeType is node.TEXT_NODE or child.nodeType is node.COMMENT_NODE:
00031 continue
00032 else:
00033 children.append(child)
00034 return children
00035
00036 class Text:
00037 def __init__(self, name, value=""):
00038 self.name = name
00039 self.value = value
00040
00041 @staticmethod
00042 def parse(node):
00043 t = Text(node.localName, node.childNodes[0].nodeValue)
00044 return t
00045
00046 def to_xml(self, doc):
00047 xml = doc.createElement(self.name)
00048 xml.appendChild( doc.createTextNode(str(self.value)) )
00049
00050 class Collision:
00051 def __init__(self, geometry=None, origin=None):
00052 self.geometry = geometry
00053 self.origin = origin
00054
00055 @staticmethod
00056 def parse(node):
00057 c = Collision()
00058 for child in children(node):
00059 if child.localName == 'geometry':
00060 c.geometry = Geometry.parse(child)
00061 elif child.localName == 'origin':
00062 c.origin = Pose.parse(child)
00063 elif child.localName == 'material':
00064 c.material = Material.parse(child)
00065 elif child.localName == 'verbose':
00066 pass
00067 else:
00068 raise Exception("Unknown collision element '%s'"%child.localName)
00069 return c
00070
00071 def to_xml(self, doc):
00072 xml = doc.createElement("collision")
00073 add(doc, xml, self.origin)
00074 add(doc, xml, self.geometry)
00075 return xml
00076
00077 class Color:
00078 def __init__(self, r=0.0, g=0.0, b=0.0, a=0.0):
00079 self.rgba=(r,g,b,a)
00080
00081 @staticmethod
00082 def parse(node):
00083 rgba = node.getAttribute("rgba").split()
00084 (r,g,b,a) = [ float(x) for x in rgba ]
00085 return Color(r,g,b,a)
00086
00087 def to_xml(self, doc):
00088 xml = doc.createElement("color")
00089 set_attribute(xml, "rgba", self.rgba)
00090 return xml
00091
00092 class Dynamics:
00093 def __init__(self, damping=None, friction=None):
00094 self.damping = damping
00095 self.friction = friction
00096
00097 @staticmethod
00098 def parse(node):
00099 d = Dynamics()
00100 if node.hasAttribute('damping'):
00101 d.damping = node.getAttribute('damping')
00102 if node.hasAttribute('friction'):
00103 d.friction = node.getAttribute('friction')
00104 return d
00105
00106 def to_xml(self, doc):
00107 xml = doc.createElement('dynamics')
00108 set_attribute(xml, 'damping', self.damping)
00109 set_attribute(xml, 'friction', self.friction)
00110 return xml
00111
00112 class Geometry:
00113 def __init__(self):
00114 None
00115
00116 @staticmethod
00117 def parse(node):
00118 shape = children(node)[0]
00119 if shape.localName=='box':
00120 return Box.parse(shape)
00121 elif shape.localName=='cylinder':
00122 return Cylinder.parse(shape)
00123 elif shape.localName=='sphere':
00124 return Sphere.parse(shape)
00125 elif shape.localName=='mesh':
00126 return Mesh.parse(shape)
00127 else:
00128 raise Exception("Unknown shape %s"%child.localName)
00129
00130 class Box(Geometry):
00131 def __init__(self, dims=None):
00132 if dims is None:
00133 self.dims = None
00134 else:
00135 self.dims = (dims[0], dims[1], dims[2])
00136
00137 @staticmethod
00138 def parse(node):
00139 dims = node.getAttribute('size').split()
00140 return Box([float(a) for a in dims])
00141
00142 def to_xml(self, doc):
00143 xml = doc.createElement("box")
00144 set_attribute(xml, "size", self.dims)
00145 geom = doc.createElement('geometry')
00146 geom.appendChild(xml)
00147 return geom
00148
00149 class Cylinder(Geometry):
00150 def __init__(self, radius=0.0, length=0.0):
00151 self.radius = radius
00152 self.length = length
00153
00154 @staticmethod
00155 def parse(node):
00156 r = node.getAttribute('radius')
00157 l = node.getAttribute('length')
00158 return Cylinder(float(r), float(l))
00159
00160 def to_xml(self, doc):
00161 xml = doc.createElement("cylinder")
00162 set_attribute(xml, "radius", self.radius)
00163 set_attribute(xml, "length", self.length)
00164 geom = doc.createElement('geometry')
00165 geom.appendChild(xml)
00166 return geom
00167
00168 class Sphere(Geometry):
00169 def __init__(self, radius=0.0):
00170 self.radius = radius
00171
00172 @staticmethod
00173 def parse(node):
00174 r = node.getAttribute('radius')
00175 return Sphere(float(r))
00176
00177 def to_xml(self, doc):
00178 xml = doc.createElement("sphere")
00179 set_attribute(xml, "radius", self.radius)
00180 geom = doc.createElement('geometry')
00181 geom.appendChild(xml)
00182 return geom
00183
00184 class Mesh(Geometry):
00185 def __init__(self, filename=None, scale=None):
00186 self.filename = filename
00187 self.scale = scale
00188
00189 @staticmethod
00190 def parse(node):
00191 fn = node.getAttribute('filename')
00192 s = node.getAttribute('scale')
00193 if s == "":
00194 s = None
00195 else:
00196 xyz = node.getAttribute('scale').split()
00197 scale = map(float, xyz)
00198 return Mesh(fn, s)
00199
00200 def to_xml(self, doc):
00201 xml = doc.createElement("mesh")
00202 set_attribute(xml, "filename", self.filename)
00203 set_attribute(xml, "scale", self.scale)
00204 geom = doc.createElement('geometry')
00205 geom.appendChild(xml)
00206 return geom
00207
00208 class Inertial:
00209 def __init__(self, ixx=0.0, ixy=0.0, ixz=0.0, iyy=0.0, iyz=0.0, izz=0.0, mass=0.0, origin=None):
00210 self.matrix = {}
00211 self.matrix['ixx'] = ixx
00212 self.matrix['ixy'] = iyy
00213 self.matrix['ixz'] = ixz
00214 self.matrix['iyy'] = iyy
00215 self.matrix['iyz'] = iyz
00216 self.matrix['izz'] = izz
00217 self.mass = mass
00218 self.origin = origin
00219
00220 @staticmethod
00221 def parse(node):
00222 inert = Inertial()
00223 for child in children(node):
00224 if child.localName=='inertia':
00225 for v in ['ixx', 'ixy', 'ixz', 'iyy', 'iyz', 'izz']:
00226 inert.matrix[v] = float(child.getAttribute(v))
00227 elif child.localName=='mass':
00228 inert.mass = float(child.getAttribute('value'))
00229 elif child.localName == 'origin':
00230 inert.origin = Pose.parse(child)
00231 return inert
00232
00233 def to_xml(self, doc):
00234 xml = doc.createElement("inertial")
00235
00236 xml.appendChild(short(doc, "mass", "value", self.mass))
00237 add(doc, xml, self.origin)
00238
00239 inertia = doc.createElement("inertia")
00240 for (n,v) in self.matrix.items():
00241 set_attribute(inertia, n, v)
00242 xml.appendChild(inertia)
00243 return xml
00244
00245 class Joint:
00246 def __init__(self, name, parent, child, joint_type, axis=None, origin=None, limits=None,
00247 dynamics=None, safety=None, calibration=None, mimic=None):
00248 self.name = name
00249 self.parent = parent
00250 self.child = child
00251 self.joint_type = joint_type
00252 self.axis = axis
00253 self.origin = origin
00254 self.limits = limits
00255 self.dynamics = dynamics
00256 self.safety = safety
00257 self.calibration = calibration
00258 self.mimic = mimic
00259
00260 @staticmethod
00261 def parse(node):
00262 joint = Joint(node.getAttribute('name'), None, None, node.getAttribute('type'))
00263 for child in children(node):
00264 if child.localName == 'parent':
00265 joint.parent = child.getAttribute('link')
00266 elif child.localName == 'child':
00267 joint.child = child.getAttribute('link')
00268 elif child.localName == 'axis':
00269 joint.axis = child.getAttribute('xyz')
00270 elif child.localName == 'origin':
00271 joint.origin = Pose.parse(child)
00272 elif child.localName == 'limit':
00273 joint.limits = JointLimit.parse(child)
00274 elif child.localName == 'dynamics':
00275 joint.dynamics = Dynamics.parse(child)
00276 elif child.localName == 'safety_controller':
00277 joint.safety = SafetyController.parse(child)
00278 elif child.localName == 'calibration':
00279 joint.calibration = JointCalibration.parse(child)
00280 elif child.localName == 'mimic':
00281 joint.mimic = JointMimic.parse(child)
00282 else:
00283 raise Exception("Unknown joint element '%s'"%child.localName)
00284 return joint
00285
00286 def to_xml(self, doc):
00287 xml = doc.createElement("joint")
00288 set_attribute(xml, "name", self.name)
00289 set_attribute(xml, "type", self.joint_type)
00290 xml.appendChild( short(doc, "parent", "link", self.parent) )
00291 xml.appendChild( short(doc, "child" , "link", self.child ) )
00292 add(doc, xml, self.origin)
00293 if self.axis is not None:
00294 xml.appendChild( short(doc, "axis", "xyz", self.axis) )
00295 add(doc, xml, self.limits)
00296 add(doc, xml, self.dynamics)
00297 add(doc, xml, self.safety)
00298 add(doc, xml, self.calibration)
00299 return xml
00300
00301 class JointCalibration:
00302 def __init__(self, rising=None, falling=None):
00303 self.rising = rising
00304 self.falling = falling
00305
00306 @staticmethod
00307 def parse(node):
00308 jc = JointCalibration()
00309 if node.hasAttribute('rising'):
00310 jc.rising = float( node.getAttribute('rising') )
00311 if node.hasAttribute('falling'):
00312 jc.falling = float( node.getAttribute('falling') )
00313 return jc
00314
00315 def to_xml(self, doc):
00316 xml = doc.createElement('calibration')
00317 set_attribute(xml, 'rising', self.rising)
00318 set_attribute(xml, 'falling', self.falling)
00319 return xml
00320
00321 class JointLimit:
00322 def __init__(self, effort, velocity, lower=None, upper=None):
00323 self.effort = effort
00324 self.velocity = velocity
00325 self.lower = lower
00326 self.upper = upper
00327
00328 @staticmethod
00329 def parse(node):
00330 jl = JointLimit( float( node.getAttribute('effort') ) ,
00331 float( node.getAttribute('velocity')))
00332 if node.hasAttribute('lower'):
00333 jl.lower = float( node.getAttribute('lower') )
00334 if node.hasAttribute('upper'):
00335 jl.upper = float( node.getAttribute('upper') )
00336 return jl
00337
00338 def to_xml(self, doc):
00339 xml = doc.createElement('limit')
00340 set_attribute(xml, 'effort', self.effort)
00341 set_attribute(xml, 'velocity', self.velocity)
00342 set_attribute(xml, 'lower', self.lower)
00343 set_attribute(xml, 'upper', self.upper)
00344 return xml
00345
00346 def __str__(self):
00347 if self.lower is not None:
00348 return "[%f, %f]"%(self.lower, self.upper)
00349 else:
00350 return "limit"
00351
00352 class JointMimic:
00353 def __init__(self, joint_name, multiplier=None, offset=None):
00354 self.joint_name = joint_name
00355 self.multiplier = multiplier
00356 self.offset = offset
00357
00358 @staticmethod
00359 def parse(node):
00360 mimic = JointMimic( node.getAttribute('joint_name') )
00361 if node.hasAttribute('multiplier'):
00362 mimic.multiplier = float( node.getAttribute('multiplier') )
00363 if node.hasAttribute('offset'):
00364 mimic.offset = float( node.getAttribute('offset') )
00365 return mimic
00366
00367 def to_xml(self, doc):
00368 xml = doc.createElement('mimic')
00369 set_attribute(xml, 'joint_name', self.joint_name)
00370 set_attribute(xml, 'multiplier', self.multiplier)
00371 set_attribute(xml, 'offset', self.offset)
00372 return xml
00373
00374 class Link:
00375 def __init__(self, name, visual=None, inertial=None, collision=None):
00376 self.name = name
00377 self.type = None
00378 self.visual = visual
00379 self.inertial=inertial
00380 self.collision=collision
00381
00382 @staticmethod
00383 def parse(node):
00384 link = Link(node.getAttribute('name'))
00385 link.type = node.getAttribute('type')
00386 for child in children(node):
00387 if child.localName == 'visual':
00388 link.visual = Visual.parse(child)
00389 elif child.localName == 'collision':
00390 link.collision = Collision.parse(child)
00391 elif child.localName == 'inertial':
00392 link.inertial = Inertial.parse(child)
00393 else:
00394 raise Exception("Unknown link element '%s'"%child.localName)
00395 return link
00396
00397 def to_xml(self, doc):
00398 xml = doc.createElement("link")
00399 xml.setAttribute("name", self.name)
00400 if self.type != "":
00401 xml.setAttribute("type", self.type)
00402 add( doc, xml, self.inertial)
00403 add( doc, xml, self.visual)
00404 add( doc, xml, self.collision)
00405 return xml
00406
00407 class Material:
00408 def __init__(self, name=None, color=None, texture=None):
00409 self.name = name
00410 self.color = color
00411 self.texture = texture
00412
00413 @staticmethod
00414 def parse(node):
00415 material = Material()
00416 if node.hasAttribute('name'):
00417 material.name = node.getAttribute('name')
00418 for child in children(node):
00419 if child.localName == 'color':
00420 material.color = Color.parse(child)
00421 elif child.localName == 'texture':
00422 material.texture = child.getAttribute('filename')
00423 else:
00424 raise Exception("Unknown material element '%s'"%child.localName)
00425
00426 return material
00427
00428 def to_xml(self, doc):
00429 xml = doc.createElement("material")
00430 set_attribute(xml, "name", self.name)
00431 add( doc, xml, self.color )
00432
00433 if self.texture is not None:
00434 text = doc.createElement("texture")
00435 text.setAttribute('filename', self.texture)
00436 xml.appendChild(text)
00437 return xml
00438
00439 class Pose:
00440 def __init__(self, position=None, rotation=None):
00441 self.position = position
00442 self.rotation = rotation
00443
00444 @staticmethod
00445 def parse(node):
00446 pose = Pose()
00447 if node.hasAttribute("xyz"):
00448 xyz = node.getAttribute('xyz').split()
00449 pose.position = map(float, xyz)
00450 if node.hasAttribute("rpy"):
00451 rpy = node.getAttribute('rpy').split()
00452 pose.rotation = map(float, rpy)
00453 return pose
00454
00455 def to_xml(self, doc):
00456 xml = doc.createElement("origin")
00457 set_attribute(xml, 'xyz', self.position)
00458 set_attribute(xml, 'rpy', self.rotation)
00459 return xml
00460
00461 class SafetyController:
00462 def __init__(self, velocity, position=None, lower=None, upper=None):
00463 self.velocity = velocity
00464 self.position = position
00465 self.lower = lower
00466 self.upper = upper
00467
00468 @staticmethod
00469 def parse(node):
00470 sc = SafetyController( float(node.getAttribute('k_velocity')) )
00471 if node.hasAttribute('soft_lower_limit'):
00472 sc.lower = float( node.getAttribute('soft_lower_limit') )
00473 if node.hasAttribute('soft_upper_limit'):
00474 sc.upper = float( node.getAttribute('soft_upper_limit') )
00475 if node.hasAttribute('k_position'):
00476 sc.position = float( node.getAttribute('k_position') )
00477 return sc
00478
00479 def to_xml(self, doc):
00480 xml = doc.createElement('safety_controller')
00481 set_attribute(xml, 'k_velocity', self.velocity)
00482 set_attribute(xml, 'k_position', self.position)
00483 set_attribute(xml, 'soft_upper_limit', self.upper)
00484 set_attribute(xml, 'soft_lower_limit', self.lower)
00485 return xml
00486
00487
00488 class Visual:
00489 def __init__(self, geometry=None, material=None, origin=None):
00490 self.geometry = geometry
00491 self.material = material
00492 self.origin = origin
00493
00494 @staticmethod
00495 def parse(node):
00496 v = Visual()
00497 for child in children(node):
00498 if child.localName == 'geometry':
00499 v.geometry = Geometry.parse(child)
00500 elif child.localName == 'origin':
00501 v.origin = Pose.parse(child)
00502 elif child.localName == 'material':
00503 v.material = Material.parse(child)
00504 else:
00505 raise Exception("Unknown visual element '%s'"%child.localName)
00506 return v
00507
00508 def to_xml(self, doc):
00509 xml = doc.createElement("visual")
00510 add( doc, xml, self.origin )
00511 add( doc, xml, self.geometry )
00512 add( doc, xml, self.material )
00513 return xml
00514
00515 class Transmission:
00516 def __init__(self, name):
00517 self.name = name
00518 self.type = None
00519 self.actuator = None
00520 self.joint = None
00521 self.reduction = None
00522 self.children = list()
00523
00524 @staticmethod
00525 def parse(node):
00526 t = Transmission(node.getAttribute('name'))
00527 t.type = node.getAttribute('type')
00528 for child in children(node):
00529 if child.localName == 'actuator':
00530 t.actuator = child.getAttribute("name")
00531 elif child.localName == 'joint':
00532 t.joint = child.getAttribute("name")
00533 elif child.localName == 'mechanicalReduction':
00534 t.reduction = float(child.childNodes[0].nodeValue)
00535 else:
00536 t.children.append(child)
00537
00538 return t
00539
00540 def to_xml(self, doc):
00541 xml = doc.createElement("transmission")
00542 set_attribute(xml, "name", self.name)
00543 if self.type is not None:
00544 set_attribute(xml, "type", self.type)
00545
00546 if self.actuator is not None:
00547 actuator = doc.createElement("actuator")
00548 actuator.setAttribute('name', self.actuator)
00549 xml.appendChild(actuator)
00550 if self.joint is not None:
00551 joint = doc.createElement("joint")
00552 joint.setAttribute('name', self.joint)
00553 xml.appendChild(joint)
00554 if self.reduction is not None:
00555 reduction = doc.createElement("mechanicalReduction")
00556 reduction.appendChild( doc.createTextNode(pfloat(self.reduction)) )
00557 xml.appendChild(reduction)
00558 for child in self.children:
00559 xml.appendChild(child)
00560 return xml
00561
00562 class Gazebo:
00563 def __init__(self):
00564 self.attributes = dict()
00565 self.children = list()
00566
00567 @staticmethod
00568 def parse(node):
00569 g = Gazebo()
00570 g.name = node.nodeName
00571 for name, attr in node._attrs.items():
00572 g.attributes[name] = attr.value
00573 for child in children(node):
00574 if child.nodeName.find("sensor") == 0 or child.nodeName.find("controller") == 0 or \
00575 child.nodeName.find("body") == 0 or child.nodeName.find("joint") == 0:
00576 g.children.append( Gazebo.parse(child) )
00577 else:
00578 g.children.append(child)
00579 return g
00580
00581 def to_xml(self, doc):
00582 xml = doc.createElement(self.name)
00583 for key, value in self.attributes.items():
00584 set_attribute(xml, key, value)
00585 for child in self.children:
00586 if "to_xml" in dir(child):
00587 xml.appendChild(child.to_xml(doc))
00588 else:
00589 xml.appendChild(child)
00590 return xml
00591
00592 class URDF:
00593 def __init__(self, name=""):
00594 self.name = name
00595 self.elements = []
00596 self.links = {}
00597 self.joints = {}
00598 self.materials = {}
00599 self.transmissions = {}
00600
00601 self.parent_map = {}
00602 self.child_map = {}
00603
00604 def parse(self, xml_string):
00605 base = xml.dom.minidom.parseString(xml_string)
00606 robot = children(base)[0]
00607 self.name = robot.getAttribute('name')
00608 self.robot_attributes = robot.attributes
00609 for node in children(robot):
00610 if node.nodeType is node.TEXT_NODE:
00611 print node.value
00612 if node.localName == 'joint':
00613 self.add_joint( Joint.parse(node) )
00614 elif node.localName == 'link':
00615 self.add_link( Link.parse(node) )
00616 elif node.localName == 'material':
00617 self.elements.append( Material.parse(node) )
00618 elif node.localName == 'gazebo':
00619 self.elements.append( Gazebo.parse(node) )
00620 elif node.localName == 'transmission':
00621 self.add_transmission( Transmission.parse(node) )
00622 else:
00623 self.elements.append(node)
00624 print "Unknown robot element '%s'"%node.localName
00625
00626 return self
00627
00628 def load(self, filename):
00629 f = open(filename, 'r')
00630 return self.parse(f.read())
00631
00632 def add_link(self, link):
00633 self.elements.append(link)
00634 self.links[link.name] = link
00635
00636 def add_joint(self, joint):
00637 self.elements.append(joint)
00638 self.joints[joint.name] = joint
00639
00640 self.parent_map[ joint.child ] = (joint.name, joint.parent)
00641 if joint.parent in self.child_map:
00642 self.child_map[joint.parent].append( (joint.name, joint.child) )
00643 else:
00644 self.child_map[joint.parent] = [ (joint.name, joint.child) ]
00645
00646 def add_transmission(self, transmission):
00647 self.elements.append(transmission)
00648 self.transmissions[transmission.name] = transmission
00649
00650 def get_chain(self, root, tip, joints=True, links=True):
00651 chain = []
00652 if links:
00653 chain.append(tip)
00654 link = tip
00655 while link != root:
00656 (joint, parent) = self.parent_map[link]
00657 if joints:
00658 chain.append(joint)
00659 if links:
00660 chain.append(parent)
00661 link = parent
00662 chain.reverse()
00663 return chain
00664
00665 def to_xml(self):
00666 doc = Document()
00667 root = doc.createElement("robot")
00668 doc.appendChild(root)
00669 for i in range(self.robot_attributes.length):
00670 root.setAttribute(self.robot_attributes.item(i).name, self.robot_attributes.item(i).value)
00671 for element in self.elements:
00672 try:
00673 root.appendChild(element.to_xml(doc))
00674 except:
00675 root.appendChild(element)
00676
00677 return doc.toprettyxml()
00678
00679 def fixed_writexml(self, writer, indent="", addindent="", newl=""):
00680 addindent = " "
00681
00682
00683
00684 writer.write(indent+"<" + self.tagName)
00685
00686 attrs = self._get_attributes()
00687 a_names = attrs.keys()
00688 a_names.sort()
00689
00690 for a_name in a_names:
00691 writer.write(" %s=\"" % a_name)
00692 xml.dom.minidom._write_data(writer, attrs[a_name].value)
00693 writer.write("\"")
00694 if self.childNodes:
00695 if len(self.childNodes) == 1 \
00696 and self.childNodes[0].nodeType == xml.dom.minidom.Node.TEXT_NODE:
00697 writer.write(">")
00698 self.childNodes[0].writexml(writer, "", "", "")
00699 writer.write("</%s>%s" % (self.tagName, newl))
00700 return
00701 writer.write(">%s"%(newl))
00702 for node in self.childNodes:
00703 node.writexml(writer,indent+addindent,addindent,newl)
00704 writer.write("%s</%s>%s" % (indent,self.tagName,newl))
00705 else:
00706 writer.write("/>%s"%(newl))
00707
00708
00709 xml.dom.minidom.Element.writexml = fixed_writexml
00710