Roadmap¶
This page tracks planned improvements, roughly ordered by priority.
High Priority¶
DH Parameters → Skeleton URDF/Xacro¶
Accept a Denavit-Hartenberg
parameter table and automatically generate a kinematic skeleton - links, joints,
and correct joint origins - ready to export as .urdf or .xacro.
This bridges the gap between classical robot kinematics textbooks (where robots are described by DH tables) and ROS/Gazebo simulation (which requires URDF).
DH conventions supported
Convention |
Description |
|---|---|
Standard (Craig) |
|
Modified (MDH) |
|
Input format
from urdfgenpy.dh import DHTable, DHRow, DHConvention
# UR5: Standard DH Parameters
table = DHTable(convention=DHConvention.MODIFIED, rows=[
# joint a alpha d theta_offset joint_type
DHRow( 1, 0.0, pi/2, 0.089159, 0.0, "revolute"),
DHRow( 2, -0.425, 0.0, 0.0, 0.0, "revolute"),
DHRow( 3, -0.39225, 0.0, 0.0, 0.0, "revolute"),
DHRow( 4, 0.0, pi/2, 0.10915, 0.0, "revolute"),
DHRow( 5, 0.0, -pi/2, 0.09465, 0.0, "revolute"),
DHRow( 6, 0.0, 0.0, 0.0823, 0.0, "revolute"),
])
Output
robot = table.to_robot(
name="ur5",
link_mass=1.0, # uniform mass per link (kg); override per-link later
link_geometry="cylinder", # placeholder visual geometry
)
robot.save("ur5_skeleton.urdf")
robot.save("ur5_skeleton.xacro")
robot.print_tree()
The generated skeleton uses placeholder Cylinder geometry (radius=0.04,
length derived from the DH d/a parameters) and uniform mass so the file is
immediately valid and loadable in RViz / Gazebo without requiring real mesh
files. Users replace geometry and mass values incrementally.
Joint origin derivation
Each <origin> is computed from the homogeneous transform:
T_i = Rot_x(α_{i-1}) · Trans_x(a_{i-1}) · Trans_z(d_i) · Rot_z(θ_i)
The xyz/rpy of <origin> are the translation and ZYX Euler angles extracted
from T_i, matching exactly what URDF expects.
Implementation plan
src/urdfgenpy/dh.py-DHRow,DHTable,DHConventiondataclassesPure
math/ no numpy dependency for the 4×4 matrix arithmeticNew test file
tests/test_dh.py- verify known robots (Panda, UR5) against published DH tablesDocs page
docs/source/dh_guide.mdwith a worked example
URDF Parser (round-trip import)¶
Currently urdfgenpy is write-only. Adding a parser would let users load an
existing .urdf file into the Python object model, modify it, and re-export.
Scope
Robot.from_urdf(path)/Robot.from_urdf_string(xml)class methodsParse all elements already supported by the exporter: links, joints, materials, geometry primitives, inertial, limits, dynamics, mimic
Unknown XML tags preserved as raw strings so round-trips are lossless
Design sketch
robot = Robot.from_urdf("pr2.urdf")
robot.get_link("base_link").inertial.mass = 100.0
robot.save("pr2_modified.urdf")
Model Validation¶
The builder currently only checks for duplicate names and missing
parent/child links. A robot.validate() method would surface physics
and geometry errors before they cause silent sim bugs.
Checks planned
Check |
Why it matters |
|---|---|
Inertia matrix positive-definite |
Non-PD tensors crash many physics engines |
Single kinematic root |
Multiple roots → ambiguous world frame |
No kinematic loops |
URDF is a tree; loops require |
Joint axis is a unit vector |
Zero or unnormalised axis silently misbehaves |
|
Required by ROS controllers |
Mass > 0 for all dynamic links |
Zero mass -> infinite acceleration |
Design sketch
errors = robot.validate() # returns list[ValidationError]
robot.validate(strict=True) # raises on first error
Medium Priority¶
SDF Export (Gazebo)¶
Simulation Description Format is the native format for Gazebo. Key differences from URDF:
World and model are top-level concepts (not just
<robot>)Inertia is expressed relative to the link frame, not a separate
<inertial>originJoints reference link names with
<parent>/<child>elements
Scope
robot.to_sdf()androbot.save("model.sdf")SDFexporter class mirroringURDFExporterSupport SDF 1.9+
Mesh-Based Inertia¶
Inertial.from_geometry() works for primitives only. For real robots the
collision mesh is an .stl or .obj, and computing inertia requires
integrating over the mesh volume.
Plan
Optional dependency:
numpy-stl/trimeshInertial.from_mesh(filename, density)- volumetric integrationGraceful
ImportErrorif the optional dep is absent
inertial = Inertial.from_mesh("arm.stl", density=1200.0) # kg/m³
Sensor Elements¶
Robots in simulation need sensor tags. URDF supports sensors via Gazebo
plugins, but the tags themselves (e.g. <gazebo>, <sensor>) are not part
of the core URDF spec and are currently not generated.
Scope
Sensorbase class with subclassesCamera,Lidar,Imu,ForceTorqueLink.add_sensor(sensor)- appends<gazebo>extension blockXacro exporter emits the sensor macros alongside the link
from urdfgenpy.sensors import Camera
cam = Camera(name="head_cam", fps=30, width=640, height=480,
fov=1.047, origin=Origin(xyz=(0.05, 0, 0)))
head_tilt_link.add_sensor(cam)
Low Priority¶
ROS 2 Package Scaffolding¶
When building a new robot from scratch, the user also needs a ROS 2 package directory. A CLI command could generate the full scaffold:
urdfgenpy init my_robot_description
Generated structure:
my_robot_description/
├── urdf/ # .urdf / .xacro files go here
├── meshes/
├── launch/
├── package.xml
├── CMakeLists.txt
└── setup.py # if ament_python
Implementation: click-based CLI entry point added to pyproject.toml
under [project.scripts].
Contributing¶
To propose a new feature or pick up an item from this roadmap, open an issue at the Bug Tracker and reference this page.