.urdf  →  .srdf

SRDF for MoveIt, generated from your URDF

MoveIt needs a second file beside every robot description: which joints plan together, what "home" is, how the robot is fixed to the world — and which pairs of links can never touch, so the planner can stop checking them. That last part is measured, not written. It is measured here, in your browser, over ten thousand poses.

How to get one

Open your robot in the viewer — or build it in the URDF editor — and choose Export → MoveIt · SRDF and collision matrix. The robot is built for MuJoCo, posed, checked and counted in the tab, and a ZIP comes back with four files:

  • <robot>.srdf — planning groups, a home pose for each, end effectors where the tree ends in a hand, the virtual joint, and one disable_collisions line per pair the planner may skip.
  • joint_limits.yaml — the velocity limits your URDF declares. URDF has no acceleration limits, so none are switched on; it says where to add them.
  • kinematics.yaml — the KDL solver for every chain group.
  • README.txt — what was measured, what was assumed, and which pairs deserve a second look.

What is inside an SRDF

The file below came out of the export for the site's sample quadruped, a robot built from boxes and cylinders. Four legs became four chain groups, each with a home pose inside its joint limits — the knees bent to −0.7 rad, because that is as straight as their limits allow. The base floats, so the virtual joint does. And of forty-five pairs of links, the planner may skip thirty-eight: five joined by a joint, thirty-three never touching in ten thousand poses.

quadruped.srdfOpen the robot
<?xml version="1.0" encoding="UTF-8"?>
<!--
  Semantic description of Sample quadruped, for MoveIt.
  Generated by urdfbase.com from the robot's own URDF. Groups and poses are
  starting points read off the shape of the tree; the collision matrix was
  measured over 10,000 random poses — see README.txt for how.
-->
<robot name="Sample quadruped">
  <group name="fl_leg">
    <chain base_link="base_link" tip_link="fl_calf"/>
  </group>
  <group name="fr_leg">
    <chain base_link="base_link" tip_link="fr_calf"/>
  </group>
  <group name="rl_leg">
    <chain base_link="base_link" tip_link="rl_calf"/>
  </group>
  <group name="rr_leg">
    <chain base_link="base_link" tip_link="rr_calf"/>
  </group>
  <group_state name="fl_leg_home" group="fl_leg">
    <joint name="fl_hip_roll" value="0"/>
    <joint name="fl_thigh_pitch" value="0"/>
    <joint name="fl_calf_pitch" value="-0.7"/>
  </group_state>
  <group_state name="fr_leg_home" group="fr_leg">
    <joint name="fr_hip_roll" value="0"/>
    <joint name="fr_thigh_pitch" value="0"/>
    <joint name="fr_calf_pitch" value="-0.7"/>
  </group_state>
  <group_state name="rl_leg_home" group="rl_leg">
    <joint name="rl_hip_roll" value="0"/>
    <joint name="rl_thigh_pitch" value="0"/>
    <joint name="rl_calf_pitch" value="-0.7"/>
  </group_state>
  <group_state name="rr_leg_home" group="rr_leg">
    <joint name="rr_hip_roll" value="0"/>
    <joint name="rr_thigh_pitch" value="0"/>
    <joint name="rr_calf_pitch" value="-0.7"/>
  </group_state>
  <virtual_joint name="virtual_joint" type="floating" parent_frame="world" child_link="base_link"/>
  <disable_collisions link1="base_link" link2="head" reason="Adjacent"/>
  <disable_collisions link1="fl_calf" link2="fl_thigh" reason="Adjacent"/>
  <disable_collisions link1="fr_calf" link2="fr_thigh" reason="Adjacent"/>
  <disable_collisions link1="rl_calf" link2="rl_thigh" reason="Adjacent"/>
  <disable_collisions link1="rr_calf" link2="rr_thigh" reason="Adjacent"/>
  <disable_collisions link1="base_link" link2="fl_calf" reason="Never"/>
  <disable_collisions link1="base_link" link2="fl_thigh" reason="Never"/>
  <disable_collisions link1="base_link" link2="fr_calf" reason="Never"/>
  <disable_collisions link1="base_link" link2="fr_thigh" reason="Never"/>
  <disable_collisions link1="base_link" link2="rl_calf" reason="Never"/>
  <disable_collisions link1="base_link" link2="rl_thigh" reason="Never"/>
  <disable_collisions link1="base_link" link2="rr_calf" reason="Never"/>
  <disable_collisions link1="base_link" link2="rr_thigh" reason="Never"/>
  <disable_collisions link1="fl_calf" link2="fr_calf" reason="Never"/>
  <disable_collisions link1="fl_calf" link2="fr_thigh" reason="Never"/>
  <disable_collisions link1="fl_calf" link2="head" reason="Never"/>
  <disable_collisions link1="fl_calf" link2="rr_calf" reason="Never"/>
  <disable_collisions link1="fl_calf" link2="rr_thigh" reason="Never"/>
  <disable_collisions link1="fl_thigh" link2="fr_calf" reason="Never"/>
  <disable_collisions link1="fl_thigh" link2="fr_thigh" reason="Never"/>
  <disable_collisions link1="fl_thigh" link2="head" reason="Never"/>
  <disable_collisions link1="fl_thigh" link2="rl_thigh" reason="Never"/>
  <disable_collisions link1="fl_thigh" link2="rr_calf" reason="Never"/>
  <disable_collisions link1="fl_thigh" link2="rr_thigh" reason="Never"/>
  <disable_collisions link1="fr_calf" link2="head" reason="Never"/>
  <disable_collisions link1="fr_calf" link2="rl_calf" reason="Never"/>
  <disable_collisions link1="fr_calf" link2="rl_thigh" reason="Never"/>
  <disable_collisions link1="fr_thigh" link2="head" reason="Never"/>
  <disable_collisions link1="fr_thigh" link2="rl_calf" reason="Never"/>
  <disable_collisions link1="fr_thigh" link2="rl_thigh" reason="Never"/>
  <disable_collisions link1="fr_thigh" link2="rr_thigh" reason="Never"/>
  <disable_collisions link1="head" link2="rl_calf" reason="Never"/>
  <disable_collisions link1="head" link2="rl_thigh" reason="Never"/>
  <disable_collisions link1="head" link2="rr_calf" reason="Never"/>
  <disable_collisions link1="head" link2="rr_thigh" reason="Never"/>
  <disable_collisions link1="rl_calf" link2="rr_thigh" reason="Never"/>
  <disable_collisions link1="rl_thigh" link2="rr_calf" reason="Never"/>
  <disable_collisions link1="rl_thigh" link2="rr_thigh" reason="Never"/>
</robot>

The collision matrix, measured

A motion planner checks every pair of links for contact at every point of every motion it considers. Most pairs on a real robot can never meet — the left foot and the right shoulder, the base and the first link of the arm — and every check of them is time spent proving nothing. The matrix is the list of pairs that need no check, each with its reason, in the four words the MoveIt Setup Assistant uses:

  • Adjacent — joined by a joint, and so always touching where they meet. Links fixed to each other count as one body: everything fixed to either side of a joint is adjacent across it.
  • Default — already in contact in the home pose.
  • Always — in contact in 95% of poses or more.
  • Never — in contact in none of the poses sampled.

The sampling is the Setup Assistant's: ten thousand poses, every joint drawn evenly within its limits, a mimic joint placed by the joint it follows, contact with the floor ignored. The draw is seeded, so the same robot always gives the same file.

What it found on real robots

Eight robots from the catalogue, through the export as it stands, ten thousand poses each. "Checked" is what the planner is left to test on every motion:

RobotLink pairsAdjacentNeverChecked
Universal Robots UR5e 15 5 1 9
Kinova Gen3 28 7 15 6
Franka Emika Panda 55 13 20 22 (2 to review)
SO-101 arm 21 8 4 9
Unitree Go2 78 12 42 24
Boston Dynamics Spot 36 4 2 30 (4 to review)
Unitree G1 378 26 94 258
ALOHA, two arms 153 19 4 130 (130 to review)

The legged robots are where it pays: a Go2's legs reach past each other only in a few places, and forty-two of its seventy-eight pairs never meet. A compact arm with generous collision capsules — the UR5e in the catalogue — can fold nearly every link onto another, and keeps nine of fifteen checked; a matrix that switched those off to look tidier would be a planner driving the arm into itself.

What is not switched off, and why

MuJoCo collides a mesh as its convex hull, and a hull contains the mesh. That makes Never safe whatever the shapes: hulls that never meet are meshes that never meet. It does not make Default or Always safe — hulls can overlap where the parts do not. ALOHA shows how far: as hulls, 130 of its 153 pairs touch in the home pose, and switching them off would leave the planner blind in exactly the places a two-armed robot needs to look.

So those two reasons are trusted only between primitive shapes, which MuJoCo tests exactly. Where a mesh is involved the pair stays on, MoveIt checks it against the real mesh, and the README lists it — that is the "to review" in the table. If the Setup Assistant shows such a pair really touching at home, disable it there with the reason "Default".

Groups are a starting point

Planning groups are read off the shape of the kinematic tree. One chain of moving joints is an arm, from its root to the last link on it. A chain that ends by splitting into short branches — two fingers, a jaw — becomes the arm up to the split, a hand or gripper group for the fingers, and an end effector joining them; an arm whose last joint opens a jaw stops before the jaw. A body with limbs gets a chain per limb, named for what it ends in — FR_leg, left_arm — and a wheel or a spinning sensor gets none, because nothing plans a wheel. The Setup Assistant opens the file, and every group can be changed there.

The rest of a MoveIt package — controllers, launch files — is the Setup Assistant's to write. The ros2_control configuration those controllers need comes out of the same Export menu.

Common questions

What is an SRDF file?

The Semantic Robot Description Format: the file MoveIt reads beside a URDF. The URDF says what the robot is made of; the SRDF says how to plan with it — which joints move together as a group, named poses such as home, which link is an end effector, how the robot is attached to the world, and which pairs of links never need a collision check.

Do I still need the MoveIt Setup Assistant?

For a whole MoveIt package — launch files, controller configuration, the package.xml — yes, that is what it is for. For the SRDF and its collision matrix, no: that is what the export measures. The Setup Assistant opens the result, so a group can still be renamed or split there before the package is written.

Is it safe to switch off the pairs it lists?

Adjacent pairs are joined by a joint and touch by construction. Never means not one of ten thousand random poses brought the two together, measured on convex hulls that contain the real shapes — so the real shapes cannot have met either. Pairs that touched at home or nearly always are switched off only between primitive shapes, which are tested exactly; where a mesh is involved they stay on, and the README lists them.

How many poses does it sample, and is the result repeatable?

Ten thousand, the Setup Assistant's own default, each joint drawn evenly within its limits and every mimic joint placed by the joint it follows. The draw is seeded, so the same robot gives the same file every time — a diff of two exports shows what changed in the robot, not in the dice.

Does it work for ROS 1 and ROS 2?

Yes. The SRDF format is the same in both, and so are the layouts of kinematics.yaml and joint_limits.yaml: put the three files in the config folder of your MoveIt configuration package.

Which kinematics solver does it set?

KDL, for every chain group — it works on any serial chain without anything to compile. Swap the plugin name for TRAC-IK or your own solver later; the group names stay.