rod 0.4.2.dev3__tar.gz → 0.4.2.dev7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/PKG-INFO +1 -1
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/urdf/exporter.py +120 -82
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod.egg-info/PKG-INFO +1 -1
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/tests/test_urdf_exporter.py +64 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/.gitattributes +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/.github/CODEOWNERS +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/.github/release.yml +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/.github/workflows/ci_cd.yml +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/.gitignore +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/.pre-commit-config.yaml +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/LICENSE +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/README.md +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/environment.yml +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/pixi.lock +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/pyproject.toml +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/setup.cfg +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/setup.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/__init__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/__main__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/builder/__init__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/builder/primitive_builder.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/builder/primitives.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/kinematics/__init__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/kinematics/kinematic_tree.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/kinematics/tree_transforms.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/logging.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/pretty_printer.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/__init__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/collision.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/common.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/element.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/geometry.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/joint.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/link.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/material.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/model.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/physics.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/scene.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/sdf.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/visual.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/sdf/world.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/tree/__init__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/tree/directed_tree.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/tree/tree_elements.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/urdf/__init__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/utils/__init__.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/utils/frame_convention.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/utils/gazebo.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/utils/resolve_frames.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod/utils/resolve_uris.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod.egg-info/SOURCES.txt +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod.egg-info/dependency_links.txt +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod.egg-info/requires.txt +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/src/rod.egg-info/top_level.txt +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/tests/test_meshbuilder.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/tests/test_urdf_parsing.py +0 -0
- {rod-0.4.2.dev3 → rod-0.4.2.dev7}/tests/utils_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rod
|
|
3
|
-
Version: 0.4.2.
|
|
3
|
+
Version: 0.4.2.dev7
|
|
4
4
|
Summary: The ultimate Python tool for RObot Descriptions processing.
|
|
5
5
|
Author-email: Diego Ferigo <dgferigo@gmail.com>
|
|
6
6
|
Maintainer-email: Filippo Luca Ferretti <filippo.ferretti@iit.it>, Carlotta Sartore <carlotta.sartore@iit.it>
|
|
@@ -56,6 +56,125 @@ class UrdfExporter(abc.ABC):
|
|
|
56
56
|
gazebo_preserve_fixed_joints=gazebo_preserve_fixed_joints,
|
|
57
57
|
).to_urdf_string(sdf=sdf)
|
|
58
58
|
|
|
59
|
+
@staticmethod
|
|
60
|
+
def _get_urdf_joint_type(joint: rod.Joint) -> str:
|
|
61
|
+
"""
|
|
62
|
+
Get the URDF joint type, converting revolute joints with infinite limits to continuous.
|
|
63
|
+
|
|
64
|
+
sdformat converts URDF continuous joints to SDF revolute joints with infinite limits,
|
|
65
|
+
so we need to convert them back to continuous when exporting to URDF.
|
|
66
|
+
"""
|
|
67
|
+
if (
|
|
68
|
+
joint.type == "revolute"
|
|
69
|
+
and joint.axis is not None
|
|
70
|
+
and joint.axis.limit is not None
|
|
71
|
+
and (joint.axis.limit.lower is None or np.isinf(joint.axis.limit.lower))
|
|
72
|
+
and (joint.axis.limit.upper is None or np.isinf(joint.axis.limit.upper))
|
|
73
|
+
):
|
|
74
|
+
return "continuous"
|
|
75
|
+
return joint.type
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def _joint_to_urdf_dict(joint: rod.Joint) -> dict[str, Any]:
|
|
79
|
+
"""
|
|
80
|
+
Convert a ROD joint to a URDF joint dictionary.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
joint: The ROD joint to convert.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
A dictionary representing the joint in URDF format.
|
|
87
|
+
"""
|
|
88
|
+
# Compute the corrected joint type once
|
|
89
|
+
urdf_joint_type = UrdfExporter._get_urdf_joint_type(joint)
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
"@name": joint.name,
|
|
93
|
+
"@type": urdf_joint_type,
|
|
94
|
+
"origin": {
|
|
95
|
+
"@xyz": " ".join(map(str, joint.pose.xyz)),
|
|
96
|
+
"@rpy": " ".join(map(str, joint.pose.rpy)),
|
|
97
|
+
},
|
|
98
|
+
"parent": {"@link": joint.parent},
|
|
99
|
+
"child": {"@link": joint.child},
|
|
100
|
+
**(
|
|
101
|
+
{"axis": {"@xyz": " ".join(map(str, joint.axis.xyz.xyz))}}
|
|
102
|
+
if joint.axis is not None
|
|
103
|
+
and joint.axis.xyz is not None
|
|
104
|
+
and urdf_joint_type != "fixed"
|
|
105
|
+
else {}
|
|
106
|
+
),
|
|
107
|
+
# calibration: does not have any SDF corresponding element
|
|
108
|
+
**(
|
|
109
|
+
{
|
|
110
|
+
"dynamics": {
|
|
111
|
+
**(
|
|
112
|
+
{"@damping": joint.axis.dynamics.damping}
|
|
113
|
+
if joint.axis.dynamics.damping is not None
|
|
114
|
+
else {}
|
|
115
|
+
),
|
|
116
|
+
**(
|
|
117
|
+
{"@friction": joint.axis.dynamics.friction}
|
|
118
|
+
if joint.axis.dynamics.friction is not None
|
|
119
|
+
else {}
|
|
120
|
+
),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if joint.axis is not None
|
|
124
|
+
and joint.axis.dynamics is not None
|
|
125
|
+
and {joint.axis.dynamics.damping, joint.axis.dynamics.friction}
|
|
126
|
+
!= {None}
|
|
127
|
+
and urdf_joint_type != "fixed"
|
|
128
|
+
else {}
|
|
129
|
+
),
|
|
130
|
+
**(
|
|
131
|
+
{
|
|
132
|
+
"limit": {
|
|
133
|
+
**(
|
|
134
|
+
{"@effort": joint.axis.limit.effort}
|
|
135
|
+
if joint.axis.limit.effort is not None
|
|
136
|
+
else (
|
|
137
|
+
{"@effort": np.finfo(np.float32).max}
|
|
138
|
+
if urdf_joint_type
|
|
139
|
+
in {"revolute", "prismatic", "continuous"}
|
|
140
|
+
else {}
|
|
141
|
+
)
|
|
142
|
+
),
|
|
143
|
+
**(
|
|
144
|
+
{"@velocity": joint.axis.limit.velocity}
|
|
145
|
+
if joint.axis.limit.velocity is not None
|
|
146
|
+
else (
|
|
147
|
+
{"@velocity": np.finfo(np.float32).max}
|
|
148
|
+
if urdf_joint_type
|
|
149
|
+
in {"revolute", "prismatic", "continuous"}
|
|
150
|
+
else {}
|
|
151
|
+
)
|
|
152
|
+
),
|
|
153
|
+
**(
|
|
154
|
+
{"@lower": joint.axis.limit.lower}
|
|
155
|
+
if joint.axis.limit.lower is not None
|
|
156
|
+
and not np.isinf(joint.axis.limit.lower)
|
|
157
|
+
and urdf_joint_type in {"revolute", "prismatic"}
|
|
158
|
+
else {}
|
|
159
|
+
),
|
|
160
|
+
**(
|
|
161
|
+
{"@upper": joint.axis.limit.upper}
|
|
162
|
+
if joint.axis.limit.upper is not None
|
|
163
|
+
and not np.isinf(joint.axis.limit.upper)
|
|
164
|
+
and urdf_joint_type in {"revolute", "prismatic"}
|
|
165
|
+
else {}
|
|
166
|
+
),
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
if joint.axis is not None
|
|
170
|
+
and joint.axis.limit is not None
|
|
171
|
+
and urdf_joint_type != "fixed"
|
|
172
|
+
else {}
|
|
173
|
+
),
|
|
174
|
+
# mimic: does not have any SDF corresponding element
|
|
175
|
+
# safety_controller: does not have any SDF corresponding element
|
|
176
|
+
}
|
|
177
|
+
|
|
59
178
|
def to_urdf_string(self, sdf: rod.Sdf | rod.Model) -> str:
|
|
60
179
|
"""
|
|
61
180
|
Convert an in-memory SDF model to a URDF string.
|
|
@@ -298,88 +417,7 @@ class UrdfExporter(abc.ABC):
|
|
|
298
417
|
+ extra_links_from_frames,
|
|
299
418
|
# http://wiki.ros.org/urdf/XML/joint
|
|
300
419
|
"joint": [
|
|
301
|
-
|
|
302
|
-
"@name": j.name,
|
|
303
|
-
"@type": j.type,
|
|
304
|
-
"origin": {
|
|
305
|
-
"@xyz": " ".join(map(str, j.pose.xyz)),
|
|
306
|
-
"@rpy": " ".join(map(str, j.pose.rpy)),
|
|
307
|
-
},
|
|
308
|
-
"parent": {"@link": j.parent},
|
|
309
|
-
"child": {"@link": j.child},
|
|
310
|
-
**(
|
|
311
|
-
{"axis": {"@xyz": " ".join(map(str, j.axis.xyz.xyz))}}
|
|
312
|
-
if j.axis is not None
|
|
313
|
-
and j.axis.xyz is not None
|
|
314
|
-
and j.type != "fixed"
|
|
315
|
-
else {}
|
|
316
|
-
),
|
|
317
|
-
# calibration: does not have any SDF corresponding element
|
|
318
|
-
**(
|
|
319
|
-
{
|
|
320
|
-
"dynamics": {
|
|
321
|
-
**(
|
|
322
|
-
{"@damping": j.axis.dynamics.damping}
|
|
323
|
-
if j.axis.dynamics.damping is not None
|
|
324
|
-
else {}
|
|
325
|
-
),
|
|
326
|
-
**(
|
|
327
|
-
{"@friction": j.axis.dynamics.friction}
|
|
328
|
-
if j.axis.dynamics.friction is not None
|
|
329
|
-
else {}
|
|
330
|
-
),
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
if j.axis is not None
|
|
334
|
-
and j.axis.dynamics is not None
|
|
335
|
-
and {j.axis.dynamics.damping, j.axis.dynamics.friction}
|
|
336
|
-
!= {None}
|
|
337
|
-
and j.type != "fixed"
|
|
338
|
-
else {}
|
|
339
|
-
),
|
|
340
|
-
**(
|
|
341
|
-
{
|
|
342
|
-
"limit": {
|
|
343
|
-
**(
|
|
344
|
-
{"@effort": j.axis.limit.effort}
|
|
345
|
-
if j.axis.limit.effort is not None
|
|
346
|
-
else (
|
|
347
|
-
{"@effort": np.finfo(np.float32).max}
|
|
348
|
-
if j.type in {"revolute", "prismatic"}
|
|
349
|
-
else {}
|
|
350
|
-
)
|
|
351
|
-
),
|
|
352
|
-
**(
|
|
353
|
-
{"@velocity": j.axis.limit.velocity}
|
|
354
|
-
if j.axis.limit.velocity is not None
|
|
355
|
-
else (
|
|
356
|
-
{"@velocity": np.finfo(np.float32).max}
|
|
357
|
-
if j.type in {"revolute", "prismatic"}
|
|
358
|
-
else {}
|
|
359
|
-
)
|
|
360
|
-
),
|
|
361
|
-
**(
|
|
362
|
-
{"@lower": j.axis.limit.lower}
|
|
363
|
-
if j.axis.limit.lower is not None
|
|
364
|
-
and j.type in {"revolute", "prismatic"}
|
|
365
|
-
else {}
|
|
366
|
-
),
|
|
367
|
-
**(
|
|
368
|
-
{"@upper": j.axis.limit.upper}
|
|
369
|
-
if j.axis.limit.upper is not None
|
|
370
|
-
and j.type in {"revolute", "prismatic"}
|
|
371
|
-
else {}
|
|
372
|
-
),
|
|
373
|
-
},
|
|
374
|
-
}
|
|
375
|
-
if j.axis is not None
|
|
376
|
-
and j.axis.limit is not None
|
|
377
|
-
and j.type != "fixed"
|
|
378
|
-
else {}
|
|
379
|
-
),
|
|
380
|
-
# mimic: does not have any SDF corresponding element
|
|
381
|
-
# safety_controller: does not have any SDF corresponding element
|
|
382
|
-
}
|
|
420
|
+
UrdfExporter._joint_to_urdf_dict(j)
|
|
383
421
|
for j in model.joints()
|
|
384
422
|
if j.type in UrdfExporter.SupportedSdfJointTypes
|
|
385
423
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rod
|
|
3
|
-
Version: 0.4.2.
|
|
3
|
+
Version: 0.4.2.dev7
|
|
4
4
|
Summary: The ultimate Python tool for RObot Descriptions processing.
|
|
5
5
|
Author-email: Diego Ferigo <dgferigo@gmail.com>
|
|
6
6
|
Maintainer-email: Filippo Luca Ferretti <filippo.ferretti@iit.it>, Carlotta Sartore <carlotta.sartore@iit.it>
|
|
@@ -124,3 +124,67 @@ def test_urdf_exporter(robot: Robot) -> None:
|
|
|
124
124
|
)
|
|
125
125
|
|
|
126
126
|
assert locked_inertia_exported == pytest.approx(locked_inertia_original, abs=1e-6)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_continuous_joint_urdf_roundtrip() -> None:
|
|
130
|
+
"""Test that continuous joints in URDF are preserved through SDF conversion."""
|
|
131
|
+
import xmltodict
|
|
132
|
+
|
|
133
|
+
# Create a URDF with a continuous joint
|
|
134
|
+
urdf_string = """<?xml version="1.0" encoding="utf-8"?>
|
|
135
|
+
<robot name="test_continuous">
|
|
136
|
+
<link name="base_link">
|
|
137
|
+
<inertial>
|
|
138
|
+
<origin xyz="0 0 0" rpy="0 0 0"/>
|
|
139
|
+
<mass value="1.0"/>
|
|
140
|
+
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
|
|
141
|
+
</inertial>
|
|
142
|
+
</link>
|
|
143
|
+
<link name="rotating_link">
|
|
144
|
+
<inertial>
|
|
145
|
+
<origin xyz="0 0 0" rpy="0 0 0"/>
|
|
146
|
+
<mass value="0.5"/>
|
|
147
|
+
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
|
|
148
|
+
</inertial>
|
|
149
|
+
</link>
|
|
150
|
+
<joint name="continuous_joint" type="continuous">
|
|
151
|
+
<origin xyz="0 0 0" rpy="0 0 0"/>
|
|
152
|
+
<parent link="base_link"/>
|
|
153
|
+
<child link="rotating_link"/>
|
|
154
|
+
<axis xyz="0 0 1"/>
|
|
155
|
+
<limit effort="100.0" velocity="10.0"/>
|
|
156
|
+
</joint>
|
|
157
|
+
</robot>
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
# Load the URDF (it gets converted to SDF internally by sdformat)
|
|
161
|
+
sdf = rod.Sdf.load(sdf=urdf_string, is_urdf=True)
|
|
162
|
+
|
|
163
|
+
# Export back to URDF
|
|
164
|
+
exporter = rod.urdf.exporter.UrdfExporter(pretty=True)
|
|
165
|
+
exported_urdf_string = exporter.to_urdf_string(sdf=sdf)
|
|
166
|
+
|
|
167
|
+
# Parse the exported URDF to check the joint properties
|
|
168
|
+
urdf_dict = xmltodict.parse(exported_urdf_string)
|
|
169
|
+
joint = urdf_dict["robot"]["joint"]
|
|
170
|
+
|
|
171
|
+
# Verify the joint type is continuous (not revolute)
|
|
172
|
+
assert (
|
|
173
|
+
joint["@type"] == "continuous"
|
|
174
|
+
), f"Expected joint type 'continuous' after roundtrip, got '{joint['@type']}'"
|
|
175
|
+
|
|
176
|
+
# Verify NO upper/lower limits are present
|
|
177
|
+
assert (
|
|
178
|
+
"@upper" not in joint["limit"]
|
|
179
|
+
), "Continuous joint should NOT have an upper position limit after roundtrip"
|
|
180
|
+
assert (
|
|
181
|
+
"@lower" not in joint["limit"]
|
|
182
|
+
), "Continuous joint should NOT have a lower position limit after roundtrip"
|
|
183
|
+
|
|
184
|
+
# Verify effort and velocity limits are still present
|
|
185
|
+
assert (
|
|
186
|
+
"@effort" in joint["limit"]
|
|
187
|
+
), "Continuous joint should still have an effort limit after roundtrip"
|
|
188
|
+
assert (
|
|
189
|
+
"@velocity" in joint["limit"]
|
|
190
|
+
), "Continuous joint should still have a velocity limit after roundtrip"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|