ManipulaPy.urdf β URDF Subpackage (v1.3.2+)#
Added in version 1.3.2: New native NumPy 2.0-compatible URDF parser introduced as the
recommended alternative to the legacy urdf_processor module.
Module overview#
ManipulaPy.urdf is a self-contained, NumPy 2.0-compatible URDF
parser written specifically for ManipulaPy. It has zero hard
dependencies on third-party URDF libraries (trimesh and
pybullet are loaded lazily and only when meshes or alternative
backends are needed). The parser understands package:// URIs,
file:// URIs, ROS 1 / ROS 2 package discovery, and Xacro macro
expansion. Loading a URDF returns a URDF
object that can be converted directly into a
SerialManipulator or a
ManipulatorDynamics instance.
URDF class#
- class ManipulaPy.urdf.URDF(name='robot', links=None, joints=None, materials=None, transmissions=None, filename_handler=None)[source]#
Bases:
objectManipulaPy native URDF parser.
Optimized for robotics kinematics and dynamics workflows with direct integration to SerialManipulator and ManipulatorDynamics.
Example
>>> robot = URDF.load("robot.urdf") >>> robot.num_actuated_joints 6 >>> fk = robot.link_fk() >>> manipulator = robot.to_serial_manipulator()
- Parameters:
- classmethod load(filename, load_meshes=False, mesh_dir=None, backend='builtin')[source]#
Load URDF from file.
- Parameters:
- Returns:
URDF object
- Return type:
Note
βbuiltinβ: Native ManipulaPy parser (NumPy 2.0 compatible)
βpybulletβ: PyBullet-based parser (requires pybullet package)
- property root_link: Link#
Root link of the kinematic tree.
- property end_effector_link: Link#
Primary end effector link (first end link).
- link_fk(cfg=None, links=None, use_names=False)[source]#
Compute forward kinematics for links.
- Parameters:
- Returns:
Dict mapping links (or link names) to 4x4 transformation matrices
- Return type:
- link_fk_batch(cfgs, links=None)[source]#
Batch compute forward kinematics for multiple configurations.
Optimized vectorized implementation.
- extract_screw_axes(tip_link=None)[source]#
Extract screw axis parameters for ManipulaPy.
- Parameters:
tip_link (str | None) β Optional link name to use as end-effector. Defaults to first end link.
- Returns:
M, S_list, B_list, G_list, omega_list, r_list, joint_limits
- Return type:
Dict with keys
- to_serial_manipulator(tip_link=None)[source]#
Convert to ManipulaPy SerialManipulator.
- Parameters:
tip_link (str | None) β Optional link name to use as end-effector. Defaults to first end link.
- Returns:
SerialManipulator instance ready for IK/FK
- Return type:
- to_manipulator_dynamics()[source]#
Convert to ManipulaPy ManipulatorDynamics.
- Returns:
ManipulatorDynamics instance ready for dynamics computation
- Return type:
- animate(cfg_trajectory, loop_time=3.0, use_collision=False)[source]#
Animate robot along trajectory.
Use URDF.load() to parse a URDF (or Xacro) file from disk. The
backend argument selects the parser implementation:
"builtin"(default) β the native ManipulaPy parser. NumPy 2.0 compatible, no extra installs required."pybullet"β PyBullet-based loader. Requires the[simulation]extra (pip install ManipulaPy[simulation]).
Once loaded, call URDF.to_serial_manipulator() to obtain a
kinematics-ready SerialManipulator, or
URDF.to_manipulator_dynamics() to obtain a fully populated
ManipulatorDynamics (mass, inertia, screw axes, joint
limits):
from ManipulaPy.urdf import URDF
from ManipulaPy.ManipulaPy_data import get_robot_urdf
robot = URDF.load(get_robot_urdf("ur5"), backend="builtin")
serial = robot.to_serial_manipulator()
dynamics = robot.to_manipulator_dynamics()
PackageResolver class#
- class ManipulaPy.urdf.PackageResolver(base_path=None, package_map=None, search_paths=None, use_ros=True)[source]#
Resolve package:// URIs and relative paths for URDF resources.
Supports multiple resolution strategies: 1. Explicit package map (package_name -> path) 2. ROS package paths (via rospack or ament_index) 3. Environment-based search paths 4. Base path relative resolution
Example
>>> resolver = PackageResolver() >>> resolver.add_package("ur_description", "/opt/ros/melodic/share/ur_description") >>> resolved = resolver.resolve("package://ur_description/meshes/ur5/visual/base.dae") "/opt/ros/melodic/share/ur_description/meshes/ur5/visual/base.dae"
- Parameters:
- resolve(uri)[source]#
Resolve a URI to an absolute file path.
Handles: - package://package_name/path/to/file - file:///absolute/path - Relative paths - Absolute paths
Mesh path resolution#
When a URDF references meshes via package://<name>/<path>,
PackageResolver walks the following strategies, in order, to map
the URI to a real file on disk:
Explicit
PackageResolver.add_package()mapping β highest precedence. Once a package is pinned withadd_package(name, path), the resolver never falls through to the other strategies for that package: if the requested file does not exist under the pinned root, the original URI is returned with a warning. This is the documented escape hatch for ambiguous workspaces.Search paths β every entry in
search_pathsis tried in both package-rooted (<search_path>/<package_name>/<rel>) and flat (<search_path>/<rel>) form.ROS package discovery β only when
use_ros=True(the default). Usesament_index_python(ROS 2), thenrospkg/catkin_find(ROS 1), then any directories listed inROS_PACKAGE_PATHandAMENT_PREFIX_PATH.base_path fallback β
<base_path>/<rel>, useful when the URDF lives next to its meshes.Ancestor heuristic β checks up to three parent directory levels above
base_pathfor either<ancestor>/<package>/<rel>or<ancestor>/<rel>. This catches the commonpackage/urdf/robot.urdfandpackage/robots/model.urdflayouts.
Candidates from strategies 2-5 are deduplicated by their canonical
(symlink-resolved) path before the ambiguity check, so symlinked or
case-insensitive workspaces do not cause spurious βmultiple matchesβ
warnings. If two genuinely distinct files match a single
package:// URI, the resolver refuses to guess and emits a warning
recommending an explicit add_package() call to disambiguate.
Two security-conscious behaviours are worth noting:
..traversal segments insidepackage://name/...paths are rejected with a warning β the resolver returns the original URI rather than escaping the package root.file://URIs are parsed viaurllib.parse.urlparse()andurllib.request.url2pathname(), so Windows-stylefile:///C:/...URIs resolve correctly.
Examples#
from ManipulaPy.urdf import URDF, PackageResolver
from ManipulaPy.ManipulaPy_data import get_robot_urdf
# Default load with native parser (NumPy 2.0 compatible)
robot = URDF.load(get_robot_urdf("ur5"))
serial = robot.to_serial_manipulator()
dynamics = robot.to_manipulator_dynamics()
# Custom resolver for unbundled meshes β pass package_map through
# the parser for the same effect as resolver.add_package(...).
from ManipulaPy.urdf.parser import URDFParser
robot = URDFParser.parse_file(
"custom.urdf",
package_map={"ur_description": "/opt/ros/jazzy/share/ur_description"},
load_meshes=True,
)
# Or build a resolver directly for advanced use:
resolver = PackageResolver()
resolver.add_package("ur_description", "/opt/ros/jazzy/share/ur_description")
resolved = resolver.resolve("package://ur_description/meshes/ur5/visual/base.dae")
# Use PyBullet's loader when simulation interoperability is needed
robot = URDF.load("custom.urdf", backend="pybullet")
See also#
URDF Processor API Reference β legacy
URDFToSerialManipulatorAPI (still supported, but the native parser is recommended for new code).