Skip to content

Commit 60421fe

Browse files
committed
export urdf documentation
1 parent 3a4a5e3 commit 60421fe

File tree

6 files changed

+211
-12
lines changed

6 files changed

+211
-12
lines changed
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [Using RViz for Turtlebot](#using-rviz-for-turtlebot)
1111
- [Running Universal Robot UR10 Arm Test Scene with MoveIt!](#running-universal-robot-ur10-arm-test-scene-with-moveit)
1212
- [Running Image Segmentation Test](#running-image-segmentation-test)
13+
- [Export URDF](#export-urdf)
1314

1415
ZeroSim is a robotics simulation engine built on the easy to use [Unity 3D](https://unity.com/) development platform and the power of the [Robotics Operating System (ROS)](https://www.ros.org/). ZeroSim is designed for ease of use and rapid development of all sorts of robotics and simulation -- from warehouses and industrial settings, to farming and outdoors -- from robotic arms to ground and drone based mobile robots.
1516

@@ -55,9 +56,11 @@ ZeroSim provides a multitude of tools for building robots and environments in Un
5556
* Machine Learning tools:
5657
* Image Segmentation for training semantic segmentation algorithms.
5758

59+
* URDF Export
60+
5861
* **COMING SOON:**
5962
* More complete documentation.
60-
* URDF import & export.
63+
* URDF import.
6164
* Finish Docker integration with Unity. (Currently incomplete)
6265
* Secure communications via WebSockets.
6366
* Support for other Physics engines such as Bullet or Havok.
@@ -186,3 +189,15 @@ roslaunch zero_sim_ros basic_unity_editor.launch
186189
7. In the VNC window press the *LEFT* mouse button and select "Terminal". ![noVNC Terminal](Documentation~/images/novnc_terminal.png)
187190
8. In the new terminal run `rqt_image_view /image/segmentation_image`.
188191
9. Open up a second terminal and run `rqt_image_view /image/image_raw` ![RQT Image View Segmentation](Documentation~/images/rqt_image_view_segmentation.png)
192+
193+
194+
### Export URDF
195+
196+
1. Make sure that the ZeroSim samples are installed as outlined above.
197+
2. Open scene the `URDF_test.scene` in the ZeroSim samples.
198+
3. Select `SimpleRobotArm` in the scene hierarchy.
199+
![Select Simple Robot Arm](Documentation~/images/select_simple_robot_arm.png)
200+
4. Select `Export URDF` in the root properties view. ![Export URDF](Documentation~/images/export_urdf.png)
201+
5. Select the directory to export to.
202+
6. An excellent online URDF viewer is available: https://gkjohnson.github.io/urdf-loaders/javascript/example/index.html Just drag and drop the files exported above.
203+

Runtime/Scripts/Util/ImportExport/ZOExportOBJ.cs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,49 @@ protected void End() {
4343
_startIndex = 0;
4444
}
4545

46+
protected string MeshToString(Mesh mesh, ZOExportOBJ.Orientation orientation) {
4647

47-
protected string MeshToString(MeshFilter meshFilter, Transform transform, bool applyLocalTransform, ZOExportOBJ.Orientation orientation) {
48+
int numVertices = 0;
49+
50+
StringBuilder sb = new StringBuilder();
51+
52+
foreach (Vector3 vv in mesh.vertices) {
53+
Vector3 v = vv;
54+
numVertices++;
55+
if (orientation == Orientation.Unity) {
56+
sb.AppendLine($"v {v.x} {v.y} {v.z}");
57+
} else if (orientation == Orientation.URDF) {
58+
sb.AppendLine($"v {v.z} {v.x} {v.y}");
59+
}
60+
}
61+
sb.AppendLine();
62+
foreach (Vector3 nn in mesh.normals) {
63+
Vector3 v = nn;
64+
if (orientation == Orientation.Unity) {
65+
sb.AppendLine($"vn {v.x} {v.y} {v.z}");
66+
} else if (orientation == Orientation.URDF) {
67+
sb.AppendLine($"vn {v.z} {v.x} {v.y}");
68+
}
69+
}
70+
71+
for (int submesh = 0; submesh < mesh.subMeshCount; submesh++) {
72+
int[] triangles = mesh.GetTriangles(submesh);
73+
for (int i = 0; i < triangles.Length; i += 3) {
74+
sb.AppendLine(string.Format("f {0}//{0} {1}//{1} {2}//{2}", // only exporting position and normal, no UV
75+
triangles[i] + 1 + _startIndex,
76+
triangles[i + 1] + 1 + _startIndex,
77+
triangles[i + 2] + 1 + _startIndex));
78+
79+
}
80+
81+
}
82+
83+
_startIndex += numVertices;
84+
return sb.ToString();
85+
}
86+
87+
88+
protected string MeshFilterToString(MeshFilter meshFilter, Transform transform, bool applyLocalTransform, ZOExportOBJ.Orientation orientation) {
4889
Vector3 s = transform.localScale;
4990
Vector3 p = transform.localPosition;
5091
Quaternion r = transform.localRotation;
@@ -141,7 +182,7 @@ protected string ProcessTransform(Transform transform, bool makeSubmeshes, bool
141182

142183
MeshFilter meshFilter = transform.GetComponent<MeshFilter>();
143184
if (meshFilter != null) {
144-
meshString.Append(MeshToString(meshFilter, transform, applyLocalTransform, orientation));
185+
meshString.Append(MeshFilterToString(meshFilter, transform, applyLocalTransform, orientation));
145186
}
146187

147188
for (int i = 0; i < transform.childCount; i++) {
@@ -265,6 +306,26 @@ public void ExportToDirectory(GameObject gameObject, string directoryPath, bool
265306
}
266307

267308
}
309+
310+
public void ExportMesh(Mesh mesh, string meshPath, ZOExportOBJ.Orientation orientation) {
311+
Start();
312+
313+
StringBuilder meshString = new StringBuilder();
314+
315+
meshString.Append("#" + Path.GetFileName(meshPath)
316+
+ "\n#" + System.DateTime.Now.ToLongDateString()
317+
+ "\n#" + System.DateTime.Now.ToLongTimeString()
318+
+ "\n#-------"
319+
+ "\n\n");
320+
321+
meshString.Append(MeshToString(mesh, orientation));
322+
323+
OBJString = meshString.ToString();
324+
325+
using (StreamWriter sw = new StreamWriter(meshPath)) {
326+
sw.Write(OBJString);
327+
}
328+
}
268329
}
269330

270331
}

Runtime/Scripts/Util/ImportExport/ZOExportURDF.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ public List<Transform> VisualMeshesToExport {
5757
get { return _visualMeshesToExport; }
5858
}
5959

60-
private List<Transform> _collisionMeshesToExport = new List<Transform>();
61-
public List<Transform> CollisionMeshesToExport {
60+
private List<Mesh> _collisionMeshesToExport = new List<Mesh>();
61+
public List<Mesh> CollisionMeshesToExport {
6262
get { return _collisionMeshesToExport; }
6363
}
6464

@@ -75,9 +75,10 @@ public static void ExportToDirectory(ZOSimDocumentRoot documentRoot, string dire
7575
exportOBJ.ExportToDirectory(meshTransform.gameObject, directoryPath, true, false, ZOExportOBJ.Orientation.URDF);
7676
}
7777

78-
foreach (Transform meshTransform in exportURDF.CollisionMeshesToExport) {
78+
foreach (Mesh mesh in exportURDF.CollisionMeshesToExport) {
7979
ZOExportOBJ exportOBJ = new ZOExportOBJ();
80-
exportOBJ.ExportToDirectory(meshTransform.gameObject, directoryPath, true, false, ZOExportOBJ.Orientation.URDF);
80+
string collisionMeshFilePath = Path.Combine(directoryPath, $"{mesh.name}_collider.obj");
81+
exportOBJ.ExportMesh(mesh, collisionMeshFilePath, ZOExportOBJ.Orientation.URDF);
8182
}
8283

8384
Debug.Log($"INFO: ZOExportURDF Saved URDF: {urdfFilePath}");
@@ -322,8 +323,33 @@ protected void BuildURDFCollisions(Transform collisionTransform, XElement link,
322323
sphereCollider.center.z * sphereCollider.transform.localScale.z);
323324

324325

326+
} else if (collider.GetType() == typeof(CapsuleCollider)) {
327+
CapsuleCollider capsuleCollider = collider as CapsuleCollider;
328+
XElement cylinder = new XElement("cylinder");
329+
float radius = capsuleCollider.radius * collider.transform.localScale.x;
330+
float height = capsuleCollider.height * collider.transform.localScale.y;
331+
cylinder.SetAttributeValue("radius", radius);
332+
cylinder.SetAttributeValue("length", height);
333+
geometry.Add(cylinder);
334+
335+
center = new Vector3(capsuleCollider.center.x * capsuleCollider.transform.localScale.x,
336+
capsuleCollider.center.y * capsuleCollider.transform.localScale.y,
337+
capsuleCollider.center.z * capsuleCollider.transform.localScale.z);
338+
339+
340+
} else if (collider.GetType() == typeof(MeshCollider)) {
341+
MeshCollider meshCollider = collider as MeshCollider;
342+
_collisionMeshesToExport.Add(meshCollider.sharedMesh);
343+
344+
XElement mesh = new XElement("mesh");
345+
mesh.SetAttributeValue("filename", $"{meshCollider.sharedMesh.name}_collider.obj");
346+
Vector3 scale = collisionTransform.localScale;
347+
mesh.SetAttributeValue("scale", scale.ToXMLString());
348+
geometry.Add(mesh);
349+
325350
}
326351

352+
327353
if (geometry.HasElements) {
328354
// build origin
329355
Vector3 position = collisionTransform.localPosition + anchorOffset + center;

Samples~/ZeroSimSamples/Scenes/URDF_test.unity

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ Transform:
841841
m_LocalScale: {x: 1, y: 1, z: 1}
842842
m_Children:
843843
- {fileID: 10280180}
844-
- {fileID: 1357432954}
844+
- {fileID: 2050636604}
845845
m_Father: {fileID: 63962903}
846846
m_RootOrder: 2
847847
m_LocalEulerAnglesHint: {x: 0, y: 45, z: 0}
@@ -2985,8 +2985,12 @@ GameObject:
29852985
serializedVersion: 6
29862986
m_Component:
29872987
- component: {fileID: 1357432954}
2988+
- component: {fileID: 1357432958}
2989+
- component: {fileID: 1357432957}
2990+
- component: {fileID: 1357432956}
2991+
- component: {fileID: 1357432955}
29882992
m_Layer: 0
2989-
m_Name: collisions
2993+
m_Name: TeapotCollision
29902994
m_TagString: Untagged
29912995
m_Icon: {fileID: 0}
29922996
m_NavMeshLayer: 0
@@ -2999,13 +3003,69 @@ Transform:
29993003
m_PrefabInstance: {fileID: 0}
30003004
m_PrefabAsset: {fileID: 0}
30013005
m_GameObject: {fileID: 1357432953}
3002-
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
3006+
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
30033007
m_LocalPosition: {x: 0, y: 0, z: 0}
30043008
m_LocalScale: {x: 1, y: 1, z: 1}
30053009
m_Children: []
3006-
m_Father: {fileID: 290498957}
3007-
m_RootOrder: 1
3010+
m_Father: {fileID: 2050636604}
3011+
m_RootOrder: 0
30083012
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
3013+
--- !u!64 &1357432955
3014+
MeshCollider:
3015+
m_ObjectHideFlags: 0
3016+
m_CorrespondingSourceObject: {fileID: 0}
3017+
m_PrefabInstance: {fileID: 0}
3018+
m_PrefabAsset: {fileID: 0}
3019+
m_GameObject: {fileID: 1357432953}
3020+
m_Material: {fileID: 0}
3021+
m_IsTrigger: 0
3022+
m_Enabled: 1
3023+
serializedVersion: 4
3024+
m_Convex: 0
3025+
m_CookingOptions: 30
3026+
m_Mesh: {fileID: 8022488761877085130, guid: 17be92f99b8fb14eaa65adc226ac3fd4, type: 3}
3027+
--- !u!64 &1357432956
3028+
MeshCollider:
3029+
m_ObjectHideFlags: 0
3030+
m_CorrespondingSourceObject: {fileID: 0}
3031+
m_PrefabInstance: {fileID: 0}
3032+
m_PrefabAsset: {fileID: 0}
3033+
m_GameObject: {fileID: 1357432953}
3034+
m_Material: {fileID: 0}
3035+
m_IsTrigger: 0
3036+
m_Enabled: 1
3037+
serializedVersion: 4
3038+
m_Convex: 0
3039+
m_CookingOptions: 30
3040+
m_Mesh: {fileID: 883126711037108357, guid: 17be92f99b8fb14eaa65adc226ac3fd4, type: 3}
3041+
--- !u!64 &1357432957
3042+
MeshCollider:
3043+
m_ObjectHideFlags: 0
3044+
m_CorrespondingSourceObject: {fileID: 0}
3045+
m_PrefabInstance: {fileID: 0}
3046+
m_PrefabAsset: {fileID: 0}
3047+
m_GameObject: {fileID: 1357432953}
3048+
m_Material: {fileID: 0}
3049+
m_IsTrigger: 0
3050+
m_Enabled: 1
3051+
serializedVersion: 4
3052+
m_Convex: 0
3053+
m_CookingOptions: 30
3054+
m_Mesh: {fileID: -3111557506051347654, guid: 17be92f99b8fb14eaa65adc226ac3fd4, type: 3}
3055+
--- !u!64 &1357432958
3056+
MeshCollider:
3057+
m_ObjectHideFlags: 0
3058+
m_CorrespondingSourceObject: {fileID: 0}
3059+
m_PrefabInstance: {fileID: 0}
3060+
m_PrefabAsset: {fileID: 0}
3061+
m_GameObject: {fileID: 1357432953}
3062+
m_Material: {fileID: 0}
3063+
m_IsTrigger: 0
3064+
m_Enabled: 1
3065+
serializedVersion: 4
3066+
m_Convex: 0
3067+
m_CookingOptions: 30
3068+
m_Mesh: {fileID: 6192547391499243942, guid: 17be92f99b8fb14eaa65adc226ac3fd4, type: 3}
30093069
--- !u!21 &1360496404
30103070
Material:
30113071
serializedVersion: 6
@@ -3921,6 +3981,37 @@ PhysicMaterial:
39213981
bounciness: 0
39223982
frictionCombine: 0
39233983
bounceCombine: 0
3984+
--- !u!1 &2050636603
3985+
GameObject:
3986+
m_ObjectHideFlags: 0
3987+
m_CorrespondingSourceObject: {fileID: 0}
3988+
m_PrefabInstance: {fileID: 0}
3989+
m_PrefabAsset: {fileID: 0}
3990+
serializedVersion: 6
3991+
m_Component:
3992+
- component: {fileID: 2050636604}
3993+
m_Layer: 0
3994+
m_Name: collisions
3995+
m_TagString: Untagged
3996+
m_Icon: {fileID: 0}
3997+
m_NavMeshLayer: 0
3998+
m_StaticEditorFlags: 0
3999+
m_IsActive: 1
4000+
--- !u!4 &2050636604
4001+
Transform:
4002+
m_ObjectHideFlags: 0
4003+
m_CorrespondingSourceObject: {fileID: 0}
4004+
m_PrefabInstance: {fileID: 0}
4005+
m_PrefabAsset: {fileID: 0}
4006+
m_GameObject: {fileID: 2050636603}
4007+
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
4008+
m_LocalPosition: {x: 0, y: 0, z: 0}
4009+
m_LocalScale: {x: 1, y: 1, z: 1}
4010+
m_Children:
4011+
- {fileID: 1357432954}
4012+
m_Father: {fileID: 290498957}
4013+
m_RootOrder: 1
4014+
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
39244015
--- !u!134 &2126012772
39254016
PhysicMaterial:
39264017
m_ObjectHideFlags: 0

0 commit comments

Comments
 (0)