0
# URDF
1
2
Loading and managing robot kinematic chains from URDF (Unified Robot Description Format) files with joint control, collision detection, and 3D visualization capabilities.
3
4
## Capabilities
5
6
### UrdfTransformManager Class
7
8
Extended TransformManager with URDF loading and joint management capabilities.
9
10
```python { .api }
11
class UrdfTransformManager(TransformManager):
12
def __init__(self, strict_check=True, check=True):
13
"""Initialize URDF-capable transformation manager."""
14
15
def load_urdf(self, urdf_xml, mesh_path=None, package_dir=None):
16
"""
17
Load robot description from URDF file.
18
19
Parameters:
20
- urdf_xml: str - URDF XML content or file path
21
- mesh_path: str, optional - Path to mesh files
22
- package_dir: str, optional - ROS package directory
23
"""
24
25
def add_joint(self, joint_name, from_frame, to_frame, child2parent, axis, limits=(float("-inf"), float("inf")), joint_type="revolute"):
26
"""
27
Add joint to robot model.
28
29
Parameters:
30
- joint_name: str - Joint identifier
31
- from_frame: str - Parent frame name
32
- to_frame: str - Child frame name
33
- child2parent: array, shape (4, 4) - Joint transformation
34
- axis: array, shape (3,) - Joint rotation axis
35
- limits: tuple - Joint angle limits (min, max)
36
- joint_type: str - Joint type ("revolute", "prismatic", "fixed")
37
"""
38
39
def set_joint(self, joint_name, value):
40
"""
41
Set joint position.
42
43
Parameters:
44
- joint_name: str - Joint identifier
45
- value: float - Joint angle in radians (or position for prismatic)
46
"""
47
48
def get_joint_limits(self, joint_name):
49
"""
50
Get joint limits.
51
52
Parameters:
53
- joint_name: str - Joint identifier
54
55
Returns:
56
- limits: tuple - (min_limit, max_limit)
57
"""
58
59
def plot_visuals(self, frame, ax=None, ax_s=1, wireframe=False, convex_hull_of_mesh=True, alpha=0.3):
60
"""Plot visual geometries of robot in specified frame."""
61
62
def plot_collision_objects(self, frame, ax=None, ax_s=1, wireframe=True, convex_hull_of_mesh=True, alpha=1.0):
63
"""Plot collision geometries of robot in specified frame."""
64
```
65
66
### Geometry Classes
67
68
Classes representing different geometric shapes in URDF files.
69
70
```python { .api }
71
class Box(Geometry):
72
def __init__(self, frame, mesh_path, package_dir, color):
73
"""Box geometry primitive."""
74
75
# Attributes:
76
# size: np.ndarray, shape (3,) - Box dimensions [width, height, depth]
77
78
class Sphere(Geometry):
79
def __init__(self, frame, mesh_path, package_dir, color):
80
"""Sphere geometry primitive."""
81
82
# Attributes:
83
# radius: float - Sphere radius
84
85
class Cylinder(Geometry):
86
def __init__(self, frame, mesh_path, package_dir, color):
87
"""Cylinder geometry primitive."""
88
89
# Attributes:
90
# radius: float - Cylinder radius
91
# length: float - Cylinder height
92
93
class Mesh(Geometry):
94
def __init__(self, frame, mesh_path, package_dir, color):
95
"""Mesh geometry from file."""
96
97
# Attributes:
98
# filename: str - Path to mesh file
99
# scale: np.ndarray, shape (3,) - Scaling factors [x, y, z]
100
```
101
102
### URDF Parsing Functions
103
104
Utility functions for parsing URDF XML content.
105
106
```python { .api }
107
def parse_urdf(urdf_xml, mesh_path=None, package_dir=None, strict_check=True):
108
"""
109
Parse URDF XML into structured data.
110
111
Parameters:
112
- urdf_xml: str - URDF XML content
113
- mesh_path: str, optional - Path to mesh files
114
- package_dir: str, optional - ROS package directory
115
- strict_check: bool - Enable strict validation
116
117
Returns:
118
- robot_name: str - Robot name from URDF
119
- links: list of Link - Parsed link objects
120
- joints: list of Joint - Parsed joint objects
121
"""
122
123
def initialize_urdf_transform_manager(tm, robot_name, links, joints):
124
"""
125
Initialize TransformManager from parsed URDF data.
126
127
Parameters:
128
- tm: UrdfTransformManager - Transform manager instance
129
- robot_name: str - Robot name
130
- links: list of Link - Link objects
131
- joints: list of Joint - Joint objects
132
"""
133
```
134
135
### Exception Class
136
137
```python { .api }
138
class UrdfException(Exception):
139
"""Exception raised during URDF parsing or processing."""
140
```
141
142
## Usage Examples
143
144
### Loading Simple Robot
145
146
```python
147
from pytransform3d.urdf import UrdfTransformManager
148
149
# Create URDF manager
150
utm = UrdfTransformManager()
151
152
# Load robot from URDF file
153
with open("robot.urdf", "r") as f:
154
urdf_content = f.read()
155
156
utm.load_urdf(urdf_content, mesh_path="./meshes/")
157
158
# Print available frames
159
print("Robot frames:", utm.nodes)
160
161
# Set joint positions
162
utm.set_joint("shoulder_joint", 0.5) # 0.5 radians
163
utm.set_joint("elbow_joint", -1.2) # -1.2 radians
164
165
# Get end effector pose
166
base_to_end = utm.get_transform("base_link", "end_effector")
167
print(f"End effector position: {base_to_end[:3, 3]}")
168
```
169
170
### Joint Control and Limits
171
172
```python
173
from pytransform3d.urdf import UrdfTransformManager
174
import numpy as np
175
176
utm = UrdfTransformManager()
177
utm.load_urdf(urdf_content)
178
179
# Get joint limits
180
shoulder_limits = utm.get_joint_limits("shoulder_joint")
181
print(f"Shoulder joint limits: {shoulder_limits}")
182
183
# Set joint within limits
184
joint_angle = np.clip(1.5, shoulder_limits[0], shoulder_limits[1])
185
utm.set_joint("shoulder_joint", joint_angle)
186
187
# Animate joint motion
188
angles = np.linspace(shoulder_limits[0], shoulder_limits[1], 50)
189
end_positions = []
190
191
for angle in angles:
192
utm.set_joint("shoulder_joint", angle)
193
T = utm.get_transform("base_link", "end_effector")
194
end_positions.append(T[:3, 3])
195
196
end_positions = np.array(end_positions)
197
print(f"End effector trajectory: {end_positions.shape}")
198
```
199
200
### Robot Visualization
201
202
```python
203
import matplotlib.pyplot as plt
204
from pytransform3d.urdf import UrdfTransformManager
205
from pytransform3d.plot_utils import make_3d_axis
206
207
# Load robot
208
utm = UrdfTransformManager()
209
utm.load_urdf(urdf_content, mesh_path="./meshes/")
210
211
# Set robot configuration
212
utm.set_joint("base_joint", 0.0)
213
utm.set_joint("shoulder_joint", 0.5)
214
utm.set_joint("elbow_joint", -1.0)
215
utm.set_joint("wrist_joint", 0.3)
216
217
# Plot robot
218
ax = make_3d_axis(ax_s=2)
219
220
# Plot coordinate frames
221
utm.plot_frames_in("base_link", ax=ax, s=0.1)
222
223
# Plot visual geometries
224
utm.plot_visuals("base_link", ax=ax, alpha=0.7)
225
226
# Plot collision objects (wireframe)
227
utm.plot_collision_objects("base_link", ax=ax, wireframe=True, alpha=0.3)
228
229
ax.set_xlabel('X')
230
ax.set_ylabel('Y')
231
ax.set_zlabel('Z')
232
plt.show()
233
```
234
235
### Multiple Robot Configurations
236
237
```python
238
import numpy as np
239
import matplotlib.pyplot as plt
240
from pytransform3d.urdf import UrdfTransformManager
241
242
utm = UrdfTransformManager()
243
utm.load_urdf(urdf_content)
244
245
# Define multiple configurations
246
configurations = [
247
{"shoulder_joint": 0.0, "elbow_joint": 0.0},
248
{"shoulder_joint": 0.5, "elbow_joint": -1.0},
249
{"shoulder_joint": 1.0, "elbow_joint": -1.5},
250
{"shoulder_joint": -0.5, "elbow_joint": 0.8},
251
]
252
253
# Plot workspace
254
fig = plt.figure(figsize=(12, 8))
255
ax = fig.add_subplot(111, projection='3d')
256
257
colors = ['red', 'green', 'blue', 'orange']
258
259
for i, config in enumerate(configurations):
260
# Set configuration
261
for joint_name, angle in config.items():
262
utm.set_joint(joint_name, angle)
263
264
# Get end effector position
265
T = utm.get_transform("base_link", "end_effector")
266
pos = T[:3, 3]
267
268
# Plot end effector
269
ax.scatter(*pos, c=colors[i], s=100, label=f'Config {i+1}')
270
271
# Plot robot frames (smaller)
272
utm.plot_frames_in("base_link", ax=ax, s=0.05)
273
274
ax.set_xlabel('X')
275
ax.set_ylabel('Y')
276
ax.set_zlabel('Z')
277
ax.legend()
278
plt.show()
279
```
280
281
### Robot Workspace Analysis
282
283
```python
284
import numpy as np
285
from pytransform3d.urdf import UrdfTransformManager
286
287
utm = UrdfTransformManager()
288
utm.load_urdf(urdf_content)
289
290
# Sample joint space
291
n_samples = 1000
292
joint_names = ["shoulder_joint", "elbow_joint", "wrist_joint"]
293
294
# Get joint limits
295
limits = {}
296
for joint_name in joint_names:
297
limits[joint_name] = utm.get_joint_limits(joint_name)
298
299
# Generate random configurations
300
workspace_points = []
301
for _ in range(n_samples):
302
config = {}
303
for joint_name in joint_names:
304
low, high = limits[joint_name]
305
if np.isfinite(low) and np.isfinite(high):
306
angle = np.random.uniform(low, high)
307
else:
308
angle = np.random.uniform(-np.pi, np.pi)
309
config[joint_name] = angle
310
utm.set_joint(joint_name, angle)
311
312
# Get end effector position
313
T = utm.get_transform("base_link", "end_effector")
314
workspace_points.append(T[:3, 3])
315
316
workspace_points = np.array(workspace_points)
317
318
# Analyze workspace
319
print(f"Workspace bounds:")
320
print(f" X: {workspace_points[:, 0].min():.3f} to {workspace_points[:, 0].max():.3f}")
321
print(f" Y: {workspace_points[:, 1].min():.3f} to {workspace_points[:, 1].max():.3f}")
322
print(f" Z: {workspace_points[:, 2].min():.3f} to {workspace_points[:, 2].max():.3f}")
323
324
# Plot workspace
325
fig = plt.figure()
326
ax = fig.add_subplot(111, projection='3d')
327
ax.scatter(workspace_points[:, 0], workspace_points[:, 1], workspace_points[:, 2],
328
alpha=0.1, s=1)
329
ax.set_xlabel('X')
330
ax.set_ylabel('Y')
331
ax.set_zlabel('Z')
332
plt.show()
333
```