Standard parts (bt.parts)¶
Generators for the structures every cell has — fences, walls, tables, pedestals, racks, conveyor bodies, pallets, light curtains, stairs, a machining centre with its door and panel — built from ordinary residents (boxes, frames, a device or a sensor) with their part identity pinned, so the BOM counts them and the layout sheet labels them. See Standard parts and CAD geometry.
bt.parts.fence(scene, "fence", path=[(-2, -2), (2, -2), (2, 2), (-2, 2)],
height=2.0, panel_pitch=1.0, door=(0, 2), model="ST20")
ped = bt.parts.pedestal(scene, "pedestal", height=0.5, position=(0, 0))
scene.set_robot_base_pose(*scene.frame(ped.frames[0]))
parts
¶
Standard structures, generated from parameters: fences, walls, tables, pedestals, racks, conveyor bodies, pallets, light curtains, stairs, control cabinets, a machining centre with its door, its panel and a vise — the scenery every cell has and nobody wants to model.
Each generator composes the ordinary scene API — add_box, add_frame,
add_conveyor, add_beam_sensor, set_part — so what it builds is plain
residents: boxes under a name prefix (fence/panels/n0, table/top), a
frame where the next thing mounts, a device or a sensor where one belongs,
and a part on the group with the quantity, so the BOM counts panels and
posts and the layout sheet labels the assembly once. Change a parameter and
the geometry, the BOM line and the sheet change together.
bt.parts.fence(scene, "fence", path=[(-2, -2), (2, -2), (2, 2), (-2, 2)],
height=2.0, panel_pitch=1.0, door=(1, 1), model="ST20")
ped = bt.parts.pedestal(scene, "pedestal", height=0.5, position=(0, 0))
scene.set_robot_base_pose(*scene.frame(ped.frames[0]))
Pass catalog= instead of a model string and the parameters come from the
catalog: the height is checked against the ones that are sold, the panels are
laid out in widths that exist, and every line of the BOM carries the part
number you would order it by.
bt.parts.fence(scene, "fence", path=[...], catalog="botrail/fence/mesh-guard",
height=2.0, door=(0, 1))
botrail does not model shapes. These are boxes arranged by parameters, and
that is the point: a fence is panels of a pitch along a path, a table is
a top on legs — the meaning is what the BOM and the sheet need, and the
few centimetres a real profile differs by change nothing a cell verifies.
Anything with a shape of its own comes in from CAD as a mesh (see the
Geometry Provider pattern in the standard-parts guide) and gets its
identity the same way, with set_part.
BELT
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.
BUTTON_BY_NAME
module-attribute
¶
BUTTON_BY_NAME = {
"cycle_start": "green",
"start": "green",
"feed_hold": "red",
"stop": "red",
"estop": "red",
"reset": "blue",
"clamp": "yellow",
"unclamp": "yellow",
"door": "white",
}
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
BUTTON_CAP
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
BUTTON_COLORS
module-attribute
¶
BUTTON_COLORS = {
"green": (0.02, 0.35, 0.06),
"red": (0.55, 0.02, 0.02),
"yellow": (0.75, 0.55, 0.02),
"blue": (0.02, 0.1, 0.45),
"white": (0.8, 0.8, 0.78),
"black": (0.02, 0.02, 0.02),
}
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
BUTTON_FORCE_N
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
BUTTON_TRAVEL
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
CABINET
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.
CHECKER_PLATE
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.
CONCRETE
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.
DARK_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.
DETAIL_MODES
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.
DOOR_DRIVES
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.
DOOR_SPEED
module-attribute
¶
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
ESTOP_CAP
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
ESTOP_FORCE_N
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
FENCE_FRAME
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.
FENCE_PANEL
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.
FENCE_POST
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.
LATHE_APERTURE
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.
LATHE_CHAMBER
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
LATHE_CHUCK
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
LATHE_SIZE
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.
LATHE_SPINDLE
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.
LATHE_TURRET
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.
MACHINE_ACCENT
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.
MACHINE_BED
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.
MACHINE_SHELL
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.
MACHINE_WINDOW
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.
MOTOR
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.
PLASTER
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.
REFLECTOR
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.
SAFETY_ORANGE
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.
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.
TABLE_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.
TYPE_CHECKING
module-attribute
¶
bool(x) -> bool
Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.
VMC_APERTURE
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.
VMC_CHAMBER
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
VMC_EXCHANGE
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.
VMC_FRONT_DOOR
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.
VMC_HEAD_CLEARANCE
module-attribute
¶
Convert a string or number to a floating-point number, if possible.
VMC_SIZE
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.
VMC_TABLE
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.
WOOD
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.
Built
¶
What a generator put into the scene, by name — the obstacles, and the frames, devices and sensors that came with them — so the caller can mount on the frame, drive the device, or take the whole thing down.
remove
¶
Takes everything this generator added out of the scene (parts go with their residents).
MachineTool
¶
Bases: botrail.parts.Built
What machine_tool built, plus the names a tending program
addresses: the side door's axis (door, None for a manual door or
none), what rides on that door (door_objects — the leaf and its
trim, for a robot that slides it by hand), its end-of-travel lanes
(door_lanes = closed, open — the axis's stop lanes, or two zone
sensors on a loose leaf), the stroke and the world direction it opens
along (door_travel, door_axis), the front door's closed switch and
the E-stop lane the machine's program is guarded by (front_door_lane,
estop), the operator panel's Built and the button sensors on it.
door_axis
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.
door_travel
class-attribute
¶
Convert a string or number to a floating-point number, if possible.
buildable_lengths
¶
The edge lengths nearest length_mm that these panels can actually
make — what to move a corner to when a run does not come out.
cabinet
¶
cabinet(
scene,
name: str,
size: Optional[tuple] = None,
position: tuple | tuple = (0.0, 0.0),
*,
catalog: Optional[CatalogRef] = None,
detail: Optional[str] = None,
base: Optional[bool] = None,
plate: Optional[bool] = None,
base_height: Optional[float] = None,
yaw: float = 0.0,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.58, 0.56, 0.49),
**attributes,
) -> Built
A control cabinet: size = (width, depth, height) standing at
position (its centre, x, y[, floor z]), door face on -Y before yaw.
Adds the frame <name>/front at the centre of the door face at floor
level — where an operator stands, and what a maintenance-space check
will measure from — and pins the enclosure (structure.cabinet).
The panel builder's customisation is what this generator carries: the
enclosure is the article (what is inside it is other people's BOM
lines), and the plinth base and the mounting plate are articles of their
own. base= stands the body on its plinth (<name>/base), plate=
stands the mounting plate inside (<name>/plate) — each is one more
line on the BOM when a catalog names it.
With catalog= — the id of a cabinet spec pack, or a package directory —
an enclosure you can order: width, depth and height are matched against
the sizes that are sold, the BOM row carries the article number they
compose into, and base and plate default to whatever the pack sells
(pass base=False / plate=False to leave them out). A combination
nobody sells is refused by the pack's mass table.
detail="full" (the default with a catalog) draws the door leaves and
their handles — or the pack's own drawing (trim:) — as decoration that
never collides. The massing stays the body (and its plinth).
chuck
¶
chuck(
scene,
name: str,
position: tuple,
quaternion: Optional[
tuple[float, float, float, float]
] = None,
*,
diameter: float = 0.165,
length: float = 0.085,
jaws: int = 3,
jaw_height: float = 0.03,
jaw_width: float = 0.025,
opening: float = 0.05,
max_opening: Optional[float] = None,
catalog: Optional[CatalogRef] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.2, 0.21, 0.23),
**attributes,
) -> Built
A lathe chuck: a diameter body length long with its face at
position, its axis the +Z of quaternion (pass a lathe's
<name>/spindle frame — bt.parts.chuck(scene, "chuck",
*scene.frame("lathe/spindle"))), and jaws jaw blocks standing
jaw_height off the face around a part of opening diameter — the
gripping diameter, so a robot loading a part along the axis meets
the jaws where they are. Frame <name>/face: the face centre, +Z out
along the spindle axis (a load comes in along -Z). Part: fixture.chuck
with the diameter, the opening and the jaw count; with catalog= the
diameter is matched against the ones sold and the maximum opening
comes from the pack.
conveyor
¶
conveyor(
scene,
name: str,
length: Optional[float] = None,
width: Optional[float] = None,
position: tuple | tuple = (0.0, 0.0),
*,
catalog: Optional[CatalogRef] = None,
detail: Optional[str] = None,
direction: tuple = (1.0, 0.0),
speed: Optional[float] = None,
running: bool = False,
zone_height: float = 0.15,
belt_thickness: Optional[float] = None,
rail: Optional[float] = None,
legs: bool = True,
leg: Optional[float] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.2, 0.21, 0.23),
**attributes,
) -> Built
A belt conveyor: length along direction, width across, its belt
surface centred at position (x, y, z of the surface). Builds the body —
belt slab, two side rails, legs — as obstacles under <name>/, and the
conveyor device <name> whose transport zone sits on the belt
(zone_height tall, speed along direction). The part is pinned on the
device (conveyor): the body is its geometry, not a second product. Adds
the frames <name>/infeed and <name>/outfeed at the belt ends.
With catalog= — the id of a conveyor spec pack, or a package directory —
a conveyor you can order: the length, belt width and stand height are
matched against the ones that are sold (omit them and the catalog's
defaults apply, so position may be given as (x, y)), the speed is
checked against the range the drive covers, and the mass follows the
length. The stands are spaced by the catalog's maximum span and land on
the BOM as their own line.
fence
¶
fence(
scene,
name: str,
path: Sequence[tuple],
*,
catalog: Optional[CatalogRef] = None,
detail: Optional[str] = None,
height: Optional[float] = None,
panel_pitch: Optional[float] = None,
post: Optional[float] = None,
panel_thickness: Optional[float] = None,
closed: bool = True,
door: Optional[tuple[int, int]] = None,
door_model: Optional[str] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
post_model: Optional[str] = None,
panel_color: tuple = (0.55, 0.58, 0.6),
post_color: tuple = (0.16, 0.17, 0.19),
**attributes,
) -> Built
A safety fence along path (floor corners, metres), with a post at
every corner and between panels. closed joins the last corner back to
the first. door=(edge, slot) makes that slot the door — its own
obstacle <name>/door and its own BOM line.
Without a catalog each edge is split into panels of about panel_pitch
(the pitch is stretched so an edge takes a whole number), and two parts
are pinned: <name> (the panels, qty = panels, with the model /
manufacturer / mass_kg you passed) and <name>/posts.
With catalog= — the id of a fence spec pack, or a package directory —
the fence is built out of panels that exist. height is checked against
the heights that are sold, the widths come from the catalog and each edge
is filled with the fewest of them that reach its length, and any catalog
parameter can be set by name (mesh_mm="20x20"). The parts pinned are
then <name> (the fence as one product, so the layout sheet still labels
it once), one group per panel width carrying that width's part number
and count, <name>/posts and <name>/door — a bill you can order from.
detail="full" (the default with a catalog) draws each panel the way it
looks — a tube frame with a grid of wire in it, posts of the section the
catalog sells, a plate under each — as decoration that never collides;
the panel slab underneath still does, so nothing about the verification
changes. detail="plain" is the bare massing. Returns the names it
made.
lathe
¶
lathe(scene, name: str, size: Optional[tuple] = None, position: tuple | tuple = (0.0, 0.0), *, yaw: float = 0.0, aperture: Optional[tuple] = None, door: Union[str, None, object] = <object object at 0x7f18d36928c0>, door_travel: Optional[float] = None, door_speed: Optional[float] = None, spindle: Optional[tuple] = None, chamber: Optional[float] = None, turret: Optional[tuple] = None, tailstock: bool = False, panel: Optional[str] = 'front', buttons: Optional[Sequence[str]] = None, panel_pitch: float = 0.045, wall: float = 0.06, catalog: Optional[CatalogRef] = None, detail: Optional[str] = None, model: Optional[str] = None, manufacturer: Optional[str] = None, color: tuple = (0.62, 0.63, 0.62), **attributes) -> MachineTool
A CNC lathe as the envelopes a tending cell verifies against —
the turning counterpart of machine_tool. size = (length, depth,
height) stands at position, front face on -Y before yaw, the
spindle axis along the length (+X toward the tailstock). Without
arguments it is the Haas ST-10 of the public spec pages (LATHE_*
above); the front opening, the spindle's height and depth are design
values, the first to replace when the drawing is at hand.
What it puts in the scene, all of it collision-checked:
- the enclosure — bed, the rear block behind the
chamber, roof, end walls, and the front wall around the door openingaperture = (width, height, sill), centred on the spindle; - the headstock and the spindle nose at
spindle = (x from the body centre, depth behind the front wall's inner face, height), the turret envelopeturret = (x, y, z size)right of it at spindle height, and a tailstock block at the far end whentailstock=True— the chuck is a part of its own (bt.parts.chuck(scene, "chuck", *scene.frame("<name>/spindle"))); - the front door: a leaf that slides toward the tailstock end by
door_travel.door="servo"/"air"make it a linear axis<name>/front_doorwith the stopsclosedandopen, checked against every robot each tick;door="manual"(the default) leaves the leaf loose for a robot that takes the handle;door=Nonebuilds a solid front. Either way the lanes<name>/front_door/closedand/openread the leaf at its ends of travel; - an operator panel on the front face, right of the opening
(
panel="front";Noneleaves it off).
Frames: <name>/spindle (the spindle nose centre, +Z out along the
axis toward the tailstock — a load comes in along -Z),
<name>/entry (the opening's centre, 150 mm outside the leaf),
<name>/door/front/handle (+Z into the leaf), the panel's.
The returned MachineTool names the door axis, its lanes, the stroke
and its world direction, the panel and its buttons, the E-stop lane —
what bt.tending and a teach read. A lathe has the one door, so its
front_door_lane is None and no door-exclusivity guard applies.
Refused rather than clipped: an opening that does not fit the front,
a stroke that runs the leaf off the body, a spindle outside the
chamber.
With catalog= a lathe spec pack's mechanical.envelope
(doors.front, spindle, turret, chamber_depth_mm) and the door
drive it sells (front_door) stand in for the figures, its articles
land on the bill, and its interface rides on the result.
light_curtain
¶
light_curtain(
scene,
name: str,
frm: tuple,
to: tuple,
*,
height: Optional[float] = None,
beam_height: Optional[float] = None,
column: float = 0.04,
watch_robot: bool = True,
watch: Optional[list[str]] = None,
catalog: Optional[CatalogRef] = None,
resolution: Optional[float] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.16, 0.17, 0.19),
**attributes,
) -> Built
A light curtain between two floor points: two mounting columns
<name>/column_a|b of height (1.2 m unless given), and a beam sensor
<name> at beam_height (half the height by default) spanning the
gap between their lens faces — pulled in off the column centres, so the
curtain is not born tripped by its own housings. With the defaults it
trips on anything that enters the field, robot links and objects alike;
watch=[...] with watch_robot=False narrows it to the named objects,
watch=[] to robot links alone. The part (sensor.light_curtain) is
pinned on the sensor; the columns are its mounting geometry.
With catalog= — the id of a light-curtain spec pack, or a package
directory — a curtain you can order: height is the protective height
and is matched against the ones sold, resolution (mm — the smallest
object it must catch: 14 for a finger, 25 for a hand) picks the type,
the columns take the maker's section, and the BOM row carries the model
number of the emitter/receiver pair and its mass. A beam longer than the
curtain's operating range is refused with the numbers — the same
range_mm a requirement check would ask of it.
machine_tool
¶
machine_tool(scene, name: str, size: Optional[tuple] = None, position: tuple | tuple = (0.0, 0.0), *, yaw: float = 0.0, aperture: Optional[tuple] = None, door: Union[str, None, object] = <object object at 0x7f18d36928c0>, door_side: Union[str, object] = <object object at 0x7f18d36928c0>, door_travel: Optional[float] = None, door_speed: Optional[float] = None, front_door: Union[tuple, None, object] = <object object at 0x7f18d36928c0>, chamber: Optional[float] = None, table: Optional[tuple] = None, exchange: Optional[tuple] = None, head_clearance: Optional[float] = None, panel: Optional[str] = 'front', buttons: Optional[Sequence[str]] = None, panel_pitch: float = 0.045, wall: float = 0.06, catalog: Optional[CatalogRef] = None, detail: Optional[str] = None, model: Optional[str] = None, manufacturer: Optional[str] = None, color: tuple = (0.62, 0.63, 0.62), **attributes) -> MachineTool
A vertical machining centre as the envelopes a tending cell
verifies against — not its shape. size = (width, depth, height)
stands at position (its centre, x, y[, floor z]), front face on -Y
before yaw. Without arguments it is the FANUC ROBODRILL
α-D21MiB5 Plus of the public catalogue (VMC_* above): change any
figure and the envelopes, the frames and the BOM line change together.
What it puts in the scene, all of it collision-checked:
- the enclosure — bed, side walls, roof, the rear column block (the
last
depth - chamberof the body), and a front wall around the front door opening (front_door = (width, height, sill)), with its leaf standing closed; - the table
table = (width, depth, top height)at the exchange position (exchange = (x, y)offset from the chamber centre, x toward the door side — a table that traverses to the door is what a tending robot reaches), and the spindle head above it fromhead_clearance(nose to table at Z max) to the roof; - the side door: an opening
aperture = (width, height, sill)in thedoor_sidewall and a leaf that slides toward the rear bydoor_travel.door="servo"/"air"make it a linear axis<name>/side_doorwith the stopsclosedandopen(bt.seq.move_to(door, "open")opens it,move_to(door, "closed")closes; the speed comes from the drive —door_speedoverrides), and the rollout checks the leaf against every robot each tick: a door closing on an arm is aDeviceCollisionby name.door="manual"leaves the leaf loose, for a robot that takes the handle (bt.seq.attachthedoor_objectsand run acartesian_line);door=Nonebuilds a plain wall. Either way the lanes<name>/side_door/closedand/openread the leaf at its ends of travel — the axis's stop lanes, or two zone sensors on a loose leaf — the limit switches a door interlock is written from; - an operator panel (
operator_panel) withbuttonsatpanel_pitch, on the front face (panel="front") or on the door-side wall ahead of the opening (panel="door", where a robot at the door reaches it);panel=Noneleaves it off.
Frames: <name>/table (centre of the table top), <name>/entry (the
side opening's centre, 150 mm outside the door leaf — where a robot
waits), <name>/door/side/handle (the leaf's handle, +Z into the
leaf), and the panel's <name>/panel/<button>[/press].
Refused rather than clipped, like a wall plan that does not close: an opening that does not fit its wall, a leaf whose stroke runs off the body, a spindle head that would stand through the roof.
detail="full" adds the windows, the door rails, the accent band and
the stack light — drawn, never collided. The part is pinned on the
group (machine_tool.vmc), the side door as <name>/side_door
(machine_tool.door, with its drive and stroke), the panel and its
buttons by operator_panel.
With catalog= — the id of a machine-tool spec pack, or a package
directory — a machine you can order: the body, the openings, the
table and the head come from the pack's mechanical.envelope, the
options it sells (column_mm, side_door, door_side) are chosen
by name and refused when nobody sells them, the door's speed follows
the drive's published time, every article lands on the bill with its
number, and the pack's interface (its handshake template and
signal table) rides on the returned MachineTool for bt.tending.
operator_panel
¶
operator_panel(
scene,
name: str,
position: tuple,
*,
yaw: float = 0.0,
tilt: float = 0.0,
size: tuple = (0.3, 0.22),
thickness: float = 0.03,
buttons: Sequence[str] = (
"cycle_start",
"feed_hold",
"reset",
"estop",
),
columns: Optional[int] = None,
pitch: float = 0.045,
cap: float = 0.0285,
travel: float = 0.0026,
proud: float = 0.01,
watch_robots: Optional[list[str]] = None,
catalog: Optional[CatalogRef] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
button_model: Union[
str, Mapping[str, str], None
] = None,
color: tuple = (0.2, 0.21, 0.23),
**attributes,
) -> Built
An operator panel: a plate size = (width, height) centred at
position, its face toward -Y before yaw, tilted up toward the
operator by tilt, with a grid of 22 mm pushbuttons on it.
A button is three things. A cap (decoration — drawn, never collided),
a zone sensor <name>/<button> the size of the cap and as deep as
the button's operating travel, sitting inside the cap face — so a
tool that touches the cap reads nothing and one that pushes it in the
2.6 mm a 22 mm actuator travels turns the input on, for as long as it
is held — and two frames: <name>/<button> on the cap face and
<name>/<button>/press the travel below it, both with +Z pointing
into the panel, which is where a pressing tool aims its approach axis.
Nothing moves: the stroke is a depth, and the input is the meaning.
A neighbouring button's zone is the check that a wide tool did not
press two.
Cap colours follow the name (cycle_start green, feed_hold red,
reset blue, …) and estop is drawn as the ø40 mushroom head with
its collar; each button's sensor is pinned as an hmi.button with the
head size, travel and actuating force, the panel itself as an
hmi.panel. By default any robot link trips a button;
watch_robots=[...] narrows it to the arms named.
With catalog= — the id of a pushbutton-box spec pack, or a package
directory — a box you can order: the number of buttons is matched
against the sizes sold (the box's face follows), the pitch, the cap
and the travel come from the pack, and the box, its buttons and the
E-stop land on the bill with their article numbers.
pallet
¶
pallet(
scene,
name: str,
position: tuple | tuple,
*,
size: tuple = (1.2, 1.0, 0.144),
deck_boards: int = 5,
yaw: float = 0.0,
model: Optional[str] = "EPAL 1",
manufacturer: Optional[str] = None,
color: tuple = (0.52, 0.36, 0.18),
**attributes,
) -> Built
A wooden pallet size = (length, width, height) on the floor at
position (centre): three bottom boards, nine blocks, deck_boards
top boards. Adds the frame <name>/top at the centre of the deck and
pins one part (pallet).
pedestal
¶
pedestal(
scene,
name: str,
height: Optional[float] = None,
position: tuple | tuple = (0.0, 0.0),
*,
catalog: Optional[CatalogRef] = None,
detail: Optional[str] = None,
top: Optional[tuple] = None,
base: Optional[tuple] = None,
column: Optional[float] = None,
plate: Optional[float] = None,
yaw: float = 0.0,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.2, 0.21, 0.23),
**attributes,
) -> Built
A robot pedestal: base plate, column, top plate, height from floor
to the top face at position. Adds the frame <name>/mount at the top
centre — the robot's base pose (scene.set_robot_base_pose(*scene.frame(
"<name>/mount"))) — and pins one part (structure.pedestal).
With catalog= — the id of a pedestal spec pack, or a package directory —
a stand you can order: the height is matched against the ones that are
sold (omit it for the pack's default) and the column, plates and their
footprints come from the pack, so the BOM names the stand a robot is
actually bolted to.
detail="full" (the default with a catalog) adds the gussets between the
column and the base — decoration that never collides.
photoelectric
¶
photoelectric(
scene,
name: str,
frm: tuple,
to: tuple,
*,
body: Optional[tuple] = None,
watch_robot: bool = False,
watch: Optional[list[str]] = None,
catalog: Optional[CatalogRef] = None,
sensing: Optional[str] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.2, 0.21, 0.23),
**attributes,
) -> Built
A photoelectric sensor: a beam <name> from the lens at frm to
to (both in metres, 3D) that trips on the named objects (watch — a
workpiece arriving on the belt) and/or on any robot link
(watch_robot), and the sensor body <name>/body behind the lens —
body = (depth, width, height), the amplifier-in-head block sold by
the million (20 x 11 x 31 mm unless given). The part
(sensor.photoelectric) is pinned on the sensor.
What stands at to follows the sensing method: a through-beam pair
puts the receiver <name>/receiver there, a retroreflective sensor its
reflector <name>/reflector, a diffuse one nothing — the beam ends on
the target itself.
With catalog= — the id of a photoelectric spec pack, or a package
directory — a sensor you can order: sensing picks the method the pack
sells (through_beam / retroreflective / diffuse / ...), the other
axes (sensing_range_mm, output, ...) are chosen by name, the body
takes the maker's dimensions, the BOM row carries the model number and
mass, and a reflector the maker sells separately is a line of its own.
A beam longer than the sensing range is refused with the numbers — the
same sensing_range_mm a requirement check would ask of it.
power_supply
¶
power_supply(
scene,
name: str,
position: tuple,
*,
size: Optional[tuple] = None,
yaw: float = 0.0,
catalog: Optional[CatalogRef] = None,
output_a: Optional[float] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.42, 0.44, 0.47),
**attributes,
) -> Built
A DIN-rail power supply: the box <name>/body, size = (width,
depth, height), standing on position (the centre of its foot — on a
rail inside a cabinet), turned by yaw. The part (power_supply)
carries output_v / output_a. Declare supply/load ports with
bt.connections to check the connected loads against this rating.
With catalog= — the id of a power-supply spec pack, or a package
directory — a unit you can order: output_a is matched against the
ratings sold, the box takes the size the pack lists for that rating
(rules.size_mm_by_output_a, width / depth / height), and the BOM row
carries the model number, its mass and its rating.
proximity
¶
proximity(
scene,
name: str,
frm: tuple,
direction: tuple = (1.0, 0.0, 0.0),
*,
sensing_range: Optional[float] = None,
body: Optional[tuple[float, float]] = None,
watch: Optional[list[str]] = None,
watch_robot: bool = False,
catalog: Optional[CatalogRef] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.42, 0.44, 0.47),
**attributes,
) -> Built
An inductive proximity switch: a beam <name> from the sensing face
at frm, sensing_range along direction — the few millimetres a
metal target must come within (4 mm unless given) — and the threaded
barrel <name>/body behind the face, body = (diameter, length) in
metres (an M12 x 47 mm barrel unless given). The beam trips on the
named objects (watch) and/or on any robot link. The part
(sensor.proximity) is pinned on the sensor.
With catalog= — the id of a proximity-switch spec pack, or a package
directory — a switch you can order: the pack's axes (size M8/M12/M18/
M30, shield, output, contact, connection …) are chosen by name,
the sensing range is the model's (sensing_range_mm), the barrel takes
the size the pack lists for the thread (rules.body_mm_by_size), and
the BOM row carries the model number and mass.
rack
¶
rack(
scene,
name: str,
size: Optional[tuple] = None,
position: tuple | tuple = (0.0, 0.0),
*,
catalog: Optional[CatalogRef] = None,
detail: Optional[str] = None,
levels: Optional[int] = None,
upright: Optional[float] = None,
shelf_thickness: Optional[float] = None,
yaw: float = 0.0,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
shelf_model: Optional[str] = None,
color: tuple = (0.42, 0.44, 0.47),
**attributes,
) -> Built
Shelving: size = (width, depth, height) standing on the floor at
position (its centre, x, y[, floor z]), with levels shelves evenly
spaced and the top one at height, on four corner uprights.
Adds a frame at the centre of every shelf's top face — <name>/level0 at
the bottom, upwards — which is where the parts on that shelf sit and what
a pick targets. Pins the bay (structure.rack) on the group.
With catalog= — the id of a rack spec pack, or a package directory — the
bay is one you can order: the width, depth, height and number of levels
are matched against what is sold (omit them for the catalog's defaults),
the shelves are a line of their own on the BOM with their own part number,
and a level spacing the catalog does not allow is refused.
Shelving sold as posts and shelves rather than as a bay works the same
way: a pack with an upright component and no bay puts the series on
the group line and the posts on their own, four of them, counted in the
packs the maker sells them in (rules.uprights_per_pack).
detail="full" (the default with a catalog) adds the beams under each
deck, the diagonal braces on the sides and the foot plates — decoration
that never collides, so the uprights and decks stay the only thing a
robot can hit.
remote_io
¶
remote_io(
scene,
name: str,
position: tuple,
*,
catalog: Optional[CatalogRef] = None,
di_units: Optional[int] = None,
do_units: Optional[int] = None,
points_per_unit: int = 16,
uplink=None,
place: Optional[str] = None,
yaw: float = 0.0,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.2, 0.21, 0.23),
**attributes,
) -> Built
A remote I/O station on a DIN rail: the bus coupler <name>/coupler
and its DI / DO terminal units <name>/di{i} / <name>/do{i} side by
side from position (the centre of the coupler's foot; the units run
along local +X, turned by yaw), and the I/O node <name>
(kind="remote_io", hung off uplink the way add_io_node takes it)
with a channel per point — DI0… and DO0…. The coupler is the part
(io.remote); each unit is a BOM line of its own.
With catalog= — the id of a remote-I/O spec pack, or a package
directory — a station you can order: di_units / do_units are matched
against what the pack sells, logic (PNP / NPN) picks the unit models,
the coupler and the units take the maker's widths and point counts, and
every line carries its model number and mass.
stairs
¶
stairs(
scene,
name: str,
*,
steps: Optional[int] = None,
rise: Optional[float] = None,
tread: Optional[float] = None,
width: Optional[float] = None,
position: tuple | tuple = (0.0, 0.0),
yaw: float = 0.0,
catalog: Optional[CatalogRef] = None,
detail: Optional[str] = None,
nosing: Optional[float] = None,
rail_height: Optional[float] = None,
rails: bool = True,
legs: bool = True,
model: Optional[str] = None,
rail_model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.42, 0.44, 0.47),
tread_color: tuple = (0.5, 0.52, 0.53),
rail_color: tuple = (0.91, 0.36, 0.02),
**attributes,
) -> Built
A steel stair flight, the kind bolted against a mezzanine: steps
checker-plate treads climbing rise per step along local +x from
position (rotated by yaw), carried on a plate stringer each side and
handed by a tubular rail in safety orange.
Every tread is a walkable box, so a legged machine's footfalls snap
onto it (see the legged guide); everything else —
stringers, support legs, the handrail — is an ordinary obstacle, so an
AGV driven into the flight fails its aisle check and an arm sweeping
through the rail collides. Adds the frames <name>/foot (on the floor
at the bottom) and <name>/top (the landing edge) — author the vehicle
path's z between them — and pins the flight (structure.stairs).
Each tread overhangs the one below by nosing, the way a real one does.
That overlap is what a walking machine needs at the seam: keep it at
least twice the foot radius, or a foothold lands in the gap between
two treads and the bake refuses it by name.
With catalog= — the id of a stair spec pack, or a package directory —
the flight is one you can order: the rise, tread, width and number of
steps are matched against what is sold, the sections come from the pack,
the handrails are a line of their own on the BOM (one per side), and a
combination the maker does not sell — too steep, too shallow for the
walking rule 2 x rise + tread — is refused with the numbers.
rails=False drops the handrail (a flight against a wall);
legs=False drops the support leg under the high end, which a flight
slung between two landings — a storey of a building stair — does not
have.
table
¶
table(
scene,
name: str,
size: Optional[tuple] = None,
position: tuple | tuple = (0.0, 0.0),
*,
catalog: Optional[CatalogRef] = None,
detail: Optional[str] = None,
top_thickness: Optional[float] = None,
leg: Optional[float] = None,
yaw: float = 0.0,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
top_model: Optional[str] = None,
color: tuple = (0.42, 0.44, 0.47),
**attributes,
) -> Built
A table size = (length, width, height) standing on the floor at
position (its centre, x, y[, floor z]): a top of top_thickness on
four legs. Adds the frame <name>/top at the centre of the top face —
where a fixture or a workpiece sits — and pins one part
(structure.table) on the group.
With catalog= — the id of a table spec pack, or a package directory — a
stand you can order: the sides are matched against the ones that are sold
(omit size for the pack's defaults, so position alone will do), the
profile section and the board thickness come from the pack, and where the
maker sells the board separately it lands on the BOM as its own line.
detail="full" (the default with a catalog) adds the rails under the
board and a pad under each foot — decoration that never collides, so the
legs and the board stay the only thing a robot can hit.
vise
¶
vise(
scene,
name: str,
position: tuple | tuple,
*,
yaw: float = 0.0,
jaw_width: float = 0.125,
opening: float = 0.06,
max_opening: float = 0.15,
jaw_height: float = 0.04,
jaw_thickness: float = 0.03,
body_height: float = 0.06,
body_length: float = 0.36,
catalog: Optional[CatalogRef] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
color: tuple = (0.46, 0.47, 0.49),
**attributes,
) -> Built
A machine vise standing on a table top at position (x, y[, the
table's top z]): the body, a fixed jaw and a moving jaw set opening
apart, the jaws clamping along local Y (the fixed jaw on +Y, the
screw end trailing off to -Y) before yaw. Adds the frame
<name>/jaw at the centre of the jaw floor between the jaws — where
the workpiece sits, jaw_width wide along X and opening across — and
pins the vise (fixture.vise).
Clamping is a signal, not a motion: the jaws stand where the part
goes and the cell's program says when it is held (a machine-tending
handshake's clamp — see bt.tending). An opening beyond
max_opening is refused with the numbers, the way a size nobody
sells is.
With catalog= — the id of a vise spec pack, or a package directory —
a vise you can order: jaw_width is matched against the ones sold,
the jaw and body figures and the maximum opening come from the pack,
and the BOM row carries its article number and mass.
wall
¶
wall(
scene,
name: str,
path: Sequence[tuple],
*,
height: float = 2.7,
thickness: float = 0.12,
base_z: float = 0.0,
closed: bool = False,
openings: Sequence[Sequence[float]] = (),
head: float = 2.1,
detail: Optional[str] = None,
color: tuple = (0.62, 0.6, 0.56),
trim_color: Optional[tuple] = None,
model: Optional[str] = None,
manufacturer: Optional[str] = None,
**attributes,
) -> Built
A partition along path (floor corners, metres): thickness thick,
height tall, standing off base_z. closed joins the last corner back
to the first, so a four-corner path is a room.
openings=[(edge, centre, width), ...] cuts a doorway width wide,
centred centre metres along that edge, and spans the wall over it —
the pier each side and the head above are ordinary obstacles, so a
machine driven at the pier fails its aisle check while one sent through
the opening passes. A fourth element sets that opening's clear height
(head otherwise); at or above the wall's own height it is a gap
through it, with nothing over. Each one adds the frame
<name>/opening{edge}_{i} on the floor at its centre, facing along the
wall — that is where a route is authored through it.
Corners get a thickness square column so two runs meet square, and
detail="full" adds a skirting to each face and a lining round each
opening as decoration (drawn, never collided). Pins one part
(structure.wall) carrying the run's length, height and thickness.
bt.parts.wall(scene, "corridor/north", path=[(0, 2.4), (18, 2.4)],
height=2.7, openings=[(0, 6.0, 0.9), (0, 11.0, 0.9)])