skrobot.assembly.RobotAssembly
- class skrobot.assembly.RobotAssembly(name: str)[source]
Manages the assembly state of multiple robot modules.
This class represents the undirected graph of module connections that the user has assembled through the UI. It does NOT represent the final kinematic tree - that is generated by the build() method.
The assembly graph is stored as: - A dictionary of module instances (nodes) - A list of connections (edges)
- connections
List of all connections between module ports.
- Type:
List[Connection]
- root_instance
The instance ID that will become the root of the final URDF tree.
- Type:
Optional[str]
- loop_closures
After a build: the closure config the loop connections produced (the
loop_closures.yamlcontent), ready to hand toskrobot.kinematics.LoopClosureSolver; None when the assembly declares no loop.- Type:
Optional[dict]
Examples
>>> # Create module definitions >>> hinge = RobotModule.from_urdf("hinge", "screw_hinge_module.urdf") >>> branch = RobotModule.from_urdf("branch", "screw_branch_module.urdf") >>> >>> # Create assembly >>> assembly = RobotAssembly("my_robot") >>> assembly.add_module_instance("base", branch) >>> assembly.add_module_instance("arm1", hinge) >>> assembly.add_module_instance("arm2", hinge) >>> >>> # Connect modules >>> assembly.connect("base", "dummy_link1", "arm1", "base_link") >>> assembly.connect("base", "dummy_link2", "arm2", "base_link") >>> >>> # Set root and build >>> assembly.set_root("base", "base_link") >>> combined_urdf = assembly.build()
Methods
- add_module_instance(instance_id: str, module: RobotModule) None[source]
Add a module instance to the assembly.
- Parameters:
instance_id (str) – Unique identifier for this instance.
module (RobotModule) – The module definition to instantiate.
- Raises:
ValueError – If instance_id already exists.
- build(output_path: str | None = None) str[source]
Build a combined URDF from the assembly.
Traverses the connection graph as a forest rooted at
root_instance, inserts aworldlink as the absolute root and attaches every module through a fixed joint from its parent’s port to its own root link, with all element names prefixed by the instance id. Modules keep their original kinematic structure (no chain reversal).Connections declared with
loop=Trueare cut edges: they never become joints. When any exist, aloop_closures.yamlsidecar (the runtime-IK relay config) is written next to the output URDF, and a parallelogram four-bar loop additionally gets exact<mimic>tags on its dependent joints.- Parameters:
output_path (str, optional) – Path to save the combined URDF. If None, uses a temporary file.
- Returns:
Path to the generated combined URDF file.
- Return type:
- Raises:
ValueError – If the assembly is empty or contains a cycle.
- build_robot_model()[source]
Assemble directly into a
skrobot.model.RobotModel.Composes the combined URDF in memory and loads it without writing the result to disk (re-rooted modules still go through temporary files). Mesh resolution follows the same rules as loading the built URDF file would.
- Returns:
The assembled robot.
- Return type:
- connect(module_a: str, port_a: str, module_b: str, port_b: str, x: float = 0.0, y: float = 0.0, z: float = 0.0, roll: float = 0.0, pitch: float = 0.0, yaw: float = 0.0, mate: bool = False, attach: str = 'root', loop: bool = False, dependent: Tuple[str, ...] | None = None, joint_type: str = 'fixed', axis: Tuple[float, float, float] = (0.0, 0.0, 1.0), lower: float | None = None, upper: float | None = None, effort: float = 100.0, velocity: float = 1.0) None[source]
Create a connection between two module ports.
- Parameters:
module_a (str) – Instance ID of the first module.
port_a (str) – Port name on module_a.
module_b (str) – Instance ID of the second module.
port_b (str) – Port name on module_b.
x (float, optional) – X position offset from port_a to port_b in meters (default: 0.0).
y (float, optional) – Y position offset from port_a to port_b in meters (default: 0.0).
z (float, optional) – Z position offset from port_a to port_b in meters (default: 0.0).
roll (float, optional) – Roll (rotation around X axis) offset in radians (default: 0.0).
pitch (float, optional) – Pitch (rotation around Y axis) offset in radians (default: 0.0).
yaw (float, optional) – Yaw (rotation around Z axis) offset in radians (default: 0.0).
- Raises:
ValueError – If either module instance doesn’t exist, or if ports are invalid.
Examples
>>> assembly.connect('h1', 'dummy_link', 'p1', 'base_link') >>> assembly.connect('h1', 'dummy_link', 'p2', 'base_link', yaw=1.57) mate : bool Derive the transform so the child port seats onto the parent port (Z axes opposed); only ``yaw`` may be combined with it. attach : str ``'root'`` (default) attaches the child-side module through its own root link, treating its declared port as placement metadata. ``'port'`` re-roots the child-side module at its declared port during the build so that port becomes the real attachment link. Applies to whichever endpoint becomes the child once the tree is rooted. loop : bool ``True`` declares this connection as a loop closure -- the cut edge of a closed linkage (e.g. a four-bar). It never becomes a joint in the built URDF; the two ports must already coincide at the zero pose of the rest of the assembly, and their common Z axis is the passive hinge axis. ``build()`` exports the closure to a ``loop_closures.yaml`` sidecar for runtime IK and, for a parallelogram four-bar, writes exact ``<mimic>`` tags. A loop connection carries no transform, so the offset, ``mate`` and ``attach`` arguments cannot be combined with it. dependent : Tuple[str, ...], optional Only with ``loop=True``: the final (prefixed, e.g. ``'arm1_elbow'``) joint names this closure solves; the other movable joints on the loop ring stay driven. Validated at build time against the actual ring. Default: the ring joint nearest the root drives, the rest are solved -- right for a 1-DOF loop, but a multi-DOF loop (five-bar and up) needs the solved joints named explicitly. joint_type : str ``'fixed'`` (default) welds the connection; ``'revolute'``, ``'continuous'`` or ``'prismatic'`` turn the mated port pair itself into an articulation. A movable connection must be declared parent-side first (``module_a`` ends up the parent once the tree is rooted): reversing a movable joint would relocate its frame, which the build refuses rather than approximates. axis : Tuple[float, float, float] Joint axis of a movable connection, expressed in the child attachment link's frame (in URDF the joint frame IS the child frame): the module root frame under ``attach='root'``, the declared port frame under ``attach='port'`` -- combine with ``attach='port'``/``mate`` to hinge about the port Z of the keyed-mate convention. Default Z. lower, upper : float, optional Joint limits in radians / metres; required for ``'revolute'`` and ``'prismatic'``. effort, velocity : float Limit attributes required by URDF alongside lower/upper.
- disconnect(module_a: str, port_a: str, module_b: str, port_b: str) None[source]
Remove the connection between two ports.
The connection is matched in either declaration order (the graph is undirected).
- Raises:
ValueError – If no such connection exists.
- classmethod from_dict(data: dict) RobotAssembly[source]
Rebuild an assembly from
to_dict()output.Modules are re-parsed from the recorded
urdf_path``s, so those files must still exist -- unless the dict was made with ``to_dict(embed=True), in which case the embedded URDF content is materialized into a temporary directory (the builds read the module files from disk) and the original paths are not needed.
- get_adjacency_list() Dict[str, List[Tuple[str, str, str, Tuple, Tuple]]][source]
Get the assembly as an adjacency list.
- Returns:
Maps instance_id to list of (connected_instance_id, this_port, other_port, xyz, rpy). When traversed from module_b to module_a, the connection transform is inverted (rigid inverse, not a component-wise negation – the latter is only correct for single-axis rotations). Loop closures (
loop=True) are cut edges, not tree edges, so they do not appear here.- Return type:
- remove_module_instance(instance_id: str) None[source]
Remove an instance and every connection that involves it.
When the removed instance was the root, the root falls back to the first remaining instance (through its module’s root link), or clears entirely on an empty assembly.
- set_root(instance_id: str, port_name: str) None[source]
Set the root link for the assembled robot.
The specified port will become the root of the final kinematic tree.
- to_dict(embed: bool = False) dict[source]
Serialize the assembly to a dictionary.
Useful for saving/loading assembly state or sending to frontend.
- Parameters:
embed (bool) – Also embed each module’s URDF XML (
urdf_content), sofrom_dict()can rebuild the assembly without the original files – e.g. across machines or through a storage backend.
- __eq__(value, /)
Return self==value.
- __ne__(value, /)
Return self!=value.
- __lt__(value, /)
Return self<value.
- __le__(value, /)
Return self<=value.
- __gt__(value, /)
Return self>value.
- __ge__(value, /)
Return self>=value.