Requirements and the check (bt.select)¶
What the cell asks of every bill-of-materials line, compared with what the chosen part says — and every static check in one report. botrail derives and compares; it does not choose. See Selecting parts.
req = scene.requirements() # bt.select.requirements(scene)
print(req.to_markdown())
req["ur5e/tool"].minimum # {"payload_kg": 2.3, "stroke_mm": 150.0}
report = scene.check() # bt.select.check(scene)
assert report.ok, report.to_markdown()
select
¶
Requirements derived from the cell, and the selection check.
botrail does not choose parts. It derives what every bill-of-materials line
must be able to do — from the cell the part sits in — compares that with what
the chosen part says it can do, and reports where the two disagree or where
it does not know. Choosing stays with the person, the agent or the vendor;
:mod:botrail.catalog finds the real candidates to choose from.
req = scene.requirements() # one row per BOM line
print(req.to_markdown())
req["tool"].minimum # {"payload_kg": 2.3, "stroke_mm": 150.0}
report = scene.check() # I/O lint + sequences + parts + requirements
assert report.ok, report.to_markdown()
Vocabulary. A requirement names a spec the catalog names too, so a value
read from a catalog package (Robot.from_catalog, bt.parts.*(catalog=...),
bt.catalog.Product.identify) or typed by hand on set_part(...) lands in the
same column. The keys, and the attribute names that answer them
(:data:ALIASES):
| requirement | derived from | answered by |
|---|---|---|
payload_kg |
tool mass + the heaviest part the robot grasps; parts riding a vehicle's deck at start | payload_kg |
reach_mm |
the farthest taught target from the base, plus a margin; the table of a machining centre (or the spindle of a lathe) in the cell, through its opening | reach_mm |
stroke_mm |
the smallest side of the grasped parts (parallel gripper) | stroke_mm, opening_mm |
sensing_range_mm |
a beam sensor's span | sensing_range_mm, range_mm, max_range_mm |
range_mm |
a light curtain's span / an area sensor's half-diagonal | range_mm, max_range_mm, sensing_range_mm |
protective_height_mm |
a light curtain's post height | protective_height_mm, height_mm |
scan_fov_deg |
a lidar's authored sweep angle | scan_fov_deg |
length_mm, width_mm |
a conveyor's zone along and across its belt | length_mm / width_mm, belt_width_mm |
speed_mps |
a conveyor's belt speed, an axis speed | max_speed_mps, speed_max_mps, speed_mps |
max_speed_mps |
a vehicle's travel speed | the same |
max_climb_mps |
an aerial vehicle's climb rate | the same |
max_descent_mps |
an aerial vehicle's descent rate | the same |
flight_time_min |
an aerial vehicle's airborne time per cycle, from the baked timeline (requirements(timeline=tl)) |
the same |
load_kg |
parts on a conveyor / an axis; robots standing on a pedestal | load_kg, capacity_kg, max_load_kg, payload_kg |
torque_nm |
a screwdriver's tightening torque: the joint torque on the screws it drives (bt.assembly.fasten) |
torque_max_nm, max_torque_nm, torque_nm |
screw_length_mm, thread_max_mm (≥) / thread_min_mm (≤) |
the longest screw, the largest and smallest thread the screwdriver has to take | screw_length_mm, thread_max_mm, thread_min_mm |
di do ai ao safe_di safe_do |
points assigned to an I/O node | the node's declared channels |
Every requirement is a minimum (>=) unless noted. The derivations are
geometric and deterministic — no sizing, no safety evaluation; a value that
cannot be derived (a grasped part without mass_kg) is reported as a note,
never guessed.
ALIASES
module-attribute
¶
ALIASES = {
"payload_kg": ("payload_kg",),
"reach_mm": ("reach_mm",),
"arm_count": ("arm_count",),
"stroke_mm": ("stroke_mm", "opening_mm"),
"aperture_mm": ("aperture_mm",),
"grip_force_n": (
"grip_force_max_n",
"grip_force_min_n",
),
"sensing_range_mm": (
"sensing_range_mm",
"range_mm",
"max_range_mm",
),
"fov_deg": ("fov_h_deg", "hfov_deg", "fov_deg"),
"resolution_h_px": ("resolution_h_px",),
"resolution_v_px": ("resolution_v_px",),
"max_range_mm": ("max_range_mm",),
"min_range_mm": ("min_range_mm",),
"scan_fov_deg": ("scan_fov_deg",),
"range_mm": (
"range_mm",
"max_range_mm",
"sensing_range_mm",
),
"protective_height_mm": (
"protective_height_mm",
"height_mm",
),
"length_mm": ("length_mm",),
"width_mm": ("width_mm", "belt_width_mm"),
"speed_mps": (
"max_speed_mps",
"speed_max_mps",
"speed_mps",
),
"max_speed_mps": (
"max_speed_mps",
"speed_max_mps",
"speed_mps",
),
"vertical_reach_min_mm": ("vertical_reach_min_mm",),
"vertical_reach_max_mm": ("vertical_reach_max_mm",),
"max_climb_mps": ("max_climb_mps",),
"max_descent_mps": ("max_descent_mps",),
"flight_time_min": ("flight_time_min",),
"load_kg": (
"load_kg",
"capacity_kg",
"max_load_kg",
"payload_kg",
),
"torque_nm": (
"torque_max_nm",
"max_torque_nm",
"torque_nm",
),
"screw_length_mm": (
"screw_length_mm",
"max_screw_length_mm",
"screw_length_max_mm",
),
"thread_max_mm": ("thread_max_mm",),
"thread_min_mm": ("thread_min_mm",),
"output_a": ("output_a", "current_a"),
"cable_m": ("cable_m", "cable_length_m"),
"di": ("di",),
"do": ("do",),
"ai": ("ai",),
"ao": ("ao",),
"safe_di": ("safe_di",),
"safe_do": ("safe_do",),
}
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)
CheckReport
¶
Every static check of a cell in one list — what botrail check prints.
Finding
¶
Finding(severity: 'str', code: 'str', message: 'str', target: 'Optional[str]' = None)
Requirement
¶
One thing a BOM line must be able to do, and whether its part can.
basis
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
lint
class-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.
op
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
status
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
Requirements
¶
What the cell asks of every BOM line — the result of :func:requirements.
Row
¶
check
¶
check(
scene,
*,
sequences: Optional[list[str]] = None,
timeline=None,
cable_slack_m: float = 1.0,
service_clearance_m: float = 0.9,
) -> CheckReport
Every static check in one report: the I/O lint, each sequence walked
for dangling references, unidentified equipment lines (with what the
cell asks of them), the requirement comparison, and the placement of
the cabinets — a controller nobody has placed (controller_unplaced),
and the service space in front of a control cabinet's or controller's
door blocked by something (service_space; service_clearance_m
deep, or what the part states as service_clearance_mm). timeline
(a baked cycle) adds the cycle-fact requirements — an aerial vehicle's
flight time; cable_slack_m is the robot cable allowance beyond the
run from arm to box. Errors make ok false; botrail check prints
exactly this.
requirements
¶
requirements(
scene,
*,
sequences: Optional[list[str]] = None,
margin: float = 0.1,
timeline=None,
cable_slack_m: float = 1.0,
) -> Requirements
Derive what every BOM line must be able to do from the cell it is in,
and compare it with what the chosen part says (its catalog specs or the
attributes typed on set_part).
sequences limits the programs whose grasps and I/O points are counted
(default: all). margin is added to the reach and flight-time
requirements (0.1 = 10 %). timeline is an optional baked
simulate_sequences result: cycle facts only it can supply — an aerial
vehicle's airborne time — are derived from it, and left as a note when
it is absent. cable_slack_m is what a placed controller's robot cable
needs beyond the run along the axes from the arm's base to the box (a
drop, a loop, the terminations). Nothing is chosen and nothing is
sized: a number the cell cannot supply (a grasped part with no
mass_kg) becomes a note, not a guess.