FixTheRobot Explore Robotics / Systems Engineering / Engineering Guides / Module 15

Engineering Guides · Module 15

Preparing and validating a robot description

Simulation, TF and planning all read the robot description. Most strange behaviour in simulation starts with a structural error in it.

  • Intermediate
  • Module 15: Simulation-to-Real Integration
  • Written for ROS 2 Jazzy where ROS 2 is used
  • Sources checked 2026-11-06

Assumptions: Numbers in the examples are illustrative and the code is executed by the site’s tests. Facts taken from a cited source are marked; other statements are general engineering knowledge and must be confirmed on the datasheet of the part you use.

1. Engineering problem

The robot description defines the links, joints, geometry and inertia used by robot_state_publisher, Gazebo and MoveIt. A missing link, two roots or a link with no mass produces silent failures: no TF, a robot that falls through the floor, or a planner with the wrong geometry.

2. When is this needed?

  • Starting a new robot or adding a sensor.
  • Before simulation or motion planning.
  • When TF or physics behaves strangely.

3. Selection and design decisions

The ROS 2 Jazzy URDF tutorials (read 2026-11-06) cover a visual model, a movable model with joints, physical and collision properties, Xacro for removing repetition, and publishing the model with robot_state_publisher. A URDF is a tree: exactly one root link and one parent joint per link. Moving links need mass and inertia for a physics engine; the inertia tensor must satisfy the triangle inequality on its principal moments.

Overview
CheckWhyTypical error
One root linkTF is a tree with one rootA forgotten joint leaves a second root
Joint endpoints existrobot_state_publisher needs both framesA typo in a link name
One parent per linkA link cannot hang from two jointsA copy-paste joint
Inertial block on moving linksThe physics engine needs mass and inertiaZero or missing mass
Collision geometry simpleFast and stable simulationUsing the visual mesh for collision
Units and axesMetres, kilograms, REP 103 axesMillimetres in a metre file

4. Advantages and disadvantages

  • Xacro macros remove duplication and add a layer to debug; expand and check the result.
  • Detailed collision meshes are accurate and slow; primitives are fast and approximate.
  • One description for simulation and hardware avoids drift between them, with simulation-only parts in conditionals.

5. Requirements and constraints

  • Names are unique and follow the frame conventions in the interface document.
  • Inertial values come from CAD or measurement, not guesses.
  • The same file (expanded from Xacro) feeds robot_state_publisher, Gazebo and MoveIt.

6. Architecture

Xacro ─▶ URDF ─┬▶ robot_state_publisher ─▶ /tf_static and /tf
              ├▶ Gazebo (spawn)
              └▶ MoveIt (planning model)

7. Implementation

  1. Write or generate the Xacro and expand it to URDF.
  2. Run the structural checker (below) and fix every finding.
  3. Check the tree with a TF viewer and the model in RViz.
  4. Check mass and inertia against CAD.
  5. Spawn in simulation and confirm it rests stably.
import xml.etree.ElementTree as ET
URDF = """<robot name="demo">
  <link name="base_link"><inertial><mass value="10"/><inertia ixx="0.4" iyy="0.4" izz="0.5" ixy="0" ixz="0" iyz="0"/></inertial></link>
  <link name="wheel_l"><inertial><mass value="1"/><inertia ixx="0.01" iyy="0.01" izz="0.02" ixy="0" ixz="0" iyz="0"/></inertial></link>
  <link name="wheel_r"/>
  <link name="lidar"/>
  <link name="orphan"/>
  <joint name="jl" type="continuous"><parent link="base_link"/><child link="wheel_l"/></joint>
  <joint name="jr" type="continuous"><parent link="base_link"/><child link="wheel_x"/></joint>
  <joint name="jlidar" type="fixed"><parent link="base_link"/><child link="lidar"/></joint>
</robot>"""
root = ET.fromstring(URDF)
links = {l.get("name"): l for l in root.findall("link")}
joints = root.findall("joint")
issues = []
children = set()
for j in joints:
    p, c = j.find("parent").get("link"), j.find("child").get("link")
    for n in (p, c):
        if n not in links:
            issues.append(f"joint {j.get('name')} refers to missing link {n}")
    if c in children:
        issues.append(f"link {c} has two parent joints")
    children.add(c)
roots = [n for n in links if n not in children]
if len(roots) != 1:
    issues.append(f"expected one root link, found {len(roots)}: {roots}")
moving = {j.find("child").get("link") for j in joints if j.get("type") != "fixed"}
for n in moving & set(links):
    inert = links[n].find("inertial")
    if inert is None:
        issues.append(f"moving link {n} has no inertial block (physics engines need mass and inertia)")
    else:
        i = {k: float(v) for k, v in inert.find("inertia").attrib.items()}
        if not (i["ixx"] + i["iyy"] >= i["izz"] and i["ixx"] + i["izz"] >= i["iyy"] and i["iyy"] + i["izz"] >= i["ixx"]):
            issues.append(f"link {n}: inertia violates the triangle inequality")
print("\n".join(issues) or "no issues")

Printed (checked by the site’s tests):

joint jr refers to missing link wheel_x
expected one root link, found 3: ['base_link', 'wheel_r', 'orphan']

Reading it: the example description names a missing child link (wheel_x) in one joint and leaves two links unconnected, so the checker finds three roots instead of one. The model is an invented example, and the checker is a short stand-in for the ROS tools.

8. Real examples

  • Mobile base: base_link, two wheel links, a lidar link; checked, spawned and inspected in RViz before any controller is added.

9. Failure scenarios

  • A wheel link without inertia that makes the simulation explode.
  • Two roots, so one sensor frame is disconnected from the robot.
  • A mesh in millimetres scaled as metres.

10. Troubleshooting

  1. View the tree and look for disconnected frames.
  2. Check the inertia against CAD, and the inertia triangle inequality.
  3. Compare the visual and collision shapes in the viewer.

11. Validation

  • The checker prints the lines shown (executed by the site tests).
  • The description loads in robot_state_publisher, Gazebo and RViz without warnings.

12. Engineering checklist

13. Further references

  • ROS 2 Jazzy URDF tutorials: docs.ros.org, read 2026-11-06 (the tutorial index lists building a visual model, a movable model, physical and collision properties, Xacro, and URDF with robot_state_publisher).

Related on FixTheRobot