Eight mistakes between CAD and URDF, and how to catch them before Gazebo does
A robot that misbehaves after it leaves CAD usually has one of eight problems: an inertia no real object could have, masses from a default density, a link with no inertia that Gazebo leaves out, meshes and joint frames that do not line up, joint axes and zeros that differ from the model, a closed loop forced into a tree, a slider without limits, or meshes too heavy for the simulator. Each one can be found before the robot reaches a simulator, with check_urdf, Gazebo’s own URDF reader or MuJoCo’s compiler, and most of them cannot happen when the URDF is written from the model rather than by hand. Below, each mistake with what it looks like, why it happens, how to check for it, and what ProtoJoint’s robot export does about it.
The error messages quoted here are the real ones, from urdfdom 4.0.0 and Gazebo Harmonic on ROS 2 Jazzy, and from MuJoCo 3.17.
1. An inertia no object could have
What you see. MuJoCo refuses the model:
Error: inertia must satisfy A + B >= C; use 'balanceinertia' to fix
Why. The inertia was typed in by hand, taken from a box around the part, computed about the link’s origin instead of its centre of mass, given products of inertia with the wrong sign, or left in the CAD program’s units: kg·mm² is a million times kg·m².
How to check. The three principal moments of inertia must be positive, and none may be larger than the other two added together. Every real body passes: a flat plate meets the limit exactly and a thin rod comes close to it. MuJoCo applies the test when it loads a model. Its balanceinertia option lets the model load by changing the inertia, which hides the mistake rather than fixing it.
In ProtoJoint. Each link’s inertia is computed from its solids, about its centre of mass, in kg·m², with URDF’s sign for the products, and checked before the files are written. One that fails stops the export: its principal moments of inertia break the triangle inequality, which no distribution of mass can do.
2. Masses from a default density
What you see. The masses in the URDF add up to something other than the robot on the scale, and every inertia is off by the same factor.
Why. A part exported without a material gets a default density, such as that of water, 1000 kg/m³. PLA is 1240 kg/m³, aluminium 6061 is 2700 and steel 7850, so that default makes a PLA part a fifth too light and a steel one nearly eight times too light. A 3D-printed part that is not printed solid weighs less than its material says, too.
How to check. Add up the link masses in the URDF and compare them with the real robot, or with the parts’ datasheets.
In ProtoJoint. Each part takes its density from its Physical material, chosen from the library or typed in. A part without one is weighed at 1000 kg/m³, and the check names it: has no material: weighed at 1000 kg/m³. Select the part and choose its material for a true mass and inertia.
Servos, motors and boards from the parts library come with the mass their makers give. For a printed part with sparse infill, weigh it and type the mass into the part’s Given mass: its inertia is its shape’s, scaled to that mass.
3. A link with no inertia, which Gazebo leaves out
What you see. RViz shows the whole robot. In Gazebo a link is missing, and so is everything that hangs from it.
Why. URDF allows a link with no <inertial>, and tools that only draw the robot treat it as a frame. Gazebo’s URDF reader merges links joined by fixed joints, then leaves out a link that still has no inertia, with the joint that moves it and everything beyond it, and carries on. For a chain base → j1 → mid → j2 → tip where mid has no inertia, the reader prints this and keeps only base:
urdf2sdf: link[mid] has no <inertial> block defined. Please ensure this link has a valid mass to prevent any missing links or joints in the resulting SDF model.
urdf2sdf: parent joint[j1] ignored.
urdf2sdf: [1] child joints ignored.
urdf2sdf: [1] child links ignored.
How to check. Run Gazebo’s reader yourself and look for ignored
in what it prints:
gz sdf -p robot.urdf > robot.sdf
In ProtoJoint. A part with no solid that a joint moves stops the ROS 2 package, MJCF and SDF exports: has no solid but a joint moves it: a simulator needs its mass. Give it a body, or join it rigidly.
A plain URDF gets a warning instead, since other tools read such a link as a frame.
4. Meshes and joint frames that do not line up
What you see. The robot looks right at its zero pose, but when a joint moves, its link turns about the wrong point, or drifts away from the link before it.
Why. A link turns about the origin of the joint that moves it. When that origin is put at the part’s own origin instead of on the joint’s axis, or the mesh is saved in other coordinates and its visual origin is adjusted by hand until the zero pose looks right, the axis ends up somewhere else.
How to check. Move every joint through its range in RViz, with the sliders of joint_state_publisher_gui, before you go to a simulator.
In ProtoJoint. Each link’s frame is its joint’s frame, on the joint axis, and its meshes are written in that frame, so every <visual> origin is zero. Drag the joints in the editor before you export: the links follow, and the limits stop them where the real joints stop.
5. Joint axes and zeros that differ from the model
What you see. A joint turns the wrong way, the robot’s home pose comes out folded, or its limits stop it in the wrong place.
Why. Each joint’s rpy was worked out by hand, its axis was given in the parent’s frame instead of the child’s, or the zero was taken wherever the model happened to be posed.
How to check. Put the robot at zero in RViz and compare it with the model, then move each joint a little in the positive direction and see that it turns the way the model’s does.
In ProtoJoint. Each joint’s origin is written at the joint’s zero, which is the design’s, and its axis as a unit vector in the child’s frame. The root link faces the way the design does, Z up. The joint values in ROS then mean what they mean in the editor, the limits carry over as they are, and the pose the robot is in when you export changes nothing.
6. A closed loop forced into a tree
What you see. A parallel gripper, a four-bar or a delta will not go into the file: one link would need two parents.
Why. A URDF is a tree, every link with one parent, and the links of a linkage form a loop.
What to do. When the loop only makes two joints move together, as two fingers or two gears do, write the follower as a <mimic> of the joint that drives it. When it is a real linkage, either leave out the joint that closes the loop, knowing that the URDF moves as an open chain, or describe the robot in MJCF, which closes loops with equality constraints.
In ProtoJoint. A motion link between two joints becomes a mimic, and the ROS 2 package’s Gazebo launch drives the follower from its leader. A loop of joints is kept in the MJCF as an equality constraint at the joint that closes it, with one joint of the loop driving it; the URDF and SDF leave that joint out, with a warning that names it.
7. A slider without limits
What you see. check_urdf and Gazebo’s reader refuse the whole file:
Error: Joint [s] is of type PRISMATIC without limits
Why. In a URDF, a prismatic joint and a revolute joint must each carry a <limit>. A revolute joint without one is refused the same way (is of type REVOLUTE but it does not specify limits
); a joint that turns all the way round is continuous.
How to check. check_urdf robot.urdf (from liburdfdom-tools) reads the file the way ROS does and prints the link tree when it parses.
In ProtoJoint. A revolute joint without limits is written as continuous. A slider without them stops the URDF and ROS 2 package exports until it has both ends: a URDF prismatic joint needs them: select the joint and set its Limits (minimum and maximum).
8. Meshes too heavy for the simulator
What you see. MuJoCo refuses an STL of more than 200,000 faces, and with it the whole model:
Error: stl_decoder: number of faces should be between 1 and 200000 in STL file
RViz and Gazebo load detailed meshes, but slowly.
Why. A mesh saved from CAD at a fine tolerance has far more triangles than a simulator needs. The collision geometry is often the same mesh, and MuJoCo collides each mesh as its convex hull, so a part that nests inside another, such as a leg between two plates of a body, overlaps it in the simulator although the parts do not touch.
In ProtoJoint. Meshes are made at the Mesh refinement chosen in the export panel, Low by default, and a mesh still over 200,000 triangles is cut into several files. In the MJCF the links collide with the floor but not with one another.
Check a URDF of your own
Two commands catch several of these in any URDF, whatever wrote it. On Ubuntu 24.04 with ROS 2 Jazzy, check_urdf comes with liburdfdom-tools and gz with Gazebo (ros-jazzy-ros-gz):
check_urdf robot.urdf # the tree, the joint types, the limits
gz sdf -p robot.urdf > robot.sdf # what Gazebo keeps: look for "ignored"
An MJCF of the robot, loaded once in MuJoCo, checks every inertia and mesh the same way. To get all three files from a model, and the ROS 2 package around them, see CAD to URDF.
Questions
Why does RViz show the robot when Gazebo does not?
RViz draws each link where TF puts it, and a link without an inertia draws like any other. Gazebo simulates the robot, so it needs the mass and inertia of every link that moves, and leaves out a link that has none.
What does ProtoJoint check before it exports a robot?
That the joints form a tree from one root, that every link with a solid has a positive mass and an inertia a real object could have, that every limit has both ends, that sliders have limits, and that a closed loop closes with every joint at zero. Errors stop the export; warnings say what the files will do differently. Robot export has the full list.
Can I use these checks on a URDF that ProtoJoint did not write?
Yes. check_urdf and gz sdf -p read any URDF.
CAD to URDF
Turn a CAD assembly into a URDF in your browser: links from parts, joints from the assembly, inertia from the exact solids. What goes in, what comes out, and a six-servo arm run in RViz, Gazebo and MuJoCo.
Robot export: URDF, MJCF, SDF and ROS 2
Turn an assembly into a robot description: links from components, joints from assembly joints, inertia from the exact solids, checked before export, as URDF, MJCF, SDF or a ROS 2 package.
A six-servo desktop arm from CAD to RViz, Gazebo and MuJoCo
Build a six-servo desktop arm in the browser, joint it on the servo axes and export URDF, SDF, MJCF and a ROS 2 package that runs in RViz, Gazebo and MuJoCo, with the model and the files to download.