Multi-purpose hands (bt.tools)¶
A bracket carrying several tools — a gripper mount, pins, forks — each
with its own tip frame, built as a joint-less robot model to weld on with
Robot.attach_tool; and the process-tool envelopes that go on it, a
rotary bur and a screwdriver with its own stroke. See
Machine tending
and Assembly and fastening.
bracket = bt.tools.multi_tool("hand", [bt.tools.Mount("gripper"), bt.tools.Pin("pusher"), bt.tools.Fork("fork")])
hand = bracket.attach_tool(coupling, flange="hand_gripper").attach_tool(gripper)
robot = arm.attach_tool(hand)
tools
¶
Multi-purpose hands: one bracket, several tools, each with its own tip.
The end-effector in a machine-tending photograph is rarely one thing. A plate on the flange carries a gripper for the workpiece, a pin for the buttons and a fork for the door handle, and the robot switches between them by turning its wrist so the right one faces the job — no tool changer, no second robot. What the cell needs from such a hand is a tip frame per tool to aim at, and the bracket's own geometry in the collision check.
multi_tool builds that bracket as a robot model (a joint-less URDF
welded on with Robot.attach_tool): a round plate on the flange, and on
it any number of pins, forks and gripper mounts, placed in the plate's
frame (+Z out of the flange). Each tool is a link with geometry and a
<name>_<tool>_tip frame whose +Z points along the tool — the axis a
press, a hook or a grasp approaches on — so the same idiom serves every
tool: scene.set_tcp_target(pos, quat, link=tip).
bracket = bt.tools.multi_tool("hand", [bt.tools.Mount("gripper"),
bt.tools.Pin("pusher"),
bt.tools.Fork("fork")])
hand = bracket.attach_tool(coupling, flange="hand_gripper").attach_tool(gripper)
robot = arm.attach_tool(hand) # tcp = the gripper's; the pin
# and fork tips keep their names
The composite's tcp_link stays the gripper's, so motions and the
studio gizmo behave as for any gripper; a pose taught for the pin or the
fork just names its tip. IK asked for a tip moves only the arm — the
gripper's fingers are off that chain and keep their value.
rotary_bur and screwdriver are process-tool envelopes to weld on the
same way (a spindle's collet, a second Mount of the bracket): a tip
frame whose +Z points back along the tool axis, and one link — the
cutter, the bit — that is allowed to touch the work.
ALUMINIUM
module-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
BLACK_STEEL
module-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
Fork
¶
A door hook: two prongs gap apart reaching reach from a
crossbar at at along direction, lying across across. The tip
frame is the seat between the prongs, seat from the crossbar, +Z
along the prongs — a handle bar is taken by driving the seat onto it
along that axis, and the door goes wherever the fork then goes.
across
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
at
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
color
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
direction
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
gap
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
name
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
prong
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
reach
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
seat
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
Mount
¶
Where a gripper (or any catalog tool) bolts on: a link whose +Z is
the tool's mounting axis. at is the mounting face's centre in the
plate frame, direction its outward normal, spin turns the tool
about that normal (the finger pads' axis, on a parallel gripper).
at
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
direction
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
name
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
spin
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
Pin
¶
A pusher: a round pin of length and diameter from at along
direction. Its tip frame sits at the free end, +Z along the pin —
a button is pressed by putting the tip the button's travel into the
cap along that axis.
at
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
color
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
diameter
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
direction
class-attribute
¶
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
length
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
name
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
Robot
¶
A parsed robot model (kinematic tree + geometry). Immutable.
allowed_collisions
property
¶
The link pairs declared to be allowed to touch, by name: the
package's own and those added with allow_collisions.
flange_link
property
¶
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
¶
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
¶
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
¶
Per actuated joint: (lower, upper) or None for continuous joints.
mimic_joints
property
¶
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
property
¶
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.
tcp_link
property
¶
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
¶
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).
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",
)
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
¶
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.
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").
from_catalog
staticmethod
¶
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
¶
Loads a locally built catalog package directory containing manifest.yaml.
Preserves its product ID, frames, BOM and mounting declarations. The
revision is local-sha256:
from_urdf
staticmethod
¶
Loads a robot from a URDF file. Mesh paths are resolved relative to
the file; package:// URIs are resolved heuristically.
from_usd
staticmethod
¶
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
¶
Expands a Xacro file (no ROS required) and loads the resulting URDF.
group
method descriptor
¶
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
¶
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
¶
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.
multi_tool
¶
multi_tool(
name: str,
tools: Sequence[Union],
*,
plate: tuple[float, float] = (0.08, 0.012),
color: tuple = (0.55, 0.56, 0.58),
) -> Robot
The bracket as a robot model — multi_tool_urdf loaded. See there.
multi_tool_urdf
¶
multi_tool_urdf(
name: str,
tools: Sequence[Union],
*,
plate: tuple[float, float] = (0.08, 0.012),
color: tuple = (0.55, 0.56, 0.58),
) -> str
The bracket as URDF text — what multi_tool loads, and what a
catalog asset of the hand is written from, so the made part and the
catalogued one are the same geometry.
A round plate plate = (diameter, thickness) whose
root link <name>_plate sits on the robot's flange (+Z outward), with
the tools on it. Every tool becomes links under <name>_:
- a
Mount— the link<name>_<mount>at its face, +Z along its normal: theflange=toattach_toolthe gripper on; - a
Pin— the pin's body<name>_<pin>and the frame<name>_<pin>_tipat its end, +Z along the pin; - a
Fork— the crossbar and prongs under<name>_<fork>and the frame<name>_<fork>_tipat the seat between the prongs, +Z along them.
Returns a joint-less Robot to weld on with attach_tool; attach_tool
a gripper onto a mount first and the composite's TCP is the gripper's.
A tool drawn through the plate's own axis is not refused — a hand is
its author's business — but a Mount whose face lies inside the
plate would bolt a gripper into the bracket, and that is.
rotary_bur
¶
rotary_bur(
*,
diameter: float,
cutting_length: float,
shank_diameter: float,
exposed_shank: float,
) -> Robot
A cylindrical bur envelope, dimensions in metres.
mount is the collet nose, +Z outward. tcp is the distal tip,
+Z toward the spindle, matching the toolpath axis convention. Only the
separate cutter link should be allowed to contact stock. The shank
inside the collet and flute geometry are omitted; this is not a model
of cutting forces or proof of sufficient insertion depth.
screwdriver
¶
screwdriver(
*,
body_length: float = 0.2,
body_diameter: float = 0.086,
nose_length: float = 0.05,
nose_diameter: float = 0.03,
bit_length: float = 0.06,
bit_diameter: float = 0.008,
stroke: Optional[float] = 0.055,
color: tuple = (0.06, 0.06, 0.07),
) -> Robot
The screwdriver envelope as a robot model — screwdriver_urdf
loaded. See there.
screwdriver_urdf
¶
screwdriver_urdf(
*,
body_length: float = 0.2,
body_diameter: float = 0.086,
nose_length: float = 0.05,
nose_diameter: float = 0.03,
bit_length: float = 0.06,
bit_diameter: float = 0.008,
stroke: Optional[float] = 0.055,
color: tuple = (0.06, 0.06, 0.07),
) -> str
A robotic screwdriver as an envelope, dimensions in metres: the
motor body (a cylinder body_diameter wide and body_length long),
a nose in front of it and the bit sticking out of the nose.
mount is the root, the face that bolts to the hand, +Z into the
tool along its axis. tip is the bit's free end, turned half a turn
about X so its +Z points from the tip toward the body — the
toolpath axis convention rotary_bur follows, so a screw is driven by
aiming the tip's +Z along the hole's outward normal.
With stroke the bit rides a prismatic joint shank (0 to stroke)
along the axis: the tool's own feed — a cobot screwdriver with an
embedded stroke advances the screw itself while the arm stands still,
which is how bt.assembly.fasten runs a screw down (a ramp of that
joint, timed from the thread). stroke=None fixes the bit for a tool
the arm has to push.
Only the bit link should be allowed to touch a screw head
(scene.allow_link_obstacle_contact); the body and the nose stay
collision-checked, and the screw a bit carries is attached to it.
Torque, speed and the fastening result are not geometry: they are the
driver program bt.assembly.driver writes. Returns URDF text — what
screwdriver loads, and what a catalog asset of the tool is written
from, so the made envelope and the catalogued one are the same
geometry.