Hardware adapter interfaces
Registration, configuration, and runtime contracts for arms, feedback, grippers, and cameras.
A hardware adapter is an independent Python package installed into the RoboAPI environment. Entry points register its components for selection in the setup wizard. They share the runtime used by the local console, HTTP API, and MCP tools. See Connect new hardware for the development workflow.
Responsibilities and data boundaries
| RoboAPI provides | The adapter implements |
|---|---|
| Home and A/B/C calibration, training-frame mapping | Measurements and control at the native flange centre |
| Paths, action timing, and 30 Hz target dispatch | SDK connections, native control frequency, interpolation, and holding |
| Model sessions, console, HTTP/MCP | Device faults, recovery, and resource cleanup |
| Component selection and configuration storage | Configuration fields, installation validation, and SDK dependencies |
Driver poses use metres and a wxyz quaternion:
[x, y, z, qw, qx, qy, qz]The eight-value model state and actions use xyzw, followed by gripper opening:
[x, y, z, qx, qy, qz, qw, gripper]Drivers read and write native flange coordinates; the shared runtime maps them to and from model task coordinates. Control and feedback must use the same control point, native base coordinates, and flange axes, with no positional TCP offset.
Register components
Declare the components you provide in the package's pyproject.toml:
[project.entry-points."roboapi.arms"]
acme = "roboapi_acme.arm:arm_plugin"
[project.entry-points."roboapi.grippers"]
acme = "roboapi_acme.gripper:gripper_plugin"
[project.entry-points."roboapi.cameras"]
acme = "roboapi_acme.camera:camera_plugin"| Entry-point group | Factory result | Device creation |
|---|---|---|
roboapi.arms | ArmPlugin | create(config, tool) |
roboapi.grippers | ComponentPlugin | create(config) |
roboapi.cameras | ComponentPlugin | create(config) |
Names are unique within a group. Different groups may use the same name, but names must not override built-in components. The wizard reports and skips external adapters that cannot load. A saved configuration selecting such an adapter fails with its load error.
Each factory provides:
| Field | Contract |
|---|---|
options | A nonempty tuple of ComponentOption, each with a display label and configuration class |
create | Constructs a disconnected device object |
configure(option, setup) | Returns the configuration for the selected component |
setup_notes | Optional tuple of instructions printed after setup |
An ArmPlugin also requires create_feedback(config) and may provide create_guiding(config, tool) and configure_tool(setup).
When a factory supports multiple variants, distinguish them with ComponentOption.selectors, such as {"model": "a"}. Declare those fields in the configuration class and preserve them in YAML so the configuration selects exactly one option.
Configuration and setup
Component configurations extend ArmConfig, GripperConfig, or CameraConfig. This is the camera configuration structure used in a generated project:
from dataclasses import dataclass
from roboapi.hardware import CameraConfig
@dataclass
class AcmeCameraConfig(CameraConfig):
type: str = "acme"
address: str = ""
@property
def connection_id(self) -> str:
return self.address
def configure(option, setup):
return option.config_type(
address=setup.ask("Camera address or device path"),
)Set type to the entry-point name. connection_id is a stable, nonempty device connection identifier; recheck calibration when that identity changes. Allow default construction and validate installation values in validate(). Call super().validate() when extending validation.
The wizard supplies setup:
| Member | Usage |
|---|---|
ask(label, default="") | Reads a string |
choose(label, choices) | Displays choices and returns a zero-based index |
device(label, patterns, default) | Selects a device path or accepts a manually entered path |
secret(label, filename) | Returns a separate credential-file path; writes are deferred until setup saves. Use a single filename |
config | The installation configuration, including components already selected |
Configuration proceeds arm → gripper → camera, followed by tool load if required by the arm. A gripper can suggest an address from the selected arm. Use secret() for credentials and store their paths in configuration. Defer SDK imports and device connections to explicit connection operations.
Arm and read-only feedback
Interfaces are exported from roboapi.hardware. An arm implements Arm:
| Member | Behavior |
|---|---|
live, last_error | Whether control is available; provide a reason when unavailable |
connect(*, calibration=False) | Opens control and prepares target handling; preserve force-sensor zeros during temporary calibration returns |
disconnect() | Stops and joins control workers, then releases the device and control ownership |
get_joints() | Returns joint positions in radians; joint count depends on the device |
get_eef_pose() | Returns the measured native flange-centre pose, length 7, in wxyz order |
servo_pose(pose7, twist=None, *, position_only=False) | Updates a target without blocking; linear velocity is in m/s and angular velocity in rad/s |
validate_calibration(device) | Checks that control preserves the sampled device identity and flange convention |
recover(should_stop=None) | Restores device control and supports cancellation; the runtime handles the subsequent return Home |
position_only=True requests target tracking without added force-driven offsets. The driver owns interpolation, control timing, and holding between target updates. Return measurements rather than the last commanded target.
create_feedback(config) returns an independent FeedbackReader. Feedback must remain readable during teaching and calibration sampling without taking motion control.
| Member | Behavior |
|---|---|
live, last_error | Feedback connection state and error |
connect(), disconnect() | Opens and releases the read-only connection |
snapshot() | Returns a dictionary with pose, timestamp, and device, optionally has_errors |
validate_device(device) | Checks saved device identity and settings |
pose is the measured native flange pose. timestamp increases with actual samples; repeated device samples must not receive new timestamps. device contains stable, JSON-compatible identity and settings used when checking saved calibration.
Optional native hand guiding
create_guiding(config, tool) returns a GuidingSession:
| Member | Behavior |
|---|---|
active | Whether native guiding is active |
start(axis=None) | Starts guiding; None allows Home adjustment |
set_axis(axis=None) | x, y, or z permits only that translation, constraining rotation and other translation axes |
check_stopped() | Confirms guiding has stopped before sampling or mode changes |
close() | Ends guiding and releases its resources |
Without native guiding, users capture Home and A/B/C through the robot's own teaching controls. Read-only feedback is still required.
Gripper
A gripper implements Gripper with one parallel-gripper opening ratio: 0 means closed and 1 means open.
| Member | Behavior |
|---|---|
live, last_error | Connection and fault state |
connect(), disconnect() | Connects the device and releases resources |
get_ratio() | Returns the measured normalized opening |
set_ratio(ratio) | Updates the opening target without blocking |
move_blocking(ratio, should_stop=None) | Waits for motion completion and supports cancellation |
recover() | Restores gripper operation after a fault |
Set full opening or closing travel time in GripperConfig.full_travel_s, in seconds, as a finite positive number. Shared motion scheduling uses it to time gripper moves. Keep device communication inside the driver so it does not block 30 Hz target dispatch.
Camera
A camera implements Camera:
| Member | Behavior |
|---|---|
live, last_error | Connection, capture, and frame-expiry state |
connect(), disconnect() | Starts capture and releases capture resources |
read() | Returns (image, age_s) |
read_after(after, timeout_s, should_stop=None) | Waits within the timeout for a capture newer than after; after=None means latest. Returns None on cancellation |
image is a uint8 RGB array with shape (720, 1280, 3), captured at 30 FPS. age_s is the actual elapsed time since capture, in seconds. Preserve raw orientation, view, and fisheye distortion without cropping, padding, or mirroring. Convert transport formats such as BGR to RGB.
after is a monotonic timestamp; correlate device capture time correctly. Reading a cached frame must not reset its age to zero. Configure a finite positive expiry threshold in CameraConfig.stale_after_s.
Tool load
Register configure_tool(setup) only when the arm needs external inertial data. It runs after component selection and returns a ToolLoad or None. Without the callback, setup does not prompt for load data. The control and guiding factories receive the load, and the driver applies or verifies it.
ToolLoad field | Meaning and units |
|---|---|
mass_kg | Mass of the complete flange assembly, kg |
com_flange_m | Three-dimensional centre of mass relative to the flange, m |
inertia_kg_m2 | 3×3 inertia about the centre of mass, with flange-parallel axes, kg·m² |
Mass is non-negative, values are finite, and inertia is symmetric and positive semidefinite. Include the complete gripper, camera, and adapter assembly without double-counting parts already represented in the controller's dynamics model.
Franka uses local tool load data; UR configures loads in PolyScope. Use standard assembly data only for matching hardware. Load, flange TCP, and spatial calibration are separate settings; updating load data does not replace recalibration.
Connection and runtime behavior
- Registration and construction do not access devices. Defer optional SDK imports until connection.
- The runtime dispatches targets at 30 Hz. The driver maintains its native control loop and holds position while the next inference is pending.
- Optionally implement
hold()for stopping. Without an arm-specifichold(), the runtime sends the measured pose as a holding target. A gripper that must interrupt motion immediately should provide its ownhold(). - Stop and recovery operations support cancellation. Cleanup is repeatable and handles partially established connections.
- When components share a CAN connection or SDK session, the adapter package coordinates access and lifetime, closing it after all users have released it.
Check discovery and configuration storage, actual feedback and device identity, calibration and returns, continuous actions, holding during inference, stopping, and recovery. Start with Connect new hardware.