Skip to content

anvil.workcell

anvil.workcell

What a workcell is: its robot's arms, where each answers and which slots it owns, its cameras and keep-out boxes.

The configuration here is plain dataclasses, so an edit is an assignment: arm.description.tuning.ee_pose.joints[3].kp = 20.0 is what that arm then runs under the ee_pose controller. Start from a shipped preset, WorkcellConfig.OPENYAM_V1_SINGLE or WorkcellConfig.shipped(...), edit what your bench differs in, and save it.

A RobotConfig and a WorkcellConfig are checked whole when they are built. An edit made afterwards, to them or to anything they hold, is checked where the robot or workcell is next handed over: to a hw interface, a controller, a Clearance, save, or a property such as RobotConfig.num_slots that has to work something out from it. A value refused there raises anvil.InvalidArgumentError saying what is wrong with it, and nothing reaches the drives.

The release wheel drives only the arms Anvil ships: a robot whose arms' joints or excluded_pairs differ from the shipped description of their variant, or whose URDF is not the one that variant ships, is refused when it is built, the error naming what differs. Tuning, a joint's gravity correction among it, homing, the gripper, the mounts, the cameras and the keep-out boxes stay yours to change.

Classes:

Name Description
Correction

A correction to one feedforward term of one joint: the term as computed, times scale, plus offset_nm.

JointDescription

One joint of an arm: the motor driving it, the addresses it answers on, and how far and how fast it may go.

GripAssist

The grip assist: the feedforward torque that does the gripping, on top of the gripper's own gains.

FingerSet

A set of interchangeable fingers a gripper carries, one directory under the gripper's fingers/.

Gripper

A gripper the stack drives: its one motor, the gains it holds a width at, and the map from jaw opening to angle.

ExternalGripper

A gripper the stack does not drive: your own, run by your code on the arm's bus beside the stack.

Gains

A drive's two gains for one joint.

ControllerTuning

How one controller drives an arm's chain joints: their gains and how fast it moves their targets.

TuningDescription

How each controller drives an arm's chain joints, as the tuning.yaml beside its arm.yaml states it.

ArmDescription

One arm variant: what it physically is and how it is driven.

Side

Which arm of a robot an arm is, where that means something.

ArmCount

How many followers a shipped workcell stands: one, or a left and a right.

Role

What an arm does in a robot.

ArmLayout

Where one arm of a robot sits in the robot's slots, as RobotConfig.layout worked it out.

RobotLayout

What follows from a robot's arms rather than being stated with them, as RobotConfig.layout worked it out.

ArmConfig

One arm of a robot: its name, the bus it answers on, what it does, which side it is, and its description.

Mount

Where a robot stands one copy of its variant's unit: an OpenArm torso carrying both arms, or one yam arm.

Stand

What a robot's units stand on, as Anvil's CAD draws it.

TimedWaypoint

One point of a TimedWaypoints: where every slot is at one time, and how fast it moves through there.

TimedWaypoints

Timed waypoints over every slot of a robot: a run the robot plans, such as its homing.

UrdfJoint

One moving joint of a robot's composed URDF and the slot it follows, as RobotConfig.urdf_joints lists it.

RobotConfig

The robot one run drives: its arms in slot order, the URDF they stand by, and where each copy of it is mounted.

NearestPair

Two links of a robot checked against each other and their gap, from Clearance.nearest or within.

NearestLink

A link of the robot and its gap to a keep-out box, from Clearance.nearest_to or within_of.

KeepOutBox

A box in the workcell the robot is kept out of: a table, a wall, a fixture.

Clearance

How near a robot's links come to one another and to keep-out boxes, posed from its motors' positions.

CameraRole

What a camera looks at.

Lens

The lens fitted to a camera, which cannot be read off the camera, so a workcell says which.

CameraConfig

One camera of a workcell: its name, the device it opens, the mode it streams, and where it is.

Preset

A shipped workcell's name and shape, from WorkcellConfig.preset_shapes, building nothing until asked.

WorkcellConfig

A workcell: its robot, the cameras it streams, and the boxes its robot is kept out of.

Functions:

Name Description
shipped_urdf

Returns the URDF text a shipped variant carries: the unit a robot's mounts stand copies of.

realhand_gripper

Returns the external gripper an arm states for the RealHand L6 bolted to it, as the hand's description gives it.

shipped_finger_sets

Returns every finger set Anvil ships for gripper, in name order: what its Gripper.fingers may be set to.

Correction dataclass

Correction(scale: float = 1.0, offset_nm: float = 0.0)

A correction to one feedforward term of one joint: the term as computed, times scale, plus offset_nm.

A model off by a factor or by a constant torque is corrected here; the defaults send the term as computed. Checked where the arm is next handed over: a scale that is negative or not a number, or an offset_nm that is not a number, is refused there.

Attributes:

Name Type Description
scale float

What the term is multiplied by: 1 sends it as computed, 0 none of it.

offset_nm float

What is added after scaling, in newton meters.

scale class-attribute instance-attribute

scale: float = 1.0

What the term is multiplied by: 1 sends it as computed, 0 none of it.

offset_nm class-attribute instance-attribute

offset_nm: float = 0.0

What is added after scaling, in newton meters.

JointDescription dataclass

JointDescription(
    name: str,
    motor: str,
    model: str,
    slave_id: int,
    master_id: int,
    limits_deg: tuple[float, float],
    max_dps: float | None = None,
    gravity: Correction = Correction(
        scale=1.0, offset_nm=0.0
    ),
    rest_pose_deg: float = 0.0,
    reflected: bool = False,
)

One joint of an arm: the motor driving it, the addresses it answers on, and how far and how fast it may go.

How hard a controller drives the joint is that controller's section of the arm's ArmDescription.tuning. Every field is checked where the arm is next handed over. In the release wheel the joints of a robot must be exactly those of the shipped description of its variant, so an edit to any field here is refused there.

Attributes:

Name Type Description
name str

The joint's name, e.g. joint_1; a gripper's motor is gripper.

motor str

The assembly-side motor name, J<number>, e.g. J5: what the bench tools print.

model str

The Damiao motor model driving it, as the descriptions name it, e.g. DM8009.

slave_id int

The CAN address the motor listens on, 0 to 255.

master_id int

The CAN address the motor replies on, 0 to 255.

limits_deg tuple[float, float]

Its travel as (min, max), in degrees, in the joint's own frame.

max_dps float | None

The fastest its target may move, in degrees per second; None leaves its speed unguarded.

gravity Correction

The correction applied to the gravity feedforward torque; as computed unless stated.

rest_pose_deg float

The posture IK pulls the joint toward, in degrees, in the joint's own frame.

reflected bool

Whether the joint's direction negates between the left and right arms of a mirror-image build.

name instance-attribute

name: str

The joint's name, e.g. joint_1; a gripper's motor is gripper.

motor instance-attribute

motor: str

The assembly-side motor name, J<number>, e.g. J5: what the bench tools print.

Any other form is refused.

model instance-attribute

model: str

The Damiao motor model driving it, as the descriptions name it, e.g. DM8009.

One this build does not know is refused.

slave_id instance-attribute

slave_id: int

The CAN address the motor listens on, 0 to 255.

master_id instance-attribute

master_id: int

The CAN address the motor replies on, 0 to 255.

limits_deg instance-attribute

limits_deg: tuple[float, float]

Its travel as (min, max), in degrees, in the joint's own frame.

A reflected joint negates it on the right arm.

max_dps class-attribute instance-attribute

max_dps: float | None = None

The fastest its target may move, in degrees per second; None leaves its speed unguarded.

Each controller's ControllerTuning.max_dps for the joint may not exceed it.

gravity class-attribute instance-attribute

gravity: Correction = Correction(scale=1.0, offset_nm=0.0)

The correction applied to the gravity feedforward torque; as computed unless stated.

rest_pose_deg class-attribute instance-attribute

rest_pose_deg: float = 0.0

The posture IK pulls the joint toward, in degrees, in the joint's own frame.

A reflected joint negates it on the right arm.

reflected class-attribute instance-attribute

reflected: bool = False

Whether the joint's direction negates between the left and right arms of a mirror-image build.

GripAssist dataclass

GripAssist(
    max_torque: float = 0.0,
    stiffness: float = 0.0,
    opening_scale: float = 1.0,
    shut_torque: float = 0.0,
)

The grip assist: the feedforward torque that does the gripping, on top of the gripper's own gains.

Each tick, with x the jaw-opening error in meters, measured minus commanded, closing pulls max_torque * tanh(stiffness * x / max_torque) toward shut, and opening pulls opening_scale times the same curve toward open. Near the target that is stiffness * x; past the knee, 1.5 * max_torque / stiffness meters of error, it sits at max_torque. A max_torque of 0 turns the assist off, leaving the gripper's own gains to move the jaws: where kp is a few per cent of the assist, as on the Anvil gripper, they creep, at the pace kp against kd sets. Every value must be a number of zero or more, checked where the arm is next handed over.

Attributes:

Name Type Description
max_torque float

The torque the curve saturates at, in newton meters.

stiffness float

The curve's slope at zero error, in newton meters per meter of jaw opening.

opening_scale float

Opening as a fraction of closing, torque, speed and slope together.

shut_torque float

The least closing torque while the command asks the jaws shut, in newton meters.

max_torque class-attribute instance-attribute

max_torque: float = 0.0

The torque the curve saturates at, in newton meters.

How hard the gripper squeezes, and so, against the drive's Gripper.kd, how fast the jaws close.

stiffness class-attribute instance-attribute

stiffness: float = 0.0

The curve's slope at zero error, in newton meters per meter of jaw opening.

How sharply the jaws stop on an object. Higher holds a width more tightly and hunts sooner; a gripper that hunts wants less.

opening_scale class-attribute instance-attribute

opening_scale: float = 1.0

Opening as a fraction of closing, torque, speed and slope together.

1 is the same both ways, 0.5 releases at half speed, 0 turns the opening assist off.

shut_torque class-attribute instance-attribute

shut_torque: float = 0.0

The least closing torque while the command asks the jaws shut, in newton meters.

So a gripper closing on nothing holds itself shut.

FingerSet dataclass

FingerSet(name: str, urdf: str | None = None)

A set of interchangeable fingers a gripper carries, one directory under the gripper's fingers/.

shipped_finger_sets lists the ones Anvil ships for a gripper; set one as a Gripper.fingers to swap to it.

Attributes:

Name Type Description
name str

The set's name, its directory under fingers/, e.g. fine_tip.

urdf str | None

The URDF fragment RobotConfig.composed_urdf lays over the gripper's own; None for the set shipped as name.

name instance-attribute

name: str

The set's name, its directory under fingers/, e.g. fine_tip.

urdf class-attribute instance-attribute

urdf: str | None = None

The URDF fragment RobotConfig.composed_urdf lays over the gripper's own; None for the set shipped as name.

The printed finger on each carrier and the tool frame their tips set.

Gripper dataclass

Gripper(
    family: str,
    version: int,
    joint: JointDescription,
    kp: float,
    kd: float,
    closed_deg: float,
    open_deg: float,
    opening_m: float,
    urdf: str | None = None,
    fingers: FingerSet | None = None,
    grip_assist: GripAssist = GripAssist(
        max_torque=0.0,
        stiffness=0.0,
        opening_scale=1.0,
        shut_torque=0.0,
    ),
    mass_kg: float = 0.0,
    com_m: tuple[float, float, float] = (0.0, 0.0, 0.0),
    inertia_kgm2: tuple[
        float, float, float, float, float, float
    ] = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
    alternate_motor: str | None = None,
)

A gripper the stack drives: its one motor, the gains it holds a width at, and the map from jaw opening to angle.

Every width the stack commands is the opening between the jaws, from 0 shut to opening_m fully open, mapped linearly onto the motor angles closed_deg and open_deg. The gripper holds a width the same way under every controller. A shipped description comes with its gripper filled in; a gripper Anvil does not ship needs its own urdf.

Checked where the arm carrying it is next handed over: an opening_m that is not more than zero, a closed_deg or open_deg outside joint's limits, a mass that is negative or not a number, a grip_assist value below zero, and a urdf that is not a gripper fragment, or that fingers does not lay over, are refused there.

Attributes:

Name Type Description
family str

The gripper family, e.g. anvil-gripper.

version int

The hardware revision, e.g. 1.

joint JointDescription

The motor driving the fingers.

kp float

The drive's position gain, in newton meters per radian of motor angle error.

kd float

The drive's velocity gain, in newton meters per radian per second of motor speed error.

closed_deg float

The motor angle with the jaws shut, in degrees.

open_deg float

The motor angle with the jaws open by opening_m, in degrees.

opening_m float

The jaw opening at full travel, in meters, between the jaws: what every commanded width is measured against.

urdf str | None

The unit's own URDF fragment, its gripper.urdf, or None for the one its family and version ship.

fingers FingerSet | None

The finger set laid over urdf, for a unit whose fingers are interchangeable.

grip_assist GripAssist

The feedforward torque that does the gripping.

mass_kg float

The unit's mass, in kilograms.

com_m tuple[float, float, float]

The unit's center of mass, in meters, in the frame of the link its fingers hang from.

inertia_kgm2 tuple[float, float, float, float, float, float]

The unit's inertia about its center of mass, in kilogram square meters, as (ixx, ixy, ixz, iyy, iyz, izz).

alternate_motor str | None

The motor name some units of this gripper were flashed as instead, e.g. J7, or None for none.

gripper_map GripperMap

Map from a commanded jaw opening to the motor angle, linear between the shut and open angles.

family instance-attribute

family: str

The gripper family, e.g. anvil-gripper.

version instance-attribute

version: int

The hardware revision, e.g. 1.

joint instance-attribute

The motor driving the fingers.

kp instance-attribute

kp: float

The drive's position gain, in newton meters per radian of motor angle error.

kd instance-attribute

kd: float

The drive's velocity gain, in newton meters per radian per second of motor speed error.

closed_deg instance-attribute

closed_deg: float

The motor angle with the jaws shut, in degrees.

open_deg instance-attribute

open_deg: float

The motor angle with the jaws open by opening_m, in degrees.

opening_m instance-attribute

opening_m: float

The jaw opening at full travel, in meters, between the jaws: what every commanded width is measured against.

urdf class-attribute instance-attribute

urdf: str | None = None

The unit's own URDF fragment, its gripper.urdf, or None for the one its family and version ship.

RobotConfig.composed_urdf grafts it onto the hand link the arm ends at. It carries the tool frame and the two finger joints the motor turns.

fingers class-attribute instance-attribute

fingers: FingerSet | None = None

The finger set laid over urdf, for a unit whose fingers are interchangeable.

None for one whose fingers are its own. A shipped arm carries the set its gripper ships with unless its arm.yaml names another.

grip_assist class-attribute instance-attribute

grip_assist: GripAssist = GripAssist(
    max_torque=0.0,
    stiffness=0.0,
    opening_scale=1.0,
    shut_torque=0.0,
)

The feedforward torque that does the gripping.

mass_kg class-attribute instance-attribute

mass_kg: float = 0.0

The unit's mass, in kilograms.

The arm's URDF carries none of it; RobotConfig.composed_urdf adds it to the link the fingers hang from. A gripper stated without it weighs nothing.

com_m class-attribute instance-attribute

com_m: tuple[float, float, float] = (0.0, 0.0, 0.0)

The unit's center of mass, in meters, in the frame of the link its fingers hang from.

inertia_kgm2 class-attribute instance-attribute

inertia_kgm2: tuple[
    float, float, float, float, float, float
] = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

The unit's inertia about its center of mass, in kilogram square meters, as (ixx, ixy, ixz, iyy, iyz, izz).

alternate_motor class-attribute instance-attribute

alternate_motor: str | None = None

The motor name some units of this gripper were flashed as instead, e.g. J7, or None for none.

A shipped arm's arm.yaml names it, as the OpenYAM's does, never the gripper's own description. The motor is then driven at that name's addresses, slave n and master n + 0x10 for J<n>, on a link where the gripper's own address is silent and that one answers; and a record numbered as it places the arm as a follower. An address a chain joint uses, or a name not J<number>, is refused where the arm is next handed over.

gripper_map property

gripper_map: GripperMap

Map from a commanded jaw opening to the motor angle, linear between the shut and open angles.

Built from the gripper as it is now; an edit after reading it does not change the map read.

Raises:

Type Description
InvalidArgumentError

The gripper is one the class docstring says is refused.

ExternalGripper dataclass

ExternalGripper(
    can_ids: list[int] = [],
    mass_kg: float = 0.0,
    com_m: tuple[float, float, float] = (0.0, 0.0, 0.0),
    inertia_kgm2: tuple[
        float, float, float, float, float, float
    ] = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
)

A gripper the stack does not drive: your own, run by your code on the arm's bus beside the stack.

Set one as ArmDescription.gripper in place of a Gripper. It takes no slot and adds no URDF, so the arm's chain ends at its hand flange, which carries the mass stated here. Checked where the arm is next handed over: a CAN ID past 0x7FF, one stated twice or one the arm's motors use, and a mass that is negative or not a number, are refused there.

Attributes:

Name Type Description
can_ids list[int]

The standard 11-bit CAN IDs your code sends and answers on, on the arm's bus.

mass_kg float

The gripper's mass, in kilograms; 0 weighs nothing.

com_m tuple[float, float, float]

Its center of mass, in meters, in the frame of the hand link it bolts to.

inertia_kgm2 tuple[float, float, float, float, float, float]

Its inertia about its center of mass, in kilogram square meters, as (ixx, ixy, ixz, iyy, iyz, izz).

can_ids class-attribute instance-attribute

can_ids: list[int] = []

The standard 11-bit CAN IDs your code sends and answers on, on the arm's bus.

mass_kg class-attribute instance-attribute

mass_kg: float = 0.0

The gripper's mass, in kilograms; 0 weighs nothing.

com_m class-attribute instance-attribute

com_m: tuple[float, float, float] = (0.0, 0.0, 0.0)

Its center of mass, in meters, in the frame of the hand link it bolts to.

inertia_kgm2 class-attribute instance-attribute

inertia_kgm2: tuple[
    float, float, float, float, float, float
] = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

Its inertia about its center of mass, in kilogram square meters, as (ixx, ixy, ixz, iyy, iyz, izz).

Gains dataclass

Gains(kp: float, kd: float)

A drive's two gains for one joint.

The drive applies kp * (position target - measured position) + kd * (velocity target - measured velocity), plus the feedforward torque. Each must be a number of zero or more, checked where the arm is next handed over.

Attributes:

Name Type Description
kp float

Position gain, in newton meters per radian.

kd float

Velocity gain, in newton meters per radian per second.

kp instance-attribute

kp: float

Position gain, in newton meters per radian.

kd instance-attribute

kd: float

Velocity gain, in newton meters per radian per second.

ControllerTuning dataclass

ControllerTuning(
    joints: list[Gains],
    max_dps: list[float],
    tcp_max_mps: float,
)

How one controller drives an arm's chain joints: their gains and how fast it moves their targets.

Checked where the arm is next handed over: joints and max_dps must each hold exactly one entry per chain joint, and every speed must be more than zero.

Attributes:

Name Type Description
joints list[Gains]

The gains of each chain joint, in chain order.

max_dps list[float]

The fastest the controller moves each chain joint's target, in degrees per second, in chain order.

tcp_max_mps float

The fastest the controller moves the arm's tool frame, in meters per second.

joints instance-attribute

joints: list[Gains]

The gains of each chain joint, in chain order.

max_dps instance-attribute

max_dps: list[float]

The fastest the controller moves each chain joint's target, in degrees per second, in chain order.

Never past the joint's own JointDescription.max_dps.

tcp_max_mps instance-attribute

tcp_max_mps: float

The fastest the controller moves the arm's tool frame, in meters per second.

TuningDescription dataclass

TuningDescription(
    joint: ControllerTuning,
    joint_trajectory: ControllerTuning,
    ee_pose: ControllerTuning,
    leader_follower: ControllerTuning,
)

How each controller drives an arm's chain joints, as the tuning.yaml beside its arm.yaml states it.

One complete section per controller, nothing shared between them. An edit before a controller is built, such as arm.description.tuning.ee_pose.joints[6].kp = 15.0, is what that controller then runs; once it is running, its handle's configure is how a gain moves.

Attributes:

Name Type Description
joint ControllerTuning

The anvil.control.JointController's section.

joint_trajectory ControllerTuning

The anvil.control.JointTrajectoryController's section.

ee_pose ControllerTuning

The anvil.control.EePoseController's section.

leader_follower ControllerTuning

The anvil.control.LeaderFollowerController's section, for the followers; it drives the leaders limp.

joint instance-attribute

The anvil.control.JointController's section.

joint_trajectory instance-attribute

joint_trajectory: ControllerTuning

The anvil.control.JointTrajectoryController's section.

ee_pose instance-attribute

The anvil.control.EePoseController's section.

leader_follower instance-attribute

leader_follower: ControllerTuning

The anvil.control.LeaderFollowerController's section, for the followers; it drives the leaders limp.

ArmDescription dataclass

ArmDescription(
    family: str,
    version: int,
    joints: list[JointDescription],
    gripper: Gripper | ExternalGripper,
    tuning: TuningDescription,
    home: dict[str, Any] | None = None,
    excluded_pairs: list[tuple[str, str]] = [],
)

One arm variant: what it physically is and how it is driven.

Where it stands is the RobotConfig's business. The shipped variants are class attributes, ArmDescription.OPENARM_V2, each built fresh on every access with nothing read from disk; shipped takes the variant as text, and read takes a description directory of your own. Every field holds the object itself, so an edit through one is what an arm carrying this description then runs. Checked where the arm is next handed over: no chain joint or more than anvil.kinematics.MAX_DOF, a motor not named J<number>, a joint or tuning refused as its class says, a gripper refused as its class says, or a home its field says is refused.

Methods:

Name Description
shipped

Returns the shipped arm variant names, built fresh: for a caller that has the variant as text.

read

Reads the arm <root>/arms/<variant>/arm.yaml describes, with its tuning, homing and gripper.

shipped_home

Returns the homing run the variant ships with, its grips opened to the same share of the gripper it carries.

to_json

Returns the description as JSON, the form from_json reads back.

from_json

Builds a description from the JSON to_json wrote, checked as it is when handed over.

Attributes:

Name Type Description
family str

The arm family, e.g. openarm.

version int

The hardware revision, e.g. 2.

joints list[JointDescription]

The chain joints, in order; the gripper's motor is not among them.

gripper Gripper | ExternalGripper

The gripper the arm carries: a Gripper the stack drives, or an ExternalGripper your own code drives.

tuning TuningDescription

The gains and speeds each controller drives the chain joints at.

home dict[str, Any] | None

The homing run, as the left or only arm sees it: the one the variant ships with, or one a workcell taught.

excluded_pairs list[tuple[str, str]]

Pairs of the arm's links, by its own URDF names, that Clearance never checks one against the other.

OPENARM_V1

The OpenArm v1, built fresh on every access.

OPENARM_V2

The OpenArm v2, built fresh on every access.

OPENYAM_V1

The OpenYAM v1, built fresh on every access.

OPENYAM_V2

The OpenYAM v2, built fresh on every access.

YAM_V1

The YAM v1, built fresh on every access.

variant str

The variant, family/vN, e.g. openyam/v1: the name the sim and shipped know the arm by.

num_slots int

Slots one such arm owns in a robot: one per chain joint, then one for a Gripper the stack drives.

gripper_map GripperMap | None

Gripper.gripper_map of the gripper the stack drives; None for an external gripper.

family instance-attribute

family: str

The arm family, e.g. openarm.

version instance-attribute

version: int

The hardware revision, e.g. 2.

joints instance-attribute

joints: list[JointDescription]

The chain joints, in order; the gripper's motor is not among them.

gripper instance-attribute

The gripper the arm carries: a Gripper the stack drives, or an ExternalGripper your own code drives.

tuning instance-attribute

The gains and speeds each controller drives the chain joints at.

home class-attribute instance-attribute

home: dict[str, Any] | None = None

The homing run, as the left or only arm sees it: the one the variant ships with, or one a workcell taught.

{"tcp_speed_mps": float, "rows": [...]}, each row {"q_deg": [...], "grip_m": float, "tcp_speed_mps": float | None}. The first row is the park pose, where the arm sits while the robot is off; the last is the home pose, where homing leaves it ready to work; the rows between are waypoints. Every shipped run is authored for an arm standing upright. Teach your own from RobotConfig.home_rows_at, as a unit hung or on a wall has to, and go back with shipped_home. None for an arm that ships none, which RobotConfig.home_trajectory then refuses.

Refused for a tcp_speed_mps that is not positive and finite, no rows, or a row that does not state one angle per chain joint inside its limits, whose grip is outside the gripper's opening, or whose own tcp_speed_mps is not positive and finite.

excluded_pairs class-attribute instance-attribute

excluded_pairs: list[tuple[str, str]] = []

Pairs of the arm's links, by its own URDF names, that Clearance never checks one against the other.

Their collision meshes overlap however it stands.

OPENARM_V1 class-attribute instance-attribute

OPENARM_V1

The OpenArm v1, built fresh on every access.

OPENARM_V2 class-attribute instance-attribute

OPENARM_V2

The OpenArm v2, built fresh on every access.

OPENYAM_V1 class-attribute instance-attribute

OPENYAM_V1

The OpenYAM v1, built fresh on every access.

OPENYAM_V2 class-attribute instance-attribute

OPENYAM_V2

The OpenYAM v2, built fresh on every access.

YAM_V1 class-attribute instance-attribute

YAM_V1

The YAM v1, built fresh on every access.

variant property

variant: str

The variant, family/vN, e.g. openyam/v1: the name the sim and shipped know the arm by.

Raises:

Type Description
InvalidArgumentError

The description is one the class docstring says is refused.

num_slots property

num_slots: int

Slots one such arm owns in a robot: one per chain joint, then one for a Gripper the stack drives.

Raises:

Type Description
InvalidArgumentError

The description is one the class docstring says is refused.

gripper_map property

gripper_map: GripperMap | None

Gripper.gripper_map of the gripper the stack drives; None for an external gripper.

Raises:

Type Description
InvalidArgumentError

The gripper is one Gripper says is refused.

shipped staticmethod

shipped(variant: str) -> ArmDescription

Returns the shipped arm variant names, built fresh: for a caller that has the variant as text.

Parameters:

Name Type Description Default
variant str

The variant, family/vN, e.g. "openarm/v2".

required

Raises:

Type Description
InvalidArgumentError

Anvil ships no such variant; the message lists the ones it does.

read staticmethod

read(root: str, variant: str) -> ArmDescription

Reads the arm <root>/arms/<variant>/arm.yaml describes, with its tuning, homing and gripper.

How a description the SDK was not built with gets in: root is a directory laid out as Anvil's descriptions are. The arm is joined to the gripper its arm.yaml names under <root>/grippers/, the tuning.yaml beside it, and the home.yaml and collision.yaml beside it where they exist. The release wheel refuses a robot built from a description that differs from the shipped one; see the module docstring.

Parameters:

Name Type Description Default
root str

The directory holding arms/ and grippers/.

required
variant str

The variant, family/vN, e.g. "openyam/v1".

required

Raises:

Type Description
InvalidArgumentError

A file could not be read, a missing one included, or does not describe an arm this SDK can use; the message names the file.

shipped_home

shipped_home() -> dict[str, Any] | None

Returns the homing run the variant ships with, its grips opened to the same share of the gripper it carries.

What a description whose home was taught goes back to: description.home = description.shipped_home().

Returns:

Type Description
dict[str, Any] | None

The run in the shape home holds it, or None for a variant that ships none.

Raises:

Type Description
InvalidArgumentError

Anvil ships no such variant, or the description is one the class docstring says is refused.

to_json

to_json() -> str

Returns the description as JSON, the form from_json reads back.

from_json classmethod

from_json(wire: str) -> Self

Builds a description from the JSON to_json wrote, checked as it is when handed over.

JSON an older release wrote reads as it was written: a field added since takes its default.

Parameters:

Name Type Description Default
wire str

The JSON text.

required

Raises:

Type Description
InvalidArgumentError

wire is not a description's JSON, or the description is one the class docstring says is refused.

Side

Bases: StrEnum

Which arm of a robot an arm is, where that means something.

The left or the right of a pair, or of a mirror-image build even alone. The only arm of its role in a symmetric build, a lone yam or either arm of a single leader and follower, has no side.

Attributes:

Name Type Description
LEFT

The left arm, which takes the lower slots of its role.

RIGHT

The right arm.

LEFT class-attribute instance-attribute

LEFT

The left arm, which takes the lower slots of its role.

RIGHT class-attribute instance-attribute

RIGHT

The right arm.

ArmCount

Bases: StrEnum

How many followers a shipped workcell stands: one, or a left and a right.

Attributes:

Name Type Description
SINGLE

One follower, with no side.

BIMANUAL

A left and a right follower.

SINGLE class-attribute instance-attribute

SINGLE

One follower, with no side.

BIMANUAL class-attribute instance-attribute

BIMANUAL

A left and a right follower.

Role

Bases: StrEnum

What an arm does in a robot.

Followers take the lower slots, so adding leaders moves no follower's slots.

Attributes:

Name Type Description
LEADER

Moved by hand; its measured state becomes the command of the follower on its side.

FOLLOWER

Driven to a command.

LEADER class-attribute instance-attribute

LEADER

Moved by hand; its measured state becomes the command of the follower on its side.

Its URDF names carry a leader_ prefix ahead of its side.

FOLLOWER class-attribute instance-attribute

FOLLOWER

Driven to a command.

ArmLayout dataclass

ArmLayout(
    first_slot: int,
    num_slots: int,
    chain_slots: tuple[int, ...],
    gripper_slot: int | None,
    tcp_link: str,
)

Where one arm of a robot sits in the robot's slots, as RobotConfig.layout worked it out.

Attributes:

Name Type Description
first_slot int

The first slot the arm owns.

num_slots int

How many slots it owns: its chain joints, then its gripper where the stack drives one.

chain_slots tuple[int, ...]

The slot of each chain joint, in description order.

gripper_slot int | None

The slot of its gripper motor, the last it owns; None for an external gripper, which owns none.

tcp_link str

The URDF link its chain ends at: the gripper's tool frame, or the hand flange for an external gripper.

first_slot instance-attribute

first_slot: int

The first slot the arm owns.

num_slots instance-attribute

num_slots: int

How many slots it owns: its chain joints, then its gripper where the stack drives one.

chain_slots instance-attribute

chain_slots: tuple[int, ...]

The slot of each chain joint, in description order.

gripper_slot instance-attribute

gripper_slot: int | None

The slot of its gripper motor, the last it owns; None for an external gripper, which owns none.

tcp_link: str

The URDF link its chain ends at: the gripper's tool frame, or the hand flange for an external gripper.

RobotLayout dataclass

RobotLayout(
    variant: str,
    num_slots: int,
    slot_summary: str,
    slot_names: tuple[str, ...],
    slot_travel_rad: tuple[tuple[float, float], ...],
    arms: tuple[ArmLayout, ...],
)

What follows from a robot's arms rather than being stated with them, as RobotConfig.layout worked it out.

A snapshot: reading it costs nothing, and an edit to the robot after it was taken does not change it.

Attributes:

Name Type Description
variant str

The variant the arms share, e.g. openyam/v1.

num_slots int

Slots the arms occupy: the num_joints of an anvil.control.JointState or anvil.control.RobotCommand.

slot_summary str

The arms and the slots they own, for an operator's log: left owns slots 0..8, right owns slots 8..16.

slot_names tuple[str, ...]

Each slot by what it drives, in slot order.

slot_travel_rad tuple[tuple[float, float], ...]

Each slot's travel as (lower, upper), in radians, in slot order.

arms tuple[ArmLayout, ...]

One per arm, in the robot's order.

variant instance-attribute

variant: str

The variant the arms share, e.g. openyam/v1.

num_slots instance-attribute

num_slots: int

Slots the arms occupy: the num_joints of an anvil.control.JointState or anvil.control.RobotCommand.

slot_summary instance-attribute

slot_summary: str

The arms and the slots they own, for an operator's log: left owns slots 0..8, right owns slots 8..16.

Each range is half-open: 0..8 is slots 0 to 7.

slot_names instance-attribute

slot_names: tuple[str, ...]

Each slot by what it drives, in slot order.

The arm's name, then the joint's or gripper, e.g. "left joint_3", "left gripper".

slot_travel_rad instance-attribute

slot_travel_rad: tuple[tuple[float, float], ...]

Each slot's travel as (lower, upper), in radians, in slot order.

A chain joint's limits from the arm's description, negated on the right arm for a JointDescription.reflected joint; a gripper motor's between its shut and open angles.

arms instance-attribute

arms: tuple[ArmLayout, ...]

One per arm, in the robot's order.

ArmConfig dataclass

ArmConfig(
    name: str,
    bus: str,
    description: ArmDescription,
    side: Side | None = None,
    role: Role = Role.FOLLOWER,
    bus_overridden: bool = False,
)

One arm of a robot: its name, the bus it answers on, what it does, which side it is, and its description.

Put it in a RobotConfig to give it slots; the slot properties ask that robot, so they follow any edit to it.

Attributes:

Name Type Description
name str

Its name, unique in the robot: what logs, errors and RobotLayout.slot_names call it.

bus str

The bus its motors answer on, e.g. "follower_l", resolved against the CAN links the host has.

description ArmDescription

Everything the arm physically is and how it is driven, as the object itself.

side Side | None

Side.LEFT or Side.RIGHT in a robot with more than one arm of its role, or for a mirror-image build.

role Role

What the arm does in the robot.

bus_overridden bool

Whether an operator set bus by hand over what the drives' identity records say.

num_slots int

Slots the arm owns: one per chain joint, then one for a Gripper the stack drives.

first_slot int | None

The first slot the arm owns in the robot it was last put in; None until it is in a RobotConfig.

chain_slots list[int]

The slot of each chain joint, in description order.

gripper_slot int | None

The slot of its gripper motor, the last one it owns; None for an external gripper, which owns none.

name instance-attribute

name: str

Its name, unique in the robot: what logs, errors and RobotLayout.slot_names call it.

bus instance-attribute

bus: str

The bus its motors answer on, e.g. "follower_l", resolved against the CAN links the host has.

Where the drives' identity records name a link, they decide and this is the fallback, unless bus_overridden; where it resolves to no link with drives, the arm is guessed onto one that has them, as anvil.hw.buses says.

description instance-attribute

description: ArmDescription

Everything the arm physically is and how it is driven, as the object itself.

So arm.description.tuning.ee_pose.joints[3].kp = 20.0 is what this arm then runs.

side class-attribute instance-attribute

side: Side | None = None

Side.LEFT or Side.RIGHT in a robot with more than one arm of its role, or for a mirror-image build.

None for the only arm of its role in a symmetric build. Any other choice is refused where the robot is built.

role class-attribute instance-attribute

role: Role = Role.FOLLOWER

What the arm does in the robot.

bus_overridden class-attribute instance-attribute

bus_overridden: bool = False

Whether an operator set bus by hand over what the drives' identity records say.

The arm then stays on bus, and the readiness check only warns where its records disagree.

num_slots property

num_slots: int

Slots the arm owns: one per chain joint, then one for a Gripper the stack drives.

Raises:

Type Description
InvalidArgumentError

The description is one ArmDescription says is refused.

first_slot property

first_slot: int | None

The first slot the arm owns in the robot it was last put in; None until it is in a RobotConfig.

Raises:

Type Description
InvalidArgumentError

That robot, as edited since, is one RobotConfig refuses.

chain_slots property

chain_slots: list[int]

The slot of each chain joint, in description order.

Raises:

Type Description
InvalidArgumentError

The robot the arm is in, as edited since, is one RobotConfig refuses.

OperationError

The arm is in no RobotConfig yet.

gripper_slot property

gripper_slot: int | None

The slot of its gripper motor, the last one it owns; None for an external gripper, which owns none.

Raises:

Type Description
InvalidArgumentError

The robot the arm is in, as edited since, is one RobotConfig refuses.

OperationError

The arm is in no RobotConfig yet.

Mount dataclass

Mount(
    prefix: str,
    xyz_m: tuple[float, float, float],
    yaw_rad: float,
    roll_rad: float = 0.0,
    pitch_rad: float = 0.0,
)

Where a robot stands one copy of its variant's unit: an OpenArm torso carrying both arms, or one yam arm.

Mounts are stated in the world frame, the composed URDF's world link, which every pose a robot's chains and controllers state is in too. A preset's mounts are where the shipped stands bolt the units; move them to match your bench. The unit is turned as a URDF origin is: rolled about the world's x, then pitched about its y, then turned about its vertical. Hung from a ceiling is roll_rad=math.pi; bolted to a wall behind it, pitch_rad=math.pi / 2. Gravity stays the world's -z, so a tilted unit's chains, gravity compensation and IK follow the tilt. Each mount of a robot has its own prefix, and every coordinate must be a finite number, checked where the robot is built or next handed over.

Attributes:

Name Type Description
prefix str

What the copy's URDF link and joint names are prefixed with.

xyz_m tuple[float, float, float]

Where the unit's base stands in the world frame, in meters.

yaw_rad float

How far the unit is turned about the world frame's vertical, in radians, counterclockwise seen from above.

roll_rad float

How far the unit is rolled about the world frame's x, in radians; zero stands it upright.

pitch_rad float

How far the unit is pitched about the world frame's y, in radians; zero stands it upright.

prefix instance-attribute

prefix: str

What the copy's URDF link and joint names are prefixed with.

For one arm, its role's prefix then its side's, "left_", "leader_left_" or "" for an arm with no side; for an OpenArm torso, its role's, "" or "leader_".

xyz_m instance-attribute

xyz_m: tuple[float, float, float]

Where the unit's base stands in the world frame, in meters.

yaw_rad instance-attribute

yaw_rad: float

How far the unit is turned about the world frame's vertical, in radians, counterclockwise seen from above.

roll_rad class-attribute instance-attribute

roll_rad: float = 0.0

How far the unit is rolled about the world frame's x, in radians; zero stands it upright.

The roll is applied first, before the pitch and then the yaw.

pitch_rad class-attribute instance-attribute

pitch_rad: float = 0.0

How far the unit is pitched about the world frame's y, in radians; zero stands it upright.

The pitch is applied after the roll and before the yaw.

Stand

Bases: StrEnum

What a robot's units stand on, as Anvil's CAD draws it.

Its frame is the one the robot's mounts are stated in, so a mount is where the stand bolts a unit, and anvil.sim.World stands the robot in the scene of the stand's name unless told another.

Attributes:

Name Type Description
LIFT

The wheeled lifting platform. Its frame is the top plate's face on the column's axis, x along the reach.

LIFT_BRACKET

The lift carrying the OpenYAM middle bracket, in the lift's frame.

LIFT class-attribute instance-attribute

LIFT

The wheeled lifting platform. Its frame is the top plate's face on the column's axis, x along the reach.

LIFT_BRACKET class-attribute instance-attribute

LIFT_BRACKET

The lift carrying the OpenYAM middle bracket, in the lift's frame.

It is the rail a yam pair stands on, and the pole between the arms with a camera on its ball head: the composed URDF's pole_cam link, which a CameraConfig.link may name.

TimedWaypoint dataclass

TimedWaypoint(
    time_from_start_s: float,
    q: list[float],
    qd: list[float],
)

One point of a TimedWaypoints: where every slot is at one time, and how fast it moves through there.

Attributes:

Name Type Description
time_from_start_s float

When the run reaches it, in seconds after the run starts.

q list[float]

Position per slot, in radians; a gripper's slot is its motor angle.

qd list[float]

Velocity per slot as the run passes through, in radians per second; zero stops there before moving on.

time_from_start_s instance-attribute

time_from_start_s: float

When the run reaches it, in seconds after the run starts.

q instance-attribute

q: list[float]

Position per slot, in radians; a gripper's slot is its motor angle.

qd instance-attribute

qd: list[float]

Velocity per slot as the run passes through, in radians per second; zero stops there before moving on.

TimedWaypoints dataclass

TimedWaypoints(num_slots: int, points: list[TimedWaypoint])

Timed waypoints over every slot of a robot: a run the robot plans, such as its homing.

One comes from RobotConfig.home_trajectory, dehome_trajectory or rehome_trajectory. The first waypoint is reached from wherever the arms are when it starts, and the last at rest. Play it with anvil.control.JointTrajectoryHandle.play, send it with anvil.control.Runtime.send_command, or build the command from it with anvil.control.JointTrajectoryCommand.from_waypoints: the controller plays it as it is.

Attributes:

Name Type Description
num_slots int

Slots every waypoint states: the robot's RobotConfig.num_slots.

points list[TimedWaypoint]

The waypoints, in time order.

duration_s float

Time from the start to the last waypoint, in seconds; 0 for a run with none.

num_slots instance-attribute

num_slots: int

Slots every waypoint states: the robot's RobotConfig.num_slots.

points instance-attribute

points: list[TimedWaypoint]

The waypoints, in time order.

duration_s property

duration_s: float

Time from the start to the last waypoint, in seconds; 0 for a run with none.

UrdfJoint dataclass

UrdfJoint(
    name: str, slot: int | None, scale: float, offset: float
)

One moving joint of a robot's composed URDF and the slot it follows, as RobotConfig.urdf_joints lists it.

The joint's value is scale times the measured position in slot, plus offset: one for one for a motor's own joint, while a gripper's finger follows its motor through the linkage.

Attributes:

Name Type Description
name str

The joint's name in the composed URDF.

slot int | None

The slot whose measured position drives the joint; None for a joint nothing drives.

scale float

What the slot's position is multiplied by; 1 for a motor's own joint.

offset float

What is added after scaling; 0 for a motor's own joint.

name instance-attribute

name: str

The joint's name in the composed URDF.

slot instance-attribute

slot: int | None

The slot whose measured position drives the joint; None for a joint nothing drives.

scale instance-attribute

scale: float

What the slot's position is multiplied by; 1 for a motor's own joint.

offset instance-attribute

offset: float

What is added after scaling; 0 for a motor's own joint.

RobotConfig dataclass

RobotConfig(
    arms: list[ArmConfig],
    urdf: str | None = None,
    mounts: list[Mount] = [],
    stand: Stand | None = None,
)

The robot one run drives: its arms in slot order, the URDF they stand by, and where each copy of it is mounted.

Get one from a workcell, WorkcellConfig.OPENYAM_V1_SINGLE.robot, or build one from its fields. Each arm owns the slots after the arm before it, chain joints then gripper, so a bimanual openarm's left arm owns slots 0 to 7 and its right 8 to 15, and a single openyam's arm 0 to 6. Arms are declared followers before leaders and, within a role, the left before the right.

It is checked as it is built, and again wherever it is handed over after an edit. Whether the URDF really carries each arm is found where a chain is cut from it, by chains, composed_urdf and the calls that use them.

Raises:

Type Description
InvalidArgumentError

No arm, or more than anvil.control.MAX_ARMS; more slots than anvil.control.MAX_JOINTS; a name or bus used twice; a side missing or given against the rule on ArmConfig.side; two arms of one role on one side; a leader before a follower, or a right arm before the left of its role; in a robot with leaders, an arm with no partner of the other role on its side; arms of different variants; a mount that is not a finite number on every axis, or two mounts of one prefix; an arm whose description is refused; or, in the release wheel, a robot Anvil does not ship.

Methods:

Name Description
arms_where

Returns the arms of the side and role asked for, in slot order: the objects themselves.

arm

Returns the arm named name, the object itself, or None when the robot has no arm of that name.

layout

Returns the robot's slot layout as it stands now, as a snapshot.

camera_links

Returns every *_cam link of the composed URDF: the links a CameraConfig.link may name.

mount_joints

Returns the URDF joint each of mounts stands its copy of the unit by, in the mounts' order.

mesh_files

Returns every mesh the composed URDF names, each once, by its path under the robot descriptions' root.

urdf_joints

Returns every moving joint of composed_urdf with the slot it follows, in the composed URDF's order.

composed_urdf

Returns the whole robot as URDF text: what gravity compensation, the sim and a recording read.

chains

Returns one anvil.kinematics.Chain per arm, in slot order, cut from the composed URDF to its tool frame.

home_rows_at

Returns one homing row per arm, in slot order, standing each where q has it: what a row is taught from.

home_trajectory

Returns the run that homes the robot from where its arms are, through the homing sequence each describes.

dehome_trajectory

Returns the homing run backwards, from where the arms are to the row a homing starts from, ending at rest.

rehome_trajectory

Returns a run straight to where a homing ends, in one segment from where the arms are.

to_json

Returns the robot as JSON, the form from_json reads back.

from_json

Builds a robot from the JSON to_json wrote, checked as one built from its fields is.

Attributes:

Name Type Description
arms list[ArmConfig]

The arms, in slot order: the objects themselves, so an edit through one is what that arm then runs.

urdf str | None

The URDF text the arms stand by, without their grippers, or None for the one their variant ships.

mounts list[Mount]

Where each copy of urdf stands; empty for a urdf that places the arms itself.

stand Stand | None

What the units stand on, in whose frame the mounts are; None for a bench nothing draws.

variant str

The variant the arms share, e.g. openyam/v1.

num_slots int

Slots the arms occupy: the num_joints of an anvil.control.JointState or anvil.control.RobotCommand.

slot_summary str

The arms and the slots they own, for an operator's log: left owns slots 0..8, right owns slots 8..16.

ik_params list[IkParams]

Each arm's IK parameters, in slot order, as an anvil.control.EePoseController over the robot starts out.

arms instance-attribute

arms: list[ArmConfig]

The arms, in slot order: the objects themselves, so an edit through one is what that arm then runs.

urdf class-attribute instance-attribute

urdf: str | None = None

The URDF text the arms stand by, without their grippers, or None for the one their variant ships.

For example Path("mine.urdf").read_text(). With mounts it is the variant's unit, placed once per mount; with none it places the arms itself.

mounts class-attribute instance-attribute

mounts: list[Mount] = []

Where each copy of urdf stands; empty for a urdf that places the arms itself.

stand class-attribute instance-attribute

stand: Stand | None = None

What the units stand on, in whose frame the mounts are; None for a bench nothing draws.

A preset without leaders stands on the lift, a yam pair on the bracket it carries; one with leaders on none.

variant property

variant: str

The variant the arms share, e.g. openyam/v1.

Raises:

Type Description
InvalidArgumentError

The robot, as edited since it was built, is refused.

num_slots property

num_slots: int

Slots the arms occupy: the num_joints of an anvil.control.JointState or anvil.control.RobotCommand.

Raises:

Type Description
InvalidArgumentError

The robot, as edited since it was built, is refused.

slot_summary property

slot_summary: str

The arms and the slots they own, for an operator's log: left owns slots 0..8, right owns slots 8..16.

Each range is half-open: 0..8 is slots 0 to 7.

Raises:

Type Description
InvalidArgumentError

The robot, as edited since it was built, is refused.

ik_params property

ik_params: list[IkParams]

Each arm's IK parameters, in slot order, as an anvil.control.EePoseController over the robot starts out.

The rest posture is the one each arm's description states, as that arm sees it; the rest are the solver's defaults. Each read builds them afresh, so an edit to one changes nothing until it is handed to a solve or a controller.

Raises:

Type Description
InvalidArgumentError

The robot, as edited since it was built, is refused.

arms_where

arms_where(
    side: Side | None = None, role: Role | None = None
) -> list[ArmConfig]

Returns the arms of the side and role asked for, in slot order: the objects themselves.

A filter left None matches every arm, so arms_where() is arms. An arm with no side matches no side: on a single-arm cell, arms_where(side=Side.LEFT) is [] and the arm is arms[0].

Parameters:

Name Type Description Default
side Side | None

The side to match, or None for any.

None
role Role | None

The role to match, or None for any.

None

arm

arm(name: str) -> ArmConfig | None

Returns the arm named name, the object itself, or None when the robot has no arm of that name.

Parameters:

Name Type Description Default
name str

The arm's name, as its ArmConfig states it.

required

layout

layout() -> RobotLayout

Returns the robot's slot layout as it stands now, as a snapshot.

Each slot property of the robot and of its arms works the layout out again; take it once where a loop reads it repeatedly.

Raises:

Type Description
InvalidArgumentError

The robot, as edited since it was built, is refused.

camera_links() -> list[str]

Returns every *_cam link of the composed URDF: the links a CameraConfig.link may name.

The sim renders each of these as the camera riding on it.

Raises:

Type Description
InvalidArgumentError

As composed_urdf raises.

mount_joints

mount_joints() -> list[str]

Returns the URDF joint each of mounts stands its copy of the unit by, in the mounts' order.

That joint is what moves the whole unit, as a viewer dragging it would. Empty for a robot with no mounts.

Raises:

Type Description
InvalidArgumentError

The robot is refused, or its urdf does not parse or hangs no joint off its world link.

mesh_files

mesh_files() -> list[str]

Returns every mesh the composed URDF names, each once, by its path under the robot descriptions' root.

These are what has to travel beside the composed URDF for it to load elsewhere, e.g. arms/openyam/v1/meshes/base_link_collision_c00.stl.

Raises:

Type Description
InvalidArgumentError

As composed_urdf raises.

urdf_joints

urdf_joints() -> list[UrdfJoint]

Returns every moving joint of composed_urdf with the slot it follows, in the composed URDF's order.

This is how to pose a drawing of the robot from the loop's state.

Raises:

Type Description
InvalidArgumentError

As composed_urdf raises.

composed_urdf

composed_urdf() -> str

Returns the whole robot as URDF text: what gravity compensation, the sim and a recording read.

urdf placed once per mount, each arm's gripper URDF and finger set grafted onto the hand link it ends at, and each gripper's mass added to the link its fingers hang from, or an external gripper's to hand; the camera a stand carries is a link fixed to world. Every mesh is named by its path under the robot descriptions' root.

Raises:

Type Description
InvalidArgumentError

The robot is refused, or a URDF does not parse, lacks the link a gripper mounts on, or already carries a joint a gripper or the stand adds; the message names the link or joint; a stand's camera on a URDF with no world link.

chains

chains() -> list[Chain]

Returns one anvil.kinematics.Chain per arm, in slot order, cut from the composed URDF to its tool frame.

Raises:

Type Description
InvalidArgumentError

As composed_urdf raises, or as anvil.kinematics.Chain.from_urdf_text raises for an arm's chain, such as the composed URDF lacking the arm's tool frame.

home_rows_at

home_rows_at(q: Sequence[float]) -> list[dict[str, Any]]

Returns one homing row per arm, in slot order, standing each where q has it: what a row is taught from.

Move the arms there by hand, then put each row in its arm's ArmDescription.home: the first row is the park pose, the last the home pose, the rows between waypoints. Each row is as the left or only arm sees it, as home states its rows, every joint held inside its travel and the gripper inside its opening, so a reading a hair past a stop still plays; its leg is paced at the run's own speed.

Example
rows = robot.home_rows_at(q=runtime.latest().q)
for arm, row in zip(robot.arms, rows, strict=True):
    arm.description.home["rows"][0] = row  # the park pose

Parameters:

Name Type Description Default
q Sequence[float]

Position per slot, in radians: a anvil.control.Snapshot's q. Entries past the robot's slots are not read.

required

Returns:

Type Description
list[dict[str, Any]]

One row per arm, in slot order, each a dict as ArmDescription.home holds its rows: q_deg, the chain joints in degrees; grip_m, the gripper's opening in meters, 0.0 for an arm with no gripper the stack drives; and tcp_speed_mps, None.

Raises:

Type Description
TypeError

q holds something other than numbers.

InvalidArgumentError

The robot is refused, or q does not cover every slot or holds a value that is not finite.

home_trajectory

home_trajectory(from_q: Sequence[float]) -> TimedWaypoints

Returns the run that homes the robot from where its arms are, through the homing sequence each describes.

Each segment lasts as long as the arm whose tip travels farthest needs at the speed the description states for it, or the joint that turns farthest needs at 60 degrees a second, whichever is longer, and never less than half a second; the waypoints are passed through without overshoot and the last is reached at rest. Play it on a anvil.control.JointTrajectoryController over the same robot.

Parameters:

Name Type Description Default
from_q Sequence[float]

Position per slot where the arms are now, in radians: a anvil.control.Snapshot's q. Entries past the robot's slots are not read.

required

Raises:

Type Description
TypeError

from_q holds something other than numbers.

InvalidArgumentError

The robot is refused; an arm's description states no homing sequence, or the arms' sequences have different numbers of rows; from_q does not cover every slot.

dehome_trajectory

dehome_trajectory(
    from_q: Sequence[float],
) -> TimedWaypoints

Returns the homing run backwards, from where the arms are to the row a homing starts from, ending at rest.

It passes through the home rows last to first, each leg at the pace it is played forward, timed as home_trajectory times it. What a teleop session plays before its drives are disabled.

Parameters:

Name Type Description Default
from_q Sequence[float]

Position per slot where the arms are now, in radians: a anvil.control.Snapshot's q.

required

Raises:

Type Description
TypeError

from_q holds something other than numbers.

InvalidArgumentError

As home_trajectory raises.

rehome_trajectory

rehome_trajectory(
    from_q: Sequence[float],
) -> TimedWaypoints

Returns a run straight to where a homing ends, in one segment from where the arms are.

What a teleop session plays when the operator asks for the arms back at home mid-session.

Parameters:

Name Type Description Default
from_q Sequence[float]

Position per slot where the arms are now, in radians: a anvil.control.Snapshot's q.

required

Raises:

Type Description
TypeError

from_q holds something other than numbers.

InvalidArgumentError

As home_trajectory raises.

to_json

to_json() -> str

Returns the robot as JSON, the form from_json reads back.

from_json classmethod

from_json(wire: str) -> Self

Builds a robot from the JSON to_json wrote, checked as one built from its fields is.

JSON an older release wrote reads as it was written: a field added since takes its default.

Parameters:

Name Type Description Default
wire str

The JSON text.

required

Raises:

Type Description
InvalidArgumentError

wire is not a robot's JSON, or the robot is refused, as the class docstring says.

NearestPair dataclass

NearestPair(
    link_a: str,
    link_b: str,
    distance_m: float,
    point_a_m: tuple[float, float, float],
    point_b_m: tuple[float, float, float],
)

Two links of a robot checked against each other and their gap, from Clearance.nearest or within.

Attributes:

Name Type Description
link_a str

One piece's link, as the composed URDF names it.

link_b str

The other piece's link.

distance_m float

The gap between them, in meters; zero where they touch or overlap.

point_a_m tuple[float, float, float]

The point of link_a's piece at one end of the gap, in the world frame, in meters.

point_b_m tuple[float, float, float]

The point of link_b's piece at the gap's other end, in the world frame, in meters.

link_a: str

One piece's link, as the composed URDF names it.

link_b: str

The other piece's link.

distance_m instance-attribute

distance_m: float

The gap between them, in meters; zero where they touch or overlap.

point_a_m instance-attribute

point_a_m: tuple[float, float, float]

The point of link_a's piece at one end of the gap, in the world frame, in meters.

point_b_m instance-attribute

point_b_m: tuple[float, float, float]

The point of link_b's piece at the gap's other end, in the world frame, in meters.

NearestLink(
    link: str,
    distance_m: float,
    point_m: tuple[float, float, float],
    box_point_m: tuple[float, float, float],
)

A link of the robot and its gap to a keep-out box, from Clearance.nearest_to or within_of.

Attributes:

Name Type Description
link str

The piece's link, as the composed URDF names it.

distance_m float

The gap between it and the box, in meters. Where they touch or overlap, zero from nearest_to, and from

point_m tuple[float, float, float]

The point of the piece at one end of the gap, in the world frame, in meters.

box_point_m tuple[float, float, float]

The point of the box at the gap's other end, in the world frame, in meters.

link: str

The piece's link, as the composed URDF names it.

distance_m instance-attribute

distance_m: float

The gap between it and the box, in meters. Where they touch or overlap, zero from nearest_to, and from within_of minus how far the link must move to leave the box.

point_m instance-attribute

point_m: tuple[float, float, float]

The point of the piece at one end of the gap, in the world frame, in meters.

box_point_m instance-attribute

box_point_m: tuple[float, float, float]

The point of the box at the gap's other end, in the world frame, in meters.

KeepOutBox dataclass

KeepOutBox(
    name: str,
    center_m: tuple[float, float, float],
    size_m: tuple[float, float, float],
)

A box in the workcell the robot is kept out of: a table, a wall, a fixture.

It stands square to the world frame, the one Mount places the units in. Its name must be unique in the workcell and every side a positive length, checked where the workcell is built or next handed over.

Attributes:

Name Type Description
name str

Its name, unique in the workcell, e.g. table.

center_m tuple[float, float, float]

Its center in the world frame, in meters.

size_m tuple[float, float, float]

Its length along each of the world frame's axes, in meters.

name instance-attribute

name: str

Its name, unique in the workcell, e.g. table.

center_m instance-attribute

center_m: tuple[float, float, float]

Its center in the world frame, in meters.

size_m instance-attribute

size_m: tuple[float, float, float]

Its length along each of the world frame's axes, in meters.

Clearance

Clearance(robot: RobotConfig)

How near a robot's links come to one another and to keep-out boxes, posed from its motors' positions.

Built once for a robot and asked as often as needed; it keeps no reference to the robot, so a later edit to the robot does not reach it. Every collision piece of the composed URDF is checked, except the pairs each arm's ArmDescription.excluded_pairs names. Each piece stands for a few dozen points on its convex hull, so a gap reads a few millimeters wide of the meshes' on a rounded piece. Freed when the object is garbage-collected.

Builds the clearance of robot as it is now.

Parameters:

Name Type Description Default
robot RobotConfig

The robot whose links are checked.

required

Raises:

Type Description
TypeError

robot is not a RobotConfig, such as a whole WorkcellConfig.

InvalidArgumentError

The robot is refused, its URDF cannot be composed, or it names a collision mesh the SDK has no hull for, as a description read from a directory of your own can.

Methods:

Name Description
arm_of

Returns the index into the robot's RobotConfig.arms of the arm that carries link.

nearest

Returns the nearest two pieces that are checked against each other, with the motors at q.

nearest_to

Returns the robot's piece nearest keep_out, with the motors at q, checking every piece.

within

Returns every pair of links checked against each other that stand no more than within_m apart at q.

within_of

Returns every link of the robot that stands no more than within_m from keep_out at q.

arm_of

arm_of(link: str) -> int | None

Returns the index into the robot's RobotConfig.arms of the arm that carries link.

An arm carries every link whose name starts with its prefix in the composed URDF, the longest prefix where several match; a robot of one follower carries every link.

Parameters:

Name Type Description Default
link str

The link, as the composed URDF names it, e.g. left_link5.

required

Returns:

Type Description
int | None

The arm's index; None for a link no arm carries, such as the torso a pair of arms stands on.

nearest

nearest(q: Sequence[float]) -> NearestPair | None

Returns the nearest two pieces that are checked against each other, with the motors at q.

Parameters:

Name Type Description Default
q Sequence[float]

Each slot's motor position, in radians, in slot order, as an anvil.control.Snapshot's q carries them. Entries past the robot's slots are ignored.

required

Returns:

Type Description
NearestPair | None

The pair and the gap between them; None for a robot with no two pieces checked against each other.

Raises:

Type Description
TypeError

q is not a flat sequence of numbers.

InvalidArgumentError

q has fewer entries than the robot has slots, or one of them is not finite.

nearest_to

nearest_to(
    q: Sequence[float], keep_out: KeepOutBox
) -> NearestLink | None

Returns the robot's piece nearest keep_out, with the motors at q, checking every piece.

Parameters:

Name Type Description Default
q Sequence[float]

Each slot's motor position, in slot order, as for nearest.

required
keep_out KeepOutBox

The box to measure to.

required

Returns:

Type Description
NearestLink | None

The piece and the gap; None for a robot with no collision pieces.

Raises:

Type Description
TypeError

q is not a flat sequence of numbers.

InvalidArgumentError

q has fewer entries than the robot has slots or one of them is not finite, or keep_out does not have three numbers for its center and its size.

within

within(
    q: Sequence[float], within_m: float
) -> list[NearestPair]

Returns every pair of links checked against each other that stand no more than within_m apart at q.

Each pair is judged on its own, so a pair that stands close however the arms move does not hide another one coming near, as it would behind nearest.

Parameters:

Name Type Description Default
q Sequence[float]

Each slot's motor position, in slot order, as for nearest.

required
within_m float

How far apart a pair may stand and still be returned, in meters; a negative or NaN one returns none.

required

Returns:

Type Description
list[NearestPair]

Each pair at the gap between its nearest two pieces, nearest first; empty where none is that near.

Raises:

Type Description
TypeError

q is not a flat sequence of numbers.

InvalidArgumentError

q has fewer entries than the robot has slots, or one of them is not finite.

within_of

within_of(
    q: Sequence[float],
    keep_out: KeepOutBox,
    within_m: float,
) -> list[NearestLink]

Returns every link of the robot that stands no more than within_m from keep_out at q.

Parameters:

Name Type Description Default
q Sequence[float]

Each slot's motor position, in slot order, as for nearest.

required
keep_out KeepOutBox

The box to measure to.

required
within_m float

How far from the box a link may stand and still be returned, in meters; a negative one returns only links at least that deep inside it, and NaN none.

required

Returns:

Type Description
list[NearestLink]

Each link at the gap from its nearest piece, nearest first; a link overlapping the box at minus how far it must move along one of the box's axes to leave it, so one sinking deeper reads nearer. Empty where none is that near.

Raises:

Type Description
TypeError

q is not a flat sequence of numbers.

InvalidArgumentError

q has fewer entries than the robot has slots or one of them is not finite, or keep_out does not have three numbers for its center and its size.

CameraRole

Bases: StrEnum

What a camera looks at.

An operator's screen shows the scene cameras big and the wrist cameras small.

Attributes:

Name Type Description
SCENE

The workspace, from off the arms: a head, chest or overhead camera.

WRIST

One arm's gripper, from a camera riding on that arm.

SCENE class-attribute instance-attribute

SCENE

The workspace, from off the arms: a head, chest or overhead camera.

WRIST class-attribute instance-attribute

WRIST

One arm's gripper, from a camera riding on that arm.

Lens

Bases: StrEnum

The lens fitted to a camera, which cannot be read off the camera, so a workcell says which.

Attributes:

Name Type Description
STANDARD

Rectilinear, 92 degrees diagonal.

PSEUDO_FISHEYE

151 degrees diagonal.

FISHEYE

186 degrees diagonal.

fov_h_deg float

Horizontal field of view, in degrees, from the lens's datasheet.

fov_v_deg float

Vertical field of view, in degrees, from the lens's datasheet.

STANDARD class-attribute instance-attribute

STANDARD

Rectilinear, 92 degrees diagonal.

PSEUDO_FISHEYE class-attribute instance-attribute

PSEUDO_FISHEYE

151 degrees diagonal.

FISHEYE class-attribute instance-attribute

FISHEYE

186 degrees diagonal.

fov_h_deg property

fov_h_deg: float

Horizontal field of view, in degrees, from the lens's datasheet.

fov_v_deg property

fov_v_deg: float

Vertical field of view, in degrees, from the lens's datasheet.

CameraConfig dataclass

CameraConfig(
    name: str,
    device: str,
    mode: Mode,
    role: CameraRole = CameraRole.SCENE,
    link: str | None = None,
    lens: Lens = Lens.STANDARD,
    controls: dict[str, int] = {},
)

One camera of a workcell: its name, the device it opens, the mode it streams, and where it is.

Checked where the workcell is built: a name that is empty or used twice, or a device used twice, is refused. An empty device opens nothing, so any number of sim cameras share it.

Attributes:

Name Type Description
name str

Its name, unique in the workcell, e.g. "head" or "wrist_l": what a recording and a viewer call it.

device str

The device node it opens, as given.

mode Mode

The anvil.camera.Mode it streams, one of those anvil.camera.modes lists for the device.

role CameraRole

What it looks at.

link str | None

The robot link it rides on, one of RobotConfig.camera_links(), which the sim renders as this camera.

lens Lens

The lens fitted to it.

controls dict[str, int]

The camera's own settings to write when it opens, each by the key anvil.camera.controls gives it.

name instance-attribute

name: str

Its name, unique in the workcell, e.g. "head" or "wrist_l": what a recording and a viewer call it.

device instance-attribute

device: str

The device node it opens, as given.

"/dev/video0", or a path that stays put across boots, such as its USB port's under /dev/v4l/by-path/.

mode instance-attribute

mode: Mode

The anvil.camera.Mode it streams, one of those anvil.camera.modes lists for the device.

role class-attribute instance-attribute

role: CameraRole = CameraRole.SCENE

What it looks at.

A workcell saved before cameras had a role reads as CameraRole.SCENE.

link: str | None = None

The robot link it rides on, one of RobotConfig.camera_links(), which the sim renders as this camera.

None for a camera on nothing of the robot's, which the sim has no view of.

lens class-attribute instance-attribute

lens: Lens = Lens.STANDARD

The lens fitted to it.

A workcell saved before cameras had a lens reads as Lens.STANDARD.

controls class-attribute instance-attribute

controls: dict[str, int] = {}

The camera's own settings to write when it opens, each by the key anvil.camera.controls gives it.

For example {"exposure_time_absolute": 156}. A setting not named keeps what the camera holds.

Preset dataclass

Preset(
    name: str, variant: str, arms: ArmCount, leaders: bool
)

A shipped workcell's name and shape, from WorkcellConfig.preset_shapes, building nothing until asked.

Methods:

Name Description
build

Returns the workcell, built fresh: WorkcellConfig.shipped of this shape.

Attributes:

Name Type Description
name str

The name of its WorkcellConfig class attribute, e.g. OPENYAM_V1_SINGLE.

variant str

The arms' variant, e.g. openyam/v1.

arms ArmCount

How many followers it stands.

leaders bool

Whether each follower has a leader.

name instance-attribute

name: str

The name of its WorkcellConfig class attribute, e.g. OPENYAM_V1_SINGLE.

variant instance-attribute

variant: str

The arms' variant, e.g. openyam/v1.

arms instance-attribute

arms: ArmCount

How many followers it stands.

leaders instance-attribute

leaders: bool

Whether each follower has a leader.

build

build() -> WorkcellConfig

Returns the workcell, built fresh: WorkcellConfig.shipped of this shape.

WorkcellConfig dataclass

WorkcellConfig(
    robot: RobotConfig,
    cameras: list[CameraConfig] = [],
    keep_out: list[KeepOutBox] = [],
)

A workcell: its robot, the cameras it streams, and the boxes its robot is kept out of.

The shipped workcells are class attributes, WorkcellConfig.OPENARM_V2_BIMANUAL, each built fresh on every access with no cameras and no keep-out boxes, since which a bench has is the bench's. Edit the arms, append a CameraConfig per camera and a KeepOutBox per obstacle, and save it; then hand robot to the hw interface and controllers you compose, and the workcell to anvil.camera.Cameras.

Raises:

Type Description
InvalidArgumentError

A camera with no name, two cameras of one name or of one non-empty device, a keep-out box with no name, two boxes of one name, or a box side that is not a positive length; or a robot refused as RobotConfig says, after an edit.

Methods:

Name Description
to_json

Returns the workcell as JSON, the form from_json reads back.

from_json

Builds a workcell from the JSON to_json wrote, checked as one built from its fields is.

load

Reads the workcell save wrote to path, checked as one built from its fields is.

save

Writes the workcell to path as indented JSON a person can edit, for load to read back.

presets

Returns every shipped workcell by its class attribute's name, each built fresh, e.g. for a --workcell flag.

preset_shapes

Returns every shipped workcell's name and shape, building none: what to choose among by variant and shape.

shipped

Builds a workcell of a shipped variant's arms, standing where the shipped stands bolt them.

Attributes:

Name Type Description
robot RobotConfig

The robot the run drives.

cameras list[CameraConfig]

The cameras, in the order stated.

keep_out list[KeepOutBox]

The boxes the robot is kept out of.

OPENARM_V1_BIMANUAL

Two OpenArm v1 arms, one torso, on the lift.

OPENARM_V2_BIMANUAL

Two OpenArm v2 arms, one torso, on the lift.

OPENYAM_V1_BIMANUAL

Two OpenYAM v1 arms on the lift's bracket, where its ports place them, 0.61 m apart.

OPENYAM_V1_SINGLE

One OpenYAM v1 arm, follower, with no side, on the lift.

OPENARM_V1_LEADER_FOLLOWER

Two OpenArm v1 torsos on no stand: the followers at the origin, and a leader torso 1 m to their left.

OPENARM_V2_LEADER_FOLLOWER

Two OpenArm v2 torsos on no stand: the followers at the origin, and a leader torso 1 m to their left.

OPENYAM_V1_LEADER_FOLLOWER

Four OpenYAM v1 arms on no stand: the followers spaced as on the bracket, each leader 0.6 m behind its own.

OPENYAM_V1_SINGLE_LEADER_FOLLOWER

One OpenYAM v1 follower on no stand and its leader 0.6 m behind it, neither with a side.

OPENYAM_V2_BIMANUAL

Two OpenYAM v2 arms on the lift's bracket, where its ports place them, 0.61 m apart.

OPENYAM_V2_SINGLE

One OpenYAM v2 arm, follower, with no side, on the lift.

OPENYAM_V2_LEADER_FOLLOWER

Four OpenYAM v2 arms on no stand: the followers spaced as on the bracket, each leader 0.6 m behind its own.

OPENYAM_V2_SINGLE_LEADER_FOLLOWER

One OpenYAM v2 follower on no stand and its leader 0.6 m behind it, neither with a side.

robot instance-attribute

robot: RobotConfig

The robot the run drives.

cameras class-attribute instance-attribute

cameras: list[CameraConfig] = []

The cameras, in the order stated.

keep_out class-attribute instance-attribute

keep_out: list[KeepOutBox] = []

The boxes the robot is kept out of.

OPENARM_V1_BIMANUAL class-attribute instance-attribute

OPENARM_V1_BIMANUAL

Two OpenArm v1 arms, one torso, on the lift.

OPENARM_V2_BIMANUAL class-attribute instance-attribute

OPENARM_V2_BIMANUAL

Two OpenArm v2 arms, one torso, on the lift.

OPENYAM_V1_BIMANUAL class-attribute instance-attribute

OPENYAM_V1_BIMANUAL

Two OpenYAM v1 arms on the lift's bracket, where its ports place them, 0.61 m apart.

OPENYAM_V1_SINGLE class-attribute instance-attribute

OPENYAM_V1_SINGLE

One OpenYAM v1 arm, follower, with no side, on the lift.

OPENARM_V1_LEADER_FOLLOWER class-attribute instance-attribute

OPENARM_V1_LEADER_FOLLOWER

Two OpenArm v1 torsos on no stand: the followers at the origin, and a leader torso 1 m to their left.

OPENARM_V2_LEADER_FOLLOWER class-attribute instance-attribute

OPENARM_V2_LEADER_FOLLOWER

Two OpenArm v2 torsos on no stand: the followers at the origin, and a leader torso 1 m to their left.

OPENYAM_V1_LEADER_FOLLOWER class-attribute instance-attribute

OPENYAM_V1_LEADER_FOLLOWER

Four OpenYAM v1 arms on no stand: the followers spaced as on the bracket, each leader 0.6 m behind its own.

OPENYAM_V1_SINGLE_LEADER_FOLLOWER class-attribute instance-attribute

OPENYAM_V1_SINGLE_LEADER_FOLLOWER

One OpenYAM v1 follower on no stand and its leader 0.6 m behind it, neither with a side.

OPENYAM_V2_BIMANUAL class-attribute instance-attribute

OPENYAM_V2_BIMANUAL

Two OpenYAM v2 arms on the lift's bracket, where its ports place them, 0.61 m apart.

OPENYAM_V2_SINGLE class-attribute instance-attribute

OPENYAM_V2_SINGLE

One OpenYAM v2 arm, follower, with no side, on the lift.

OPENYAM_V2_LEADER_FOLLOWER class-attribute instance-attribute

OPENYAM_V2_LEADER_FOLLOWER

Four OpenYAM v2 arms on no stand: the followers spaced as on the bracket, each leader 0.6 m behind its own.

OPENYAM_V2_SINGLE_LEADER_FOLLOWER class-attribute instance-attribute

OPENYAM_V2_SINGLE_LEADER_FOLLOWER

One OpenYAM v2 follower on no stand and its leader 0.6 m behind it, neither with a side.

to_json

to_json() -> str

Returns the workcell as JSON, the form from_json reads back.

from_json classmethod

from_json(wire: str) -> Self

Builds a workcell from the JSON to_json wrote, checked as one built from its fields is.

JSON an older release wrote reads as it was written: a field added since takes its default.

Parameters:

Name Type Description Default
wire str

The JSON text.

required

Raises:

Type Description
InvalidArgumentError

wire is not a workcell's JSON, or the workcell is refused.

load classmethod

load(path: Path) -> Self

Reads the workcell save wrote to path, checked as one built from its fields is.

A file an older release saved reads as it was saved: a field added since takes its default.

Parameters:

Name Type Description Default
path Path

The file to read.

required

Raises:

Type Description
NotFoundError

There is no file at path.

InvalidArgumentError

The file is not a workcell, a newer release saved it, or the workcell is refused; the message names the path.

OperationError

The file could not be read for another reason, such as being a directory.

save

save(path: Path) -> None

Writes the workcell to path as indented JSON a person can edit, for load to read back.

The file is to_json's JSON with the format's version beside it. The directory is made if it is missing, and the file is replaced whole, so a crash mid-save leaves the old file or the new one, never a torn one.

Parameters:

Name Type Description Default
path Path

The file to write.

required

Raises:

Type Description
InvalidArgumentError

The workcell is refused; nothing is written.

OperationError

The directory could not be made or the file could not be written; a subclass where the failure names the reason, as for load, such as anvil.NotFoundError.

presets staticmethod

presets() -> dict[str, WorkcellConfig]

Returns every shipped workcell by its class attribute's name, each built fresh, e.g. for a --workcell flag.

Builds all of them; preset_shapes lists them without building any.

preset_shapes staticmethod

preset_shapes() -> list[Preset]

Returns every shipped workcell's name and shape, building none: what to choose among by variant and shape.

shipped classmethod

shipped(
    variant: str, arms: ArmCount, leaders: bool
) -> Self

Builds a workcell of a shipped variant's arms, standing where the shipped stands bolt them.

Every preset is one of these shapes, and a shape no preset names is built the same way, e.g. a single yam/v1. A pair's arms are left and right, their leaders leader_left and leader_right; a single arm is follower and its leader leader, neither with a side. Each leader carries the leader handle in place of a gripper. Without leaders it stands on Stand.LIFT, a yam pair on Stand.LIFT_BRACKET; with leaders on no stand, a yam pair spaced as the bracket spaces it. The workcell has no cameras and no keep-out boxes.

Parameters:

Name Type Description Default
variant str

The variant, family/vN, e.g. "openyam/v1".

required
arms ArmCount

One follower or a pair.

required
leaders bool

Whether each follower gets a leader.

required

Raises:

Type Description
InvalidArgumentError

Anvil ships no description or URDF for variant, or arms is ArmCount.SINGLE for an OpenArm, whose unit is a torso carrying both arms.

shipped_urdf

shipped_urdf(variant: str) -> str

Returns the URDF text a shipped variant carries: the unit a robot's mounts stand copies of.

For a yam that is one arm on its base plate; for an OpenArm, the torso carrying both arms.

Parameters:

Name Type Description Default
variant str

The variant, family/vN, e.g. "openyam/v1".

required

Raises:

Type Description
InvalidArgumentError

Anvil ships no URDF for variant.

realhand_gripper

realhand_gripper(
    family: str, version: int, side: Side
) -> ExternalGripper

Returns the external gripper an arm states for the RealHand L6 bolted to it, as the hand's description gives it.

It holds the CAN ID the side hand answers on, which the arm's motors must not use, and the hand's mass in the arm's hand frame, which gravity compensation carries. Set it as that arm's ArmDescription.gripper.

Parameters:

Name Type Description Default
family str

The arm's family, ArmDescription.family, e.g. "openarm".

required
version int

The arm's version, ArmDescription.version, e.g. 2.

required
side Side

Which hand: the two answer on different CAN IDs, and their masses mirror each other.

required

Raises:

Type Description
ValueError

side is not a side.

InvalidArgumentError

The hand's description has no mass on that arm.

shipped_finger_sets

shipped_finger_sets(gripper: str) -> list[FingerSet]

Returns every finger set Anvil ships for gripper, in name order: what its Gripper.fingers may be set to.

Parameters:

Name Type Description Default
gripper str

The gripper, family/vN, e.g. "anvil-gripper/v1".

required

Returns:

Type Description
list[FingerSet]

The finger sets; empty for a gripper whose fingers are its own, and for a name Anvil ships no gripper by.