Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when adding a new robot to EmbodiChain — scaffolds a RobotCfg subclass (single-file or package layout) with the _build_defaults hook, build_pk_serial_chain, registration, docs page, and test stub.
.claude/skills/dexforce-add-robot/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 1821% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -13% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 16% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 35% | 0% |
RobotCfg subclass.Every robot config subclasses RobotCfg and overrides two hooks:
_build_defaults(self, init_dict=None) — read variant fields from init_dict,set them on self, then populate urdf_cfg / control_parts / solver_cfg / joint_drive_props / attrs.
build_pk_serial_chain(self, device=...) — return {control_part: pk.SerialChain},reading the PK URDF from a single _pk_urdf_path source.
from_dict is a 3-line template (do not reimplement):
pythoncfg = cls() cfg._build_defaults(init_dict) return merge_robot_cfg(cfg, init_dict)
to_dict / to_string / save_to_file are inherited from RobotCfg and round-trip.
| Layout | When | Files | |--------|------|-------| | Single-file | Variant-less robot | my_robot.py | | Package | Robot with variants (versions / arm kinds / hand brands) | types.py, cfg.py, optional params.py/utils.py, __init__.py |
A cfg's _build_defaults must populate:
uid (str)urdf_cfg (URDFCfg) or fpathcontrol_parts (Dictstr, Liststr]]; joint names support regex)solver_cfg (Dictstr, SolverCfg]; keys match control_parts)joint_drive_props (JointDrivePropertiesCfg)attrs (RigidBodyPhysicsCfg)build_pk_serial_chain must read from _pk_urdf_path (a property for constant-path robots, a method for variant-dependent paths). The PK chain's DOF must match the matching control_parts entry (the test stub asserts this).
package for robots with variants.
RobotCfg. Declare variant fields (enums)if using the package layout.
_build_defaults(self, init_dict=None). Set variant fields frominit_dict, then populate the Contract fields. Single-file template:
python 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.joint_drive_props = JointDrivePropertiesCfg(stiffness={"JOINT[1-6]": 1e4})
Variant-aware template (reads version / arm_kind):
python 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")) ... # then urdf_cfg / control_parts / solver_cfg / joint_drive_props / attrs
build_pk_serial_chain reading from _pk_urdf_path:python @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}
from_dict as the 3-line template — do not reimplement it.__all__ and register in embodichain/lab/sim/robots/__init__.py:python from .my_robot import MyRobotCfg __all__ = ["MyRobotCfg"]
docs/source/resources/robot/<name>.md and addit to docs/source/resources/robot/index.rst.
__main__ smoke test + the DOF drift guard. Use/add-test for full test scaffolding; the guard snippet is:
python chains = cfg.build_pk_serial_chain() for part, chain in chains.items(): assert len(chain.get_joint_parameter_names()) == len(cfg.control_parts[part])
preview-asset CLI + RobotCfg.from_dict(cfg.to_dict()) round-trip.RobotCfg owns simulation construction, control parts, solvers, and physical properties. It does not own a task, sensor suite, or Task Program skill profile.
If the user also wants the robot selectable from task deployments, invoke $add-embodiment-component after the robot config passes its own tests. That component selects the robot, owns its sensors, and optionally maps stable logical resources/endpoints/capabilities/commands into skill_profile. Do not copy those semantic declarations into the robot class.
| Mistake | Fix | |---------|-----| | all instead of __all__ | Use __all__ — lowercase all breaks import *. | | solver_cfg set twice | Set it once in _build_defaults only. | | PK URDF drifts from sim URDF | Route PK through _pk_urdf_path; keep the DOF guard. | | Reimplementing from_dict | Keep the 3-line template; put logic in _build_defaults. | | root_link_name as a tuple | It must be a str. | | Calling a nonexistent validate | Don't call methods that don't exist. |
| Parameter | Type | Description | |-----------|------|-------------| | uid | str | Unique robot identifier | | urdf_cfg | URDFCfg | URDF file and components | | control_parts | Dictstr, Liststr]] | Joint groups for control | | solver_cfg | Dictstr, SolverCfg] | IK solver configurations | | joint_drive_props | JointDrivePropertiesCfg | Joint drive, limits, friction, and armature | | attrs | RigidBodyPhysicsCfg | Rigid-body physics attributes | | variant fields | enum / str / bool | Optional subclass fields | | _pk_urdf_path | property or method → str | URDF for the FK/IK serial chain |
File locations:
embodichain/lab/sim/robots/<name>.py or embodichain/lab/sim/robots/<name>/embodichain/lab/sim/robots/__init__.pydocs/source/resources/robot/<name>.mdtests/sim/objects/test_robot_cfg.pyembodichain/lab/sim/cfg/robot.py (RobotCfg)docs/source/guides/add_robot.rst · Tutorial: docs/source/tutorial/add_robot.rst| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 20,428 | 16,623 | -19% | 1 | 1 | 0% | 3,871 | 5,602 | +45% | 0 | 0 | — |
case-02 | fail→pass | 7,551 | 29,869 | +296% | 1 | 1 | 0% | 369 | 7,089 | +1821% | 0 | 0 | — |
case-03 | fail→pass | 39,965 | 22,370 | -44% | 1 | 1 | 0% | 7,828 | 6,777 | -13% | 0 | 0 | — |
case-04 | fail→fail | 16,989 | 6,815 | -60% | 1 | 1 | 0% | 2,488 | 2,731 | +10% | 0 | 0 | — |
case-05 | pass→pass | 12,241 | 7,999 | -35% | 1 | 1 | 0% | 1,821 | 3,000 | +65% | 0 | 0 | — |
case-06 | fail→pass | 17,719 | 7,944 | -55% | 1 | 1 | 0% | 2,814 | 3,277 | +16% | 0 | 0 | — |
case-07 | fail→pass | 16,119 | 8,451 | -48% | 1 | 1 | 0% | 2,691 | 3,643 | +35% | 0 | 0 | — |
case-08 | fail→pass | 20,729 | 10,741 | -48% | 1 | 1 | 0% | 3,191 | 3,780 | +18% | 0 | 0 | — |
case-09 | pass→pass | 9,448 | 4,190 | -56% | 1 | 1 | 0% | 1,335 | 2,575 | +93% | 0 | 0 | — |
case-10 | fail→pass | 14,982 | 6,136 | -59% | 1 | 1 | 0% | 2,334 | 2,953 | +27% | 0 | 0 | — |
case-11 | fail→pass | 9,160 | 7,212 | -21% | 1 | 1 | 0% | 1,620 | 3,196 | +97% | 0 | 0 | — |
case-12 | fail→pass | 18,530 | 8,570 | -54% | 1 | 1 | 0% | 3,222 | 3,710 | +15% | 0 | 0 | — |
case-13 | pass→pass | 9,886 | 4,647 | -53% | 1 | 1 | 0% | 1,533 | 2,583 | +68% | 0 | 0 | — |
case-14 | pass→pass | 16,657 | 4,454 | -73% | 1 | 1 | 0% | 2,448 | 2,642 | +8% | 0 | 0 | — |
case-15 | fail→pass | 14,781 | 5,260 | -64% | 1 | 1 | 0% | 2,480 | 2,882 | +16% | 0 | 0 | — |
case-16 | pass→pass | 13,649 | 6,367 | -53% | 1 | 1 | 0% | 1,756 | 2,965 | +69% | 0 | 0 | — |
case-17 | fail→pass | 11,523 | 3,740 | -68% | 1 | 1 | 0% | 1,718 | 2,324 | +35% | 0 | 0 | — |
case-18 | fail→pass | 12,293 | 6,268 | -49% | 1 | 1 | 0% | 2,084 | 2,834 | +36% | 0 | 0 | — |
case-19 | pass→pass | 14,287 | 8,950 | -37% | 1 | 1 | 0% | 2,358 | 3,441 | +46% | 0 | 0 | — |
case-20 | fail→pass | 16,035 | 10,321 | -36% | 1 | 1 | 0% | 2,533 | 3,796 | +50% | 0 | 0 | — |
case-21 | fail→pass | 18,803 | 10,867 | -42% | 1 | 1 | 0% | 2,757 | 3,638 | +32% | 0 | 0 | — |
case-22 | fail→pass | 15,846 | 8,808 | -44% | 1 | 1 | 0% | 2,398 | 3,288 | +37% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +68 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 9/3/2026 | +58% |
| gemini-3.6-flash | verified | 8/22/2026 | +73% |
Other measured skills in the registry, with their headline benchmark lift.