Auto-Provisioning (Detection)
Part of the OpenRAL public-symbol inventory. Hand-curated;
(LNN)markers are refreshed bytools/refresh_methods_linenos.py.
python/detect/src/openral_detect/__init__.py
detect_hardware(*, dds_timeout_s=5.0, include=None, exclude=None) -> DetectionReport— Umbrella probe entry. (indetect.py:L33)build_compute_spec(gpu, report) -> ComputeSpec— Build aComputeSpecfrom a GPU probe +DetectionReport, extracting the same fieldsassemble_robot_descriptionwrites intocompute_edge/compute_local, for callers (e.g.openral doctor) that only need the compute spec. (inassemble.py:L83)assemble_robot_description(detection, *, base_description=None, force_robot_type=None, enrich_cameras=True) -> RobotDescription— Identify-then-enrich: infers the robot from DDS, then CAN, then USB (strongest evidence first).force_robot_typepins the canonical base manifest and raisesROSConfigErrorwhen it doesn't resolve — e.g. to select SO-100 over the USB-indistinguishable SO-101 default.enrich_cameras=False(the interactive wizard's choice) leaves the sensor list for the camera-binding wizard to own; the probe-only--no-writepath usesTrue. (inassemble.py:L72)check_installed_rskills(robot, *, registry_path=None, rskills_dir=None) -> CompatibilityReport— Walk-all: runrSkill.check_compatibilityagainst every installed (and optionally in-tree) skill. (incompatibility.py:L107)check_single_rskill(rskill_id, robot) -> CompatibilityReport— Resolve one id viaload_rskill_manifestand emit a one-row report with per-section verdicts. (incompatibility.py:L294)- const
PROBE_NAMES: frozenset[str]— Names accepted bydetect_hardware(include=...).
python/detect/src/openral_detect/assemble.py
Assemble a complete RobotDescription from a DetectionReport: pick a canonical base manifest (by override, DDS/CAN/USB inference, or a minimal scaffold), then enrich sensors, compute, and ROS 2 metadata. Never raises on a missing catalog entry — falls back to a generic SensorSpec and records a warning.
build_compute_spec(gpu, report) -> ComputeSpec— Build aComputeSpecfrom a GPU probe +DetectionReport, extracting the same fieldsassemble_robot_descriptionwrites intocompute_edge/compute_local, for callers (e.g.openral doctor) that only need the compute spec. (L59)assemble_robot_description(detection, *, base_description=None, force_robot_type=None, enrich_cameras=True) -> RobotDescription— Identify-then-enrich; inference order is DDS then CAN then USB, strongest evidence first.force_robot_typepins the canonical base manifest and raisesROSConfigErrorwhen it does not resolve.enrich_cameras=Falseleavessensors/sensor_bundlesuntouched for the camera-binding wizard to own; compute and ROS 2 enrichment always run regardless. (L98)
python/detect/src/openral_detect/compatibility.py
class SectionVerdict(BaseModel)— Per-section verdict foropenral rskill check <rskill_id>. (L56)class RSkillCompatRow(BaseModel)— One row in the compatibility report. (L81) fields:repo_id, version, role, manifest_path, embodiment_tags, compatible, reason, failure_kind, sectionsclass CompatibilityReport(BaseModel)—openral rskill checkoutput. (L97) fields:schema_version, generated_at, robot_name, robot_embodiment_tags, rowscompatible -> list[RSkillCompatRow](property) — Rows that passed every check. (L109)incompatible -> list[RSkillCompatRow](property) — Rows that failed at least one check. (L114)check_installed_rskills(robot, *, registry_path=None, rskills_dir=None) -> CompatibilityReport— Walk-all: runrSkill.check_compatibilityagainst every installed (and optionally in-tree) skill. (L119)- const
_CLASSIFICATION_KEYWORDS: tuple[tuple[str, FailureKind], ...]— Keyword →FailureKindlookup used by_classifyto coarsely classify aROSCapabilityMismatchmessage; most-specific match wins, falls back to"capability_flag". (L227) _evaluate_sections(manifest, robot) -> list[SectionVerdict]— Run each per-section production check and collect the six verdicts. (L283)check_single_rskill(rskill_id, robot) -> CompatibilityReport— Resolverskill_idviaload_rskill_manifestand report its compatibility withrobotas a one-row report with per-section verdicts; aggregatecompatibleis true when every non-informational section passes. (L365)
python/detect/src/openral_detect/detect.py
Umbrella entry point detect_hardware(): runs every probe sequentially (each wrapped in a safety_span for OTel) and aggregates into a single DetectionReport. Synchronous and never raises — probe failures append typed warnings to DetectionReport.warnings instead.
- const
PROBE_NAMES: frozenset[str]— Recognized probe names forinclude/exclude:usb,can,dds,gpu,cameras_v4l2,cameras_realsense,network. (L34) detect_hardware(*, dds_timeout_s=5.0, include=None, exclude=None) -> DetectionReport— Probe every supported domain and return a typed report. (L39)
python/detect/src/openral_detect/probes/
probe_usb(*, warnings=None) -> UsbProbeResult— Wrapsopenral_cli.autodetect.enumerate_usb_devices+match_known_devices. (usb.py L21)_enrich_can_buses(description, detection) -> RobotDescription— Replaces the CAN interface names inhal.parameters.defaultswith the ones actually found on this host, for any robot declaringhal.parameters.can_bus_bindings— without it a config could name interfaces that merely look host-derived. A role matching no interface, or more than one, appends a warning and leaves the manifest value alone rather than guessing. Defined inassemble.py, not this package.probe_can(*, warnings=None) -> CanProbeResult— Wrapsopenral_cli.autodetect.enumerate_can_interfaces+match_can_interfaces. Finds what USB-serial enumeration structurally can't: a CAN-attached arm (OpenArm) registers a network device, not a/dev/tty*node. Read-only — never opens a socket, so probing a live robot can't perturb it. (can.py L85)diagnose_can_matches(matches) -> list[str]— Turn controller states on matched buses into operator-facing lines:ERROR-PASSIVEon an up, correctly-configured bus means frames are leaving the adapter unacknowledged, i.e. the motors are unpowered;BUS-OFFpoints at termination / bitrate. (can.py L47)probe_dds(*, timeout_s=5.0, warnings=None) -> Ros2TopologyResult— Wrapsscan_dds_topics+infer_robot_from_topicsand captures RMW / domain id. (dds.py L19)- const
NVIDIA_TOPS_BY_NAME_KEYWORD: tuple[tuple[str, float], ...]— Coarse TOPS estimate keyed by a substring that must appear in the GPUnamefield; first-match-wins, most-specific keyword first. (gpu.py L50) - const
JETSON_BOARD_TOPS: dict[str, float]— TOPS per Jetson board (peak INT8, NVIDIA product briefs). AGX Thor is deliberately absent — NVIDIA publishes its headline figure in sparse FP4 TFLOPS with no documented INT8 conversion. (gpu.py L77) - const
DTYPES_BY_COMPUTE_CAPABILITY: tuple[tuple[tuple[int, int], tuple[QuantizationDtype, ...]], ...]— Quantization dtypes a CUDA accelerator can reasonably execute, keyed by(major, minor)compute capability; conservative — only adds a dtype with hardware backing. (gpu.py L89) probe_gpus(*, warnings=None) -> GpuProbeResult— NVIDIA via pynvml with an nvidia-smi fallback, Jetson via jtop/proc, Apple Silicon via system_profiler. AGX Thor's compute capability is confirmed (11.0) but it's deliberately excluded fromJETSON_BOARD_TOPS. On a unified-memory SoC (GB10/DGX Spark, Thor) VRAM falls back to_unified_memory_vram_fallbackwith a shared-with-the-OS warning rather than dropping the GPU. (gpu.py L649)_unified_memory_vram_fallback(warnings, missing_warning, found_warning) -> tuple[int, int] | None— Shared unified-memory-SoC VRAM fallback for both NVIDIA probes: no discrete VRAM pool → falls back to_system_memory_mib()(total RAM +/proc/meminfoMemAvailable), appending a warning either way. (gpu.py L301)_probe_cuda_toolkit_version() -> str | None— prefers/usr/local/cuda/bin/nvcc(_CUDA_HOME_NVCC) over$PATH, so a distro nvcc at/usr/bincannot shadow a newer/usr/local/cuda-Ninstall and wrongly close the cuMotion CUDA≥13 gate.- const
_CUDA_HOME_NVCC: Path— Canonical "currently selected CUDA toolkit" symlink (/usr/local/cuda/bin/nvcc) preferred by_probe_cuda_toolkit_versionover$PATH. (gpu.py L616) _cc_for_jetson_board(board: str) -> tuple[int, int] | None— Map device-tree board string to CUDA compute capability via_JETSON_CC_BY_BOARD_KEYWORD(Thor→11.0, Orin→8.7, Xavier→7.2, Nano→5.3). (gpu.py L213)- const
_JETSON_CC_BY_BOARD_KEYWORD: tuple[tuple[str, tuple[int, int]], ...]— Device-tree model keyword → CUDA compute capability table read by_cc_for_jetson_board; more-specific keys first. (gpu.py L200) _probe_jetson(warnings, *, model_path=None, release_path=None) -> JetsonInfo | None— Probe a Tegra host.model_path/release_pathaccept fixtures for unit tests; production reads/proc/device-tree/model+/etc/nv_tegra_release. ReturnsNone+ warning when the board is unknown. (gpu.py L475)- const
_DEFAULT_MODEL_PATH: Path— Default/proc/device-tree/modelpath probed by_probe_jetson; overridable for tests. (gpu.py L471) - const
_DEFAULT_RELEASE_PATH: Path—openral_core.TEGRA_RELEASE_PATH, the default release file probed by_probe_jetson; overridable for tests. (gpu.py L472) _probe_nvmm_available(*, search_paths=None) -> bool— True whenlibnvbufsurface.sois installed (L4T multimedia stack). PopulatesRobotCapabilities.nvmm_available.search_pathsoverrides the canonical roots (_NVBUFSURFACE_SEARCH_PATHS) for tests. (gpu.py L238)- const
_NVBUFSURFACE_SEARCH_PATHS: tuple[Path, ...]— Canonical L4T install paths forlibnvbufsurface.sosearched by_probe_nvmm_available. (gpu.py L232) - const
_SYSFS_V4L2: str—/sys/class/video4linux— the always-present backend root tried before falling back tov4l2-ctl --list-devices. (cameras.py L27) probe_v4l2_cameras(*, warnings=None) -> list[V4l2CameraInfo]— Linux V4L2 enumeration, one row per physical camera. Prefers the/sys/class/video4linuxbackend (always present, no root) and falls back tov4l2-ctl --list-devices; only sysfs exposes the USB descriptor that resolves ausb_uvccatalog signature. (cameras.py L30)probe_realsense_devices(*, warnings=None) -> list[RealsenseDeviceInfo]—pyrealsense2.context()wrapper; produces canonicalmodel_idready for catalog reverse-lookup. (realsense.py L15)probe_network(*, warnings=None) -> NetworkProbeResult— Hostname / per-interface MAC / IPv4 / MTU / link-speed / default route via psutil. (network.py L17)
python/detect/src/openral_detect/registry.py
- const
_WORKSPACE_MARKERS: tuple[str, ...]— Both"robots"and"python"must be sibling dirs of a candidate root; a barerobots/alone is a plausible name for unrelated content. (L44) - const
_MAX_ROOT_SEARCH_DEPTH: int— How many ancestor directories_discover_workspace_rootwalks before giving up. (L45) - const
_PACKAGE_WORKSPACE_ROOT: Path | None— Workspace root discovered once at import time by walking up from this module;Nonewhen no ancestor has bothrobots/andpython/. (L57) - const
_OPENRAL_ROBOT_TYPE_TO_DIR: dict[str, str]— Alias table from abh_robot_typeslug to its canonicalrobots/<name>/directory (e.g."so100"→"so100_follower"); a slug absent from this table is tried verbatim as a directory name. (L72) canonical_robot_path(bh_robot_type) -> Path | None— Resolve a robot-type slug ("so101","aloha", …) torobots/<name>/robot.yaml: alias lookup in_OPENRAL_ROBOT_TYPE_TO_DIRfirst, then the slug tried verbatim as arobots/<slug>/dir so an operator override can name any committed robot directly. Therobots/tree is located by walking up to the nearest ancestor holding bothrobots/andpython/, so resolution doesn't depend on cwd. (L94)signature_for_realsense(model_id) -> SensorSignature(L138)signature_for_v4l2(name) -> SensorSignature(L143)signature_for_usb_uvc(vid, pid) -> SensorSignature(L148)
python/detect/src/openral_detect/report.py
class UsbDeviceRecord(BaseModel)— One USB serial device captured for the report. (L53) fields:port, vid, pid, descriptionclass UsbMatchRecord(BaseModel)— Detected USB device matched against the VID/PID table. (L66) fields:device, chip, driver_hint, embodiment_tag, bh_robot_typeclass UsbProbeResult(BaseModel)— USB enumeration output. (L76) fields:devices, matchesclass CanInterfaceInfo(BaseModel)— One SocketCAN interface captured for the report.stateis the field to read when a detected arm will not move — an up, correctly-configured link inERROR-PASSIVEis transmitting into a bus whose motors are unpowered. (L86) fields:name, is_up, fd_enabled, bitrate, data_bitrate, state, driver, mtu, vid, pid, adapterclass CanMatchRecord(BaseModel)— A group of CAN interfaces whose names identify one known robot (a bimanual arm contributes one per side). (L111) fields:interfaces, chip, driver_hint, embodiment_tag, bh_robot_typeclass CanProbeResult(BaseModel)— SocketCAN enumeration output. (L121) fields:interfaces, matchesclass NvidiaGpuInfo(BaseModel)— One discrete NVIDIA GPU with full attribute set. (L131) fields:index, name, vram_total_mib, vram_free_mib, pci_bus_id, driver_version, cuda_compute_capability, cuda_toolkit_version, tensorrt_version, supported_dtypes, tops_estimateclass JetsonInfo(BaseModel)— An NVIDIA Jetson SoC. (L152) fields:board, soc, jetpack_version, tops, ram_gb, cuda_compute_capability, cuda_toolkit_version, tensorrt_version, supported_dtypes, power_modeclass AppleSiliconInfo(BaseModel)— An Apple Silicon SoC. (L167) fields:chip, gpu_cores, unified_mem_gb, supported_dtypesclass GpuProbeResult(BaseModel)— GPU / SoC discovery output. (L187) fields:nvidia, jetson, apple_silicon, backendclass V4l2CameraInfo(BaseModel)— One V4L2 camera device (not node — a UVC camera registers a capture and a metadata node;device_pathnames the capture one).vid/pid/serialcome from the camera's own USB descriptor and are what resolve ausb_uvccatalog signature;0/""for MIPI-CSI cameras and for hosts probed throughv4l2-ctl. (L199) fields:device_path, name, bus_info, formats, max_resolution, vid, pid, serialclass RealsenseDeviceInfo(BaseModel)— Intel RealSense device discovered via pyrealsense2. (L222) fields:serial, name, model_id, firmware_version, usb_typeclass OrbbecDeviceInfo(BaseModel)— Orbbec depth camera. (L232) fields:serial, name, model_id, firmware_versionclass CameraProbeResult(BaseModel)— Per-host camera discovery output. (L241) fields:v4l2, realsense, orbbecclass DdsTopicRecord(BaseModel)— One ROS 2 topic discovered during DDS scan. (L252) fields:name, type_nameclass Ros2TopologyResult(BaseModel)— ROS 2 topology snapshot. (L259) fields:topics, inferred_robot_type, has_robot_description, has_tf, nodes, rmw_implementation, domain_idclass NetworkInterfaceInfo(BaseModel)— One network interface. (L274) fields:name, mac, ipv4, mtu, link_speed_mbps, is_upclass NetworkProbeResult(BaseModel)— Per-host network discovery output. (L285) fields:hostname, interfaces, default_routeclass DetectionReport(BaseModel)— Typed result of a singledetect_hardware()invocation. (L296) fields:schema_version, detected_at, host_os, python_version, usb, can, gpu, cameras, ros2, network, warningsderived_runtimes() -> list[RSkillRuntime]— Translate detected accelerators into a host-supported runtime list. (L331)derived_dtypes() -> list[QuantizationDtype]— Union of supported quantization dtypes across detected accelerators. (L364)