Files
cad-Integration/text-to-cad-main/skills/srdf/references/generator-contract.md
T
2026-07-22 13:48:46 +08:00

3.5 KiB

SRDF generator contract

Define a top-level zero-argument gen_srdf() function in a Python source file. It must return an envelope dictionary with the root SRDF XML and the owning URDF path.

import xml.etree.ElementTree as ET


def gen_srdf():
    robot = ET.Element("robot", {"name": "sample_robot"})
    group = ET.SubElement(robot, "group", {"name": "manipulator"})
    ET.SubElement(group, "chain", {"base_link": "base_link", "tip_link": "tool0"})
    return {
        "xml": robot,
        "urdf": "sample_robot.urdf",
    }

Required envelope

{
    "xml": robot_root_element_or_xml_string,
    "urdf": "relative/path/to/robot.urdf",
}

The current runtime accepts only xml and urdf fields. Do not add unsupported fields.

XML root

The XML root must be:

<robot name="...">

The SRDF robot name must match the linked URDF robot name.

URDF path

The urdf field is resolved relative to the generator source file. It must:

  • be a non-empty string;
  • use POSIX / separators;
  • be relative, not absolute;
  • end in .urdf;
  • refer to an existing file.

The CLI injects or updates:

<tcad:urdf path="..."/>

The tcad prefix is emitted with the https://text-to-cad.dev/srdf namespace. The injected path is relative from the generated .srdf location to the linked URDF. This metadata is a local SRDF convention for downstream tools; it is not a core SRDF semantic element. Readers also accept existing legacy <explorer:urdf/> metadata.

Supported target forms

python scripts/srdf path/to/source.py
python scripts/srdf path/to/source.py -o path/to/robot.srdf
python scripts/srdf a.py=out/a.srdf b.py=out/b.srdf

The generated output path is selected by the CLI. The generator should not write the .srdf file itself.

Group-state units

Group-state joint values are URDF-native values:

  • revolute and continuous joints: radians;
  • prismatic joints: meters.

Do not store degrees in SRDF. Legacy UI/protocol degree fields are compatibility aliases and must be converted by joint type.

Example with common semantic elements

import xml.etree.ElementTree as ET


def gen_srdf():
    robot = ET.Element("robot", {"name": "sample_robot"})

    ET.SubElement(
        robot,
        "virtual_joint",
        {
            "name": "fixed_base",
            "type": "fixed",
            "parent_frame": "world",
            "child_link": "base_link",
        },
    )

    arm = ET.SubElement(robot, "group", {"name": "manipulator"})
    ET.SubElement(arm, "chain", {"base_link": "base_link", "tip_link": "tool0"})

    gripper = ET.SubElement(robot, "group", {"name": "gripper"})
    ET.SubElement(gripper, "joint", {"name": "finger_joint"})

    ET.SubElement(
        robot,
        "end_effector",
        {
            "name": "gripper_eef",
            "parent_link": "tool0",
            "group": "gripper",
            "parent_group": "manipulator",
        },
    )

    home = ET.SubElement(robot, "group_state", {"name": "home", "group": "manipulator"})
    ET.SubElement(home, "joint", {"name": "shoulder_pan_joint", "value": "0.0"})

    ET.SubElement(
        robot,
        "disable_collisions",
        {"link1": "base_link", "link2": "shoulder_link", "reason": "Adjacent"},
    )

    return {"xml": robot, "urdf": "sample_robot.urdf"}

Virtual and passive joints are valid SRDF concepts. The current lightweight runtime preserves them but does not yet fully inventory or validate them; verify them with MoveIt Setup Assistant or a MoveIt smoke test.