first commit
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
# SDF builder helpers
|
||||
|
||||
Builder helpers are optional. They exist to reduce common LLM mistakes in XML construction, not to replace SDFormat or libsdformat.
|
||||
|
||||
## Why helpers exist
|
||||
|
||||
LLMs often make these mistakes when writing raw XML:
|
||||
|
||||
- wrong pose value length;
|
||||
- hidden degree/radian conversion;
|
||||
- missing `relative_to` or `expressed_in`;
|
||||
- zero or non-finite joint axes;
|
||||
- negative primitive dimensions;
|
||||
- mesh scale applied inconsistently;
|
||||
- inertial values copied from visuals;
|
||||
- plugin filenames invented from plausible names.
|
||||
|
||||
Helpers should make the common path explicit, typed-ish, and auditable while still returning ordinary `xml.etree.ElementTree.Element` nodes.
|
||||
|
||||
## Design constraints
|
||||
|
||||
- Standard library only.
|
||||
- No mandatory Gazebo, ROS, NumPy, lxml, CAD, or mesh dependency.
|
||||
- Return ElementTree elements.
|
||||
- Compose with raw ElementTree calls.
|
||||
- Validate local numeric shape and finiteness.
|
||||
- Do not attempt full SDFormat schema validation.
|
||||
- Do not silently invent frames, axes, inertials, plugins, or sensor parameters.
|
||||
|
||||
## Recommended helper surface
|
||||
|
||||
Names may vary to match the current runtime package, but the helper surface should stay small.
|
||||
|
||||
```python
|
||||
# XML basics
|
||||
text(parent, tag, value, attrib=None)
|
||||
fmt_float(value)
|
||||
fmt_vector(values)
|
||||
|
||||
# Poses and axes
|
||||
pose(parent, xyz=(0, 0, 0), rpy=(0, 0, 0), *, relative_to=None,
|
||||
rotation_format="euler_rpy", degrees=False)
|
||||
quat_pose(parent, xyz=(0, 0, 0), quat_xyzw=(0, 0, 0, 1), *, relative_to=None)
|
||||
axis(parent, xyz=(0, 0, 1), *, expressed_in=None)
|
||||
|
||||
# Document structure
|
||||
sdf_root(version="1.12")
|
||||
world(parent, name)
|
||||
model(parent, name, *, static=None, pose=None)
|
||||
frame(parent, name, *, attached_to=None, pose=None)
|
||||
link(parent, name, *, pose=None, inertial=None)
|
||||
joint(parent, name, joint_type, parent_link, child_link, *, pose=None,
|
||||
axis_xyz=None, axis_expressed_in=None, axis2_xyz=None, limits=None)
|
||||
|
||||
# Geometry
|
||||
visual(parent, name, *, pose=None)
|
||||
collision(parent, name, *, pose=None)
|
||||
box(parent, size_xyz)
|
||||
sphere(parent, radius)
|
||||
cylinder(parent, radius, length)
|
||||
capsule(parent, radius, length)
|
||||
mesh(parent, uri, *, scale=None)
|
||||
|
||||
# Physics and metadata
|
||||
inertial(parent, mass, inertia, *, pose=None)
|
||||
sensor(parent, name, sensor_type, *, pose=None, topic=None, update_rate=None)
|
||||
plugin(parent, name, filename, params=None)
|
||||
include(parent, uri, *, name=None, pose=None)
|
||||
```
|
||||
|
||||
## Numeric behavior
|
||||
|
||||
Helpers should reject:
|
||||
|
||||
- non-finite numbers;
|
||||
- vectors with the wrong length;
|
||||
- negative or zero primitive dimensions;
|
||||
- zero joint axes;
|
||||
- zero-mass inertials;
|
||||
- zero-norm quaternions.
|
||||
|
||||
Helpers may warn, but should not silently fix:
|
||||
|
||||
- non-unit axes;
|
||||
- non-normalized quaternions;
|
||||
- use of degrees;
|
||||
- missing `relative_to` on nontrivial poses.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from sdf.builder import axis, box, collision, joint, link, model, sdf_root, visual
|
||||
|
||||
BASE_SIZE_M = (0.4, 0.3, 0.1)
|
||||
LIFT_AXIS_Z = (0.0, 0.0, 1.0)
|
||||
|
||||
|
||||
def gen_sdf():
|
||||
sdf = sdf_root("1.12")
|
||||
robot = model(sdf, "lift_fixture", static=False)
|
||||
|
||||
base = link(robot, "base_link")
|
||||
v = visual(base, "base_visual")
|
||||
box(v, BASE_SIZE_M)
|
||||
c = collision(base, "base_collision")
|
||||
box(c, BASE_SIZE_M)
|
||||
|
||||
carriage = link(robot, "carriage_link")
|
||||
j = joint(
|
||||
robot,
|
||||
"lift_joint",
|
||||
"prismatic",
|
||||
parent_link="base_link",
|
||||
child_link="carriage_link",
|
||||
axis_xyz=LIFT_AXIS_Z,
|
||||
axis_expressed_in="base_link",
|
||||
)
|
||||
|
||||
return {
|
||||
"xml": sdf,
|
||||
"assumptions": [
|
||||
{"code": "inertials_placeholder", "message": "Inertials omitted pending measured masses."}
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## When not to use helpers
|
||||
|
||||
Raw ElementTree is acceptable when:
|
||||
|
||||
- a target simulator needs unusual extension XML;
|
||||
- a plugin has arbitrary nested configuration;
|
||||
- an existing generator already has a clear internal abstraction;
|
||||
- the user asked for a minimal XML-only source.
|
||||
|
||||
Even then, keep named constants, ledger comments, and validation.
|
||||
Reference in New Issue
Block a user