Skip to content

Robot

A kinematic model loaded from URDF, Xacro, or a USD articulation. A Robot is immutable and holds no scene state — joint values live in the Scene that owns it, so the same model can be added to a scene more than once.

import botrail as bt

robot = bt.Robot.from_urdf("arm.urdf")
robot = bt.Robot.from_xacro("arm.urdf.xacro")          # expanded without ROS
robot = bt.Robot.from_usd("franka.usd")                # Isaac Sim articulations

Robot

A parsed robot model (kinematic tree + geometry). Immutable.

allowed_collisions property

allowed_collisions

The link pairs declared to be allowed to touch, by name: the package's own and those added with allow_collisions.

dof property

dof
flange_link

Declared tool-mounting face (catalog frames.flange_frame; after attach_tool, the mounted tool's onward flange if it has one). attach_tool uses it when flange is omitted.

grasp_frames property

grasp_frames

Declared grasp-surface frames (catalog frames.grasp_frames): the fingertips of a hand, the pads of a gripper — where the product says it holds things. Empty when the source declares none; after attach_tool the mounted tool's ride along.

groups property

groups

The planning groups — the arms — by name: what every group= argument addresses. A single-arm robot has one ("arm", every joint, tipped at tcp_link); a dual-arm robot has one per arm. Declared by the catalog manifest or define_group, else read off the kinematic tree: each moving branch of a body is a group, tipped at the branch's own tool mount rather than at a fingertip.

joint_limits property

joint_limits

Per actuated joint: (lower, upper) or None for continuous joints.

joint_names property

joint_names

Actuated joint names in q-vector order.

link_names

mimic_joints property

mimic_joints

Joints that follow another joint (URDF <mimic>, USD PhysxMimicJointAPI) instead of carrying a DOF of their own: {joint: (source joint, multiplier, offset)}. Their value is multiplier * <source position> + offset, and they never appear in joint_names or in a joint position vector.

mount_link

Declared mounting face when this model is a tool (catalog frames.mount_frame). attach_tool uses it when mount is omitted, falling back to the tool's root link.

name property

name
tcp_link

Default end-effector link name: the TCP declared by a tool attachment or catalog manifest when present, otherwise the deepest leaf in the kinematic tree. A robot declared to have several arms has no single TCP — ask group(name).tip.

allow_collisions method descriptor

allow_collisions(pairs)

Declares link pairs that may touch — an SRDF's disable_collisions — and returns the robot with them added; the input is not modified. A scene already allows adjacent links and pairs that touch in nearly every pose on its own; this is for what only the machine's maker can say: the bulky torso capsules of a whole-body machine overlap in most poses, and that is the design. A catalog package carries its own (self_collision.allowed_pairs).

robot = robot.allow_collisions([("link_torso_2", "link_torso_4")])

attach_tool method descriptor

attach_tool(
    tool,
    flange=None,
    mount=None,
    offset_position=None,
    offset_quaternion=None,
    tcp=None,
    prefix=None,
    group=None,
)

Welds a tool (end-effector) onto this robot's flange and returns the composite robot; neither input is modified. The DOF vector becomes this robot's joints followed by the tool's, mimic joints included.

flange defaults to the robot's declared flange_link and mount to the tool's declared mount_link (catalog manifests declare both), falling back to the tool's root — so catalog parts attach with no arguments, and a coupling's outward face becomes the composite's flange for the next attach_tool in the stack. offset places the mount relative to the flange (e.g. a coupling's thickness); the mount must resolve to the tool's root link. tcp names a tool link to become the composite's tcp_link — otherwise a TCP declared on the tool carries over, falling back to the deepest-leaf heuristic. When both models share a link/joint name, pass prefix to namespace the tool's names.

robot = ur5e.attach_tool(coupling).attach_tool(gripper)  # catalog parts
robot = ur5e.attach_tool(
    gripper, flange="flange", mount="robotiq_arg2f_base_link",
    offset_position=(0, 0, 0.0139), tcp="tcp",
)
On a dual-arm robot, group names the arm the tool goes on: its declared flange is the default flange, and its TCP becomes the tool's. Without it, a flange on one of the arms says which.

define_group method descriptor

define_group(name, tip, joints=None, flange=None)

Declares (or redeclares, by name) a planning group and returns the robot carrying it; this robot is not modified. tip is the link a motion of the group places — an arm's TCP. joints names the actuated joints it drives; omitted, they are the joints on tip's chain minus those of any group tipped above it (a waist declared as a group of its own stays out of the arm). flange names the arm's tool-mounting face, which attach_tool(..., group=) uses.

The first declaration replaces the derived groups outright: from then on the declaration is the truth, and an arm not declared is not a group.

robot = robot.define_group("left", tip="left_ee_link",
                           joints=[f"left_joint{i}" for i in range(1, 8)])

dual_arm staticmethod

dual_arm(
    left,
    right,
    left_position=None,
    left_quaternion=None,
    right_position=None,
    right_quaternion=None,
    body=None,
    left_mount=None,
    right_mount=None,
)

Two arms on one body, as a single robot with the groups left and right (link and joint names prefixed left_ / right_): the dual-arm a single controller drives. body is the torso the arms bolt to — omitted, a bare frame named after the left arm — and left_mount / right_mount name the body links the arm roots go on (the body's root by default) at the given poses. Tools then attach per arm: attach_tool(gripper, group="left").

ur = bt.Robot.from_catalog("ur5e")
pair = bt.Robot.dual_arm(ur, ur, left_position=(0, 0.3, 0.8), left_quaternion=QL,
                         right_position=(0, -0.3, 0.8), right_quaternion=QR)
pair.groups   # ['left', 'right']

from_catalog staticmethod

from_catalog(id, revision=None, format=None)

Loads a robot or tool from the botrail model catalog (the Hugging Face dataset botrail/botrail-catalog) by id — exact (robotiq/2f/2f-85/r1) or any unambiguous shorthand (2f-85, robotiq/2f-85). Needs the optional dependency huggingface_hub (pip install botrail[catalog]).

revision pins a dataset commit SHA; without it the newest catalog is fetched and the resolved SHA is recorded in the robot's source, so saved projects and generated scripts replay bit-identically. Downloads land in the standard Hugging Face cache. format forces "urdf" or "usd"; by default the URDF is preferred. A TCP the package manifest declares (frames.tcp_default) becomes tcp_link. Packages distributed as recipe_only/metadata_only raise with a pointer to building them locally.

from_package staticmethod

from_package(path, format=None, *, catalog_root=None)

Loads a locally built catalog package directory containing manifest.yaml. Preserves its product ID, frames, BOM and mounting declarations. The revision is local-sha256:, not a Hub commit. Needs PyYAML (included in botrail[catalog]); no network request is made. Save a project to bundle its geometry for portable, offline replay.

from_urdf staticmethod

from_urdf(path)

Loads a robot from a URDF file. Mesh paths are resolved relative to the file; package:// URIs are resolved heuristically.

from_usd staticmethod

from_usd(path, articulation_root=None, search_paths=None)

Imports a robot from a USD articulation (UsdPhysics joints and rigid bodies, e.g. Isaac Sim assets). Link/joint names are the prim paths; revolute limits are converted from degrees, distances from the stage's metersPerUnit, and Y-up stages are re-modeled as Z-up. articulation_root defaults to the first prim carrying PhysicsArticulationRootAPI; search_paths resolve external (omniverse://) references against local directories.

from_xacro staticmethod

from_xacro(path)

Expands a Xacro file (no ROS required) and loads the resulting URDF.

group method descriptor

group(name)

One planning group: its joints, tip (TCP) link, flange and base.

ik method descriptor

ik(
    position,
    quaternion=None,
    link=None,
    seed=None,
    max_iters=100,
    restarts=None,
    group=None,
    centering=None,
)

Solves inverse kinematics. With quaternion=None only the position is matched. link defaults to the TCP link, seed to the neutral configuration. When the seeded solve does not converge, up to restarts further attempts run from deterministically generated seeds (limits midpoint first, then fixed-seed uniform samples within the limits) — the same call always returns the same answer. Pass restarts=0 to solve strictly from the given seed. Always returns the best configuration found; check result.converged. group names the arm to solve with (its tip is the default link); the joints of any other arm stay at the seed. centering is how strongly a redundant arm's free joints are drawn toward mid-range once the pose is met (default 0.1): 0 leaves them where the seed had them, so the answer lands next to the seed — what teaching a pose from a near one, or a step along a path, wants.

joint_values method descriptor

joint_values(positions)

Value of every joint at positions, keyed by joint name: the DOF value for actuated joints, the mimic relation for driven ones, and 0 for fixed joints.

mount method descriptor

mount(
    part,
    at,
    offset_position=None,
    offset_quaternion=None,
    prefix=None,
    group=None,
)

Bolts part — a manipulator — onto this robot's link at as an arm of its own, and returns the composite; neither input is modified. The part's root goes on at at the offset; prefix namespaces its link and joint names (required when the names collide — two of the same arm). The arm becomes a planning group named group (the prefix without its trailing _ by default) with the part's TCP and flange, so tools then attach per arm: attach_tool(gripper, group="left"). See dual_arm for the two-arm shorthand.

IkResult

Returned by Robot.ik and Scene.set_tcp_target. The solver is best-effort: it applies the closest configuration it reached, so always check converged before trusting the pose.

IkResult

Result of an IK solve.

converged property

converged

iters property

iters

pos_error property

pos_error

Remaining position error (m).

q property

q

Best joint configuration found (always within limits).

rot_error property

rot_error

Remaining orientation error (rad).

catalog_package

The catalog holds more than robots. A workpiece or a fixture is meshes and a manifest, with no articulation to build a Robot from, so this resolves the same product ids to a downloaded package directory and leaves the loading to the caller — see the catalog section.

catalog_package builtin

catalog_package(id, *, revision=None)

The catalog package directory for query, downloaded whole.

Not every catalog package is a robot. A body-in-white is a pile of collision meshes that a cell loads as obstacles, and a fixture is a mesh plus a frame — both want the package on disk, not a RobotModel. Returning the directory keeps those callers off huggingface_hub internals and, more to the point, off a hand-written snapshot path that silently stops matching when the dataset moves.