Repository: bulletphysics/bullet3

Issue:
Error when loading combined URDF
Hi @erwincoumans,

I would like to load a combined URDF, though running `pybullet.loadURDF("combined.urdf")` always causes the following error `pybullet.error: Cannot load URDF file.`.

The combined URDF was created with [combineUrdf.py](https://github.com/bulletphysics/bullet3/blob/master/examples/pybullet/gym/pybullet_utils/examples/combineUrdf.py)

```
import pybullet_utils.bullet_client as bc
import pybullet_utils.urdfEditor as ed
import pybullet
import pybullet_data
import time

p0 = bc.BulletClient(connection_mode=pybullet.DIRECT)
p0.setAdditionalSearchPath(pybullet_data.getDataPath())

p1 = bc.BulletClient(connection_mode=pybullet.DIRECT)
p1.setAdditionalSearchPath(pybullet_data.getDataPath())

#can also connect using different modes, GUI, SHARED_MEMORY, TCP, UDP, SHARED_MEMORY_SERVER, GUI_SERVER

husky = p1.loadURDF("husky/husky.urdf", flags=p0.URDF_USE_IMPLICIT_CYLINDER)
kuka = p0.loadURDF("kuka_iiwa/model.urdf")

ed0 = ed.UrdfEditor()
ed0.initializeFromBulletBody(husky, p1._client)
ed1 = ed.UrdfEditor()
ed1.initializeFromBulletBody(kuka, p0._client)
#ed1.saveUrdf("combined.urdf")

parentLinkIndex = 0

jointPivotXYZInParent = [0, 0, 0]
jointPivotRPYInParent = [0, 0, 0]

jointPivotXYZInChild = [0, 0, 0]
jointPivotRPYInChild = [0, 0, 0]

newjoint = ed0.joinUrdf(ed1, parentLinkIndex, jointPivotXYZInParent, jointPivotRPYInParent,
                        jointPivotXYZInChild, jointPivotRPYInChild, p0._client, p1._client)
newjoint.joint_type = p0.JOINT_FIXED

ed0.saveUrdf("combined.urdf")
```

Afterwards I try to load the model with..
```
pybullet.connect(pybullet.GUI)
model = pybullet.loadURDF("combined.urdf")
```
.. and receive the following output:

```
pybullet.error: Cannot load URDF file.
Warning: b3Error[examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp,121]:

Warning: Joint is of type REVOLUTE but it does not specify limits
Warning: b3Error[examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp,121]:

Warning: lbr_iiwa_joint_1
Warning: b3Error[examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp,121]:

Warning: joint xml is not initialized correctly
numActiveThreads = 0
stopping threads
destroy semaphore
semaphore destroyed
Thread with taskId 0 exiting
Thread TERMINATED
destroy main semaphore
main semaphore destroyed

```

Did I miss something or is there some error in the combined URDF generation? I'm grateful for any help!   




Discussion:
I can confirm the latest urdf importer fails if a revolute joint doesn't have joint limits.
For now, just do a string replace revolute to continuous.
We need to write the limits in the urdfEditor.py

Patch:
diff --git a/examples/pybullet/gym/pybullet_utils/urdfEditor.py b/examples/pybullet/gym/pybullet_utils/urdfEditor.py
index a9d9c5fa1a..3b6a9fcbc0 100644
--- a/examples/pybullet/gym/pybullet_utils/urdfEditor.py
+++ b/examples/pybullet/gym/pybullet_utils/urdfEditor.py
@@ -64,6 +64,8 @@ def __init__(self):
     self.joint_type = p.JOINT_REVOLUTE
     self.joint_lower_limit = 0
     self.joint_upper_limit = -1
+    self.joint_max_force = 0
+    self.joint_max_velocity = 0
     self.parent_name = "parentName"
     self.child_name = "childName"
     self.joint_origin_xyz = [1, 2, 3]
@@ -171,6 +173,8 @@ def initializeFromBulletBody(self, bodyUid, physicsClientId):
       urdfJoint.joint_type = jointInfo[2]
       urdfJoint.joint_lower_limit = jointInfo[8]
       urdfJoint.joint_upper_limit = jointInfo[9]
+      urdfJoint.joint_max_force = jointInfo[10]
+      urdfJoint.joint_max_velocity = jointInfo[11]
       urdfJoint.joint_axis_xyz = jointInfo[13]
       orgParentIndex = jointInfo[16]
       if (orgParentIndex < 0):
@@ -326,12 +330,14 @@ def writeJoint(self, file, urdfJoint, precision=5):
     str = '\t\t<child link=\"{}\"/>\n'.format(urdfJoint.child_name)
     file.write(str)
 
-    if urdfJoint.joint_type == p.JOINT_PRISMATIC:
-      #todo: handle limits
-      lowerLimit = -0.5
-      upperLimit = 0.5
-      str = '<limit effort="1000.0" lower="{:.{prec}f}" upper="{:.{prec}f}" velocity="0.5"/>'.format(
-          lowerLimit, upperLimit, prec=precision)
+    if jointTypeStr == "prismatic" or jointTypeStr == "revolute":
+      lowerLimit = urdfJoint.joint_lower_limit
+      upperLimit = urdfJoint.joint_upper_limit
+      maxForce = urdfJoint.joint_max_force
+      maxVelocity = urdfJoint.joint_max_velocity
+      
+      str = '\t\t<limit effort="{:.{prec}f}" lower="{:.{prec}f}" upper="{:.{prec}f}" velocity="{:.{prec}f}"/>\n'.format(
+          maxForce, lowerLimit, upperLimit, maxVelocity, prec=precision)
       file.write(str)
 
     file.write("\t\t<dynamics damping=\"1.0\" friction=\"0.0001\"/>\n")