28 from __future__
import print_function
39 from xml.dom.minidom
import Document
58 'legs':
'base_footprint',
63 COLLISION_SUFFIX =
'_0.10.stl' 65 parser = argparse.ArgumentParser(usage=
'Load an URDF file')
66 parser.add_argument(
'-i',
'--input', default=
'', help=
'URDF file to load')
67 parser.add_argument(
'-r',
'--REP120', choices=[
'true',
'false'],
68 default=
'true', help=
'Rename the links to be REP120 compliant')
69 parser.add_argument(
'-x',
'--xacro', choices=[
'urdf',
'robot'],
70 default=
'robot', help=
'Chose robot part to generate. choosing urdf create a' 71 'single urdf file with the entire robot. robot will OUTPUT a xacro file ' 72 'for every kinematic chain on the robot')
80 """Create a few materials. 82 to display geometrical shapes in a given color. 85 robot.add_material(ur.Material(
'Black', ur.Color(0.1, 0.1, 0.1, 1)))
86 robot.add_material(ur.Material(
'LightGrey', ur.Color(0.9, 0.9, 0.9, 1)))
87 robot.add_material(ur.Material(
'Grey', ur.Color(0.6, 0.6, 0.6, 1)))
88 robot.add_material(ur.Material(
'DarkGrey', ur.Color(0.3, 0.3, 0.3, 1)))
92 """Add frames defined by ROS for humanoid robots. 94 (REP120): http://www.ros.org/reps/rep-0120.html 97 global robot, NAME, MESH_VERSION, VERSION, LINKS_DICO, OFFSETS_DICO
98 print(
'creating and renaming joints & links to comply to REP120')
101 for joint
in robot.joints:
102 if robot.joints[joint].name.endswith(
'_joint'):
103 robot.joints[joint].name = robot.joints[joint].name[0:-6]
104 if robot.joints[joint].name.endswith(
'_actuator'):
105 robot.joints[joint].name = robot.joints[joint].name[0:-9]
106 if robot.joints[joint].mimic
is not None:
107 if robot.joints[joint].mimic.joint_name.endswith(
'_actuator'):
108 robot.joints[joint].mimic.joint_name = \
109 robot.joints[joint].mimic.joint_name[0:-9]
110 if robot.joints[joint].mimic.joint_name.endswith(
'_joint'):
111 robot.joints[joint].mimic.joint_name = \
112 robot.joints[joint].mimic.joint_name[0:-6]
114 robot.joints[joint].parent = LINKS_DICO[robot.joints[joint].parent]
118 robot.joints[joint].child = LINKS_DICO[robot.joints[joint].child]
121 for link
in robot.links.keys():
123 robot.rename_link(link, LINKS_DICO[link])
124 except KeyError, ValueError:
128 robot.add_link(ur.Link(
'gaze'))
129 robot.add_joint(ur.Joint(
'gaze_joint',
'HeadRoll_link',
130 'gaze',
'fixed',
None, ur.Pose(
131 (OFFSETS_DICO[
'CameraLeftEyeOffsetX'], 0,
132 OFFSETS_DICO[
'CameraLeftEyeOffsetZ']), (0, 0, 0))))
136 robot.add_link(ur.Link(
'gaze'))
137 robot.add_joint(ur.Joint(
'gaze_joint',
'Head',
138 'gaze',
'fixed',
None, ur.Pose(
139 (OFFSETS_DICO[
'CameraTopV4OffsetX'], 0,
140 OFFSETS_DICO[
'CameraTopV4OffsetZ']), (0, 0, 0))))
142 MESH_VERSION = VERSION
143 elif VERSION ==
'V33' or VERSION ==
'V40' or VERSION ==
'V50':
146 elif NAME ==
'pepper':
147 MESH_VERSION = VERSION
149 robot.add_link(ur.Link(
'base_footprint'))
150 robot.add_joint(ur.Joint(
'base_footprint_joint',
'Tibia',
151 'base_footprint',
'fixed',
None, ur.Pose(
152 (OFFSETS_DICO[
'BaseFootprintOffsetX'],
153 OFFSETS_DICO[
'BaseFootprintOffsetY'],
154 OFFSETS_DICO[
'BaseFootprintOffsetZ']),
155 (OFFSETS_DICO[
'BaseFootprintRotX'],
156 OFFSETS_DICO[
'BaseFootprintRotY'],
157 OFFSETS_DICO[
'BaseFootprintRotZ']))))
161 laser_links = [c
for c
in robot.links.keys()
162 if 'surrounding' in c.lower()]
163 for joint
in robot.joints.values():
164 if joint.child
in laser_links:
165 laser_frame = joint.child
166 laser_device_frame = laser_frame[:-5] +
'device_frame' 168 joint.child = laser_device_frame
170 robot.add_link(ur.Link(laser_device_frame))
171 joint_new = copy.deepcopy(joint)
172 joint_new.name = joint.name[:-17] + \
173 'projected_sensor_fixedjoint' 174 joint_new.child = laser_frame
175 joint_new.origin.rotation[0] = 0
176 joint_new.origin.rotation[1] = 0
178 joint_new.origin.position[2] = -0.334
179 if 'left' in laser_frame.lower():
182 joint_new.origin.rotation[2] = math.pi/2.0 + \
184 elif 'right' in laser_frame.lower():
187 joint.origin.position[0] = -0.018
188 joint_new.origin.position[0] = -0.018
191 joint_new.origin.rotation[2] = -math.pi/2.0 \
193 elif 'front' in laser_frame.lower():
194 joint_new.origin.rotation[2] = 0
195 robot.add_joint(joint_new)
198 camera_frames = [c
for c
in robot.links.keys()
if 'camera' in c.lower()]
199 for camera_frame
in camera_frames:
200 camera_optical_frame = camera_frame[:-6] +
'_optical_frame' 201 robot.add_link(ur.Link(camera_optical_frame))
202 robot.add_joint(ur.Joint(
'%s_fixedjoint' % camera_optical_frame,
203 camera_frame, camera_optical_frame,
'fixed',
None,
204 ur.Pose((0, 0, 0), (-math.pi/2.0, 0, -math.pi/2.0))))
212 """Should instanciate all transmission tags. 215 - mechanical reduction ratio for each motor 216 - joint and actuator to which the transmission tag reference 217 for now naoTransmission.xacro has been done by hand based on the work 218 of Konstantinos Chatzilygeroudis in his nao_dcm project 219 https://github.com/costashatz/nao_dcm 227 """Should instanciate all gazebo tags. 230 - mimic joints plugins 232 - disable_links plugins 233 - gazebo reference for every link (ok) 234 for now naoGazebo.xacro has been done by hand based on the work of 235 Konstantinos Chatzilygeroudis in his nao_dcm project 236 https://github.com/costashatz/nao_dcm 244 """Add a dummy Inertial tag to every links containing keyword in list.""" 247 for link
in robot.links:
248 if robot.links[link].name.find(string) != -1:
249 robot.links[link].inertial = ur.Inertial(1.1e-9, 0.0, 0.0,
250 1.1e-9, 0.0, 1.1e-9, 2e-06)
254 """Add a Box collision tag to every links containing keyword in list.""" 257 for link
in robot.links:
258 if robot.links[link].name.find(string) != -1:
259 robot.links[link].collision = ur.Collision(
260 ur.Box([0.01, 0.01, 0.005]),
261 ur.Pose((0, 0, 0), (0, 0, 0)))
269 """Create a <ROBOT>_visual_collision.xacro file. 271 with xacro macros for visual and collision tags of the urdf. 272 This function creates xacro macros for visualisation and collisions. 273 It checks if the meshes are on the computer and set the 'meshes_installed' 284 prefix =
'insert_visu_' 286 root = doc.createElement(
'robot')
287 doc.appendChild(root)
288 root.setAttribute(
"xmlns:xacro",
"http://www.ros.org/wiki/xacro")
290 cmd =
'rospack find ' + NAME + MESHPKG
292 path_mesh_pkg = subprocess.check_output(cmd, stderr=subprocess.STDOUT,
295 print(
'unable to find ' + NAME + MESHPKG +
' package')
299 node = ur.short(doc,
'xacro:property',
'name',
'PI_2')
300 node.setAttribute(
'value', str(math.pi/2.0))
301 root.appendChild(node)
302 node = ur.short(doc,
'xacro:property',
'name',
'meshes_installed')
304 if os.path.isdir(os.path.join(path_mesh_pkg,
'meshes', MESH_VERSION)):
305 node.setAttribute(
'value',
'true')
307 node.setAttribute(
'value',
'false')
308 root.appendChild(node)
311 for link
in robot.links:
313 if robot.links[link].visual
is not None:
314 robot.links[link].xacro =
'xacro:' + prefix + \
315 robot.links[link].name
316 node = ur.short(doc,
'xacro:macro',
'name', prefix +
317 robot.links[link].name)
320 node2 = ur.short(doc,
'xacro:unless',
'value',
321 '${meshes_installed}')
322 if tempVisu
is not None:
323 node2.appendChild(tempVisu.to_xml(doc))
324 if tempCol
is not None:
325 node2.appendChild(tempCol.to_xml(doc))
326 node.appendChild(node2)
327 node3 = ur.short(doc,
'xacro:if',
'value',
'${meshes_installed}')
328 node3.appendChild(robot.links[link].visual.to_xml(doc))
329 node3.appendChild(robot.links[link].collision.to_xml(doc))
330 node.appendChild(node3)
332 node.appendChild(robot.links[link].visual.to_xml(doc))
333 node.appendChild(robot.links[link].collision.to_xml(doc))
334 root.appendChild(node)
335 robot.links[link].visual =
None 336 robot.links[link].collision =
None 337 filename = OUTPUT[0:OUTPUT.rfind(
'.')] +
'_visual_collisions.xacro' 348 Export the 'elements' related to the keyword 'element'. 350 :param : element, string in ['Transmission', 'Gazebo', 'material'] 352 The output file is <ROBOT>_<element>.xacro 356 root = doc.createElement(
'robot')
357 doc.appendChild(root)
358 root.setAttribute(
"xmlns:xacro",
"http://www.ros.org/wiki/xacro")
359 for i
in robot.elements:
361 if element ==
'Transmission':
362 if i.name.find(element) != -1:
363 root.appendChild(i.to_xml(doc))
364 elif element ==
'Gazebo':
365 if i.reference
is not None:
366 root.appendChild(i.to_xml(doc))
367 elif i.plugins != []:
368 root.appendChild(i.to_xml(doc))
369 elif element ==
'material':
371 root.appendChild(i.to_xml(doc))
372 except AttributeError:
374 filename = OUTPUT[0:OUTPUT.rfind(
'.')] +
'_' + str(element) +
'.xacro' 375 print(
'exporting ' + element +
' xacro')
381 Export the entire 'robot' in several xacro files. 383 One xacro file per kinematic chain (<ROBOT>_legs.xacro, 384 <ROBOT>_arms.xacro, <ROBOT>_torso.xacro...) 385 Xacro file for specific parts of the robot (<ROBOT>_fingers.xacro, 386 <ROBOT>_sensors.xacro) 387 One xacro file for visual elements (<ROBOT>_visual_collision.xacro, 388 <ROBOT>_material.xacro) 389 One xacro file per type of element needed for gazebo simulation 390 (<ROBOT>_Gazebo.xacro, <ROBOT>_Transmission.xacro) 391 One generic robot file which includes all the other ones 392 (<ROBOT>_robot.xacro) 394 global robot, OUTPUT, NAME
396 root = doc.createElement(
'robot')
397 doc.appendChild(root)
398 root.setAttribute(
"xmlns:xacro",
"http://www.ros.org/wiki/xacro")
399 root.setAttribute(
"name", robot.name)
400 root.appendChild(ur.short(doc,
'xacro:include',
'filename', NAME +
401 '_visual_collisions.xacro'))
403 for i
in XACRO_DICO.keys():
404 print(
'exporting ' + NAME +
'_' + i +
'.xacro')
405 if i.find(
'eye') != -1:
410 filenamerobot = NAME +
'_' + i +
'.xacro' 411 root.appendChild(ur.short(doc,
'xacro:include',
'filename',
415 root.appendChild(ur.short(doc,
'xacro:include',
'filename', NAME +
416 '_Transmission.xacro'))
419 root.appendChild(ur.short(doc,
'xacro:include',
'filename', NAME +
421 root.appendChild(ur.short(doc,
'xacro:include',
'filename', NAME +
425 root.appendChild(ur.short(doc,
'xacro:include',
'filename', NAME +
430 root.appendChild(ur.short(doc,
'xacro:include',
'filename', NAME +
435 root.appendChild(ur.short(doc,
'xacro:include',
'filename',
438 filename = OUTPUT[0:OUTPUT.rfind(
'.')] +
'_robot.xacro' 440 print(
'output directory : ' + OUTPUT[0:OUTPUT.rfind(
'/') + 1])
444 tipRefChain=
'default'):
445 """Export a specific kinematic chain to a xacro file. 447 :param : keyword, string defining kinematic chains to export 448 (legs,arms,head,torso) 449 :param : baseChain, string representing the name of the link where 450 the reference chain starts 451 :param : tipRefChain, string representing the name of the link where 452 the reference chain ends 455 if tipRefChain ==
'default':
456 print(
'applying torso to end of ref chain')
457 tipRefChain = XACRO_DICO[
'torso']
458 chainRef = robot.get_chain(baseChain, tipRefChain)
461 root = doc.createElement(
'robot')
462 doc.appendChild(root)
463 root.setAttribute(
'xmlns:xacro',
'http://www.ros.org/wiki/xacro')
466 chain1 = robot.get_chain(baseChain,
'l_' + XACRO_DICO[keyword])
467 chain2 = robot.get_chain(baseChain,
'r_' + XACRO_DICO[keyword])
471 chain1 = robot.get_chain(baseChain,
'L' + XACRO_DICO[keyword])
472 chain2 = robot.get_chain(baseChain,
'R' + XACRO_DICO[keyword]) 476 chain1 = robot.get_chain(baseChain, XACRO_DICO[keyword])
479 print(
'the chain ' + keyword +
' cannot be found')
483 for i
in range(len(chain1)):
484 for j
in range(len(chainRef)):
485 if chain1[i] == chainRef[j]:
487 if duplicate == 0
or keyword ==
'torso':
489 root.appendChild(robot.links[chain1[i]].to_xml(doc))
492 root.appendChild(robot.joints[chain1[i]].to_xml(doc))
494 print(
'unknown element' + chain1[i])
498 for i
in range(len(chain2)):
499 for j
in range(len(chainRef)):
500 if chain2[i] == chainRef[j]:
504 root.appendChild(robot.links[chain2[i]].to_xml(doc))
508 robot.joints[chain2[i]].to_xml(doc))
510 print(
'unknown element' + chain2[i])
513 filename = OUTPUT[0:OUTPUT.rfind(
'.')] +
'_' + keyword + str(
'.xacro')
519 Write the content of the XML Document doc to a file named filename. 521 Also add comments at the beginning of the file 523 :param : doc, minidom Document to write 524 :param : filename, absolute path of the file to write to 526 if(
not os.path.isdir(filename[0:filename.rfind(
'/') + 1])):
527 os.makedirs(filename[0:filename.rfind(
'/')])
529 file = open(filename,
'w+')
530 file.write(doc.toprettyxml())
532 file = open(filename,
'r') 533 firstline, remaining = file.readline(), file.read() 535 file = open(filename, 'w')
536 file.write(firstline)
538 '<!--**************************************************************\n' 539 ' **** File automatically generated by generate_urdf.py script ****\n' 540 ' **************************************************************-->\n')
541 file.write(remaining)
546 """Export all links containing a string and its parent joint. 548 :param : list, list of strings to look for 549 :param : filename, absolute path of the file to write to 553 root = doc.createElement(
'robot')
554 doc.appendChild(root)
555 root.setAttribute(
"xmlns:xacro",
"http://www.ros.org/wiki/xacro")
556 print (
'exporting ' + os.path.basename(filename))
558 for link
in robot.links:
559 if robot.links[link].name.find(string) != -1:
560 root.appendChild(robot.links[link].to_xml(doc))
561 for joint
in robot.joints:
562 if robot.joints[joint].child == robot.links[link].name:
563 root.appendChild(robot.joints[joint].to_xml(doc))
569 Find the path of a mesh according to the link definition. 571 Set the visual and collision element of the given link 573 :param : path_mesh_pkg, absolute path of the package where the meshes 575 :param : link, dictionary key of the link we want to set 576 visual and collision parameters 578 :return : tempVisu, Visual element with the NAO visual geometrical shape 579 for people who don't have the meshes 580 :return : tempCol, Collision element with the NAO collision geometrical 581 shape for people who don't have the meshes 583 global robot, SCALE, MESHPKG, MESH_VERSION
588 if robot.links[link].visual
is not None:
591 LINKS_DICO.keys()[list(LINKS_DICO.values()).index(link)])
592 if meshname.endswith(
'_link'):
593 meshfile = meshname[0:-5]
600 if meshfile.endswith(
'_link'):
601 meshfile = meshfile[0:-5]
602 tempVisuMesh = ur.Visual(ur.Mesh(
'', (SCALE, SCALE, SCALE)))
603 tempColMesh = ur.Collision(ur.Mesh(
'', (SCALE, SCALE, SCALE)))
604 tempVisuMesh.origin = robot.links[link].visual.origin
605 tempColMesh.origin = robot.links[link].visual.origin
606 if os.path.isfile(os.path.join(path_mesh_pkg,
'meshes', MESH_VERSION,
607 robot.links[link].visual.geometry.filename[
608 robot.links[link].visual.geometry.filename.rfind(
'/') + 1:])):
609 tempVisuMesh.geometry.filename = os.path.join(
610 'package://', NAME + MESHPKG,
'meshes', MESH_VERSION,
611 robot.links[link].visual.geometry.filename[
612 robot.links[link].visual.geometry.filename.rfind(
'/')+1:])
613 tempColMesh.geometry.filename = \
614 tempVisuMesh.geometry.filename[0:-4] + COLLISION_SUFFIX
616 tempVisuMesh.geometry.filename = os.path.join(
617 'package://', NAME + MESHPKG,
'meshes', MESH_VERSION,
619 tempColMesh.geometry.filename = \
620 tempVisuMesh.geometry.filename[0:-4] + COLLISION_SUFFIX
624 tempVisu = ur.Visual(
625 VISU_DICO[meshname], ur.Material(
'LightGrey'),
626 dico.Nao_orig[meshname])
627 tempCol = ur.Collision(VISU_DICO[meshname],
628 dico.Nao_orig[meshname])
632 robot.links[link].visual = tempVisuMesh
633 robot.links[link].collision = tempColMesh
634 return (tempVisu, tempCol)
640 args = parser.parse_args()
642 robot = URDF.from_parameter_server()
644 robot = URDF.load_xml_file(args.input)
646 if robot.name.find(
'V') != -1:
647 VERSION = robot.name[robot.name.find(
'V'):]
649 VERSION = args.input[args.input.find(
'V'):args.input.find(
'V') + 3]
651 if robot.name.lower().find(
'nao') != -1:
655 print(
'import nao dictionaries')
657 print(
'unable to import nao dictionaries')
659 LINKS_DICO = dico.Nao_links
660 VISU_DICO = dico.Nao_visu
661 OFFSETS_DICO = dico.Nao_offsets
662 XACRO_DICO = NAO_XACRO_DICO
665 elif robot.name.lower().find(
'romeo') != -1:
670 print(
'unable to import romeo dictionaries')
672 LINKS_DICO = dico.Romeo_links
673 OFFSETS_DICO = dico.Romeo_offsets
675 XACRO_DICO = ROMEO_XACRO_DICO
676 MESHPKG =
'_description' 679 elif robot.name.lower().find(
'juliette')
or robot.name.lower().find(
'pepper'):
684 print(
'unable to import pepper dictionaries')
686 LINKS_DICO = dico.Pepper_links
687 OFFSETS_DICO = dico.Pepper_offsets
689 XACRO_DICO = PEPPER_XACRO_DICO
690 print(
'PROCESSING PEPPER ROBOT')
695 for element
in robot.elements:
697 robot.elements.remove(element)
698 cmd =
'rospack find ' + NAME +
'_description' 700 pathdescription = subprocess.check_output(cmd,
701 stderr=subprocess.STDOUT, shell=
True)[:-1]
703 print(
'unable to find ' + NAME +
'_description package')
705 OUTPUT = os.path.join(pathdescription,
'urdf', NAME + VERSION +
706 '_generated_urdf', NAME +
'.urdf')
707 print(
'processing ' + NAME +
' (' + VERSION +
") robot's urdf file in " +
709 cmd =
'rospack find ' + NAME + MESHPKG
711 path_mesh_pkg = subprocess.check_output(cmd, stderr=subprocess.STDOUT,
714 print(
'unable to find ' + NAME + MESHPKG +
' package')
718 if args.REP120 ==
'true':
721 for link
in robot.links:
724 if args.xacro ==
'robot':
727 elif args.xacro ==
'urdf':
728 robot.write_xml(OUTPUT)
def export_kinematic_chain_to_xacro(keyword, baseChain='base_link', tipRefChain='default')
def export_robot_element(element)
XACRO FUNCTIONS ########.
def export_list_to_xacro(list, filename)
def add_dummy_inertia(list)
def write_comments_in_xacro(doc, filename)
def create_visual_xacro()
Meshes #####.
def REP120_compatibility()
def adjustMeshPath(path_mesh_pkg, link)
def define_materials()
FUNCTIONS ####.
def add_dummy_collision(list)
def add_transmission_tags()
def export_robot_to_xacro_files()