-
Pick a layout using the table above. Single-file for variant-less robots;
package for robots with variants.
-
Create the cfg file(s). Subclass RobotCfg. Declare variant fields (enums)
if using the package layout.
-
Implement _build_defaults(self, init_dict=None). Set variant fields from
init_dict, then populate the Contract fields. Single-file template:
def _build_defaults(self, init_dict=None):
init_dict = init_dict or {}
self.uid = "MyRobot"
self.urdf_cfg = URDFCfg(components=[...])
self.control_parts = {"arm": ["JOINT[1-6]"]}
self.solver_cfg = {"arm": OPWSolverCfg(end_link_name="link6", root_link_name="base_link")}
self.drive_pros = JointDrivePropertiesCfg(stiffness={"JOINT[1-6]": 1e4})
Variant-aware template (reads version / arm_kind):
def _build_defaults(self, init_dict=None):
init_dict = init_dict or {}
self.version = MyRobotVersion(init_dict.get("version", "v1"))
self.arm_kind = MyRobotArmKind(init_dict.get("arm_kind", "default"))
...
-
Implement build_pk_serial_chain reading from _pk_urdf_path:
@property
def _pk_urdf_path(self) -> str:
return get_data_path("MyRobot/arm.urdf")
def build_pk_serial_chain(self, device=torch.device("cpu"), **kwargs):
chain = create_pk_serial_chain(
urdf_path=self._pk_urdf_path, device=device,
end_link_name="link6", root_link_name="base_link",
)
return {"arm": chain}
-
Keep from_dict as the 3-line template — do not reimplement it.
-
Add __all__ and register in embodichain/lab/sim/robots/__init__.py:
from .my_robot import MyRobotCfg
__all__ = ["MyRobotCfg"]
-
Add documentation: create docs/source/resources/robot/<name>.md and add
it to docs/source/resources/robot/index.rst.
-
Add a test stub with a __main__ smoke test + the DOF drift guard. Use
/add-test for full test scaffolding; the guard snippet is:
chains = cfg.build_pk_serial_chain()
for part, chain in chains.items():
assert len(chain.get_joint_parameter_names()) == len(cfg.control_parts[part])
-
Verify: preview-asset CLI + RobotCfg.from_dict(cfg.to_dict()) round-trip.