A Python URDF parser for robot-specific dynamics and code generation. It returns
a Robot with joint topology, spatial inertias, symbolic transforms, and explicit
configuration/velocity indexing. Active development lives at
A2R-Lab/URDFParser; the
robot-acceleration repository
is the archival implementation associated with the original GRiD work.
from URDFParser import URDFParser
parser = URDFParser()
robot = parser.parse(urdf_filepath, floating_base = False, joint_ordering = "pinocchio_order")Where joint_ordering controls how DFS sibling ties are resolved.
joint_ordering="pinocchio_order" # DFS with Pinocchio-style sibling sorting (default)
joint_ordering="urdf_order" # DFS preserving raw URDF sibling order
joint_ordering="alphabetical_order" # DFS sorting sibling joints by joint nameThe legacy alpha_tie_breaker argument is still accepted for backward compatibility:
alpha_tie_breaker=False # equivalent to joint_ordering="urdf_order"
alpha_tie_breaker=True # equivalent to joint_ordering="alphabetical_order"Floating-base parsing also accepts a public input convention flag:
robot = parser.parse(
urdf_filepath,
floating_base=True,
floating_base_convention="pinocchio",
)Supported values are:
floating_base_convention="pinocchio" # default: q = [x, y, z, qx, qy, qz, qw], v = [vx, vy, vz, wx, wy, wz]
floating_base_convention="legacy" # legacy public input/output order: q = [x, y, z, qw, qx, qy, qz], v = [wx, wy, wz, vx, vy, vz]Internally the parser and downstream dynamics code normalize floating-base states into the Pinocchio-style convention so generated code and reference algorithms stay consistent under the hood.
Revolute, continuous, prismatic, fixed, helical/screw, planar, translation
(alias cartesian), and spherical
joints are supported, as are mimic joints. An arbitrary/skew <axis> (a
non-cardinal direction) is parsed into a dense 6-vector motion subspace S; such
joints (and helical joints, whose S is intrinsically coupled) need downstream
algorithms that support dense motion subspaces. Parser support does not by itself
guarantee support in every GRiD kernel.
Fixed joints are merged into their parent, including transformed inertias.
Planar and translation joints expand into chains of scalar joints with synthetic
links. Mimic joints retain their bodies but share an independent driver's state
coordinates. Joint IDs therefore need not equal position or velocity indices.
Use get_joint_index_q(jid) and get_joint_index_v(jid) for indexing.
Helical / screw joints (<joint type="helical"> or type="screw") are a
1-DOF (NQ=NV=1) extension: a single coordinate θ drives coupled rotation about
and translation along the SAME axis, with motion subspace S = [axis; pitch·axis].
URDF has no native helical type, so the screw pitch is carried as a custom
pitch attribute on <axis>:
<joint type="helical"> <!-- or type="screw" -->
<axis xyz="0 0 1" pitch="0.05"/> <!-- translation = pitch · θ -->
</joint>The convention is pitch in meters / radian (translation = pitch · angle),
matching Pinocchio's JointModelHelical. Closed kinematic loops are unsupported.
Keep the checkout named URDFParser, with its parent on Python's import path
(for example, run Python from that parent directory). This repository is a
source package, not a pip install . distribution. From the checkout:
There are 4 required runtime packages beautifulsoup4, lxml, numpy, sympy which can be automatically installed by running:
pip3 install -r requirements.txt(lxml is never imported directly — it is the backend BeautifulSoup(..., "xml")
uses, so it is a real runtime dependency.)
The tests/ suite runs standalone with the dev requirements:
pip3 install -r requirements-dev.txt
python3 -m pytest tests -qtests/conftest.py puts the package's parent on sys.path, so the checkout
directory must be named exactly URDFParser. The dynamics-facing tests
additionally validate against Pinocchio and the sibling RBDReference
package: check it out NEXT TO this repo (directory named exactly
RBDReference, common parent on sys.path). The pin<4 / cmeel-eigen /
cmeel-urdfdom<5 pins in requirements-dev.txt are load-bearing — see the
comments there. CI (.github/workflows/ci.yml) runs exactly this shape.
URDFParser().parse(path, floating_base=..., strict_inertial=...)—strict_inertial=Truerejects degenerate/missing inertials on moving bodies asURDFParseError; root/base and synthetic dummy links are exempt. The lenient default permits missing inertials and warns. Use strict mode when preparing dynamics inputs.- Parse failures raise
URDFParseError, including missing files, malformed model fields, and invalid options. They no longer silently returnNone. Wrapped failures preserve the original exception as__cause__. - Typed exceptions live in
errors.py:URDFParseError,UnsupportedJointTypeError,MimicResolutionError(chained mimics are flattened at resolve time; cycles raise). - Joint limits metadata:
get_joint_limits_by_id,get_velocity_limit_by_id,get_effort_limit_by_id(Robot API), from the URDF<limit>tags. - Spherical helpers:
joint_is_spherical(jid),robot_has_spherical(). A spherical joint is a second source ofNQ != NV(quaternion position, 3-wide tangent) in addition to the floating base. get_origin_params_ordered_by_id()exposes the per-joint[x,y,z,r,p,y]origin table (runtime-transform workflows downstream).
The main API is as follows where XXX can be replaced by:
- joint: a joint object (see API below)
- link: a link object (see API below)
- Xmat: a symbolic spatial transform; scalar joints have one coordinate, while quaternion joints use coordinate blocks (also 4x4 homogeneous variants and derivatives)
- Xmat_Func: a callable numerical transform; use the joint's coordinate block rather than assuming every joint takes a scalar
- Imat: a numpy 6x6 inertia matrix
- S: a motion subspace (6-vector for a scalar joint, 6-by-DoF for a multi-DoF joint), with spatial rows ordered angular then linear
# A single object by its ID or by its name as defined in the URDF
get_XXX_by_id(lid) # jid for joints
get_XXX_by_name(name)
# A list of objects at a numeric BFS level (plural family name)
get_XXXs_by_bfs_level(level) # joint/link/Xmat/Xmat_Func/Imat families
get_S_by_bfs_level(level) # the motion-subspace family uses singular S
# A list of the object ordered by their IDs or by their names as defined in the URDF
# Note: The base link/inertia exists at index -1 and so will appear at the beginning of the list
get_XXXs_ordered_by_id(reverse = False)
get_XXXs_ordered_by_name(reverse = False)
# A dictionary of objects by their ID or by their name as defined in the URDF
# Note: The base link/inertia exists at index -1
get_XXXs_dict_by_id()
get_XXXs_dict_by_name()The API also includes the following functions:
# get the robot name
get_name()
# get the robot type (if applicable)
is_serial_chain()
# get the number of positions and velocities in the robot state as well as numbers of links and joints
# NQ = len(q); NV = len(qd) = len(qdd) = len(generalized_force).
# With default quaternion coordinates, NQ = NV + one per free-flyer/spherical joint.
# Fixed-base scalar-joint robots have NQ == NV; fixed-base spherical robots do not.
# Joint/body counts are topology sizes, not state-vector widths.
get_num_pos()
get_num_vel()
get_num_bodies() # effective links, excluding the world/base sentinel
get_num_joints()
get_num_links()
get_num_links_effective() # num_links - 1; includes a floating physical base
# get the max bfs_level
get_max_bfs_level()
# get the IDs at a given bfs level and the bfs level for a given id
get_ids_by_bfs_level(level)
get_bfs_level_by_id(jid)
# get the ID of the parent(s) of a given link(s) by id
get_parent_id(lid)
get_parent_ids(lids)
get_unique_parent_ids(lids) # remove duplicates
# get the full list of parents ordered by id
get_parent_id_array()
# test if there is a repeated parent by ids
has_repeated_parents(jids)
# get the subtree IDs for a given id and total count and test if in a subtree
get_subtree_by_id(jid)
get_total_subtree_count()
get_is_in_subtree_of(jid,jid_of)
# get the ancestor IDs for a given id and total count and test if an ancestor
get_ancestors_by_id(jid)
get_total_ancestor_count()
get_is_ancestor_of(jid,jid_of)
# get all joints that have parent link name as the parent or child link name as the child
get_joints_by_parent_name(parent_name)
get_joints_by_child_name(child_name)
# get the joint that has parent link name as the parent and child link name as the child
get_joint_by_parent_child_name(parent_name,child_name)
# see if the following joints have the same S (useful for codegen)
are_Ss_identical(jids)# get the name, id, and bfs of the joint
get_name()
get_id()
get_bfs_id()
get_bfs_level()
# get the parent and child link name
get_parent()
get_child()
# get the Xmat or Xmat_Func for this joint as defined above
# also get the 4x4 homogenous transformation matrix (and derivative and hessian)
get_transformation_matrix()
get_transformation_matrix_function()
get_transformation_matrix_hom()
get_transformation_matrix_hom_function()
get_dtransformation_matrix_hom()
get_dtransformation_matrix_hom_function()
get_d2transformation_matrix_hom()
get_d2transformation_matrix_hom_function()
# get the S for this joint as defined above
get_joint_subspace()
# get the velocity damping coefficent for this joint
get_damping()# get the name, id, and bfs of the link
get_name()
get_id()
get_bfs_id()
get_bfs_level()
# get the link's spatial inertia matrix
get_spatial_inertia()