Merge branch '6-model-config-xml' into 'master'
Gazebo Export Package restucturing Closes #5 and #6 See merge request robosphere/forks/ARBench!2
This commit is contained in:
commit
8241627436
5 changed files with 186 additions and 91 deletions
17
ARTools.py
17
ARTools.py
|
@ -394,6 +394,16 @@ def exportPartInfoAndFeaturesDialogue():
|
||||||
+ str(unique_selected[0].Label)
|
+ str(unique_selected[0].Label)
|
||||||
+ " exported to " + str(ofile) + "\n")
|
+ " exported to " + str(ofile) + "\n")
|
||||||
|
|
||||||
|
def exportGazeboModels():
|
||||||
|
import GazeboExport
|
||||||
|
doc = FreeCAD.activeDocument()
|
||||||
|
for obj in doc.Objects:
|
||||||
|
"""Export solid shapes."""
|
||||||
|
if (isinstance(obj.Shape, Part.Solid) if hasattr(obj, 'Shape') else False):
|
||||||
|
GazeboExport.export_gazebo_model(obj, os.path.split(doc.FileName)[0], configs={})
|
||||||
|
elif isinstance(obj, Part.Feature):
|
||||||
|
FreeCAD.Console.PrintMessage('{0} part is not valid. It has a Compound type, but Solids there are hidden. Please convert it to single Solid'.format(obj.Label))
|
||||||
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# GUI Commands
|
# GUI Commands
|
||||||
|
@ -401,12 +411,19 @@ def exportPartInfoAndFeaturesDialogue():
|
||||||
uidir = os.path.join(FreeCAD.getUserAppDataDir(),
|
uidir = os.path.join(FreeCAD.getUserAppDataDir(),
|
||||||
"Mod", __workbenchname__, "UI")
|
"Mod", __workbenchname__, "UI")
|
||||||
icondir = os.path.join(uidir, "icons")
|
icondir = os.path.join(uidir, "icons")
|
||||||
|
|
||||||
spawnClassCommand("ExportPartInfoAndFeaturesDialogueCommand",
|
spawnClassCommand("ExportPartInfoAndFeaturesDialogueCommand",
|
||||||
exportPartInfoAndFeaturesDialogue,
|
exportPartInfoAndFeaturesDialogue,
|
||||||
{"Pixmap": str(os.path.join(icondir, "parttojson.svg")),
|
{"Pixmap": str(os.path.join(icondir, "parttojson.svg")),
|
||||||
"MenuText": "Export info and featureframes",
|
"MenuText": "Export info and featureframes",
|
||||||
"ToolTip": "Export part properties (placement, C.O.M) and feature frames"})
|
"ToolTip": "Export part properties (placement, C.O.M) and feature frames"})
|
||||||
|
|
||||||
|
spawnClassCommand("ExportGazeboModels",
|
||||||
|
exportGazeboModels,
|
||||||
|
{"Pixmap": str(os.path.join(icondir, "gazeboexport.svg")),
|
||||||
|
"MenuText": "Export SDF-models to Gazebo",
|
||||||
|
"ToolTip": "Export SDF-models for all solid parts"})
|
||||||
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Information from primitive type
|
# Information from primitive type
|
||||||
|
|
|
@ -5,17 +5,14 @@ import collada
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
from xml.dom.minidom import parseString
|
from xml.dom.minidom import parseString
|
||||||
from math import radians as _radians
|
from math import radians as _radians
|
||||||
|
import Part
|
||||||
|
|
||||||
|
def export_gazebo_model(obj, export_dir, configs={}):
|
||||||
|
name = obj.Label
|
||||||
|
|
||||||
def export_gazebo_model(model_dir, configs={}):
|
|
||||||
doc = FreeCAD.activeDocument()
|
|
||||||
assembly_dir = os.path.split(doc.FileName)[0]
|
|
||||||
scale = configs.get('scale', 0.001)
|
scale = configs.get('scale', 0.001)
|
||||||
scale_vec = FreeCAD.Vector([scale]*3)
|
scale_vec = FreeCAD.Vector([scale]*3)
|
||||||
|
|
||||||
for obj in doc.Objects:
|
|
||||||
if obj.isDerivedFrom("Part::Feature"):
|
|
||||||
name = obj.Label
|
|
||||||
density = configs.get('density', 1000)
|
density = configs.get('density', 1000)
|
||||||
|
|
||||||
bounding_box = obj.Shape.BoundBox
|
bounding_box = obj.Shape.BoundBox
|
||||||
|
@ -40,12 +37,12 @@ def export_gazebo_model(model_dir, configs={}):
|
||||||
placement = shape.Placement
|
placement = shape.Placement
|
||||||
placement.Base.scale(*scale_vec)
|
placement.Base.scale(*scale_vec)
|
||||||
|
|
||||||
mesh_file = os.path.join(model_dir, name, 'meshes')
|
model_dir = os.path.join(export_dir, name)
|
||||||
mesh_file = os.path.splitext(mesh_file)[0] + name + '.dae'
|
mesh_dir = os.path.join(model_dir, 'meshes')
|
||||||
mesh_dir = os.path.split(mesh_file)[0]
|
mesh_file = os.path.join(mesh_dir, name + '.dae')
|
||||||
|
|
||||||
os.makedirs(mesh_dir, exist_ok=True)
|
os.makedirs(mesh_dir, exist_ok=True)
|
||||||
export_collada(doc, [obj], mesh_file, scale=scale, offset=com*-1)
|
export_collada([obj], mesh_file, scale=scale, offset=com*-1)
|
||||||
|
|
||||||
pose = placement.copy()
|
pose = placement.copy()
|
||||||
pose.Base = com
|
pose.Base = com
|
||||||
|
@ -59,9 +56,7 @@ def export_gazebo_model(model_dir, configs={}):
|
||||||
mass=mass,
|
mass=mass,
|
||||||
inertia=inertia)
|
inertia=inertia)
|
||||||
|
|
||||||
package = configs.get('ros_package', name)
|
mesh_uri = os.path.relpath(mesh_file, export_dir)
|
||||||
mesh_uri = os.path.join(package,
|
|
||||||
os.path.relpath(mesh_file, model_dir))
|
|
||||||
mesh_uri = os.path.normpath(mesh_uri)
|
mesh_uri = os.path.normpath(mesh_uri)
|
||||||
|
|
||||||
visual = Visual(name=name+'_visual', mesh=mesh_uri)
|
visual = Visual(name=name+'_visual', mesh=mesh_uri)
|
||||||
|
@ -74,9 +69,11 @@ def export_gazebo_model(model_dir, configs={}):
|
||||||
collision=collision)
|
collision=collision)
|
||||||
model.links.append(link)
|
model.links.append(link)
|
||||||
|
|
||||||
with open(os.path.join(model_dir, name+'.sdf'), 'w') as sdf_file:
|
with open(os.path.join(model_dir, 'model.sdf'), 'w') as sdf_file:
|
||||||
sdf_file.write(model.to_xml_string('sdf'))
|
sdf_file.write(model.to_xml_string('sdf'))
|
||||||
|
|
||||||
|
with open(os.path.join(model_dir, 'model.config'), 'w') as config_file:
|
||||||
|
config_file.write(config(name, 'model.sdf', 'Author', 'Email', 'Comment', 'Version'))
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Export helpers
|
# Export helpers
|
||||||
|
@ -84,10 +81,9 @@ def export_gazebo_model(model_dir, configs={}):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def export_collada(doc, exportList, filename, scale=1, quality=1, offset=np.zeros(3)):
|
def export_collada(exportList, filename, scale=1, quality=1, offset=np.zeros(3)):
|
||||||
'''FreeCAD collada exporter
|
'''FreeCAD collada exporter
|
||||||
doc - FreeCAD document
|
exportList - list of objects
|
||||||
exportList - list of objects from doc
|
|
||||||
scale - scaling factor for the mesh
|
scale - scaling factor for the mesh
|
||||||
quality - mesh tessellation quality
|
quality - mesh tessellation quality
|
||||||
offset - offset of the origin of the resulting mesh'''
|
offset - offset of the origin of the resulting mesh'''
|
||||||
|
@ -218,6 +214,28 @@ def pose_xyz(pose):
|
||||||
xyz = pose.Base if hasattr(pose, 'Base') else pose
|
xyz = pose.Base if hasattr(pose, 'Base') else pose
|
||||||
return ' '.join([flt2str(i) for i in xyz])
|
return ' '.join([flt2str(i) for i in xyz])
|
||||||
|
|
||||||
|
def config(model_name, sdf, author, email, desc, version):
|
||||||
|
top = ET.Element('model')
|
||||||
|
name = ET.SubElement(top, 'name')
|
||||||
|
name.text = model_name
|
||||||
|
ver = ET.SubElement(top, 'version')
|
||||||
|
ver.text = version
|
||||||
|
sdf_file = ET.SubElement(top, 'sdf')
|
||||||
|
sdf_file.text = sdf
|
||||||
|
sdf_file.set('version', '1.5')
|
||||||
|
|
||||||
|
author_tag = ET.SubElement(top, 'author')
|
||||||
|
author_name = ET.SubElement(author_tag, 'name')
|
||||||
|
author_name.text = author
|
||||||
|
email_address = ET.SubElement(author_tag, 'email')
|
||||||
|
email_address.text = email
|
||||||
|
|
||||||
|
description = ET.SubElement(top, 'description')
|
||||||
|
description.text = desc
|
||||||
|
|
||||||
|
dom = parseString(ET.tostring(top, encoding="unicode"))
|
||||||
|
return dom.toprettyxml(indent=' '*2)
|
||||||
|
|
||||||
|
|
||||||
class SpatialEntity(object):
|
class SpatialEntity(object):
|
||||||
'''A base class for sdf/urdf elements containing name, pose and urdf_pose'''
|
'''A base class for sdf/urdf elements containing name, pose and urdf_pose'''
|
||||||
|
|
|
@ -14,7 +14,8 @@ class ARBench(Workbench):
|
||||||
self.framecommands = ["FrameCommand",
|
self.framecommands = ["FrameCommand",
|
||||||
"AllPartFramesCommand",
|
"AllPartFramesCommand",
|
||||||
"FeatureFrameCommand"]
|
"FeatureFrameCommand"]
|
||||||
self.toolcommands = ["ExportPartInfoAndFeaturesDialogueCommand"]
|
self.toolcommands = ["ExportPartInfoAndFeaturesDialogueCommand",
|
||||||
|
"ExportGazeboModels"]
|
||||||
self.appendToolbar("AR Frames", self.framecommands)
|
self.appendToolbar("AR Frames", self.framecommands)
|
||||||
self.appendToolbar("AR Tools", self.toolcommands)
|
self.appendToolbar("AR Tools", self.toolcommands)
|
||||||
|
|
||||||
|
|
47
README.md
47
README.md
|
@ -1,5 +1,8 @@
|
||||||

|

|
||||||
# Arbench
|
# Arbench
|
||||||
|
|
||||||
|
---___!!! USE WITH CAUTION! Plugin on heavy developement !!!___---
|
||||||
|
|
||||||
Annotation for robotics bench. A FreeCAD workbench for annotating frames of interest, exporting these w.r.t. the part frame, and exporting part information.
|
Annotation for robotics bench. A FreeCAD workbench for annotating frames of interest, exporting these w.r.t. the part frame, and exporting part information.
|
||||||
|
|
||||||
# Installation instructions
|
# Installation instructions
|
||||||
|
@ -19,6 +22,8 @@ This workbench supports versions of FreeCAD>0.16.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
|
## Export meta-data for part's feature frames
|
||||||
|
|
||||||
1. Click a small feature e.g. a circle
|
1. Click a small feature e.g. a circle
|
||||||
2. Press the feature frame creator (cone with a magnifying glass on it icon)
|
2. Press the feature frame creator (cone with a magnifying glass on it icon)
|
||||||
3. Chose type of feature to create
|
3. Chose type of feature to create
|
||||||
|
@ -29,41 +34,15 @@ This workbench supports versions of FreeCAD>0.16.
|
||||||
8. Use the json with whatever you want. E.g. [`arbench_part_publisher`](https://github.com/mahaarbo/arbench_part_publisher)
|
8. Use the json with whatever you want. E.g. [`arbench_part_publisher`](https://github.com/mahaarbo/arbench_part_publisher)
|
||||||
|
|
||||||
|
|
||||||
# Freecad to Gazebo exporter
|
## Generate part's model packages for Gazebo simulator
|
||||||
|
|
||||||
To generate SDF and URDF model from freecad assembly use python call:
|
To generate SDF model packages from FreeCAD Document just press "Gazebo Export" button in ARBench UI. It will create folder for every `Solid` part in Document (`Compound` parts currently doesn't supported) with such structure
|
||||||
|
|
||||||
```python
|
|
||||||
freecad_exporter.export_gazebo_model(freecad_assembly_file, model_destination_folder, config)
|
|
||||||
```
|
```
|
||||||
Note: Only links and joints are generated in the SDF model. To use the model with ros, use the URDF model.
|
name_of_part
|
||||||
|
├── model.sdf
|
||||||
|
├── meshes
|
||||||
|
│ └── part.dae
|
||||||
|
└── model.config
|
||||||
|
|
||||||
## Config specification
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "robot_name",
|
|
||||||
"joints_limits": {"upper": 90, "lower": -90, "effort": 10, "velocity": 5},
|
|
||||||
"transmission": {
|
|
||||||
"type": "transmission_interface/SimpleTransmission",
|
|
||||||
"hardware_interface": "hardware_interface/PositionJointInterface"
|
|
||||||
},
|
|
||||||
"joints_config": {
|
|
||||||
"type": "position_controllers/JointGroupPositionController",
|
|
||||||
"grouped": true
|
|
||||||
},
|
|
||||||
"joints_pid": {"p": 20.0, "i": 10.0, "d": 0.0, "i_clamp": 0.0},
|
|
||||||
"root_link": "base_link",
|
|
||||||
"ros_package": "humanoid_17dof_description",
|
|
||||||
"sdf_only": false,
|
|
||||||
"export": true
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
This packages will placed by default in your FreeCAD Document's folder and could be moved to gazebo model's folder for using them in sumulator.
|
||||||
**sdf_only**: Export only SDF.
|
|
||||||
|
|
||||||
**export**: Export mesh files.
|
|
||||||
|
|
||||||
## Future plans
|
|
||||||
* Extend collada exporter to export materials from assemblies.
|
|
||||||
* Create a FreeCAD workbench to interactively assign joints and export to gazebo.
|
|
||||||
* Support any valid structures of assemblies.
|
|
80
UI/icons/gazeboexport.svg
Normal file
80
UI/icons/gazeboexport.svg
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="300"
|
||||||
|
height="300"
|
||||||
|
id="svg6408"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="New document 18">
|
||||||
|
<defs
|
||||||
|
id="defs6410" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.979899"
|
||||||
|
inkscape:cx="170.62581"
|
||||||
|
inkscape:cy="178.84029"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1749"
|
||||||
|
inkscape:window-height="1203"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata6413">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-752.36218)">
|
||||||
|
<g
|
||||||
|
transform="matrix(1.7219458,0,0,1.7219458,-240.52546,490.39612)"
|
||||||
|
id="g6387">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4004"
|
||||||
|
d="m 164.74024,205.99021 1.89902,67.41531 59.81923,38.92997 0.94951,-34.65717 35.13192,-19.46498 z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 222.38424,244.55563 2.02975,1.25975 c 0.72813,0.45312 1.55275,0.67775 2.38038,0.67775 0.82574,0 1.65337,-0.22463 2.38337,-0.68113 l 2.03225,-1.26463 20.60538,12.75975 -27.51213,17.13288 c -1.32725,0.82362 -2.13288,2.27775 -2.1265,3.84512 l 0.0805,28.07713 -49.7045,-30.86425 49.8315,-30.94237 z m -54.03223,-33.54926 45.47998,28.23975 -45.47998,28.23825 0,-56.478 z m 125.63548,28.14788 c -0.004,-1.63325 -0.8925,-3.13962 -2.32625,-3.933 -1.43,-0.792 -3.17875,-0.74712 -4.5675,0.11712 l -26.73338,16.648 -20.60312,-12.75887 52.209,-32.51463 c 1.325,-0.82512 2.13125,-2.27537 2.13125,-3.83637 -0.004,-1.55763 -0.8125,-3.00838 -2.1375,-3.82963 l -62.79113,-38.88062 c -1.4575,-0.90375 -3.29824,-0.90125 -4.75487,0.002 l -62.79292,38.99137 c -1.3252,0.82175 -2.13233,2.272 -2.13233,3.83301 0,0.40574 0,72.09862 0,72.50487 0,1.56062 0.80713,3.01025 2.13233,3.8345 l 62.79292,38.98977 c 0.0307,0.019 0.0625,0.0303 0.0929,0.0478 0.0346,0.0186 0.0634,0.0449 0.0991,0.0659 0.0708,0.0376 0.1455,0.0664 0.21963,0.10254 0.0732,0.0376 0.14499,0.0737 0.22224,0.10645 0.11713,0.0474 0.23676,0.0889 0.35638,0.125 0.0709,0.0249 0.13925,0.0513 0.2115,0.0688 0.13425,0.041 0.27187,0.0625 0.40863,0.0884 0.0571,0.0117 0.11724,0.0254 0.17574,0.0288 0.19688,0.0303 0.3955,0.0439 0.59238,0.0439 l 0,0 c 0,0 0.001,0 0.002,0 0.40926,0 0.82188,-0.0576 1.21825,-0.16748 0.004,-0.004 0.006,-0.004 0.006,-0.004 0.15387,-0.041 0.30525,-0.10254 0.45412,-0.16113 0.0449,-0.0186 0.0927,-0.0298 0.13676,-0.0474 0.126,-0.0552 0.24262,-0.12891 0.36225,-0.19532 0.066,-0.0361 0.13574,-0.0649 0.20074,-0.10253 l 0.002,-0.003 c 0.003,0 0.003,0 0.003,0 l 0.12062,-0.0762 62.66538,-38.91018 c 1.33125,-0.82762 2.13625,-2.28175 2.13375,-3.84912 l -0.11,-36.32913"
|
||||||
|
id="path3115"
|
||||||
|
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
d="m 231.20499,280.76812 0.0772,25.623 53.784,-33.398 -0.075,-25.71625 -53.78625,33.49125"
|
||||||
|
id="path3117"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
d="m 172.55274,202.99313 54.23975,33.67824 54.23925,-33.77687 -54.23437,-33.58575 -54.24463,33.68438"
|
||||||
|
id="path3119"
|
||||||
|
style="fill:#f58113;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.3 KiB |
Loading…
Add table
Add a link
Reference in a new issue