21 from xml.dom.minidom
import Document
22 from xml.dom
import minidom
24 from numpy
import array, pi
28 ZERO_THRESHOLD = 0.000000001
32 """Reindent a string for tree structure pretty printing.""" 33 s = string.split(s,
'\n')
34 s = [(numSpaces *
' ') + line
for line
in s]
35 s = string.join(s,
'\n')
39 def add(doc, base, element):
40 """Add an XML element for URDF export""" 41 if element
is not None:
42 base.appendChild(element.to_xml(doc))
46 """Add an XML element for OpenRAVE XML export""" 47 if element
is not None:
49 newelements = element.to_openrave_xml(doc)
50 if hasattr(newelements,
'__iter__'):
54 base.appendChild(newelements)
58 """Print float value as string""" 59 return "{0}".format(x).rstrip(
'.')
63 """Convert data fromvarious types to urdf string format""" 66 if hasattr(data,
'__iter__'):
70 if abs(a) > ZERO_THRESHOLD:
76 return ' '.join(outlist)
79 return pfloat(data
if abs(data) > ZERO_THRESHOLD
else 0)
80 elif not isinstance(data, str):
86 """Set an attribute on an XML node, converting data to string format""" 91 """Create a text node and add it to the current element""" 94 node.appendChild(doc.createTextNode(
to_string(data)))
97 def short(doc, name, key, value):
98 element = doc.createElement(name)
104 element = doc.createElement(name)
105 if contents
is not None:
114 doc.appendChild(
create_element(doc, name, contents=
None, key=
None,
120 for child
in node.childNodes:
121 if child.nodeType
is node.TEXT_NODE \
122 or child.nodeType
is node.COMMENT_NODE:
125 children.append(child)
135 Represent any type of gazebo tag: 136 - global gazebo tag inside a robot tag 137 - gazebo tags referencing links 138 - gazebo tags instantiating plugins and sensors 140 ATTRIBUTE_NAMES = [
'material',
'turnGravityOff',
'dampingFactor',
141 'maxVel',
'minDepth',
'mu1',
'mu2',
'fdir1',
'kp',
142 'kd',
'selfCollide',
'maxContacts',
'laserRetro']
144 def __init__(self, reference=None, material=None, gravity=None,
145 dampingFactor=
None, maxVel=
None, minDepth=
None, mu1=
None,
146 mu2=
None, fdir1=
None, kp=
None, kd=
None, selfCollide=
None,
147 maxContacts=
None, laserRetro=
None, plugin=[], sensor=[]):
170 if node.hasAttribute(
'reference'):
171 gaze.reference = node.getAttribute(
'reference')
173 for attribute
in gaze.ATTRIBUTE_NAMES:
174 if child.localName == attribute:
175 if attribute ==
'sensor':
176 gaze.sensors.append(Sensor.parse(child, verbose))
178 setattr(gaze, attribute,
179 str(child.childNodes[0].nodeValue))
182 if child.localName ==
'plugin':
183 gaze.plugins.append(BasePlugin.parse(child, verbose))
187 xml = doc.createElement(
'gazebo')
191 value = getattr(self, attribute)
192 if value
is not None:
193 xml.appendChild(createElement(doc, attribute, value))
197 add(doc, xml, sensor)
201 add(doc, xml, plugin)
208 s +=
'Reference: {0}\n'.format(self.
reference)
210 value = getattr(self, name)
211 if value
is not None:
212 s += name.title() +
':\n' 216 s +=
reindent(str(sensor), 1) +
'\n' 220 s +=
reindent(str(plugin), 1) +
'\n' 229 Abstract class used to parse plugin tags and instantiating 230 inherited plugin accordingly 235 def __init__(self, name=None, filename=None, attributes=None):
243 if child.localName ==
'cameraName':
244 plug = CameraPlugin(node.getAttribute(
'name'),
245 node.getAttribute(
'filename'))
247 elif child.localName ==
'bumperTopicName':
249 node.getAttribute(
'filename'))
251 elif child.localName ==
'leftFrontJoint':
252 plug = DrivePlugin(node.getAttribute(
'name'),
253 node.getAttribute(
'filename'))
255 elif child.localName ==
'odometryTopic':
257 node.getAttribute(
'filename'))
259 elif child.localName ==
'height':
261 node.getAttribute(
'filename'))
263 elif child.localName ==
'mimicJoint':
265 node.getAttribute(
'filename'))
267 elif child.localName ==
'robotSimType':
269 node.getAttribute(
'filename'))
274 setattr(plug, child.localName,
275 str(child.childNodes[0].nodeValue))
279 xml = doc.createElement(
'plugin')
280 if self.
name is not None:
285 value = getattr(self, attribute)
286 if value
is not None:
291 if self.
name is not None:
292 s =
'Name: {0}\n'.format(self.
name)
294 s +=
'Filename: {0}\n'.format(self.
filename)
296 value = getattr(self, attribute)
297 if value
is not None:
298 s += name.title() +
":\n" 304 Define the simulation plugin used to initialize simulation parameters in 309 ATTRIBUTE_NAMES = [
'robotNamespace',
'robotSimType']
311 def __init__(self, name=None, filename=None, robot_namespace=None,
312 robot_sim_type=
None):
313 super(SimuPlugin, self).
__init__(name, filename,
314 {
'robotNamespace': robot_namespace,
315 'robotSimType': robot_sim_type})
321 Define the plugin used to define mimic joints 323 (joint not commanded directly by an actuator 324 but depending on another joint position) 331 ATTRIBUTE_NAMES = [
'joint',
'mimicJoint',
'multiplier',
'offset']
333 def __init__(self, name=None, filename=None, joint=None, mimic_joint=None,
334 multiplier=
None, offset=
None):
335 super(MimicJointPlugin, self).
__init__(name, filename,
337 'mimicJoint': mimic_joint,
338 'multiplier': multiplier,
345 Class defining the plugin used to simulate bumpers on a robot 352 ATTRIBUTE_NAMES = [
'bumperTopicName',
'frameName',
'alwaysOn',
355 def __init__(self, name=None, filename=None, bumper_topic_name=None,
356 frame_name=
None, always_on=
None, update_rate=
None):
357 super(BumperPlugin, self).
__init__(name, filename,
358 {
'bumperTopicName': bumper_topic_name,
359 'frameName': frame_name,
360 'alwaysOn': always_on,
361 'updateRate': update_rate})
366 Class defining the plugin used to IMU on a robot 367 note: gazebo official IMU plugin has some weird behaviour 385 ATTRIBUTE_NAMES = [
'robotNamespace',
'topicName',
'gaussianNoise',
386 'xyzOffset',
'rpyOffset',
'alwaysOn',
'updateRate',
387 'frameName',
'hokuyoMinIntensity',
'frameId',
388 'accelGaussianNoise',
'headingGaussianNoise',
389 'rateGaussianNoise',
'bodyName']
391 def __init__(self, name=None, filename=None, robot_namespace=None,
392 topic_name=
None, gaussian_noise=
None, xyz_offset=
None,
393 rpy_offset=
None, always_on=
None, update_rate=
None,
394 hokuyo_min_intensity=
None, frame_id=
None,
395 accel_gaussian_noise=
None, heading_gaussian_noise=
None,
396 rate_gaussian_noise=
None, body_name=
None):
397 super(ImuLaserPlugin, self).
__init__(name, filename,
398 {
'robotNamespace': robot_namespace,
399 'topicName': topic_name,
400 'gaussianNoise': gaussian_noise,
401 'xyzOffset': xyz_offset,
402 'rpyOffset': rpy_offset,
403 'alwaysOn': always_on,
404 'updateRate': update_rate,
405 'hokuyoMinIntensity': hokuyo_min_intensity,
407 'accelGaussianNoise': accel_gaussian_noise,
408 'headingGaussianNoise': heading_gaussian_noise,
409 'rateGaussianNoise': rate_gaussian_noise,
410 'bodyName': body_name})
415 Class defining the plugin used to simulate video on a robot 423 ATTRIBUTE_NAMES = [
'topicName',
'height',
'width',
'alwaysOn',
426 def __init__(self, name=None, filename=None, topic_name=None, height=None,
427 width=
None, always_on=
None, update_rate=
None):
428 super(VideoPlugin, self).
__init__(name, filename,
429 {
'topicName': topic_name,
432 'alwaysOn': always_on,
433 'updateRate': update_rate})
438 Class defining the plugin used to odometry on a robot 448 ATTRIBUTE_NAMES = [
'commandTopic',
'odometryTopic',
'odometryFrame',
449 'odometryRate',
'robotBaseFrame',
'alwaysOn',
452 def __init__(self, name=None, filename=None, command_topic=None,
453 odometry_topic=
None, odometry_frame=
None, odometry_rate=
None,
454 robot_base_frame=
None, always_on=
None, update_rate=
None):
455 super(OdometryPlugin, self).
__init__(name, filename,
456 {
'commandTopic': command_topic,
457 'odometryTopic': odometry_topic,
458 'odometryFrame': odometry_frame,
459 'odometryRate': odometry_rate,
460 'robotBaseFrame': robot_base_frame,
461 'alwaysOn': always_on,
462 'updateRate': update_rate})
471 Class defining the sensors and the related plugins 483 def __init__(self, name=None, type=None, update_rate=None, camera=[],
484 plugin=[], pose=
None, visualize=
None, ray=
None):
496 sens =
Sensor(node.getAttribute(
'name'), node.getAttribute(
'type'))
500 if child.localName ==
'ray':
501 sens.ray = Ray.parse(child, verbose)
502 if child.localName ==
'update_rate':
503 sens.update_rate = str(child.childNodes[0].nodeValue)
504 if child.localName ==
'camera':
505 sens.cameras.append(CameraSensor.parse(child, verbose))
506 if child.localName ==
'plugin':
507 sens.plugins.append(BasePlugin.parse(child, verbose))
508 if child.localName ==
'pose':
509 sens.pose = array([float(x)
for x
in 510 child.childNodes[0].nodeValue.split(
' ')])
511 if child.localName ==
'visualize':
512 sens.visualize = str(child.childNodes[0].nodeValue)
516 xml = doc.createElement(
'sensor')
523 add(doc, xml, camera)
525 add(doc, xml, plugin)
526 if self.
pose is not None:
531 if self.
ray is not None:
537 s +=
"Name: {0}\n".format(self.
name)
538 s +=
"Type: {0}\n".format(self.
type)
540 s +=
"Update_rate:\n" 544 s +=
reindent(str(camera), 1) +
"\n" 547 s +=
reindent(str(plugin), 1) +
"\n" 553 if self.
ray is not None:
561 Class defining the camera sensor. 563 This can instantiate specific plugin according to the type of camera 564 (RGB,Stereo,Depth...) camera 570 def __init__(self, name=None, horizontal_fov=None, image=None, clip=None,
581 if node.hasAttribute(
'name'):
582 sens.name = node.getAttribute(
'name')
584 if child.localName ==
'horizontal_fov':
585 sens.horizontal_fov = str(child.childNodes[0].nodeValue)
586 elif child.localName ==
'image':
587 sens.image = Image.parse(child, verbose)
588 elif child.localName ==
'clip':
589 sens.clip = Clip.parse(child, verbose)
590 elif child.localName ==
'noise':
591 sens.noise = Noise.parse(child, verbose)
595 xml = doc.createElement(
'camera')
596 if self.
name is not None:
601 if self.
image is not None:
603 if self.
clip is not None:
605 if self.
noise is not None:
611 if self.
name is not None:
612 s =
"Name: {0}\n".format(self.
name)
614 s +=
"Horizontal_fov:\n" 616 if self.
image is not None:
619 if self.
clip is not None:
622 if self.
noise is not None:
634 Class defining the clip element used in sensors 636 (for range or beaming sensors) 650 if child.localName ==
'near':
651 clip.near = str(child.childNodes[0].nodeValue)
652 elif child.localName ==
'far':
653 clip.far = str(child.childNodes[0].nodeValue)
657 xml = doc.createElement(
'clip')
658 if self.
near is not None:
660 if self.
far is not None:
666 if self.
near is not None:
669 if self.
far is not None:
677 Class defining the image element used in camera sensor 683 def __init__(self, height=None, width=None, format=None):
692 if child.localName ==
'height':
693 img.height = str(child.childNodes[0].nodeValue)
694 elif child.localName ==
'width':
695 img.width = str(child.childNodes[0].nodeValue)
696 elif child.localName ==
'format':
697 img.format = str(child.childNodes[0].nodeValue)
701 xml = doc.createElement(
'image')
702 if self.
height is not None:
704 if self.
width is not None:
706 if self.
format is not None:
712 if self.
height is not None:
715 if self.
width is not None:
718 if self.
format is not None:
726 Class defining the noise element used in most sensors definition 732 def __init__(self, type=None, mean=None, stddev=None):
741 if child.localName ==
'type':
742 noise.type = str(child.childNodes[0].nodeValue)
743 elif child.localName ==
'mean':
744 noise.mean = str(child.childNodes[0].nodeValue)
745 elif child.localName ==
'stddev':
746 noise.stddev = str(child.childNodes[0].nodeValue)
750 xml = doc.createElement(
'noise')
751 if self.
type is not None:
753 if self.
mean is not None:
755 if self.
stddev is not None:
761 if self.
type is not None:
764 if self.
mean is not None:
767 if self.
stddev is not None:
775 Class defining the ray element used in most range sensors 781 def __init__(self, scan=None, range=None, noise=None):
790 if child.localName ==
'scan':
791 ray.scan = Scan.parse(child, verbose)
793 elif child.localName ==
'range':
794 ray.range = Range.parse(child, verbose)
796 elif child.localName ==
'noise':
797 ray.noise = Noise.parse(child, verbose)
801 xml = doc.createElement(
'ray')
802 if self.
scan is not None:
804 if self.
range is not None:
806 if self.
noise is not None:
812 if self.
scan is not None:
815 if self.
range is not None:
818 if self.
noise is not None:
826 Class defining the range element used in most range sensors (mostly LASERs) 832 def __init__(self, min=None, max=None, resolution=None):
841 if child.localName ==
'min':
842 rang.min = str(child.childNodes[0].nodeValue)
843 elif child.localName ==
'max':
844 rang.max = str(child.childNodes[0].nodeValue)
845 elif child.localName ==
'resolution':
846 rang.resolution = str(child.childNodes[0].nodeValue)
850 xml = doc.createElement(
'range')
851 if self.
min is not None:
853 if self.
max is not None:
861 if self.
min is not None:
864 if self.
max is not None:
875 Class defining the scan element used in most range sensors (mostly LASERs) 886 if child.localName ==
'horizontal':
887 scan.horizontal = Horizontal.parse(child, verbose)
892 xml = doc.createElement(
'scan')
907 Class defining the horizontal element 908 used in most range sensors(mostly LASERs) 915 def __init__(self, samples=None, resolution=None, min_angle=None,
926 if child.localName ==
'samples':
927 hori.samples = str(child.childNodes[0].nodeValue)
928 elif child.localName ==
'resolution':
929 hori.resolution = str(child.childNodes[0].nodeValue)
930 elif child.localName ==
'min_angle':
931 hori.min_angle = str(child.childNodes[0].nodeValue)
933 elif child.localName ==
'max_angle':
934 hori.max_angle = str(child.childNodes[0].nodeValue)
938 xml = doc.createElement(
'horizontal')