Skip to content

Catalog search (bt.catalog)

Real products to choose from: the catalog's published index, filtered the way a requirement reads. See Selecting parts.

cands = bt.catalog.search("gripper.parallel", stroke_mm=150, payload_kg=2.3)
cands[0].identify(scene, "ur5e/tool")
bt.catalog.search_for(scene.requirements()["eye"], level="V2")

Public UR Series and UR+ reference models

The r2 packages for UR8 Long, UR15, UR18, UR20, UR30, OnRobot RG6 and Ewellix LIFTKIT-UR are prebuilt public models. They load without running catalog-builder or installing ROS:

import botrail as bt

arm = bt.Robot.from_catalog("universal_robots/ur/ur20/r2")
gripper = bt.Robot.from_catalog("onrobot/rg/rg6/r2")
lift = bt.Robot.from_catalog("ewellix/liftkit/liftkit-ur/r2")
# Also available: ur8-long, ur15, ur18 and ur30 in universal_robots/ur/*/r2.
# Use format="usd" to choose USD instead of the default URDF.

These are reference shells: the UR arms keep the official kinematics, joint limits, inertia and tool0 frame under approximate covers; the RG6 is a one-drive parallel linkage (0 rad opens about 160 mm, 1.3 rad closes it until the pads touch — aim a little short of that for an empty grasp); the LIFTKIT is the 620 variant with its 800 mm stroke (0–0.4 m of drive, the second stage following, 0.905–1.705 m of mount height, 0.08 m/s over both stages). Mounting holes, adapters and communication are not in them, and compatibility.model_fidelity in each manifest says how far to trust the geometry and the masses. The r1 revisions they replace were built from meshes the catalog has no clearance to redistribute, so it never shipped their models — and it no longer lists them. Pin the full id and a revision to reproduce a cell, and save_project bundles the geometry for replay without the catalog.

catalog

Search the botrail catalog — real products to choose from.

The catalog (the Hugging Face dataset botrail/botrail-catalog) publishes an index.json with every product's category, maker, numeric specs and validation level. This module reads that index and filters it the way a requirement reads — minimums on spec keys — so a person or an agent picks from products that exist, with the numbers that decide the pick in view. botrail still does not choose: :func:search returns candidates, and :meth:Product.identify writes the one you picked onto the cell.

cands = bt.catalog.search("gripper.parallel", stroke_mm=150, payload_kg=2.3)
cands[0].identify(scene, "ur5e/tool")           # set_part with the catalog id and its specs
bt.catalog.search_for(scene.requirements()["eye"])   # straight from a requirement row

Spec keys are the requirement vocabulary of :mod:botrail.select — the catalog's specs / mechanical / electrical numbers, read through the same aliases. A product that does not state a filtered key does not match (unknown is not a pass). Results are ordered by validation level, then by closeness to the requested minimums, then by id — the same query gives the same list.

The index is fetched with huggingface_hub (pip install botrail[catalog]) and pinned to a dataset commit; a copy already in the Hugging Face cache is used when the hub cannot be reached, and index(path=...) / BOTRAIL_CATALOG_INDEX read a local file (a builder's dist/index.json).

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)

LEVELS module-attribute

LEVELS = ('V0', 'V1', 'V2', 'V3', 'V4', 'V5')

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.

REPO_ID module-attribute

REPO_ID = 'botrail/botrail-catalog'

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'.

REPO_URL module-attribute

REPO_URL = "https://huggingface.co/datasets/botrail/botrail-catalog"

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'.

Index

The catalog index: every product, and the search over them.

get

get(query: str) -> Product

A product by exact id, or by a unique id whose path segments contain the query's segments in order (universal_robots/ur5e); several revisions of one product prefer the newest public revision, matching Robot.from_catalog. If none is public, return the newest metadata entry; loading it requires a local build. Exact IDs are unchanged, and search/iteration still include every revision.

search

search(
    category: Optional[str] = None,
    *,
    kind: Optional[str] = None,
    manufacturer: Optional[str] = None,
    level: Optional[str] = None,
    text: Optional[str] = None,
    limit: Optional[int] = None,
    strict: bool = False,
    **specs: Any,
) -> list[Product]

Products matching a category (prefix: gripper matches gripper.parallel), a maker, a minimum validation level, a text fragment of id or name, and spec filters: key=value means the product states key >= value; key__max=value means <= value; a string value must equal the product's string spec.

A product that states a value that falls short is out. One that states nothing for an asked spec is not the same thing: when other products of its category do state it, the spec applies to that category and this product is a candidate nobody has confirmed — it comes back with the silent keys in unstated, after the confirmed ones. (A spec nobody in the category states does not apply to it: a vacuum gripper is not asked its stroke.) strict=True keeps only products that state everything asked.

Ordered by how much is unstated (nothing first), then validation level (best first), then closeness to the minimums (smallest headroom first), then id.

Product

One catalog entry as the index lists it.

catalog_ref property

catalog_ref

What set_part(catalog=...) records: the id and the revision.

distribution class-attribute

distribution = 'public'

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'.

kind class-attribute

kind = 'model'

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'.

level property

level

Validation level as a number (V0 = 0); unknown counts as -1.

unstated class-attribute

unstated = ()

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.

attributes

attributes() -> dict[str, float]

Every numeric spec the product states, flattened — what lands on the part when it is identified, and what :func:search filters on.

identify

identify(
    scene,
    target: str,
    *,
    kind: Optional[str] = None,
    qty: int = 1,
    **overrides: Any,
) -> str

Write this product onto a resident: set_part(target, catalog=(id, revision), manufacturer, model, category, attributes). The numeric specs come along, so the requirement check reads them. overrides add or replace attributes (mass_kg=...). Returns what set_part returns (the kind the target resolved to).

text

text(key: str) -> Optional[str]

A string spec (ip_rating, flange_standard, ...), if stated.

value

value(key: str) -> Optional[float]

The number answering a requirement key, through the same aliases the requirement check uses.

index

index(
    *,
    revision: Optional[str] = None,
    path: Union[str, Path, None] = None,
    offline: Optional[bool] = None,
    refresh: bool = False,
) -> Index

The catalog index. path (or BOTRAIL_CATALOG_INDEX) reads a local index.json; otherwise the hub resolves revision (default: the newest commit) and downloads the index pinned to it. When the hub cannot be reached the newest index already in the Hugging Face cache is used — offline=True goes straight to the cache, offline=False never does. Hub lookups are cached per process (refresh=True asks again).

search

search(
    category: Optional[str] = None,
    *,
    index: Union[Index, str, Path, None] = None,
    revision: Optional[str] = None,
    kind: Optional[str] = None,
    manufacturer: Optional[str] = None,
    level: Optional[str] = None,
    text: Optional[str] = None,
    limit: Optional[int] = None,
    strict: bool = False,
    **specs: Any,
) -> list[Product]

Products in the catalog that match — see :meth:Index.search. index takes an :class:Index or a path to one; otherwise the published index is used (pinned to revision when given).

bt.catalog.search("gripper.parallel", stroke_mm=150, payload_kg=2.3)
bt.catalog.search("manipulator", reach_mm=900, payload_kg=6, level="V3")
bt.catalog.search(kind="spec", category="structure.fence")

search_for

search_for(
    row,
    *,
    category: Optional[str] = None,
    index: Union[Index, str, Path, None] = None,
    **extra: Any,
) -> list[Product]

Candidates for one requirement row (scene.requirements()["tool"]): its category, every >= requirement (a minimum) and every <= one (a key__max maximum) become the filters; extra adds or overrides filters (level="V3", ip_rating="IP54"), and key=None drops one. Candidates that do not state an asked spec come back too, marked (Product.unstated) and last — strict=True leaves them out (see :meth:Index.search).