-
-
Notifications
You must be signed in to change notification settings - Fork 45
Automatic conversion tool + urdf library w/ meshes #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Simon-Steinmann
wants to merge
34
commits into
master
Choose a base branch
from
Simon-Steinmann-batch-conversion
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 15 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
08ef58b
add optparser
Simon-Steinmann 1f77776
Test world generation
Simon-Steinmann ed2c886
add LineSet Collada fix, so there are no errors
Simon-Steinmann 6c09cfc
Add motomann
Simon-Steinmann cad05ee
pep8 compliancy
Simon-Steinmann a8cdae1
fix for first time running
Simon-Steinmann da6fc0e
delete urdf data
Simon-Steinmann d471af3
parse input, output direcotries
Simon-Steinmann 949aa57
include latest collada fix
Simon-Steinmann 88a3167
gitignore cleaned
Simon-Steinmann f452237
pep8 cleanup
Simon-Steinmann 972a499
revert to master
Simon-Steinmann 33c354a
Merge branch 'master' into Simon-Steinmann-batch-conversion
Simon-Steinmann 280a6e4
add batch_conversion changes back in
Simon-Steinmann 3ab9905
change expected proto files
Simon-Steinmann 4dfaf4f
Update batch_conversion.py
Simon-Steinmann c89b9e1
Update batch_conversion.py
Simon-Steinmann 71646b3
Update batch_conversion.py
Simon-Steinmann 1787e7c
Update batch_conversion.py
Simon-Steinmann 9536839
Update batch_conversion.py
Simon-Steinmann 0040788
Update batch_conversion.py
Simon-Steinmann b33b2d4
Update batch_conversion.py
Simon-Steinmann 55e21ec
Update batch_conversion.py
Simon-Steinmann b3e4405
Update batch_conversion.py
Simon-Steinmann 28490a8
Update batch_conversion.py
DavidMansolino e20c429
Update batch_conversion.py
Simon-Steinmann 1778318
Update batch_conversion.py
Simon-Steinmann 0c9e3e0
Update test.
DavidMansolino 640afae
Merge branch 'Simon-Steinmann-batch-conversion' of https://github.com…
DavidMansolino bbdd0c2
Add execution flag.
DavidMansolino e347776
Update batch_conversion.py
Simon-Steinmann 5a6d27a
Update batch_conversion.py
Simon-Steinmann 34f1576
Update batch_conversion.py
Simon-Steinmann d2a7bfc
Detailed description
Simon-Steinmann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,254 @@ | ||
| #!/usr/bin/env python | ||
|
|
||
| """URDF files to Webots PROTO converter.""" | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| import optparse | ||
| import datetime | ||
| import os | ||
| import shutil | ||
| import json | ||
| import math | ||
| # from urdf2webots.importer import convert2urdf | ||
| import urdf2webots.importer as importer | ||
| from copy import deepcopy | ||
|
|
||
| # Set Override = True if you want to override ALL config settings with the | ||
| # value in the OverrideCfg. This can be usefull for testing functionality | ||
| # quickly, and then doing a slow optimized conversion | ||
| Override = False | ||
| OverrideCfg = { | ||
| 'disableMeshOptimization': False | ||
| } | ||
|
|
||
| # This defines the default config. If a urdf model doesnt have a config, one | ||
| # will created after this. If the importer.py is updated, so should this to | ||
| # add the additional functionality- | ||
| default_config = { | ||
| # outFile will get constructed with protosPath + robotName + '.proto' | ||
| 'robotName': None, | ||
| 'normal': False, | ||
| 'boxCollision': True, | ||
| 'disableMeshOptimization': True, | ||
| 'enableMultiFile': True, | ||
| 'staticBase': True, | ||
| 'toolSlot': None, | ||
| 'initRotation': "1 0 0 -1.5708" | ||
| } | ||
|
|
||
|
|
||
| class batchConversion(): | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| def __init__(self, sourcePath, outPath): | ||
| self.urdf2webots_path = os.path.dirname(os.path.abspath(__file__)) | ||
| # create a unique path for a new batch-conversion | ||
| today = datetime.date.today() | ||
| todaystr = today.isoformat() | ||
| self.protoTargetDir = outPath + '/protos_' + todaystr | ||
| n = 2 | ||
| while os.path.exists(self.protoTargetDir): | ||
| while os.path.exists(self.protoTargetDir + '_Nr-' + str(n)): | ||
| n += 1 | ||
| self.protoTargetDir = self.protoTargetDir + '_Nr-' + str(n) | ||
|
|
||
| # Find all the urdf files, and create the corresponding proto filePaths | ||
| urdf_root_dir = sourcePath # 'automatic_conversion/urdf' | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| os.chdir(urdf_root_dir) | ||
| # Walk the tree. | ||
| self.urdf_files = [] # List of the full filepaths. | ||
| self.protosPaths = [] | ||
| for root, directories, files in os.walk('./'): | ||
| for filename in files: | ||
| # Join the two strings in order to form the full filepath. | ||
| if filename.endswith(".urdf"): | ||
| filepath = os.path.join(root, filename) | ||
| filepath = filepath[1:] | ||
| self.urdf_files.append(urdf_root_dir + filepath) | ||
| self.protosPaths.append(self.protoTargetDir + os.path.dirname(filepath) + '/') | ||
| os.chdir(self.urdf2webots_path) | ||
|
|
||
| # Report dict we can print at the end | ||
| self.EndReportMessage = { | ||
| 'updateConfigs': [], | ||
| 'newConfigs': [], | ||
| 'converted': [], | ||
| 'failed': [] | ||
| } | ||
|
|
||
| def update_and_convert(self): | ||
| # Make sure all configs exist and are up to date, then convert all URDF files | ||
DavidMansolino marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| self.check_all_configs() | ||
| self.convert_all_urdfs() | ||
| self.print_end_report() | ||
|
|
||
| def create_proto_dir(self): | ||
| # create a new unique directory for our conversions | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| print(self.protoTargetDir) | ||
| os.makedirs(self.protoTargetDir) | ||
|
|
||
| def replace_ExtraProjectPath(self): | ||
| # this copies the converted files and puts them in the "automatic_conversion/ExtraProjectTest" directory | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # This path can be added to webots, so we can test each new conversion, without having to adjust the Path | ||
| # every time | ||
| destination = os.path.dirname(self.protoTargetDir) + '/ExtraProjectTest' | ||
| if os.path.isfile(destination) or os.path.islink(destination): | ||
| os.remove(destination) # remove the file | ||
| elif os.path.isdir(destination): | ||
| shutil.rmtree(destination) # remove dir and all contains | ||
| shutil.copytree(self.protoTargetDir, destination) | ||
|
|
||
| def update_config(self, configFile, config=None): | ||
| # makes sure the existing configs are in the same format as the default. Existing options are conserved | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| new_config = deepcopy(default_config) | ||
| for key in new_config.keys(): | ||
| try: | ||
| new_config[key] = config[key] | ||
| except: | ||
| pass | ||
| with open(configFile, 'w') as outfile: | ||
| json.dump(new_config, outfile, indent=4, sort_keys=True) | ||
|
|
||
| def check_all_configs(self): | ||
| # makes sure ever URDF file has a config and it is up do date | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| print('Checking config files...') | ||
| print('---------------------------------------') | ||
| for i in range(len(self.urdf_files)): | ||
| configFile = os.path.splitext(self.urdf_files[i])[0] + '.json' | ||
| try: | ||
| with open(configFile) as json_file: | ||
| config = json.load(json_file) | ||
| if config.keys() != default_config.keys(): | ||
| print('Updating config (old settings will be carried over) - ', configFile) | ||
| self.EndReportMessage['updateConfigs'].append(configFile) | ||
| update_config(configFile, config) | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| else: | ||
| print('Config up to date - ', configFile) | ||
| except: | ||
| print('Generating new config for - ' + os.path.splitext(self.urdf_files[i])[0]) | ||
| self.EndReportMessage['newConfigs'].append(configFile) | ||
| update_config(configFile) | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| def convert_all_urdfs(self): | ||
| # convertes all URDF files according to their .json config files | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| self.create_proto_dir() | ||
| print('---------------------------------------') | ||
| print('Converting URDF to PROTO...') | ||
| print('---------------------------------------') | ||
| print('---------------------------------------') | ||
| for i in range(len(self.urdf_files)): | ||
| # clears any previous Geometry and Material references, so there is no conflict | ||
| importer.urdf2webots.parserURDF.Geometry.reference = {} | ||
| importer.urdf2webots.parserURDF.Material.namedMaterial = {} | ||
| configFile = os.path.splitext(self.urdf_files[i])[0] + '.json' | ||
| print('---------------------------------------') | ||
| print('Converting: ', self.urdf_files[i]) | ||
| print('---------------------------------------') | ||
| try: | ||
| with open(configFile) as json_file: | ||
| config = json.load(json_file) | ||
| importer.convert2urdf( | ||
| inFile=self.urdf_files[i], | ||
| outFile=self.protosPaths[i] if not config['robotName'] else self.protosPaths[i] + config['robotName'] + '.proto', | ||
| normal=config['normal'], | ||
| boxCollision=config['boxCollision'], | ||
| disableMeshOptimization=OverrideCfg['disableMeshOptimization'] if Override else config['disableMeshOptimization'], | ||
| enableMultiFile=config['enableMultiFile'], | ||
| staticBase=config['staticBase'], | ||
| toolSlot=config['toolSlot'], | ||
| initRotation=config['initRotation']) | ||
| self.EndReportMessage['converted'].append(self.urdf_files[i]) | ||
| except Exception as e: | ||
| print(e) | ||
| self.EndReportMessage['failed'].append(self.urdf_files[i]) | ||
| print('---------------------------------------') | ||
|
|
||
| def print_end_report(self): | ||
| # print the Report | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| print('---------------------------------------') | ||
| print('------------End Report-----------------') | ||
| print('---------------------------------------') | ||
| print('Configs updated:') | ||
| print('---------------------------------------') | ||
| for config in self.EndReportMessage['updateConfigs']: | ||
| print(config) | ||
| print('---------------------------------------') | ||
| print('Configs created:') | ||
| print('---------------------------------------') | ||
| for config in self.EndReportMessage['newConfigs']: | ||
| print(config) | ||
| print('---------------------------------------') | ||
| print('Successfully converted URDF files:') | ||
| print('---------------------------------------') | ||
| for urdf in self.EndReportMessage['converted']: | ||
| print(urdf) | ||
| if len(self.EndReportMessage['failed']) > 0: | ||
| print('---------------------------------------') | ||
| print('Failedd URDF conversions:') | ||
| print('---------------------------------------') | ||
| for urdf in self.EndReportMessage['failed']: | ||
| print(urdf) | ||
|
|
||
| def create_test_world(self, spacing=3): | ||
Simon-Steinmann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| n_models = len(self.urdf_files) | ||
| n_row = math.ceil(n_models ** 0.5) # round up the sqrt of the number of models | ||
| grid_size = spacing * (n_row + 1) | ||
| projectDir = os.path.dirname(self.protoTargetDir) + '/ExtraProjectTest/TestAllRobots.wbt' | ||
| worldFile = open(projectDir, 'w') | ||
| worldFile.write('#VRML_SIM R2020b utf8\n') | ||
| worldFile.write('\n') | ||
| worldFile.write('WorldInfo {\n') | ||
| worldFile.write(' basicTimeStep 16\n') | ||
| worldFile.write(' coordinateSystem "NUE"\n') | ||
| worldFile.write('}\n') | ||
| worldFile.write('Viewpoint {\n') | ||
| worldFile.write(' orientation 0.7180951961816571 -0.6372947429425837 -0.27963315225232777 5.235063704863283\n') | ||
| worldFile.write(' position 1.9410928989638234 2.447392518518642 1.7311802992777219\n') | ||
| worldFile.write('}\n') | ||
| worldFile.write('TexturedBackground {\n') | ||
| worldFile.write('}\n') | ||
| worldFile.write('TexturedBackgroundLight {\n') | ||
| worldFile.write('}\n') | ||
| worldFile.write('RectangleArena {\n') | ||
| worldFile.write(' floorSize ' + str(grid_size) + ' ' + str(grid_size) + '\n') | ||
| worldFile.write(' floorTileSize 0.25 0.25\n') | ||
| worldFile.write(' wallHeight 0.05\n') | ||
| worldFile.write('}\n') | ||
| filenames = [] # List which will store all of the full filepaths. | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| row = 0 | ||
| column = 1 | ||
| for root, directories, files in os.walk(os.path.dirname(projectDir)): | ||
| for filename in files: | ||
| # Join the two strings in order to form the full filepath. | ||
| if filename.endswith(".proto"): | ||
| filepath = os.path.join(root, filename) | ||
| if os.path.dirname(filepath).split('_')[-1] != 'meshes': | ||
| name = os.path.splitext(os.path.basename(filename))[0] | ||
| if row == n_row: | ||
| column += 1 | ||
| row = 0 | ||
| row += 1 | ||
| # add the model to the world file with translation to be spaced in a grid | ||
| worldFile.write(name + ' {\n') | ||
| worldFile.write(' translation ' + str(column * spacing - grid_size / 2) + ' 0 ' + str(row * spacing - grid_size / 2) + '\n') | ||
| worldFile.write('}\n') | ||
| worldFile.close() | ||
| os.chdir(self.urdf2webots_path) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| optParser = optparse.OptionParser(usage='usage: %prog [options]') | ||
| optParser.add_option('--input', dest='sourcePath', default='', help='Specifies the urdf file to convert.') | ||
| optParser.add_option('--output', dest='outPath', default='', help='Specifies the path and, if ending in ".proto", name of the resulting PROTO file.' | ||
| ' The filename minus the .proto extension will be the robot name.') | ||
| optParser.add_option('--force-mesh-optimization', dest='forceMesh', action='store_true', default=False, help='Set if mesh-optimization should be turned on for all conversions. This will take much longer!') | ||
| optParser.add_option('--no-project-override', dest='extraProj', action='store_true', default=False, help='Set if new conversions should NOT replace existing ones in "automatic_conversion/ExtraProjectTest".') | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| optParser.add_option('--update-cfg', dest='cfgUpdate', action='store_true', default=False, help='No conversion. Only updates or creates .json config for every URDF file in "automatic_conversion/urdf".') | ||
|
|
||
| options, args = optParser.parse_args() | ||
| bc = batchConversion(options.sourcePath, options.outPath) | ||
Simon-Steinmann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Override = options.forceMesh | ||
| if options.cfgUpdate: | ||
| bc.check_all_configs() | ||
| else: | ||
| bc.update_and_convert() | ||
| if not options.extraProj: | ||
| bc.replace_ExtraProjectPath() | ||
| bc.create_test_world() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -155,10 +155,9 @@ def URDFLink(proto, link, level, parentList, childList, linkList, jointList, sen | |
| proto.write((level + 1) * indent + 'physics Physics {\n') | ||
| proto.write((level + 2) * indent + 'density -1\n') | ||
| proto.write((level + 2) * indent + 'mass %lf\n' % link.inertia.mass) | ||
| if link.inertia.position != [0.0, 0.0, 0.0]: | ||
| proto.write((level + 2) * indent + 'centerOfMass [ %lf %lf %lf ]\n' % (link.inertia.position[0], | ||
| link.inertia.position[1], | ||
| link.inertia.position[2])) | ||
| proto.write((level + 2) * indent + 'centerOfMass [ %lf %lf %lf ]\n' % (link.inertia.position[0], | ||
| link.inertia.position[1], | ||
| link.inertia.position[2])) | ||
| if link.inertia.ixx > 0.0 and link.inertia.iyy > 0.0 and link.inertia.izz > 0.0: | ||
| i = link.inertia | ||
| inertiaMatrix = [i.ixx, i.ixy, i.ixz, i.ixy, i.iyy, i.iyz, i.ixz, i.iyz, i.izz] | ||
|
|
@@ -503,8 +502,8 @@ def URDFShape(proto, link, level, normal=False): | |
| if name is None: | ||
| if visualNode.geometry.name is not None: | ||
| name = computeDefName(visualNode.geometry.name) | ||
| if visualNode.geometry.defName is None: | ||
| name = robotNameMain + '_' + name if robotNameMain else name | ||
| name = robotNameMain + '_' + name if robotNameMain else name | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have to define the name before the condition, otherwise the adjusted path is not used, causing errors |
||
| if visualNode.geometry.defName is None: | ||
| print('Create meshFile: %sMesh.proto' % name) | ||
| filepath = '%s/%sMesh.proto' % (meshFilesPath, name) | ||
| meshProtoFile = open(filepath, 'w') | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.