CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pytransform3d

3D transformations for Python with comprehensive rotation representations, coordinate conversions, and visualization tools

Pending
Overview
Eval results
Files

urdf.mddocs/

URDF

Loading and managing robot kinematic chains from URDF (Unified Robot Description Format) files with joint control, collision detection, and 3D visualization capabilities.

Capabilities

UrdfTransformManager Class

Extended TransformManager with URDF loading and joint management capabilities.

class UrdfTransformManager(TransformManager):
    def __init__(self, strict_check=True, check=True):
        """Initialize URDF-capable transformation manager."""

    def load_urdf(self, urdf_xml, mesh_path=None, package_dir=None):
        """
        Load robot description from URDF file.
        
        Parameters:
        - urdf_xml: str - URDF XML content or file path
        - mesh_path: str, optional - Path to mesh files
        - package_dir: str, optional - ROS package directory
        """

    def add_joint(self, joint_name, from_frame, to_frame, child2parent, axis, limits=(float("-inf"), float("inf")), joint_type="revolute"):
        """
        Add joint to robot model.
        
        Parameters:
        - joint_name: str - Joint identifier
        - from_frame: str - Parent frame name
        - to_frame: str - Child frame name
        - child2parent: array, shape (4, 4) - Joint transformation
        - axis: array, shape (3,) - Joint rotation axis
        - limits: tuple - Joint angle limits (min, max)
        - joint_type: str - Joint type ("revolute", "prismatic", "fixed")
        """

    def set_joint(self, joint_name, value):
        """
        Set joint position.
        
        Parameters:
        - joint_name: str - Joint identifier
        - value: float - Joint angle in radians (or position for prismatic)
        """

    def get_joint_limits(self, joint_name):
        """
        Get joint limits.
        
        Parameters:
        - joint_name: str - Joint identifier
        
        Returns:
        - limits: tuple - (min_limit, max_limit)
        """

    def plot_visuals(self, frame, ax=None, ax_s=1, wireframe=False, convex_hull_of_mesh=True, alpha=0.3):
        """Plot visual geometries of robot in specified frame."""

    def plot_collision_objects(self, frame, ax=None, ax_s=1, wireframe=True, convex_hull_of_mesh=True, alpha=1.0):
        """Plot collision geometries of robot in specified frame."""

Geometry Classes

Classes representing different geometric shapes in URDF files.

class Box(Geometry):
    def __init__(self, frame, mesh_path, package_dir, color):
        """Box geometry primitive."""
        
    # Attributes:
    # size: np.ndarray, shape (3,) - Box dimensions [width, height, depth]

class Sphere(Geometry):
    def __init__(self, frame, mesh_path, package_dir, color):
        """Sphere geometry primitive."""
        
    # Attributes:
    # radius: float - Sphere radius

class Cylinder(Geometry):
    def __init__(self, frame, mesh_path, package_dir, color):
        """Cylinder geometry primitive."""
        
    # Attributes:
    # radius: float - Cylinder radius
    # length: float - Cylinder height

class Mesh(Geometry):
    def __init__(self, frame, mesh_path, package_dir, color):
        """Mesh geometry from file."""
        
    # Attributes:
    # filename: str - Path to mesh file
    # scale: np.ndarray, shape (3,) - Scaling factors [x, y, z]

URDF Parsing Functions

Utility functions for parsing URDF XML content.

def parse_urdf(urdf_xml, mesh_path=None, package_dir=None, strict_check=True):
    """
    Parse URDF XML into structured data.
    
    Parameters:
    - urdf_xml: str - URDF XML content
    - mesh_path: str, optional - Path to mesh files
    - package_dir: str, optional - ROS package directory
    - strict_check: bool - Enable strict validation
    
    Returns:
    - robot_name: str - Robot name from URDF
    - links: list of Link - Parsed link objects
    - joints: list of Joint - Parsed joint objects
    """

def initialize_urdf_transform_manager(tm, robot_name, links, joints):
    """
    Initialize TransformManager from parsed URDF data.
    
    Parameters:
    - tm: UrdfTransformManager - Transform manager instance
    - robot_name: str - Robot name
    - links: list of Link - Link objects
    - joints: list of Joint - Joint objects
    """

Exception Class

class UrdfException(Exception):
    """Exception raised during URDF parsing or processing."""

Usage Examples

Loading Simple Robot

from pytransform3d.urdf import UrdfTransformManager

# Create URDF manager
utm = UrdfTransformManager()

# Load robot from URDF file
with open("robot.urdf", "r") as f:
    urdf_content = f.read()

utm.load_urdf(urdf_content, mesh_path="./meshes/")

# Print available frames
print("Robot frames:", utm.nodes)

# Set joint positions
utm.set_joint("shoulder_joint", 0.5)  # 0.5 radians
utm.set_joint("elbow_joint", -1.2)    # -1.2 radians

# Get end effector pose
base_to_end = utm.get_transform("base_link", "end_effector")
print(f"End effector position: {base_to_end[:3, 3]}")

Joint Control and Limits

from pytransform3d.urdf import UrdfTransformManager
import numpy as np

utm = UrdfTransformManager()
utm.load_urdf(urdf_content)

# Get joint limits
shoulder_limits = utm.get_joint_limits("shoulder_joint")
print(f"Shoulder joint limits: {shoulder_limits}")

# Set joint within limits
joint_angle = np.clip(1.5, shoulder_limits[0], shoulder_limits[1])
utm.set_joint("shoulder_joint", joint_angle)

# Animate joint motion
angles = np.linspace(shoulder_limits[0], shoulder_limits[1], 50)
end_positions = []

for angle in angles:
    utm.set_joint("shoulder_joint", angle)
    T = utm.get_transform("base_link", "end_effector")
    end_positions.append(T[:3, 3])

end_positions = np.array(end_positions)
print(f"End effector trajectory: {end_positions.shape}")

Robot Visualization

import matplotlib.pyplot as plt
from pytransform3d.urdf import UrdfTransformManager
from pytransform3d.plot_utils import make_3d_axis

# Load robot
utm = UrdfTransformManager()
utm.load_urdf(urdf_content, mesh_path="./meshes/")

# Set robot configuration
utm.set_joint("base_joint", 0.0)
utm.set_joint("shoulder_joint", 0.5)
utm.set_joint("elbow_joint", -1.0)
utm.set_joint("wrist_joint", 0.3)

# Plot robot
ax = make_3d_axis(ax_s=2)

# Plot coordinate frames
utm.plot_frames_in("base_link", ax=ax, s=0.1)

# Plot visual geometries
utm.plot_visuals("base_link", ax=ax, alpha=0.7)

# Plot collision objects (wireframe)
utm.plot_collision_objects("base_link", ax=ax, wireframe=True, alpha=0.3)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()

Multiple Robot Configurations

import numpy as np
import matplotlib.pyplot as plt
from pytransform3d.urdf import UrdfTransformManager

utm = UrdfTransformManager()
utm.load_urdf(urdf_content)

# Define multiple configurations
configurations = [
    {"shoulder_joint": 0.0, "elbow_joint": 0.0},
    {"shoulder_joint": 0.5, "elbow_joint": -1.0},
    {"shoulder_joint": 1.0, "elbow_joint": -1.5},
    {"shoulder_joint": -0.5, "elbow_joint": 0.8},
]

# Plot workspace
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')

colors = ['red', 'green', 'blue', 'orange']

for i, config in enumerate(configurations):
    # Set configuration
    for joint_name, angle in config.items():
        utm.set_joint(joint_name, angle)
    
    # Get end effector position
    T = utm.get_transform("base_link", "end_effector")
    pos = T[:3, 3]
    
    # Plot end effector
    ax.scatter(*pos, c=colors[i], s=100, label=f'Config {i+1}')
    
    # Plot robot frames (smaller)
    utm.plot_frames_in("base_link", ax=ax, s=0.05)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.legend()
plt.show()

Robot Workspace Analysis

import numpy as np
from pytransform3d.urdf import UrdfTransformManager

utm = UrdfTransformManager()
utm.load_urdf(urdf_content)

# Sample joint space
n_samples = 1000
joint_names = ["shoulder_joint", "elbow_joint", "wrist_joint"]

# Get joint limits
limits = {}
for joint_name in joint_names:
    limits[joint_name] = utm.get_joint_limits(joint_name)

# Generate random configurations
workspace_points = []
for _ in range(n_samples):
    config = {}
    for joint_name in joint_names:
        low, high = limits[joint_name]
        if np.isfinite(low) and np.isfinite(high):
            angle = np.random.uniform(low, high)
        else:
            angle = np.random.uniform(-np.pi, np.pi)
        config[joint_name] = angle
        utm.set_joint(joint_name, angle)
    
    # Get end effector position
    T = utm.get_transform("base_link", "end_effector")
    workspace_points.append(T[:3, 3])

workspace_points = np.array(workspace_points)

# Analyze workspace
print(f"Workspace bounds:")
print(f"  X: {workspace_points[:, 0].min():.3f} to {workspace_points[:, 0].max():.3f}")
print(f"  Y: {workspace_points[:, 1].min():.3f} to {workspace_points[:, 1].max():.3f}")
print(f"  Z: {workspace_points[:, 2].min():.3f} to {workspace_points[:, 2].max():.3f}")

# Plot workspace
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(workspace_points[:, 0], workspace_points[:, 1], workspace_points[:, 2], 
          alpha=0.1, s=1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()

Install with Tessl CLI

npx tessl i tessl/pypi-pytransform3d

docs

batch-operations.md

batch-rotations.md

camera.md

coordinates.md

editor.md

index.md

plot-utils.md

rotations.md

trajectories.md

transform-manager.md

transformations.md

uncertainty.md

urdf.md

visualization.md

tile.json